IRTranslator.java revision 1786:80120e9b3273
1/*
2 * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25package jdk.nashorn.api.tree;
26
27import java.util.ArrayList;
28import java.util.Comparator;
29import java.util.List;
30import java.util.Map;
31import jdk.nashorn.internal.ir.AccessNode;
32import jdk.nashorn.internal.ir.BinaryNode;
33import jdk.nashorn.internal.ir.Block;
34import jdk.nashorn.internal.ir.BlockStatement;
35import jdk.nashorn.internal.ir.BreakNode;
36import jdk.nashorn.internal.ir.CallNode;
37import jdk.nashorn.internal.ir.CaseNode;
38import jdk.nashorn.internal.ir.CatchNode;
39import jdk.nashorn.internal.ir.ClassNode;
40import jdk.nashorn.internal.ir.ContinueNode;
41import jdk.nashorn.internal.ir.DebuggerNode;
42import jdk.nashorn.internal.ir.EmptyNode;
43import jdk.nashorn.internal.ir.ErrorNode;
44import jdk.nashorn.internal.ir.Expression;
45import jdk.nashorn.internal.ir.ExpressionStatement;
46import jdk.nashorn.internal.ir.ForNode;
47import jdk.nashorn.internal.ir.FunctionNode;
48import jdk.nashorn.internal.ir.IdentNode;
49import jdk.nashorn.internal.ir.IfNode;
50import jdk.nashorn.internal.ir.IndexNode;
51import jdk.nashorn.internal.ir.LabelNode;
52import jdk.nashorn.internal.ir.LiteralNode;
53import jdk.nashorn.internal.ir.Node;
54import jdk.nashorn.internal.ir.ObjectNode;
55import jdk.nashorn.internal.ir.PropertyNode;
56import jdk.nashorn.internal.ir.ReturnNode;
57import jdk.nashorn.internal.ir.RuntimeNode;
58import jdk.nashorn.internal.ir.SplitNode;
59import jdk.nashorn.internal.ir.Statement;
60import jdk.nashorn.internal.ir.SwitchNode;
61import jdk.nashorn.internal.ir.TemplateLiteral;
62import jdk.nashorn.internal.ir.TernaryNode;
63import jdk.nashorn.internal.ir.ThrowNode;
64import jdk.nashorn.internal.ir.TryNode;
65import jdk.nashorn.internal.ir.UnaryNode;
66import jdk.nashorn.internal.ir.VarNode;
67import jdk.nashorn.internal.ir.WhileNode;
68import jdk.nashorn.internal.ir.WithNode;
69import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
70import jdk.nashorn.internal.parser.Lexer;
71import jdk.nashorn.internal.parser.TokenType;
72
73/**
74 * This class translates from nashorn IR Node objects
75 * to nashorn parser API Tree objects.
76 */
77final class IRTranslator extends SimpleNodeVisitor {
78
79    public IRTranslator() {
80    }
81
82    // currently translated Statement
83    private StatementTreeImpl curStat;
84    // currently translated Expression
85    private ExpressionTreeImpl curExpr;
86
87    // entry point for translator
88    CompilationUnitTree translate(final FunctionNode node) {
89        if (node == null) {
90            return null;
91        }
92
93        assert node.getKind() == FunctionNode.Kind.SCRIPT ||
94                node.getKind() == FunctionNode.Kind.MODULE :
95                "script or module function expected";
96
97        final Block body = node.getBody();
98        return new CompilationUnitTreeImpl(node,
99                translateStats(body != null? getOrderedStatements(body.getStatements()) : null),
100                translateModule(node));
101    }
102
103    @Override
104    public boolean enterAccessNode(final AccessNode accessNode) {
105        curExpr = new MemberSelectTreeImpl(accessNode, translateExpr(accessNode.getBase()));
106        return false;
107    }
108
109    @Override
110    public boolean enterBlock(final Block block) {
111        return handleBlock(block, false);
112    }
113
114    @Override
115    public boolean enterBinaryNode(final BinaryNode binaryNode) {
116        if (binaryNode.isAssignment()) {
117            final ExpressionTree srcTree = translateExpr(binaryNode.getAssignmentSource());
118            final ExpressionTree destTree = translateExpr(binaryNode.getAssignmentDest());
119
120            if (binaryNode.isTokenType(TokenType.ASSIGN)) {
121                curExpr = new AssignmentTreeImpl(binaryNode, destTree, srcTree);
122            } else {
123                curExpr = new CompoundAssignmentTreeImpl(binaryNode, destTree, srcTree);
124            }
125        } else {
126            final ExpressionTree leftTree = translateExpr(binaryNode.lhs());
127            final ExpressionTree rightTree = translateExpr(binaryNode.rhs());
128
129            if (binaryNode.isTokenType(TokenType.INSTANCEOF)) {
130                curExpr = new InstanceOfTreeImpl(binaryNode, leftTree, rightTree);
131            } else {
132                curExpr = new BinaryTreeImpl(binaryNode, leftTree, rightTree);
133            }
134        }
135
136        return false;
137    }
138
139    @Override
140    public boolean enterBreakNode(final BreakNode breakNode) {
141        curStat = new BreakTreeImpl(breakNode);
142        return false;
143    }
144
145    @Override
146    public boolean enterCallNode(final CallNode callNode) {
147        curExpr = null;
148        callNode.getFunction().accept(this);
149        final ExpressionTree funcTree = curExpr;
150        final List<? extends ExpressionTree> argTrees = translateExprs(callNode.getArgs());
151        curExpr = new FunctionCallTreeImpl(callNode, funcTree, argTrees);
152        return false;
153    }
154
155    @Override
156    public boolean enterCaseNode(final CaseNode caseNode) {
157        assert false : "should not reach here!";
158        return false;
159    }
160
161    @Override
162    public boolean enterCatchNode(final CatchNode catchNode) {
163        assert false : "should not reach here";
164        return false;
165    }
166
167    @Override
168    public boolean enterContinueNode(final ContinueNode continueNode) {
169        curStat = new ContinueTreeImpl(continueNode);
170        return false;
171    }
172
173    @Override
174    public boolean enterDebuggerNode(final DebuggerNode debuggerNode) {
175        curStat = new DebuggerTreeImpl(debuggerNode);
176        return false;
177    }
178
179    @Override
180    public boolean enterEmptyNode(final EmptyNode emptyNode) {
181        curStat = new EmptyStatementTreeImpl(emptyNode);
182        return false;
183    }
184
185    @Override
186    public boolean enterErrorNode(final ErrorNode errorNode) {
187        curExpr = new ErroneousTreeImpl(errorNode);
188        return false;
189    }
190
191    @Override
192    public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
193        if (expressionStatement.destructuringDeclarationType() != null) {
194            final ExpressionTree expr = translateExpr(expressionStatement.getExpression());
195            assert expr instanceof AssignmentTree : "destructuring decl. statement does not have assignment";
196            final AssignmentTree assign = (AssignmentTree)expr;
197            curStat = new DestructuringDeclTreeImpl(expressionStatement, assign.getVariable(), assign.getExpression());
198        } else {
199            curStat = new ExpressionStatementTreeImpl(expressionStatement,
200                translateExpr(expressionStatement.getExpression()));
201        }
202        return false;
203    }
204
205    @Override
206    public boolean enterBlockStatement(final BlockStatement blockStatement) {
207        final Block block = blockStatement.getBlock();
208        if (blockStatement.isSynthetic()) {
209            assert block != null && block.getStatements() != null && block.getStatements().size() == 1;
210            curStat = translateStat(block.getStatements().get(0));
211        } else {
212            curStat = new BlockTreeImpl(blockStatement,
213                translateStats(block != null? block.getStatements() : null));
214        }
215        return false;
216    }
217
218    @Override
219    public boolean enterForNode(final ForNode forNode) {
220        if (forNode.isForIn()) {
221            curStat = new ForInLoopTreeImpl(forNode,
222                    translateExpr(forNode.getInit()),
223                    translateExpr(forNode.getModify()),
224                    translateBlock(forNode.getBody()));
225        } else if (forNode.isForOf()) {
226            curStat = new ForOfLoopTreeImpl(forNode,
227                    translateExpr(forNode.getInit()),
228                    translateExpr(forNode.getModify()),
229                    translateBlock(forNode.getBody()));
230        } else {
231            curStat = new ForLoopTreeImpl(forNode,
232                    translateExpr(forNode.getInit()),
233                    translateExpr(forNode.getTest()),
234                    translateExpr(forNode.getModify()),
235                    translateBlock(forNode.getBody()));
236        }
237
238        return false;
239    }
240
241    @Override
242    public boolean enterFunctionNode(final FunctionNode functionNode) {
243        assert !functionNode.isDeclared() || functionNode.isAnonymous() : "should not reach here for function declaration";
244
245        final List<? extends ExpressionTree> paramTrees = translateParameters(functionNode);
246        final BlockTree blockTree = (BlockTree) translateBlock(functionNode.getBody(), true);
247        curExpr = new FunctionExpressionTreeImpl(functionNode, paramTrees, blockTree);
248
249        return false;
250    }
251
252    @Override
253    public boolean enterIdentNode(final IdentNode identNode) {
254        curExpr = new IdentifierTreeImpl(identNode);
255        return false;
256    }
257
258    @Override
259    public boolean enterIfNode(final IfNode ifNode) {
260        curStat = new IfTreeImpl(ifNode,
261                translateExpr(ifNode.getTest()),
262                translateBlock(ifNode.getPass()),
263                translateBlock(ifNode.getFail()));
264        return false;
265    }
266
267    @Override
268    public boolean enterIndexNode(final IndexNode indexNode) {
269        curExpr = new ArrayAccessTreeImpl(indexNode,
270                translateExpr(indexNode.getBase()),
271                translateExpr(indexNode.getIndex()));
272        return false;
273    }
274
275    @Override
276    public boolean enterLabelNode(final LabelNode labelNode) {
277        curStat = new LabeledStatementTreeImpl(labelNode,
278                translateBlock(labelNode.getBody()));
279        return false;
280    }
281
282    @Override
283    public boolean enterLiteralNode(final LiteralNode<?> literalNode) {
284        final Object value = literalNode.getValue();
285        if (value instanceof Lexer.RegexToken) {
286            curExpr = new RegExpLiteralTreeImpl(literalNode);
287        } else if (literalNode.isArray()) {
288            final List<Expression> exprNodes = literalNode.getElementExpressions();
289            final List<ExpressionTreeImpl> exprTrees = new ArrayList<>(exprNodes.size());
290            for (final Node node : exprNodes) {
291                if (node == null) {
292                    exprTrees.add(null);
293                } else {
294                    curExpr = null;
295                    node.accept(this);
296                    assert curExpr != null : "null for " + node;
297                    exprTrees.add(curExpr);
298                }
299            }
300            curExpr = new ArrayLiteralTreeImpl(literalNode, exprTrees);
301        } else {
302            curExpr = new LiteralTreeImpl(literalNode);
303        }
304
305        return false;
306    }
307
308    @Override
309    public boolean enterObjectNode(final ObjectNode objectNode) {
310        final List<PropertyNode> propNodes = objectNode.getElements();
311        final List<? extends PropertyTree> propTrees = translateProperties(propNodes);
312        curExpr = new ObjectLiteralTreeImpl(objectNode, propTrees);
313        return false;
314    }
315
316    @Override
317    public boolean enterPropertyNode(final PropertyNode propertyNode) {
318        assert false : "should not reach here!";
319        return false;
320    }
321
322    @Override
323    public boolean enterReturnNode(final ReturnNode returnNode) {
324        curStat = new ReturnTreeImpl(returnNode,
325                translateExpr(returnNode.getExpression()));
326        return false;
327    }
328
329    @Override
330    public boolean enterRuntimeNode(final RuntimeNode runtimeNode) {
331        assert false : "should not reach here: RuntimeNode";
332        return false;
333    }
334
335    @Override
336    public boolean enterSplitNode(final SplitNode splitNode) {
337        assert false : "should not reach here!";
338        return false;
339    }
340
341    @Override
342    public boolean enterSwitchNode(final SwitchNode switchNode) {
343        final List<CaseNode> caseNodes = switchNode.getCases();
344        final List<CaseTreeImpl> caseTrees = new ArrayList<>(caseNodes.size());
345        for (final CaseNode caseNode : caseNodes) {
346            final Block body = caseNode.getBody();
347            caseTrees.add(
348                    new CaseTreeImpl(caseNode,
349                            translateExpr(caseNode.getTest()),
350                            translateStats(body != null? body.getStatements() : null)));
351        }
352
353        curStat = new SwitchTreeImpl(switchNode,
354                translateExpr(switchNode.getExpression()),
355                caseTrees);
356        return false;
357    }
358
359    @Override
360    public boolean enterTemplateLiteral(final TemplateLiteral templateLiteral) {
361        curExpr = new TemplateLiteralTreeImpl(templateLiteral, translateExprs(templateLiteral.getExpressions()));
362        return false;
363    }
364
365    @Override
366    public boolean enterTernaryNode(final TernaryNode ternaryNode) {
367        curExpr = new ConditionalExpressionTreeImpl(ternaryNode,
368                translateExpr(ternaryNode.getTest()),
369                translateExpr(ternaryNode.getTrueExpression()),
370                translateExpr(ternaryNode.getFalseExpression()));
371        return false;
372    }
373
374    @Override
375    public boolean enterThrowNode(final ThrowNode throwNode) {
376        curStat = new ThrowTreeImpl(throwNode,
377                translateExpr(throwNode.getExpression()));
378        return false;
379    }
380
381    @Override
382    public boolean enterTryNode(final TryNode tryNode) {
383        final List<? extends CatchNode> catchNodes = tryNode.getCatches();
384        final List<CatchTreeImpl> catchTrees = new ArrayList<>(catchNodes.size());
385        for (final CatchNode catchNode : catchNodes) {
386            catchTrees.add(new CatchTreeImpl(catchNode,
387                    translateIdent(catchNode.getException()),
388                    (BlockTree) translateBlock(catchNode.getBody()),
389                    translateExpr(catchNode.getExceptionCondition())));
390        }
391
392        curStat = new TryTreeImpl(tryNode,
393                (BlockTree) translateBlock(tryNode.getBody()),
394                catchTrees,
395                (BlockTree) translateBlock(tryNode.getFinallyBody()));
396
397        return false;
398    }
399
400    @Override
401    public boolean enterUnaryNode(final UnaryNode unaryNode) {
402        if (unaryNode.isTokenType(TokenType.NEW)) {
403            curExpr = new NewTreeImpl(unaryNode,
404                    translateExpr(unaryNode.getExpression()));
405        } else if (unaryNode.isTokenType(TokenType.YIELD) ||
406                unaryNode.isTokenType(TokenType.YIELD_STAR)) {
407            curExpr = new YieldTreeImpl(unaryNode,
408                    translateExpr(unaryNode.getExpression()));
409        } else if (unaryNode.isTokenType(TokenType.SPREAD_ARGUMENT) ||
410                unaryNode.isTokenType(TokenType.SPREAD_ARRAY)) {
411            curExpr = new SpreadTreeImpl(unaryNode,
412                    translateExpr(unaryNode.getExpression()));
413        } else {
414            curExpr = new UnaryTreeImpl(unaryNode,
415                    translateExpr(unaryNode.getExpression()));
416        }
417        return false;
418    }
419
420    @Override
421    public boolean enterVarNode(final VarNode varNode) {
422        final Expression initNode = varNode.getInit();
423        if (initNode instanceof FunctionNode && ((FunctionNode)initNode).isDeclared()) {
424            final FunctionNode funcNode = (FunctionNode) initNode;
425
426            final List<? extends ExpressionTree> paramTrees = translateParameters(funcNode);
427            final BlockTree blockTree = (BlockTree) translateBlock(funcNode.getBody(), true);
428            curStat = new FunctionDeclarationTreeImpl(varNode, paramTrees, blockTree);
429        } else if (initNode instanceof ClassNode && ((ClassNode)initNode).isStatement()) {
430            final ClassNode classNode = (ClassNode) initNode;
431
432            curStat = new ClassDeclarationTreeImpl(varNode,
433                    translateIdent(classNode.getIdent()),
434                    translateExpr(classNode.getClassHeritage()),
435                    translateProperty(classNode.getConstructor()),
436                    translateProperties(classNode.getClassElements()));
437        } else {
438            curStat = new VariableTreeImpl(varNode, translateIdent(varNode.getName()), translateExpr(initNode));
439        }
440
441        return false;
442    }
443
444    @Override
445    public boolean enterWhileNode(final WhileNode whileNode) {
446        final ExpressionTree condTree = translateExpr(whileNode.getTest());
447        final StatementTree statTree = translateBlock(whileNode.getBody());
448
449        if (whileNode.isDoWhile()) {
450            curStat = new DoWhileLoopTreeImpl(whileNode, condTree, statTree);
451        } else {
452            curStat = new WhileLoopTreeImpl(whileNode, condTree, statTree);
453        }
454
455        return false;
456    }
457
458    @Override
459    public boolean enterWithNode(final WithNode withNode) {
460        curStat = new WithTreeImpl(withNode,
461                translateExpr(withNode.getExpression()),
462                translateBlock(withNode.getBody()));
463
464        return false;
465    }
466
467    /**
468     * Callback for entering a ClassNode
469     *
470     * @param  classNode  the node
471     * @return true if traversal should continue and node children be traversed, false otherwise
472     */
473    @Override
474    public boolean enterClassNode(final ClassNode classNode) {
475        assert !classNode.isStatement(): "should not reach here for class declaration";
476
477        curExpr = new ClassExpressionTreeImpl(classNode,
478            translateIdent(classNode.getIdent()),
479            translateExpr(classNode.getClassHeritage()),
480            translateProperty(classNode.getConstructor()),
481            translateProperties(classNode.getClassElements()));
482
483        return false;
484    }
485
486    private StatementTree translateBlock(final Block blockNode) {
487        return translateBlock(blockNode, false);
488    }
489
490    private StatementTree translateBlock(final Block blockNode, final boolean sortStats) {
491        if (blockNode == null) {
492            return null;
493        }
494        curStat = null;
495        handleBlock(blockNode, sortStats);
496        return curStat;
497    }
498
499    private boolean handleBlock(final Block block, final boolean sortStats) {
500        // FIXME: revisit this!
501        if (block.isSynthetic()) {
502            final int statCount = block.getStatementCount();
503            switch (statCount) {
504                case 0: {
505                    final EmptyNode emptyNode = new EmptyNode(-1, block.getToken(), block.getFinish());
506                    curStat = new EmptyStatementTreeImpl(emptyNode);
507                    return false;
508                }
509                case 1: {
510                    curStat = translateStat(block.getStatements().get(0));
511                    return false;
512                }
513                default: {
514                    // fall through
515                    break;
516                }
517            }
518        }
519
520        final List<? extends Statement> stats = block.getStatements();
521        curStat = new BlockTreeImpl(block,
522            translateStats(sortStats? getOrderedStatements(stats) : stats));
523        return false;
524    }
525
526    private List<? extends Statement> getOrderedStatements(final List<? extends Statement> stats) {
527        final List<? extends Statement> statList = new ArrayList<>(stats);
528        statList.sort(Comparator.comparingInt(Node::getSourceOrder));
529        return statList;
530    }
531
532    private List<? extends StatementTree> translateStats(final List<? extends Statement> stats) {
533        if (stats == null) {
534            return null;
535        }
536        final List<StatementTreeImpl> statTrees = new ArrayList<>(stats.size());
537        for (final Statement stat : stats) {
538            curStat = null;
539            stat.accept(this);
540            assert curStat != null;
541            statTrees.add(curStat);
542        }
543        return statTrees;
544    }
545
546    private List<? extends ExpressionTree> translateParameters(final FunctionNode func) {
547        final Map<IdentNode, Expression> paramExprs = func.getParameterExpressions();
548        if (paramExprs != null) {
549            final List<IdentNode> params = func.getParameters();
550            final List<ExpressionTreeImpl> exprTrees = new ArrayList<>(params.size());
551            for (final IdentNode ident : params) {
552                final Expression expr = paramExprs.containsKey(ident)? paramExprs.get(ident) : ident;
553                curExpr = null;
554                expr.accept(this);
555                assert curExpr != null;
556                exprTrees.add(curExpr);
557            }
558            return exprTrees;
559        } else {
560            return translateExprs(func.getParameters());
561        }
562    }
563
564    private List<? extends ExpressionTree> translateExprs(final List<? extends Expression> exprs) {
565        if (exprs == null) {
566            return null;
567        }
568        final List<ExpressionTreeImpl> exprTrees = new ArrayList<>(exprs.size());
569        for (final Expression expr : exprs) {
570            curExpr = null;
571            expr.accept(this);
572            assert curExpr != null;
573            exprTrees.add(curExpr);
574        }
575        return exprTrees;
576    }
577
578    private ExpressionTreeImpl translateExpr(final Expression expr) {
579        if (expr == null) {
580            return null;
581        }
582
583        curExpr = null;
584        expr.accept(this);
585        assert curExpr != null : "null for " + expr;
586        return curExpr;
587    }
588
589    private StatementTreeImpl translateStat(final Statement stat) {
590        if (stat == null) {
591            return null;
592        }
593
594        curStat = null;
595        stat.accept(this);
596        assert curStat != null : "null for " + stat;
597        return curStat;
598    }
599
600    private static IdentifierTree translateIdent(final IdentNode ident) {
601        return new IdentifierTreeImpl(ident);
602    }
603
604    private List<? extends PropertyTree> translateProperties(final List<PropertyNode> propNodes) {
605        final List<PropertyTree> propTrees = new ArrayList<>(propNodes.size());
606        for (final PropertyNode propNode : propNodes) {
607            propTrees.add(translateProperty(propNode));
608        }
609        return propTrees;
610    }
611
612    private PropertyTree translateProperty(final PropertyNode propNode) {
613        return new PropertyTreeImpl(propNode,
614                    translateExpr(propNode.getKey()),
615                    translateExpr(propNode.getValue()),
616                    (FunctionExpressionTree) translateExpr(propNode.getGetter()),
617                    (FunctionExpressionTree) translateExpr(propNode.getSetter()));
618    }
619
620    private ModuleTree translateModule(final FunctionNode func) {
621        return func.getKind() == FunctionNode.Kind.MODULE?
622            ModuleTreeImpl.create(func) : null;
623    }
624}
625