package jdk.nashorn.api.tree;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import jdk.nashorn.internal.ir.AccessNode;
import jdk.nashorn.internal.ir.BinaryNode;
import jdk.nashorn.internal.ir.Block;
import jdk.nashorn.internal.ir.BlockStatement;
import jdk.nashorn.internal.ir.BreakNode;
import jdk.nashorn.internal.ir.CallNode;
import jdk.nashorn.internal.ir.CaseNode;
import jdk.nashorn.internal.ir.CatchNode;
import jdk.nashorn.internal.ir.ClassNode;
import jdk.nashorn.internal.ir.ContinueNode;
import jdk.nashorn.internal.ir.DebuggerNode;
import jdk.nashorn.internal.ir.EmptyNode;
import jdk.nashorn.internal.ir.ErrorNode;
import jdk.nashorn.internal.ir.Expression;
import jdk.nashorn.internal.ir.ExpressionStatement;
import jdk.nashorn.internal.ir.ForNode;
import jdk.nashorn.internal.ir.FunctionNode;
import jdk.nashorn.internal.ir.IdentNode;
import jdk.nashorn.internal.ir.IfNode;
import jdk.nashorn.internal.ir.IndexNode;
import jdk.nashorn.internal.ir.LabelNode;
import jdk.nashorn.internal.ir.LiteralNode;
import jdk.nashorn.internal.ir.Node;
import jdk.nashorn.internal.ir.ObjectNode;
import jdk.nashorn.internal.ir.PropertyNode;
import jdk.nashorn.internal.ir.ReturnNode;
import jdk.nashorn.internal.ir.RuntimeNode;
import jdk.nashorn.internal.ir.SplitNode;
import jdk.nashorn.internal.ir.Statement;
import jdk.nashorn.internal.ir.SwitchNode;
import jdk.nashorn.internal.ir.TemplateLiteral;
import jdk.nashorn.internal.ir.TernaryNode;
import jdk.nashorn.internal.ir.ThrowNode;
import jdk.nashorn.internal.ir.TryNode;
import jdk.nashorn.internal.ir.UnaryNode;
import jdk.nashorn.internal.ir.VarNode;
import jdk.nashorn.internal.ir.WhileNode;
import jdk.nashorn.internal.ir.WithNode;
import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
import jdk.nashorn.internal.parser.Lexer;
import jdk.nashorn.internal.parser.TokenType;
final class IRTranslator extends SimpleNodeVisitor {
public IRTranslator() {
}
private StatementTreeImpl curStat;
private ExpressionTreeImpl curExpr;
CompilationUnitTree translate(final FunctionNode node) {
if (node == null) {
return null;
}
assert node.getKind() == FunctionNode.Kind.SCRIPT ||
node.getKind() == FunctionNode.Kind.MODULE :
"script or module function expected";
final Block body = node.getBody();
return new CompilationUnitTreeImpl(node,
translateStats(body != null? getOrderedStatements(body.getStatements()) : null),
translateModule(node));
}
@Override
public boolean enterAccessNode(final AccessNode accessNode) {
curExpr = new MemberSelectTreeImpl(accessNode, translateExpr(accessNode.getBase()));
return false;
}
@Override
public boolean enterBlock(final Block block) {
return handleBlock(block, false);
}
@Override
public boolean enterBinaryNode(final BinaryNode binaryNode) {
if (binaryNode.isAssignment()) {
final ExpressionTree srcTree = translateExpr(binaryNode.getAssignmentSource());
final ExpressionTree destTree = translateExpr(binaryNode.getAssignmentDest());
if (binaryNode.isTokenType(TokenType.ASSIGN)) {
curExpr = new AssignmentTreeImpl(binaryNode, destTree, srcTree);
} else {
curExpr = new CompoundAssignmentTreeImpl(binaryNode, destTree, srcTree);
}
} else {
final ExpressionTree leftTree = translateExpr(binaryNode.lhs());
final ExpressionTree rightTree = translateExpr(binaryNode.rhs());
if (binaryNode.isTokenType(TokenType.INSTANCEOF)) {
curExpr = new InstanceOfTreeImpl(binaryNode, leftTree, rightTree);
} else {
curExpr = new BinaryTreeImpl(binaryNode, leftTree, rightTree);
}
}
return false;
}
@Override
public boolean enterBreakNode(final BreakNode breakNode) {
curStat = new BreakTreeImpl(breakNode);
return false;
}
@Override
public boolean enterCallNode(final CallNode callNode) {
curExpr = null;
callNode.getFunction().accept(this);
final ExpressionTree funcTree = curExpr;
final List<? extends ExpressionTree> argTrees = translateExprs(callNode.getArgs());
curExpr = new FunctionCallTreeImpl(callNode, funcTree, argTrees);
return false;
}
@Override
public boolean enterCaseNode(final CaseNode caseNode) {
assert false : "should not reach here!";
return false;
}
@Override
public boolean enterCatchNode(final CatchNode catchNode) {
assert false : "should not reach here";
return false;
}
@Override
public boolean enterContinueNode(final ContinueNode continueNode) {
curStat = new ContinueTreeImpl(continueNode);
return false;
}
@Override
public boolean enterDebuggerNode(final DebuggerNode debuggerNode) {
curStat = new DebuggerTreeImpl(debuggerNode);
return false;
}
@Override
public boolean enterEmptyNode(final EmptyNode emptyNode) {
curStat = new EmptyStatementTreeImpl(emptyNode);
return false;
}
@Override
public boolean enterErrorNode(final ErrorNode errorNode) {
curExpr = new ErroneousTreeImpl(errorNode);
return false;
}
@Override
public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
if (expressionStatement.destructuringDeclarationType() != null) {
final ExpressionTree expr = translateExpr(expressionStatement.getExpression());
assert expr instanceof AssignmentTree : "destructuring decl. statement does not have assignment";
final AssignmentTree assign = (AssignmentTree)expr;
curStat = new DestructuringDeclTreeImpl(expressionStatement, assign.getVariable(), assign.getExpression());
} else {
curStat = new ExpressionStatementTreeImpl(expressionStatement,
translateExpr(expressionStatement.getExpression()));
}
return false;
}
@Override
public boolean enterBlockStatement(final BlockStatement blockStatement) {
final Block block = blockStatement.getBlock();
if (blockStatement.isSynthetic()) {
assert block != null && block.getStatements() != null && block.getStatements().size() == 1;
curStat = translateStat(block.getStatements().get(0));
} else {
curStat = new BlockTreeImpl(blockStatement,
translateStats(block != null? block.getStatements() : null));
}
return false;
}
@Override
public boolean enterForNode(final ForNode forNode) {
if (forNode.isForIn()) {
curStat = new ForInLoopTreeImpl(forNode,
translateExpr(forNode.getInit()),
translateExpr(forNode.getModify()),
translateBlock(forNode.getBody()));
} else if (forNode.isForOf()) {
curStat = new ForOfLoopTreeImpl(forNode,
translateExpr(forNode.getInit()),
translateExpr(forNode.getModify()),
translateBlock(forNode.getBody()));
} else {
curStat = new ForLoopTreeImpl(forNode,
translateExpr(forNode.getInit()),
translateExpr(forNode.getTest()),
translateExpr(forNode.getModify()),
translateBlock(forNode.getBody()));
}
return false;
}
@Override
public boolean enterFunctionNode(final FunctionNode functionNode) {
assert !functionNode.isDeclared() || functionNode.isAnonymous() : "should not reach here for function declaration";
final List<? extends ExpressionTree> paramTrees = translateParameters(functionNode);
final BlockTree blockTree = (BlockTree) translateBlock(functionNode.getBody(), true);
curExpr = new FunctionExpressionTreeImpl(functionNode, paramTrees, blockTree);
return false;
}
@Override
public boolean enterIdentNode(final IdentNode identNode) {
curExpr = new IdentifierTreeImpl(identNode);
return false;
}
@Override
public boolean enterIfNode(final IfNode ifNode) {
curStat = new IfTreeImpl(ifNode,
translateExpr(ifNode.getTest()),
translateBlock(ifNode.getPass()),
translateBlock(ifNode.getFail()));
return false;
}
@Override
public boolean enterIndexNode(final IndexNode indexNode) {
curExpr = new ArrayAccessTreeImpl(indexNode,
translateExpr(indexNode.getBase()),
translateExpr(indexNode.getIndex()));
return false;
}
@Override
public boolean enterLabelNode(final LabelNode labelNode) {
curStat = new LabeledStatementTreeImpl(labelNode,
translateBlock(labelNode.getBody()));
return false;
}
@Override
public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
final Object value = literalNode.getValue();
if (value instanceof Lexer.RegexToken) {
curExpr = new RegExpLiteralTreeImpl(literalNode);
} else if (literalNode.isArray()) {
final List<Expression> exprNodes = literalNode.getElementExpressions();
final List<ExpressionTreeImpl> exprTrees = new ArrayList<>(exprNodes.size());
for (final Node node : exprNodes) {
if (node == null) {
exprTrees.add(null);
} else {
curExpr = null;
node.accept(this);
assert curExpr != null : "null for " + node;
exprTrees.add(curExpr);
}
}
curExpr = new ArrayLiteralTreeImpl(literalNode, exprTrees);
} else {
curExpr = new LiteralTreeImpl(literalNode);
}
return false;
}
@Override
public boolean enterObjectNode(final ObjectNode objectNode) {
final List<PropertyNode> propNodes = objectNode.getElements();
final List<? extends PropertyTree> propTrees = translateProperties(propNodes);
curExpr = new ObjectLiteralTreeImpl(objectNode, propTrees);
return false;
}
@Override
public boolean enterPropertyNode(final PropertyNode propertyNode) {
assert false : "should not reach here!";
return false;
}
@Override
public boolean enterReturnNode(final ReturnNode returnNode) {
curStat = new ReturnTreeImpl(returnNode,
translateExpr(returnNode.getExpression()));
return false;
}
@Override
public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
assert false : "should not reach here: RuntimeNode";
return false;
}
@Override
public boolean enterSplitNode(final SplitNode splitNode) {
assert false : "should not reach here!";
return false;
}
@Override
public boolean enterSwitchNode(final SwitchNode switchNode) {
final List<CaseNode> caseNodes = switchNode.getCases();
final List<CaseTreeImpl> caseTrees = new ArrayList<>(caseNodes.size());
for (final CaseNode caseNode : caseNodes) {
final Block body = caseNode.getBody();
caseTrees.add(
new CaseTreeImpl(caseNode,
translateExpr(caseNode.getTest()),
translateStats(body != null? body.getStatements() : null)));
}
curStat = new SwitchTreeImpl(switchNode,
translateExpr(switchNode.getExpression()),
caseTrees);
return false;
}
@Override
public boolean enterTemplateLiteral(final TemplateLiteral templateLiteral) {
curExpr = new TemplateLiteralTreeImpl(templateLiteral, translateExprs(templateLiteral.getExpressions()));
return false;
}
@Override
public boolean enterTernaryNode(final TernaryNode ternaryNode) {
curExpr = new ConditionalExpressionTreeImpl(ternaryNode,
translateExpr(ternaryNode.getTest()),
translateExpr(ternaryNode.getTrueExpression()),
translateExpr(ternaryNode.getFalseExpression()));
return false;
}
@Override
public boolean enterThrowNode(final ThrowNode throwNode) {
curStat = new ThrowTreeImpl(throwNode,
translateExpr(throwNode.getExpression()));
return false;
}
@Override
public boolean enterTryNode(final TryNode tryNode) {
final List<? extends CatchNode> catchNodes = tryNode.getCatches();
final List<CatchTreeImpl> catchTrees = new ArrayList<>(catchNodes.size());
for (final CatchNode catchNode : catchNodes) {
catchTrees.add(new CatchTreeImpl(catchNode,
translateExpr(catchNode.getException()),
(BlockTree) translateBlock(catchNode.getBody()),
translateExpr(catchNode.getExceptionCondition())));
}
curStat = new TryTreeImpl(tryNode,
(BlockTree) translateBlock(tryNode.getBody()),
catchTrees,
(BlockTree) translateBlock(tryNode.getFinallyBody()));
return false;
}
@Override
public boolean enterUnaryNode(final UnaryNode unaryNode) {
if (unaryNode.isTokenType(TokenType.NEW)) {
curExpr = new NewTreeImpl(unaryNode,
translateExpr(unaryNode.getExpression()));
} else if (unaryNode.isTokenType(TokenType.YIELD) ||
unaryNode.isTokenType(TokenType.YIELD_STAR)) {
curExpr = new YieldTreeImpl(unaryNode,
translateExpr(unaryNode.getExpression()));
} else if (unaryNode.isTokenType(TokenType.SPREAD_ARGUMENT) ||
unaryNode.isTokenType(TokenType.SPREAD_ARRAY)) {
curExpr = new SpreadTreeImpl(unaryNode,
translateExpr(unaryNode.getExpression()));
} else {
curExpr = new UnaryTreeImpl(unaryNode,
translateExpr(unaryNode.getExpression()));
}
return false;
}
@Override
public boolean enterVarNode(final VarNode varNode) {
final Expression initNode = varNode.getInit();
if (initNode instanceof FunctionNode && ((FunctionNode)initNode).isDeclared()) {
final FunctionNode funcNode = (FunctionNode) initNode;
final List<? extends ExpressionTree> paramTrees = translateParameters(funcNode);
final BlockTree blockTree = (BlockTree) translateBlock(funcNode.getBody(), true);
curStat = new FunctionDeclarationTreeImpl(varNode, paramTrees, blockTree);
} else if (initNode instanceof ClassNode && ((ClassNode)initNode).isStatement()) {
final ClassNode classNode = (ClassNode) initNode;
curStat = new ClassDeclarationTreeImpl(varNode,
translateIdent(classNode.getIdent()),
translateExpr(classNode.getClassHeritage()),
translateProperty(classNode.getConstructor()),
translateProperties(classNode.getClassElements()));
} else {
curStat = new VariableTreeImpl(varNode, translateIdent(varNode.getName()), translateExpr(initNode));
}
return false;
}
@Override
public boolean enterWhileNode(final WhileNode whileNode) {
final ExpressionTree condTree = translateExpr(whileNode.getTest());
final StatementTree statTree = translateBlock(whileNode.getBody());
if (whileNode.isDoWhile()) {
curStat = new DoWhileLoopTreeImpl(whileNode, condTree, statTree);
} else {
curStat = new WhileLoopTreeImpl(whileNode, condTree, statTree);
}
return false;
}
@Override
public boolean enterWithNode(final WithNode withNode) {
curStat = new WithTreeImpl(withNode,
translateExpr(withNode.getExpression()),
translateBlock(withNode.getBody()));
return false;
}
@Override
public boolean enterClassNode(final ClassNode classNode) {
assert !classNode.isStatement(): "should not reach here for class declaration";
curExpr = new ClassExpressionTreeImpl(classNode,
translateIdent(classNode.getIdent()),
translateExpr(classNode.getClassHeritage()),
translateProperty(classNode.getConstructor()),
translateProperties(classNode.getClassElements()));
return false;
}
private StatementTree translateBlock(final Block blockNode) {
return translateBlock(blockNode, false);
}
private StatementTree translateBlock(final Block blockNode, final boolean sortStats) {
if (blockNode == null) {
return null;
}
curStat = null;
handleBlock(blockNode, sortStats);
return curStat;
}
private boolean handleBlock(final Block block, final boolean sortStats) {
if (block.isSynthetic()) {
final int statCount = block.getStatementCount();
switch (statCount) {
case 0: {
final EmptyNode emptyNode = new EmptyNode(-1, block.getToken(), block.getFinish());
curStat = new EmptyStatementTreeImpl(emptyNode);
return false;
}
case 1: {
curStat = translateStat(block.getStatements().get(0));
return false;
}
default: {
break;
}
}
}
final List<? extends Statement> stats = block.getStatements();
curStat = new BlockTreeImpl(block,
translateStats(sortStats? getOrderedStatements(stats) : stats));
return false;
}
private List<? extends Statement> getOrderedStatements(final List<? extends Statement> stats) {
final List<? extends Statement> statList = new ArrayList<>(stats);
statList.sort(Comparator.comparingInt(Node::getSourceOrder));
return statList;
}
private List<? extends StatementTree> translateStats(final List<? extends Statement> stats) {
if (stats == null) {
return null;
}
final List<StatementTreeImpl> statTrees = new ArrayList<>(stats.size());
for (final Statement stat : stats) {
curStat = null;
stat.accept(this);
assert curStat != null;
statTrees.add(curStat);
}
return statTrees;
}
private List<? extends ExpressionTree> translateParameters(final FunctionNode func) {
final Map<IdentNode, Expression> paramExprs = func.getParameterExpressions();
if (paramExprs != null) {
final List<IdentNode> params = func.getParameters();
final List<ExpressionTreeImpl> exprTrees = new ArrayList<>(params.size());
for (final IdentNode ident : params) {
final Expression expr = paramExprs.containsKey(ident)? paramExprs.get(ident) : ident;
curExpr = null;
expr.accept(this);
assert curExpr != null;
exprTrees.add(curExpr);
}
return exprTrees;
} else {
return translateExprs(func.getParameters());
}
}
private List<? extends ExpressionTree> translateExprs(final List<? extends Expression> exprs) {
if (exprs == null) {
return null;
}
final List<ExpressionTreeImpl> exprTrees = new ArrayList<>(exprs.size());
for (final Expression expr : exprs) {
curExpr = null;
expr.accept(this);
assert curExpr != null;
exprTrees.add(curExpr);
}
return exprTrees;
}
private ExpressionTreeImpl translateExpr(final Expression expr) {
if (expr == null) {
return null;
}
curExpr = null;
expr.accept(this);
assert curExpr != null : "null for " + expr;
return curExpr;
}
private StatementTreeImpl translateStat(final Statement stat) {
if (stat == null) {
return null;
}
curStat = null;
stat.accept(this);
assert curStat != null : "null for " + stat;
return curStat;
}
private static IdentifierTree translateIdent(final IdentNode ident) {
return new IdentifierTreeImpl(ident);
}
private List<? extends PropertyTree> translateProperties(final List<PropertyNode> propNodes) {
final List<PropertyTree> propTrees = new ArrayList<>(propNodes.size());
for (final PropertyNode propNode : propNodes) {
propTrees.add(translateProperty(propNode));
}
return propTrees;
}
private PropertyTree translateProperty(final PropertyNode propNode) {
return new PropertyTreeImpl(propNode,
translateExpr(propNode.getKey()),
translateExpr(propNode.getValue()),
(FunctionExpressionTree) translateExpr(propNode.getGetter()),
(FunctionExpressionTree) translateExpr(propNode.getSetter()));
}
private ModuleTree translateModule(final FunctionNode func) {
return func.getKind() == FunctionNode.Kind.MODULE?
ModuleTreeImpl.create(func) : null;
}
}