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 26 package jdk.nashorn.internal.ir.debug; 27 28 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.CALLSITE_PROGRAM_POINT_SHIFT; 29 import static jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor.FLAGS_MASK; 30 31 import java.io.File; 32 import java.io.FileNotFoundException; 33 import java.io.FileOutputStream; 34 import java.io.PrintWriter; 35 import java.util.HashMap; 36 import java.util.HashSet; 37 import java.util.Iterator; 38 import java.util.LinkedHashSet; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.Set; 42 import jdk.internal.org.objectweb.asm.Attribute; 43 import jdk.internal.org.objectweb.asm.Handle; 44 import jdk.internal.org.objectweb.asm.Label; 45 import jdk.internal.org.objectweb.asm.Opcodes; 46 import jdk.internal.org.objectweb.asm.Type; 47 import jdk.internal.org.objectweb.asm.signature.SignatureReader; 48 import jdk.internal.org.objectweb.asm.util.Printer; 49 import jdk.internal.org.objectweb.asm.util.TraceSignatureVisitor; 50 import jdk.nashorn.internal.runtime.ScriptEnvironment; 51 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 52 53 /** 54 * Pretty printer for --print-code. 55 * Also supports dot formats if --print-code has arguments 56 */ 57 public final class NashornTextifier extends Printer { 58 59 private String currentClassName; 60 private Iterator<Label> labelIter; 61 private Graph graph; 62 private String currentBlock; 63 64 // Following variables are used to govern the state of collapsing long sequences of NOP. 65 /** True if the last instruction was a NOP. */ 66 private boolean lastWasNop = false; 67 /** True if ellipse ("...") was emitted in place of a second NOP. */ 68 private boolean lastWasEllipse = false; 69 70 private static final int INTERNAL_NAME = 0; 71 private static final int FIELD_DESCRIPTOR = 1; 72 private static final int FIELD_SIGNATURE = 2; 73 private static final int METHOD_DESCRIPTOR = 3; 74 private static final int METHOD_SIGNATURE = 4; 75 private static final int CLASS_SIGNATURE = 5; 76 77 private final String tab = " "; 78 private final String tab2 = " "; 79 private final String tab3 = " "; 80 81 private Map<Label, String> labelNames; 82 83 private boolean localVarsStarted = false; 84 85 private NashornClassReader cr; 86 private ScriptEnvironment env; 87 88 /** 89 * Constructs a new {@link NashornTextifier}. <i>Subclasses must not use this 90 * constructor</i>. Instead, they must use the {@link #NashornTextifier(int)} 91 * version. 92 * @param env script environment 93 * @param cr a customized classreader for gathering, among other things, label 94 * information 95 */ 96 public NashornTextifier(final ScriptEnvironment env, final NashornClassReader cr) { 97 this(Opcodes.ASM5); 98 this.env = env; 99 this.cr = cr; 100 } 101 102 private NashornTextifier(final ScriptEnvironment env, final NashornClassReader cr, final Iterator<Label> labelIter, final Graph graph) { 103 this(env, cr); 104 this.labelIter = labelIter; 105 this.graph = graph; 106 } 107 108 /** 109 * Constructs a new {@link NashornTextifier}. 110 * 111 * @param api 112 * the ASM API version implemented by this visitor. Must be one 113 * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}. 114 */ 115 protected NashornTextifier(final int api) { 116 super(api); 117 } 118 119 @Override 120 public void visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces) { 121 final int major = version & 0xFFFF; 122 final int minor = version >>> 16; 123 124 currentClassName = name; 125 126 final StringBuilder sb = new StringBuilder(); 127 sb.append("// class version "). 128 append(major). 129 append('.'). 130 append(minor).append(" ("). 131 append(version). 132 append(")\n"); 133 134 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 135 sb.append("// DEPRECATED\n"); 136 } 137 138 sb.append("// access flags 0x"). //TODO TRANSLATE TO WHAT THEY MEAN 139 append(Integer.toHexString(access).toUpperCase()). 140 append('\n'); 141 142 appendDescriptor(sb, CLASS_SIGNATURE, signature); 143 if (signature != null) { 144 final TraceSignatureVisitor sv = new TraceSignatureVisitor(access); 145 final SignatureReader r = new SignatureReader(signature); 146 r.accept(sv); 147 sb.append("// declaration: "). 148 append(name). 149 append(sv.getDeclaration()). 150 append('\n'); 151 } 152 153 appendAccess(sb, access & ~Opcodes.ACC_SUPER); 154 if ((access & Opcodes.ACC_ANNOTATION) != 0) { 155 sb.append("@interface "); 156 } else if ((access & Opcodes.ACC_INTERFACE) != 0) { 157 sb.append("interface "); 158 } else if ((access & Opcodes.ACC_ENUM) == 0) { 159 sb.append("class "); 160 } 161 appendDescriptor(sb, INTERNAL_NAME, name); 162 163 if (superName != null && !"java/lang/Object".equals(superName)) { 164 sb.append(" extends "); 165 appendDescriptor(sb, INTERNAL_NAME, superName); 166 sb.append(' '); 167 } 168 if (interfaces != null && interfaces.length > 0) { 169 sb.append(" implements "); 170 for (final String interface1 : interfaces) { 171 appendDescriptor(sb, INTERNAL_NAME, interface1); 172 sb.append(' '); 173 } 174 } 175 sb.append(" {\n"); 176 177 addText(sb); 178 } 179 180 @Override 181 public void visitSource(final String file, final String debug) { 182 final StringBuilder sb = new StringBuilder(); 183 if (file != null) { 184 sb.append(tab). 185 append("// compiled from: "). 186 append(file). 187 append('\n'); 188 } 189 if (debug != null) { 190 sb.append(tab). 191 append("// debug info: "). 192 append(debug). 193 append('\n'); 194 } 195 if (sb.length() > 0) { 196 addText(sb); 197 } 198 } 199 200 @Override 201 public void visitOuterClass(final String owner, final String name, final String desc) { 202 final StringBuilder sb = new StringBuilder(); 203 sb.append(tab).append("outer class "); 204 appendDescriptor(sb, INTERNAL_NAME, owner); 205 sb.append(' '); 206 if (name != null) { 207 sb.append(name).append(' '); 208 } 209 appendDescriptor(sb, METHOD_DESCRIPTOR, desc); 210 sb.append('\n'); 211 addText(sb); 212 } 213 214 @Override 215 public NashornTextifier visitField(final int access, final String name, final String desc, final String signature, final Object value) { 216 final StringBuilder sb = new StringBuilder(); 217 // sb.append('\n'); 218 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 219 sb.append(tab).append("// DEPRECATED\n"); 220 } 221 222 /* sb.append(tab). 223 append("// access flags 0x"). 224 append(Integer.toHexString(access).toUpperCase()). 225 append('\n'); 226 */ 227 228 if (signature != null) { 229 sb.append(tab); 230 appendDescriptor(sb, FIELD_SIGNATURE, signature); 231 232 final TraceSignatureVisitor sv = new TraceSignatureVisitor(0); 233 final SignatureReader r = new SignatureReader(signature); 234 r.acceptType(sv); 235 sb.append(tab). 236 append("// declaration: "). 237 append(sv.getDeclaration()). 238 append('\n'); 239 } 240 241 sb.append(tab); 242 appendAccess(sb, access); 243 244 final String prunedDesc = desc.endsWith(";") ? desc.substring(0, desc.length() - 1) : desc; 245 appendDescriptor(sb, FIELD_DESCRIPTOR, prunedDesc); 246 sb.append(' ').append(name); 247 if (value != null) { 248 sb.append(" = "); 249 if (value instanceof String) { 250 sb.append('\"').append(value).append('\"'); 251 } else { 252 sb.append(value); 253 } 254 } 255 256 sb.append(";\n"); 257 addText(sb); 258 259 final NashornTextifier t = createNashornTextifier(); 260 addText(t.getText()); 261 262 return t; 263 } 264 265 @Override 266 public NashornTextifier visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions) { 267 268 graph = new Graph(name); 269 270 final List<Label> extraLabels = cr.getExtraLabels(currentClassName, name, desc); 271 this.labelIter = extraLabels == null ? null : extraLabels.iterator(); 272 273 final StringBuilder sb = new StringBuilder(); 274 275 sb.append('\n'); 276 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 277 sb.append(tab). 278 append("// DEPRECATED\n"); 279 } 280 281 sb.append(tab). 282 append("// access flags 0x"). 283 append(Integer.toHexString(access).toUpperCase()). 284 append('\n'); 285 286 if (signature != null) { 287 sb.append(tab); 288 appendDescriptor(sb, METHOD_SIGNATURE, signature); 289 290 final TraceSignatureVisitor v = new TraceSignatureVisitor(0); 291 final SignatureReader r = new SignatureReader(signature); 292 r.accept(v); 293 final String genericDecl = v.getDeclaration(); 294 final String genericReturn = v.getReturnType(); 295 final String genericExceptions = v.getExceptions(); 296 297 sb.append(tab). 298 append("// declaration: "). 299 append(genericReturn). 300 append(' '). 301 append(name). 302 append(genericDecl); 303 304 if (genericExceptions != null) { 305 sb.append(" throws ").append(genericExceptions); 306 } 307 sb.append('\n'); 308 } 309 310 sb.append(tab); 311 appendAccess(sb, access); 312 if ((access & Opcodes.ACC_NATIVE) != 0) { 313 sb.append("native "); 314 } 315 if ((access & Opcodes.ACC_VARARGS) != 0) { 316 sb.append("varargs "); 317 } 318 if ((access & Opcodes.ACC_BRIDGE) != 0) { 319 sb.append("bridge "); 320 } 321 322 sb.append(name); 323 appendDescriptor(sb, METHOD_DESCRIPTOR, desc); 324 if (exceptions != null && exceptions.length > 0) { 325 sb.append(" throws "); 326 for (final String exception : exceptions) { 327 appendDescriptor(sb, INTERNAL_NAME, exception); 328 sb.append(' '); 329 } 330 } 331 332 sb.append('\n'); 333 addText(sb); 334 335 final NashornTextifier t = createNashornTextifier(); 336 addText(t.getText()); 337 return t; 338 } 339 340 @Override 341 public void visitClassEnd() { 342 addText("}\n"); 343 } 344 345 @Override 346 public void visitFieldEnd() { 347 //empty 348 } 349 350 @Override 351 public void visitParameter(final String name, final int access) { 352 final StringBuilder sb = new StringBuilder(); 353 sb.append(tab2).append("// parameter "); 354 appendAccess(sb, access); 355 sb.append(' ').append(name == null ? "<no name>" : name) 356 .append('\n'); 357 addText(sb); 358 } 359 360 @Override 361 public void visitCode() { 362 //empty 363 } 364 365 @Override 366 public void visitFrame(final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack) { 367 final StringBuilder sb = new StringBuilder(); 368 sb.append("frame "); 369 switch (type) { 370 case Opcodes.F_NEW: 371 case Opcodes.F_FULL: 372 sb.append("full ["); 373 appendFrameTypes(sb, nLocal, local); 374 sb.append("] ["); 375 appendFrameTypes(sb, nStack, stack); 376 sb.append(']'); 377 break; 378 case Opcodes.F_APPEND: 379 sb.append("append ["); 380 appendFrameTypes(sb, nLocal, local); 381 sb.append(']'); 382 break; 383 case Opcodes.F_CHOP: 384 sb.append("chop ").append(nLocal); 385 break; 386 case Opcodes.F_SAME: 387 sb.append("same"); 388 break; 389 case Opcodes.F_SAME1: 390 sb.append("same1 "); 391 appendFrameTypes(sb, 1, stack); 392 break; 393 default: 394 assert false; 395 break; 396 } 397 sb.append('\n'); 398 sb.append('\n'); 399 addText(sb); 400 } 401 402 private StringBuilder appendOpcode(final StringBuilder sb, final int opcode) { 403 final Label next = getNextLabel(); 404 if (next instanceof NashornLabel) { 405 final int bci = next.getOffset(); 406 if (bci != -1) { 407 final String bcis = "" + bci; 408 for (int i = 0; i < 5 - bcis.length(); i++) { 409 sb.append(' '); 410 } 411 sb.append(bcis); 412 sb.append(' '); 413 } else { 414 sb.append(" "); 415 } 416 } 417 418 return sb.append(tab2).append(OPCODES[opcode].toLowerCase()); 419 } 420 421 private Label getNextLabel() { 422 return labelIter == null ? null : labelIter.next(); 423 } 424 425 @Override 426 public void visitInsn(final int opcode) { 427 if(opcode == Opcodes.NOP) { 428 if(lastWasEllipse) { 429 getNextLabel(); 430 return; 431 } else if(lastWasNop) { 432 getNextLabel(); 433 addText(" ...\n"); 434 lastWasEllipse = true; 435 return; 436 } else { 437 lastWasNop = true; 438 } 439 } else { 440 lastWasNop = lastWasEllipse = false; 441 } 442 final StringBuilder sb = new StringBuilder(); 443 appendOpcode(sb, opcode).append('\n'); 444 addText(sb); 445 checkNoFallThru(opcode, null); 446 } 447 448 @Override 449 public void visitIntInsn(final int opcode, final int operand) { 450 final StringBuilder sb = new StringBuilder(); 451 appendOpcode(sb, opcode) 452 .append(' ') 453 .append(opcode == Opcodes.NEWARRAY ? TYPES[operand] : Integer 454 .toString(operand)).append('\n'); 455 addText(sb); 456 } 457 458 @Override 459 public void visitVarInsn(final int opcode, final int var) { 460 final StringBuilder sb = new StringBuilder(); 461 appendOpcode(sb, opcode).append(' ').append(var).append('\n'); 462 addText(sb); 463 } 464 465 @Override 466 public void visitTypeInsn(final int opcode, final String type) { 467 final StringBuilder sb = new StringBuilder(); 468 appendOpcode(sb, opcode).append(' '); 469 appendDescriptor(sb, INTERNAL_NAME, type); 470 sb.append('\n'); 471 addText(sb); 472 } 473 474 @Override 475 public void visitFieldInsn(final int opcode, final String owner, final String name, final String desc) { 476 final StringBuilder sb = new StringBuilder(); 477 appendOpcode(sb, opcode).append(' '); 478 appendDescriptor(sb, INTERNAL_NAME, owner); 479 sb.append('.').append(name).append(" : "); 480 appendDescriptor(sb, FIELD_DESCRIPTOR, desc); 481 sb.append('\n'); 482 addText(sb); 483 } 484 485 @Override 486 public void visitMethodInsn(final int opcode, final String owner, final String name, final String desc, final boolean itf) { 487 final StringBuilder sb = new StringBuilder(); 488 appendOpcode(sb, opcode).append(' '); 489 appendDescriptor(sb, INTERNAL_NAME, owner); 490 sb.append('.').append(name); 491 appendDescriptor(sb, METHOD_DESCRIPTOR, desc); 492 sb.append('\n'); 493 addText(sb); 494 } 495 496 @Override 497 public void visitInvokeDynamicInsn(final String name, final String desc, final Handle bsm, final Object... bsmArgs) { 498 final StringBuilder sb = new StringBuilder(); 499 500 appendOpcode(sb, Opcodes.INVOKEDYNAMIC).append(' '); 501 sb.append(name); 502 appendDescriptor(sb, METHOD_DESCRIPTOR, desc); 503 final int len = sb.length(); 504 for (int i = 0; i < 80 - len ; i++) { 505 sb.append(' '); 506 } 507 sb.append(" ["); 508 appendHandle(sb, bsm); 509 if (bsmArgs.length == 0) { 510 sb.append("none"); 511 } else { 512 for (final Object cst : bsmArgs) { 513 if (cst instanceof String) { 514 appendStr(sb, (String)cst); 515 } else if (cst instanceof Type) { 516 sb.append(((Type)cst).getDescriptor()).append(".class"); 517 } else if (cst instanceof Handle) { 518 appendHandle(sb, (Handle)cst); 519 } else if (cst instanceof Integer) { 520 final int c = (Integer)cst; 521 final int pp = c >> CALLSITE_PROGRAM_POINT_SHIFT; 522 if (pp != 0) { 523 sb.append(" pp=").append(pp); 524 } 525 sb.append(NashornCallSiteDescriptor.toString(c & FLAGS_MASK)); 526 } else { 527 sb.append(cst); 528 } 529 sb.append(", "); 530 } 531 sb.setLength(sb.length() - 2); 532 } 533 534 sb.append("]\n"); 535 addText(sb); 536 } 537 538 private static final boolean noFallThru(final int opcode) { 539 switch (opcode) { 540 case Opcodes.GOTO: 541 case Opcodes.ATHROW: 542 case Opcodes.ARETURN: 543 case Opcodes.IRETURN: 544 case Opcodes.LRETURN: 545 case Opcodes.FRETURN: 546 case Opcodes.DRETURN: 547 return true; 548 default: 549 return false; 550 } 551 } 552 553 private void checkNoFallThru(final int opcode, final String to) { 554 if (noFallThru(opcode)) { 555 graph.setNoFallThru(currentBlock); 556 } 557 558 if (currentBlock != null && to != null) { 559 graph.addEdge(currentBlock, to); 560 } 561 } 562 563 @Override 564 public void visitJumpInsn(final int opcode, final Label label) { 565 final StringBuilder sb = new StringBuilder(); 566 appendOpcode(sb, opcode).append(' '); 567 final String to = appendLabel(sb, label); 568 sb.append('\n'); 569 addText(sb); 570 checkNoFallThru(opcode, to); 571 } 572 573 private void addText(final Object t) { 574 text.add(t); 575 if (currentBlock != null) { 576 graph.addText(currentBlock, t.toString()); 577 } 578 } 579 580 @Override 581 public void visitLabel(final Label label) { 582 final StringBuilder sb = new StringBuilder(); 583 sb.append("\n"); 584 final String name = appendLabel(sb, label); 585 sb.append(" [bci="); 586 sb.append(label.info); 587 sb.append("]"); 588 sb.append("\n"); 589 590 graph.addNode(name); 591 if (currentBlock != null && !graph.isNoFallThru(currentBlock)) { 592 graph.addEdge(currentBlock, name); 593 } 594 currentBlock = name; 595 addText(sb); 596 } 597 598 @Override 599 public void visitLdcInsn(final Object cst) { 600 final StringBuilder sb = new StringBuilder(); 601 appendOpcode(sb, Opcodes.LDC).append(' '); 602 if (cst instanceof String) { 603 appendStr(sb, (String) cst); 604 } else if (cst instanceof Type) { 605 sb.append(((Type) cst).getDescriptor()).append(".class"); 606 } else { 607 sb.append(cst); 608 } 609 sb.append('\n'); 610 addText(sb); 611 } 612 613 @Override 614 public void visitIincInsn(final int var, final int increment) { 615 final StringBuilder sb = new StringBuilder(); 616 appendOpcode(sb, Opcodes.IINC).append(' '); 617 sb.append(var).append(' ') 618 .append(increment).append('\n'); 619 addText(sb); 620 } 621 622 @Override 623 public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) { 624 final StringBuilder sb = new StringBuilder(); 625 appendOpcode(sb, Opcodes.TABLESWITCH).append(' '); 626 for (int i = 0; i < labels.length; ++i) { 627 sb.append(tab3).append(min + i).append(": "); 628 final String to = appendLabel(sb, labels[i]); 629 graph.addEdge(currentBlock, to); 630 sb.append('\n'); 631 } 632 sb.append(tab3).append("default: "); 633 appendLabel(sb, dflt); 634 sb.append('\n'); 635 addText(sb); 636 } 637 638 @Override 639 public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { 640 final StringBuilder sb = new StringBuilder(); 641 appendOpcode(sb, Opcodes.LOOKUPSWITCH).append(' '); 642 for (int i = 0; i < labels.length; ++i) { 643 sb.append(tab3).append(keys[i]).append(": "); 644 final String to = appendLabel(sb, labels[i]); 645 graph.addEdge(currentBlock, to); 646 sb.append('\n'); 647 } 648 sb.append(tab3).append("default: "); 649 final String to = appendLabel(sb, dflt); 650 graph.addEdge(currentBlock, to); 651 sb.append('\n'); 652 addText(sb.toString()); 653 } 654 655 @Override 656 public void visitMultiANewArrayInsn(final String desc, final int dims) { 657 final StringBuilder sb = new StringBuilder(); 658 appendOpcode(sb, Opcodes.MULTIANEWARRAY).append(' '); 659 appendDescriptor(sb, FIELD_DESCRIPTOR, desc); 660 sb.append(' ').append(dims).append('\n'); 661 addText(sb); 662 } 663 664 @Override 665 public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { 666 final StringBuilder sb = new StringBuilder(); 667 sb.append(tab2).append("try "); 668 final String from = appendLabel(sb, start); 669 sb.append(' '); 670 appendLabel(sb, end); 671 sb.append(' '); 672 final String to = appendLabel(sb, handler); 673 sb.append(' '); 674 appendDescriptor(sb, INTERNAL_NAME, type); 675 sb.append('\n'); 676 addText(sb); 677 graph.setIsCatch(to, type); 678 graph.addTryCatch(from, to); 679 } 680 681 @Override 682 public void visitLocalVariable(final String name, final String desc,final String signature, final Label start, final Label end, final int index) { 683 684 final StringBuilder sb = new StringBuilder(); 685 if (!localVarsStarted) { 686 text.add("\n"); 687 localVarsStarted = true; 688 graph.addNode("vars"); 689 currentBlock = "vars"; 690 } 691 692 sb.append(tab2).append("local ").append(name).append(' '); 693 final int len = sb.length(); 694 for (int i = 0; i < 25 - len; i++) { 695 sb.append(' '); 696 } 697 String label; 698 699 label = appendLabel(sb, start); 700 for (int i = 0; i < 5 - label.length(); i++) { 701 sb.append(' '); 702 } 703 label = appendLabel(sb, end); 704 for (int i = 0; i < 5 - label.length(); i++) { 705 sb.append(' '); 706 } 707 708 sb.append(index).append(tab2); 709 710 appendDescriptor(sb, FIELD_DESCRIPTOR, desc); 711 sb.append('\n'); 712 713 if (signature != null) { 714 sb.append(tab2); 715 appendDescriptor(sb, FIELD_SIGNATURE, signature); 716 717 final TraceSignatureVisitor sv = new TraceSignatureVisitor(0); 718 final SignatureReader r = new SignatureReader(signature); 719 r.acceptType(sv); 720 sb.append(tab2).append("// declaration: ") 721 .append(sv.getDeclaration()).append('\n'); 722 } 723 addText(sb.toString()); 724 } 725 726 @Override 727 public void visitLineNumber(final int line, final Label start) { 728 final StringBuilder sb = new StringBuilder(); 729 sb.append("<line "); 730 sb.append(line); 731 sb.append(">\n"); 732 addText(sb.toString()); 733 } 734 735 @Override 736 public void visitMaxs(final int maxStack, final int maxLocals) { 737 final StringBuilder sb = new StringBuilder(); 738 sb.append('\n'); 739 sb.append(tab2).append("max stack = ").append(maxStack); 740 sb.append(", max locals = ").append(maxLocals).append('\n'); 741 addText(sb.toString()); 742 } 743 744 private void printToDir(final Graph g) { 745 if (env._print_code_dir != null) { 746 final File dir = new File(env._print_code_dir); 747 if (!dir.exists() && !dir.mkdirs()) { 748 throw new RuntimeException(dir.toString()); 749 } 750 751 File file; 752 int uniqueId = 0; 753 do { 754 final String fileName = g.getName() + (uniqueId == 0 ? "" : "_" + uniqueId) + ".dot"; 755 file = new File(dir, fileName); 756 uniqueId++; 757 } while (file.exists()); 758 759 try (PrintWriter pw = new PrintWriter(new FileOutputStream(file))) { 760 pw.println(g); 761 } catch (final FileNotFoundException e) { 762 throw new RuntimeException(e); 763 } 764 } 765 } 766 767 @Override 768 public void visitMethodEnd() { 769 //here we need to do several bytecode guesses best upon the ldc instructions. 770 //for each instruction, assign bci. for an ldc/w/2w, guess a byte and keep 771 //iterating. if the next label is wrong, backtrack. 772 if (env._print_code_func == null || env._print_code_func.equals(graph.getName())) { 773 if (env._print_code_dir != null) { 774 printToDir(graph); 775 } 776 } 777 } 778 779 /** 780 * Creates a new TraceVisitor instance. 781 * 782 * @return a new TraceVisitor. 783 */ 784 protected NashornTextifier createNashornTextifier() { 785 return new NashornTextifier(env, cr, labelIter, graph); 786 } 787 788 private static void appendDescriptor(final StringBuilder sb, final int type, final String desc) { 789 if (desc != null) { 790 if (type == CLASS_SIGNATURE || type == FIELD_SIGNATURE || type == METHOD_SIGNATURE) { 791 sb.append("// signature ").append(desc).append('\n'); 792 } else { 793 appendShortDescriptor(sb, desc); 794 } 795 } 796 } 797 798 private String appendLabel(final StringBuilder sb, final Label l) { 799 if (labelNames == null) { 800 labelNames = new HashMap<>(); 801 } 802 String name = labelNames.get(l); 803 if (name == null) { 804 name = "L" + labelNames.size(); 805 labelNames.put(l, name); 806 } 807 sb.append(name); 808 return name; 809 } 810 811 private static void appendHandle(final StringBuilder sb, final Handle h) { 812 switch (h.getTag()) { 813 case Opcodes.H_GETFIELD: 814 sb.append("getfield"); 815 break; 816 case Opcodes.H_GETSTATIC: 817 sb.append("getstatic"); 818 break; 819 case Opcodes.H_PUTFIELD: 820 sb.append("putfield"); 821 break; 822 case Opcodes.H_PUTSTATIC: 823 sb.append("putstatic"); 824 break; 825 case Opcodes.H_INVOKEINTERFACE: 826 sb.append("interface"); 827 break; 828 case Opcodes.H_INVOKESPECIAL: 829 sb.append("special"); 830 break; 831 case Opcodes.H_INVOKESTATIC: 832 sb.append("static"); 833 break; 834 case Opcodes.H_INVOKEVIRTUAL: 835 sb.append("virtual"); 836 break; 837 case Opcodes.H_NEWINVOKESPECIAL: 838 sb.append("new_special"); 839 break; 840 default: 841 assert false; 842 break; 843 } 844 sb.append(" '"); 845 sb.append(h.getName()); 846 sb.append("'"); 847 } 848 849 private static void appendAccess(final StringBuilder sb, final int access) { 850 if ((access & Opcodes.ACC_PUBLIC) != 0) { 851 sb.append("public "); 852 } 853 if ((access & Opcodes.ACC_PRIVATE) != 0) { 854 sb.append("private "); 855 } 856 if ((access & Opcodes.ACC_PROTECTED) != 0) { 857 sb.append("protected "); 858 } 859 if ((access & Opcodes.ACC_FINAL) != 0) { 860 sb.append("final "); 861 } 862 if ((access & Opcodes.ACC_STATIC) != 0) { 863 sb.append("static "); 864 } 865 if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) { 866 sb.append("synchronized "); 867 } 868 if ((access & Opcodes.ACC_VOLATILE) != 0) { 869 sb.append("volatile "); 870 } 871 if ((access & Opcodes.ACC_TRANSIENT) != 0) { 872 sb.append("transient "); 873 } 874 if ((access & Opcodes.ACC_ABSTRACT) != 0) { 875 sb.append("abstract "); 876 } 877 if ((access & Opcodes.ACC_STRICT) != 0) { 878 sb.append("strictfp "); 879 } 880 if ((access & Opcodes.ACC_SYNTHETIC) != 0) { 881 sb.append("synthetic "); 882 } 883 if ((access & Opcodes.ACC_MANDATED) != 0) { 884 sb.append("mandated "); 885 } 886 if ((access & Opcodes.ACC_ENUM) != 0) { 887 sb.append("enum "); 888 } 889 } 890 891 private void appendFrameTypes(final StringBuilder sb, final int n, final Object[] o) { 892 for (int i = 0; i < n; ++i) { 893 if (i > 0) { 894 sb.append(' '); 895 } 896 if (o[i] instanceof String) { 897 final String desc = (String) o[i]; 898 if (desc.startsWith("[")) { 899 appendDescriptor(sb, FIELD_DESCRIPTOR, desc); 900 } else { 901 appendDescriptor(sb, INTERNAL_NAME, desc); 902 } 903 } else if (o[i] instanceof Integer) { 904 switch (((Integer)o[i]).intValue()) { 905 case 0: 906 appendDescriptor(sb, FIELD_DESCRIPTOR, "T"); 907 break; 908 case 1: 909 appendDescriptor(sb, FIELD_DESCRIPTOR, "I"); 910 break; 911 case 2: 912 appendDescriptor(sb, FIELD_DESCRIPTOR, "F"); 913 break; 914 case 3: 915 appendDescriptor(sb, FIELD_DESCRIPTOR, "D"); 916 break; 917 case 4: 918 appendDescriptor(sb, FIELD_DESCRIPTOR, "J"); 919 break; 920 case 5: 921 appendDescriptor(sb, FIELD_DESCRIPTOR, "N"); 922 break; 923 case 6: 924 appendDescriptor(sb, FIELD_DESCRIPTOR, "U"); 925 break; 926 default: 927 assert false; 928 break; 929 } 930 } else { 931 appendLabel(sb, (Label) o[i]); 932 } 933 } 934 } 935 936 private static void appendShortDescriptor(final StringBuilder sb, final String desc) { 937 //final StringBuilder buf = new StringBuilder(); 938 if (desc.charAt(0) == '(') { 939 for (int i = 0; i < desc.length(); i++) { 940 if (desc.charAt(i) == 'L') { 941 int slash = i; 942 while (desc.charAt(i) != ';') { 943 i++; 944 if (desc.charAt(i) == '/') { 945 slash = i; 946 } 947 } 948 sb.append(desc.substring(slash + 1, i)).append(';'); 949 } else { 950 sb.append(desc.charAt(i)); 951 } 952 } 953 } else { 954 final int lastSlash = desc.lastIndexOf('/'); 955 final int lastBracket = desc.lastIndexOf('['); 956 if(lastBracket != -1) { 957 sb.append(desc, 0, lastBracket + 1); 958 } 959 sb.append(lastSlash == -1 ? desc : desc.substring(lastSlash + 1)); 960 } 961 } 962 963 private static void appendStr(final StringBuilder sb, final String s) { 964 sb.append('\"'); 965 for (int i = 0; i < s.length(); ++i) { 966 final char c = s.charAt(i); 967 if (c == '\n') { 968 sb.append("\\n"); 969 } else if (c == '\r') { 970 sb.append("\\r"); 971 } else if (c == '\\') { 972 sb.append("\\\\"); 973 } else if (c == '"') { 974 sb.append("\\\""); 975 } else if (c < 0x20 || c > 0x7f) { 976 sb.append("\\u"); 977 if (c < 0x10) { 978 sb.append("000"); 979 } else if (c < 0x100) { 980 sb.append("00"); 981 } else if (c < 0x1000) { 982 sb.append('0'); 983 } 984 sb.append(Integer.toString(c, 16)); 985 } else { 986 sb.append(c); 987 } 988 } 989 sb.append('\"'); 990 } 991 992 private static class Graph { 993 private final LinkedHashSet<String> nodes; 994 private final Map<String, StringBuilder> contents; 995 private final Map<String, Set<String>> edges; 996 private final Set<String> hasPreds; 997 private final Set<String> noFallThru; 998 private final Map<String, String> catches; 999 private final Map<String, Set<String>> exceptionMap; //maps catch nodes to all their trys that can reach them 1000 private final String name; 1001 1002 private static final String LEFT_ALIGN = "\\l"; 1003 private static final String COLOR_CATCH = "\"#ee9999\""; 1004 private static final String COLOR_ORPHAN = "\"#9999bb\""; 1005 private static final String COLOR_DEFAULT = "\"#99bb99\""; 1006 private static final String COLOR_LOCALVARS = "\"#999999\""; 1007 1008 Graph(final String name) { 1009 this.name = name; 1010 this.nodes = new LinkedHashSet<>(); 1011 this.contents = new HashMap<>(); 1012 this.edges = new HashMap<>(); 1013 this.hasPreds = new HashSet<>(); 1014 this.catches = new HashMap<>(); 1015 this.noFallThru = new HashSet<>(); 1016 this.exceptionMap = new HashMap<>(); 1017 } 1018 1019 void addEdge(final String from, final String to) { 1020 Set<String> edgeSet = edges.get(from); 1021 if (edgeSet == null) { 1022 edgeSet = new LinkedHashSet<>(); 1023 edges.put(from, edgeSet); 1024 } 1025 edgeSet.add(to); 1026 hasPreds.add(to); 1027 } 1028 1029 void addTryCatch(final String tryNode, final String catchNode) { 1030 Set<String> tryNodes = exceptionMap.get(catchNode); 1031 if (tryNodes == null) { 1032 tryNodes = new HashSet<>(); 1033 exceptionMap.put(catchNode, tryNodes); 1034 } 1035 if (!tryNodes.contains(tryNode)) { 1036 addEdge(tryNode, catchNode); 1037 } 1038 tryNodes.add(tryNode); 1039 } 1040 1041 void addNode(final String node) { 1042 assert !nodes.contains(node); 1043 nodes.add(node); 1044 } 1045 1046 void setNoFallThru(final String node) { 1047 noFallThru.add(node); 1048 } 1049 1050 boolean isNoFallThru(final String node) { 1051 return noFallThru.contains(node); 1052 } 1053 1054 void setIsCatch(final String node, final String exception) { 1055 catches.put(node, exception); 1056 } 1057 1058 String getName() { 1059 return name; 1060 } 1061 1062 void addText(final String node, final String text) { 1063 StringBuilder sb = contents.get(node); 1064 if (sb == null) { 1065 sb = new StringBuilder(); 1066 } 1067 1068 for (int i = 0; i < text.length(); i++) { 1069 switch (text.charAt(i)) { 1070 case '\n': 1071 sb.append(LEFT_ALIGN); 1072 break; 1073 case '"': 1074 sb.append("'"); 1075 break; 1076 default: 1077 sb.append(text.charAt(i)); 1078 break; 1079 } 1080 } 1081 1082 contents.put(node, sb); 1083 } 1084 1085 private static String dottyFriendly(final String name) { 1086 return name.replace(':', '_'); 1087 } 1088 1089 @Override 1090 public String toString() { 1091 1092 final StringBuilder sb = new StringBuilder(); 1093 sb.append("digraph " + dottyFriendly(name) + " {"); 1094 sb.append("\n"); 1095 sb.append("\tgraph [fontname=courier]\n"); 1096 sb.append("\tnode [style=filled,color="+COLOR_DEFAULT+",fontname=courier]\n"); 1097 sb.append("\tedge [fontname=courier]\n\n"); 1098 1099 for (final String node : nodes) { 1100 sb.append("\t"); 1101 sb.append(node); 1102 sb.append(" ["); 1103 sb.append("id="); 1104 sb.append(node); 1105 sb.append(", label=\""); 1106 String c = contents.get(node).toString(); 1107 if (c.startsWith(LEFT_ALIGN)) { 1108 c = c.substring(LEFT_ALIGN.length()); 1109 } 1110 final String ex = catches.get(node); 1111 if (ex != null) { 1112 sb.append("*** CATCH: ").append(ex).append(" ***\n"); 1113 } 1114 sb.append(c); 1115 sb.append("\"]\n"); 1116 } 1117 1118 for (final String from : edges.keySet()) { 1119 for (final String to : edges.get(from)) { 1120 sb.append("\t"); 1121 sb.append(from); 1122 sb.append(" -> "); 1123 sb.append(to); 1124 sb.append("[label=\""); 1125 sb.append(to); 1126 sb.append("\""); 1127 if (catches.get(to) != null) { 1128 sb.append(", color=red, style=dashed"); 1129 } 1130 sb.append(']'); 1131 sb.append(";\n"); 1132 } 1133 } 1134 1135 sb.append("\n"); 1136 for (final String node : nodes) { 1137 sb.append("\t"); 1138 sb.append(node); 1139 sb.append(" [shape=box"); 1140 if (catches.get(node) != null) { 1141 sb.append(", color=" + COLOR_CATCH); 1142 } else if ("vars".equals(node)) { 1143 sb.append(", shape=hexagon, color=" + COLOR_LOCALVARS); 1144 } else if (!hasPreds.contains(node)) { 1145 sb.append(", color=" + COLOR_ORPHAN); 1146 } 1147 sb.append("]\n"); 1148 } 1149 1150 sb.append("}\n"); 1151 return sb.toString(); 1152 } 1153 } 1154 1155 static class NashornLabel extends Label { 1156 final Label label; 1157 final int bci; 1158 final int opcode; 1159 1160 NashornLabel(final Label label, final int bci) { 1161 this.label = label; 1162 this.bci = bci; 1163 this.opcode = -1; 1164 } 1165 1166 //not an ASM label 1167 NashornLabel(final int opcode, final int bci) { 1168 this.opcode = opcode; 1169 this.bci = bci; 1170 this.label = null; 1171 } 1172 1173 Label getLabel() { 1174 return label; 1175 } 1176 1177 @Override 1178 public int getOffset() { 1179 return bci; 1180 } 1181 1182 @Override 1183 public String toString() { 1184 return "label " + bci; 1185 } 1186 } 1187 1188 @Override 1189 public Printer visitAnnotationDefault() { 1190 throw new AssertionError(); 1191 } 1192 1193 @Override 1194 public Printer visitClassAnnotation(final String arg0, final boolean arg1) { 1195 return this; 1196 } 1197 1198 @Override 1199 public void visitClassAttribute(final Attribute arg0) { 1200 throw new AssertionError(); 1201 } 1202 1203 @Override 1204 public Printer visitFieldAnnotation(final String arg0, final boolean arg1) { 1205 throw new AssertionError(); 1206 } 1207 1208 @Override 1209 public void visitFieldAttribute(final Attribute arg0) { 1210 throw new AssertionError(); 1211 } 1212 1213 @Override 1214 public Printer visitMethodAnnotation(final String arg0, final boolean arg1) { 1215 return this; 1216 } 1217 1218 @Override 1219 public void visitMethodAttribute(final Attribute arg0) { 1220 throw new AssertionError(); 1221 } 1222 1223 @Override 1224 public Printer visitParameterAnnotation(final int arg0, final String arg1, final boolean arg2) { 1225 throw new AssertionError(); 1226 } 1227 1228 @Override 1229 public void visit(final String arg0, final Object arg1) { 1230 throw new AssertionError(); 1231 } 1232 1233 @Override 1234 public Printer visitAnnotation(final String arg0, final String arg1) { 1235 throw new AssertionError(); 1236 } 1237 1238 @Override 1239 public void visitAnnotationEnd() { 1240 //empty 1241 } 1242 1243 @Override 1244 public Printer visitArray(final String arg0) { 1245 throw new AssertionError(); 1246 } 1247 1248 @Override 1249 public void visitEnum(final String arg0, final String arg1, final String arg2) { 1250 throw new AssertionError(); 1251 } 1252 1253 @Override 1254 public void visitInnerClass(final String arg0, final String arg1, final String arg2, final int arg3) { 1255 throw new AssertionError(); 1256 } 1257 }