Permalink
Browse files

Support isInitialized intrinsic for lateinit properties

See Kotlin/KEEP#73

 #KT-9327 Fixed
  • Loading branch information...
udalov committed Jul 12, 2017
1 parent 08052e6 commit c6263ac8e6cab87d18f92d77c401faf4b6cad431
Showing with 665 additions and 55 deletions.
  1. +60 −33 compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java
  2. +3 −1 compiler/backend/src/org/jetbrains/kotlin/codegen/FunctionCodegen.java
  3. +7 −5 compiler/backend/src/org/jetbrains/kotlin/codegen/MemberCodegen.java
  4. +15 −13 compiler/backend/src/org/jetbrains/kotlin/codegen/StackValue.java
  5. +5 −0 compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/IntrinsicCallable.kt
  6. +2 −1 compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/IntrinsicMethods.java
  7. +45 −0 compiler/backend/src/org/jetbrains/kotlin/codegen/intrinsics/LateinitIntrinsics.kt
  8. +1 −0 compiler/backend/src/org/jetbrains/kotlin/codegen/syntheticAccessorUtil.kt
  9. +4 −0 compiler/frontend/src/org/jetbrains/kotlin/diagnostics/Errors.java
  10. +4 −0 compiler/frontend/src/org/jetbrains/kotlin/diagnostics/rendering/DefaultErrorMessages.java
  11. +1 −1 compiler/frontend/src/org/jetbrains/kotlin/resolve/TargetPlatform.kt
  12. +81 −0 ...frontend/src/org/jetbrains/kotlin/resolve/calls/checkers/LateinitIntrinsicApplicabilityChecker.kt
  13. +35 −0 compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt
  14. +21 −0 compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/sideEffects.kt
  15. +36 −0 ...iler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/simpleIsInitialized.kt
  16. +12 −0 compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/topLevelProperty.kt
  17. +88 −0 compiler/testData/diagnostics/testsWithStdLib/lateinit/isInitialized.kt
  18. +78 −0 compiler/testData/diagnostics/testsWithStdLib/lateinit/isInitialized.txt
  19. +33 −0 compiler/tests-ir-jvm/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java
  20. +15 −0 compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestWithStdLibGenerated.java
  21. +15 −0 compiler/tests/org/jetbrains/kotlin/checkers/javac/DiagnosticsTestWithStdLibUsingJavacGenerated.java
  22. +33 −0 compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java
  23. +33 −0 compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java
  24. +9 −0 js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java
  25. +10 −1 libraries/stdlib/src/kotlin/internal/Annotations.kt
  26. +19 −0 libraries/stdlib/src/kotlin/util/Lateinit.kt
@@ -1722,7 +1722,8 @@ public StackValue visitSimpleNameExpression(@NotNull KtSimpleNameExpression expr
receiver = StackValue.receiverWithoutReceiverArgument(receiver);
}

return intermediateValueForProperty(propertyDescriptor, directToField, directToField, superCallTarget, false, receiver, resolvedCall);
return intermediateValueForProperty(propertyDescriptor, directToField, directToField, superCallTarget, false, receiver,
resolvedCall, false);
}

