JS: make functions called call to inline properly. See KT-7004

This commit is contained in:
Alexey Andreev
2017-01-11 12:17:32 +03:00
parent 10a3fbf53f
commit 2907eafa18
8 changed files with 129 additions and 7 deletions
@@ -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)
@@ -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()
}
@@ -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");
@@ -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<JsName> names = new ArrayList<JsName>();
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<JsName> {
@@ -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 }
}
}
@@ -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);
}
+54
View File
@@ -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"
}
@@ -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"
}