Generate CHECKCAST after ACONST_NULL in coroutines
If we do not do this, the state-machine builder will not know the type of the ACONST_NULL, defaulting to Object, leading to VerifyError. Alternatively, we could use LVT to deduce the type, but getting types from LVT is something I got rid of long time ago, and I have no desire to return it back. Generating CHECKCAST hints the state-machine builder the type of the variable avoiding the issue of VerifyError. However, this CHECKCAST replaces StrictBasicValue.NULL_VALUE with BasicValue in OptimizationBasicInterpreter. To preserve optimization on not-spilling known nulls, introduce BasicValues, which represent typed nulls and create BasicInterpreter, which is aware of them. This way we have the best of two worlds - we do not spill known nulls, and we know the type of ACONST_NULL. #KT-51718 Fixed
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
// WITH_STDLIB
|
||||
|
||||
import kotlin.coroutines.*
|
||||
|
||||
fun builder(c: suspend () -> Unit) {
|
||||
c.startCoroutine(Continuation(EmptyCoroutineContext) {
|
||||
it.getOrThrow()
|
||||
})
|
||||
}
|
||||
|
||||
class Service {
|
||||
suspend fun getCanalConfig() {
|
||||
var start: Start? = null
|
||||
var configuration: String? = null
|
||||
val starts: Array<Start>? = null
|
||||
if (starts != null && starts.isNotEmpty()) {
|
||||
start = starts[0]
|
||||
configuration = call2()
|
||||
}
|
||||
//start = start as Start // kotlin 1.5.0 need for produce correct bytecode
|
||||
call(start!!)
|
||||
}
|
||||
|
||||
val canalConfig = suspend {
|
||||
var start: Start? = null
|
||||
var configuration: String? = null
|
||||
val starts: Array<Start>? = null
|
||||
if (starts != null && starts.isNotEmpty()) {
|
||||
start = starts[0]
|
||||
configuration = call2()
|
||||
}
|
||||
//start = start as Start // kotlin 1.5.0 need for produce correct bytecode
|
||||
call(start!!)
|
||||
}
|
||||
|
||||
fun call(start: Start) {
|
||||
|
||||
}
|
||||
|
||||
suspend fun call2(
|
||||
): String? {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
class Start()
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
try {
|
||||
Service().getCanalConfig()
|
||||
error("FAIL 1")
|
||||
} catch (ignored: NullPointerException) {}
|
||||
try {
|
||||
Service().canalConfig()
|
||||
error("FAIL 2")
|
||||
} catch (ignored: NullPointerException) {}
|
||||
}
|
||||
return "OK"
|
||||
}
|
||||
-5
@@ -4,7 +4,6 @@ public final class CrossinlineKt$box$1$filter$$inlined$source$1$1 {
|
||||
// source: 'crossinline.kt'
|
||||
enclosing method CrossinlineKt$box$1$filter$$inlined$source$1.consume(LSink;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
field L$0: java.lang.Object
|
||||
field L$1: java.lang.Object
|
||||
field label: int
|
||||
synthetic field result: java.lang.Object
|
||||
synthetic final field this$0: CrossinlineKt$box$1$filter$$inlined$source$1
|
||||
@@ -89,7 +88,6 @@ public final class CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1$1 {
|
||||
// source: 'crossinline.kt'
|
||||
enclosing method CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1.consume(LSink;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
field L$0: java.lang.Object
|
||||
field L$1: java.lang.Object
|
||||
field label: int
|
||||
synthetic field result: java.lang.Object
|
||||
synthetic final field this$0: CrossinlineKt$box$1$invokeSuspend$$inlined$filter$1
|
||||
@@ -181,7 +179,6 @@ public final class CrossinlineKt$filter$$inlined$source$1$1 {
|
||||
// source: 'crossinline.kt'
|
||||
enclosing method CrossinlineKt$filter$$inlined$source$1.consume(LSink;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
field L$0: java.lang.Object
|
||||
field L$1: java.lang.Object
|
||||
field label: int
|
||||
synthetic field result: java.lang.Object
|
||||
synthetic final field this$0: CrossinlineKt$filter$$inlined$source$1
|
||||
@@ -282,7 +279,6 @@ public final class CrossinlineKt$range$$inlined$source$1$1 {
|
||||
field I$1: int
|
||||
field L$0: java.lang.Object
|
||||
field L$1: java.lang.Object
|
||||
field L$2: java.lang.Object
|
||||
field label: int
|
||||
synthetic field result: java.lang.Object
|
||||
synthetic final field this$0: CrossinlineKt$range$$inlined$source$1
|
||||
@@ -310,7 +306,6 @@ public final class CrossinlineKt$source$1$consume$1 {
|
||||
// source: 'crossinline.kt'
|
||||
enclosing method CrossinlineKt$source$1.consume(LSink;Lkotlin/coroutines/Continuation;)Ljava/lang/Object;
|
||||
field L$0: java.lang.Object
|
||||
field L$1: java.lang.Object
|
||||
field label: int
|
||||
synthetic field result: java.lang.Object
|
||||
synthetic final field this$0: CrossinlineKt$source$1
|
||||
|
||||
@@ -14,10 +14,5 @@ suspend fun test() {
|
||||
|
||||
// 2 PUTFIELD .*L\$0 : Ljava/lang/Object;
|
||||
|
||||
// JVM_TEMPLATES:
|
||||
// just before suspension point
|
||||
// 1 ACONST_NULL
|
||||
|
||||
// JVM_IR_TEMPLATES:
|
||||
// two stores to initialize the `a` variable and one null constant to store in the spill slot.
|
||||
// 3 ACONST_NULL
|
||||
|
||||
Reference in New Issue
Block a user