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.codegen.AsmUtil.*;
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.JvmCodegenUtil.*;
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.*;
@@ -1737,16 +1738,14 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
if (descriptor instanceof ClassDescriptor) {
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
if (isPossiblyUninitializedSingleton(classDescriptor) && isInsideSingleton(classDescriptor)) {
return generateThisOrOuterFromContext(classDescriptor, false, false);
}
if (isObject(classDescriptor)) {
return StackValue.singleton(classDescriptor, typeMapper);
}
if (isEnumEntry(classDescriptor)) {
if (isInsideEnumEntry(classDescriptor)) {
return generateThisOrOuterFromContext(classDescriptor, false, false);
}
else {
return StackValue.enumEntry(classDescriptor, typeMapper);
}
return StackValue.enumEntry(classDescriptor, typeMapper);
}
ClassDescriptor companionObjectDescriptor = classDescriptor.getCompanionObjectDescriptor();
if (companionObjectDescriptor != null) {
@@ -2488,6 +2487,9 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
if (contextDescriptor instanceof FunctionDescriptor && receiverDescriptor == contextDescriptor.getContainingDeclaration()) {
return StackValue.LOCAL_0;
}
else if (isPossiblyUninitializedSingleton(receiverDescriptor) && isInsideSingleton(receiverDescriptor)) {
return generateThisOrOuterFromContext(receiverDescriptor, false, false);
}
else {
return StackValue.singleton(receiverDescriptor, typeMapper);
}
@@ -2572,13 +2574,13 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
return generateThisOrOuter(calleeContainingClass, isSuper, false);
}
private boolean isInsideEnumEntry(@NotNull ClassDescriptor enumEntryDescriptor) {
assert DescriptorUtils.isEnumEntry(enumEntryDescriptor) :
"Enum entry reference expected: " + enumEntryDescriptor;
private boolean isInsideSingleton(@NotNull ClassDescriptor singletonClassDescriptor) {
assert singletonClassDescriptor.getKind().isSingleton() :
"Singleton expected: " + singletonClassDescriptor;
DeclarationDescriptor descriptor = context.getContextDescriptor();
while (descriptor != null) {
if (descriptor == enumEntryDescriptor) return true;
if (descriptor == singletonClassDescriptor) return true;
if (descriptor instanceof ClassDescriptor &&
!(((ClassDescriptor) descriptor).isInner() || DescriptorUtils.isAnonymousObject(descriptor))) {
@@ -2593,24 +2595,23 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
@NotNull
public StackValue generateThisOrOuter(@NotNull ClassDescriptor calleeContainingClass, boolean isSuper, boolean forceOuter) {
boolean isSingleton = calleeContainingClass.getKind().isSingleton();
if (isSingleton) {
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);
}
if (!calleeContainingClass.getKind().isSingleton()) {
return generateThisOrOuterFromContext(calleeContainingClass, isSuper, forceOuter);
}
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) {
@@ -419,4 +419,8 @@ fun ExpressionCodegen.generateCallReceiver(rangeCall: ResolvedCall<out CallableD
generateReceiverValue(rangeCall.extensionReceiver ?: rangeCall.dispatchReceiver!!, false)
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);
}
@TestMetadata("interfaceCompanionObjectReference.kt")
public void testInterfaceCompanionObjectReference() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/interfaceCompanionObjectReference.kt");
doTest(fileName);
}
@TestMetadata("kt1047.kt")
public void testKt1047() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt");
@@ -11846,6 +11846,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
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")
public void testKt1047() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt");
@@ -11846,6 +11846,12 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes
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")
public void testKt1047() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt");
@@ -13106,6 +13106,12 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
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")
public void testKt1047() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt");