/* * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ 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; /** * This class translates from nashorn IR Node objects * to nashorn parser API Tree objects. */ final class IRTranslator extends SimpleNodeVisitor { public IRTranslator() { } // currently translated Statement private StatementTreeImpl curStat; // currently translated Expression private ExpressionTreeImpl curExpr; // entry point for translator 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 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 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 exprNodes = literalNode.getElementExpressions(); final List 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 propNodes = objectNode.getElements(); final List 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 caseNodes = switchNode.getCases(); final List 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 catchNodes = tryNode.getCatches(); final List 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 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; } /** * Callback for entering a ClassNode * * @param classNode the node * @return true if traversal should continue and node children be traversed, false otherwise */ @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) { // FIXME: revisit this! 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: { // fall through break; } } } final List stats = block.getStatements(); curStat = new BlockTreeImpl(block, translateStats(sortStats? getOrderedStatements(stats) : stats)); return false; } private List getOrderedStatements(final List stats) { final List statList = new ArrayList<>(stats); statList.sort(Comparator.comparingInt(Node::getSourceOrder)); return statList; } private List translateStats(final List stats) { if (stats == null) { return null; } final List 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 translateParameters(final FunctionNode func) { final Map paramExprs = func.getParameterExpressions(); if (paramExprs != null) { final List params = func.getParameters(); final List 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 translateExprs(final List exprs) { if (exprs == null) { return null; } final List 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 translateProperties(final List propNodes) { final List 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; } }