Skip to content

Commit

Permalink
Fixed NoSuchMethodError when parsing a JSON stream on Java 8
Browse files Browse the repository at this point in the history
Fixes #2326

In Java 8 and earlier, the `position(I)` method was final in `Buffer`, and returned a `Buffer`.
In Java 9 and later, the method was opened, and `ByteBuffer` overrides it, returning a `ByteBuffer`.
This causes a `NoSuchMethodError` when running a class, compiled with a newer Java version, on Java 8.

Adding `-Xjdk-release=1.8` parameter allows the compiler to generate bytecode compatible with JVM 1.8
  • Loading branch information
shanshin committed Jun 12, 2023
1 parent 780f43e commit 2209692
Show file tree
Hide file tree
Showing 2 changed files with 8 additions and 13 deletions.
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,12 @@ def excludedFromBomProjects = unpublishedProjects + "kotlinx-serialization-bom"
def uncoveredProjects = ["kotlinx-serialization-bom", "benchmark", "guide"] as Set

subprojects {
tasks.withType(org.jetbrains.kotlin.gradle.tasks.KotlinCompile).configureEach { task ->
if (!(task.name.contains("Test") || task.name.contains("Jmh"))) {
task.kotlinOptions.freeCompilerArgs += "-Xjdk-release=1.8"
}
}

tasks.withType(org.jetbrains.kotlin.gradle.tasks.AbstractKotlinCompile).all { task ->
if (task.name.contains("Test") || task.name.contains("Jmh")) {
task.kotlinOptions.freeCompilerArgs += experimentalsInTestEnabled
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,9 @@ 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 +94,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() // see the `init` block in this class for the reasoning behind the cast
}
return byteBuffer.remaining()
}
Expand Down

0 comments on commit 2209692

Please sign in to comment.