diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java index 4d2a90cf481..fa8ddf1b0df 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/AsmUtil.java @@ -641,7 +641,7 @@ public class AsmUtil { } public static boolean isPropertyWithBackingFieldInOuterClass(@NotNull PropertyDescriptor propertyDescriptor) { - return isPropertyWithSpecialBackingField(propertyDescriptor.getContainingDeclaration(), ClassKind.CLASS); + return isClassObjectWithBackingFieldsInOuter(propertyDescriptor.getContainingDeclaration()); } public static int getVisibilityForSpecialPropertyBackingField(@NotNull PropertyDescriptor propertyDescriptor, boolean isDelegate) { @@ -668,13 +668,13 @@ public class AsmUtil { public static boolean isPropertyWithBackingFieldCopyInOuterClass(@NotNull PropertyDescriptor propertyDescriptor) { boolean isExtensionProperty = propertyDescriptor.getReceiverParameter() != null; return !propertyDescriptor.isVar() && !isExtensionProperty - && isPropertyWithSpecialBackingField(propertyDescriptor.getContainingDeclaration(), ClassKind.TRAIT) + && isClassObjectOfClassWithKind(propertyDescriptor.getContainingDeclaration(), ClassKind.TRAIT) && areBothAccessorDefault(propertyDescriptor) && getVisibilityForSpecialPropertyBackingField(propertyDescriptor, false) == ACC_PUBLIC; } public static boolean isClassObjectWithBackingFieldsInOuter(@NotNull DeclarationDescriptor classObject) { - return isPropertyWithSpecialBackingField(classObject, ClassKind.CLASS); + return isClassObjectOfClassWithKind(classObject, ClassKind.CLASS); } private static boolean areBothAccessorDefault(@NotNull PropertyDescriptor propertyDescriptor) { @@ -686,7 +686,7 @@ public class AsmUtil { return accessorDescriptor == null || !accessorDescriptor.hasBody(); } - private static boolean isPropertyWithSpecialBackingField(@NotNull DeclarationDescriptor classObject, ClassKind kind) { + private static boolean isClassObjectOfClassWithKind(@NotNull DeclarationDescriptor classObject, @NotNull ClassKind kind) { return isClassObject(classObject) && isKindOf(classObject.getContainingDeclaration(), kind); } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/FieldInfo.java b/compiler/backend/src/org/jetbrains/jet/codegen/FieldInfo.java index 5c6778c54e7..b17950814b3 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/FieldInfo.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/FieldInfo.java @@ -17,36 +17,35 @@ package org.jetbrains.jet.codegen; import org.jetbrains.annotations.NotNull; -import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.jet.codegen.state.JetTypeMapper; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; import org.jetbrains.jet.lang.descriptors.ClassKind; import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.java.JvmAbi; +import org.jetbrains.org.objectweb.asm.Type; public class FieldInfo { - @NotNull - public static FieldInfo createForSingleton(@NotNull ClassDescriptor fieldClassDescriptor, @NotNull JetTypeMapper typeMapper) { - ClassKind kind = fieldClassDescriptor.getKind(); - if (kind != ClassKind.OBJECT && kind != ClassKind.CLASS_OBJECT && kind != ClassKind.ENUM_ENTRY) { - throw new UnsupportedOperationException(); + public static FieldInfo createForSingleton(@NotNull ClassDescriptor classDescriptor, @NotNull JetTypeMapper typeMapper) { + ClassKind kind = classDescriptor.getKind(); + if (!kind.isSingleton()) { + throw new UnsupportedOperationException("Can't create singleton field for class: " + classDescriptor); } - Type fieldType = typeMapper.mapType(fieldClassDescriptor); - ClassDescriptor ownerDescriptor = kind == ClassKind.OBJECT - ? fieldClassDescriptor: DescriptorUtils.getParentOfType(fieldClassDescriptor, ClassDescriptor.class); - assert ownerDescriptor != null; + ? classDescriptor + : DescriptorUtils.getParentOfType(classDescriptor, ClassDescriptor.class); + assert ownerDescriptor != null : "Owner not found for class: " + classDescriptor; Type ownerType = typeMapper.mapType(ownerDescriptor); String fieldName = kind == ClassKind.ENUM_ENTRY - ? fieldClassDescriptor.getName().asString() - : fieldClassDescriptor.getKind() == ClassKind.CLASS_OBJECT ? JvmAbi.CLASS_OBJECT_FIELD : JvmAbi.INSTANCE_FIELD; - return new FieldInfo(ownerType, fieldType, fieldName, true); + ? classDescriptor.getName().asString() + : classDescriptor.getKind() == ClassKind.CLASS_OBJECT ? JvmAbi.CLASS_OBJECT_FIELD : JvmAbi.INSTANCE_FIELD; + return new FieldInfo(ownerType, typeMapper.mapType(classDescriptor), fieldName, true); } - public static FieldInfo createForHiddenField(@NotNull Type owner, Type fieldType, String fieldName) { + @NotNull + public static FieldInfo createForHiddenField(@NotNull Type owner, @NotNull Type fieldType, @NotNull String fieldName) { return new FieldInfo(owner, fieldType, fieldName, false); } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java index f7f6e93dfee..8354f20ad1a 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/ImplementationBodyCodegen.java @@ -1091,8 +1091,16 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { v.newField(OtherOrigin(original), ACC_PUBLIC | ACC_STATIC | ACC_FINAL, field.name, field.type.getDescriptor(), null, null); - if (!AsmUtil.isClassObjectWithBackingFieldsInOuter(fieldTypeDescriptor)) { - genInitSingleton(fieldTypeDescriptor, field); + if (state.getClassBuilderMode() != ClassBuilderMode.FULL) return; + + if (isObject(descriptor)) { + // Invoke the object constructor but ignore the result because INSTANCE$ will be initialized in the first line of + InstructionAdapter v = createOrGetClInitCodegen().v; + v.anew(classAsmType); + v.invokespecial(classAsmType.getInternalName(), "", "()V", false); + } + else if (!isClassObjectWithBackingFieldsInOuter(fieldTypeDescriptor)) { + generateClassObjectInitializer(fieldTypeDescriptor); } } @@ -1142,16 +1150,11 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { field.store(field.type, codegen.v); } - private void genInitSingleton(ClassDescriptor fieldTypeDescriptor, StackValue.Field field) { - if (state.getClassBuilderMode() == ClassBuilderMode.FULL) { - Collection constructors = fieldTypeDescriptor.getConstructors(); - assert constructors.size() == 1 : "Class of singleton object must have only one constructor: " + constructors; - - ExpressionCodegen codegen = createOrGetClInitCodegen(); - FunctionDescriptor fd = codegen.accessibleFunctionDescriptor(constructors.iterator().next()); - generateMethodCallTo(fd, codegen.v); - field.store(field.type, codegen.v); - } + private void generateClassObjectInitializer(@NotNull ClassDescriptor classObject) { + ExpressionCodegen codegen = createOrGetClInitCodegen(); + FunctionDescriptor constructor = codegen.accessibleFunctionDescriptor(KotlinPackage.single(classObject.getConstructors())); + generateMethodCallTo(constructor, codegen.v); + StackValue.singleton(classObject, typeMapper).store(typeMapper.mapClass(classObject), codegen.v); } private void generatePrimaryConstructor(final DelegationFieldsInfo delegationFieldsInfo) { @@ -1211,6 +1214,11 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { generateDelegatorToConstructorCall(iv, codegen, constructorDescriptor); } + if (isObject(descriptor)) { + iv.load(0, classAsmType); + StackValue.singleton(descriptor, typeMapper).store(classAsmType, iv); + } + for (JetDelegationSpecifier specifier : myClass.getDelegationSpecifiers()) { if (specifier instanceof JetDelegatorByExpressionSpecifier) { genCallToDelegatorByExpressionSpecifier(iv, codegen, (JetDelegatorByExpressionSpecifier) specifier, fieldsInfo); @@ -1232,18 +1240,18 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { curParam++; } - boolean generateInitializerInOuter = isClassObjectWithBackingFieldsInOuter(descriptor); - if (generateInitializerInOuter) { + if (isClassObjectWithBackingFieldsInOuter(descriptor)) { final ImplementationBodyCodegen parentCodegen = getParentBodyCodegen(this); //generate OBJECT$ - parentCodegen.genInitSingleton(descriptor, StackValue.singleton(descriptor, typeMapper)); + parentCodegen.generateClassObjectInitializer(descriptor); generateInitializers(new Function0() { @Override public ExpressionCodegen invoke() { return parentCodegen.createOrGetClInitCodegen(); } }); - } else { + } + else { generateInitializers(new Function0() { @Override public ExpressionCodegen invoke() { diff --git a/compiler/testData/codegen/box/objects/anonymousObjectPropertyInitialization.kt b/compiler/testData/codegen/box/objects/anonymousObjectPropertyInitialization.kt new file mode 100644 index 00000000000..a46db9d4e66 --- /dev/null +++ b/compiler/testData/codegen/box/objects/anonymousObjectPropertyInitialization.kt @@ -0,0 +1,14 @@ +trait T { + fun foo(): String +} + +val o = object : T { + val a = "OK" + val f = { + a + }() + + override fun foo() = f +} + +fun box() = o.foo() diff --git a/compiler/testData/codegen/box/objects/localFunctionInObjectInitializer_kt4516.kt b/compiler/testData/codegen/box/objects/localFunctionInObjectInitializer_kt4516.kt new file mode 100644 index 00000000000..1b2d80b876f --- /dev/null +++ b/compiler/testData/codegen/box/objects/localFunctionInObjectInitializer_kt4516.kt @@ -0,0 +1,18 @@ +import java.util.HashMap + +object O { + val mmmap = HashMap(); + + { + fun doStuff() { + mmmap.put("two", 2) + } + doStuff() + } +} + +fun box(): String { + val r = O.mmmap["two"] + if (r != 2) return "Fail: $r" + return "OK" +} diff --git a/compiler/testData/codegen/box/objects/objectInitialization_kt5523.kt b/compiler/testData/codegen/box/objects/objectInitialization_kt5523.kt new file mode 100644 index 00000000000..3238fe1f9ce --- /dev/null +++ b/compiler/testData/codegen/box/objects/objectInitialization_kt5523.kt @@ -0,0 +1,6 @@ +object A { + val a = "OK" + val b = A.a +} + +fun box() = A.b diff --git a/compiler/testData/codegen/box/objects/objectVsClassInitialization_kt5291.kt b/compiler/testData/codegen/box/objects/objectVsClassInitialization_kt5291.kt new file mode 100644 index 00000000000..88ec3c5bb6e --- /dev/null +++ b/compiler/testData/codegen/box/objects/objectVsClassInitialization_kt5291.kt @@ -0,0 +1,24 @@ +public inline fun T.with(f: T.() -> Unit): T { + this.f() + return this +} + +public class Cls { + val string = "Cls" + val buffer = StringBuilder().with { + append(string) + } +} + +public object Obj { + val string = "Obj" + val buffer = StringBuilder().with { + append(string) + } +} + +fun box(): String { + if (Cls().buffer.toString() != "Cls") return "Fail class" + if (Obj.buffer.toString() != "Obj") return "Fail object" + return "OK" +} diff --git a/compiler/testData/codegen/box/objects/privateExtensionFromInitializer_kt4543.kt b/compiler/testData/codegen/box/objects/privateExtensionFromInitializer_kt4543.kt new file mode 100644 index 00000000000..ffcb2ada647 --- /dev/null +++ b/compiler/testData/codegen/box/objects/privateExtensionFromInitializer_kt4543.kt @@ -0,0 +1,16 @@ +class A(val result: String) + +fun a(body: A.() -> String): String { + val r = A("OK") + return r.body() +} + +object C { + private fun A.f() = result + + val g = a { + f() + } +} + +fun box() = C.g diff --git a/compiler/testData/codegen/box/objects/privateFunctionFromClosureInInitializer_kt5582.kt b/compiler/testData/codegen/box/objects/privateFunctionFromClosureInInitializer_kt5582.kt new file mode 100644 index 00000000000..833336b325c --- /dev/null +++ b/compiler/testData/codegen/box/objects/privateFunctionFromClosureInInitializer_kt5582.kt @@ -0,0 +1,20 @@ +trait T + +object Foo { + private fun foo(p: T) = p + + private val v: Int = { + val x = foo(O) + 42 + }() + + private object O : T + + val result = v +} + +fun box(): String { + val foo = Foo + if (foo.result != 42) return "Fail: ${foo.result}" + return "OK" +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java index 4e44ef08a5c..9033b83d5d8 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxCodegenTestGenerated.java @@ -4654,6 +4654,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/objects"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("anonymousObjectPropertyInitialization.kt") + public void testAnonymousObjectPropertyInitialization() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/anonymousObjectPropertyInitialization.kt"); + doTest(fileName); + } + @TestMetadata("flist.kt") public void testFlist() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/flist.kt"); @@ -4768,6 +4774,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("localFunctionInObjectInitializer_kt4516.kt") + public void testLocalFunctionInObjectInitializer_kt4516() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/localFunctionInObjectInitializer_kt4516.kt"); + doTest(fileName); + } + @TestMetadata("methodOnObject.kt") public void testMethodOnObject() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/methodOnObject.kt"); @@ -4786,6 +4798,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("objectInitialization_kt5523.kt") + public void testObjectInitialization_kt5523() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/objectInitialization_kt5523.kt"); + doTest(fileName); + } + @TestMetadata("objectLiteral.kt") public void testObjectLiteral() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/objectLiteral.kt"); @@ -4798,6 +4816,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("objectVsClassInitialization_kt5291.kt") + public void testObjectVsClassInitialization_kt5291() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/objectVsClassInitialization_kt5291.kt"); + doTest(fileName); + } + @TestMetadata("objectWithSuperclass.kt") public void testObjectWithSuperclass() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/objectWithSuperclass.kt"); @@ -4810,6 +4834,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("privateExtensionFromInitializer_kt4543.kt") + public void testPrivateExtensionFromInitializer_kt4543() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/privateExtensionFromInitializer_kt4543.kt"); + doTest(fileName); + } + + @TestMetadata("privateFunctionFromClosureInInitializer_kt5582.kt") + public void testPrivateFunctionFromClosureInInitializer_kt5582() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/privateFunctionFromClosureInInitializer_kt5582.kt"); + doTest(fileName); + } + @TestMetadata("receiverInConstructor.kt") public void testReceiverInConstructor() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/receiverInConstructor.kt");