From cf89e24b490481f66cb688def354b2ee24b2dd8a Mon Sep 17 00:00:00 2001 From: Alexey Andreev Date: Mon, 21 Nov 2016 20:24:25 +0300 Subject: [PATCH] JS: fix removal of unused lambdas after inlining --- .../clean/removeUnusedFunctionDefinitions.kt | 10 +++-- .../js/test/semantics/BoxJsTestGenerated.java | 6 +++ .../kotlin/js/test/utils/CallCounter.java | 44 ++++++++++++++++++- .../js/test/utils/DirectiveTestUtils.java | 10 +++-- .../inlineLambdaCleanup.kt | 7 +++ 5 files changed, 69 insertions(+), 8 deletions(-) create mode 100644 js/js.translator/testData/box/inlineSizeReduction/inlineLambdaCleanup.kt diff --git a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/removeUnusedFunctionDefinitions.kt b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/removeUnusedFunctionDefinitions.kt index 49c0a1c5148..f17d80c0b92 100644 --- a/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/removeUnusedFunctionDefinitions.kt +++ b/js/js.inliner/src/org/jetbrains/kotlin/js/inline/clean/removeUnusedFunctionDefinitions.kt @@ -37,9 +37,13 @@ fun removeUnusedFunctionDefinitions(root: JsNode, functions: Map + val expression = when (statement) { + is JsExpressionStatement -> statement.expression + is JsVars -> if (statement.vars.size == 1) statement.vars[0].initExpression else null + else -> null + } + expression is JsFunction && expression in removable }.accept(root) } 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 5a017334fc4..83c3e8307d9 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 @@ -4562,6 +4562,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("js/js.translator/testData/box/inlineSizeReduction"), Pattern.compile("^([^_](.+))\\.kt$"), TargetBackend.ANY, true); } + @TestMetadata("inlineLambdaCleanup.kt") + public void testInlineLambdaCleanup() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineSizeReduction/inlineLambdaCleanup.kt"); + doTest(fileName); + } + @TestMetadata("inlineOrder.kt") public void testInlineOrder() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/inlineSizeReduction/inlineOrder.kt"); diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/CallCounter.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/CallCounter.java index f23ce9a782e..bab14d5c2ac 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/CallCounter.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/utils/CallCounter.java @@ -28,6 +28,11 @@ public class CallCounter extends RecursiveJsVisitor { @NotNull private final Set exceptFunctionNames; + @NotNull + private final Set exceptScopes; + + private int excludedScopeOccurrenceCount; + @NotNull public static CallCounter countCalls(@NotNull JsNode node) { return countCalls(node, Collections.emptySet()); @@ -35,14 +40,23 @@ public class CallCounter extends RecursiveJsVisitor { @NotNull public static CallCounter countCalls(@NotNull JsNode node, @NotNull Set exceptFunctionNames) { - CallCounter visitor = new CallCounter(new HashSet(exceptFunctionNames)); + CallCounter visitor = new CallCounter(new HashSet(exceptFunctionNames), Collections.emptySet()); node.accept(visitor); return visitor; } - private CallCounter(@NotNull Set exceptFunctionNames) { + @NotNull + public static CallCounter countCallsWithExcludedScopes(@NotNull JsNode node, @NotNull Set exceptScopes) { + CallCounter visitor = new CallCounter(Collections.emptySet(), new HashSet(exceptScopes)); + node.accept(visitor); + + return visitor; + } + + private CallCounter(@NotNull Set exceptFunctionNames, @NotNull Set exceptScopes) { this.exceptFunctionNames = exceptFunctionNames; + this.exceptScopes = exceptScopes; } public int getTotalCallsCount() { @@ -89,6 +103,28 @@ public class CallCounter extends RecursiveJsVisitor { } } + @Override + public void visitFunction(@NotNull JsFunction x) { + if (x.getName() != null && exceptScopes.contains(x.getName().getIdent())) { + excludedScopeOccurrenceCount++; + return; + } + super.visitFunction(x); + } + + @Override + public void visitVars(@NotNull JsVars x) { + for (JsVars.JsVar jsVar : x.getVars()) { + if (jsVar.getInitExpression() == null) continue; + if (!exceptScopes.contains(jsVar.getName().getIdent())) { + accept(jsVar.getInitExpression()); + } + else { + excludedScopeOccurrenceCount++; + } + } + } + private static boolean matchesQualifiers(JsNameRef nameRef, List expectedQualifierChain) { JsExpression currentQualifier = nameRef; @@ -108,4 +144,8 @@ public class CallCounter extends RecursiveJsVisitor { return true; } + + public int getExcludedScopeOccurrenceCount() { + return excludedScopeOccurrenceCount; + } } 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 20802242275..9f664268d51 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 @@ -49,7 +49,7 @@ public class DirectiveTestUtils { private static final DirectiveHandler FUNCTION_NOT_CALLED = new DirectiveHandler("CHECK_NOT_CALLED") { @Override void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception { - checkFunctionNotCalled(ast, arguments.getFirst()); + checkFunctionNotCalled(ast, arguments.getFirst(), arguments.findNamedArgument("except")); } }; @@ -296,12 +296,16 @@ public class DirectiveTestUtils { } - public static void checkFunctionNotCalled(JsNode node, String functionName) throws Exception { - CallCounter counter = CallCounter.countCalls(node); + public static void checkFunctionNotCalled(@NotNull JsNode node, @NotNull String functionName, @Nullable String exceptFunction) + throws Exception { + Set excludedScopes = exceptFunction != null ? Collections.singleton(exceptFunction) : Collections.emptySet(); + + CallCounter counter = CallCounter.countCallsWithExcludedScopes(node, excludedScopes); int functionCalledCount = counter.getQualifiedCallsCount(functionName); String errorMessage = "inline function `" + functionName + "` is called"; assertEquals(errorMessage, 0, functionCalledCount); + assertEquals("Not all excluded scopes found", excludedScopes.size(), counter.getExcludedScopeOccurrenceCount()); } public static void checkCalledInScope( diff --git a/js/js.translator/testData/box/inlineSizeReduction/inlineLambdaCleanup.kt b/js/js.translator/testData/box/inlineSizeReduction/inlineLambdaCleanup.kt new file mode 100644 index 00000000000..3315e6e82fc --- /dev/null +++ b/js/js.translator/testData/box/inlineSizeReduction/inlineLambdaCleanup.kt @@ -0,0 +1,7 @@ +// CHECK_NOT_CALLED: produceOK except=box + +fun produceOK() = "OK" + +private inline fun block(f: () -> T) = f() + +fun box(): String = block { produceOK() } \ No newline at end of file