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