-
Notifications
You must be signed in to change notification settings - Fork 73
Java 8 incompatible references of java.nio.Buffer classes generated by Java 9 compilers
The Java 9 compiler and later can generate Java 8-incompatible bytecode through the --release 8
option. However, library developers sometimes fail to use this flag when they release the library. As the result, the generated bytecode contains references to methods that do not exist in Java 8 runtime. The invalid references manifest as NoSuchMethodError
. For example, protobuf-java 3.12.4 had this problem and bigtable-client-core
, which depends on protobuf-java, suffered the error:
https://github.com/protocolbuffers/protobuf/issues/7827
java.lang.NoSuchMethodError: java.nio.CharBuffer.flip()Ljava/nio/CharBuffer;
at com.google.protobuf.TextFormat$Parser.toStringBuilder(TextFormat.java)
In the example above, why does TextFormat$Parser throws NoSuchMethodError? In Java 8, java.nio.CharBuffer
's flip()
method has return type java.nio.Buffer
. The class file of TextFormat$Parser
tried to call the method but with return type java.nio.CharBuffer
. The Java 8 runtime looks up method using both method name and types (both the parameter and return type). However, there's no method flip
in Java 8's java.nio.CharBuffer
that returns java.nio.CharBuffer
.
Detecting this problem is difficult. Protobuf-java 3.12.4 was released on July 28th and the issue above was filed August 18th. By that time, around 172 Google Cloud Java client libraries had the version of protobuf in their dependencies.
Here are reasons why it's difficult to prevent:
- The project might already set
-target 8
injavac
or<target>8</target>
in Maven. This works for Java 8 but not enough for Java 9+. - The Java8 runtime can load the bytecode (major version: 52) without problem until it hits
NoSuchMethodError
. - The affected classes are small. It's
java.nio.CharBuffer
,java.nio.ByteBuffer
, andjava.nio.Buffer
. - The problem does not surface when a pull request check uses only one Java version. Compiling the project in Java 11 and running tests in Java 8 would detect this issue but such setup is rare.
The problem occurs when we use JDK 9+ that targets Java 8 runtime. If we keep using JDK 8 for now, the generated bytecode is correct.
If we cast instances of CharBuffer and ByteBuffer to Buffer, then the generated bytecode will not have the bad references.
((Buffer)charBuffer).flip();
Animal Sniffer can check your classes against the signature of different versions of Java runtime.