JS: do not extract expressions, when inline call can be expression
This commit is contained in:
@@ -224,6 +224,10 @@ class FunctionInlineMutator {
|
|||||||
return !thisRefs.isEmpty();
|
return !thisRefs.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean canBeExpression(JsFunction function) {
|
||||||
|
return canBeExpression(function.getBody());
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean canBeExpression(JsBlock body) {
|
private static boolean canBeExpression(JsBlock body) {
|
||||||
List<JsStatement> statements = body.getStatements();
|
List<JsStatement> statements = body.getStatements();
|
||||||
return statements.size() == 1 && statements.get(0) instanceof JsReturn;
|
return statements.size() == 1 && statements.get(0) instanceof JsReturn;
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.resolve.inline.InlineStrategy;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
import kotlin.Function1;
|
import kotlin.Function1;
|
||||||
|
|
||||||
|
import static org.jetbrains.kotlin.js.inline.FunctionInlineMutator.canBeExpression;
|
||||||
import static org.jetbrains.kotlin.js.inline.FunctionInlineMutator.getInlineableCallReplacement;
|
import static org.jetbrains.kotlin.js.inline.FunctionInlineMutator.getInlineableCallReplacement;
|
||||||
import static org.jetbrains.kotlin.js.inline.clean.CleanPackage.removeUnusedFunctionDefinitions;
|
import static org.jetbrains.kotlin.js.inline.clean.CleanPackage.removeUnusedFunctionDefinitions;
|
||||||
import static org.jetbrains.kotlin.js.inline.clean.CleanPackage.removeUnusedLocalFunctionDeclarations;
|
import static org.jetbrains.kotlin.js.inline.clean.CleanPackage.removeUnusedLocalFunctionDeclarations;
|
||||||
@@ -55,7 +56,13 @@ public class JsInliner extends JsVisitorWithContextImpl {
|
|||||||
if (!(node instanceof JsInvocation)) return false;
|
if (!(node instanceof JsInvocation)) return false;
|
||||||
|
|
||||||
JsInvocation call = (JsInvocation) node;
|
JsInvocation call = (JsInvocation) node;
|
||||||
return hasToBeInlined(call);
|
|
||||||
|
if (hasToBeInlined(call)) {
|
||||||
|
JsFunction function = getFunctionContext().getFunctionDefinition(call);
|
||||||
|
return !canBeExpression(function);
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -16,10 +16,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.js.test.utils;
|
package org.jetbrains.kotlin.js.test.utils;
|
||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.JsExpression;
|
import com.google.dart.compiler.backend.js.ast.*;
|
||||||
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.NotNull;
|
||||||
import org.jetbrains.annotations.Nullable;
|
import org.jetbrains.annotations.Nullable;
|
||||||
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata;
|
import org.jetbrains.kotlin.js.translate.expression.InlineMetadata;
|
||||||
@@ -94,29 +91,57 @@ public class DirectiveTestUtils {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final DirectiveHandler COUNT_LABELS = new DirectiveHandler("CHECK_LABELS_COUNT") {
|
private abstract static class CountNodesDirective<T extends JsNode> extends DirectiveHandler {
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
private final Class<T> klass;
|
||||||
|
|
||||||
|
CountNodesDirective(@NotNull String directive, @NotNull Class<T> klass) {
|
||||||
|
super(directive);
|
||||||
|
this.klass = klass;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception {
|
void processEntry(@NotNull JsNode ast, @NotNull ArgumentsHelper arguments) throws Exception {
|
||||||
String functionName = arguments.getNamedArgument("function");
|
String functionName = arguments.getNamedArgument("function");
|
||||||
String labelName = arguments.getNamedArgument("name");
|
|
||||||
String countStr = arguments.getNamedArgument("count");
|
String countStr = arguments.getNamedArgument("count");
|
||||||
int expectedCount = Integer.valueOf(countStr);
|
int expectedCount = Integer.valueOf(countStr);
|
||||||
|
|
||||||
JsNode scope = AstSearchUtil.getFunction(ast, functionName);
|
JsNode scope = AstSearchUtil.getFunction(ast, functionName);
|
||||||
List<JsLabel> labels = collectInstances(JsLabel.class, scope);
|
List<T> nodes = collectInstances(klass, scope);
|
||||||
int actualCount = 0;
|
int actualCount = 0;
|
||||||
for (JsLabel label : labels) {
|
|
||||||
if (label.getName().getIdent().equals(labelName)) {
|
for (T node : nodes) {
|
||||||
actualCount++;
|
actualCount += getActualCountFor(node, arguments);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
String message = "Label " + labelName +
|
String message = "Function " + functionName + " contains " + actualCount +
|
||||||
" is expected to be counted " + expectedCount +
|
" nodes of type " + klass.getName() +
|
||||||
" times at function " + functionName +
|
", but expected count is " + expectedCount;
|
||||||
" but was encountered " + actualCount + " times";
|
|
||||||
assertEquals(message, expectedCount, actualCount);
|
assertEquals(message, expectedCount, actualCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected abstract int getActualCountFor(@NotNull T node, @NotNull ArgumentsHelper arguments);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final DirectiveHandler COUNT_LABELS = new CountNodesDirective<JsLabel>("CHECK_LABELS_COUNT", JsLabel.class) {
|
||||||
|
@Override
|
||||||
|
protected int getActualCountFor(@NotNull JsLabel node, @NotNull ArgumentsHelper arguments) {
|
||||||
|
String labelName = arguments.getNamedArgument("name");
|
||||||
|
|
||||||
|
if (node.getName().getIdent().equals(labelName)) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final DirectiveHandler COUNT_VARS = new CountNodesDirective<JsVars>("CHECK_VARS_COUNT", JsVars.class) {
|
||||||
|
@Override
|
||||||
|
protected int getActualCountFor(@NotNull JsVars node, @NotNull ArgumentsHelper arguments) {
|
||||||
|
return node.getVars().size();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final DirectiveHandler HAS_INLINE_METADATA = new DirectiveHandler("CHECK_HAS_INLINE_METADATA") {
|
private static final DirectiveHandler HAS_INLINE_METADATA = new DirectiveHandler("CHECK_HAS_INLINE_METADATA") {
|
||||||
@@ -146,6 +171,7 @@ public class DirectiveTestUtils {
|
|||||||
FUNCTION_NOT_CALLED_IN_SCOPE.process(ast, sourceCode);
|
FUNCTION_NOT_CALLED_IN_SCOPE.process(ast, sourceCode);
|
||||||
FUNCTIONS_HAVE_SAME_LINES.process(ast, sourceCode);
|
FUNCTIONS_HAVE_SAME_LINES.process(ast, sourceCode);
|
||||||
COUNT_LABELS.process(ast, sourceCode);
|
COUNT_LABELS.process(ast, sourceCode);
|
||||||
|
COUNT_VARS.process(ast, sourceCode);
|
||||||
HAS_INLINE_METADATA.process(ast, sourceCode);
|
HAS_INLINE_METADATA.process(ast, sourceCode);
|
||||||
HAS_NO_INLINE_METADATA.process(ast, sourceCode);
|
HAS_NO_INLINE_METADATA.process(ast, sourceCode);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
package foo
|
||||||
|
|
||||||
|
// CHECK_CONTAINS_NO_CALLS: test
|
||||||
|
// COUNT_VARS: function=test count=0
|
||||||
|
|
||||||
|
// A copy of stdlib run function.
|
||||||
|
// Copied to not to depend on run implementation.
|
||||||
|
// It's important, that the body is just `return fn()`.
|
||||||
|
inline fun evaluate<T>(fn: ()->T): T = fn()
|
||||||
|
|
||||||
|
fun test(x: Int): Int =
|
||||||
|
evaluate {
|
||||||
|
evaluate { 2 } * evaluate { x }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
assertEquals(6, test(3))
|
||||||
|
assertEquals(8, test(4))
|
||||||
|
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user