Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8029852

Bad code generated (VerifyError) when lambda instantiates enclosing local class and has captured variables

XMLWordPrintable

    • b15
    • generic
    • generic

        The compiler generates bad code that raises a verification error at class load time.

        public class SingleLocalTest {
            interface F {void f();}
            
            static F f;

            public static void main(String[] args) {
                StringBuffer sb = new StringBuffer();
                class Local1 {
                    public Local1() {
                        f = () -> new Local1();
                        sb.append("1");
                    }
                }
                new Local1();
                f.f();
                System.err.printf("Result: %s\n", sb);
            }
        }

        The above code currently crashes the compiler (JDK-8029725). But if JDK-8029725 is fixed, then the code generated for the program above throws a VerifyError at run-time:

        Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
        Exception Details:
          Location:
            SingleLocalTest$1Local1.lambda$new$0(Ljava/lang/StringBuffer;)V @5: getfield
          Reason:
            Type 'java/lang/StringBuffer' (current frame, stack[2]) is not assignable to 'SingleLocalTest$1Local1'
          Current Frame:
            bci: @5
            flags: { }
            locals: { 'java/lang/StringBuffer' }
            stack: { uninitialized 0, uninitialized 0, 'java/lang/StringBuffer' }
          Bytecode:
            0000000: bb00 0759 2ab4 0001 b700 0857 b1

                at SingleLocalTest.main(SingleLocalTest.java:14)

        This bad code will be generated in cases where a local variable is referenced within the local class. In the generated code, we see that two different mechanisms of capturing the local variable (sb) are both at play and both incomplete:

          final java.lang.StringBuffer val$sb;

          private static void lambda$new$0(java.lang.StringBuffer);
                 0: new #7 // class SingleLocalTest$1Local1
                 3: dup
                 4: aload_0
                 5: getfield #1 // Field val$sb:Ljava/lang/StringBuffer;
                 8: invokespecial #8 // Method "<init>":(Ljava/lang/StringBuffer;)V
                11: pop
                12: return

        -----

        Fix should also address nested locals:

        public class OuterLocalTest {
            interface F {void f();}
            
            static F f;

            public static void main(String[] args) {
                StringBuffer sb = new StringBuffer();
                class Local1 {
                    public Local1() {
                        class Local2 {
                            public Local2() {
                                f = () -> new Local1();
                                sb.append("2");
                            }
                        }
                        sb.append("1");
                        new Local2();
                    }
                }
                new Local1();
                f.f();
                System.err.printf("Result: %s\n", sb);
            }
        }

              rfield Robert Field (Inactive)
              rfield Robert Field (Inactive)
              Votes:
              0 Vote for this issue
              Watchers:
              3 Start watching this issue

                Created:
                Updated:
                Resolved: