JS inline: create JsInliningContext per function processed by JsInliner
This commit is contained in:
committed by
Zalim Bashorov
parent
0151b7a2d1
commit
49718f143b
@@ -17,36 +17,32 @@
|
|||||||
package org.jetbrains.k2js.inline;
|
package org.jetbrains.k2js.inline;
|
||||||
|
|
||||||
import com.google.dart.compiler.backend.js.ast.*;
|
import com.google.dart.compiler.backend.js.ast.*;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
class FunctionInlineMutator {
|
class FunctionInlineMutator {
|
||||||
private static final String RESULT_LABEL = "result_inlined";
|
private JsBlock body;
|
||||||
|
|
||||||
private final JsInvocation call;
|
|
||||||
private final JsScope callerScope;
|
|
||||||
private final JsFunction invokedFunction;
|
private final JsFunction invokedFunction;
|
||||||
private final List<JsStatement> statements = new ArrayList<JsStatement>();
|
|
||||||
private final IdentityHashMap<JsName, JsNameRef> renameMap = new IdentityHashMap<JsName, JsNameRef>();
|
|
||||||
|
|
||||||
public static InlineableResult getInlineableCallReplacement(
|
public static InlineableResult getInlineableCallReplacement(
|
||||||
JsInvocation call,
|
@NotNull JsInvocation call,
|
||||||
JsScope callerScope,
|
@NotNull InliningContext inliningContext
|
||||||
JsFunction invokedFunction
|
|
||||||
) {
|
) {
|
||||||
return (new FunctionInlineMutator(call, callerScope, invokedFunction)).process();
|
FunctionInlineMutator mutator = new FunctionInlineMutator(call, inliningContext);
|
||||||
|
mutator.process();
|
||||||
|
|
||||||
|
JsStatement inlineableBody = mutator.body;
|
||||||
|
return new InlineableResult(inlineableBody, call);
|
||||||
}
|
}
|
||||||
|
|
||||||
private FunctionInlineMutator(JsInvocation call, JsScope callerScope, JsFunction invokedFunction) {
|
private FunctionInlineMutator(@NotNull JsInvocation call, @NotNull InliningContext inliningContext) {
|
||||||
this.call = call;
|
FunctionContext functionContext = inliningContext.getFunctionContext();
|
||||||
this.callerScope = callerScope;
|
invokedFunction = functionContext.getFunctionDefinition(call);
|
||||||
this.invokedFunction = invokedFunction;
|
body = invokedFunction.getBody().deepCopy();
|
||||||
}
|
}
|
||||||
|
|
||||||
private InlineableResult process() {
|
private void process() {
|
||||||
JsBlock inlinedBody = new JsBlock(statements);
|
|
||||||
JsNameRef result = callerScope.declareName(RESULT_LABEL).makeRef();
|
|
||||||
statements.addAll(invokedFunction.getBody().getStatements());
|
|
||||||
return new InlineableResult(inlinedBody, result);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,31 +27,79 @@ import static org.jetbrains.k2js.inline.FunctionInlineMutator.getInlineableCallR
|
|||||||
|
|
||||||
public class JsInliner extends JsVisitorWithContextImpl {
|
public class JsInliner extends JsVisitorWithContextImpl {
|
||||||
|
|
||||||
|
private class JsInliningContext implements InliningContext {
|
||||||
|
private final FunctionContext functionContext;
|
||||||
|
|
||||||
|
JsInliningContext(JsFunction function) {
|
||||||
|
functionContext = new FunctionContext(function, this) {
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
protected JsFunction lookUpStaticFunction(@Nullable JsName functionName) {
|
||||||
|
return functions.get(functionName);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T extends JsNode> RenamingContext<T> getRenamingContext() {
|
||||||
|
return new RenamingContext<T>(getFunctionContext().getScope());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public StatementContext getStatementContext() {
|
||||||
|
return new StatementContext() {
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public JsContext getCurrentStatementContext() {
|
||||||
|
return getLastStatementLevelContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
protected JsStatement getEmptyStatement() {
|
||||||
|
return getFunctionContext().getEmpty();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public FunctionContext getFunctionContext() {
|
||||||
|
return functionContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isResultNeeded(JsInvocation call) {
|
||||||
|
JsStatement currentStatement = getStatementContext().getCurrentStatement();
|
||||||
|
return InvocationUtil.isResultUsed(currentStatement, call);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private final IdentityHashMap<JsName, JsFunction> functions;
|
private final IdentityHashMap<JsName, JsFunction> functions;
|
||||||
private final Stack<JsScope> scopeStack = new Stack<JsScope>();
|
private final Stack<JsInliningContext> inliningContexts = new Stack<JsInliningContext>();
|
||||||
private final JsProgram program;
|
|
||||||
|
|
||||||
public static JsProgram process(JsProgram program) {
|
public static JsProgram process(JsProgram program) {
|
||||||
IdentityHashMap<JsName, JsFunction> functions = FunctionCollector.collectFunctions(program);
|
IdentityHashMap<JsName, JsFunction> functions = FunctionCollector.collectFunctions(program);
|
||||||
JsInliner inliner = new JsInliner(program, functions);
|
JsInliner inliner = new JsInliner(functions);
|
||||||
return inliner.process();
|
return inliner.accept(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
JsInliner(JsProgram program, IdentityHashMap<JsName, JsFunction> functions) {
|
JsInliner(IdentityHashMap<JsName, JsFunction> functions) {
|
||||||
this.program = program;
|
|
||||||
this.functions = functions;
|
this.functions = functions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean visit(JsFunction function, JsContext context) {
|
public boolean visit(JsFunction function, JsContext context) {
|
||||||
scopeStack.push(function.getScope());
|
inliningContexts.push(new JsInliningContext(function));
|
||||||
return super.visit(function, context);
|
return super.visit(function, context);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void endVisit(JsFunction function, JsContext context) {
|
public void endVisit(JsFunction function, JsContext context) {
|
||||||
super.endVisit(function, context);
|
super.endVisit(function, context);
|
||||||
scopeStack.pop();
|
inliningContexts.pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -66,65 +114,52 @@ public class JsInliner extends JsVisitorWithContextImpl {
|
|||||||
inline(call, context);
|
inline(call, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private JsProgram process() {
|
|
||||||
return this.accept(program);
|
|
||||||
}
|
|
||||||
|
|
||||||
private JsScope getCurrentFunctionScope() {
|
|
||||||
assert !scopeStack.isEmpty();
|
|
||||||
return scopeStack.peek();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void inline(@NotNull JsInvocation call, @NotNull JsContext context) {
|
private void inline(@NotNull JsInvocation call, @NotNull JsContext context) {
|
||||||
JsFunction functionToInline = findDeclaration(call);
|
JsInliningContext inliningContext = getInliningContext();
|
||||||
assert functionToInline != null;
|
FunctionContext functionContext = getFunctionContext();
|
||||||
JsContext statementLevelContext = getLastStatementLevelContext();
|
functionContext.declareFunctionConstructorCalls(call.getArguments());
|
||||||
assert statementLevelContext != null;
|
InlineableResult inlineableResult = getInlineableCallReplacement(call, inliningContext);
|
||||||
|
|
||||||
JsScope currentScope = getCurrentFunctionScope();
|
|
||||||
InlineableResult inlineableResult = getInlineableCallReplacement(call, currentScope, functionToInline);
|
|
||||||
|
|
||||||
JsStatement inlineableBody = inlineableResult.getInlineableBody();
|
JsStatement inlineableBody = inlineableResult.getInlineableBody();
|
||||||
JsExpression resultExpression = inlineableResult.getResultExpression();
|
JsExpression resultExpression = inlineableResult.getResultExpression();
|
||||||
|
StatementContext statementContext = inliningContext.getStatementContext();
|
||||||
|
|
||||||
statementLevelContext.insertAfter(statementLevelContext.getCurrentNode());
|
/**
|
||||||
statementLevelContext.insertAfter(inlineableBody);
|
* Assumes, that resultExpression == null, when result is not needed.
|
||||||
statementLevelContext.replaceMe(program.getEmptyStatement());
|
* @see FunctionInlineMutator.isResultNeeded()
|
||||||
|
*/
|
||||||
|
if (resultExpression == null) {
|
||||||
|
statementContext.removeCurrentStatement();
|
||||||
|
} else {
|
||||||
|
context.replaceMe(resultExpression);
|
||||||
|
}
|
||||||
|
|
||||||
context.replaceMe(resultExpression);
|
statementContext.shiftCurrentStatementForward();
|
||||||
|
InsertionPoint<JsStatement> insertionPoint = statementContext.getInsertionPoint();
|
||||||
|
if (inlineableBody instanceof JsBlock) {
|
||||||
|
JsBlock block = (JsBlock) inlineableBody;
|
||||||
|
insertionPoint.insertAllAfter(block.getStatements());
|
||||||
|
} else {
|
||||||
|
insertionPoint.insertAfter(inlineableBody);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@NotNull
|
||||||
private JsFunction findDeclaration(@NotNull JsInvocation call) {
|
private JsInliningContext getInliningContext() {
|
||||||
JsName name = getFunctionName(call);
|
return inliningContexts.peek();
|
||||||
if (functions.containsKey(name)) {
|
}
|
||||||
return functions.get(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (name.getStaticRef() != null && name.getStaticRef() instanceof JsFunction) {
|
@NotNull FunctionContext getFunctionContext() {
|
||||||
return (JsFunction) name.getStaticRef();
|
return getInliningContext().getFunctionContext();
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean canInline(@NotNull JsInvocation call) {
|
private boolean canInline(@NotNull JsInvocation call) {
|
||||||
return findDeclaration(call) != null;
|
FunctionContext functionContext = getFunctionContext();
|
||||||
|
return functionContext.hasFunctionDefinition(call);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean shouldInline(@NotNull JsInvocation call) {
|
private static boolean shouldInline(@NotNull JsInvocation call) {
|
||||||
return call.getInlineStrategy().isInline();
|
return call.getInlineStrategy().isInline();
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
|
||||||
private static JsName getFunctionName(@NotNull JsInvocation call) {
|
|
||||||
JsExpression qualifier = call.getQualifier();
|
|
||||||
assert qualifier instanceof JsNameRef;
|
|
||||||
|
|
||||||
JsName name = ((JsNameRef) qualifier).getName();
|
|
||||||
assert name != null;
|
|
||||||
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user