Refine stack frames markup calculation
It fixes VerifyError with coroutines on Dalvik happening because of variables spilling before/after suspension point BasicInterpreter from ASM does not distinct 'int' types from other int-like types like 'byte' or 'boolean', neither do HotSpot and JVM spec. But it seems like Dalvik does not follow it, and spilling boolean value into an 'int' field fails with VerifyError on Android 4, so it's necessary to distinct int types for variables spilling #KT-13289 Fixed
This commit is contained in:
+32
@@ -0,0 +1,32 @@
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
}
|
||||
|
||||
fun foo() = true
|
||||
|
||||
private var booleanResult = false
|
||||
fun setBooleanRes(x: Boolean) {
|
||||
booleanResult = x
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
val x = true
|
||||
val y = false
|
||||
suspendHere()
|
||||
setBooleanRes(if (foo()) x else y)
|
||||
}
|
||||
|
||||
if (!booleanResult) return "fail 1"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// 1 PUTFIELD .*\.Z\$0 : Z
|
||||
// 1 PUTFIELD .*\.Z\$1 : Z
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
}
|
||||
|
||||
private var byteResult: Byte = 0
|
||||
fun setByteRes(x: Byte) {
|
||||
byteResult = x
|
||||
}
|
||||
|
||||
fun foo(): Int = 1
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
val x: Byte = foo().toByte()
|
||||
suspendHere()
|
||||
setByteRes(x)
|
||||
}
|
||||
|
||||
if (byteResult != 1.toByte()) return "fail 1"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// 1 PUTFIELD .*\.B\$0 : B
|
||||
Vendored
+29
@@ -0,0 +1,29 @@
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
}
|
||||
|
||||
private var booleanResult = false
|
||||
fun setBooleanRes(x: Boolean) {
|
||||
booleanResult = x
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
val a = booleanArrayOf(true)
|
||||
val x = a[0]
|
||||
suspendHere()
|
||||
setBooleanRes(x)
|
||||
}
|
||||
|
||||
if (!booleanResult) return "fail 1"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// 1 PUTFIELD .*\.Z\$0 : Z
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
}
|
||||
|
||||
private var byteResult: Byte = 0
|
||||
fun setByteRes(x: Byte) {
|
||||
byteResult = x
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
val a = byteArrayOf(1)
|
||||
val x = a[0]
|
||||
suspendHere()
|
||||
setByteRes(x)
|
||||
}
|
||||
|
||||
if (byteResult != 1.toByte()) return "fail 1"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// 1 PUTFIELD .*\.B\$0 : B
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
}
|
||||
|
||||
private var booleanResult = false
|
||||
fun setBooleanRes(x: Boolean, ignored: Unit) {
|
||||
booleanResult = x
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
// 'true' value is spilled into variable and saved to field before suspension point
|
||||
// It's important that there is no type info about this variable in local var table,
|
||||
// so we should infer that ICONST_1 is a boolean value from it's usage
|
||||
setBooleanRes(true, suspendHere())
|
||||
}
|
||||
|
||||
if (!booleanResult) return "fail 1"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// 1 PUTFIELD .*\.Z\$0 : Z
|
||||
Vendored
+33
@@ -0,0 +1,33 @@
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
}
|
||||
|
||||
private var result: String = ""
|
||||
fun setRes(x: Byte, y: Int) {
|
||||
result = "$x#$y"
|
||||
}
|
||||
|
||||
fun foo(): Int = 1
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
val x: Byte = 1
|
||||
// No actual cast happens here
|
||||
val y: Int = x.toInt()
|
||||
suspendHere()
|
||||
setRes(x, y)
|
||||
}
|
||||
|
||||
if (result != "1#1") return "fail 1"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// 1 PUTFIELD .*\.B\$0 : B
|
||||
// 1 PUTFIELD .*\.I\$0 : I
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// WITH_RUNTIME
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
}
|
||||
|
||||
@JvmField
|
||||
var booleanResult = booleanArrayOf()
|
||||
@JvmField
|
||||
var charResult = charArrayOf()
|
||||
@JvmField
|
||||
var byteResult = byteArrayOf()
|
||||
@JvmField
|
||||
var shortResult = shortArrayOf()
|
||||
@JvmField
|
||||
var intResult = intArrayOf()
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
val x = true
|
||||
suspendHere()
|
||||
val a = BooleanArray(1)
|
||||
a[0] = x
|
||||
booleanResult = a
|
||||
}
|
||||
|
||||
if (!booleanResult[0]) return "fail 1"
|
||||
|
||||
builder {
|
||||
val x = '1'
|
||||
suspendHere()
|
||||
val a = CharArray(1)
|
||||
a[0] = x
|
||||
charResult = a
|
||||
}
|
||||
|
||||
if (charResult[0] != '1') return "fail 2"
|
||||
|
||||
builder {
|
||||
val x: Byte = 1
|
||||
suspendHere()
|
||||
val a = ByteArray(1)
|
||||
a[0] = x
|
||||
byteResult = a
|
||||
}
|
||||
|
||||
if (byteResult[0] != 1.toByte()) return "fail 3"
|
||||
|
||||
builder {
|
||||
val x: Short = 1
|
||||
suspendHere()
|
||||
val a = ShortArray(1)
|
||||
a[0] = x
|
||||
shortResult = a
|
||||
}
|
||||
|
||||
if (shortResult[0] != 1.toShort()) return "fail 4"
|
||||
|
||||
builder {
|
||||
val x: Int = 1
|
||||
suspendHere()
|
||||
val a = IntArray(1)
|
||||
a[0] = x
|
||||
intResult = a
|
||||
}
|
||||
|
||||
if (intResult[0] != 1) return "fail 5"
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// 1 PUTFIELD .*\.B\$0 : B
|
||||
// 1 PUTFIELD .*\.C\$0 : C
|
||||
// 1 PUTFIELD .*\.S\$0 : S
|
||||
// 1 PUTFIELD .*\.Z\$0 : Z
|
||||
// 1 PUTFIELD .*\.I\$0 : I
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
}
|
||||
|
||||
private var booleanResult = false
|
||||
fun setBooleanRes(x: Boolean) {
|
||||
booleanResult = x
|
||||
}
|
||||
|
||||
private var charResult: Char = '0'
|
||||
fun setCharRes(x: Char) {
|
||||
charResult = x
|
||||
}
|
||||
|
||||
private var byteResult: Byte = 0
|
||||
fun setByteRes(x: Byte) {
|
||||
byteResult = x
|
||||
}
|
||||
|
||||
private var shortResult: Short = 0
|
||||
fun setShortRes(x: Short) {
|
||||
shortResult = x
|
||||
}
|
||||
|
||||
private var intResult: Int = 0
|
||||
fun setIntRes(x: Int) {
|
||||
intResult = x
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
val x = true
|
||||
suspendHere()
|
||||
setBooleanRes(x)
|
||||
}
|
||||
|
||||
if (!booleanResult) return "fail 1"
|
||||
|
||||
builder {
|
||||
val x = '1'
|
||||
suspendHere()
|
||||
setCharRes(x)
|
||||
}
|
||||
|
||||
if (charResult != '1') return "fail 2"
|
||||
|
||||
builder {
|
||||
val x: Byte = 1
|
||||
suspendHere()
|
||||
setByteRes(x)
|
||||
}
|
||||
|
||||
if (byteResult != 1.toByte()) return "fail 3"
|
||||
|
||||
builder {
|
||||
val x: Short = 1
|
||||
suspendHere()
|
||||
setShortRes(x)
|
||||
}
|
||||
|
||||
if (shortResult != 1.toShort()) return "fail 4"
|
||||
|
||||
builder {
|
||||
val x: Int = 1
|
||||
suspendHere()
|
||||
setIntRes(x)
|
||||
}
|
||||
|
||||
if (intResult != 1) return "fail 5"
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// 1 PUTFIELD .*\.B\$0 : B
|
||||
// 1 PUTFIELD .*\.C\$0 : C
|
||||
// 1 PUTFIELD .*\.S\$0 : S
|
||||
// 1 PUTFIELD .*\.Z\$0 : Z
|
||||
// 1 PUTFIELD .*\.I\$0 : I
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// WITH_RUNTIME
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
}
|
||||
|
||||
@JvmField
|
||||
var booleanResult = false
|
||||
@JvmField
|
||||
var charResult: Char = '0'
|
||||
@JvmField
|
||||
var byteResult: Byte = 0
|
||||
@JvmField
|
||||
var shortResult: Short = 0
|
||||
@JvmField
|
||||
var intResult: Int = 0
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
val x = true
|
||||
suspendHere()
|
||||
booleanResult = x
|
||||
}
|
||||
|
||||
if (!booleanResult) return "fail 1"
|
||||
|
||||
builder {
|
||||
val x = '1'
|
||||
suspendHere()
|
||||
charResult = x
|
||||
}
|
||||
|
||||
if (charResult != '1') return "fail 2"
|
||||
|
||||
builder {
|
||||
val x: Byte = 1
|
||||
suspendHere()
|
||||
byteResult = x
|
||||
}
|
||||
|
||||
if (byteResult != 1.toByte()) return "fail 3"
|
||||
|
||||
builder {
|
||||
val x: Short = 1
|
||||
suspendHere()
|
||||
shortResult = x
|
||||
}
|
||||
|
||||
if (shortResult != 1.toShort()) return "fail 4"
|
||||
|
||||
builder {
|
||||
val x: Int = 1
|
||||
suspendHere()
|
||||
intResult = x
|
||||
}
|
||||
|
||||
if (intResult != 1) return "fail 5"
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// 1 PUTFIELD .*\.B\$0 : B
|
||||
// 1 PUTFIELD .*\.C\$0 : C
|
||||
// 1 PUTFIELD .*\.S\$0 : S
|
||||
// 1 PUTFIELD .*\.Z\$0 : Z
|
||||
// 1 PUTFIELD .*\.I\$0 : I
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
class Controller {
|
||||
suspend fun suspendHere(x: Continuation<Unit>) {
|
||||
x.resume(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun builder(coroutine c: Controller.() -> Continuation<Unit>) {
|
||||
c(Controller()).resume(Unit)
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
builder {
|
||||
val x = true
|
||||
suspendHere()
|
||||
val y: Boolean = x
|
||||
if (!y) throw IllegalStateException("fail 1")
|
||||
}
|
||||
|
||||
builder {
|
||||
val x = '1'
|
||||
suspendHere()
|
||||
|
||||
val y: Char = x
|
||||
if (y != '1') throw IllegalStateException("fail 2")
|
||||
}
|
||||
|
||||
builder {
|
||||
val x: Byte = 1
|
||||
suspendHere()
|
||||
|
||||
val y: Byte = x
|
||||
if (y != 1.toByte()) throw IllegalStateException("fail 3")
|
||||
}
|
||||
|
||||
builder {
|
||||
val x: Short = 1
|
||||
|
||||
suspendHere()
|
||||
|
||||
val y: Short = x
|
||||
if (y != 1.toShort()) throw IllegalStateException("fail 4")
|
||||
}
|
||||
|
||||
builder {
|
||||
val x: Int = 1
|
||||
suspendHere()
|
||||
|
||||
val y: Int = x
|
||||
if (y != 1) throw IllegalStateException("fail 5")
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
// 1 PUTFIELD .*\.B\$0 : B
|
||||
// 1 PUTFIELD .*\.C\$0 : C
|
||||
// 1 PUTFIELD .*\.S\$0 : S
|
||||
// 1 PUTFIELD .*\.Z\$0 : Z
|
||||
// 1 PUTFIELD .*\.I\$0 : I
|
||||
Reference in New Issue
Block a user