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
// WITH_RUNTIME
@@ -1,4 +1,4 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: NATIVE
// LANGUAGE_VERSION: 1.2
// WITH_RUNTIME
@@ -1,4 +1,4 @@
// TARGET_BACKEND: JVM
// IGNORE_BACKEND: NATIVE
// LANGUAGE_VERSION: 1.2
// WITH_RUNTIME
@@ -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)
@@ -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");
@@ -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")
@@ -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<? 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() {
@NotNull
@Override
public JsExpression apply(
@Nullable JsExpression receiver,
@NotNull List<? extends JsExpression> arguments,
@NotNull TranslationContext context
@NotNull List<? extends JsExpression> 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", "<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.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
}
}
@@ -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;
}
@@ -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;
@@ -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.JvmVersion
@file:Suppress("unused")
package kotlin