From 2907eafa1800082044a1c5c8195c94c754e73309 Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Wed, 11 Jan 2017 12:17:32 +0300 Subject: [PATCH] JS: make functions called `call` to inline properly. See KT-7004 --- .../ast/metadata/metadataProperties.kt | 3 ++ .../kotlin/js/inline/util/invocationUtils.kt | 4 ++ .../js/test/semantics/BoxJsTestGenerated.java | 12 +++++ .../js/translate/context/StaticContext.java | 19 ++++--- .../js/translate/context/UsageTracker.kt | 3 +- .../utils/FunctionBodyTranslator.java | 1 + .../testData/box/inline/callFunction.kt | 54 +++++++++++++++++++ .../box/inlineMultiModule/callFunction.kt | 40 ++++++++++++++ 8 files changed, 129 insertions(+), 7 deletions(-) create mode 100644 js/js.translator/testData/box/inline/callFunction.kt create mode 100644 js/js.translator/testData/box/inlineMultiModule/callFunction.kt 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 0052189f977..6859d98dbe0 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 @@ -20,6 +20,7 @@ package org.jetbrains.kotlin.js.backend.ast.metadata import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.CallableDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.js.backend.ast.* import org.jetbrains.kotlin.resolve.inline.InlineStrategy @@ -27,6 +28,8 @@ import org.jetbrains.kotlin.types.KotlinType var JsName.staticRef: JsNode? by MetadataProperty(default = null) +var JsName.descriptor: DeclarationDescriptor? by MetadataProperty(default = null) + // TODO: move this to module 'js.inliner' and change dependency on 'frontend' to dependency on 'descriptors' var JsInvocation.inlineStrategy: InlineStrategy? by MetadataProperty(default = null) diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/invocationUtils.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/invocationUtils.kt index 8c2d03556f9..9bcb1e75709 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/invocationUtils.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/invocationUtils.kt @@ -17,8 +17,10 @@ package org.jetbrains.kotlin.js.inline.util import org.jetbrains.kotlin.js.backend.ast.* +import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor import org.jetbrains.kotlin.js.backend.ast.metadata.staticRef import org.jetbrains.kotlin.js.translate.context.Namer +import org.jetbrains.kotlin.js.translate.utils.name /** * Gets invocation qualifier name. @@ -66,6 +68,8 @@ fun isCallInvocation(invocation: JsInvocation): Boolean { val qualifier = invocation.qualifier as? JsNameRef val arguments = invocation.arguments + if (qualifier.name?.descriptor != null) return false + return qualifier?.ident == Namer.CALL_FUNCTION && arguments.isNotEmpty() } 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 eb22946e202..d892be88c82 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 @@ -3656,6 +3656,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("callFunction.kt") + public void testCallFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inline/callFunction.kt"); + doTest(fileName); + } + @TestMetadata("callInlineFunctionOnTopLevel.kt") public void testCallInlineFunctionOnTopLevel() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inline/callInlineFunctionOnTopLevel.kt"); @@ -4517,6 +4523,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("callFunction.kt") + public void testCallFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineMultiModule/callFunction.kt"); + doTest(fileName); + } + @TestMetadata("callableReference.kt") public void testCallableReference() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineMultiModule/callableReference.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java index fd5eb779bda..31bdeeca544 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/StaticContext.java @@ -18,9 +18,6 @@ package org.jetbrains.kotlin.js.translate.context; import com.google.common.collect.Lists; import com.google.common.collect.Maps; -import org.jetbrains.kotlin.js.backend.ast.*; -import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; -import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind; import com.intellij.openapi.util.Factory; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.LinkedHashMap; @@ -29,6 +26,9 @@ import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor; +import org.jetbrains.kotlin.js.backend.ast.*; +import org.jetbrains.kotlin.js.backend.ast.metadata.MetadataProperties; +import org.jetbrains.kotlin.js.backend.ast.metadata.SideEffectKind; import org.jetbrains.kotlin.js.config.JsConfig; import org.jetbrains.kotlin.js.naming.NameSuggestion; import org.jetbrains.kotlin.js.naming.SuggestedName; @@ -304,7 +304,9 @@ public final class StaticContext { @NotNull public JsName getNameForDescriptor(@NotNull DeclarationDescriptor descriptor) { if (descriptor instanceof ClassDescriptor && KotlinBuiltIns.isAny((ClassDescriptor) descriptor)) { - return rootScope.declareName("Object"); + JsName result = rootScope.declareName("Object"); + MetadataProperties.setDescriptor(result, descriptor); + return result; } SuggestedName suggested = nameSuggestion.suggest(descriptor); if (suggested == null) { @@ -360,7 +362,9 @@ public final class StaticContext { List names = new ArrayList(); if (suggested.getStable()) { for (String namePart : suggested.getNames()) { - names.add(scope.declareName(namePart)); + JsName name = scope.declareName(namePart); + MetadataProperties.setDescriptor(name, suggested.getDescriptor()); + names.add(name); } } else { @@ -382,6 +386,7 @@ public final class StaticContext { } } nameCache.put(suggested.getDescriptor(), name); + MetadataProperties.setDescriptor(name, suggested.getDescriptor()); names.add(name); } @@ -448,9 +453,11 @@ public final class StaticContext { @NotNull private JsName localOrImportedName(@NotNull DeclarationDescriptor descriptor, @NotNull String suggestedName) { ModuleDescriptor module = DescriptorUtilsKt.getModule(descriptor); - return module != currentModule ? + JsName name = module != currentModule ? importDeclaration(suggestedName, getQualifiedReference(descriptor)) : rootFunction.getScope().declareTemporaryName(suggestedName); + MetadataProperties.setDescriptor(name, descriptor); + return name; } private final class InnerNameGenerator extends Generator { diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt index 294b0e49817..86dd7450e02 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/UsageTracker.kt @@ -19,6 +19,7 @@ package org.jetbrains.kotlin.js.translate.context import org.jetbrains.kotlin.js.backend.ast.JsName import org.jetbrains.kotlin.js.backend.ast.JsScope import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.js.backend.ast.metadata.descriptor import org.jetbrains.kotlin.js.descriptorUtils.isCoroutineLambda import org.jetbrains.kotlin.js.naming.NameSuggestion import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -171,7 +172,7 @@ class UsageTracker( } } - return scope.declareTemporaryName(suggestedName) + return scope.declareTemporaryName(suggestedName).apply { descriptor = this@getJsNameForCapturedDescriptor } } } diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java index dc895be767c..b9ba0d36eba 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/FunctionBodyTranslator.java @@ -58,6 +58,7 @@ public final class FunctionBodyTranslator extends AbstractTranslator { for (FunctionDescriptor localFunction : functionCollector.getFunctions()) { String localIdent = localFunction.getName().isSpecial() ? "lambda" : localFunction.getName().asString(); JsName localName = functionBodyContext.scope().getParent().declareTemporaryName(NameSuggestion.sanitizeName(localIdent)); + MetadataProperties.setDescriptor(localName, localFunction); JsExpression alias = JsAstUtils.pureFqn(localName, null); aliases.put(localFunction, alias); } diff --git a/js/js.translator/testData/box/inline/callFunction.kt b/js/js.translator/testData/box/inline/callFunction.kt new file mode 100644 index 00000000000..d683adbc98d --- /dev/null +++ b/js/js.translator/testData/box/inline/callFunction.kt @@ -0,0 +1,54 @@ +// CHECK_CONTAINS_NO_CALLS: box except=equals;Baz_getInstance;callLocal;callLocalExtension +// CHECK_CONTAINS_NO_CALLS: callLocal +// CHECK_CONTAINS_NO_CALLS: callLocalExtension + +object Foo { + @JsName("call") + inline fun call(a: Int) = "Foo.call($a)" +} + +class Bar { + @JsName("call") + inline fun call(a: Int) = "Bar.call($a)" +} + + +inline fun call(a: Int) = "call($a)" + +fun callLocal(a: Int): String { + inline fun call(a: Int) = "callLocal($a)" + return call(a) +} + +object Baz + +inline fun Baz.call(a: Int) = "Baz.call($a)" + +class Boo + +fun callLocalExtension(a: Int): String { + inline fun Boo.call(a: Int) = "Boo.callLocal($a)" + return Boo().call(a) +} + +fun box(): String { + var result = call(1) + if (result != "call(1)") return "fail1: $result" + + result = Foo.call(2) + if (result != "Foo.call(2)") return "fail2: $result" + + result = Bar().call(3) + if (result != "Bar.call(3)") return "fail3: $result" + + result = Baz.call(4) + if (result != "Baz.call(4)") return "fail4: $result" + + result = callLocal(5) + if (result != "callLocal(5)") return "fail5: $result" + + result = callLocalExtension(6) + if (result != "Boo.callLocal(6)") return "fail6: $result" + + return "OK" +} \ No newline at end of file diff --git a/js/js.translator/testData/box/inlineMultiModule/callFunction.kt b/js/js.translator/testData/box/inlineMultiModule/callFunction.kt new file mode 100644 index 00000000000..e64a117a997 --- /dev/null +++ b/js/js.translator/testData/box/inlineMultiModule/callFunction.kt @@ -0,0 +1,40 @@ +// MODULE: lib +// FILE: lib.kt + +object Foo { + @JsName("call") + inline fun call(a: Int) = "Foo.call($a)" +} + +class Bar { + @JsName("call") + inline fun call(a: Int) = "Bar.call($a)" +} + + +inline fun call(a: Int) = "call($a)" + +object Baz + +inline fun Baz.call(a: Int) = "Baz.call($a)" + +// MODULE: main(lib) +// FILE: main.kt + +// CHECK_CONTAINS_NO_CALLS: box except=equals + +fun box(): String { + var result = call(1) + if (result != "call(1)") return "fail1: $result" + + result = Foo.call(2) + if (result != "Foo.call(2)") return "fail2: $result" + + result = Bar().call(3) + if (result != "Bar.call(3)") return "fail3: $result" + + result = Baz.call(4) + if (result != "Baz.call(4)") return "fail4: $result" + + return "OK" +} \ No newline at end of file