Use captured instance in interface companion object initializer

Similar to enum entry initialization, when we have a companion object
in an interface, its constructor (or clinit) initializes its state
before the instance field in corresponding interface is initialized.
So, interface companion object must be accessed via a captured object
reference (#0, or #0.this$0 for inner anonymous objects).
This commit is contained in:
Dmitry Petrov
2017-09-13 15:04:03 +03:00
parent 179e720e4a
commit d20af1133a
7 changed files with 93 additions and 27 deletions
@@ -104,6 +104,7 @@ import java.util.*;
import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isInt; import static org.jetbrains.kotlin.builtins.KotlinBuiltIns.isInt;
import static org.jetbrains.kotlin.codegen.AsmUtil.*; import static org.jetbrains.kotlin.codegen.AsmUtil.*;
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.extractReificationArgument; import static org.jetbrains.kotlin.codegen.CodegenUtilKt.extractReificationArgument;
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isPossiblyUninitializedSingleton;
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.unwrapInitialSignatureDescriptor; import static org.jetbrains.kotlin.codegen.CodegenUtilKt.unwrapInitialSignatureDescriptor;
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*; import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*; import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
@@ -1737,16 +1738,14 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
if (descriptor instanceof ClassDescriptor) { if (descriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor; ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
if (isPossiblyUninitializedSingleton(classDescriptor) && isInsideSingleton(classDescriptor)) {
return generateThisOrOuterFromContext(classDescriptor, false, false);
}
if (isObject(classDescriptor)) { if (isObject(classDescriptor)) {
return StackValue.singleton(classDescriptor, typeMapper); return StackValue.singleton(classDescriptor, typeMapper);
} }
if (isEnumEntry(classDescriptor)) { if (isEnumEntry(classDescriptor)) {
if (isInsideEnumEntry(classDescriptor)) { return StackValue.enumEntry(classDescriptor, typeMapper);
return generateThisOrOuterFromContext(classDescriptor, false, false);
}
else {
return StackValue.enumEntry(classDescriptor, typeMapper);
}
} }
ClassDescriptor companionObjectDescriptor = classDescriptor.getCompanionObjectDescriptor(); ClassDescriptor companionObjectDescriptor = classDescriptor.getCompanionObjectDescriptor();
if (companionObjectDescriptor != null) { if (companionObjectDescriptor != null) {
@@ -2488,6 +2487,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
if (contextDescriptor instanceof FunctionDescriptor && receiverDescriptor == contextDescriptor.getContainingDeclaration()) { if (contextDescriptor instanceof FunctionDescriptor && receiverDescriptor == contextDescriptor.getContainingDeclaration()) {
return StackValue.LOCAL_0; return StackValue.LOCAL_0;
} }
else if (isPossiblyUninitializedSingleton(receiverDescriptor) && isInsideSingleton(receiverDescriptor)) {
return generateThisOrOuterFromContext(receiverDescriptor, false, false);
}
else { else {
return StackValue.singleton(receiverDescriptor, typeMapper); return StackValue.singleton(receiverDescriptor, typeMapper);
} }
@@ -2572,13 +2574,13 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return generateThisOrOuter(calleeContainingClass, isSuper, false); return generateThisOrOuter(calleeContainingClass, isSuper, false);
} }
private boolean isInsideEnumEntry(@NotNull ClassDescriptor enumEntryDescriptor) { private boolean isInsideSingleton(@NotNull ClassDescriptor singletonClassDescriptor) {
assert DescriptorUtils.isEnumEntry(enumEntryDescriptor) : assert singletonClassDescriptor.getKind().isSingleton() :
"Enum entry reference expected: " + enumEntryDescriptor; "Singleton expected: " + singletonClassDescriptor;
DeclarationDescriptor descriptor = context.getContextDescriptor(); DeclarationDescriptor descriptor = context.getContextDescriptor();
while (descriptor != null) { while (descriptor != null) {
if (descriptor == enumEntryDescriptor) return true; if (descriptor == singletonClassDescriptor) return true;
if (descriptor instanceof ClassDescriptor && if (descriptor instanceof ClassDescriptor &&
!(((ClassDescriptor) descriptor).isInner() || DescriptorUtils.isAnonymousObject(descriptor))) { !(((ClassDescriptor) descriptor).isInner() || DescriptorUtils.isAnonymousObject(descriptor))) {
@@ -2593,24 +2595,23 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@NotNull @NotNull
public StackValue generateThisOrOuter(@NotNull ClassDescriptor calleeContainingClass, boolean isSuper, boolean forceOuter) { public StackValue generateThisOrOuter(@NotNull ClassDescriptor calleeContainingClass, boolean isSuper, boolean forceOuter) {
boolean isSingleton = calleeContainingClass.getKind().isSingleton(); if (!calleeContainingClass.getKind().isSingleton()) {
if (isSingleton) { return generateThisOrOuterFromContext(calleeContainingClass, isSuper, forceOuter);
if (calleeContainingClass.equals(context.getThisDescriptor()) &&
!CodegenUtilKt.isJvmStaticInObjectOrClass(context.getFunctionDescriptor())) {
return StackValue.local(0, typeMapper.mapType(calleeContainingClass));
}
else if (isEnumEntry(calleeContainingClass)) {
if (!isInsideEnumEntry(calleeContainingClass)) {
return StackValue.enumEntry(calleeContainingClass, typeMapper);
}
// else fall-through
}
else {
return StackValue.singleton(calleeContainingClass, typeMapper);
}
} }
return generateThisOrOuterFromContext(calleeContainingClass, isSuper, forceOuter); if (calleeContainingClass.equals(context.getThisDescriptor()) &&
!CodegenUtilKt.isJvmStaticInObjectOrClass(context.getFunctionDescriptor())) {
return StackValue.local(0, typeMapper.mapType(calleeContainingClass));
}
else if (CodegenUtilKt.isPossiblyUninitializedSingleton(calleeContainingClass) && isInsideSingleton(calleeContainingClass)) {
return generateThisOrOuterFromContext(calleeContainingClass, isSuper, forceOuter);
}
else if (isEnumEntry(calleeContainingClass)) {
return StackValue.enumEntry(calleeContainingClass, typeMapper);
}
else {
return StackValue.singleton(calleeContainingClass, typeMapper);
}
} }
private StackValue generateThisOrOuterFromContext(@NotNull ClassDescriptor calleeContainingClass, boolean isSuper, boolean forceOuter) { private StackValue generateThisOrOuterFromContext(@NotNull ClassDescriptor calleeContainingClass, boolean isSuper, boolean forceOuter) {
@@ -420,3 +420,7 @@ fun ExpressionCodegen.generateCallReceiver(rangeCall: ResolvedCall<out CallableD
fun ExpressionCodegen.generateCallSingleArgument(rangeCall: ResolvedCall<out CallableDescriptor>): StackValue = fun ExpressionCodegen.generateCallSingleArgument(rangeCall: ResolvedCall<out CallableDescriptor>): StackValue =
gen(ExpressionCodegen.getSingleArgumentExpression(rangeCall)!!) gen(ExpressionCodegen.getSingleArgumentExpression(rangeCall)!!)
fun ClassDescriptor.isPossiblyUninitializedSingleton() =
DescriptorUtils.isEnumEntry(this) ||
DescriptorUtils.isCompanionObject(this) && DescriptorUtils.isInterface(this.containingDeclaration)
@@ -0,0 +1,37 @@
// WITH_RUNTIME
import kotlin.test.*
interface Test {
companion object {
val x = "OK"
val y1 = Test.x
val y2 = 42.let { x }
val y3: String
init {
fun localFun() = x
y3 = localFun()
}
fun method() = x
val y4 = method()
val anonObject = object {
override fun toString() = x
}
val y5 = anonObject.toString()
init {
assertEquals(x, y1)
assertEquals(x, y2)
assertEquals(x, y3)
assertEquals(x, y4)
assertEquals(x, y5)
}
}
}
fun box() = Test.x
@@ -11846,6 +11846,12 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes
doTest(fileName); doTest(fileName);
} }
@TestMetadata("interfaceCompanionObjectReference.kt")
public void testInterfaceCompanionObjectReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/interfaceCompanionObjectReference.kt");
doTest(fileName);
}
@TestMetadata("kt1047.kt") @TestMetadata("kt1047.kt")
public void testKt1047() throws Exception { public void testKt1047() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt");
@@ -11846,6 +11846,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("interfaceCompanionObjectReference.kt")
public void testInterfaceCompanionObjectReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/interfaceCompanionObjectReference.kt");
doTest(fileName);
}
@TestMetadata("kt1047.kt") @TestMetadata("kt1047.kt")
public void testKt1047() throws Exception { public void testKt1047() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt");
@@ -11846,6 +11846,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
doTest(fileName); doTest(fileName);
} }
@TestMetadata("interfaceCompanionObjectReference.kt")
public void testInterfaceCompanionObjectReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/interfaceCompanionObjectReference.kt");
doTest(fileName);
}
@TestMetadata("kt1047.kt") @TestMetadata("kt1047.kt")
public void testKt1047() throws Exception { public void testKt1047() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt");
@@ -13106,6 +13106,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("interfaceCompanionObjectReference.kt")
public void testInterfaceCompanionObjectReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/interfaceCompanionObjectReference.kt");
doTest(fileName);
}
@TestMetadata("kt1047.kt") @TestMetadata("kt1047.kt")
public void testKt1047() throws Exception { public void testKt1047() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt");