PrintVisitor.java revision 1444:f436923c391f
1/*
2 * Copyright (c) 2010, 2013, 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 */
25
26package jdk.nashorn.internal.ir.debug;
27
28import java.util.List;
29import jdk.nashorn.internal.ir.BinaryNode;
30import jdk.nashorn.internal.ir.Block;
31import jdk.nashorn.internal.ir.BlockStatement;
32import jdk.nashorn.internal.ir.BreakNode;
33import jdk.nashorn.internal.ir.CaseNode;
34import jdk.nashorn.internal.ir.CatchNode;
35import jdk.nashorn.internal.ir.ContinueNode;
36import jdk.nashorn.internal.ir.ExpressionStatement;
37import jdk.nashorn.internal.ir.ForNode;
38import jdk.nashorn.internal.ir.FunctionNode;
39import jdk.nashorn.internal.ir.IdentNode;
40import jdk.nashorn.internal.ir.IfNode;
41import jdk.nashorn.internal.ir.JoinPredecessor;
42import jdk.nashorn.internal.ir.JoinPredecessorExpression;
43import jdk.nashorn.internal.ir.LabelNode;
44import jdk.nashorn.internal.ir.LocalVariableConversion;
45import jdk.nashorn.internal.ir.Node;
46import jdk.nashorn.internal.ir.SplitNode;
47import jdk.nashorn.internal.ir.Statement;
48import jdk.nashorn.internal.ir.SwitchNode;
49import jdk.nashorn.internal.ir.ThrowNode;
50import jdk.nashorn.internal.ir.TryNode;
51import jdk.nashorn.internal.ir.UnaryNode;
52import jdk.nashorn.internal.ir.VarNode;
53import jdk.nashorn.internal.ir.WhileNode;
54import jdk.nashorn.internal.ir.WithNode;
55import jdk.nashorn.internal.ir.visitor.SimpleNodeVisitor;
56
57/**
58 * Print out the AST as human readable source code.
59 * This works both on lowered and unlowered ASTs
60 *
61 * see the flags --print-parse and --print-lower-parse
62 */
63public final class PrintVisitor extends SimpleNodeVisitor {
64    /** Tab width */
65    private static final int TABWIDTH = 4;
66
67    /** Composing buffer. */
68    private final StringBuilder sb;
69
70    /** Indentation factor. */
71    private int indent;
72
73    /** Line separator. */
74    private final String EOLN;
75
76    /** Print line numbers */
77    private final boolean printLineNumbers;
78
79    /** Print inferred and optimistic types */
80    private final boolean printTypes;
81
82    private int lastLineNumber = -1;
83
84    /**
85     * Constructor.
86     */
87    public PrintVisitor() {
88        this(true, true);
89    }
90
91    /**
92     * Constructor
93     *
94     * @param printLineNumbers  should line number nodes be included in the output?
95     * @param printTypes        should we print optimistic and inferred types?
96     */
97    public PrintVisitor(final boolean printLineNumbers, final boolean printTypes) {
98        this.EOLN             = System.lineSeparator();
99        this.sb               = new StringBuilder();
100        this.printLineNumbers = printLineNumbers;
101        this.printTypes       = printTypes;
102    }
103
104    /**
105     * Constructor
106     *
107     * @param root  a node from which to start printing code
108     */
109    public PrintVisitor(final Node root) {
110        this(root, true, true);
111    }
112
113    /**
114     * Constructor
115     *
116     * @param root              a node from which to start printing code
117     * @param printLineNumbers  should line numbers nodes be included in the output?
118     * @param printTypes        should we print optimistic and inferred types?
119     */
120    public PrintVisitor(final Node root, final boolean printLineNumbers, final boolean printTypes) {
121        this(printLineNumbers, printTypes);
122        visit(root);
123    }
124
125    private void visit(final Node root) {
126        root.accept(this);
127    }
128
129    @Override
130    public String toString() {
131        return sb.append(EOLN).toString();
132    }
133
134    /**
135     * Insert spaces before a statement.
136     */
137    private void indent() {
138        for (int i = indent; i > 0; i--) {
139            sb.append(' ');
140        }
141    }
142
143    /*
144     * Visits.
145     */
146
147    @Override
148    public boolean enterDefault(final Node node) {
149        node.toString(sb, printTypes);
150        return false;
151    }
152
153    @Override
154    public boolean enterContinueNode(final ContinueNode node) {
155        node.toString(sb, printTypes);
156        printLocalVariableConversion(node);
157        return false;
158    }
159
160    @Override
161    public boolean enterBreakNode(final BreakNode node) {
162        node.toString(sb, printTypes);
163        printLocalVariableConversion(node);
164        return false;
165    }
166
167    @Override
168    public boolean enterThrowNode(final ThrowNode node) {
169        node.toString(sb, printTypes);
170        printLocalVariableConversion(node);
171        return false;
172    }
173
174    @Override
175    public boolean enterBlock(final Block block) {
176        sb.append(' ');
177        sb.append('{');
178
179        indent += TABWIDTH;
180
181        final List<Statement> statements = block.getStatements();
182
183        for (final Statement statement : statements) {
184            if (printLineNumbers) {
185                final int lineNumber = statement.getLineNumber();
186                sb.append('\n');
187                if (lineNumber != lastLineNumber) {
188                    indent();
189                    sb.append("[|").append(lineNumber).append("|];").append('\n');
190                }
191                lastLineNumber = lineNumber;
192            }
193            indent();
194
195            statement.accept(this);
196
197            int  lastIndex = sb.length() - 1;
198            char lastChar  = sb.charAt(lastIndex);
199            while (Character.isWhitespace(lastChar) && lastIndex >= 0) {
200                lastChar = sb.charAt(--lastIndex);
201            }
202
203            if (lastChar != '}' && lastChar != ';') {
204                sb.append(';');
205            }
206
207            if (statement.hasGoto()) {
208                sb.append(" [GOTO]");
209            }
210
211            if (statement.isTerminal()) {
212                sb.append(" [TERMINAL]");
213            }
214        }
215
216        indent -= TABWIDTH;
217
218        sb.append(EOLN);
219        indent();
220        sb.append('}');
221        printLocalVariableConversion(block);
222
223        return false;
224    }
225
226    @Override
227    public boolean enterBlockStatement(final BlockStatement statement) {
228        statement.getBlock().accept(this);
229        return false;
230    }
231
232    @Override
233    public boolean enterBinaryNode(final BinaryNode binaryNode) {
234        binaryNode.lhs().accept(this);
235        sb.append(' ');
236        sb.append(binaryNode.tokenType());
237        sb.append(' ');
238        binaryNode.rhs().accept(this);
239        return false;
240    }
241
242    @Override
243    public boolean enterJoinPredecessorExpression(final JoinPredecessorExpression expr) {
244        expr.getExpression().accept(this);
245        printLocalVariableConversion(expr);
246        return false;
247    }
248
249    @Override
250    public boolean enterIdentNode(final IdentNode identNode) {
251        identNode.toString(sb, printTypes);
252        printLocalVariableConversion(identNode);
253        return true;
254    }
255
256    private void printLocalVariableConversion(final JoinPredecessor joinPredecessor) {
257        LocalVariableConversion.toString(joinPredecessor.getLocalVariableConversion(), sb);
258    }
259
260    @Override
261    public boolean enterUnaryNode(final UnaryNode unaryNode) {
262        unaryNode.toString(sb, new Runnable() {
263            @Override
264            public void run() {
265                unaryNode.getExpression().accept(PrintVisitor.this);
266            }
267        }, printTypes);
268        return false;
269    }
270
271    @Override
272    public boolean enterExpressionStatement(final ExpressionStatement expressionStatement) {
273        expressionStatement.getExpression().accept(this);
274        return false;
275    }
276
277    @Override
278    public boolean enterForNode(final ForNode forNode) {
279        forNode.toString(sb, printTypes);
280        forNode.getBody().accept(this);
281        return false;
282    }
283
284    @Override
285    public boolean enterFunctionNode(final FunctionNode functionNode) {
286        functionNode.toString(sb, printTypes);
287        enterBlock(functionNode.getBody());
288        return false;
289    }
290
291    @Override
292    public boolean enterIfNode(final IfNode ifNode) {
293        ifNode.toString(sb, printTypes);
294        ifNode.getPass().accept(this);
295
296        final Block fail = ifNode.getFail();
297
298        if (fail != null) {
299            sb.append(" else ");
300            fail.accept(this);
301        }
302        if(ifNode.getLocalVariableConversion() != null) {
303            assert fail == null;
304            sb.append(" else ");
305            printLocalVariableConversion(ifNode);
306            sb.append(";");
307        }
308        return false;
309    }
310
311    @Override
312    public boolean enterLabelNode(final LabelNode labeledNode) {
313        indent -= TABWIDTH;
314        indent();
315        indent += TABWIDTH;
316        labeledNode.toString(sb, printTypes);
317        labeledNode.getBody().accept(this);
318        printLocalVariableConversion(labeledNode);
319        return false;
320    }
321
322    @Override
323    public boolean enterSplitNode(final SplitNode splitNode) {
324        splitNode.toString(sb, printTypes);
325        sb.append(EOLN);
326        indent += TABWIDTH;
327        indent();
328        return true;
329    }
330
331    @Override
332    public Node leaveSplitNode(final SplitNode splitNode) {
333        sb.append("</split>");
334        sb.append(EOLN);
335        indent -= TABWIDTH;
336        indent();
337        return splitNode;
338    }
339
340    @Override
341    public boolean enterSwitchNode(final SwitchNode switchNode) {
342        switchNode.toString(sb, printTypes);
343        sb.append(" {");
344
345        final List<CaseNode> cases = switchNode.getCases();
346
347        for (final CaseNode caseNode : cases) {
348            sb.append(EOLN);
349            indent();
350            caseNode.toString(sb, printTypes);
351            printLocalVariableConversion(caseNode);
352            indent += TABWIDTH;
353            caseNode.getBody().accept(this);
354            indent -= TABWIDTH;
355            sb.append(EOLN);
356        }
357        if(switchNode.getLocalVariableConversion() != null) {
358            sb.append(EOLN);
359            indent();
360            sb.append("default: ");
361            printLocalVariableConversion(switchNode);
362            sb.append("{}");
363        }
364        sb.append(EOLN);
365        indent();
366        sb.append("}");
367
368        return false;
369    }
370
371    @Override
372    public boolean enterTryNode(final TryNode tryNode) {
373        tryNode.toString(sb, printTypes);
374        printLocalVariableConversion(tryNode);
375        tryNode.getBody().accept(this);
376
377        final List<Block> catchBlocks = tryNode.getCatchBlocks();
378
379        for (final Block catchBlock : catchBlocks) {
380            final CatchNode catchNode = (CatchNode)catchBlock.getStatements().get(0);
381            catchNode.toString(sb, printTypes);
382            catchNode.getBody().accept(this);
383        }
384
385        final Block finallyBody = tryNode.getFinallyBody();
386
387        if (finallyBody != null) {
388            sb.append(" finally ");
389            finallyBody.accept(this);
390        }
391
392        for (final Block inlinedFinally : tryNode.getInlinedFinallies()) {
393            inlinedFinally.accept(this);
394        }
395        return false;
396    }
397
398    @Override
399    public boolean enterVarNode(final VarNode varNode) {
400        sb.append("var ");
401        varNode.getName().toString(sb, printTypes);
402        printLocalVariableConversion(varNode.getName());
403        final Node init = varNode.getInit();
404        if (init != null) {
405            sb.append(" = ");
406            init.accept(this);
407        }
408
409        return false;
410    }
411
412    @Override
413    public boolean enterWhileNode(final WhileNode whileNode) {
414        printLocalVariableConversion(whileNode);
415        if (whileNode.isDoWhile()) {
416            sb.append("do");
417            whileNode.getBody().accept(this);
418            sb.append(' ');
419            whileNode.toString(sb, printTypes);
420        } else {
421            whileNode.toString(sb, printTypes);
422            whileNode.getBody().accept(this);
423        }
424
425        return false;
426    }
427
428    @Override
429    public boolean enterWithNode(final WithNode withNode) {
430        withNode.toString(sb, printTypes);
431        withNode.getBody().accept(this);
432
433        return false;
434    }
435
436}
437