From 083b506fe15598c4a0612587b03ef7283536705d Mon Sep 17 00:00:00 2001 From: Alexey Tsvetkov Date: Wed, 4 Mar 2015 17:47:12 +0300 Subject: [PATCH] JS: test metadata generation --- .../kotlin/js/inline/util/collectUtils.kt | 25 +++++-- ...ctionCollector.kt => PropertyCollector.kt} | 13 +--- .../test/semantics/InlineJsTestGenerated.java | 6 ++ .../kotlin/js/test/utils/AstSearchUtil.java | 21 +++++- .../js/test/utils/DirectiveTestUtils.java | 24 +++++++ .../inline/cases/metadataForPublicFunction.kt | 68 +++++++++++++++++++ 6 files changed, 140 insertions(+), 17 deletions(-) rename js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectors/{FunctionCollector.kt => PropertyCollector.kt} (75%) create mode 100644 js/js.translator/testData/inline/cases/metadataForPublicFunction.kt diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt index 25a13698c43..2f6658df74e 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectUtils.kt @@ -23,7 +23,8 @@ import java.util.IdentityHashMap import org.jetbrains.kotlin.js.inline.util.collectors.ReferenceNameCollector import org.jetbrains.kotlin.js.inline.util.collectors.NameCollector import org.jetbrains.kotlin.js.inline.util.collectors.InstanceCollector -import org.jetbrains.kotlin.js.inline.util.collectors.FunctionCollector +import org.jetbrains.kotlin.js.inline.util.collectors.PropertyCollector +import org.jetbrains.kotlin.js.translate.expression.* public fun collectFunctionReferencesInside(scope: JsNode): List = collectReferencesInside(scope) filter { it.staticRef is JsFunction } @@ -44,11 +45,27 @@ public fun collectLocalNames(function: JsFunction): List { } } +public fun collectJsProperties(scope: JsNode): IdentityHashMap { + val collector = PropertyCollector() + collector.accept(scope) + return collector.properties +} + public fun collectNamedFunctions(scope: JsNode): IdentityHashMap { - return with(FunctionCollector()) { - accept(scope) - functions + val namedFunctions = IdentityHashMap() + + for ((name, value) in collectJsProperties(scope)) { + val function: JsFunction? = when (value) { + is JsFunction -> value + else -> InlineMetadata.decompose(value)?.function + } + + if (function != null) { + namedFunctions[name] = function + } } + + return namedFunctions } public fun collectInstances(klass: Class, scope: JsNode): List { diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectors/FunctionCollector.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectors/PropertyCollector.kt similarity index 75% rename from js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectors/FunctionCollector.kt rename to js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectors/PropertyCollector.kt index c0e178dd1fb..b9f5bd6feac 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectors/FunctionCollector.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/util/collectors/PropertyCollector.kt @@ -21,8 +21,8 @@ import org.jetbrains.kotlin.js.translate.expression.InlineMetadata import java.util.IdentityHashMap -class FunctionCollector : RecursiveJsVisitor() { - public val functions: IdentityHashMap = IdentityHashMap() +class PropertyCollector : RecursiveJsVisitor() { + public val properties: IdentityHashMap = IdentityHashMap() override fun visitPropertyInitializer(x: JsPropertyInitializer?) { super.visitPropertyInitializer(x) @@ -32,13 +32,6 @@ class FunctionCollector : RecursiveJsVisitor() { if (name == null) return val value = x?.getValueExpr() - val function = when (value) { - is JsFunction -> value - else -> InlineMetadata.decompose(value)?.function - } - - if (function is JsFunction) { - functions[name] = function - } + properties[name] = value } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InlineJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InlineJsTestGenerated.java index 99e8d041a1f..666ea4d8922 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InlineJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/InlineJsTestGenerated.java @@ -252,6 +252,12 @@ public class InlineJsTestGenerated extends AbstractInlineJsTest { doTest(fileName); } + @TestMetadata("metadataForPublicFunction.kt") + public void testMetadataForPublicFunction() throws Exception { + String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inline/cases/metadataForPublicFunction.kt"); + doTest(fileName); + } + @TestMetadata("noInlineLambda.kt") public void testNoInlineLambda() throws Exception { String fileName = JetTestUtils.navigationMetadata("js/js.translator/testData/inline/cases/noInlineLambda.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/AstSearchUtil.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/AstSearchUtil.java index 3f466141b63..5ffda8962dd 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/AstSearchUtil.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/AstSearchUtil.java @@ -16,26 +16,41 @@ package org.jetbrains.kotlin.js.test.utils; +import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsFunction; import com.google.dart.compiler.backend.js.ast.JsName; import com.google.dart.compiler.backend.js.ast.JsNode; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Map; import static org.jetbrains.kotlin.js.inline.util.UtilPackage.collectNamedFunctions; +import static org.jetbrains.kotlin.js.inline.util.UtilPackage.collectJsProperties; public class AstSearchUtil { @NotNull public static JsFunction getFunction(@NotNull JsNode searchRoot, String name) { - Map functions = collectNamedFunctions(searchRoot); + JsFunction function = findByIdent(collectNamedFunctions(searchRoot), name); + assert function != null: "Function `" + name + "` was not found"; + return function; + } - for (Map.Entry entry : functions.entrySet()) { + @NotNull + public static JsExpression getProperty(@NotNull JsNode searchRoot, @NotNull String name) { + JsExpression property = findByIdent(collectJsProperties(searchRoot), name); + assert property != null: "Property `" + name + "` was not found"; + return property; + } + + @Nullable + private static T findByIdent(@NotNull Map properties, @NotNull String name) { + for (Map.Entry entry : properties.entrySet()) { if (entry.getKey().getIdent().equals(name)) { return entry.getValue(); } } - throw new AssertionError("Function `" + name + "` was not found"); + return null; } } diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java index 7ad6ad26ac2..29b22e8f171 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/DirectiveTestUtils.java @@ -16,11 +16,13 @@ package org.jetbrains.kotlin.js.test.utils; +import com.google.dart.compiler.backend.js.ast.JsExpression; import com.google.dart.compiler.backend.js.ast.JsFunction; import com.google.dart.compiler.backend.js.ast.JsLabel; import com.google.dart.compiler.backend.js.ast.JsNode; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.kotlin.js.translate.expression.InlineMetadata; import java.util.ArrayList; import java.util.HashMap; @@ -117,6 +119,26 @@ public class DirectiveTestUtils { } }; + private static final DirectiveHandler HAS_INLINE_METADATA = new DirectiveHandler("CHECK_HAS_INLINE_METADATA") { + @Override + void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception { + String functionName = arguments.getPositionalArgument(0); + JsExpression property = AstSearchUtil.getProperty(ast, functionName); + String message = "Inline metadata has not been generated for function " + functionName; + assertNotNull(message, InlineMetadata.decompose(property)); + } + }; + + private static final DirectiveHandler HAS_NO_INLINE_METADATA = new DirectiveHandler("CHECK_HAS_NO_INLINE_METADATA") { + @Override + void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception { + String functionName = arguments.getPositionalArgument(0); + JsExpression property = AstSearchUtil.getProperty(ast, functionName); + String message = "Inline metadata has been generated for not effectively public function " + functionName; + assertTrue(message, property instanceof JsFunction); + } + }; + public static void processDirectives(@NotNull JsNode ast, @NotNull String sourceCode) throws Exception { FUNCTION_CONTAINS_NO_CALLS.process(ast, sourceCode); FUNCTION_NOT_CALLED.process(ast, sourceCode); @@ -124,6 +146,8 @@ public class DirectiveTestUtils { FUNCTION_NOT_CALLED_IN_SCOPE.process(ast, sourceCode); FUNCTIONS_HAVE_SAME_LINES.process(ast, sourceCode); COUNT_LABELS.process(ast, sourceCode); + HAS_INLINE_METADATA.process(ast, sourceCode); + HAS_NO_INLINE_METADATA.process(ast, sourceCode); } public static void checkFunctionContainsNoCalls(JsNode node, String functionName) throws Exception { diff --git a/js/js.translator/testData/inline/cases/metadataForPublicFunction.kt b/js/js.translator/testData/inline/cases/metadataForPublicFunction.kt new file mode 100644 index 00000000000..ba98b2e8f94 --- /dev/null +++ b/js/js.translator/testData/inline/cases/metadataForPublicFunction.kt @@ -0,0 +1,68 @@ +package foo + +// CHECK_CONTAINS_NO_CALLS: test1 +// CHECK_CONTAINS_NO_CALLS: test2 +// CHECK_CONTAINS_NO_CALLS: test3 +// CHECK_CONTAINS_NO_CALLS: test4 +// CHECK_CONTAINS_NO_CALLS: test5 +// CHECK_HAS_INLINE_METADATA: apply_hiyix$ +// CHECK_HAS_INLINE_METADATA: applyL_hiyix$ +// CHECK_HAS_INLINE_METADATA: applyM_hiyix$ +// CHECK_HAS_NO_INLINE_METADATA: applyN +// CHECK_HAS_NO_INLINE_METADATA: applyO_hiyix$ + +inline +public fun apply(arg: T, func: (T)->T): T = func(arg) + +public open class L { + inline + protected fun applyL(arg: T, func: (T)->T): T = func(arg) +} + +public class M { + inline + public fun applyM(arg: T, func: (T)->T): T = func(arg) +} + +private class N { + inline + public fun applyN(arg: T, func: (T)->T): T = func(arg) +} + +private object O { + public object OInner { + inline + public fun applyO(arg: T, func: (T)->T): T = func(arg) + } +} + +fun test1(x: Int, y: Int): Int = apply(x) { it * y } + +fun test2(m: M, x: Int, y: Int): Int = m.applyM(x) { it * y } + +fun test3(n: N, x: Int, y: Int): Int = n.applyN(x) { it * y } + +object LTest : L() { + fun test4(l: L, x: Int, y: Int): Int = l.applyL(x) { it * y } +} + +fun test5(x: Int, y: Int): Int = O.OInner.applyO(x) { it * y } + +fun box(): String { + assertEquals(6, test1(2, 3)) + assertEquals(20, test1(5, 4)) + + assertEquals(6, test2(M(), 2, 3)) + assertEquals(20, test2(M(), 5, 4)) + + assertEquals(6, test3(N(), 2, 3)) + assertEquals(20, test3(N(), 5, 4)) + + assertEquals(6, LTest.test4(L(), 2, 3)) + assertEquals(20, LTest.test4(L(), 5, 4)) + + assertEquals(6, test5(2, 3)) + assertEquals(20, test5(5, 4)) + + return "OK" +} \ No newline at end of file