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
@@ -4019,6 +4019,111 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
}
}
@TestMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)
public static class CapturingOuterClassInstanceInSuperCall extends AbstractBlackBoxCodegenTest {
public void testAllFilesPresentInCapturingOuterClassInstanceInSuperCall() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true);
}
@TestMetadata("kt13454.kt")
public void testKt13454() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/kt13454.kt");
doTest(fileName);
}
@TestMetadata("kt14148.kt")
public void testKt14148() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/kt14148.kt");
doTest(fileName);
}
@TestMetadata("kt4174.kt")
public void testKt4174() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/kt4174.kt");
doTest(fileName);
}
@TestMetadata("kt4174a.kt")
public void testKt4174a() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/kt4174a.kt");
doTest(fileName);
}
@TestMetadata("outerCapturedAsImplicitThisInBoundReference.kt")
public void testOuterCapturedAsImplicitThisInBoundReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/outerCapturedAsImplicitThisInBoundReference.kt");
doTest(fileName);
}
@TestMetadata("outerCapturedInFunctionLiteral.kt")
public void testOuterCapturedInFunctionLiteral() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/outerCapturedInFunctionLiteral.kt");
doTest(fileName);
}
@TestMetadata("outerCapturedInInlineLambda.kt")
public void testOuterCapturedInInlineLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/outerCapturedInInlineLambda.kt");
doTest(fileName);
}
@TestMetadata("outerCapturedInLambda.kt")
public void testOuterCapturedInLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/outerCapturedInLambda.kt");
doTest(fileName);
}
@TestMetadata("outerCapturedInLambda2.kt")
public void testOuterCapturedInLambda2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/outerCapturedInLambda2.kt");
doTest(fileName);
}
@TestMetadata("outerCapturedInLambdaInSecondaryConstructor.kt")
public void testOuterCapturedInLambdaInSecondaryConstructor() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/outerCapturedInLambdaInSecondaryConstructor.kt");
doTest(fileName);
}
@TestMetadata("outerCapturedInLocalClass.kt")
public void testOuterCapturedInLocalClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/outerCapturedInLocalClass.kt");
doTest(fileName);
}
@TestMetadata("outerCapturedInNestedLambda.kt")
public void testOuterCapturedInNestedLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/outerCapturedInNestedLambda.kt");
doTest(fileName);
}
@TestMetadata("outerCapturedInNestedObject.kt")
public void testOuterCapturedInNestedObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/outerCapturedInNestedObject.kt");
doTest(fileName);
}
@TestMetadata("outerCapturedInObject.kt")
public void testOuterCapturedInObject() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/outerCapturedInObject.kt");
doTest(fileName);
}
@TestMetadata("outerCapturedInObject2.kt")
public void testOuterCapturedInObject2() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/outerCapturedInObject2.kt");
doTest(fileName);
}
@TestMetadata("outerEnumEntryCapturedInLambdaInInnerClass.kt")
public void testOuterEnumEntryCapturedInLambdaInInnerClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/closures/capturingOuterClassInstanceInSuperCall/outerEnumEntryCapturedInLambdaInInnerClass.kt");
doTest(fileName);
}
}
@TestMetadata("compiler/testData/codegen/box/closures/closureInsideClosure")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)