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 java.io.File; 29 import java.io.FileNotFoundException; 30 import java.io.FileOutputStream; 31 import java.io.PrintWriter; 32 import java.util.HashMap; 33 import java.util.HashSet; 34 import java.util.Iterator; 35 import java.util.LinkedHashSet; 36 import java.util.List; 37 import java.util.Map; 38 import java.util.Set; 39 import jdk.internal.org.objectweb.asm.Attribute; 40 import jdk.internal.org.objectweb.asm.Handle; 41 import jdk.internal.org.objectweb.asm.Label; 42 import jdk.internal.org.objectweb.asm.Opcodes; 43 import jdk.internal.org.objectweb.asm.Type; 44 import jdk.internal.org.objectweb.asm.signature.SignatureReader; 45 import jdk.internal.org.objectweb.asm.util.Printer; 46 import jdk.internal.org.objectweb.asm.util.TraceSignatureVisitor; 47 import jdk.nashorn.internal.runtime.ScriptEnvironment; 48 import jdk.nashorn.internal.runtime.linker.Bootstrap; 49 import jdk.nashorn.internal.runtime.linker.NameCodec; 50 import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor; 51 52 /** 53 * Pretty printer for --print-code. 54 * Also supports dot formats if --print-code has arguments 55 */ 56 public final class NashornTextifier extends Printer { 57 private static final String BOOTSTRAP_CLASS_NAME = Bootstrap.class.getName().replace('.', '/'); 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 final boolean isNashornBootstrap = isNashornBootstrap(bsm); 502 final boolean isNashornMathBootstrap = isNashornMathBootstrap(bsm); 503 if (isNashornBootstrap) { 504 sb.append(NashornCallSiteDescriptor.getOperationName((Integer)bsmArgs[0])); 505 final String decodedName = NameCodec.decode(name); 506 if (!decodedName.isEmpty()) { 507 sb.append(':').append(decodedName); 508 } 509 } else { 510 sb.append(name); 511 } 512 appendDescriptor(sb, METHOD_DESCRIPTOR, desc); 513 final int len = sb.length(); 514 for (int i = 0; i < 80 - len ; i++) { 515 sb.append(' '); 516 } 517 sb.append(" ["); 518 appendHandle(sb, bsm); 519 if (bsmArgs.length == 0) { 520 sb.append("none"); 521 } else { 522 for (final Object cst : bsmArgs) { 523 if (cst instanceof String) { 524 appendStr(sb, (String)cst); 525 } else if (cst instanceof Type) { 526 sb.append(((Type)cst).getDescriptor()).append(".class"); 527 } else if (cst instanceof Handle) { 528 appendHandle(sb, (Handle)cst); 529 } else if (cst instanceof Integer && isNashornBootstrap) { 530 NashornCallSiteDescriptor.appendFlags((Integer) cst, sb); 531 } else if (cst instanceof Integer && isNashornMathBootstrap) { 532 sb.append(" pp=").append(cst); 533 } else { 534 sb.append(cst); 535 } 536 sb.append(", "); 537 } 538 sb.setLength(sb.length() - 2); 539 } 540 541 sb.append("]\n"); 542 addText(sb); 543 } 544 545 private static boolean isNashornBootstrap(final Handle bsm) { 546 return "bootstrap".equals(bsm.getName()) && BOOTSTRAP_CLASS_NAME.equals(bsm.getOwner()); 547 } 548 549 private static boolean isNashornMathBootstrap(final Handle bsm) { 550 return "mathBootstrap".equals(bsm.getName()) && BOOTSTRAP_CLASS_NAME.equals(bsm.getOwner()); 551 } 552 553 private static boolean noFallThru(final int opcode) { 554 switch (opcode) { 555 case Opcodes.GOTO: 556 case Opcodes.ATHROW: 557 case Opcodes.ARETURN: 558 case Opcodes.IRETURN: 559 case Opcodes.LRETURN: 560 case Opcodes.FRETURN: 561 case Opcodes.DRETURN: 562 return true; 563 default: 564 return false; 565 } 566 } 567 568 private void checkNoFallThru(final int opcode, final String to) { 569 if (noFallThru(opcode)) { 570 graph.setNoFallThru(currentBlock); 571 } 572 573 if (currentBlock != null && to != null) { 574 graph.addEdge(currentBlock, to); 575 } 576 } 577 578 @Override 579 public void visitJumpInsn(final int opcode, final Label label) { 580 final StringBuilder sb = new StringBuilder(); 581 appendOpcode(sb, opcode).append(' '); 582 final String to = appendLabel(sb, label); 583 sb.append('\n'); 584 addText(sb); 585 checkNoFallThru(opcode, to); 586 } 587 588 private void addText(final Object t) { 589 text.add(t); 590 if (currentBlock != null) { 591 graph.addText(currentBlock, t.toString()); 592 } 593 } 594 595 @Override 596 public void visitLabel(final Label label) { 597 final StringBuilder sb = new StringBuilder(); 598 sb.append("\n"); 599 final String name = appendLabel(sb, label); 600 sb.append(" [bci="); 601 sb.append(label.info); 602 sb.append("]"); 603 sb.append("\n"); 604 605 graph.addNode(name); 606 if (currentBlock != null && !graph.isNoFallThru(currentBlock)) { 607 graph.addEdge(currentBlock, name); 608 } 609 currentBlock = name; 610 addText(sb); 611 } 612 613 @Override 614 public void visitLdcInsn(final Object cst) { 615 final StringBuilder sb = new StringBuilder(); 616 appendOpcode(sb, Opcodes.LDC).append(' '); 617 if (cst instanceof String) { 618 appendStr(sb, (String) cst); 619 } else if (cst instanceof Type) { 620 sb.append(((Type) cst).getDescriptor()).append(".class"); 621 } else { 622 sb.append(cst); 623 } 624 sb.append('\n'); 625 addText(sb); 626 } 627 628 @Override 629 public void visitIincInsn(final int var, final int increment) { 630 final StringBuilder sb = new StringBuilder(); 631 appendOpcode(sb, Opcodes.IINC).append(' '); 632 sb.append(var).append(' ') 633 .append(increment).append('\n'); 634 addText(sb); 635 } 636 637 @Override 638 public void visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels) { 639 final StringBuilder sb = new StringBuilder(); 640 appendOpcode(sb, Opcodes.TABLESWITCH).append(' '); 641 for (int i = 0; i < labels.length; ++i) { 642 sb.append(tab3).append(min + i).append(": "); 643 final String to = appendLabel(sb, labels[i]); 644 graph.addEdge(currentBlock, to); 645 sb.append('\n'); 646 } 647 sb.append(tab3).append("default: "); 648 appendLabel(sb, dflt); 649 sb.append('\n'); 650 addText(sb); 651 } 652 653 @Override 654 public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { 655 final StringBuilder sb = new StringBuilder(); 656 appendOpcode(sb, Opcodes.LOOKUPSWITCH).append(' '); 657 for (int i = 0; i < labels.length; ++i) { 658 sb.append(tab3).append(keys[i]).append(": "); 659 final String to = appendLabel(sb, labels[i]); 660 graph.addEdge(currentBlock, to); 661 sb.append('\n'); 662 } 663 sb.append(tab3).append("default: "); 664 final String to = appendLabel(sb, dflt); 665 graph.addEdge(currentBlock, to); 666 sb.append('\n'); 667 addText(sb.toString()); 668 } 669 670 @Override 671 public void visitMultiANewArrayInsn(final String desc, final int dims) { 672 final StringBuilder sb = new StringBuilder(); 673 appendOpcode(sb, Opcodes.MULTIANEWARRAY).append(' '); 674 appendDescriptor(sb, FIELD_DESCRIPTOR, desc); 675 sb.append(' ').append(dims).append('\n'); 676 addText(sb); 677 } 678 679 @Override 680 public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type) { 681 final StringBuilder sb = new StringBuilder(); 682 sb.append(tab2).append("try "); 683 final String from = appendLabel(sb, start); 684 sb.append(' '); 685 appendLabel(sb, end); 686 sb.append(' '); 687 final String to = appendLabel(sb, handler); 688 sb.append(' '); 689 appendDescriptor(sb, INTERNAL_NAME, type); 690 sb.append('\n'); 691 addText(sb); 692 graph.setIsCatch(to, type); 693 graph.addTryCatch(from, to); 694 } 695 696 @Override 697 public void visitLocalVariable(final String name, final String desc,final String signature, final Label start, final Label end, final int index) { 698 699 final StringBuilder sb = new StringBuilder(); 700 if (!localVarsStarted) { 701 text.add("\n"); 702 localVarsStarted = true; 703 graph.addNode("vars"); 704 currentBlock = "vars"; 705 } 706 707 sb.append(tab2).append("local ").append(name).append(' '); 708 final int len = sb.length(); 709 for (int i = 0; i < 25 - len; i++) { 710 sb.append(' '); 711 } 712 String label; 713 714 label = appendLabel(sb, start); 715 for (int i = 0; i < 5 - label.length(); i++) { 716 sb.append(' '); 717 } 718 label = appendLabel(sb, end); 719 for (int i = 0; i < 5 - label.length(); i++) { 720 sb.append(' '); 721 } 722 723 sb.append(index).append(tab2); 724 725 appendDescriptor(sb, FIELD_DESCRIPTOR, desc); 726 sb.append('\n'); 727 728 if (signature != null) { 729 sb.append(tab2); 730 appendDescriptor(sb, FIELD_SIGNATURE, signature); 731 732 final TraceSignatureVisitor sv = new TraceSignatureVisitor(0); 733 final SignatureReader r = new SignatureReader(signature); 734 r.acceptType(sv); 735 sb.append(tab2).append("// declaration: ") 736 .append(sv.getDeclaration()).append('\n'); 737 } 738 addText(sb.toString()); 739 } 740 741 @Override 742 public void visitLineNumber(final int line, final Label start) { 743 final StringBuilder sb = new StringBuilder(); 744 sb.append("<line "); 745 sb.append(line); 746 sb.append(">\n"); 747 addText(sb.toString()); 748 } 749 750 @Override 751 public void visitMaxs(final int maxStack, final int maxLocals) { 752 final StringBuilder sb = new StringBuilder(); 753 sb.append('\n'); 754 sb.append(tab2).append("max stack = ").append(maxStack); 755 sb.append(", max locals = ").append(maxLocals).append('\n'); 756 addText(sb.toString()); 757 } 758 759 private void printToDir(final Graph g) { 760 if (env._print_code_dir != null) { 761 final File dir = new File(env._print_code_dir); 762 if (!dir.exists() && !dir.mkdirs()) { 763 throw new RuntimeException(dir.toString()); 764 } 765 766 File file; 767 int uniqueId = 0; 768 do { 769 final String fileName = g.getName() + (uniqueId == 0 ? "" : "_" + uniqueId) + ".dot"; 770 file = new File(dir, fileName); 771 uniqueId++; 772 } while (file.exists()); 773 774 try (PrintWriter pw = new PrintWriter(new FileOutputStream(file))) { 775 pw.println(g); 776 } catch (final FileNotFoundException e) { 777 throw new RuntimeException(e); 778 } 779 } 780 } 781 782 @Override 783 public void visitMethodEnd() { 784 //here we need to do several bytecode guesses best upon the ldc instructions. 785 //for each instruction, assign bci. for an ldc/w/2w, guess a byte and keep 786 //iterating. if the next label is wrong, backtrack. 787 if (env._print_code_func == null || env._print_code_func.equals(graph.getName())) { 788 if (env._print_code_dir != null) { 789 printToDir(graph); 790 } 791 } 792 } 793 794 /** 795 * Creates a new TraceVisitor instance. 796 * 797 * @return a new TraceVisitor. 798 */ 799 protected NashornTextifier createNashornTextifier() { 800 return new NashornTextifier(env, cr, labelIter, graph); 801 } 802 803 private static void appendDescriptor(final StringBuilder sb, final int type, final String desc) { 804 if (desc != null) { 805 if (type == CLASS_SIGNATURE || type == FIELD_SIGNATURE || type == METHOD_SIGNATURE) { 806 sb.append("// signature ").append(desc).append('\n'); 807 } else { 808 appendShortDescriptor(sb, desc); 809 } 810 } 811 } 812 813 private String appendLabel(final StringBuilder sb, final Label l) { 814 if (labelNames == null) { 815 labelNames = new HashMap<>(); 816 } 817 String name = labelNames.get(l); 818 if (name == null) { 819 name = "L" + labelNames.size(); 820 labelNames.put(l, name); 821 } 822 sb.append(name); 823 return name; 824 } 825 826 private static void appendHandle(final StringBuilder sb, final Handle h) { 827 switch (h.getTag()) { 828 case Opcodes.H_GETFIELD: 829 sb.append("getfield"); 830 break; 831 case Opcodes.H_GETSTATIC: 832 sb.append("getstatic"); 833 break; 834 case Opcodes.H_PUTFIELD: 835 sb.append("putfield"); 836 break; 837 case Opcodes.H_PUTSTATIC: 838 sb.append("putstatic"); 839 break; 840 case Opcodes.H_INVOKEINTERFACE: 841 sb.append("interface"); 842 break; 843 case Opcodes.H_INVOKESPECIAL: 844 sb.append("special"); 845 break; 846 case Opcodes.H_INVOKESTATIC: 847 sb.append("static"); 848 break; 849 case Opcodes.H_INVOKEVIRTUAL: 850 sb.append("virtual"); 851 break; 852 case Opcodes.H_NEWINVOKESPECIAL: 853 sb.append("new_special"); 854 break; 855 default: 856 assert false; 857 break; 858 } 859 sb.append(" '"); 860 sb.append(h.getName()); 861 sb.append("'"); 862 } 863 864 private static void appendAccess(final StringBuilder sb, final int access) { 865 if ((access & Opcodes.ACC_PUBLIC) != 0) { 866 sb.append("public "); 867 } 868 if ((access & Opcodes.ACC_PRIVATE) != 0) { 869 sb.append("private "); 870 } 871 if ((access & Opcodes.ACC_PROTECTED) != 0) { 872 sb.append("protected "); 873 } 874 if ((access & Opcodes.ACC_FINAL) != 0) { 875 sb.append("final "); 876 } 877 if ((access & Opcodes.ACC_STATIC) != 0) { 878 sb.append("static "); 879 } 880 if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) { 881 sb.append("synchronized "); 882 } 883 if ((access & Opcodes.ACC_VOLATILE) != 0) { 884 sb.append("volatile "); 885 } 886 if ((access & Opcodes.ACC_TRANSIENT) != 0) { 887 sb.append("transient "); 888 } 889 if ((access & Opcodes.ACC_ABSTRACT) != 0) { 890 sb.append("abstract "); 891 } 892 if ((access & Opcodes.ACC_STRICT) != 0) { 893 sb.append("strictfp "); 894 } 895 if ((access & Opcodes.ACC_SYNTHETIC) != 0) { 896 sb.append("synthetic "); 897 } 898 if ((access & Opcodes.ACC_MANDATED) != 0) { 899 sb.append("mandated "); 900 } 901 if ((access & Opcodes.ACC_ENUM) != 0) { 902 sb.append("enum "); 903 } 904 } 905 906 private void appendFrameTypes(final StringBuilder sb, final int n, final Object[] o) { 907 for (int i = 0; i < n; ++i) { 908 if (i > 0) { 909 sb.append(' '); 910 } 911 if (o[i] instanceof String) { 912 final String desc = (String) o[i]; 913 if (desc.startsWith("[")) { 914 appendDescriptor(sb, FIELD_DESCRIPTOR, desc); 915 } else { 916 appendDescriptor(sb, INTERNAL_NAME, desc); 917 } 918 } else if (o[i] instanceof Integer) { 919 switch (((Integer)o[i])) { 920 case 0: 921 appendDescriptor(sb, FIELD_DESCRIPTOR, "T"); 922 break; 923 case 1: 924 appendDescriptor(sb, FIELD_DESCRIPTOR, "I"); 925 break; 926 case 2: 927 appendDescriptor(sb, FIELD_DESCRIPTOR, "F"); 928 break; 929 case 3: 930 appendDescriptor(sb, FIELD_DESCRIPTOR, "D"); 931 break; 932 case 4: 933 appendDescriptor(sb, FIELD_DESCRIPTOR, "J"); 934 break; 935 case 5: 936 appendDescriptor(sb, FIELD_DESCRIPTOR, "N"); 937 break; 938 case 6: 939 appendDescriptor(sb, FIELD_DESCRIPTOR, "U"); 940 break; 941 default: 942 assert false; 943 break; 944 } 945 } else { 946 appendLabel(sb, (Label) o[i]); 947 } 948 } 949 } 950 951 private static void appendShortDescriptor(final StringBuilder sb, final String desc) { 952 //final StringBuilder buf = new StringBuilder(); 953 if (desc.charAt(0) == '(') { 954 for (int i = 0; i < desc.length(); i++) { 955 if (desc.charAt(i) == 'L') { 956 int slash = i; 957 while (desc.charAt(i) != ';') { 958 i++; 959 if (desc.charAt(i) == '/') { 960 slash = i; 961 } 962 } 963 sb.append(desc.substring(slash + 1, i)).append(';'); 964 } else { 965 sb.append(desc.charAt(i)); 966 } 967 } 968 } else { 969 final int lastSlash = desc.lastIndexOf('/'); 970 final int lastBracket = desc.lastIndexOf('['); 971 if(lastBracket != -1) { 972 sb.append(desc, 0, lastBracket + 1); 973 } 974 sb.append(lastSlash == -1 ? desc : desc.substring(lastSlash + 1)); 975 } 976 } 977 978 private static void appendStr(final StringBuilder sb, final String s) { 979 sb.append('\"'); 980 for (int i = 0; i < s.length(); ++i) { 981 final char c = s.charAt(i); 982 if (c == '\n') { 983 sb.append("\\n"); 984 } else if (c == '\r') { 985 sb.append("\\r"); 986 } else if (c == '\\') { 987 sb.append("\\\\"); 988 } else if (c == '"') { 989 sb.append("\\\""); 990 } else if (c < 0x20 || c > 0x7f) { 991 sb.append("\\u"); 992 if (c < 0x10) { 993 sb.append("000"); 994 } else if (c < 0x100) { 995 sb.append("00"); 996 } else if (c < 0x1000) { 997 sb.append('0'); 998 } 999 sb.append(Integer.toString(c, 16)); 1000 } else { 1001 sb.append(c); 1002 } 1003 } 1004 sb.append('\"'); 1005 } 1006 1007 private static class Graph { 1008 private final LinkedHashSet<String> nodes; 1009 private final Map<String, StringBuilder> contents; 1010 private final Map<String, Set<String>> edges; 1011 private final Set<String> hasPreds; 1012 private final Set<String> noFallThru; 1013 private final Map<String, String> catches; 1014 private final Map<String, Set<String>> exceptionMap; //maps catch nodes to all their trys that can reach them 1015 private final String name; 1016 1017 private static final String LEFT_ALIGN = "\\l"; 1018 private static final String COLOR_CATCH = "\"#ee9999\""; 1019 private static final String COLOR_ORPHAN = "\"#9999bb\""; 1020 private static final String COLOR_DEFAULT = "\"#99bb99\""; 1021 private static final String COLOR_LOCALVARS = "\"#999999\""; 1022 1023 Graph(final String name) { 1024 this.name = name; 1025 this.nodes = new LinkedHashSet<>(); 1026 this.contents = new HashMap<>(); 1027 this.edges = new HashMap<>(); 1028 this.hasPreds = new HashSet<>(); 1029 this.catches = new HashMap<>(); 1030 this.noFallThru = new HashSet<>(); 1031 this.exceptionMap = new HashMap<>(); 1032 } 1033 1034 void addEdge(final String from, final String to) { 1035 Set<String> edgeSet = edges.get(from); 1036 if (edgeSet == null) { 1037 edgeSet = new LinkedHashSet<>(); 1038 edges.put(from, edgeSet); 1039 } 1040 edgeSet.add(to); 1041 hasPreds.add(to); 1042 } 1043 1044 void addTryCatch(final String tryNode, final String catchNode) { 1045 Set<String> tryNodes = exceptionMap.get(catchNode); 1046 if (tryNodes == null) { 1047 tryNodes = new HashSet<>(); 1048 exceptionMap.put(catchNode, tryNodes); 1049 } 1050 if (!tryNodes.contains(tryNode)) { 1051 addEdge(tryNode, catchNode); 1052 } 1053 tryNodes.add(tryNode); 1054 } 1055 1056 void addNode(final String node) { 1057 assert !nodes.contains(node); 1058 nodes.add(node); 1059 } 1060 1061 void setNoFallThru(final String node) { 1062 noFallThru.add(node); 1063 } 1064 1065 boolean isNoFallThru(final String node) { 1066 return noFallThru.contains(node); 1067 } 1068 1069 void setIsCatch(final String node, final String exception) { 1070 catches.put(node, exception); 1071 } 1072 1073 String getName() { 1074 return name; 1075 } 1076 1077 void addText(final String node, final String text) { 1078 StringBuilder sb = contents.get(node); 1079 if (sb == null) { 1080 sb = new StringBuilder(); 1081 } 1082 1083 for (int i = 0; i < text.length(); i++) { 1084 switch (text.charAt(i)) { 1085 case '\n': 1086 sb.append(LEFT_ALIGN); 1087 break; 1088 case '"': 1089 sb.append("'"); 1090 break; 1091 default: 1092 sb.append(text.charAt(i)); 1093 break; 1094 } 1095 } 1096 1097 contents.put(node, sb); 1098 } 1099 1100 private static String dottyFriendly(final String name) { 1101 return name.replace(':', '_'); 1102 } 1103 1104 @Override 1105 public String toString() { 1106 1107 final StringBuilder sb = new StringBuilder(); 1108 sb.append("digraph ").append(dottyFriendly(name)).append(" {"); 1109 sb.append("\n"); 1110 sb.append("\tgraph [fontname=courier]\n"); 1111 sb.append("\tnode [style=filled,color="+COLOR_DEFAULT+",fontname=courier]\n"); 1112 sb.append("\tedge [fontname=courier]\n\n"); 1113 1114 for (final String node : nodes) { 1115 sb.append("\t"); 1116 sb.append(node); 1117 sb.append(" ["); 1118 sb.append("id="); 1119 sb.append(node); 1120 sb.append(", label=\""); 1121 String c = contents.get(node).toString(); 1122 if (c.startsWith(LEFT_ALIGN)) { 1123 c = c.substring(LEFT_ALIGN.length()); 1124 } 1125 final String ex = catches.get(node); 1126 if (ex != null) { 1127 sb.append("*** CATCH: ").append(ex).append(" ***\\l"); 1128 } 1129 sb.append(c); 1130 sb.append("\"]\n"); 1131 } 1132 1133 for (final String from : edges.keySet()) { 1134 for (final String to : edges.get(from)) { 1135 sb.append("\t"); 1136 sb.append(from); 1137 sb.append(" -> "); 1138 sb.append(to); 1139 sb.append("[label=\""); 1140 sb.append(to); 1141 sb.append("\""); 1142 if (catches.get(to) != null) { 1143 sb.append(", color=red, style=dashed"); 1144 } 1145 sb.append(']'); 1146 sb.append(";\n"); 1147 } 1148 } 1149 1150 sb.append("\n"); 1151 for (final String node : nodes) { 1152 sb.append("\t"); 1153 sb.append(node); 1154 sb.append(" [shape=box"); 1155 if (catches.get(node) != null) { 1156 sb.append(", color=" + COLOR_CATCH); 1157 } else if ("vars".equals(node)) { 1158 sb.append(", shape=hexagon, color=" + COLOR_LOCALVARS); 1159 } else if (!hasPreds.contains(node)) { 1160 sb.append(", color=" + COLOR_ORPHAN); 1161 } 1162 sb.append("]\n"); 1163 } 1164 1165 sb.append("}\n"); 1166 return sb.toString(); 1167 } 1168 } 1169 1170 static class NashornLabel extends Label { 1171 final Label label; 1172 final int bci; 1173 final int opcode; 1174 1175 NashornLabel(final Label label, final int bci) { 1176 this.label = label; 1177 this.bci = bci; 1178 this.opcode = -1; 1179 } 1180 1181 //not an ASM label 1182 NashornLabel(final int opcode, final int bci) { 1183 this.opcode = opcode; 1184 this.bci = bci; 1185 this.label = null; 1186 } 1187 1188 Label getLabel() { 1189 return label; 1190 } 1191 1192 @Override 1193 public int getOffset() { 1194 return bci; 1195 } 1196 1197 @Override 1198 public String toString() { 1199 return "label " + bci; 1200 } 1201 } 1202 1203 @Override 1204 public Printer visitAnnotationDefault() { 1205 throw new AssertionError(); 1206 } 1207 1208 @Override 1209 public Printer visitClassAnnotation(final String arg0, final boolean arg1) { 1210 return this; 1211 } 1212 1213 @Override 1214 public void visitClassAttribute(final Attribute arg0) { 1215 throw new AssertionError(); 1216 } 1217 1218 @Override 1219 public Printer visitFieldAnnotation(final String arg0, final boolean arg1) { 1220 throw new AssertionError(); 1221 } 1222 1223 @Override 1224 public void visitFieldAttribute(final Attribute arg0) { 1225 throw new AssertionError(); 1226 } 1227 1228 @Override 1229 public Printer visitMethodAnnotation(final String arg0, final boolean arg1) { 1230 return this; 1231 } 1232 1233 @Override 1234 public void visitMethodAttribute(final Attribute arg0) { 1235 throw new AssertionError(); 1236 } 1237 1238 @Override 1239 public Printer visitParameterAnnotation(final int arg0, final String arg1, final boolean arg2) { 1240 throw new AssertionError(); 1241 } 1242 1243 @Override 1244 public void visit(final String arg0, final Object arg1) { 1245 throw new AssertionError(); 1246 } 1247 1248 @Override 1249 public Printer visitAnnotation(final String arg0, final String arg1) { 1250 throw new AssertionError(); 1251 } 1252 1253 @Override 1254 public void visitAnnotationEnd() { 1255 //empty 1256 } 1257 1258 @Override 1259 public Printer visitArray(final String arg0) { 1260 throw new AssertionError(); 1261 } 1262 1263 @Override 1264 public void visitEnum(final String arg0, final String arg1, final String arg2) { 1265 throw new AssertionError(); 1266 } 1267 1268 @Override 1269 public void visitInnerClass(final String arg0, final String arg1, final String arg2, final int arg3) { 1270 throw new AssertionError(); 1271 } 1272 }