Fix self-reference to singleton in initializer
Singleton instance is "initialized" by delegating constructor call, which is superclass constructor call in case of singletons (because singletons can't have more than one constructor). Singleton constructor is effectively split into two stages: - before a super constructor call; - after a super constructor call. Before super constructor call, singleton instance can't be used directly (see KT-20662), because neither 'this' nor static instance is initialized yet. However, it can be used in closures, in which case a static instance should be used (escaping uninitialized this is prohibited by JVM). Actually using this static instance before it is initialized (e.g., invoking a method that uses this singleton) will cause a correct ExceptionInInitializerError. After a super constructor call, static instance of a singleton may be not initialized yet (in case of enum entries and interface companion objects). However, we already have an initialized 'this', which we should use for singleton references. #KT-20651 Fixed
This commit is contained in:
@@ -1736,7 +1736,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
|
||||
if (descriptor instanceof ClassDescriptor) {
|
||||
ClassDescriptor classDescriptor = (ClassDescriptor) descriptor;
|
||||
if (isPossiblyUninitializedSingleton(classDescriptor) && isInsideSingleton(classDescriptor)) {
|
||||
if (shouldGenerateSingletonAsThisOrOuterFromContext(classDescriptor)) {
|
||||
return generateThisOrOuterFromContext(classDescriptor, false, false);
|
||||
}
|
||||
if (isObject(classDescriptor)) {
|
||||
@@ -1759,6 +1759,27 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
throw new UnsupportedOperationException("don't know how to generate reference " + descriptor);
|
||||
}
|
||||
|
||||
private boolean shouldGenerateSingletonAsThisOrOuterFromContext(ClassDescriptor classDescriptor) {
|
||||
return isPossiblyUninitializedSingleton(classDescriptor) &&
|
||||
isInsideSingleton(classDescriptor) &&
|
||||
isThisInitialized(classDescriptor);
|
||||
}
|
||||
|
||||
private boolean isThisInitialized(ClassDescriptor classDescriptor) {
|
||||
CodegenContext context = this.context;
|
||||
while (context != null) {
|
||||
if (context instanceof ConstructorContext) {
|
||||
ConstructorContext constructorContext = (ConstructorContext) context;
|
||||
ClassDescriptor constructedClass = constructorContext.getConstructorDescriptor().getConstructedClass();
|
||||
if (constructedClass == classDescriptor) {
|
||||
return constructorContext.isThisInitialized();
|
||||
}
|
||||
}
|
||||
context = context.getParentContext();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private StackValue applyIntrinsic(
|
||||
DeclarationDescriptor descriptor,
|
||||
@@ -2601,7 +2622,7 @@ public class ExpressionCodegen extends KtVisitor<StackValue, StackValue> impleme
|
||||
!CodegenUtilKt.isJvmStaticInObjectOrClass(context.getFunctionDescriptor())) {
|
||||
return StackValue.local(0, typeMapper.mapType(calleeContainingClass));
|
||||
}
|
||||
else if (CodegenUtilKt.isPossiblyUninitializedSingleton(calleeContainingClass) && isInsideSingleton(calleeContainingClass)) {
|
||||
else if (shouldGenerateSingletonAsThisOrOuterFromContext(calleeContainingClass)) {
|
||||
return generateThisOrOuterFromContext(calleeContainingClass, isSuper, forceOuter);
|
||||
}
|
||||
else if (isEnumEntry(calleeContainingClass)) {
|
||||
|
||||
@@ -1382,10 +1382,28 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen {
|
||||
@NotNull ClassConstructorDescriptor constructorDescriptor,
|
||||
@Nullable ResolvedCall<ConstructorDescriptor> delegationConstructorCall
|
||||
) {
|
||||
MethodContext codegenContext = codegen.context;
|
||||
assert codegenContext instanceof ConstructorContext :
|
||||
"Constructor context expected: " + codegenContext;
|
||||
assert !((ConstructorContext) codegenContext).isThisInitialized() :
|
||||
"Delegating constructor call is already generated for " + ((ConstructorContext) codegenContext).getConstructorDescriptor();
|
||||
|
||||
if (delegationConstructorCall == null) {
|
||||
genSimpleSuperCall(iv);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
generateDelegationConstructorCall(iv, codegen, constructorDescriptor, delegationConstructorCall);
|
||||
}
|
||||
|
||||
((ConstructorContext) codegenContext).setThisInitialized(true);
|
||||
}
|
||||
|
||||
private void generateDelegationConstructorCall(
|
||||
@NotNull InstructionAdapter iv,
|
||||
@NotNull ExpressionCodegen codegen,
|
||||
@NotNull ClassConstructorDescriptor constructorDescriptor,
|
||||
@NotNull ResolvedCall<ConstructorDescriptor> delegationConstructorCall
|
||||
) {
|
||||
iv.load(0, OBJECT_TYPE);
|
||||
ConstructorDescriptor delegateConstructor = SamCodegenUtil.resolveSamAdapter(codegen.getConstructorDescriptor(delegationConstructorCall));
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import static org.jetbrains.kotlin.resolve.jvm.AsmTypes.OBJECT_TYPE;
|
||||
|
||||
public class ConstructorContext extends MethodContext {
|
||||
private static final StackValue LOCAL_1 = StackValue.local(1, OBJECT_TYPE);
|
||||
private boolean thisInitialized = false;
|
||||
|
||||
public ConstructorContext(
|
||||
@NotNull ConstructorDescriptor contextDescriptor,
|
||||
@@ -46,6 +47,18 @@ public class ConstructorContext extends MethodContext {
|
||||
return stackValue;
|
||||
}
|
||||
|
||||
public ConstructorDescriptor getConstructorDescriptor() {
|
||||
return (ConstructorDescriptor) getContextDescriptor();
|
||||
}
|
||||
|
||||
public boolean isThisInitialized() {
|
||||
return thisInitialized;
|
||||
}
|
||||
|
||||
public void setThisInitialized(boolean thisInitialized) {
|
||||
this.thisInitialized = thisInitialized;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Constructor: " + getContextDescriptor();
|
||||
|
||||
Reference in New Issue
Block a user