I found this minimal set of code which seems to break any Java version I can find. (Also reproducible in https://www.onlinegdb.com/online_java_compiler with Java 23.0.2+7-58
). The example is not meant to do anything useful except to demonstrate the issue — my actual code is more complex.
record VehicleInput(Vehicle vehicle) {}
sealed interface Vehicle permits Car {}
record Car(String licensePlate) implements Vehicle {}
sealed interface Animal permits Cat, Dog {}
record Cat() implements Animal {}
record Dog() implements Animal {}
class VehicleProgressor {
private boolean progress(VehicleInput input, Animal animal) {
if (input.vehicle() instanceof Car(String licensePlate)) {
return true;
}
return switch (animal) {
case Cat cat -> true;
case Dog dog -> true;
};
}
}
public class Main {
public static void main(String[] args) {
System.out.println(Runtime.version());
new VehicleProgressor();
}
}
(Versions managed with sdkman)
$ sdk install java 24.0.1-amzn
$ sdk use java 24.0.1-amzn
(The error output is exactly the same with java 21.0.7-amzn
, java 24.0.1-amzn
, java 25.ea.25-open
, and java 25.ea.25-graal
.)
Compiling works:
$ javac Main.java
but running fails with:
$ java Main
24.0.1+9-FR
Exception in thread "main" java.lang.VerifyError: Inconsistent stackmap frames at branch target 96
Exception Details:
Location:
VehicleProgressor.progress(LVehicleInput;LAnimal;)Z @96: aload_3
Reason:
Type top (current frame, locals[5]) is not assignable to 'Cat' (stack map, locals[5])
Current Frame:
bci: @50
flags: { }
locals: { 'VehicleProgressor', 'VehicleInput', 'Animal', 'Animal', integer }
stack: { integer }
Stackmap Frame:
bci: @96
flags: { }
locals: { 'VehicleProgressor', 'VehicleInput', 'Animal', 'Animal', integer, 'Cat' }
stack: { }
Bytecode:
0000000: 2bb6 0007 3a05 1905 c100 0d99 0015 1905
0000010: c000 0d4e 2db6 000f 3a06 1906 3a04 04ac
0000020: 2c59 b800 1357 4e03 3604 2d15 04ba 0019
0000030: 0000 ab00 0000 001a 0000 0002 0000 0000
0000040: 0000 0024 0000 0001 0000 002e bb00 1d59
0000050: 0101 b700 1fbf 2dc0 0022 3a05 04a7 000a
0000060: 2dc0 0024 3a06 04ac 4ebb 001d 592d b600
0000070: 282d b700 1fbf
Exception Handler Table:
bci [21, 24] => handler: 104
Stackmap Table:
same_frame(@32)
append_frame(@42,Object[#50],Integer)
same_frame(@76)
same_frame(@86)
append_frame(@96,Object[#34])
full_frame(@103,{Object[#43],Object[#8],Object[#50]},{Integer})
same_locals_1_stack_item_frame(@104,Object[#38])
at Main.main(Main.java:30)
Is this a bug in Java/JDK?
If I change the class in the above code into:
class VehicleProgressor {
private boolean progress(VehicleInput input, Animal animal) {
if (input.vehicle() instanceof Car) {
return true;
}
return switch (animal) {
case Cat cat -> true;
case Dog dog -> true;
};
}
}
or into:
class VehicleProgressor {
private boolean progress(VehicleInput input, Animal animal) {
if (input.vehicle() instanceof Car(String licensePlate)) {
return true;
}
return true;
}
}
or into:
class VehicleProgressor {
private boolean progress(Vehicle vehicle, Animal animal) {
if (vehicle instanceof Car(String licensePlate)) {
return true;
}
return switch (animal) {
case Cat cat -> true;
case Dog dog -> true;
};
}
}
it runs fine just printing the version number.
The super strange part is that I'm not even calling the progress
method anywhere. I also tried having everything in separate files as public
interface
s/record
s, but that made no difference.
Turned out to be a clear bug. Reported based on @Jorn Vernee's suggestion to compiler-dev@openjdk.org
.
Based on the commenters, seems to only be an issue with javac and not with Eclipse Compiler for Java (ECJ).
Update: The bug could be reproduced with an even more minimal example; PR for the bugfix with additional details: https://github.com/openjdk/jdk/pull/25849
Targeted to be included in version 26: https://bugs.openjdk.org/browse/JDK-8358801