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:
@@ -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
|
||||
+20
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user