KT-19251 Process uninitialized stores in mandatory bytecode pass

See
https://youtrack.jetbrains.com/issue/KT-19251
https://github.com/puniverse/quasar/issues/280
https://bugs.openjdk.java.net/browse/JDK-8046233

Inline function calls (as well as try/catch expressions) in constructor
arguments produce bytecode that spills stack, and stores uninitialized
objects (created by 'NEW C', but not initialized by 'C.<init>') to
local variables. Such bytecode is valid according to the JVM spec, but
confuses Quasar (and other bytecode postprocessing tools),
and fails to verify under some (buggy) versions of JDK 8.

In order to avoid that, we apply 'processUnitializedStores' already
implemented for coroutines. It moves 'NEW' instructions after the
constructor arguments evaluation, producing code like

<initialize class C using Class.forName>
<evaluate constructor arguments>
<store constructor arguments to variables>
NEW C
DUP
<load constructor arguments from variables>
INVOKESPECIAL C.<init>(...)

NB some other expressions, such as break/continue in the constructor
arguments, also can produce "weird" bytecode: object is created by a
'NEW C' instruction, but later (conditionally) POPped from stack and
left uninitialized. This, as we know, also can screw bytecode
postprocessing. However, it looks like we can get away with it ATM.
Otherwise it looks like we'd have to analyze constructor arguments, see
if the evaluation can "jump out", and perform argument linearization in
codegen.
This commit is contained in:
Dmitry Petrov
2017-07-28 16:20:10 +03:00
parent 3d8486e8a6
commit c0a83c3c8a
17 changed files with 494 additions and 108 deletions
@@ -0,0 +1,41 @@
// TARGET_BACKEND: JVM
// WITH_RUNTIME
// FILE: test.kt
fun box(): String {
class Local(val i: Int, val j: Int) : Foo() {
init {
log.append("Local.<init>;")
}
}
Local(
logged("i;", 1.let { it }),
logged("j;", 2.let { it })
)
val result = log.toString()
if (result != "Foo.<clinit>;i;j;Foo.<init>;Local.<init>;") return "Fail: '$result'"
return "OK"
}
// FILE: util.kt
val log = StringBuilder()
fun <T> logged(msg: String, value: T): T {
log.append(msg)
return value
}
// FILE: Foo.kt
open class Foo {
init {
log.append("Foo.<init>;")
}
companion object {
init {
log.append("Foo.<clinit>;")
}
}
}