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