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