JS: fix removal of unused lambdas after inlining

This commit is contained in:
Alexey Andreev
2016-11-21 20:24:25 +03:00
parent 40e00a62f5
commit cf89e24b49
5 changed files with 69 additions and 8 deletions
@@ -37,9 +37,13 @@ fun removeUnusedFunctionDefinitions(root: JsNode, functions: Map<JsName, JsFunct
removableFunctions
}
NodeRemover(JsPropertyInitializer::class.java) {
val function = it.valueExpr as? JsFunction
function != null && function in removable
NodeRemover(JsStatement::class.java) { statement ->
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)
}
@@ -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");
@@ -28,6 +28,11 @@ public class CallCounter extends RecursiveJsVisitor {
@NotNull
private final Set<String> exceptFunctionNames;
@NotNull
private final Set<String> exceptScopes;
private int excludedScopeOccurrenceCount;
@NotNull
public static CallCounter countCalls(@NotNull JsNode node) {
return countCalls(node, Collections.<String>emptySet());
@@ -35,14 +40,23 @@ public class CallCounter extends RecursiveJsVisitor {
@NotNull
public static CallCounter countCalls(@NotNull JsNode node, @NotNull Set<String> exceptFunctionNames) {
CallCounter visitor = new CallCounter(new HashSet<String>(exceptFunctionNames));
CallCounter visitor = new CallCounter(new HashSet<String>(exceptFunctionNames), Collections.<String>emptySet());
node.accept(visitor);
return visitor;
}
private CallCounter(@NotNull Set<String> exceptFunctionNames) {
@NotNull
public static CallCounter countCallsWithExcludedScopes(@NotNull JsNode node, @NotNull Set<String> exceptScopes) {
CallCounter visitor = new CallCounter(Collections.<String>emptySet(), new HashSet<String>(exceptScopes));
node.accept(visitor);
return visitor;
}
private CallCounter(@NotNull Set<String> exceptFunctionNames, @NotNull Set<String> 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<String> expectedQualifierChain) {
JsExpression currentQualifier = nameRef;
@@ -108,4 +144,8 @@ public class CallCounter extends RecursiveJsVisitor {
return true;
}
public int getExcludedScopeOccurrenceCount() {
return excludedScopeOccurrenceCount;
}
}
@@ -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<String> excludedScopes = exceptFunction != null ? Collections.singleton(exceptFunction) : Collections.<String>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(
@@ -0,0 +1,7 @@
// CHECK_NOT_CALLED: produceOK except=box
fun produceOK() = "OK"
private inline fun <T> block(f: () -> T) = f()
fun box(): String = block { produceOK() }