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