From 18f3eb87e45b565cf193afe1bae81e547cf31484 Mon Sep 17 00:00:00 2001 From: Michael Bogdanov Date: Fri, 23 Oct 2015 12:27:41 +0300 Subject: [PATCH] INSTANCE field deprecated in companion object Fix for KT-9692: Deadlock between of a class (KtSimpleNameExpressionImpl) and of its companion object #KT-9692 Fixed --- .../jetbrains/kotlin/codegen/FieldInfo.java | 30 ++++++++++++++----- .../codegen/ImplementationBodyCodegen.java | 30 ++++++++++--------- .../jetbrains/kotlin/codegen/StackValue.java | 4 +-- .../ClassObjectField.java | 3 ++ .../TraitClassObjectField.java | 3 ++ .../publicField/CompanionObject.java | 3 ++ .../CompanionInitialization.java | 7 +++++ .../classCompanion/CompanionInitialization.kt | 20 +++++++++++++ .../CompanionInitialization.java | 7 +++++ .../CompanionInitialization.kt | 22 ++++++++++++++ .../bytecodeText/constValsGetterDeprecated.kt | 4 +-- .../BlackBoxWithJavaCodegenTestGenerated.java | 12 ++++++++ .../debugger/tinyApp/outs/fwBackingField.out | 1 - 13 files changed, 120 insertions(+), 26 deletions(-) create mode 100644 compiler/testData/codegen/boxWithJava/classCompanion/CompanionInitialization.java create mode 100644 compiler/testData/codegen/boxWithJava/classCompanion/CompanionInitialization.kt create mode 100644 compiler/testData/codegen/boxWithJava/interfaceCompanion/CompanionInitialization.java create mode 100644 compiler/testData/codegen/boxWithJava/interfaceCompanion/CompanionInitialization.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/FieldInfo.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/FieldInfo.java index 3294922b0cf..3eec2bd48d2 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/FieldInfo.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/FieldInfo.java @@ -17,21 +17,19 @@ package org.jetbrains.kotlin.codegen; import org.jetbrains.annotations.NotNull; +import org.jetbrains.kotlin.builtins.CompanionObjectMapping; import org.jetbrains.kotlin.codegen.state.JetTypeMapper; import org.jetbrains.kotlin.descriptors.ClassDescriptor; import org.jetbrains.kotlin.load.java.JvmAbi; import org.jetbrains.kotlin.resolve.DescriptorUtils; +import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform; import org.jetbrains.org.objectweb.asm.Type; +import static org.jetbrains.kotlin.resolve.DescriptorUtils.isNonCompanionObject; + public class FieldInfo { - @NotNull - public static FieldInfo createForCompanionSingleton(@NotNull ClassDescriptor companionObject, @NotNull JetTypeMapper typeMapper) { - ClassDescriptor ownerDescriptor = DescriptorUtils.getParentOfType(companionObject, ClassDescriptor.class); - assert ownerDescriptor != null : "Owner not found for class: " + companionObject; - Type ownerType = typeMapper.mapType(ownerDescriptor); - return new FieldInfo(ownerType, typeMapper.mapType(companionObject), companionObject.getName().asString(), true); - } + private static final CompanionObjectMapping COMPANION_OBJECT_MAPPING = new CompanionObjectMapping(JvmPlatform.INSTANCE.getBuiltIns()); @NotNull public static FieldInfo createForSingleton(@NotNull ClassDescriptor classDescriptor, @NotNull JetTypeMapper typeMapper) { @@ -43,6 +41,24 @@ public class FieldInfo { if (!classDescriptor.getKind().isSingleton()) { throw new UnsupportedOperationException("Can't create singleton field for class: " + classDescriptor); } + + if (isNonCompanionObject(classDescriptor) || COMPANION_OBJECT_MAPPING.hasMappingToObject(classDescriptor)) { + return createSingletonViaInstance(classDescriptor, typeMapper, oldSingleton); + } + else { + ClassDescriptor ownerDescriptor = DescriptorUtils.getParentOfType(classDescriptor, ClassDescriptor.class); + assert ownerDescriptor != null : "Owner not found for class: " + classDescriptor; + Type ownerType = typeMapper.mapType(ownerDescriptor); + return new FieldInfo(ownerType, typeMapper.mapType(classDescriptor), classDescriptor.getName().asString(), true); + } + } + + @NotNull + public static FieldInfo createSingletonViaInstance( + @NotNull ClassDescriptor classDescriptor, + @NotNull JetTypeMapper typeMapper, + boolean oldSingleton + ) { Type type = typeMapper.mapType(classDescriptor); return new FieldInfo(type, type, oldSingleton ? JvmAbi.DEPRECATED_INSTANCE_FIELD : JvmAbi.INSTANCE_FIELD, true); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index d814f128a0e..12ec11a6d95 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -835,9 +835,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { private void generateFieldForSingleton() { if (isEnumEntry(descriptor)) return; - if (isObject(descriptor)) { - StackValue.Field field = StackValue.singleton(descriptor, typeMapper); - v.newField(JvmDeclarationOriginKt.OtherOrigin(myClass), ACC_PUBLIC | ACC_STATIC | ACC_FINAL, field.name, field.type.getDescriptor(), null, null); + boolean isCompanionObject = isCompanionObject(descriptor); + if (isNonCompanionObject(descriptor) || isCompanionObject) { + StackValue.Field field = StackValue.singletonViaInstance(descriptor, typeMapper); + v.newField(JvmDeclarationOriginKt.OtherOrigin(myClass), + ACC_PUBLIC | ACC_STATIC | ACC_FINAL | (isCompanionObject ? ACC_DEPRECATED : 0), + field.name, field.type.getDescriptor(), null, null); if (isNonCompanionObject(descriptor)) { StackValue.Field oldField = StackValue.oldSingleton(descriptor, typeMapper); @@ -845,18 +848,12 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { } if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return; - // Invoke the object constructor but ignore the result because INSTANCE$ will be initialized in the first line of InstructionAdapter v = createOrGetClInitCodegen().v; markLineNumberForSyntheticFunction(element, v); v.anew(classAsmType); v.invokespecial(classAsmType.getInternalName(), "", "()V", false); - if (isCompanionObjectWithBackingFieldsInOuter(descriptor)) { - //We should load containing class to initialize companion fields - StackValue companion = StackValue.singletonForCompanion(descriptor, typeMapper); - companion.put(companion.type, v); - AsmUtil.pop(v, companion.type); - } + return; } @@ -868,7 +865,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { KtObjectDeclaration companionObject = CollectionsKt.firstOrNull(((KtClass) myClass).getCompanionObjects()); assert companionObject != null : "Companion object not found: " + myClass.getText(); - StackValue.Field field = StackValue.singletonForCompanion(companionObjectDescriptor, typeMapper); + StackValue.Field field = StackValue.singleton(companionObjectDescriptor, typeMapper); v.newField(JvmDeclarationOriginKt.OtherOrigin(companionObject), ACC_PUBLIC | ACC_STATIC | ACC_FINAL, field.name, field.type.getDescriptor(), null, null); if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return; @@ -927,8 +924,13 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { private void generateCompanionObjectInitializer(@NotNull ClassDescriptor companionObject) { ExpressionCodegen codegen = createOrGetClInitCodegen(); - StackValue.singletonForCompanion(companionObject, typeMapper) - .store(StackValue.singleton(companionObject, typeMapper), codegen.v, true); + //TODO: uncomment when DEPRECATED INSTANCE is removed + //FunctionDescriptor constructor = (FunctionDescriptor) context.accessibleDescriptor( + // CollectionsKt.single(companionObject.getConstructors()), /* superCallExpression = */ null + //); + //generateMethodCallTo(constructor, null, codegen.v); + //StackValue instance = StackValue.onStack(typeMapper.mapClass(companionObject)); + StackValue.singleton(companionObject, typeMapper).store(StackValue.singletonViaInstance(companionObject, typeMapper), codegen.v, true); } private void generatePrimaryConstructor(final DelegationFieldsInfo delegationFieldsInfo) { @@ -998,7 +1000,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { getDelegationConstructorCall(bindingContext, constructorDescriptor)); if (isObject(descriptor)) { - StackValue.singleton(descriptor, typeMapper).store(StackValue.LOCAL_0, iv); + StackValue.singletonViaInstance(descriptor, typeMapper).store(StackValue.LOCAL_0, iv); if (isNonCompanionObject(descriptor)) { StackValue.oldSingleton(descriptor, typeMapper).store(StackValue.LOCAL_0, iv); } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java index 04da4b83393..a90023fafa5 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java @@ -578,8 +578,8 @@ public abstract class StackValue { return field(FieldInfo.createForSingleton(classDescriptor, typeMapper)); } - public static Field singletonForCompanion(ClassDescriptor companionObject, JetTypeMapper typeMapper) { - return field(FieldInfo.createForCompanionSingleton(companionObject, typeMapper)); + public static Field singletonViaInstance(ClassDescriptor classDescriptor, JetTypeMapper typeMapper) { + return field(FieldInfo.createSingletonViaInstance(classDescriptor, typeMapper, false)); } public static Field oldSingleton(ClassDescriptor classDescriptor, JetTypeMapper typeMapper) { diff --git a/compiler/testData/asJava/lightClasses/nullabilityAnnotations/ClassObjectField.java b/compiler/testData/asJava/lightClasses/nullabilityAnnotations/ClassObjectField.java index c7d173c0486..a697a4cfa68 100644 --- a/compiler/testData/asJava/lightClasses/nullabilityAnnotations/ClassObjectField.java +++ b/compiler/testData/asJava/lightClasses/nullabilityAnnotations/ClassObjectField.java @@ -10,6 +10,9 @@ public final class ClassObjectField { public ClassObjectField() { /* compiled code */ } public static final class Companion { + /** + * @deprecated + */ public static final ClassObjectField.Companion INSTANCE; @org.jetbrains.annotations.Nullable diff --git a/compiler/testData/asJava/lightClasses/nullabilityAnnotations/TraitClassObjectField.java b/compiler/testData/asJava/lightClasses/nullabilityAnnotations/TraitClassObjectField.java index c5a20016a51..45196f6920f 100644 --- a/compiler/testData/asJava/lightClasses/nullabilityAnnotations/TraitClassObjectField.java +++ b/compiler/testData/asJava/lightClasses/nullabilityAnnotations/TraitClassObjectField.java @@ -7,6 +7,9 @@ public interface TraitClassObjectField { @org.jetbrains.annotations.Nullable public static final java.lang.String x = ""; private static final java.lang.String y = ""; + /** + * @deprecated + */ public static final TraitClassObjectField.Companion INSTANCE; /** diff --git a/compiler/testData/asJava/lightClasses/publicField/CompanionObject.java b/compiler/testData/asJava/lightClasses/publicField/CompanionObject.java index 7417ae71d71..dfa1fb1841b 100644 --- a/compiler/testData/asJava/lightClasses/publicField/CompanionObject.java +++ b/compiler/testData/asJava/lightClasses/publicField/CompanionObject.java @@ -7,6 +7,9 @@ public final class C { public C() { /* compiled code */ } public static final class Companion { + /** + * @deprecated + */ public static final C.Companion INSTANCE; private Companion() { /* compiled code */ } diff --git a/compiler/testData/codegen/boxWithJava/classCompanion/CompanionInitialization.java b/compiler/testData/codegen/boxWithJava/classCompanion/CompanionInitialization.java new file mode 100644 index 00000000000..ceb302b9b0e --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/classCompanion/CompanionInitialization.java @@ -0,0 +1,7 @@ +public class CompanionInitialization { + + public static Object getCompanion() { + return ConcreteWithStatic.Companion; + } + +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithJava/classCompanion/CompanionInitialization.kt b/compiler/testData/codegen/boxWithJava/classCompanion/CompanionInitialization.kt new file mode 100644 index 00000000000..6dea847c6c1 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/classCompanion/CompanionInitialization.kt @@ -0,0 +1,20 @@ +interface IStatic + +open class Static(x: IStatic) { + fun doSth() { + } +} + +class ConcreteWithStatic : IStatic { + companion object : Static(ConcreteWithStatic()) +} + +fun box(): String { + ConcreteWithStatic.doSth() + + val companion: Any? = CompanionInitialization.getCompanion() + if (companion == null) return "fail 1" + if (companion != ConcreteWithStatic) return "fail 2" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithJava/interfaceCompanion/CompanionInitialization.java b/compiler/testData/codegen/boxWithJava/interfaceCompanion/CompanionInitialization.java new file mode 100644 index 00000000000..273e04a0f33 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/interfaceCompanion/CompanionInitialization.java @@ -0,0 +1,7 @@ +public class CompanionInitialization { + + public static Object getCompanion() { + return IStatic.Companion; + } + +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxWithJava/interfaceCompanion/CompanionInitialization.kt b/compiler/testData/codegen/boxWithJava/interfaceCompanion/CompanionInitialization.kt new file mode 100644 index 00000000000..b648ac53472 --- /dev/null +++ b/compiler/testData/codegen/boxWithJava/interfaceCompanion/CompanionInitialization.kt @@ -0,0 +1,22 @@ +open class Static(): IStatic { + val p = IStatic::class.java.getDeclaredField("const").get(null) +} + +interface IStatic { + fun doSth() { + } + + companion object : Static() { + const val const = 1; + } +} + +fun box(): String { + IStatic.doSth() + + val companion: Any? = CompanionInitialization.getCompanion() + if (companion == null) return "fail 1" + if (companion != IStatic) return "fail 2" + + return "OK" +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/constValsGetterDeprecated.kt b/compiler/testData/codegen/bytecodeText/constValsGetterDeprecated.kt index 48938640429..3ba36142acd 100644 --- a/compiler/testData/codegen/bytecodeText/constValsGetterDeprecated.kt +++ b/compiler/testData/codegen/bytecodeText/constValsGetterDeprecated.kt @@ -11,8 +11,8 @@ class B { } /* - One DEPRECATED is for INSTANCE$ temporarily + Two DEPRECATED are for INSTANCE$ and INSTANCE temporarily 3 others are for getCONST_VAL */ -// 4 DEPRECATED \ No newline at end of file +// 5 DEPRECATED \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java index 2b708c093f6..0302275fddc 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithJavaCodegenTestGenerated.java @@ -47,6 +47,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege doTestWithJava(fileName); } + @TestMetadata("classCompanion") + public void testClassCompanion() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/classCompanion/"); + doTestWithJava(fileName); + } + @TestMetadata("classObjectAccessor") public void testClassObjectAccessor() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/classObjectAccessor/"); @@ -65,6 +71,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege doTestWithJava(fileName); } + @TestMetadata("interfaceCompanion") + public void testInterfaceCompanion() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/interfaceCompanion/"); + doTestWithJava(fileName); + } + @TestMetadata("interfaceDefaultImpls") public void testInterfaceDefaultImpls() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/interfaceDefaultImpls/"); diff --git a/idea/testData/debugger/tinyApp/outs/fwBackingField.out b/idea/testData/debugger/tinyApp/outs/fwBackingField.out index 0fd06099923..d2fb29efdf4 100644 --- a/idea/testData/debugger/tinyApp/outs/fwBackingField.out +++ b/idea/testData/debugger/tinyApp/outs/fwBackingField.out @@ -31,7 +31,6 @@ fwBackingField.kt:38 fwBackingField.kt:62 fwBackingField.kt:45 fwBackingField.kt:48 -fwBackingField.kt:43 fwBackingField.kt:62 fwBackingField.kt:45 fwBackingField.kt:51