From ac508a510eaa2f1a3e5949c45d78a0fe7bed2f41 Mon Sep 17 00:00:00 2001 From: Anton Bannykh Date: Fri, 15 Sep 2017 15:36:30 +0300 Subject: [PATCH] JS: Support isInitialized intrisic for lateinit properties --- .../innerSubclass.kt | 2 +- .../sideEffects.kt | 2 +- .../topLevelProperty.kt | 2 +- .../ast/metadata/metadataProperties.kt | 2 + .../js/test/semantics/BoxJsTestGenerated.java | 6 +++ .../semantics/JsCodegenBoxTestGenerated.java | 18 ++++++++ .../functions/factories/TopLevelFIF.java | 41 ++++++++++++++++++- .../reference/CallableReferenceTranslator.kt | 10 ++--- .../js/translate/utils/JsDescriptorUtils.java | 6 +-- .../js/translate/utils/TranslationUtils.java | 22 ++++++---- .../simpleLateInitIsInitialized.kt | 30 ++++++++++++++ libraries/stdlib/src/kotlin/util/Lateinit.kt | 1 - 12 files changed, 119 insertions(+), 23 deletions(-) create mode 100644 js/js.translator/testData/box/propertyAccess/simpleLateInitIsInitialized.kt diff --git a/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt index 78947e1a378..691f6277775 100644 --- a/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt +++ b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/innerSubclass.kt @@ -1,4 +1,4 @@ -// TARGET_BACKEND: JVM +// IGNORE_BACKEND: NATIVE // LANGUAGE_VERSION: 1.2 // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/sideEffects.kt b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/sideEffects.kt index e6d1e9f8a4c..54d75bcd3b3 100644 --- a/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/sideEffects.kt +++ b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/sideEffects.kt @@ -1,4 +1,4 @@ -// TARGET_BACKEND: JVM +// IGNORE_BACKEND: NATIVE // LANGUAGE_VERSION: 1.2 // WITH_RUNTIME diff --git a/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/topLevelProperty.kt b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/topLevelProperty.kt index 4be6e0b48bf..fc4e774df7d 100644 --- a/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/topLevelProperty.kt +++ b/compiler/testData/codegen/box/properties/lateinit/isInitializedAndDeinitialize/topLevelProperty.kt @@ -1,4 +1,4 @@ -// TARGET_BACKEND: JVM +// IGNORE_BACKEND: NATIVE // LANGUAGE_VERSION: 1.2 // WITH_RUNTIME diff --git a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt index 718196510b7..53cf010ebab 100644 --- a/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt +++ b/js/js.ast/src/org/jetbrains/kotlin/js/backend/ast/metadata/metadataProperties.kt @@ -41,6 +41,8 @@ var JsInvocation.inlineStrategy: InlineStrategy? by MetadataProperty(default = n 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.psiElement: PsiElement? by MetadataProperty(default = null) diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index e48dcbc4952..21e087ae36a 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -7193,6 +7193,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { 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") public void testStaticAccessorsWithJsName() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/propertyAccess/staticAccessorsWithJsName.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index f35b18b2181..dc65021acb4 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -15245,6 +15245,24 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { public void testAllFilesPresentInIsInitializedAndDeinitialize() throws Exception { 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") diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java index 487763c4dd8..c840a5403e5 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/intrinsic/functions/factories/TopLevelFIF.java @@ -20,10 +20,13 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.descriptors.CallableDescriptor; import org.jetbrains.kotlin.descriptors.FunctionDescriptor; +import org.jetbrains.kotlin.descriptors.PropertyDescriptor; import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor; import org.jetbrains.kotlin.js.backend.ast.JsExpression; 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.metadata.MetadataProperties; import org.jetbrains.kotlin.js.patterns.DescriptorPredicate; import org.jetbrains.kotlin.js.patterns.NamePredicate; 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.FunctionIntrinsicWithReceiverComputed; 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.UtilsKt; +import org.jetbrains.kotlin.psi.KtCallableReferenceExpression; +import org.jetbrains.kotlin.psi.KtPsiUtil; 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.scopes.receivers.ExpressionReceiver; import org.jetbrains.kotlin.types.KotlinType; 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 arguments, + @NotNull TranslationContext context + ) { + ResolvedCall call = callInfo.getResolvedCall(); + ExpressionReceiver receiver = (ExpressionReceiver) call.getExtensionReceiver(); + + KtCallableReferenceExpression expression = + (KtCallableReferenceExpression) KtPsiUtil.safeDeparenthesize(receiver.getExpression()); + ResolvedCall 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() { @NotNull @Override public JsExpression apply( @Nullable JsExpression receiver, - @NotNull List arguments, - @NotNull TranslationContext context + @NotNull List arguments, + @NotNull TranslationContext context ) { 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", "enumValueOf"), ENUM_VALUE_OF_INTRINSIC); + + add(pattern("kotlin", "").isExtensionOf("kotlin.reflect.KProperty0"), + LATEINIT_KPROPERTY_ISINITIALIZED_INTRINSIC); } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt index 33477cdd581..d774b9ac10c 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/reference/CallableReferenceTranslator.kt @@ -19,10 +19,7 @@ package org.jetbrains.kotlin.js.translate.reference import org.jetbrains.kotlin.backend.common.CodegenUtil import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.js.backend.ast.* -import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind -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.backend.ast.metadata.* import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator import org.jetbrains.kotlin.js.translate.context.Namer 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)) val rawCallableRef = bindIfNecessary(function, receiver) - return context.wrapFunctionCallableRef(expression.callableReference.getReferencedName(), rawCallableRef) + return context.wrapFunctionCallableRef(receiver, expression.callableReference.getReferencedName(), rawCallableRef) } private fun translateForProperty( @@ -243,10 +240,12 @@ object CallableReferenceTranslator { if (setter != null) { invocation.arguments += setter } + invocation.callableReferenceReceiver = receiver return invocation } private fun TranslationContext.wrapFunctionCallableRef( + receiver: JsExpression?, name: String, function: JsExpression ): JsExpression { @@ -256,6 +255,7 @@ object CallableReferenceTranslator { val invocation = JsInvocation(invokeFun, nameLiteral, function) invocation.isCallableReference = true invocation.sideEffects = SideEffectKind.PURE + invocation.callableReferenceReceiver = receiver return invocation } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsDescriptorUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsDescriptorUtils.java index 142ce065aa2..8e11fc61653 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsDescriptorUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/JsDescriptorUtils.java @@ -170,12 +170,12 @@ public final class JsDescriptorUtils { } @Nullable - private static FunctionDescriptor findRealDeclaration(@NotNull FunctionDescriptor descriptor) { + public static CallableMemberDescriptor findRealDeclaration(@NotNull CallableMemberDescriptor descriptor) { if (descriptor.getModality() == Modality.ABSTRACT) return null; if (descriptor.getKind().isReal()) return descriptor; - for (FunctionDescriptor o : descriptor.getOverriddenDescriptors()) { - FunctionDescriptor child = findRealDeclaration(o); + for (CallableMemberDescriptor o : descriptor.getOverriddenDescriptors()) { + CallableMemberDescriptor child = findRealDeclaration(o); if (child != null) { return child; } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java index 3cfa71d1a67..f8723abd65e 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/TranslationUtils.java @@ -55,9 +55,7 @@ import java.util.stream.Collectors; 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.JsAstUtils.assignment; -import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.createDataDescriptor; -import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn; +import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.*; public final class TranslationUtils { @@ -173,16 +171,22 @@ public final class TranslationUtils { } @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(); - JsName backingFieldName = containingDescriptor instanceof PackageFragmentDescriptor ? - context.getInnerNameForDescriptor(descriptor) : - context.getNameForDescriptor(descriptor); 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; if (containingDescriptor instanceof PackageFragmentDescriptor) { receiver = null; @@ -191,7 +195,7 @@ public final class TranslationUtils { 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)); return result; diff --git a/js/js.translator/testData/box/propertyAccess/simpleLateInitIsInitialized.kt b/js/js.translator/testData/box/propertyAccess/simpleLateInitIsInitialized.kt new file mode 100644 index 00000000000..2743597367e --- /dev/null +++ b/js/js.translator/testData/box/propertyAccess/simpleLateInitIsInitialized.kt @@ -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() +} diff --git a/libraries/stdlib/src/kotlin/util/Lateinit.kt b/libraries/stdlib/src/kotlin/util/Lateinit.kt index a7f9602b3a6..dddbac3db3d 100644 --- a/libraries/stdlib/src/kotlin/util/Lateinit.kt +++ b/libraries/stdlib/src/kotlin/util/Lateinit.kt @@ -1,5 +1,4 @@ @file:kotlin.jvm.JvmName("LateinitKt") -@file:kotlin.jvm.JvmVersion @file:Suppress("unused") package kotlin