1 /* 2 * Copyright (c) 2010, 2018, 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 */ NashornTextifier(final ScriptEnvironment env, final NashornClassReader cr)96 public NashornTextifier(final ScriptEnvironment env, final NashornClassReader cr) { 97 this(Opcodes.ASM7); 98 this.env = env; 99 this.cr = cr; 100 } 101 NashornTextifier(final ScriptEnvironment env, final NashornClassReader cr, final Iterator<Label> labelIter, final Graph graph)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 */ NashornTextifier(final int api)115 protected NashornTextifier(final int api) { 116 super(api); 117 } 118 119 @Override visit(final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces)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 visitSource(final String file, final String debug)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 visitOuterClass(final String owner, final String name, final String desc)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 visitField(final int access, final String name, final String desc, final String signature, final Object value)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 visitMethod(final int access, final String name, final String desc, final String signature, final String[] exceptions)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 visitClassEnd()341 public void visitClassEnd() { 342 addText("}\n"); 343 } 344 345 @Override visitFieldEnd()346 public void visitFieldEnd() { 347 //empty 348 } 349 350 @Override visitParameter(final String name, final int access)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 visitCode()361 public void visitCode() { 362 //empty 363 } 364 365 @Override visitFrame(final int type, final int nLocal, final Object[] local, final int nStack, final Object[] stack)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 appendOpcode(final StringBuilder sb, final int opcode)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 getNextLabel()421 private Label getNextLabel() { 422 return labelIter == null ? null : labelIter.next(); 423 } 424 425 @Override visitInsn(final int opcode)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 visitIntInsn(final int opcode, final int operand)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 visitVarInsn(final int opcode, final int var)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 visitTypeInsn(final int opcode, final String type)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 visitFieldInsn(final int opcode, final String owner, final String name, final String desc)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 visitMethodInsn(final int opcode, final String owner, final String name, final String desc, final boolean itf)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 visitInvokeDynamicInsn(final String name, final String desc, final Handle bsm, final Object... bsmArgs)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 isNashornBootstrap(final Handle bsm)545 private static boolean isNashornBootstrap(final Handle bsm) { 546 return "bootstrap".equals(bsm.getName()) && BOOTSTRAP_CLASS_NAME.equals(bsm.getOwner()); 547 } 548 isNashornMathBootstrap(final Handle bsm)549 private static boolean isNashornMathBootstrap(final Handle bsm) { 550 return "mathBootstrap".equals(bsm.getName()) && BOOTSTRAP_CLASS_NAME.equals(bsm.getOwner()); 551 } 552 noFallThru(final int opcode)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 checkNoFallThru(final int opcode, final String to)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 visitJumpInsn(final int opcode, final Label label)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 addText(final Object t)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 visitLabel(final Label label)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 visitLdcInsn(final Object cst)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 visitIincInsn(final int var, final int increment)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 visitTableSwitchInsn(final int min, final int max, final Label dflt, final Label... labels)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 visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels)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 visitMultiANewArrayInsn(final String desc, final int dims)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 visitTryCatchBlock(final Label start, final Label end, final Label handler, final String type)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 visitLocalVariable(final String name, final String desc,final String signature, final Label start, final Label end, final int index)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 visitLineNumber(final int line, final Label start)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 visitMaxs(final int maxStack, final int maxLocals)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 printToDir(final Graph g)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 visitMethodEnd()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 */ createNashornTextifier()799 protected NashornTextifier createNashornTextifier() { 800 return new NashornTextifier(env, cr, labelIter, graph); 801 } 802 appendDescriptor(final StringBuilder sb, final int type, final String desc)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 appendLabel(final StringBuilder sb, final Label l)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 appendHandle(final StringBuilder sb, final Handle h)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 appendAccess(final StringBuilder sb, final int access)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 appendFrameTypes(final StringBuilder sb, final int n, final Object[] o)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 appendShortDescriptor(final StringBuilder sb, final String desc)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 appendStr(final StringBuilder sb, final String s)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 Graph(final String name)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 addEdge(final String from, final String to)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 addTryCatch(final String tryNode, final String catchNode)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 addNode(final String node)1056 void addNode(final String node) { 1057 assert !nodes.contains(node); 1058 nodes.add(node); 1059 } 1060 setNoFallThru(final String node)1061 void setNoFallThru(final String node) { 1062 noFallThru.add(node); 1063 } 1064 isNoFallThru(final String node)1065 boolean isNoFallThru(final String node) { 1066 return noFallThru.contains(node); 1067 } 1068 setIsCatch(final String node, final String exception)1069 void setIsCatch(final String node, final String exception) { 1070 catches.put(node, exception); 1071 } 1072 getName()1073 String getName() { 1074 return name; 1075 } 1076 addText(final String node, final String text)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 dottyFriendly(final String name)1100 private static String dottyFriendly(final String name) { 1101 return name.replace(':', '_'); 1102 } 1103 1104 @Override toString()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 NashornLabel(final Label label, final int bci)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 NashornLabel(final int opcode, final int bci)1182 NashornLabel(final int opcode, final int bci) { 1183 this.opcode = opcode; 1184 this.bci = bci; 1185 this.label = null; 1186 } 1187 getLabel()1188 Label getLabel() { 1189 return label; 1190 } 1191 1192 @Override getOffset()1193 public int getOffset() { 1194 return bci; 1195 } 1196 1197 @Override toString()1198 public String toString() { 1199 return "label " + bci; 1200 } 1201 } 1202 1203 @Override visitAnnotationDefault()1204 public Printer visitAnnotationDefault() { 1205 throw new AssertionError(); 1206 } 1207 1208 @Override visitClassAnnotation(final String arg0, final boolean arg1)1209 public Printer visitClassAnnotation(final String arg0, final boolean arg1) { 1210 return this; 1211 } 1212 1213 @Override visitClassAttribute(final Attribute arg0)1214 public void visitClassAttribute(final Attribute arg0) { 1215 throw new AssertionError(); 1216 } 1217 1218 @Override visitFieldAnnotation(final String arg0, final boolean arg1)1219 public Printer visitFieldAnnotation(final String arg0, final boolean arg1) { 1220 throw new AssertionError(); 1221 } 1222 1223 @Override visitFieldAttribute(final Attribute arg0)1224 public void visitFieldAttribute(final Attribute arg0) { 1225 throw new AssertionError(); 1226 } 1227 1228 @Override visitMethodAnnotation(final String arg0, final boolean arg1)1229 public Printer visitMethodAnnotation(final String arg0, final boolean arg1) { 1230 return this; 1231 } 1232 1233 @Override visitMethodAttribute(final Attribute arg0)1234 public void visitMethodAttribute(final Attribute arg0) { 1235 throw new AssertionError(); 1236 } 1237 1238 @Override visitParameterAnnotation(final int arg0, final String arg1, final boolean arg2)1239 public Printer visitParameterAnnotation(final int arg0, final String arg1, final boolean arg2) { 1240 throw new AssertionError(); 1241 } 1242 1243 @Override visit(final String arg0, final Object arg1)1244 public void visit(final String arg0, final Object arg1) { 1245 throw new AssertionError(); 1246 } 1247 1248 @Override visitAnnotation(final String arg0, final String arg1)1249 public Printer visitAnnotation(final String arg0, final String arg1) { 1250 throw new AssertionError(); 1251 } 1252 1253 @Override visitAnnotationEnd()1254 public void visitAnnotationEnd() { 1255 //empty 1256 } 1257 1258 @Override visitArray(final String arg0)1259 public Printer visitArray(final String arg0) { 1260 throw new AssertionError(); 1261 } 1262 1263 @Override visitEnum(final String arg0, final String arg1, final String arg2)1264 public void visitEnum(final String arg0, final String arg1, final String arg2) { 1265 throw new AssertionError(); 1266 } 1267 1268 @Override visitInnerClass(final String arg0, final String arg1, final String arg2, final int arg3)1269 public void visitInnerClass(final String arg0, final String arg1, final String arg2, final int arg3) { 1270 throw new AssertionError(); 1271 } 1272 } 1273