Fix determining enclosing class for closure

Enclosing class for closure is a class whose instance is captured by
closure as an outer 'this', and stored in a field 'this$0'.
Usually enclosing class for closure is an immediate outer class,
including classes for nested closures. For example:

  class C {
    fun foo() {}
    val example1 = L1@ { foo() }
    // Enclosing class for lambda 'L1' is 'C'
    val example2 = L2a@ { L2b@ { foo() } }
    // Enclosing class for nested lambda 'L2b'
    // is a closure class for outer lambda 'L2a'
  }

However, if the closure is created in a super type constructor call for
the outer class, corresponding instance is considered "uninitialized",
and can't be used as a proper class instance, and can't be referenced:
corresponding code is rejected by front-end.

  class Outer {
    fun foo() {}
    inner class Inner : Base(L3@ { foo() })
    // Enclosing class for lambda 'L3' is 'Outer',
    // because 'Inner' is uninitialized in super type constructor call.
  }

In CodegenAnnotatingVisitor, we maintain a stack of currently
uninitialized classes, and chose enclosing class for closure
as an inner-most surrounding class with initialized instance.

When generating code for this or outer class instance, we skip
contexts corresponding to classes with uninitialized instances.

This fixes a number of bytecode verification errors caused by incorrect
enclosing class for closure.

 #KT-4174 Fixed Target versions 1.2.20
 #KT-13454 Fixed Target versions 1.2.20
 #KT-14148 Fixed Target versions 1.2.20
This commit is contained in:
Dmitry Petrov
2017-10-30 11:15:02 +03:00
parent 6648657e65
commit bbb0389c51
24 changed files with 730 additions and 10 deletions
@@ -0,0 +1,9 @@
open class Foo(val x: () -> String)
class Outer {
val s = "OK"
inner class Inner : Foo({ s })
}
fun box() = Outer().Inner().x()
@@ -0,0 +1,17 @@
interface Test {
fun test(): String
}
open class Base(val test: Test)
open class Outer(val x: String) {
open inner class Inner
inner class JavacBug : Base(
object : Outer.Inner(), Test {
override fun test() = x
}
)
}
fun box() = Outer("OK").JavacBug().test.test()
@@ -0,0 +1,10 @@
open class C(val f: () -> String)
class B(val x: String) {
fun foo(): C {
class A : C({x}) {}
return A()
}
}
fun box() = B("OK").foo().f()
@@ -0,0 +1,20 @@
open class C(val s: String) {
fun test(): String {
return s
}
}
class B {
fun foo(): String {
var s = "OK"
class Z : C(s) {}
return Z().test()
}
}
fun box() : String {
val b = B()
val result = b.foo()
return result
}
@@ -0,0 +1,9 @@
abstract class Base(val fn: () -> String)
class Outer {
val ok = "OK"
inner class Inner : Base(::ok)
}
fun box() = Outer().Inner().fn()
@@ -0,0 +1,14 @@
open class Base(val callback: () -> String)
class Outer {
val ok = "OK"
inner class Inner : Base(
fun(): String {
return ok
}
)
}
fun box(): String =
Outer().Inner().callback()
@@ -0,0 +1,15 @@
open class Base(val callback: () -> String)
class Outer {
val ok = "OK"
inner class Inner : Base(
run {
val x = ok
{ x }
}
)
}
fun box(): String =
Outer().Inner().callback()
@@ -0,0 +1,10 @@
open class Base(val callback: () -> String)
class Outer {
val ok = "OK"
inner class Inner : Base({ ok })
}
fun box(): String =
Outer().Inner().callback()
@@ -0,0 +1,13 @@
open class Base(val callback: () -> String)
class Outer {
val ok = "OK"
inner class Inner1 {
inner class Inner2 : Base({ ok })
}
}
fun box(): String =
Outer().Inner1().Inner2().callback()
@@ -0,0 +1,15 @@
// IGNORE_BACKEND: JS
// ^ see KT-21041
open class Base(val callback: () -> String)
class Outer {
val ok = "OK"
inner class Inner : Base {
constructor() : super({ ok })
}
}
fun box(): String =
Outer().Inner().callback()
@@ -0,0 +1,13 @@
abstract class Base(val fn: () -> String)
class Outer {
val ok = "OK"
fun foo(): String {
class Local : Base({ ok })
return Local().fn()
}
}
fun box() = Outer().foo()
@@ -0,0 +1,10 @@
open class Base(val callback: () -> String)
class Outer {
val ok = "OK"
inner class Inner : Base({ { ok }() })
}
fun box(): String =
Outer().Inner().callback()
@@ -0,0 +1,21 @@
interface Callback {
fun invoke(): String
}
open class Base(val callback: Callback)
class Outer {
val ok = "OK"
inner class Inner : Base(
object : Callback {
override fun invoke() =
(object : Callback {
override fun invoke() = ok
}).invoke()
}
)
}
fun box(): String =
Outer().Inner().callback.invoke()
@@ -0,0 +1,18 @@
interface Callback {
fun invoke(): String
}
open class Base(val callback: Callback)
class Outer {
val ok = "OK"
inner class Inner : Base(
object : Callback {
override fun invoke() = ok
}
)
}
fun box(): String =
Outer().Inner().callback.invoke()
@@ -0,0 +1,20 @@
interface Callback {
fun invoke(): String
}
open class Base(val callback: Callback)
class Outer {
val ok = "OK"
inner class Inner1 {
inner class Inner2 : Base(
object : Callback {
override fun invoke() = ok
}
)
}
}
fun box(): String =
Outer().Inner1().Inner2().callback.invoke()
@@ -0,0 +1,14 @@
abstract class Base(val fn: () -> Test)
enum class Test(val ok: String) {
TEST("OK") {
inner class Inner : Base({ TEST })
override val base: Base
get() = Inner()
};
abstract val base: Base
}
fun box() = Test.TEST.base.fn().ok