Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

java.lang.NoSuchMethodError when parsing a JSON stream on Java 8 #2218

Closed
madhead opened this issue Mar 3, 2023 · 3 comments · Fixed by #2350
Closed

java.lang.NoSuchMethodError when parsing a JSON stream on Java 8 #2218

madhead opened this issue Mar 3, 2023 · 3 comments · Fixed by #2350
Assignees

Comments

@madhead
Copy link
Contributor

madhead commented Mar 3, 2023

Describe the bug

I suspect the JSON format to be prone to this issue: plasma-umass/doppio#497 (comment)

The problem seems to be in the CharsetReader's constructor, on the line #23:

init {
decoder = charset.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE)
byteBuffer = ByteBuffer.wrap(ByteArrayPool8k.take())
byteBuffer.flip() // Make empty
}

It seem that this class is compiled with a higher language level and, running on Java 8, causing the issue.

I've javap-ed the CharsetReader class (from the 1.5.0 versio of the library) and although I see the its class version is 52 (Java 8) it seems to be not enough for this nasty bug. The bytecode seems to use the ByteBuffer, which is incorrect 👇

image

It seems that a proper compiler option needs to be passed during the compilation process. The other way to solve this is using a manual cast.

To Reproduce

Please, refer to this minimal reproducible example: https://github.com/madhead/kotlinx-serialization-json-java8. Follow its README.

Basically, to reproduce the issues one needs to call Json.decodeFromStream on Java 8. As a result, he gets an exception like this:

Exception in thread "main" java.lang.NoSuchMethodError: java.nio.ByteBuffer.flip()Ljava/nio/ByteBuffer;
        at kotlinx.serialization.json.internal.CharsetReader.<init>(CharsetReader.kt:23)
        at kotlinx.serialization.json.internal.JavaStreamSerialReader.<init>(JvmJsonStreams.kt:258)
        at kotlinx.serialization.json.JvmStreamsKt.decodeFromStream(JvmStreams.kt:59)
        at me.madhead.kotlinx.serialization.json.java8.AppKt.main(App.kt:13)
        at me.madhead.kotlinx.serialization.json.java8.AppKt.main(App.kt)

Expected behavior

No exception is thrown, the stream is parsed correctly.

Environment

  • Kotlin platforms: JVM
  • JVM: 8.x
    openjdk version "1.8.0_322"
    OpenJDK Runtime Environment (Temurin)(build 1.8.0_322-b06)
    OpenJDK 64-Bit Server VM (Temurin)(build 25.322-b06, mixed mode)
    
  • Kotlin version: 1.8.10
  • Library version: 1.5.0
@madhead madhead changed the title java.lang.NoSuchMethodError when parsing a JSON stream java.lang.NoSuchMethodError when parsing a JSON stream on Java 8 Mar 3, 2023
madhead added a commit to madhead/kotlinx.serialization that referenced this issue Mar 3, 2023
madhead added a commit to madhead/kotlinx.serialization that referenced this issue Mar 4, 2023
@madhead
Copy link
Contributor Author

madhead commented Mar 4, 2023

Actually, --release is a javac option, a shortcut over --source + --target + --bootclasspath (JEP #247, and an article for mortals, where they explain the need for this option… with Buffer's flip() method!)

There seems to be no such option as bootclasspath for the kotlinc, right?

So, I guess, casting manually is the easiest fix for this particular bug. There are only two flips in the whole codebase…

madhead added a commit to madhead/kotlinx.serialization that referenced this issue Mar 7, 2023
madhead added a commit to madhead/kotlinx.serialization that referenced this issue Mar 7, 2023
@lukellmann
Copy link
Contributor

There seems to be no such option as bootclasspath for the kotlinc, right?

but there is an option like release: https://kotlinlang.org/docs/compiler-reference.html#xjdk-release-version

@madhead
Copy link
Contributor Author

madhead commented Mar 9, 2023

@lukellmann, indeed. But here we need specifically the bootclasspath, I guess. It's not only about targeting a specific JVM version, but using it's rt.jar during the compilation process:

javac … -Xbootclasspath ${jdk8path}/jre/lib/rt.jar

Also, the default value for the release is already 1.8 and it didn't work apparently.

Anyway, I'm just afraid of introducing such a change to the build process (and I obviously don't have enough knowledge to do that). I guess it may require some access to the TeamCity configs or what. So let the team think of it.

@sandwwraith sandwwraith self-assigned this Mar 13, 2023
shanshin added a commit that referenced this issue Jun 12, 2023
Fixes #2326

An explicit cast is needed here due to an API change in Java 9, see #2218.
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 `ByteFuffer` overrides it, returning a `ByteBuffer`.
This causes a `NoSuchMethodError` when running a class, compiled with a newer Java version, on Java 8.
shanshin added a commit that referenced this issue Jun 13, 2023
Fixes #2326

An explicit cast is needed here due to an API change in Java 9, see #2218.
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.
woainikk pushed a commit that referenced this issue Jun 20, 2023
Fixes #2326

An explicit cast is needed here due to an API change in Java 9, see #2218.
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.
Vampire added a commit to Vampire/kotlinx.serialization that referenced this issue Jun 26, 2023
Vampire added a commit to Vampire/kotlinx.serialization that referenced this issue Jun 26, 2023
JesusMcCloud pushed a commit to a-sit-plus/kotlinx.serialization that referenced this issue Jul 5, 2023
…2328)

Fixes Kotlin#2326

An explicit cast is needed here due to an API change in Java 9, see Kotlin#2218.
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.
Vampire added a commit to Vampire/kotlinx.serialization that referenced this issue Jul 16, 2023
Vampire added a commit to Vampire/kotlinx.serialization that referenced this issue Jul 16, 2023
Vampire added a commit to Vampire/kotlinx.serialization that referenced this issue Jul 20, 2023
Vampire added a commit to Vampire/kotlinx.serialization that referenced this issue Aug 1, 2023
Vampire added a commit to Vampire/kotlinx.serialization that referenced this issue Aug 1, 2023
sandwwraith pushed a commit that referenced this issue Aug 2, 2023
There are 7 methods in ByteBuffer that have the problem described in #2218 and was attempted to be fixed in #2219.
Unfortunately, the fix in #2219 was incomplete as another of these 7 methods is used in the same class.
Also it could happen anytime and with similar cases, that this happens again and is only recognized very late.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants