Skip to content

Commit bea3903

Browse files
Mikhael Bogdanovilya-g
Mikhael Bogdanov
authored andcommitted
Move object initialization from <init> to <clinit>
Codegen generates static backing fields for object properties. They are initialized in class constructor but some of them are final static and such access is prohibited in specification but it's allowed in java bytecode <= 1.8. Such access in 1.9 bytecode cause "IllegalAccessError: Update to static final field Object.INSTANCE attempted from a different method (<init>) than the initializer method <clinit>" Added additional hidden field in interface companion to pass out companion instance from <clinit>. #KT-15894 Fixed
1 parent 6bbad95 commit bea3903

File tree

13 files changed

+223
-47
lines changed

13 files changed

+223
-47
lines changed

compiler/backend/src/org/jetbrains/kotlin/codegen/FieldInfo.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public static FieldInfo createForSingleton(@NotNull ClassDescriptor classDescrip
3434
}
3535

3636
if (isNonCompanionObject(classDescriptor) || CompanionObjectMapping.INSTANCE.isMappedIntrinsicCompanionObject(classDescriptor)) {
37-
return createSingletonViaInstance(classDescriptor, typeMapper);
37+
return createSingletonViaInstance(classDescriptor, typeMapper, JvmAbi.INSTANCE_FIELD);
3838
}
3939

4040
ClassDescriptor ownerDescriptor = DescriptorUtils.getParentOfType(classDescriptor, ClassDescriptor.class);
@@ -46,10 +46,11 @@ public static FieldInfo createForSingleton(@NotNull ClassDescriptor classDescrip
4646
@NotNull
4747
public static FieldInfo createSingletonViaInstance(
4848
@NotNull ClassDescriptor classDescriptor,
49-
@NotNull KotlinTypeMapper typeMapper
49+
@NotNull KotlinTypeMapper typeMapper,
50+
@NotNull String name
5051
) {
5152
Type type = typeMapper.mapType(classDescriptor);
52-
return new FieldInfo(type, type, JvmAbi.INSTANCE_FIELD, true);
53+
return new FieldInfo(type, type, name, true);
5354
}
5455

5556
@NotNull

compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java

Lines changed: 68 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import org.jetbrains.kotlin.descriptors.*;
4040
import org.jetbrains.kotlin.incremental.components.NoLookupLocation;
4141
import org.jetbrains.kotlin.lexer.KtTokens;
42-
import org.jetbrains.kotlin.load.java.JvmAbi;
4342
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor;
4443
import org.jetbrains.kotlin.name.FqName;
4544
import org.jetbrains.kotlin.name.Name;
@@ -76,6 +75,7 @@
7675
import static org.jetbrains.kotlin.codegen.CodegenUtilKt.isNonGenericToArray;
7776
import static org.jetbrains.kotlin.codegen.JvmCodegenUtil.*;
7877
import static org.jetbrains.kotlin.codegen.binding.CodegenBinding.enumEntryNeedSubclass;
78+
import static org.jetbrains.kotlin.load.java.JvmAbi.*;
7979
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getDelegationConstructorCall;
8080
import static org.jetbrains.kotlin.resolve.BindingContextUtils.getNotNull;
8181
import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
@@ -344,6 +344,8 @@ protected void generateSyntheticParts() {
344344

345345
generateFieldForSingleton();
346346

347+
initializeObjects();
348+
347349
generateCompanionObjectBackingFieldCopies();
348350

349351
generateTraitMethods();
@@ -798,21 +800,21 @@ private void generateEnumValueOfMethod() {
798800
}
799801

800802
private void generateFieldForSingleton() {
803+
if (isCompanionObjectInInterfaceNotIntrinsic(descriptor)) {
804+
StackValue.Field field = StackValue.createSingletonViaInstance(descriptor, typeMapper, HIDDEN_INSTANCE_FIELD);
805+
//hidden instance in interface companion
806+
v.newField(JvmDeclarationOriginKt.OtherOrigin(descriptor),
807+
ACC_SYNTHETIC | ACC_STATIC | ACC_FINAL, field.name, field.type.getDescriptor(), null, null);
808+
}
809+
801810
if (isEnumEntry(descriptor) || isCompanionObject(descriptor)) return;
802811

803812
if (isNonCompanionObject(descriptor)) {
804-
StackValue.Field field = StackValue.singletonViaInstance(descriptor, typeMapper);
813+
StackValue.Field field = StackValue.createSingletonViaInstance(descriptor, typeMapper, INSTANCE_FIELD);
805814
v.newField(JvmDeclarationOriginKt.OtherOrigin(myClass),
806815
ACC_PUBLIC | ACC_STATIC | ACC_FINAL,
807816
field.name, field.type.getDescriptor(), null, null);
808817

809-
if (!state.getClassBuilderMode().generateBodies) return;
810-
// Invoke the object constructor but ignore the result because INSTANCE will be initialized in the first line of <init>
811-
InstructionAdapter v = createOrGetClInitCodegen().v;
812-
markLineNumberForElement(element.getPsiOrParent(), v);
813-
v.anew(classAsmType);
814-
v.invokespecial(classAsmType.getInternalName(), "<init>", "()V", false);
815-
816818
return;
817819
}
818820

@@ -828,6 +830,60 @@ private void generateFieldForSingleton() {
828830
ACC_PUBLIC | ACC_STATIC | ACC_FINAL, field.name, field.type.getDescriptor(), null, null);
829831
}
830832

833+
private void initializeObjects() {
834+
if (!DescriptorUtils.isObject(descriptor)) return;
835+
if (!state.getClassBuilderMode().generateBodies) return;
836+
837+
boolean isNonCompanionObject = isNonCompanionObject(descriptor);
838+
boolean isInterfaceCompanion = isCompanionObjectInInterfaceNotIntrinsic(descriptor);
839+
boolean isMappedIntrinsicCompanionObject = isMappedIntrinsicCompanionObject(descriptor);
840+
if (isNonCompanionObject || isInterfaceCompanion || isMappedIntrinsicCompanionObject) {
841+
ExpressionCodegen clInitCodegen = createOrGetClInitCodegen();
842+
InstructionAdapter v = clInitCodegen.v;
843+
markLineNumberForElement(element.getPsiOrParent(), v);
844+
v.anew(classAsmType);
845+
v.dup();
846+
v.invokespecial(classAsmType.getInternalName(), "<init>", "()V", false);
847+
848+
//local0 emulates this in object constructor
849+
int local0Index = clInitCodegen.getFrameMap().enterTemp(classAsmType);
850+
assert local0Index == 0 : "Local variable with index 0 in clInit should be used only for singleton instance keeping";
851+
StackValue.Local local0 = StackValue.local(0, classAsmType);
852+
local0.store(StackValue.onStack(classAsmType), clInitCodegen.v);
853+
StackValue.Field singleton =
854+
StackValue.createSingletonViaInstance(
855+
descriptor, typeMapper, isInterfaceCompanion ? HIDDEN_INSTANCE_FIELD : INSTANCE_FIELD
856+
);
857+
singleton.store(local0, clInitCodegen.v);
858+
859+
generateInitializers(clInitCodegen);
860+
861+
if (isInterfaceCompanion) {
862+
//initialize singleton instance in outer by hidden instance
863+
StackValue.singleton(descriptor, typeMapper).store(
864+
singleton, getParentCodegen().createOrGetClInitCodegen().v, true
865+
);
866+
}
867+
}
868+
else if (isCompanionObjectWithBackingFieldsInOuter(descriptor)) {
869+
ImplementationBodyCodegen parentCodegen = (ImplementationBodyCodegen) getParentCodegen();
870+
ExpressionCodegen parentClInitCodegen = parentCodegen.createOrGetClInitCodegen();
871+
InstructionAdapter parentVisitor = parentClInitCodegen.v;
872+
873+
FunctionDescriptor constructor = (FunctionDescriptor) parentCodegen.context.accessibleDescriptor(
874+
CollectionsKt.single(descriptor.getConstructors()), /* superCallExpression = */ null
875+
);
876+
parentCodegen.generateMethodCallTo(constructor, null, parentVisitor);
877+
StackValue instance = StackValue.onStack(parentCodegen.typeMapper.mapClass(descriptor));
878+
StackValue.singleton(descriptor, parentCodegen.typeMapper).store(instance, parentVisitor, true);
879+
880+
generateInitializers(parentClInitCodegen);
881+
}
882+
else {
883+
assert false : "Unknown object type: " + descriptor;
884+
}
885+
}
886+
831887
private void generateCompanionObjectBackingFieldCopies() {
832888
if (companionObjectPropertiesToCopy == null) return;
833889

@@ -875,17 +931,6 @@ private void copyFieldFromCompanionObject(PropertyDescriptor propertyDescriptor)
875931
field.store(property, codegen.v);
876932
}
877933

878-
private void generateCompanionObjectInitializer(@NotNull ClassDescriptor companionObject) {
879-
ExpressionCodegen codegen = createOrGetClInitCodegen();
880-
881-
FunctionDescriptor constructor = (FunctionDescriptor) context.accessibleDescriptor(
882-
CollectionsKt.single(companionObject.getConstructors()), /* superCallExpression = */ null
883-
);
884-
generateMethodCallTo(constructor, null, codegen.v);
885-
StackValue instance = StackValue.onStack(typeMapper.mapClass(companionObject));
886-
StackValue.singleton(companionObject, typeMapper).store(instance, codegen.v, true);
887-
}
888-
889934
private void generatePrimaryConstructor(DelegationFieldsInfo delegationFieldsInfo) {
890935
if (isInterface(descriptor) || isAnnotationClass(descriptor)) return;
891936

@@ -953,10 +998,6 @@ private void generatePrimaryConstructorImpl(
953998
generateDelegatorToConstructorCall(iv, codegen, constructorDescriptor,
954999
getDelegationConstructorCall(bindingContext, constructorDescriptor));
9551000

956-
if (isNonCompanionObject(descriptor)) {
957-
StackValue.singletonViaInstance(descriptor, typeMapper).store(StackValue.LOCAL_0, iv);
958-
}
959-
9601001
for (KtSuperTypeListEntry specifier : myClass.getSuperTypeListEntries()) {
9611002
if (specifier instanceof KtDelegatedSuperTypeEntry) {
9621003
genCallToDelegatorByExpressionSpecifier(iv, codegen, (KtDelegatedSuperTypeEntry) specifier, fieldsInfo);
@@ -978,18 +1019,10 @@ private void generatePrimaryConstructorImpl(
9781019
curParam++;
9791020
}
9801021

981-
if (isCompanionObject(descriptor)) {
982-
ImplementationBodyCodegen parentCodegen = (ImplementationBodyCodegen) getParentCodegen();
983-
parentCodegen.generateCompanionObjectInitializer(descriptor);
984-
}
985-
986-
if (JvmAbi.isCompanionObjectWithBackingFieldsInOuter(descriptor)) {
987-
generateInitializers(((ImplementationBodyCodegen) getParentCodegen())::createOrGetClInitCodegen);
988-
}
989-
else {
1022+
//object initialization was moved to initializeObjects()
1023+
if (!isObject(descriptor)) {
9901024
generateInitializers(codegen);
9911025
}
992-
9931026
iv.visitInsn(RETURN);
9941027
}
9951028

@@ -1125,7 +1158,7 @@ private DelegationFieldsInfo getDelegationFieldsInfo(@NotNull List<KtSuperTypeLi
11251158
KotlinType expressionType = expression != null ? bindingContext.getType(expression) : null;
11261159
Type asmType =
11271160
expressionType != null ? typeMapper.mapType(expressionType) : typeMapper.mapType(getSuperClass(specifier));
1128-
result.addField((KtDelegatedSuperTypeEntry) specifier, asmType, JvmAbi.DELEGATE_SUPER_FIELD_PREFIX + n);
1161+
result.addField((KtDelegatedSuperTypeEntry) specifier, asmType, DELEGATE_SUPER_FIELD_PREFIX + n);
11291162
}
11301163
n++;
11311164
}

compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -594,8 +594,8 @@ public static Field singleton(@NotNull ClassDescriptor classDescriptor, @NotNull
594594
return field(FieldInfo.createForSingleton(classDescriptor, typeMapper), none());
595595
}
596596

597-
public static Field singletonViaInstance(ClassDescriptor classDescriptor, KotlinTypeMapper typeMapper) {
598-
return field(FieldInfo.createSingletonViaInstance(classDescriptor, typeMapper), none());
597+
public static Field createSingletonViaInstance(@NotNull ClassDescriptor classDescriptor, @NotNull KotlinTypeMapper typeMapper, @NotNull String name) {
598+
return field(FieldInfo.createSingletonViaInstance(classDescriptor, typeMapper, name), none());
599599
}
600600

601601
public static StackValue operation(Type type, Function1<InstructionAdapter, Unit> lambda) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
var result = ""
2+
3+
class A {
4+
5+
companion object {
6+
7+
val prop = test()
8+
9+
fun test(): String {
10+
result += "OK"
11+
return result
12+
}
13+
}
14+
}
15+
16+
fun box(): String {
17+
if (A.prop != "OK") return "fail ${A.prop}"
18+
return result
19+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
var result = ""
2+
3+
interface A {
4+
5+
companion object {
6+
7+
val prop = test()
8+
9+
fun test(): String {
10+
result += "OK"
11+
return result
12+
}
13+
}
14+
}
15+
16+
fun box(): String {
17+
if (A.prop != "OK") return "fail ${A.prop}"
18+
return result
19+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
var result = ""
2+
3+
object A {
4+
val prop = test()
5+
6+
fun test(): String {
7+
result += "OK"
8+
return result
9+
}
10+
}
11+
12+
fun box(): String {
13+
if (A.prop != "OK") return "fail ${A.prop}"
14+
return result
15+
}

compiler/testData/codegen/box/reflection/enclosing/lambdaInObjectDeclaration.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ fun box(): String {
1414
if (enclosingMethod != null) return "method: $enclosingMethod"
1515

1616
val enclosingConstructor = javaClass.getEnclosingConstructor()
17-
if (enclosingConstructor == null) return "no enclosing constructor"
17+
if (enclosingConstructor != null) return "field should be initialized in clInit"
1818

1919
val enclosingClass = javaClass.getEnclosingClass()
2020
if (enclosingClass?.getName() != "O") return "enclosing class: $enclosingClass"

compiler/testData/codegen/dumpDeclarations/interfaces.json

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@
4949
"class": "PublicInterface$Companion",
5050
"members": [
5151
{"visibility": "private", "declaration": "constructor Companion()", "name": "<init>", "desc": "()V"},
52-
{"visibility": "private", "declaration": "constructor Companion()", "name": "<init>", "desc": "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V"}
52+
{"visibility": "public", "declaration": "companion object", "name": "$$INSTANCE", "desc": "LPublicInterface$Companion;"},
53+
{"visibility": "public", "declaration": "companion object", "name": "<clinit>", "desc": "()V"}
5354
]
5455
},
5556
{
@@ -75,7 +76,8 @@
7576
"class": "InternalInterface$Companion",
7677
"members": [
7778
{"visibility": "private", "declaration": "constructor Companion()", "name": "<init>", "desc": "()V"},
78-
{"visibility": "private", "declaration": "constructor Companion()", "name": "<init>", "desc": "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V"}
79+
{"visibility": "public", "declaration": "companion object", "name": "$$INSTANCE", "desc": "LInternalInterface$Companion;"},
80+
{"visibility": "public", "declaration": "companion object", "name": "<clinit>", "desc": "()V"}
7981
]
8082
},
8183
{
@@ -101,7 +103,8 @@
101103
"class": "InternalInterfacePrivateCompanion$Companion",
102104
"members": [
103105
{"visibility": "private", "declaration": "constructor Companion()", "name": "<init>", "desc": "()V"},
104-
{"visibility": "private", "declaration": "constructor Companion()", "name": "<init>", "desc": "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V"}
106+
{"visibility": "private", "declaration": "companion object", "name": "$$INSTANCE", "desc": "LInternalInterfacePrivateCompanion$Companion;"},
107+
{"visibility": "private", "declaration": "companion object", "name": "<clinit>", "desc": "()V"}
105108
]
106109
},
107110
{
@@ -163,7 +166,8 @@
163166
"class": "PrivateInterface$Companion",
164167
"members": [
165168
{"visibility": "private", "declaration": "constructor Companion()", "name": "<init>", "desc": "()V"},
166-
{"visibility": "private", "declaration": "constructor Companion()", "name": "<init>", "desc": "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V"}
169+
{"visibility": "public", "declaration": "companion object", "name": "$$INSTANCE", "desc": "LPrivateInterface$Companion;"},
170+
{"visibility": "public", "declaration": "companion object", "name": "<clinit>", "desc": "()V"}
167171
]
168172
},
169173
{
@@ -189,7 +193,8 @@
189193
"class": "PrivateInterfacePrivateCompanion$Companion",
190194
"members": [
191195
{"visibility": "private", "declaration": "constructor Companion()", "name": "<init>", "desc": "()V"},
192-
{"visibility": "private", "declaration": "constructor Companion()", "name": "<init>", "desc": "(Lkotlin/jvm/internal/DefaultConstructorMarker;)V"}
196+
{"visibility": "private", "declaration": "companion object", "name": "$$INSTANCE", "desc": "LPrivateInterfacePrivateCompanion$Companion;"},
197+
{"visibility": "private", "declaration": "companion object", "name": "<clinit>", "desc": "()V"}
193198
]
194199
},
195200
{

compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11360,6 +11360,12 @@ public void testClassCallsProtectedInheritedByCompanion() throws Exception {
1136011360
doTest(fileName);
1136111361
}
1136211362

11363+
@TestMetadata("classCompanion.kt")
11364+
public void testClassCompanion() throws Exception {
11365+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/classCompanion.kt");
11366+
doTest(fileName);
11367+
}
11368+
1136311369
@TestMetadata("flist.kt")
1136411370
public void testFlist() throws Exception {
1136511371
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/flist.kt");
@@ -11372,6 +11378,12 @@ public void testInitializationOrder() throws Exception {
1137211378
doTest(fileName);
1137311379
}
1137411380

11381+
@TestMetadata("interfaceCompanion.kt")
11382+
public void testInterfaceCompanion() throws Exception {
11383+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/interfaceCompanion.kt");
11384+
doTest(fileName);
11385+
}
11386+
1137511387
@TestMetadata("kt1047.kt")
1137611388
public void testKt1047() throws Exception {
1137711389
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/kt1047.kt");
@@ -11504,6 +11516,12 @@ public void testNestedObjectWithSuperclass() throws Exception {
1150411516
doTest(fileName);
1150511517
}
1150611518

11519+
@TestMetadata("object.kt")
11520+
public void testObject() throws Exception {
11521+
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/object.kt");
11522+
doTest(fileName);
11523+
}
11524+
1150711525
@TestMetadata("objectExtendsInnerAndReferencesOuterMember.kt")
1150811526
public void testObjectExtendsInnerAndReferencesOuterMember() throws Exception {
1150911527
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/objects/objectExtendsInnerAndReferencesOuterMember.kt");

0 commit comments

Comments
 (0)