【问题标题】:Reason for the exception java.lang.VerifyError: Bad type on operand stack异常 java.lang.VerifyError 的原因:操作数堆栈上的类型错误
【发布时间】:2015-05-21 05:16:15
【问题描述】:

下面的简单 java 代码发送 java.lang.VerifyError: 操作数堆栈上的错误类型 异常

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);
    }
}

如果我删除代码

parentData = 1

来自B的构造函数,异常不会来。

谁能说出原因?

【问题讨论】:

    标签: java lambda java-8


    【解决方案1】:

    似乎这样的代码根本不应该编译。我最小化了你的代码:

    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;
                });
            }
        }
    }
    

    javac 1.8.0.25、1.8.0.40 和 1.9b57 编译没有问题。每个编译版本在启动时都会产生相同的输出:

    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)
    

    此代码不是由 ECJ 编译器编译的。报编译错误:

    ----------
    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)
    

    所以它看起来像 javac 编译器中的一个错误:它应该返回一个编译错误(如 ECJ)。

    我在 OpenJDK 错误跟踪器中没有发现类似的错误,所以通过 webform 提交了一个新的错误报告。如果 Java 人员正在阅读本文,则分配的内部评论 ID 为 JI-9021379。

    更新:错误报告已被接受 (JDK-8129740)

    【讨论】:

    • @Yazad Khambata:“好得多”在哪方面?声明“此类代码根本不应该编译”是错误的,链接的错误报告承认 ECJ 拒绝代码的行为是错误的。从 Java 8 开始,更新 121,javac 将此代码编译为有效的类文件,该文件运行时不会出错。
    • 是的,但是我在 Java 8-121 stackoverflow.com/questions/61021950/…987654322@ 上的代码失败了
    【解决方案2】:

    问题出现是因为您的 lambda 表达式没有引用 thisthis 的成员,而是 outer this 的成员。你有没有写过B的类

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

    编译器毫无疑问地拒绝了它,因为访问 innerData 意味着访问 this

    关于外部实例的要点是它是一个常量,即使内部实例尚未完全构建时也可用。所以接受代码是正确的,但不幸的是,编译器生成的代码试图通过内部类实例的隐式字段访问外部实例,因此 lambda 表达式需要内部类的实例并尝试使用未完全构造的内部类实例产生错误。

    很容易证明代码可以正确编译:

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

    有了这个微小的变化,lambda 表达式引用了外部实例而不访问内部实例并且不会出现错误。

    【讨论】:

    • 你是对的,这样的代码也可以工作:public static class A { public A(int x) {} } public class B extends A { public B() { super(parentData = 1); } }。因此,在调用超类之前,甚至允许显式更改外部类。似乎ECJ编译器也有错误。
    猜你喜欢
    • 2022-10-06
    • 1970-01-01
    • 2017-12-25
    • 1970-01-01
    • 2021-11-10
    • 1970-01-01
    • 2023-02-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多