RFR: 8332106: VerifyError when using switch pattern in this(...) or super(…)

Jan Lahoda jlahoda at openjdk.org
Mon May 13 14:43:01 UTC 2024


Considering code like:

public class Test {

    public Test(int i) {
    }

    public Test(Object o) {
        this(o instanceof R(String s) ? s.length() : -1);
    }

    public static void main(String... args) {}

    record R(String s) {}
}


Running this crashes:

$ java .../Test.java
Exception in thread "main" java.lang.VerifyError: Stack map does not match the one at exception handler 36
Exception Details:
  Location:
    Test.<init>(Ljava/lang/Object;)V @36: astore_2
  Reason:
    Type uninitializedThis (current frame, locals[0]) is not assignable to 'Test' (stack map, locals[0])
  Current Frame:
    bci: @14
    flags: { flagThisUninit }
    locals: { uninitializedThis, 'java/lang/Object', top, 'Test$R' }
    stack: { 'java/lang/Throwable' }
  Stackmap Frame:
    bci: @36
    flags: { }
    locals: { 'Test', 'java/lang/Object' }
    stack: { 'java/lang/Throwable' }
  Bytecode:
    0000000: 2a2b c100 0799 0018 2bc0 0007 4e2d b600
    0000010: 093a 0419 044d 2cb6 000d a700 0402 b700
    0000020: 13a7 0011 4dbb 001a 592c b600 1c2c b700
    0000030: 1fbf b1                                
  Exception Handler Table:
    bci [14, 17] => handler: 36
  Stackmap Table:
    same_locals_1_stack_item_frame(@29,UninitializedThis)
    full_frame(@30,{UninitializedThis,Object[#2]},{UninitializedThis,Integer})
    full_frame(@36,{Object[#20],Object[#2]},{Object[#24]})
    same_frame(@50)

        at java.base/java.lang.Class.forName0(Native Method)
        at java.base/java.lang.Class.forName(Class.java:534)
        at java.base/java.lang.Class.forName(Class.java:513)
        at jdk.compiler/com.sun.tools.javac.launcher.Main.execute(Main.java:432)
        at jdk.compiler/com.sun.tools.javac.launcher.Main.run(Main.java:208)
        at jdk.compiler/com.sun.tools.javac.launcher.Main.main(Main.java:135)


The reason is that there is a synthetic catch generated wrapping the record accessors inside the pattern matching. The range where the exception this catch is catching is before the real `this(...)` `invokespecial`, and hence the `this` is still an "uninitialized this". But the code for the catch is generated at the end of the constructor, when `this` is already initialized, and javac generates the stack maps as if `this` was initialized.

In general, the pattern matching code can be both before and after the `this` has been initialized. But, I don't think a stack map frame can be generated for the handler originating in places with both "uninitialized this " and (initialized) this.

The proposal here is to generate one set of the catch handlers for the state with "uninitialized this", and another for (initialized) this, if needed.

-------------

Commit messages:
 - 8332106: VerifyError when using switch pattern in this(...) or super(...)

Changes: https://git.openjdk.org/jdk/pull/19217/files
  Webrev: https://webrevs.openjdk.org/?repo=jdk&pr=19217&range=00
  Issue: https://bugs.openjdk.org/browse/JDK-8332106
  Stats: 320 lines in 3 files changed: 305 ins; 4 del; 11 mod
  Patch: https://git.openjdk.org/jdk/pull/19217.diff
  Fetch: git fetch https://git.openjdk.org/jdk.git pull/19217/head:pull/19217

PR: https://git.openjdk.org/jdk/pull/19217


More information about the compiler-dev mailing list