Skip to content

Commit

Permalink
Properly fix Java 8 API compatibility (#2218)
Browse files Browse the repository at this point in the history
  • Loading branch information
Vampire committed Jul 16, 2023
1 parent ba7d17a commit ed9fcf0
Show file tree
Hide file tree
Showing 4 changed files with 53 additions and 55 deletions.
76 changes: 41 additions & 35 deletions buildSrc/src/main/kotlin/Java9Modularity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
*/

import org.gradle.api.*
import org.gradle.api.file.*
import org.gradle.api.provider.*
import org.gradle.api.tasks.*
import org.gradle.api.tasks.bundling.*
import org.gradle.api.tasks.compile.*
import org.gradle.jvm.toolchain.*
import org.gradle.kotlin.dsl.*
import org.gradle.process.*
import org.jetbrains.kotlin.gradle.dsl.*
import org.jetbrains.kotlin.gradle.plugin.mpp.*
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.*
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.*
import org.jetbrains.kotlin.gradle.targets.jvm.*
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
Expand Down Expand Up @@ -46,19 +50,17 @@ object Java9Modularity {

kotlin.sourceSets.create(sourceSetName) {
val sourceFile = this.kotlin.find { it.name == "module-info.java" }
val targetFile = compileKotlinTask.destinationDirectory.file("../module-info.class").get().asFile
val targetDirectory = compileKotlinTask.destinationDirectory.map {
it.dir("../${it.asFile.name}Module")
}

// only configure the compilation if necessary
if (sourceFile != null) {
// the default source set depends on this new source set
defaultSourceSet.dependsOn(this)

// register a new compile module task
val compileModuleTask = registerCompileModuleTask(compileModuleTaskName, compileKotlinTask, sourceFile, targetFile)
val compileModuleTask = registerCompileModuleTask(compileModuleTaskName, compileKotlinTask, sourceFile, targetDirectory)

// add the resulting module descriptor to this target's artifact
artifactTask.dependsOn(compileModuleTask)
artifactTask.from(targetFile) {
artifactTask.from(compileModuleTask) {
if (multiRelease) {
into("META-INF/versions/9/")
}
Expand All @@ -73,39 +75,43 @@ object Java9Modularity {
}
}

private fun Project.registerCompileModuleTask(taskName: String, compileTask: KotlinCompile, sourceFile: File, targetFile: File) =
private fun Project.registerCompileModuleTask(taskName: String, compileTask: KotlinCompile, sourceFile: File, targetDirectory: Provider<out Directory>) =
tasks.register(taskName, JavaCompile::class) {
// Also add the module-info.java source file to the Kotlin compile task;
// the Kotlin compiler will parse and check module dependencies,
// but it currently won't compile to a module-info.class file.
compileTask.source(sourceFile)


// Configure the module compile task.
dependsOn(compileTask)
source(sourceFile)
outputs.file(targetFile)
classpath = files()
destinationDirectory.set(compileTask.destinationDirectory)
sourceCompatibility = JavaVersion.VERSION_1_9.toString()
targetCompatibility = JavaVersion.VERSION_1_9.toString()
destinationDirectory.set(targetDirectory)
javaCompiler.set(
this@registerCompileModuleTask.the<JavaToolchainService>().compilerFor {
languageVersion.set(JavaLanguageVersion.of(11))
}
)
options.release.set(9)

options.compilerArgumentProviders.add(object : CommandLineArgumentProvider {
@get:CompileClasspath
val compileClasspath = compileTask.libraries

doFirst {
// Provide the module path to the compiler instead of using a classpath.
// The module path should be the same as the classpath of the compiler.
options.compilerArgs = listOf(
"--release", "9",
"--module-path", compileTask.libraries.asPath,
@get:CompileClasspath
val compiledClasses = compileTask.destinationDirectory

@get:Input
val moduleName = sourceFile
.readLines()
.single { it.contains("module ") }
.substringAfter("module ")
.substringBefore(' ')
.trim()

override fun asArguments() = mutableListOf(
// Provide the module path to the compiler instead of using a classpath.
// The module path should be the same as the classpath of the compiler.
"--module-path",
compileClasspath.asPath,
"--patch-module",
"$moduleName=${compiledClasses.get()}",
"-Xlint:-requires-transitive-automatic"
)
}

doLast {
// Move the compiled file out of the Kotlin compile task's destination dir,
// so it won't disturb Gradle's caching mechanisms.
val compiledFile = destinationDirectory.file(targetFile.name).get().asFile
targetFile.parentFile.mkdirs()
compiledFile.renameTo(targetFile)
}
})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,7 @@ internal class CharsetReader(
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
byteBuffer = ByteBuffer.wrap(ByteArrayPool8k.take())
// An explicit cast is needed here due to an API change in Java 9, see #2218.
//
// In Java 8 and earlier, the `flip` method was final in `Buffer`, and returned a `Buffer`.
// In Java 9 and later, the method was opened, and `ByteFuffer` overrides it, returning a `ByteBuffer`.
//
// You could observe this by decompiling this call with `javap`:
// Compiled with Java 8 it produces `INVOKEVIRTUAL java/nio/ByteBuffer.flip ()Ljava/nio/Buffer;`
// Compiled with Java 9+ it produces `INVOKEVIRTUAL java/nio/ByteBuffer.flip ()Ljava/nio/ByteBuffer;`
//
// This causes a `NoSuchMethodError` when running a class, compiled with a newer Java version, on Java 8.
//
// To mitigate that, `--bootclasspath` / `--release` options were introduced in `javac`, but there are no
// counterparts for these options in `kotlinc`, so an explicit cast is required.
(byteBuffer as Buffer).flip() // Make empty
byteBuffer.flip() // Make empty
}

@Suppress("NAME_SHADOWING")
Expand Down Expand Up @@ -105,7 +92,7 @@ internal class CharsetReader(
if (bytesRead < 0) return bytesRead
byteBuffer.position(position + bytesRead)
} finally {
(byteBuffer as Buffer).flip() // see the `init` block in this class for the reasoning behind the cast
byteBuffer.flip()
}
return byteBuffer.remaining()
}
Expand Down
11 changes: 6 additions & 5 deletions gradle/configure-source-sets.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@
* Copyright 2017-2022 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

java {
toolchain {
languageVersion.set(JavaLanguageVersion.of(8))
}
}

kotlin {
jvm {
withJava()
configure([compilations.main, compilations.test]) {
kotlinOptions {
jvmTarget = '1.8'
}
}
}

js {
Expand Down
4 changes: 4 additions & 0 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
* Copyright 2017-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
*/

plugins {
id 'org.gradle.toolchains.foojay-resolver-convention' version '0.5.0'
}

rootProject.name = 'kotlinx-serialization'

include ':kotlinx-serialization-core'
Expand Down

0 comments on commit ed9fcf0

Please sign in to comment.