Reden voor de uitzondering java.lang.VerifyError: Onjuist type op operand-stack

De onderstaande eenvoudige Java-code stuurt de
java.lang.VerifyError: Onjuist type op operand-stackuitzondering

public class TestJavaCodes {
    int parentData = 0;
    public void init() {
        A ob = new B();
    }
    public static void main(String[] args) {
        TestJavaCodes testJavaCodes = new TestJavaCodes();
        testJavaCodes.init();
    }
    public static class A {
        public A(MyLambdaFunc lambdaFunc) {
        }
    }
    public class B extends A {
        public B() {
            super((data1, type) -> {
                parentData = 1;
            });
        }
    }
    @FunctionalInterface
    public static interface MyLambdaFunc {
        public void onData(String data, int type);
    }
}

Als ik de code verwijder

parentData = 1

van de constructor van B, de uitzondering komt niet.

Kan iemand de reden hiervoor vertellen?


Antwoord 1, autoriteit 100%

Het probleem doet zich voor omdat uw lambda-expressie niet verwijst naar thisof een lid van thismaar naar een lid van de outerthis. Had je klasse Bzoals

. geschreven

public class B extends A {
    int innerData;
    public B() {
        super((data1, type) -> innerData = 1);
    }
}

de compiler heeft het zonder enige twijfel afgewezen omdat toegang tot innerDatabetekent toegang tot this.

Het punt met de buitenste instantie is dat het een constante is die zelfs beschikbaar is als de binnenste instantie nog niet volledig is geconstrueerd. Het is dus correct om de code te accepteren, maar helaas genereert de compiler code die probeert toegang te krijgen tot de buitenste instantie via een impliciet veld van de instantie van de binnenste klasse, dus de lambda-expressie vereist een instantie van de innerlijke klasse en probeert de niet volledig geconstrueerde binnenste te gebruiken class instance produceert de fout.

Het kan eenvoudig worden aangetoond dat de code correctcorrect kan worden gecompileerd:

public class B extends A {
    public B() {
        this(TestJavaCodes.this);
    }
    private B(TestJavaCodes outer) {
        super((data1, type) -> outer.parentData = 1);
    }
}

met die kleine wijziging verwijst de lambda-expressie naar de buitenste instantie zonder toegang tot de binnenste instantie en treedt er geen fout op.


Antwoord 2, autoriteit 90%

Het lijkt erop dat dergelijke code helemaal niet zou moeten compileren. Ik heb je code geminimaliseerd:

public class CompilerBug {
    int var = 0;
    public static void main(String[] args) {
        new CompilerBug().new Inner();
    }
    public class Inner {
        public Inner(Runnable r) {}
        public Inner() {
            this(() -> {
                var = 1;
            });
        }
    }
}

Het is zonder problemen gecompileerd door javac 1.8.0.25, 1.8.0.40 en 1.9b57. Elke gecompileerde versie produceert dezelfde uitvoer bij het starten:

Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
  Location:
    CompilerBug$Inner.<init>(LCompilerBug;)V @3: invokedynamic
  Reason:
    Type uninitializedThis (current frame, stack[2]) is not assignable to 'CompilerBug$Inner'
  Current Frame:
    bci: @3
    flags: { flagThisUninit }
    locals: { uninitializedThis, 'CompilerBug' }
    stack: { uninitializedThis, 'CompilerBug', uninitializedThis }
  Bytecode:
    0000000: 2a2b 2aba 0003 0000 b700 04b1
        at CompilerBug.main(CompilerBug.java:5)

Deze code is niet gecompileerd door de ECJ-compiler. Het rapporteert een compilatiefout:

----------
1. ERROR in C:\projects\Test\src\CompilerBug.java (at line 12)
    this(() -> {
         ^^^^^
Cannot refer to 'this' nor 'super' while explicitly invoking a constructor
----------
1 problem (1 error)

Het lijkt dus op een bug in de javac-compiler: het zou in plaats daarvan een compilatiefout moeten retourneren (zoals ECJ).

Ik heb geen vergelijkbare bug gevonden in OpenJDK bugtracker, dus heb een nieuw bugrapport ingediend via het webformulier. Als Java-mensen dit lezen, is de toegewezen interne beoordelings-ID JI-9021379.

Update:het bugrapport is geaccepteerd (JDK-8129740 )

Other episodes