Jvm 非常神秘的运行时错误:堆栈上的操作数类型错误

Jvm 非常神秘的运行时错误:堆栈上的操作数类型错误,jvm,kotlin,Jvm,Kotlin,我发现了一个非常神秘的错误: java.lang.VerifyError:操作数堆栈上的类型错误 例外情况详情: 地点: org/bh/tools/base/strings/TestUtils.concat(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence@28:invokevirtual 原因: 类型“java/lang/Object”(当前帧,堆栈[1])不可分配给“java/lang/String” 当前帧

我发现了一个非常神秘的错误:

java.lang.VerifyError:操作数堆栈上的类型错误
例外情况详情:
地点:
org/bh/tools/base/strings/TestUtils.concat(Ljava/lang/Object;Ljava/lang/CharSequence;)Ljava/lang/CharSequence@28:invokevirtual
原因:
类型“java/lang/Object”(当前帧,堆栈[1])不可分配给“java/lang/String”
当前帧:
密件抄送:@28
标志:{}
局部变量:{'java/lang/Object','java/lang/CharSequence'}
堆栈:{'java/lang/StringBuilder','java/lang/Object'}
字节码:
0x0000000:2a12 59b8 0012 2b12 43b8 0012 2ac1 005b
0x0000010:9900 1a2a bb00 1659 b700 1a5f b600 512b
0x0000020:b600 5eb6 0052 c000 23b0 2ac1 0016 9900
0x0000030:152a c000 162b b600 5e59 1260 b800 63c0
0x0000040:0023 b0bb 0016 59b7 001a 2ab6 0047 2bb6
0x0000050:005e 5912 65b8 0063 c000 23b0
堆栈映射表:
同一帧(@42)
同一帧(@67)
在org.bh.tools.base.math.NumberConversionKtTestKt.assertNumbersClose(NumberConversionKtTest.kt:488)
位于org.bh.tools.base.math.NumberConversionKtTest.Number\u float32Value(NumberConversionKtTest.kt:47)
在sun.reflect.NativeMethodAccessorImpl.invoke0(本机方法)处
位于sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
在sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)中
位于java.lang.reflect.Method.invoke(Method.java:498)
位于org.junit.runners.model.FrameworkMethod$1.runReflectVeCall(FrameworkMethod.java:50)
位于org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
位于org.junit.runners.model.FrameworkMethod.invokeeexplosive(FrameworkMethod.java:47)
位于org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
位于org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
位于org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
位于org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
位于org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
位于org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
位于org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
访问org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
位于org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
位于org.junit.runners.ParentRunner.run(ParentRunner.java:363)
位于org.junit.runners.Suite.runChild(Suite.java:128)
位于org.junit.runners.Suite.runChild(Suite.java:27)
位于org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
位于org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
位于org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
访问org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
位于org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
位于org.junit.runners.ParentRunner.run(ParentRunner.java:363)
位于org.junit.runner.JUnitCore.run(JUnitCore.java:137)
位于com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
位于com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:58)
位于com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:237)
位于com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
在这一行:

我认为它涉及到这个功能:

但我不可能理解出了什么问题。这似乎表明我在调用
TestUtils
类中的
对象
CharSequence
上的串联方法(我认为在运行时不会触及该方法),但听起来非常接近此处未使用的方法

发生了一些我无法理解的事情。有人有什么想法吗?



伴随JetBrains的bug:因为我没有字节码要研究,下面所有的解释都建立在一个假设上:Kotlin再次犯了智能铸造错误

tl;dr JVM验证整个类,而不仅仅是使用的比例。 这种情况发生在以下步骤中:

  • 测试运行。Junit找到测试并尝试调用
    org.bh.tools.base.math.NumberConversionKtTest::Number\u float32Value
    反射。这触发了类
    NumberConversionKtTest
    的加载、链接和初始化。一切都很好
  • 此方法调用了
    org.bh.tools.base.math.NumberConversionKtTestKt.assertNumbersClose
    ,它位于尚未加载的类中(请注意前一个类没有的试用
    Kt
    )。JVM找到、加载、链接并初始化了它
  • assertNumbersClose
    现在正在执行,即创建一个新帧并将其推入堆栈。在执行一些字节码之后,程序流移动到第488行,并找到一条方法调用指令,该指令要求JVM执行类
    org.bh.tools.base.strings.TestUtils中的方法。这个类还没有加载,所以JVM开始加载一个新类
  • JVM加载该类,在加载过程中,发现方法
    concat
    ,而不是
    assertNumbersClose
    中使用的方法不好。它会停止验证,并出现
    验证错误
    。由于方法
    differingCharacters
    尚未执行,即未创建新框架并将其推入堆栈,因此它不在堆栈跟踪上,因此您可以看到顶部的
    org.bh.tools.base.math.NumberConversionKtTestKt.assertNumbersClose

  • 可能的解决办法 您需要以某种方式修复
    concat
    方法的字节码。你有下面这些问题

  • 尝试升级kotlin编译器。我记得kotlin 1.1中修复了一些与smartcast相关的问题
  • 因为这个错误是kotl
    fun concat(lhs: Any, rhs: CharSequence): CharSequence {
        if (lhs is String) {
            val s: String = lhs as String
            return s.plus(rhs)
        } else if (lhs is StringBuilder) {
            val sb: StringBuilder= lhs as StringBuilder
            return sb.append(rhs)
        } else {
            return StringBuilder().append(lhs).append(rhs)
        }
    }