JS: cache PropertyMetadata objects in property delegates
Also, remove unused PropertyMetadata instances See KT-20737, KT-20738
This commit is contained in:
@@ -1352,6 +1352,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("metadataReferentialEquality.kt")
|
||||
public void testMetadataReferentialEquality() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/delegateProperty/metadataReferentialEquality.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("peculiarName.kt")
|
||||
public void testPeculiarName() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/delegateProperty/peculiarName.kt");
|
||||
@@ -1388,6 +1394,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("unusedPropertyMetadata.kt")
|
||||
public void testUnusedPropertyMetadata() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/delegateProperty/unusedPropertyMetadata.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("withGenerics.kt")
|
||||
public void testWithGenerics() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/delegateProperty/withGenerics.kt");
|
||||
|
||||
@@ -146,6 +146,8 @@ public final class StaticContext {
|
||||
@NotNull
|
||||
private final SourceFilePathResolver sourceFilePathResolver;
|
||||
|
||||
private final Map<VariableDescriptorWithAccessors, JsName> propertyMetadataVariables = new HashMap<>();
|
||||
|
||||
private final boolean isStdlib;
|
||||
|
||||
private static final Set<String> BUILTIN_JS_PROPERTIES = Sets.union(
|
||||
@@ -890,4 +892,23 @@ public final class StaticContext {
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsName getVariableForPropertyMetadata(@NotNull VariableDescriptorWithAccessors property) {
|
||||
return propertyMetadataVariables.computeIfAbsent(property, p -> {
|
||||
String id = getSuggestedName(property) + "_metadata";
|
||||
JsName name = JsScope.declareTemporaryName(NameSuggestion.sanitizeName(id));
|
||||
|
||||
// Unexpectedly! However, the only thing, for which 'imported' property is relevant, is a import clener.
|
||||
// We want similar cleanup to be performed for unused MetadataProperty instances.
|
||||
// TODO: consider a different name for 'imported' property
|
||||
MetadataProperties.setImported(name, true);
|
||||
|
||||
JsStringLiteral propertyNameLiteral = new JsStringLiteral(property.getName().asString());
|
||||
JsExpression construction = new JsNew(getReferenceToIntrinsic("PropertyMetadata"),
|
||||
Collections.singletonList(propertyNameLiteral));
|
||||
fragment.getDeclarationBlock().getStatements().add(JsAstUtils.newVar(name, construction));
|
||||
return name;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -911,4 +911,9 @@ public class TranslationContext {
|
||||
public SourceFilePathResolver getSourceFilePathResolver() {
|
||||
return staticContext.getSourceFilePathResolver();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public JsName getVariableForPropertyMetadata(@NotNull VariableDescriptorWithAccessors property) {
|
||||
return staticContext.getVariableForPropertyMetadata(property);
|
||||
}
|
||||
}
|
||||
|
||||
+4
-2
@@ -20,6 +20,7 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableAccessorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor
|
||||
import org.jetbrains.kotlin.js.backend.ast.*
|
||||
import org.jetbrains.kotlin.js.backend.ast.metadata.synthetic
|
||||
import org.jetbrains.kotlin.js.translate.callTranslator.CallTranslator
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer
|
||||
import org.jetbrains.kotlin.js.translate.context.Namer.getReceiverParameterName
|
||||
@@ -29,6 +30,7 @@ import org.jetbrains.kotlin.js.translate.expression.translateFunction
|
||||
import org.jetbrains.kotlin.js.translate.general.AbstractTranslator
|
||||
import org.jetbrains.kotlin.js.translate.general.Translation
|
||||
import org.jetbrains.kotlin.js.translate.utils.BindingUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn
|
||||
import org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils
|
||||
import org.jetbrains.kotlin.js.translate.utils.TranslationUtils.*
|
||||
@@ -187,15 +189,15 @@ fun TranslationContext.contextWithPropertyMetadataCreationIntrinsified(
|
||||
property: VariableDescriptorWithAccessors,
|
||||
host: JsExpression
|
||||
): TranslationContext {
|
||||
val propertyNameLiteral = JsStringLiteral(property.name.asString())
|
||||
// 0th argument is instance, 1st is KProperty, 2nd (for setter) is value
|
||||
val hostExpression =
|
||||
(delegatedCall.valueArgumentsByIndex!![0] as ExpressionValueArgument).valueArgument!!.getArgumentExpression()
|
||||
val fakeArgumentExpression =
|
||||
(delegatedCall.valueArgumentsByIndex!![1] as ExpressionValueArgument).valueArgument!!.getArgumentExpression()
|
||||
val metadataRef = pureFqn(getVariableForPropertyMetadata(property), null).apply { synthetic = true }
|
||||
return innerContextWithAliasesForExpressions(mapOf(
|
||||
hostExpression to host,
|
||||
fakeArgumentExpression to JsNew(getReferenceToIntrinsic("PropertyMetadata"), listOf(propertyNameLiteral))
|
||||
fakeArgumentExpression to metadataRef
|
||||
))
|
||||
}
|
||||
|
||||
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1144
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
var lastGeneratedId = 0
|
||||
val idMap = mutableMapOf<KProperty<*>, Int>()
|
||||
|
||||
class MyDelegate(val value: String) {
|
||||
operator fun getValue(receiver: Any?, property: KProperty<*>): String {
|
||||
val id = idMap.getOrPut(property) { lastGeneratedId++ }
|
||||
return "${property.name}:$value:$id"
|
||||
}
|
||||
}
|
||||
|
||||
class C {
|
||||
val foo by MyDelegate("C")
|
||||
}
|
||||
|
||||
val bar by MyDelegate("toplevel")
|
||||
|
||||
fun box(): String {
|
||||
val c = C()
|
||||
|
||||
var a = c.foo
|
||||
var b = c.foo
|
||||
if (a !== b) return "fail: member property referential equality"
|
||||
if (!a.startsWith("foo:C:")) return "fail: member property value"
|
||||
|
||||
a = bar
|
||||
b = bar
|
||||
if (a !== b) return "fail: top level property referential equality"
|
||||
if (!a.startsWith("bar:toplevel:")) return "fail: top level property value"
|
||||
|
||||
val baz by MyDelegate("local")
|
||||
a = baz
|
||||
b = baz
|
||||
if (a !== b) return "fail: local property referential equality"
|
||||
if (!a.startsWith("baz:local:")) return "fail: local property value"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// EXPECTED_REACHABLE_NODES: 1131
|
||||
// PROPERTY_NOT_USED: PropertyMetadata
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
class MyDelegate(val value: String) {
|
||||
inline operator fun getValue(receiver: Any?, property: KProperty<*>): String = value
|
||||
}
|
||||
|
||||
fun box(): String {
|
||||
val x by MyDelegate("OK")
|
||||
return x
|
||||
}
|
||||
@@ -13,4 +13,4 @@ class Delegate(val f: () -> Int) {
|
||||
}
|
||||
}
|
||||
|
||||
// LINES: 1 2 3 2 2 3 3 3 * 8 7 7 10 10 13 12 12
|
||||
// LINES: 1 2 3 * 2 2 3 3 3 * 8 7 7 10 10 13 12 12
|
||||
|
||||
@@ -15,4 +15,4 @@ class B {
|
||||
}
|
||||
}
|
||||
|
||||
// LINES: 3 4 5 4 4 4 2 * 8 11 10 10 15 14 14
|
||||
// LINES: 3 4 5 * 4 4 4 2 * 8 11 10 10 15 14 14
|
||||
Reference in New Issue
Block a user