if (descriptor instanceof TypeAliasDescriptor) {
@@ -1874,7 +1875,7 @@ public int lookupLocalIndex(DeclarationDescriptor descriptor) {
@Nullable ClassDescriptor superCallTarget,
@NotNull StackValue receiver
) {
return intermediateValueForProperty(propertyDescriptor, forceField, false, superCallTarget, false, receiver, null);
return intermediateValueForProperty(propertyDescriptor, forceField, false, superCallTarget, false, receiver, null, false);
}

private CodegenContext getBackingFieldContext(
@@ -1892,9 +1893,14 @@ private CodegenContext getBackingFieldContext(
return context.getParentContext();
// For companion object property, backing field lives in object containing class
// Otherwise, it lives in its containing declaration
case IN_CLASS_COMPANION: return context.findParentContextWithDescriptor(containingDeclaration.getContainingDeclaration());
case FIELD_FROM_LOCAL: return context.findParentContextWithDescriptor(containingDeclaration);
default: throw new IllegalStateException();
case IN_CLASS_COMPANION:
return context.findParentContextWithDescriptor(containingDeclaration.getContainingDeclaration());
case FIELD_FROM_LOCAL:
return context.findParentContextWithDescriptor(containingDeclaration);
case LATEINIT_INTRINSIC:
return context.findParentContextWithDescriptor(containingDeclaration);
default:
throw new IllegalStateException("Unknown field accessor kind: " + accessorKind);
}
}

@@ -1905,7 +1911,8 @@ private CodegenContext getBackingFieldContext(
@Nullable ClassDescriptor superCallTarget,
boolean skipAccessorsForPrivateFieldInOuterClass,
@NotNull StackValue receiver,
@Nullable ResolvedCall resolvedCall
@Nullable ResolvedCall resolvedCall,
boolean skipLateinitAssertion
) {
if (propertyDescriptor instanceof SyntheticJavaPropertyDescriptor) {
return intermediateValueForSyntheticExtensionProperty((SyntheticJavaPropertyDescriptor) propertyDescriptor, receiver);
@@ -1917,14 +1924,22 @@ private CodegenContext getBackingFieldContext(

DeclarationDescriptor containingDeclaration = propertyDescriptor.getContainingDeclaration();

FieldAccessorKind fieldAccessorKind = FieldAccessorKind.NORMAL;
boolean isBackingFieldInClassCompanion = JvmAbi.isPropertyWithBackingFieldInOuterClass(propertyDescriptor);
if (isBackingFieldInClassCompanion && (forceField || propertyDescriptor.isConst() && Visibilities.isPrivate(propertyDescriptor.getVisibility()))) {
FieldAccessorKind fieldAccessorKind;
if (skipLateinitAssertion) {
fieldAccessorKind = FieldAccessorKind.LATEINIT_INTRINSIC;
}
else if (isBackingFieldInClassCompanion &&
(forceField || propertyDescriptor.isConst() && Visibilities.isPrivate(propertyDescriptor.getVisibility()))) {
fieldAccessorKind = FieldAccessorKind.IN_CLASS_COMPANION;
}
else if (syntheticBackingField && context.getFirstCrossInlineOrNonInlineContext().getParentContext().getContextDescriptor() != containingDeclaration) {
else if ((syntheticBackingField &&
context.getFirstCrossInlineOrNonInlineContext().getParentContext().getContextDescriptor() != containingDeclaration)) {
fieldAccessorKind = FieldAccessorKind.FIELD_FROM_LOCAL;
}
else {
fieldAccessorKind = FieldAccessorKind.NORMAL;
}
boolean isStaticBackingField = DescriptorUtils.isStaticDeclaration(propertyDescriptor) ||
AsmUtil.isInstancePropertyWithStaticBackingField(propertyDescriptor);
boolean isSuper = superCallTarget != null;
@@ -1937,15 +1952,24 @@ else if (syntheticBackingField && context.getFirstCrossInlineOrNonInlineContext(
CallableMethod callableSetter = null;

CodegenContext backingFieldContext = getBackingFieldContext(fieldAccessorKind, containingDeclaration);
DeclarationDescriptor ownerDescriptor = containingDeclaration;
boolean isPrivateProperty = (AsmUtil.getVisibilityForBackingField(propertyDescriptor, isDelegatedProperty) & ACC_PRIVATE) != 0;
DeclarationDescriptor ownerDescriptor;
boolean skipPropertyAccessors;

PropertyDescriptor originalPropertyDescriptor = DescriptorUtils.unwrapFakeOverride(propertyDescriptor);

if (fieldAccessorKind != FieldAccessorKind.NORMAL) {
int flags = AsmUtil.getVisibilityForBackingField(propertyDescriptor, isDelegatedProperty);
if (fieldAccessorKind == FieldAccessorKind.LATEINIT_INTRINSIC) {
skipPropertyAccessors = !isPrivateProperty || context.getClassOrPackageParentContext() == backingFieldContext;

if (!skipPropertyAccessors) {
propertyDescriptor = (AccessorForPropertyBackingField)
backingFieldContext.getAccessor(propertyDescriptor, fieldAccessorKind, delegateType, superCallTarget);
}
ownerDescriptor = propertyDescriptor;
}
else if (fieldAccessorKind == FieldAccessorKind.IN_CLASS_COMPANION || fieldAccessorKind == FieldAccessorKind.FIELD_FROM_LOCAL) {
boolean isInlinedConst = propertyDescriptor.isConst() && state.getShouldInlineConstVals();
skipPropertyAccessors = isInlinedConst || (flags & ACC_PRIVATE) == 0 || skipAccessorsForPrivateFieldInOuterClass;
skipPropertyAccessors = isInlinedConst || !isPrivateProperty || skipAccessorsForPrivateFieldInOuterClass;

if (!skipPropertyAccessors) {
//noinspection ConstantConditions
@@ -1956,12 +1980,13 @@ else if (syntheticBackingField && context.getFirstCrossInlineOrNonInlineContext(
"Unexpected accessor descriptor: " + propertyDescriptor;
ownerDescriptor = propertyDescriptor;
}
else {
ownerDescriptor = containingDeclaration;
}
}
else {
if (!isBackingFieldInClassCompanion) {
ownerDescriptor = propertyDescriptor;
}
skipPropertyAccessors = forceField;
ownerDescriptor = isBackingFieldInClassCompanion ? containingDeclaration : propertyDescriptor;
}

if (!skipPropertyAccessors) {
@@ -2005,10 +2030,11 @@ else if (originalPropertyDescriptor.getContainingDeclaration() == backingFieldCo
fieldName = KotlinTypeMapper.mapDefaultFieldName(propertyDescriptor, isDelegatedProperty);
}

return StackValue.property(propertyDescriptor, backingFieldOwner,
typeMapper.mapType(
isDelegatedProperty && forceField ? delegateType : propertyDescriptor.getOriginal().getType()),
isStaticBackingField, fieldName, callableGetter, callableSetter, receiver, this, resolvedCall);
return StackValue.property(
propertyDescriptor, backingFieldOwner,
typeMapper.mapType(isDelegatedProperty && forceField ? delegateType : propertyDescriptor.getOriginal().getType()),
isStaticBackingField, fieldName, callableGetter, callableSetter, receiver, this, resolvedCall, skipLateinitAssertion
);
}

@NotNull
@@ -2022,7 +2048,8 @@ else if (originalPropertyDescriptor.getContainingDeclaration() == backingFieldCo
FunctionDescriptor setMethod = propertyDescriptor.getSetMethod();
CallableMethod callableSetter =
setMethod != null ? typeMapper.mapToCallableMethod(context.accessibleDescriptor(setMethod, null), false) : null;
return StackValue.property(propertyDescriptor, null, type, false, null, callableGetter, callableSetter, receiver, this, null);
return StackValue.property(propertyDescriptor, null, type, false, null, callableGetter, callableSetter, receiver, this,
null, false);
}

@Override
@@ -2226,7 +2253,9 @@ public void invokeMethodWithArguments(
) {
boolean isSuspendCall = CoroutineCodegenUtilKt.isSuspendNoInlineCall(resolvedCall);
boolean isConstructor = resolvedCall.getResultingDescriptor() instanceof ConstructorDescriptor;
putReceiverAndInlineMarkerIfNeeded(callableMethod, resolvedCall, receiver, isSuspendCall, isConstructor);
if (!(callableMethod instanceof IntrinsicWithSpecialReceiver)) {
putReceiverAndInlineMarkerIfNeeded(callableMethod, resolvedCall, receiver, isSuspendCall, isConstructor);
}

callGenerator.processAndPutHiddenParameters(false);

@@ -2356,12 +2385,15 @@ private CallGenerator getOrCreateCallGenerator(
) {
if (callElement == null) return defaultCallGenerator;

boolean isIntrinsic = descriptor instanceof CallableMemberDescriptor &&
state.getIntrinsics().getIntrinsic((CallableMemberDescriptor) descriptor) != null;

boolean isInline = (InlineUtil.isInline(descriptor) && !isIntrinsic) || InlineUtil.isArrayConstructorWithLambda(descriptor);

// We should inline callable containing reified type parameters even if inline is disabled
// because they may contain something to reify and straight call will probably fail at runtime
boolean isInline = (!state.isInlineDisabled() || InlineUtil.containsReifiedTypeParameters(descriptor)) &&
(InlineUtil.isInline(descriptor) || InlineUtil.isArrayConstructorWithLambda(descriptor));

if (!isInline) return defaultCallGenerator;
boolean shouldInline = isInline && (!state.isInlineDisabled() || InlineUtil.containsReifiedTypeParameters(descriptor));
if (!shouldInline) return defaultCallGenerator;

FunctionDescriptor original =
CoroutineCodegenUtilKt.getOriginalSuspendFunctionView(
@@ -3606,13 +3638,8 @@ private void initializeDestructuringDeclarationVariables(

if (asProperty && variableDescriptor instanceof PropertyDescriptor) {
StackValue.Property propertyValue = intermediateValueForProperty(
(PropertyDescriptor) variableDescriptor,
true,
false,
null,
true,
StackValue.LOCAL_0,
null);
(PropertyDescriptor) variableDescriptor, true, false, null, true, StackValue.LOCAL_0, null, false
);

propertyValue.store(invokeFunction(call, resolvedCall, receiverStackValue), v);
}
@@ -835,7 +835,9 @@ public static void endVisit(MethodVisitor mv, @Nullable String description, @Nul
}
}

private static String renderByteCodeIfAvailable(MethodVisitor mv) {
@SuppressWarnings("WeakerAccess") // Useful in debug
@Nullable
public static String renderByteCodeIfAvailable(@NotNull MethodVisitor mv) {
String bytecode = null;

if (mv instanceof TransformationMethodVisitor) {
@@ -522,7 +522,8 @@ public void initializeProperty(@NotNull ExpressionCodegen codegen, @NotNull KtPr
assert initializer != null : "shouldInitializeProperty must return false if initializer is null";

StackValue.Property propValue = codegen.intermediateValueForProperty(
propertyDescriptor, true, false, null, true, StackValue.LOCAL_0, null);
propertyDescriptor, true, false, null, true, StackValue.LOCAL_0, null, false
);

ResolvedCall<FunctionDescriptor> provideDelegateResolvedCall = bindingContext.get(PROVIDE_DELEGATE_RESOLVED_CALL, propertyDescriptor);
if (provideDelegateResolvedCall == null) {
@@ -743,17 +744,18 @@ private PropertyAccessorStrategy(@NotNull PropertyAccessorDescriptor callableDes

@Override
public void doGenerateBody(@NotNull ExpressionCodegen codegen, @NotNull JvmMethodSignature signature) {
boolean syntheticBackingField =
accessor instanceof AccessorForPropertyBackingField &&
((AccessorForPropertyBackingField) accessor).getFieldAccessorKind() == FieldAccessorKind.FIELD_FROM_LOCAL;
FieldAccessorKind fieldAccessorKind = accessor instanceof AccessorForPropertyBackingField
? ((AccessorForPropertyBackingField) accessor).getFieldAccessorKind() : null;
boolean syntheticBackingField = fieldAccessorKind == FieldAccessorKind.FIELD_FROM_LOCAL;
boolean forceFieldForCompanionProperty = JvmAbi.isPropertyWithBackingFieldInOuterClass(original) &&
!isCompanionObject(accessor.getContainingDeclaration());
boolean forceField = forceFieldForCompanionProperty ||
syntheticBackingField ||
original.getVisibility() == JavaVisibilities.PROTECTED_STATIC_VISIBILITY;
StackValue property = codegen.intermediateValueForProperty(
original, forceField, syntheticBackingField, accessor.getSuperCallTarget(),
forceFieldForCompanionProperty, StackValue.none(), null
forceFieldForCompanionProperty, StackValue.none(), null,
fieldAccessorKind == FieldAccessorKind.LATEINIT_INTRINSIC
);

InstructionAdapter iv = codegen.v;
@@ -306,9 +306,11 @@ public static Property property(
@Nullable CallableMethod setter,
@NotNull StackValue receiver,
@NotNull ExpressionCodegen codegen,
@Nullable ResolvedCall resolvedCall
@Nullable ResolvedCall resolvedCall,
boolean skipLateinitAssertion
) {
return new Property(descriptor, backingFieldOwner, getter, setter, isStaticBackingField, fieldName, type, receiver, codegen, resolvedCall);
return new Property(descriptor, backingFieldOwner, getter, setter, isStaticBackingField, fieldName, type, receiver, codegen,
resolvedCall, skipLateinitAssertion);
}

@NotNull
@@ -1187,20 +1189,17 @@ protected StackValueWithSimpleReceiver changeReceiver(@NotNull StackValue newRec
private final CallableMethod getter;
private final CallableMethod setter;
private final Type backingFieldOwner;

private final PropertyDescriptor descriptor;

private final String fieldName;
@NotNull private final ExpressionCodegen codegen;
@Nullable private final ResolvedCall resolvedCall;
private final ExpressionCodegen codegen;
private final ResolvedCall resolvedCall;
private final boolean skipLateinitAssertion;

public Property(
@NotNull PropertyDescriptor descriptor, @Nullable Type backingFieldOwner,
@Nullable CallableMethod getter, @Nullable CallableMethod setter, boolean isStaticBackingField,
@Nullable String fieldName, @NotNull Type type,
@NotNull StackValue receiver,
@NotNull ExpressionCodegen codegen,
@Nullable ResolvedCall resolvedCall
@NotNull PropertyDescriptor descriptor, @Nullable Type backingFieldOwner, @Nullable CallableMethod getter,
@Nullable CallableMethod setter, boolean isStaticBackingField, @Nullable String fieldName, @NotNull Type type,
@NotNull StackValue receiver, @NotNull ExpressionCodegen codegen, @Nullable ResolvedCall resolvedCall,
boolean skipLateinitAssertion
) {
super(type, isStatic(isStaticBackingField, getter), isStatic(isStaticBackingField, setter), receiver, true);
this.backingFieldOwner = backingFieldOwner;
@@ -1210,6 +1209,7 @@ public Property(
this.fieldName = fieldName;
this.codegen = codegen;
this.resolvedCall = resolvedCall;
this.skipLateinitAssertion = skipLateinitAssertion;
}

@Override
@@ -1221,7 +1221,9 @@ public void putSelector(@NotNull Type type, @NotNull InstructionAdapter v) {

v.visitFieldInsn(isStaticPut ? GETSTATIC : GETFIELD,
backingFieldOwner.getInternalName(), fieldName, this.type.getDescriptor());
genNotNullAssertionForLateInitIfNeeded(v);
if (!skipLateinitAssertion) {
genNotNullAssertionForLateInitIfNeeded(v);
}
coerceTo(type, v);
}
else {
@@ -109,3 +109,8 @@ fun createIntrinsicCallable(
): IntrinsicCallable {
return IntrinsicCallable(callable, invoke)
}

/**
* A marker interface that signifies that this [IntrinsicCallable] instance generates the receiver of the intrinsic function call itself.
*/
interface IntrinsicWithSpecialReceiver : Callable
@@ -22,7 +22,6 @@
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.builtins.PrimitiveType;
import org.jetbrains.kotlin.codegen.state.GenerationState;
import org.jetbrains.kotlin.config.JvmTarget;
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor;
import org.jetbrains.kotlin.name.FqName;
@@ -70,6 +69,8 @@ public IntrinsicMethods(JvmTarget jvmTarget) {
intrinsicsMap.registerIntrinsic(new FqName("kotlin.jvm.internal.unsafe"), null, "monitorExit", 1, MonitorInstruction.MONITOR_EXIT);
intrinsicsMap.registerIntrinsic(KOTLIN_JVM, KotlinBuiltIns.FQ_NAMES.array, "isArrayOf", 0, new IsArrayOf());

intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, KotlinBuiltIns.FQ_NAMES.kProperty0, "isInitialized", -1, LateinitIsInitialized.INSTANCE);

intrinsicsMap.registerIntrinsic(BUILT_INS_PACKAGE_FQ_NAME, null, "arrayOf", 1, new ArrayOf());

ImmutableList<Name> primitiveCastMethods = OperatorConventions.NUMBER_CONVERSIONS.asList();
Oops, something went wrong.

0 comments on commit c6263ac

Please sign in to comment.