Move lateinit assertion for companion property to companion object
Previously, for a property named `x` in the companion object of a class named `Foo`, we generated: - `Foo.access$getX$cp`, consisting of `GETFIELD Foo.x` and lateinit assertion - `Foo.Companion.getX`, consisting of `INVOKEVIRTUAL Foo.access$getX$cp` Now, we generate: - `Foo.access$getX$cp`, consisting of `GETFIELD Foo.x` - `Foo.Companion.getX`, consisting of `INVOKEVIRTUAL Foo.access$getX$cp` and lateinit assertion The reason is that this way we can avoid generating another accessor and reuse `Foo.access$getX$cp` in case `isInitialized` is called on a lateinit property from companion. For private properties, getX is not generated, but instead the assertion is generated on each access to the field (which can be improved, see KT-28331). The same happens for access to non-private properties from inside the same context where they're declared. #KT-21862 In Progress
This commit is contained in:
@@ -1660,8 +1660,14 @@ public abstract class StackValue {
|
||||
|
||||
v.visitFieldInsn(isStaticPut ? GETSTATIC : GETFIELD,
|
||||
backingFieldOwner.getInternalName(), fieldName, this.type.getDescriptor());
|
||||
if (!skipLateinitAssertion) {
|
||||
genNotNullAssertionForLateInitIfNeeded(v);
|
||||
if (!skipLateinitAssertion && descriptor.isLateInit()) {
|
||||
CallableMemberDescriptor contextDescriptor = codegen.context.getContextDescriptor();
|
||||
boolean isCompanionAccessor =
|
||||
contextDescriptor instanceof AccessorForPropertyBackingField &&
|
||||
((AccessorForPropertyBackingField) contextDescriptor).getAccessorKind() == AccessorKind.IN_CLASS_COMPANION;
|
||||
if (!isCompanionAccessor) {
|
||||
genNonNullAssertForLateinit(v, this.descriptor.getName().asString());
|
||||
}
|
||||
}
|
||||
coerceTo(type, kotlinType, v);
|
||||
}
|
||||
@@ -1694,6 +1700,19 @@ public abstract class StackValue {
|
||||
|
||||
coerce(typeOfValueOnStack, kotlinTypeOfValueOnStack, type, kotlinType, v);
|
||||
|
||||
// For non-private lateinit properties in companion object, the assertion is generated in the public getFoo method
|
||||
// in the companion and _not_ in the synthetic accessor access$getFoo$cp in the outer class. The reason is that this way,
|
||||
// the synthetic accessor can be reused for isInitialized checks, which require there to be no assertion.
|
||||
// For lateinit properties that are accessed via the backing field directly (or via the synthetic accessor, if the access
|
||||
// is from a different context), the assertion will be generated on each access, see KT-28331.
|
||||
if (descriptor instanceof AccessorForPropertyBackingField) {
|
||||
PropertyDescriptor property = ((AccessorForPropertyBackingField) descriptor).getCalleeDescriptor();
|
||||
if (!skipLateinitAssertion && property.isLateInit() && JvmAbi.isPropertyWithBackingFieldInOuterClass(property) &&
|
||||
!JvmCodegenUtil.couldUseDirectAccessToProperty(property, true, false, codegen.context, false)) {
|
||||
genNonNullAssertForLateinit(v, property.getName().asString());
|
||||
}
|
||||
}
|
||||
|
||||
KotlinType returnType = descriptor.getReturnType();
|
||||
if (returnType != null && KotlinBuiltIns.isNothing(returnType)) {
|
||||
v.aconst(null);
|
||||
@@ -1732,12 +1751,6 @@ public abstract class StackValue {
|
||||
return true;
|
||||
}
|
||||
|
||||
private void genNotNullAssertionForLateInitIfNeeded(@NotNull InstructionAdapter v) {
|
||||
if (!descriptor.isLateInit()) return;
|
||||
|
||||
StackValue.genNonNullAssertForLateinit(v, descriptor.getName().asString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void store(@NotNull StackValue rightSide, @NotNull InstructionAdapter v, boolean skipReceiver) {
|
||||
PropertySetterDescriptor setterDescriptor = descriptor.getSetter();
|
||||
|
||||
Reference in New Issue
Block a user