Fix initialization of object's INSTANCE$ field

Call object's constructor in <clinit> and ignore the result, but in the first
line of <init> save the current "this" to INSTANCE$

 #KT-4516 Fixed
 #KT-4543 Fixed
 #KT-5291 Fixed
 #KT-5523 Fixed
 #KT-5582 Fixed
This commit is contained in:
Alexander Udalov
2014-09-11 14:19:32 +04:00
parent b907f35995
commit 81004889eb
10 changed files with 175 additions and 34 deletions
@@ -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);
}
@@ -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);
}
@@ -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 <init>
InstructionAdapter v = createOrGetClInitCodegen().v;
v.anew(classAsmType);
v.invokespecial(classAsmType.getInternalName(), "<init>", "()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<ConstructorDescriptor> 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<ExpressionCodegen>() {
@Override
public ExpressionCodegen invoke() {
return parentCodegen.createOrGetClInitCodegen();
}
});
} else {
}
else {
generateInitializers(new Function0<ExpressionCodegen>() {
@Override
public ExpressionCodegen invoke() {
@@ -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()
@@ -0,0 +1,18 @@
import java.util.HashMap
object O {
val mmmap = HashMap<String, Int>();
{
fun doStuff() {
mmmap.put("two", 2)
}
doStuff()
}
}
fun box(): String {
val r = O.mmmap["two"]
if (r != 2) return "Fail: $r"
return "OK"
}
@@ -0,0 +1,6 @@
object A {
val a = "OK"
val b = A.a
}
fun box() = A.b
@@ -0,0 +1,24 @@
public inline fun <T> 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"
}
@@ -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
@@ -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"
}
@@ -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");