JS: Support isInitialized intrisic for lateinit properties

This commit is contained in:
Anton Bannykh
2017-09-15 15:36:30 +03:00
committed by Ilya Gorbunov
parent 5cbcbe4a9c
commit ac508a510e
12 changed files with 119 additions and 23 deletions
@@ -1,4 +1,4 @@
// TARGET_BACKEND: JVM // IGNORE_BACKEND: NATIVE
// LANGUAGE_VERSION: 1.2 // LANGUAGE_VERSION: 1.2
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,4 +1,4 @@
// TARGET_BACKEND: JVM // IGNORE_BACKEND: NATIVE
// LANGUAGE_VERSION: 1.2 // LANGUAGE_VERSION: 1.2
// WITH_RUNTIME // WITH_RUNTIME
@@ -1,4 +1,4 @@
// TARGET_BACKEND: JVM // IGNORE_BACKEND: NATIVE
// LANGUAGE_VERSION: 1.2 // LANGUAGE_VERSION: 1.2
// WITH_RUNTIME // WITH_RUNTIME
@@ -41,6 +41,8 @@ var JsInvocation.inlineStrategy: InlineStrategy? by MetadataProperty(default = n
var JsInvocation.isCallableReference by MetadataProperty(default = false) var JsInvocation.isCallableReference by MetadataProperty(default = false)
var JsInvocation.callableReferenceReceiver: JsExpression? by MetadataProperty(default = null)
var JsInvocation.descriptor: CallableDescriptor? by MetadataProperty(default = null) var JsInvocation.descriptor: CallableDescriptor? by MetadataProperty(default = null)
var JsInvocation.psiElement: PsiElement? by MetadataProperty(default = null) var JsInvocation.psiElement: PsiElement? by MetadataProperty(default = null)
@@ -7193,6 +7193,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("simpleLateInitIsInitialized.kt")
public void testSimpleLateInitIsInitialized() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/propertyAccess/simpleLateInitIsInitialized.kt");
doTest(fileName);
}
@TestMetadata("staticAccessorsWithJsName.kt") @TestMetadata("staticAccessorsWithJsName.kt")
public void testStaticAccessorsWithJsName() throws Exception { public void testStaticAccessorsWithJsName() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/propertyAccess/staticAccessorsWithJsName.kt"); String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/propertyAccess/staticAccessorsWithJsName.kt");
@@ -15245,6 +15245,24 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest {
public void testAllFilesPresentInIsInitializedAndDeinitialize() throws Exception { public void testAllFilesPresentInIsInitializedAndDeinitialize() throws Exception {
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true); KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JS, true);
} }
@TestMetadata("innerSubclass.kt")
public void testInnerSubclass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt");
doTest(fileName);
}
@TestMetadata("sideEffects.kt")
public void testSideEffects() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/sideEffects.kt");
doTest(fileName);
}
@TestMetadata("topLevelProperty.kt")
public void testTopLevelProperty() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/topLevelProperty.kt");
doTest(fileName);
}
} }
@TestMetadata("compiler/testData/codegen/box/properties/lateinit/local") @TestMetadata("compiler/testData/codegen/box/properties/lateinit/local")
@@ -20,10 +20,13 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.CallableDescriptor;
import org.jetbrains.kotlin.descriptors.FunctionDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor;
import org.jetbrains.kotlin.descriptors.PropertyDescriptor;
import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor; import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor;
import org.jetbrains.kotlin.js.backend.ast.JsExpression; import org.jetbrains.kotlin.js.backend.ast.JsExpression;
import org.jetbrains.kotlin.js.backend.ast.JsInvocation; import org.jetbrains.kotlin.js.backend.ast.JsInvocation;
import org.jetbrains.kotlin.js.backend.ast.JsName;
import org.jetbrains.kotlin.js.backend.ast.JsNameRef; import org.jetbrains.kotlin.js.backend.ast.JsNameRef;
import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties;
import org.jetbrains.kotlin.js.patterns.DescriptorPredicate; import org.jetbrains.kotlin.js.patterns.DescriptorPredicate;
import org.jetbrains.kotlin.js.patterns.NamePredicate; import org.jetbrains.kotlin.js.patterns.NamePredicate;
import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo; import org.jetbrains.kotlin.js.translate.callTranslator.CallInfo;
@@ -32,10 +35,15 @@ import org.jetbrains.kotlin.js.translate.context.TranslationContext;
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic; import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsic;
import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed; import org.jetbrains.kotlin.js.translate.intrinsic.functions.basic.FunctionIntrinsicWithReceiverComputed;
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils; import org.jetbrains.kotlin.js.translate.utils.JsAstUtils;
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils;
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils; import org.jetbrains.kotlin.js.translate.utils.TranslationUtils;
import org.jetbrains.kotlin.js.translate.utils.UtilsKt; import org.jetbrains.kotlin.js.translate.utils.UtilsKt;
import org.jetbrains.kotlin.psi.KtCallableReferenceExpression;
import org.jetbrains.kotlin.psi.KtPsiUtil;
import org.jetbrains.kotlin.resolve.DescriptorFactory; import org.jetbrains.kotlin.resolve.DescriptorFactory;
import org.jetbrains.kotlin.resolve.calls.callUtil.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall; import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.types.KotlinType; import org.jetbrains.kotlin.types.KotlinType;
import java.util.List; import java.util.List;
@@ -150,13 +158,39 @@ public final class TopLevelFIF extends CompositeFIF {
} }
}; };
private static final FunctionIntrinsic LATEINIT_KPROPERTY_ISINITIALIZED_INTRINSIC = new FunctionIntrinsic() {
@NotNull
@Override
public JsExpression apply(
@NotNull CallInfo callInfo,
@NotNull List<? extends JsExpression> arguments,
@NotNull TranslationContext context
) {
ResolvedCall<? extends CallableDescriptor> call = callInfo.getResolvedCall();
ExpressionReceiver receiver = (ExpressionReceiver) call.getExtensionReceiver();
KtCallableReferenceExpression expression =
(KtCallableReferenceExpression) KtPsiUtil.safeDeparenthesize(receiver.getExpression());
ResolvedCall<? extends CallableDescriptor> referencedProperty =
CallUtilKt.getResolvedCall(expression.getCallableReference(), context.bindingContext());
PropertyDescriptor propertyDescriptor = (PropertyDescriptor) referencedProperty.getResultingDescriptor();
JsExpression receiverExpression =
MetadataProperties.getCallableReferenceReceiver((JsInvocation) callInfo.getExtensionReceiver());
JsName backingFieldName = TranslationUtils.getNameForBackingField(
context, (PropertyDescriptor) JsDescriptorUtils.findRealDeclaration(propertyDescriptor));
JsNameRef backingFieldRef = new JsNameRef(backingFieldName, receiverExpression);
return TranslationUtils.nullCheck(backingFieldRef, true);
}
};
private static final FunctionIntrinsic STRING_SUBSTRING = new FunctionIntrinsicWithReceiverComputed() { private static final FunctionIntrinsic STRING_SUBSTRING = new FunctionIntrinsicWithReceiverComputed() {
@NotNull @NotNull
@Override @Override
public JsExpression apply( public JsExpression apply(
@Nullable JsExpression receiver, @Nullable JsExpression receiver,
@NotNull List<? extends JsExpression> arguments, @NotNull List<? extends JsExpression> arguments,
@NotNull TranslationContext context @NotNull TranslationContext context
) { ) {
return new JsInvocation(new JsNameRef("substring", receiver), arguments); return new JsInvocation(new JsNameRef("substring", receiver), arguments);
} }
@@ -202,6 +236,9 @@ public final class TopLevelFIF extends CompositeFIF {
add(pattern("kotlin", "enumValues"), ENUM_VALUES_INTRINSIC); add(pattern("kotlin", "enumValues"), ENUM_VALUES_INTRINSIC);
add(pattern("kotlin", "enumValueOf"), ENUM_VALUE_OF_INTRINSIC); add(pattern("kotlin", "enumValueOf"), ENUM_VALUE_OF_INTRINSIC);
add(pattern("kotlin", "<get-isInitialized>").isExtensionOf("kotlin.reflect.KProperty0"),
LATEINIT_KPROPERTY_ISINITIALIZED_INTRINSIC);
} }
} }
@@ -19,10 +19,7 @@ package org.jetbrains.kotlin.js.translate.reference
import org.jetbrains.kotlin.backend.common.CodegenUtil import org.jetbrains.kotlin.backend.common.CodegenUtil
import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.js.backend.ast.*
import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind import org.jetbrains.kotlin.js.backend.ast.metadata.*
import org.jetbrains.kotlin.js.backend.ast.metadata.isCallableReference
import org.jetbrains.kotlin.js.backend.ast.metadata.sideEffects
import org.jetbrains.kotlin.js.backend.ast.metadata.type
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator
import org.jetbrains.kotlin.js.translate.context.Namer import org.jetbrains.kotlin.js.translate.context.Namer
import org.jetbrains.kotlin.js.translate.context.TranslationContext import org.jetbrains.kotlin.js.translate.context.TranslationContext
@@ -135,7 +132,7 @@ object CallableReferenceTranslator {
function.body.statements += JsReturn(TranslationUtils.coerce(context, invocation, context.currentModule.builtIns.anyType)) function.body.statements += JsReturn(TranslationUtils.coerce(context, invocation, context.currentModule.builtIns.anyType))
val rawCallableRef = bindIfNecessary(function, receiver) val rawCallableRef = bindIfNecessary(function, receiver)
return context.wrapFunctionCallableRef(expression.callableReference.getReferencedName(), rawCallableRef) return context.wrapFunctionCallableRef(receiver, expression.callableReference.getReferencedName(), rawCallableRef)
} }
private fun translateForProperty( private fun translateForProperty(
@@ -243,10 +240,12 @@ object CallableReferenceTranslator {
if (setter != null) { if (setter != null) {
invocation.arguments += setter invocation.arguments += setter
} }
invocation.callableReferenceReceiver = receiver
return invocation return invocation
} }
private fun TranslationContext.wrapFunctionCallableRef( private fun TranslationContext.wrapFunctionCallableRef(
receiver: JsExpression?,
name: String, name: String,
function: JsExpression function: JsExpression
): JsExpression { ): JsExpression {
@@ -256,6 +255,7 @@ object CallableReferenceTranslator {
val invocation = JsInvocation(invokeFun, nameLiteral, function) val invocation = JsInvocation(invokeFun, nameLiteral, function)
invocation.isCallableReference = true invocation.isCallableReference = true
invocation.sideEffects = SideEffectKind.PURE invocation.sideEffects = SideEffectKind.PURE
invocation.callableReferenceReceiver = receiver
return invocation return invocation
} }
} }
@@ -170,12 +170,12 @@ public final class JsDescriptorUtils {
} }
@Nullable @Nullable
private static FunctionDescriptor findRealDeclaration(@NotNull FunctionDescriptor descriptor) { public static CallableMemberDescriptor findRealDeclaration(@NotNull CallableMemberDescriptor descriptor) {
if (descriptor.getModality() == Modality.ABSTRACT) return null; if (descriptor.getModality() == Modality.ABSTRACT) return null;
if (descriptor.getKind().isReal()) return descriptor; if (descriptor.getKind().isReal()) return descriptor;
for (FunctionDescriptor o : descriptor.getOverriddenDescriptors()) { for (CallableMemberDescriptor o : descriptor.getOverriddenDescriptors()) {
FunctionDescriptor child = findRealDeclaration(o); CallableMemberDescriptor child = findRealDeclaration(o);
if (child != null) { if (child != null) {
return child; return child;
} }
@@ -55,9 +55,7 @@ import java.util.stream.Collectors;
import static org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator.*; import static org.jetbrains.kotlin.js.backend.ast.JsBinaryOperator.*;
import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression; import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.assignment; import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.createDataDescriptor;
import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn;
public final class TranslationUtils { public final class TranslationUtils {
@@ -173,16 +171,22 @@ public final class TranslationUtils {
} }
@NotNull @NotNull
public static JsNameRef backingFieldReference(@NotNull TranslationContext context, @NotNull PropertyDescriptor descriptor) { public static JsName getNameForBackingField(@NotNull TranslationContext context, @NotNull PropertyDescriptor descriptor) {
DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration(); DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration();
JsName backingFieldName = containingDescriptor instanceof PackageFragmentDescriptor ?
context.getInnerNameForDescriptor(descriptor) :
context.getNameForDescriptor(descriptor);
if (!JsDescriptorUtils.isSimpleFinalProperty(descriptor) && !(containingDescriptor instanceof PackageFragmentDescriptor)) { if (!JsDescriptorUtils.isSimpleFinalProperty(descriptor) && !(containingDescriptor instanceof PackageFragmentDescriptor)) {
backingFieldName = context.getNameForBackingField(descriptor); return context.getNameForBackingField(descriptor);
} }
return containingDescriptor instanceof PackageFragmentDescriptor ?
context.getInnerNameForDescriptor(descriptor) :
context.getNameForDescriptor(descriptor);
}
@NotNull
public static JsNameRef backingFieldReference(@NotNull TranslationContext context, @NotNull PropertyDescriptor descriptor) {
DeclarationDescriptor containingDescriptor = descriptor.getContainingDeclaration();
JsExpression receiver; JsExpression receiver;
if (containingDescriptor instanceof PackageFragmentDescriptor) { if (containingDescriptor instanceof PackageFragmentDescriptor) {
receiver = null; receiver = null;
@@ -191,7 +195,7 @@ public final class TranslationUtils {
receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor)); receiver = context.getDispatchReceiver(JsDescriptorUtils.getReceiverParameterForDeclaration(containingDescriptor));
} }
JsNameRef result = new JsNameRef(backingFieldName, receiver); JsNameRef result = new JsNameRef(getNameForBackingField(context, descriptor), receiver);
MetadataProperties.setType(result, getReturnTypeForCoercion(descriptor)); MetadataProperties.setType(result, getReturnTypeForCoercion(descriptor));
return result; return result;
@@ -0,0 +1,30 @@
// EXPECTED_REACHABLE_NODES: 1035
// LANGUAGE_VERSION: 1.2
// WITH_RUNTIME
fun deinitialize(foo: dynamic) {
foo.bar = null
}
class Foo {
lateinit var bar: String
fun test(): String {
if (this::bar.isInitialized) return "Fail 1"
deinitialize(this)
if (this::bar.isInitialized) return "Fail 2"
bar = "A"
if (!this::bar.isInitialized) return "Fail 3"
deinitialize(this)
if (this::bar.isInitialized) return "Fail 4"
bar = "OK"
if (!this::bar.isInitialized) return "Fail 5"
return bar
}
}
fun box(): String {
return Foo().test()
}
@@ -1,5 +1,4 @@
@file:kotlin.jvm.JvmName("LateinitKt") @file:kotlin.jvm.JvmName("LateinitKt")
@file:kotlin.jvm.JvmVersion
@file:Suppress("unused") @file:Suppress("unused")
package kotlin package kotlin