Kotlin 1.2.10协程代码编译通过运行抛VerifyError原因咨询
Kotlin 1.2.10中嵌套lambda引发VerifyError的原因分析
这是Kotlin 1.2.x版本特有的编译器bug导致的问题,咱们一步步拆解来看:
问题场景
你写的这段代码编译完全没问题,但运行时会直接抛出java.lang.VerifyError异常;可只要把Test类去掉,把函数移到顶层,代码就能正常执行。
重现代码
import kotlinx.coroutines.experimental.delay import kotlinx.coroutines.experimental.launch import kotlinx.coroutines.experimental.runBlocking import java.util.* import kotlin.concurrent.schedule class Test { fun scheduleTimeout() { Timer(true).schedule(300) { launch { runSuspended("hello") } } } suspend fun runSuspended(txt: String) = println(txt) } fun main(args: Array<String>) { Test().scheduleTimeout() runBlocking { delay(10000) } }
运行时异常
Exception in thread "Timer-0" java.lang.VerifyError: Bad type on operand stack Exception Details: Location: Test$scheduleTimeout$$inlined$schedule$1$lambda$1.create(Lkotlinx/coroutines/experimental/CoroutineScope;Lkotlin/coroutines/experimental/Continuation;)Lkotlin/coroutines/experimental/Continuation; @18: invokespecial Reason: Type 'Test$scheduleTimeout$$inlined$schedule$1$lambda$1' (current frame, stack[3]) is not assignable to 'Test$scheduleTimeout$$inlined$schedule$1' Current Frame: bci: @18 flags: { } locals: { 'Test$scheduleTimeout$$inlined$schedule$1$lambda$1', 'kotlinx/coroutines/experimental/CoroutineScope', 'kotlin/coroutines/experimental/Continuation' } stack: { uninitialized 12, uninitialized 12, 'kotlin/coroutines/experimental/Continuation', 'Test$scheduleTimeout$$inlined$schedule$1$lambda$1' } Bytecode: 0x0000000: 2b12 64b8 006a 2c12 6cb8 006a bb00 0259 0x0000010: 2c2a b700 6e4e 2d2b b500 3d2d 3a04 1904 0x0000020: b0 at Test$scheduleTimeout$$inlined$schedule$1.run(Timer.kt:145) at java.util.TimerThread.mainLoop(Timer.java:555) at java.util.TimerThread.run(Timer.java:505)
问题根源
这个问题出在Kotlin 1.2.x版本处理内联函数嵌套lambda的字节码生成逻辑上:
kotlin.concurrent.schedule是一个内联函数,当你在类的成员函数中调用它,并且在它的lambda参数内部再嵌套另一个lambda(也就是launch的参数)时,编译器生成的内部类结构出现了类型混淆。- 从异常信息可以看到,生成的内部类
Test$scheduleTimeout$$inlined$schedule$1$lambda$1在调用构造函数时,错误地把自身实例当成了Test$scheduleTimeout$$inlined$schedule$1类型传递——但这两个类并没有继承关系,JVM的字节码验证器发现这个类型不匹配,直接抛出了VerifyError。
至于为什么把函数移到顶层就正常?因为顶层函数不需要持有外部类的引用,编译器生成的内部类结构更简单,不会触发这个嵌套内联lambda的bug。
解决办法
有两个可行的方案:
- 升级Kotlin版本:这个bug在Kotlin 1.3及以后的稳定版本中已经被官方修复了,升级到1.3.0或更高版本就能彻底解决问题。
- 临时规避:如果暂时无法升级,可以把嵌套的lambda逻辑提取成单独的成员函数,避免在
schedule的lambda内部直接嵌套另一个lambda:
class Test { fun scheduleTimeout() { Timer(true).schedule(300) { launchSuspendedTask() } } private fun launchSuspendedTask() { launch { runSuspended("hello") } } suspend fun runSuspended(txt: String) = println(txt) }
内容的提问来源于stack exchange,提问作者andresp
相关产品推荐
相关产品推荐

