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:
Denis Zharkov
2016-08-09 11:53:43 +03:00
parent d8c3cefc7c
commit 1df9724c0c
24 changed files with 1284 additions and 5 deletions
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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