1 // ASM: a very small and fast Java bytecode manipulation framework 2 // Copyright (c) 2000-2011 INRIA, France Telecom 3 // All rights reserved. 4 // 5 // Redistribution and use in source and binary forms, with or without 6 // modification, are permitted provided that the following conditions 7 // are met: 8 // 1. Redistributions of source code must retain the above copyright 9 // notice, this list of conditions and the following disclaimer. 10 // 2. Redistributions in binary form must reproduce the above copyright 11 // notice, this list of conditions and the following disclaimer in the 12 // documentation and/or other materials provided with the distribution. 13 // 3. Neither the name of the copyright holders nor the names of its 14 // contributors may be used to endorse or promote products derived from 15 // this software without specific prior written permission. 16 // 17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 27 // THE POSSIBILITY OF SUCH DAMAGE. 28 package org.objectweb.asm.util; 29 30 import java.io.IOException; 31 import java.io.PrintWriter; 32 import java.util.Arrays; 33 import java.util.Collections; 34 import java.util.HashMap; 35 import java.util.List; 36 import java.util.Map; 37 import org.objectweb.asm.Attribute; 38 import org.objectweb.asm.Handle; 39 import org.objectweb.asm.Label; 40 import org.objectweb.asm.Opcodes; 41 import org.objectweb.asm.Type; 42 import org.objectweb.asm.TypePath; 43 import org.objectweb.asm.TypeReference; 44 import org.objectweb.asm.signature.SignatureReader; 45 46 /** 47 * A {@link Printer} that prints a disassembled view of the classes it visits. 48 * 49 * @author Eric Bruneton 50 */ 51 public class Textifier extends Printer { 52 53 /** The help message shown when command line arguments are incorrect. */ 54 private static final String USAGE = 55 "Prints a disassembled view of the given class.\n" 56 + "Usage: Textifier [-nodebug] <fully qualified class name or class file name>"; 57 58 /** 59 * The type of internal names (see {@link Type#getInternalName()}). See {@link #appendDescriptor}. 60 */ 61 public static final int INTERNAL_NAME = 0; 62 63 /** The type of field descriptors. See {@link #appendDescriptor}. */ 64 public static final int FIELD_DESCRIPTOR = 1; 65 66 /** The type of field signatures. See {@link #appendDescriptor}. */ 67 public static final int FIELD_SIGNATURE = 2; 68 69 /** The type of method descriptors. See {@link #appendDescriptor}. */ 70 public static final int METHOD_DESCRIPTOR = 3; 71 72 /** The type of method signatures. See {@link #appendDescriptor}. */ 73 public static final int METHOD_SIGNATURE = 4; 74 75 /** The type of class signatures. See {@link #appendDescriptor}. */ 76 public static final int CLASS_SIGNATURE = 5; 77 78 /** The type of method handle descriptors. See {@link #appendDescriptor}. */ 79 public static final int HANDLE_DESCRIPTOR = 9; 80 81 private static final String CLASS_SUFFIX = ".class"; 82 private static final String DEPRECATED = "// DEPRECATED\n"; 83 private static final String RECORD = "// RECORD\n"; 84 private static final String INVISIBLE = " // invisible\n"; 85 86 private static final List<String> FRAME_TYPES = 87 Collections.unmodifiableList(Arrays.asList("T", "I", "F", "D", "J", "N", "U")); 88 89 /** The indentation of class members at depth level 1 (e.g. fields, methods). */ 90 protected String tab = " "; 91 92 /** The indentation of class elements at depth level 2 (e.g. bytecode instructions in methods). */ 93 protected String tab2 = " "; 94 95 /** The indentation of class elements at depth level 3 (e.g. switch cases in methods). */ 96 protected String tab3 = " "; 97 98 /** The indentation of labels. */ 99 protected String ltab = " "; 100 101 /** The names of the labels. */ 102 protected Map<Label, String> labelNames; 103 104 /** The access flags of the visited class. */ 105 private int access; 106 107 /** The number of annotation values visited so far. */ 108 private int numAnnotationValues; 109 110 /** 111 * Constructs a new {@link Textifier}. <i>Subclasses must not use this constructor</i>. Instead, 112 * they must use the {@link #Textifier(int)} version. 113 * 114 * @throws IllegalStateException If a subclass calls this constructor. 115 */ Textifier()116 public Textifier() { 117 this(/* latest api = */ Opcodes.ASM9); 118 if (getClass() != Textifier.class) { 119 throw new IllegalStateException(); 120 } 121 } 122 123 /** 124 * Constructs a new {@link Textifier}. 125 * 126 * @param api the ASM API version implemented by this visitor. Must be one of the {@code 127 * ASM}<i>x</i> values in {@link Opcodes}. 128 */ Textifier(final int api)129 protected Textifier(final int api) { 130 super(api); 131 } 132 133 /** 134 * Prints a disassembled view of the given class to the standard output. 135 * 136 * <p>Usage: Textifier [-nodebug] <binary class name or class file name > 137 * 138 * @param args the command line arguments. 139 * @throws IOException if the class cannot be found, or if an IOException occurs. 140 */ main(final String[] args)141 public static void main(final String[] args) throws IOException { 142 main(args, new PrintWriter(System.out, true), new PrintWriter(System.err, true)); 143 } 144 145 /** 146 * Prints a disassembled view of the given class to the given output. 147 * 148 * <p>Usage: Textifier [-nodebug] <binary class name or class file name > 149 * 150 * @param args the command line arguments. 151 * @param output where to print the result. 152 * @param logger where to log errors. 153 * @throws IOException if the class cannot be found, or if an IOException occurs. 154 */ main(final String[] args, final PrintWriter output, final PrintWriter logger)155 static void main(final String[] args, final PrintWriter output, final PrintWriter logger) 156 throws IOException { 157 main(args, USAGE, new Textifier(), output, logger); 158 } 159 160 // ----------------------------------------------------------------------------------------------- 161 // Classes 162 // ----------------------------------------------------------------------------------------------- 163 164 @Override visit( final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces)165 public void visit( 166 final int version, 167 final int access, 168 final String name, 169 final String signature, 170 final String superName, 171 final String[] interfaces) { 172 if ((access & Opcodes.ACC_MODULE) != 0) { 173 // Modules are printed in visitModule. 174 return; 175 } 176 this.access = access; 177 int majorVersion = version & 0xFFFF; 178 int minorVersion = version >>> 16; 179 stringBuilder.setLength(0); 180 stringBuilder 181 .append("// class version ") 182 .append(majorVersion) 183 .append('.') 184 .append(minorVersion) 185 .append(" (") 186 .append(version) 187 .append(")\n"); 188 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 189 stringBuilder.append(DEPRECATED); 190 } 191 if ((access & Opcodes.ACC_RECORD) != 0) { 192 stringBuilder.append(RECORD); 193 } 194 appendRawAccess(access); 195 196 appendDescriptor(CLASS_SIGNATURE, signature); 197 if (signature != null) { 198 appendJavaDeclaration(name, signature); 199 } 200 201 appendAccess(access & ~(Opcodes.ACC_SUPER | Opcodes.ACC_MODULE)); 202 if ((access & Opcodes.ACC_ANNOTATION) != 0) { 203 stringBuilder.append("@interface "); 204 } else if ((access & Opcodes.ACC_INTERFACE) != 0) { 205 stringBuilder.append("interface "); 206 } else if ((access & Opcodes.ACC_ENUM) == 0) { 207 stringBuilder.append("class "); 208 } 209 appendDescriptor(INTERNAL_NAME, name); 210 211 if (superName != null && !"java/lang/Object".equals(superName)) { 212 stringBuilder.append(" extends "); 213 appendDescriptor(INTERNAL_NAME, superName); 214 } 215 if (interfaces != null && interfaces.length > 0) { 216 stringBuilder.append(" implements "); 217 for (int i = 0; i < interfaces.length; ++i) { 218 appendDescriptor(INTERNAL_NAME, interfaces[i]); 219 if (i != interfaces.length - 1) { 220 stringBuilder.append(' '); 221 } 222 } 223 } 224 stringBuilder.append(" {\n\n"); 225 226 text.add(stringBuilder.toString()); 227 } 228 229 @Override visitSource(final String file, final String debug)230 public void visitSource(final String file, final String debug) { 231 stringBuilder.setLength(0); 232 if (file != null) { 233 stringBuilder.append(tab).append("// compiled from: ").append(file).append('\n'); 234 } 235 if (debug != null) { 236 stringBuilder.append(tab).append("// debug info: ").append(debug).append('\n'); 237 } 238 if (stringBuilder.length() > 0) { 239 text.add(stringBuilder.toString()); 240 } 241 } 242 243 @Override visitModule(final String name, final int access, final String version)244 public Printer visitModule(final String name, final int access, final String version) { 245 stringBuilder.setLength(0); 246 if ((access & Opcodes.ACC_OPEN) != 0) { 247 stringBuilder.append("open "); 248 } 249 stringBuilder 250 .append("module ") 251 .append(name) 252 .append(" { ") 253 .append(version == null ? "" : "// " + version) 254 .append("\n\n"); 255 text.add(stringBuilder.toString()); 256 return addNewTextifier(null); 257 } 258 259 @Override visitNestHost(final String nestHost)260 public void visitNestHost(final String nestHost) { 261 stringBuilder.setLength(0); 262 stringBuilder.append(tab).append("NESTHOST "); 263 appendDescriptor(INTERNAL_NAME, nestHost); 264 stringBuilder.append('\n'); 265 text.add(stringBuilder.toString()); 266 } 267 268 @Override visitOuterClass(final String owner, final String name, final String descriptor)269 public void visitOuterClass(final String owner, final String name, final String descriptor) { 270 stringBuilder.setLength(0); 271 stringBuilder.append(tab).append("OUTERCLASS "); 272 appendDescriptor(INTERNAL_NAME, owner); 273 stringBuilder.append(' '); 274 if (name != null) { 275 stringBuilder.append(name).append(' '); 276 } 277 appendDescriptor(METHOD_DESCRIPTOR, descriptor); 278 stringBuilder.append('\n'); 279 text.add(stringBuilder.toString()); 280 } 281 282 @Override visitClassAnnotation(final String descriptor, final boolean visible)283 public Textifier visitClassAnnotation(final String descriptor, final boolean visible) { 284 text.add("\n"); 285 return visitAnnotation(descriptor, visible); 286 } 287 288 @Override visitClassTypeAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible)289 public Printer visitClassTypeAnnotation( 290 final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { 291 text.add("\n"); 292 return visitTypeAnnotation(typeRef, typePath, descriptor, visible); 293 } 294 295 @Override visitClassAttribute(final Attribute attribute)296 public void visitClassAttribute(final Attribute attribute) { 297 text.add("\n"); 298 visitAttribute(attribute); 299 } 300 301 @Override visitNestMember(final String nestMember)302 public void visitNestMember(final String nestMember) { 303 stringBuilder.setLength(0); 304 stringBuilder.append(tab).append("NESTMEMBER "); 305 appendDescriptor(INTERNAL_NAME, nestMember); 306 stringBuilder.append('\n'); 307 text.add(stringBuilder.toString()); 308 } 309 310 @Override visitPermittedSubclass(final String permittedSubclass)311 public void visitPermittedSubclass(final String permittedSubclass) { 312 stringBuilder.setLength(0); 313 stringBuilder.append(tab).append("PERMITTEDSUBCLASS "); 314 appendDescriptor(INTERNAL_NAME, permittedSubclass); 315 stringBuilder.append('\n'); 316 text.add(stringBuilder.toString()); 317 } 318 319 @Override visitInnerClass( final String name, final String outerName, final String innerName, final int access)320 public void visitInnerClass( 321 final String name, final String outerName, final String innerName, final int access) { 322 stringBuilder.setLength(0); 323 stringBuilder.append(tab); 324 appendRawAccess(access & ~Opcodes.ACC_SUPER); 325 stringBuilder.append(tab); 326 appendAccess(access); 327 stringBuilder.append("INNERCLASS "); 328 appendDescriptor(INTERNAL_NAME, name); 329 stringBuilder.append(' '); 330 appendDescriptor(INTERNAL_NAME, outerName); 331 stringBuilder.append(' '); 332 appendDescriptor(INTERNAL_NAME, innerName); 333 stringBuilder.append('\n'); 334 text.add(stringBuilder.toString()); 335 } 336 337 @Override visitRecordComponent( final String name, final String descriptor, final String signature)338 public Printer visitRecordComponent( 339 final String name, final String descriptor, final String signature) { 340 stringBuilder.setLength(0); 341 stringBuilder.append(tab).append("RECORDCOMPONENT "); 342 if (signature != null) { 343 stringBuilder.append(tab); 344 appendDescriptor(FIELD_SIGNATURE, signature); 345 stringBuilder.append(tab); 346 appendJavaDeclaration(name, signature); 347 } 348 349 stringBuilder.append(tab); 350 351 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 352 stringBuilder.append(' ').append(name); 353 354 stringBuilder.append('\n'); 355 text.add(stringBuilder.toString()); 356 return addNewTextifier(null); 357 } 358 359 @Override visitField( final int access, final String name, final String descriptor, final String signature, final Object value)360 public Textifier visitField( 361 final int access, 362 final String name, 363 final String descriptor, 364 final String signature, 365 final Object value) { 366 stringBuilder.setLength(0); 367 stringBuilder.append('\n'); 368 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 369 stringBuilder.append(tab).append(DEPRECATED); 370 } 371 stringBuilder.append(tab); 372 appendRawAccess(access); 373 if (signature != null) { 374 stringBuilder.append(tab); 375 appendDescriptor(FIELD_SIGNATURE, signature); 376 stringBuilder.append(tab); 377 appendJavaDeclaration(name, signature); 378 } 379 380 stringBuilder.append(tab); 381 appendAccess(access); 382 383 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 384 stringBuilder.append(' ').append(name); 385 if (value != null) { 386 stringBuilder.append(" = "); 387 if (value instanceof String) { 388 stringBuilder.append('\"').append(value).append('\"'); 389 } else { 390 stringBuilder.append(value); 391 } 392 } 393 394 stringBuilder.append('\n'); 395 text.add(stringBuilder.toString()); 396 return addNewTextifier(null); 397 } 398 399 @Override visitMethod( final int access, final String name, final String descriptor, final String signature, final String[] exceptions)400 public Textifier visitMethod( 401 final int access, 402 final String name, 403 final String descriptor, 404 final String signature, 405 final String[] exceptions) { 406 stringBuilder.setLength(0); 407 stringBuilder.append('\n'); 408 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 409 stringBuilder.append(tab).append(DEPRECATED); 410 } 411 stringBuilder.append(tab); 412 appendRawAccess(access); 413 414 if (signature != null) { 415 stringBuilder.append(tab); 416 appendDescriptor(METHOD_SIGNATURE, signature); 417 stringBuilder.append(tab); 418 appendJavaDeclaration(name, signature); 419 } 420 421 stringBuilder.append(tab); 422 appendAccess(access & ~(Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT)); 423 if ((access & Opcodes.ACC_NATIVE) != 0) { 424 stringBuilder.append("native "); 425 } 426 if ((access & Opcodes.ACC_VARARGS) != 0) { 427 stringBuilder.append("varargs "); 428 } 429 if ((access & Opcodes.ACC_BRIDGE) != 0) { 430 stringBuilder.append("bridge "); 431 } 432 if ((this.access & Opcodes.ACC_INTERFACE) != 0 433 && (access & (Opcodes.ACC_ABSTRACT | Opcodes.ACC_STATIC)) == 0) { 434 stringBuilder.append("default "); 435 } 436 437 stringBuilder.append(name); 438 appendDescriptor(METHOD_DESCRIPTOR, descriptor); 439 if (exceptions != null && exceptions.length > 0) { 440 stringBuilder.append(" throws "); 441 for (String exception : exceptions) { 442 appendDescriptor(INTERNAL_NAME, exception); 443 stringBuilder.append(' '); 444 } 445 } 446 447 stringBuilder.append('\n'); 448 text.add(stringBuilder.toString()); 449 return addNewTextifier(null); 450 } 451 452 @Override visitClassEnd()453 public void visitClassEnd() { 454 text.add("}\n"); 455 } 456 457 // ----------------------------------------------------------------------------------------------- 458 // Modules 459 // ----------------------------------------------------------------------------------------------- 460 461 @Override visitMainClass(final String mainClass)462 public void visitMainClass(final String mainClass) { 463 stringBuilder.setLength(0); 464 stringBuilder.append(" // main class ").append(mainClass).append('\n'); 465 text.add(stringBuilder.toString()); 466 } 467 468 @Override visitPackage(final String packaze)469 public void visitPackage(final String packaze) { 470 stringBuilder.setLength(0); 471 stringBuilder.append(" // package ").append(packaze).append('\n'); 472 text.add(stringBuilder.toString()); 473 } 474 475 @Override visitRequire(final String require, final int access, final String version)476 public void visitRequire(final String require, final int access, final String version) { 477 stringBuilder.setLength(0); 478 stringBuilder.append(tab).append("requires "); 479 if ((access & Opcodes.ACC_TRANSITIVE) != 0) { 480 stringBuilder.append("transitive "); 481 } 482 if ((access & Opcodes.ACC_STATIC_PHASE) != 0) { 483 stringBuilder.append("static "); 484 } 485 stringBuilder.append(require).append(';'); 486 appendRawAccess(access); 487 if (version != null) { 488 stringBuilder.append(" // version ").append(version).append('\n'); 489 } 490 text.add(stringBuilder.toString()); 491 } 492 493 @Override visitExport(final String packaze, final int access, final String... modules)494 public void visitExport(final String packaze, final int access, final String... modules) { 495 visitExportOrOpen("exports ", packaze, access, modules); 496 } 497 498 @Override visitOpen(final String packaze, final int access, final String... modules)499 public void visitOpen(final String packaze, final int access, final String... modules) { 500 visitExportOrOpen("opens ", packaze, access, modules); 501 } 502 visitExportOrOpen( final String method, final String packaze, final int access, final String... modules)503 private void visitExportOrOpen( 504 final String method, final String packaze, final int access, final String... modules) { 505 stringBuilder.setLength(0); 506 stringBuilder.append(tab).append(method); 507 stringBuilder.append(packaze); 508 if (modules != null && modules.length > 0) { 509 stringBuilder.append(" to"); 510 } else { 511 stringBuilder.append(';'); 512 } 513 appendRawAccess(access); 514 if (modules != null && modules.length > 0) { 515 for (int i = 0; i < modules.length; ++i) { 516 stringBuilder.append(tab2).append(modules[i]); 517 stringBuilder.append(i != modules.length - 1 ? ",\n" : ";\n"); 518 } 519 } 520 text.add(stringBuilder.toString()); 521 } 522 523 @Override visitUse(final String use)524 public void visitUse(final String use) { 525 stringBuilder.setLength(0); 526 stringBuilder.append(tab).append("uses "); 527 appendDescriptor(INTERNAL_NAME, use); 528 stringBuilder.append(";\n"); 529 text.add(stringBuilder.toString()); 530 } 531 532 @Override visitProvide(final String provide, final String... providers)533 public void visitProvide(final String provide, final String... providers) { 534 stringBuilder.setLength(0); 535 stringBuilder.append(tab).append("provides "); 536 appendDescriptor(INTERNAL_NAME, provide); 537 stringBuilder.append(" with\n"); 538 for (int i = 0; i < providers.length; ++i) { 539 stringBuilder.append(tab2); 540 appendDescriptor(INTERNAL_NAME, providers[i]); 541 stringBuilder.append(i != providers.length - 1 ? ",\n" : ";\n"); 542 } 543 text.add(stringBuilder.toString()); 544 } 545 546 @Override visitModuleEnd()547 public void visitModuleEnd() { 548 // Nothing to do. 549 } 550 551 // ----------------------------------------------------------------------------------------------- 552 // Annotations 553 // ----------------------------------------------------------------------------------------------- 554 555 // DontCheck(OverloadMethodsDeclarationOrder): overloads are semantically different. 556 @Override visit(final String name, final Object value)557 public void visit(final String name, final Object value) { 558 visitAnnotationValue(name); 559 if (value instanceof String) { 560 visitString((String) value); 561 } else if (value instanceof Type) { 562 visitType((Type) value); 563 } else if (value instanceof Byte) { 564 visitByte(((Byte) value).byteValue()); 565 } else if (value instanceof Boolean) { 566 visitBoolean(((Boolean) value).booleanValue()); 567 } else if (value instanceof Short) { 568 visitShort(((Short) value).shortValue()); 569 } else if (value instanceof Character) { 570 visitChar(((Character) value).charValue()); 571 } else if (value instanceof Integer) { 572 visitInt(((Integer) value).intValue()); 573 } else if (value instanceof Float) { 574 visitFloat(((Float) value).floatValue()); 575 } else if (value instanceof Long) { 576 visitLong(((Long) value).longValue()); 577 } else if (value instanceof Double) { 578 visitDouble(((Double) value).doubleValue()); 579 } else if (value.getClass().isArray()) { 580 stringBuilder.append('{'); 581 if (value instanceof byte[]) { 582 byte[] byteArray = (byte[]) value; 583 for (int i = 0; i < byteArray.length; i++) { 584 maybeAppendComma(i); 585 visitByte(byteArray[i]); 586 } 587 } else if (value instanceof boolean[]) { 588 boolean[] booleanArray = (boolean[]) value; 589 for (int i = 0; i < booleanArray.length; i++) { 590 maybeAppendComma(i); 591 visitBoolean(booleanArray[i]); 592 } 593 } else if (value instanceof short[]) { 594 short[] shortArray = (short[]) value; 595 for (int i = 0; i < shortArray.length; i++) { 596 maybeAppendComma(i); 597 visitShort(shortArray[i]); 598 } 599 } else if (value instanceof char[]) { 600 char[] charArray = (char[]) value; 601 for (int i = 0; i < charArray.length; i++) { 602 maybeAppendComma(i); 603 visitChar(charArray[i]); 604 } 605 } else if (value instanceof int[]) { 606 int[] intArray = (int[]) value; 607 for (int i = 0; i < intArray.length; i++) { 608 maybeAppendComma(i); 609 visitInt(intArray[i]); 610 } 611 } else if (value instanceof long[]) { 612 long[] longArray = (long[]) value; 613 for (int i = 0; i < longArray.length; i++) { 614 maybeAppendComma(i); 615 visitLong(longArray[i]); 616 } 617 } else if (value instanceof float[]) { 618 float[] floatArray = (float[]) value; 619 for (int i = 0; i < floatArray.length; i++) { 620 maybeAppendComma(i); 621 visitFloat(floatArray[i]); 622 } 623 } else if (value instanceof double[]) { 624 double[] doubleArray = (double[]) value; 625 for (int i = 0; i < doubleArray.length; i++) { 626 maybeAppendComma(i); 627 visitDouble(doubleArray[i]); 628 } 629 } 630 stringBuilder.append('}'); 631 } 632 text.add(stringBuilder.toString()); 633 } 634 visitInt(final int value)635 private void visitInt(final int value) { 636 stringBuilder.append(value); 637 } 638 visitLong(final long value)639 private void visitLong(final long value) { 640 stringBuilder.append(value).append('L'); 641 } 642 visitFloat(final float value)643 private void visitFloat(final float value) { 644 stringBuilder.append(value).append('F'); 645 } 646 visitDouble(final double value)647 private void visitDouble(final double value) { 648 stringBuilder.append(value).append('D'); 649 } 650 visitChar(final char value)651 private void visitChar(final char value) { 652 stringBuilder.append("(char)").append((int) value); 653 } 654 visitShort(final short value)655 private void visitShort(final short value) { 656 stringBuilder.append("(short)").append(value); 657 } 658 visitByte(final byte value)659 private void visitByte(final byte value) { 660 stringBuilder.append("(byte)").append(value); 661 } 662 visitBoolean(final boolean value)663 private void visitBoolean(final boolean value) { 664 stringBuilder.append(value); 665 } 666 visitString(final String value)667 private void visitString(final String value) { 668 appendString(stringBuilder, value); 669 } 670 visitType(final Type value)671 private void visitType(final Type value) { 672 stringBuilder.append(value.getClassName()).append(CLASS_SUFFIX); 673 } 674 675 @Override visitEnum(final String name, final String descriptor, final String value)676 public void visitEnum(final String name, final String descriptor, final String value) { 677 visitAnnotationValue(name); 678 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 679 stringBuilder.append('.').append(value); 680 text.add(stringBuilder.toString()); 681 } 682 683 @Override visitAnnotation(final String name, final String descriptor)684 public Textifier visitAnnotation(final String name, final String descriptor) { 685 visitAnnotationValue(name); 686 stringBuilder.append('@'); 687 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 688 stringBuilder.append('('); 689 text.add(stringBuilder.toString()); 690 return addNewTextifier(")"); 691 } 692 693 @Override visitArray(final String name)694 public Textifier visitArray(final String name) { 695 visitAnnotationValue(name); 696 stringBuilder.append('{'); 697 text.add(stringBuilder.toString()); 698 return addNewTextifier("}"); 699 } 700 701 @Override visitAnnotationEnd()702 public void visitAnnotationEnd() { 703 // Nothing to do. 704 } 705 visitAnnotationValue(final String name)706 private void visitAnnotationValue(final String name) { 707 stringBuilder.setLength(0); 708 maybeAppendComma(numAnnotationValues++); 709 if (name != null) { 710 stringBuilder.append(name).append('='); 711 } 712 } 713 714 // ----------------------------------------------------------------------------------------------- 715 // Record components 716 // ----------------------------------------------------------------------------------------------- 717 718 @Override visitRecordComponentAnnotation(final String descriptor, final boolean visible)719 public Textifier visitRecordComponentAnnotation(final String descriptor, final boolean visible) { 720 return visitAnnotation(descriptor, visible); 721 } 722 723 @Override visitRecordComponentTypeAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible)724 public Printer visitRecordComponentTypeAnnotation( 725 final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { 726 return visitTypeAnnotation(typeRef, typePath, descriptor, visible); 727 } 728 729 @Override visitRecordComponentAttribute(final Attribute attribute)730 public void visitRecordComponentAttribute(final Attribute attribute) { 731 visitAttribute(attribute); 732 } 733 734 @Override visitRecordComponentEnd()735 public void visitRecordComponentEnd() { 736 // Nothing to do. 737 } 738 739 // ----------------------------------------------------------------------------------------------- 740 // Fields 741 // ----------------------------------------------------------------------------------------------- 742 743 @Override visitFieldAnnotation(final String descriptor, final boolean visible)744 public Textifier visitFieldAnnotation(final String descriptor, final boolean visible) { 745 return visitAnnotation(descriptor, visible); 746 } 747 748 @Override visitFieldTypeAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible)749 public Printer visitFieldTypeAnnotation( 750 final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { 751 return visitTypeAnnotation(typeRef, typePath, descriptor, visible); 752 } 753 754 @Override visitFieldAttribute(final Attribute attribute)755 public void visitFieldAttribute(final Attribute attribute) { 756 visitAttribute(attribute); 757 } 758 759 @Override visitFieldEnd()760 public void visitFieldEnd() { 761 // Nothing to do. 762 } 763 764 // ----------------------------------------------------------------------------------------------- 765 // Methods 766 // ----------------------------------------------------------------------------------------------- 767 768 @Override visitParameter(final String name, final int access)769 public void visitParameter(final String name, final int access) { 770 stringBuilder.setLength(0); 771 stringBuilder.append(tab2).append("// parameter "); 772 appendAccess(access); 773 stringBuilder.append(' ').append((name == null) ? "<no name>" : name).append('\n'); 774 text.add(stringBuilder.toString()); 775 } 776 777 @Override visitAnnotationDefault()778 public Textifier visitAnnotationDefault() { 779 text.add(tab2 + "default="); 780 return addNewTextifier("\n"); 781 } 782 783 @Override visitMethodAnnotation(final String descriptor, final boolean visible)784 public Textifier visitMethodAnnotation(final String descriptor, final boolean visible) { 785 return visitAnnotation(descriptor, visible); 786 } 787 788 @Override visitMethodTypeAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible)789 public Printer visitMethodTypeAnnotation( 790 final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { 791 return visitTypeAnnotation(typeRef, typePath, descriptor, visible); 792 } 793 794 @Override visitAnnotableParameterCount(final int parameterCount, final boolean visible)795 public Textifier visitAnnotableParameterCount(final int parameterCount, final boolean visible) { 796 stringBuilder.setLength(0); 797 stringBuilder.append(tab2).append("// annotable parameter count: "); 798 stringBuilder.append(parameterCount); 799 stringBuilder.append(visible ? " (visible)\n" : " (invisible)\n"); 800 text.add(stringBuilder.toString()); 801 return this; 802 } 803 804 @Override visitParameterAnnotation( final int parameter, final String descriptor, final boolean visible)805 public Textifier visitParameterAnnotation( 806 final int parameter, final String descriptor, final boolean visible) { 807 stringBuilder.setLength(0); 808 stringBuilder.append(tab2).append('@'); 809 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 810 stringBuilder.append('('); 811 text.add(stringBuilder.toString()); 812 813 stringBuilder.setLength(0); 814 stringBuilder 815 .append(visible ? ") // parameter " : ") // invisible, parameter ") 816 .append(parameter) 817 .append('\n'); 818 return addNewTextifier(stringBuilder.toString()); 819 } 820 821 @Override visitMethodAttribute(final Attribute attribute)822 public void visitMethodAttribute(final Attribute attribute) { 823 visitAttribute(attribute); 824 } 825 826 @Override visitCode()827 public void visitCode() { 828 // Nothing to do. 829 } 830 831 @Override visitFrame( final int type, final int numLocal, final Object[] local, final int numStack, final Object[] stack)832 public void visitFrame( 833 final int type, 834 final int numLocal, 835 final Object[] local, 836 final int numStack, 837 final Object[] stack) { 838 stringBuilder.setLength(0); 839 stringBuilder.append(ltab); 840 stringBuilder.append("FRAME "); 841 switch (type) { 842 case Opcodes.F_NEW: 843 case Opcodes.F_FULL: 844 stringBuilder.append("FULL ["); 845 appendFrameTypes(numLocal, local); 846 stringBuilder.append("] ["); 847 appendFrameTypes(numStack, stack); 848 stringBuilder.append(']'); 849 break; 850 case Opcodes.F_APPEND: 851 stringBuilder.append("APPEND ["); 852 appendFrameTypes(numLocal, local); 853 stringBuilder.append(']'); 854 break; 855 case Opcodes.F_CHOP: 856 stringBuilder.append("CHOP ").append(numLocal); 857 break; 858 case Opcodes.F_SAME: 859 stringBuilder.append("SAME"); 860 break; 861 case Opcodes.F_SAME1: 862 stringBuilder.append("SAME1 "); 863 appendFrameTypes(1, stack); 864 break; 865 default: 866 throw new IllegalArgumentException(); 867 } 868 stringBuilder.append('\n'); 869 text.add(stringBuilder.toString()); 870 } 871 872 @Override visitInsn(final int opcode)873 public void visitInsn(final int opcode) { 874 stringBuilder.setLength(0); 875 stringBuilder.append(tab2).append(OPCODES[opcode]).append('\n'); 876 text.add(stringBuilder.toString()); 877 } 878 879 @Override visitIntInsn(final int opcode, final int operand)880 public void visitIntInsn(final int opcode, final int operand) { 881 stringBuilder.setLength(0); 882 stringBuilder 883 .append(tab2) 884 .append(OPCODES[opcode]) 885 .append(' ') 886 .append(opcode == Opcodes.NEWARRAY ? TYPES[operand] : Integer.toString(operand)) 887 .append('\n'); 888 text.add(stringBuilder.toString()); 889 } 890 891 @Override visitVarInsn(final int opcode, final int varIndex)892 public void visitVarInsn(final int opcode, final int varIndex) { 893 stringBuilder.setLength(0); 894 stringBuilder.append(tab2).append(OPCODES[opcode]).append(' ').append(varIndex).append('\n'); 895 text.add(stringBuilder.toString()); 896 } 897 898 @Override visitTypeInsn(final int opcode, final String type)899 public void visitTypeInsn(final int opcode, final String type) { 900 stringBuilder.setLength(0); 901 stringBuilder.append(tab2).append(OPCODES[opcode]).append(' '); 902 appendDescriptor(INTERNAL_NAME, type); 903 stringBuilder.append('\n'); 904 text.add(stringBuilder.toString()); 905 } 906 907 @Override visitFieldInsn( final int opcode, final String owner, final String name, final String descriptor)908 public void visitFieldInsn( 909 final int opcode, final String owner, final String name, final String descriptor) { 910 stringBuilder.setLength(0); 911 stringBuilder.append(tab2).append(OPCODES[opcode]).append(' '); 912 appendDescriptor(INTERNAL_NAME, owner); 913 stringBuilder.append('.').append(name).append(" : "); 914 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 915 stringBuilder.append('\n'); 916 text.add(stringBuilder.toString()); 917 } 918 919 @Override visitMethodInsn( final int opcode, final String owner, final String name, final String descriptor, final boolean isInterface)920 public void visitMethodInsn( 921 final int opcode, 922 final String owner, 923 final String name, 924 final String descriptor, 925 final boolean isInterface) { 926 stringBuilder.setLength(0); 927 stringBuilder.append(tab2).append(OPCODES[opcode]).append(' '); 928 appendDescriptor(INTERNAL_NAME, owner); 929 stringBuilder.append('.').append(name).append(' '); 930 appendDescriptor(METHOD_DESCRIPTOR, descriptor); 931 if (isInterface) { 932 stringBuilder.append(" (itf)"); 933 } 934 stringBuilder.append('\n'); 935 text.add(stringBuilder.toString()); 936 } 937 938 @Override visitInvokeDynamicInsn( final String name, final String descriptor, final Handle bootstrapMethodHandle, final Object... bootstrapMethodArguments)939 public void visitInvokeDynamicInsn( 940 final String name, 941 final String descriptor, 942 final Handle bootstrapMethodHandle, 943 final Object... bootstrapMethodArguments) { 944 stringBuilder.setLength(0); 945 stringBuilder.append(tab2).append("INVOKEDYNAMIC").append(' '); 946 stringBuilder.append(name); 947 appendDescriptor(METHOD_DESCRIPTOR, descriptor); 948 stringBuilder.append(" ["); 949 stringBuilder.append('\n'); 950 stringBuilder.append(tab3); 951 appendHandle(bootstrapMethodHandle); 952 stringBuilder.append('\n'); 953 stringBuilder.append(tab3).append("// arguments:"); 954 if (bootstrapMethodArguments.length == 0) { 955 stringBuilder.append(" none"); 956 } else { 957 stringBuilder.append('\n'); 958 for (Object value : bootstrapMethodArguments) { 959 stringBuilder.append(tab3); 960 if (value instanceof String) { 961 Printer.appendString(stringBuilder, (String) value); 962 } else if (value instanceof Type) { 963 Type type = (Type) value; 964 if (type.getSort() == Type.METHOD) { 965 appendDescriptor(METHOD_DESCRIPTOR, type.getDescriptor()); 966 } else { 967 visitType(type); 968 } 969 } else if (value instanceof Handle) { 970 appendHandle((Handle) value); 971 } else { 972 stringBuilder.append(value); 973 } 974 stringBuilder.append(", \n"); 975 } 976 stringBuilder.setLength(stringBuilder.length() - 3); 977 } 978 stringBuilder.append('\n'); 979 stringBuilder.append(tab2).append("]\n"); 980 text.add(stringBuilder.toString()); 981 } 982 983 @Override visitJumpInsn(final int opcode, final Label label)984 public void visitJumpInsn(final int opcode, final Label label) { 985 stringBuilder.setLength(0); 986 stringBuilder.append(tab2).append(OPCODES[opcode]).append(' '); 987 appendLabel(label); 988 stringBuilder.append('\n'); 989 text.add(stringBuilder.toString()); 990 } 991 992 @Override visitLabel(final Label label)993 public void visitLabel(final Label label) { 994 stringBuilder.setLength(0); 995 stringBuilder.append(ltab); 996 appendLabel(label); 997 stringBuilder.append('\n'); 998 text.add(stringBuilder.toString()); 999 } 1000 1001 @Override visitLdcInsn(final Object value)1002 public void visitLdcInsn(final Object value) { 1003 stringBuilder.setLength(0); 1004 stringBuilder.append(tab2).append("LDC "); 1005 if (value instanceof String) { 1006 Printer.appendString(stringBuilder, (String) value); 1007 } else if (value instanceof Type) { 1008 stringBuilder.append(((Type) value).getDescriptor()).append(CLASS_SUFFIX); 1009 } else { 1010 stringBuilder.append(value); 1011 } 1012 stringBuilder.append('\n'); 1013 text.add(stringBuilder.toString()); 1014 } 1015 1016 @Override visitIincInsn(final int varIndex, final int increment)1017 public void visitIincInsn(final int varIndex, final int increment) { 1018 stringBuilder.setLength(0); 1019 stringBuilder 1020 .append(tab2) 1021 .append("IINC ") 1022 .append(varIndex) 1023 .append(' ') 1024 .append(increment) 1025 .append('\n'); 1026 text.add(stringBuilder.toString()); 1027 } 1028 1029 @Override visitTableSwitchInsn( final int min, final int max, final Label dflt, final Label... labels)1030 public void visitTableSwitchInsn( 1031 final int min, final int max, final Label dflt, final Label... labels) { 1032 stringBuilder.setLength(0); 1033 stringBuilder.append(tab2).append("TABLESWITCH\n"); 1034 for (int i = 0; i < labels.length; ++i) { 1035 stringBuilder.append(tab3).append(min + i).append(": "); 1036 appendLabel(labels[i]); 1037 stringBuilder.append('\n'); 1038 } 1039 stringBuilder.append(tab3).append("default: "); 1040 appendLabel(dflt); 1041 stringBuilder.append('\n'); 1042 text.add(stringBuilder.toString()); 1043 } 1044 1045 @Override visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels)1046 public void visitLookupSwitchInsn(final Label dflt, final int[] keys, final Label[] labels) { 1047 stringBuilder.setLength(0); 1048 stringBuilder.append(tab2).append("LOOKUPSWITCH\n"); 1049 for (int i = 0; i < labels.length; ++i) { 1050 stringBuilder.append(tab3).append(keys[i]).append(": "); 1051 appendLabel(labels[i]); 1052 stringBuilder.append('\n'); 1053 } 1054 stringBuilder.append(tab3).append("default: "); 1055 appendLabel(dflt); 1056 stringBuilder.append('\n'); 1057 text.add(stringBuilder.toString()); 1058 } 1059 1060 @Override visitMultiANewArrayInsn(final String descriptor, final int numDimensions)1061 public void visitMultiANewArrayInsn(final String descriptor, final int numDimensions) { 1062 stringBuilder.setLength(0); 1063 stringBuilder.append(tab2).append("MULTIANEWARRAY "); 1064 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 1065 stringBuilder.append(' ').append(numDimensions).append('\n'); 1066 text.add(stringBuilder.toString()); 1067 } 1068 1069 @Override visitInsnAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible)1070 public Printer visitInsnAnnotation( 1071 final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { 1072 return visitTypeAnnotation(typeRef, typePath, descriptor, visible); 1073 } 1074 1075 @Override visitTryCatchBlock( final Label start, final Label end, final Label handler, final String type)1076 public void visitTryCatchBlock( 1077 final Label start, final Label end, final Label handler, final String type) { 1078 stringBuilder.setLength(0); 1079 stringBuilder.append(tab2).append("TRYCATCHBLOCK "); 1080 appendLabel(start); 1081 stringBuilder.append(' '); 1082 appendLabel(end); 1083 stringBuilder.append(' '); 1084 appendLabel(handler); 1085 stringBuilder.append(' '); 1086 appendDescriptor(INTERNAL_NAME, type); 1087 stringBuilder.append('\n'); 1088 text.add(stringBuilder.toString()); 1089 } 1090 1091 @Override visitTryCatchAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible)1092 public Printer visitTryCatchAnnotation( 1093 final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { 1094 stringBuilder.setLength(0); 1095 stringBuilder.append(tab2).append("TRYCATCHBLOCK @"); 1096 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 1097 stringBuilder.append('('); 1098 text.add(stringBuilder.toString()); 1099 1100 stringBuilder.setLength(0); 1101 stringBuilder.append(") : "); 1102 appendTypeReference(typeRef); 1103 stringBuilder.append(", ").append(typePath); 1104 stringBuilder.append(visible ? "\n" : INVISIBLE); 1105 return addNewTextifier(stringBuilder.toString()); 1106 } 1107 1108 @Override visitLocalVariable( final String name, final String descriptor, final String signature, final Label start, final Label end, final int index)1109 public void visitLocalVariable( 1110 final String name, 1111 final String descriptor, 1112 final String signature, 1113 final Label start, 1114 final Label end, 1115 final int index) { 1116 stringBuilder.setLength(0); 1117 stringBuilder.append(tab2).append("LOCALVARIABLE ").append(name).append(' '); 1118 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 1119 stringBuilder.append(' '); 1120 appendLabel(start); 1121 stringBuilder.append(' '); 1122 appendLabel(end); 1123 stringBuilder.append(' ').append(index).append('\n'); 1124 1125 if (signature != null) { 1126 stringBuilder.append(tab2); 1127 appendDescriptor(FIELD_SIGNATURE, signature); 1128 stringBuilder.append(tab2); 1129 appendJavaDeclaration(name, signature); 1130 } 1131 text.add(stringBuilder.toString()); 1132 } 1133 1134 @Override visitLocalVariableAnnotation( final int typeRef, final TypePath typePath, final Label[] start, final Label[] end, final int[] index, final String descriptor, final boolean visible)1135 public Printer visitLocalVariableAnnotation( 1136 final int typeRef, 1137 final TypePath typePath, 1138 final Label[] start, 1139 final Label[] end, 1140 final int[] index, 1141 final String descriptor, 1142 final boolean visible) { 1143 stringBuilder.setLength(0); 1144 stringBuilder.append(tab2).append("LOCALVARIABLE @"); 1145 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 1146 stringBuilder.append('('); 1147 text.add(stringBuilder.toString()); 1148 1149 stringBuilder.setLength(0); 1150 stringBuilder.append(") : "); 1151 appendTypeReference(typeRef); 1152 stringBuilder.append(", ").append(typePath); 1153 for (int i = 0; i < start.length; ++i) { 1154 stringBuilder.append(" [ "); 1155 appendLabel(start[i]); 1156 stringBuilder.append(" - "); 1157 appendLabel(end[i]); 1158 stringBuilder.append(" - ").append(index[i]).append(" ]"); 1159 } 1160 stringBuilder.append(visible ? "\n" : INVISIBLE); 1161 return addNewTextifier(stringBuilder.toString()); 1162 } 1163 1164 @Override visitLineNumber(final int line, final Label start)1165 public void visitLineNumber(final int line, final Label start) { 1166 stringBuilder.setLength(0); 1167 stringBuilder.append(tab2).append("LINENUMBER ").append(line).append(' '); 1168 appendLabel(start); 1169 stringBuilder.append('\n'); 1170 text.add(stringBuilder.toString()); 1171 } 1172 1173 @Override visitMaxs(final int maxStack, final int maxLocals)1174 public void visitMaxs(final int maxStack, final int maxLocals) { 1175 stringBuilder.setLength(0); 1176 stringBuilder.append(tab2).append("MAXSTACK = ").append(maxStack).append('\n'); 1177 text.add(stringBuilder.toString()); 1178 1179 stringBuilder.setLength(0); 1180 stringBuilder.append(tab2).append("MAXLOCALS = ").append(maxLocals).append('\n'); 1181 text.add(stringBuilder.toString()); 1182 } 1183 1184 @Override visitMethodEnd()1185 public void visitMethodEnd() { 1186 // Nothing to do. 1187 } 1188 1189 // ----------------------------------------------------------------------------------------------- 1190 // Common methods 1191 // ----------------------------------------------------------------------------------------------- 1192 1193 /** 1194 * Prints a disassembled view of the given annotation. 1195 * 1196 * @param descriptor the class descriptor of the annotation class. 1197 * @param visible {@literal true} if the annotation is visible at runtime. 1198 * @return a visitor to visit the annotation values. 1199 */ 1200 // DontCheck(OverloadMethodsDeclarationOrder): overloads are semantically different. visitAnnotation(final String descriptor, final boolean visible)1201 public Textifier visitAnnotation(final String descriptor, final boolean visible) { 1202 stringBuilder.setLength(0); 1203 stringBuilder.append(tab).append('@'); 1204 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 1205 stringBuilder.append('('); 1206 text.add(stringBuilder.toString()); 1207 return addNewTextifier(visible ? ")\n" : ") // invisible\n"); 1208 } 1209 1210 /** 1211 * Prints a disassembled view of the given type annotation. 1212 * 1213 * @param typeRef a reference to the annotated type. See {@link TypeReference}. 1214 * @param typePath the path to the annotated type argument, wildcard bound, array element type, or 1215 * static inner type within 'typeRef'. May be {@literal null} if the annotation targets 1216 * 'typeRef' as a whole. 1217 * @param descriptor the class descriptor of the annotation class. 1218 * @param visible {@literal true} if the annotation is visible at runtime. 1219 * @return a visitor to visit the annotation values. 1220 */ visitTypeAnnotation( final int typeRef, final TypePath typePath, final String descriptor, final boolean visible)1221 public Textifier visitTypeAnnotation( 1222 final int typeRef, final TypePath typePath, final String descriptor, final boolean visible) { 1223 stringBuilder.setLength(0); 1224 stringBuilder.append(tab).append('@'); 1225 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 1226 stringBuilder.append('('); 1227 text.add(stringBuilder.toString()); 1228 1229 stringBuilder.setLength(0); 1230 stringBuilder.append(") : "); 1231 appendTypeReference(typeRef); 1232 stringBuilder.append(", ").append(typePath); 1233 stringBuilder.append(visible ? "\n" : INVISIBLE); 1234 return addNewTextifier(stringBuilder.toString()); 1235 } 1236 1237 /** 1238 * Prints a disassembled view of the given attribute. 1239 * 1240 * @param attribute an attribute. 1241 */ visitAttribute(final Attribute attribute)1242 public void visitAttribute(final Attribute attribute) { 1243 stringBuilder.setLength(0); 1244 stringBuilder.append(tab).append("ATTRIBUTE "); 1245 appendDescriptor(-1, attribute.type); 1246 1247 if (attribute instanceof TextifierSupport) { 1248 if (labelNames == null) { 1249 labelNames = new HashMap<>(); 1250 } 1251 ((TextifierSupport) attribute).textify(stringBuilder, labelNames); 1252 } else { 1253 stringBuilder.append(" : unknown\n"); 1254 } 1255 1256 text.add(stringBuilder.toString()); 1257 } 1258 1259 // ----------------------------------------------------------------------------------------------- 1260 // Utility methods 1261 // ----------------------------------------------------------------------------------------------- 1262 1263 /** 1264 * Appends a string representation of the given access flags to {@link #stringBuilder}. 1265 * 1266 * @param accessFlags some access flags. 1267 */ appendAccess(final int accessFlags)1268 private void appendAccess(final int accessFlags) { 1269 if ((accessFlags & Opcodes.ACC_PUBLIC) != 0) { 1270 stringBuilder.append("public "); 1271 } 1272 if ((accessFlags & Opcodes.ACC_PRIVATE) != 0) { 1273 stringBuilder.append("private "); 1274 } 1275 if ((accessFlags & Opcodes.ACC_PROTECTED) != 0) { 1276 stringBuilder.append("protected "); 1277 } 1278 if ((accessFlags & Opcodes.ACC_FINAL) != 0) { 1279 stringBuilder.append("final "); 1280 } 1281 if ((accessFlags & Opcodes.ACC_STATIC) != 0) { 1282 stringBuilder.append("static "); 1283 } 1284 if ((accessFlags & Opcodes.ACC_SYNCHRONIZED) != 0) { 1285 stringBuilder.append("synchronized "); 1286 } 1287 if ((accessFlags & Opcodes.ACC_VOLATILE) != 0) { 1288 stringBuilder.append("volatile "); 1289 } 1290 if ((accessFlags & Opcodes.ACC_TRANSIENT) != 0) { 1291 stringBuilder.append("transient "); 1292 } 1293 if ((accessFlags & Opcodes.ACC_ABSTRACT) != 0) { 1294 stringBuilder.append("abstract "); 1295 } 1296 if ((accessFlags & Opcodes.ACC_STRICT) != 0) { 1297 stringBuilder.append("strictfp "); 1298 } 1299 if ((accessFlags & Opcodes.ACC_SYNTHETIC) != 0) { 1300 stringBuilder.append("synthetic "); 1301 } 1302 if ((accessFlags & Opcodes.ACC_MANDATED) != 0) { 1303 stringBuilder.append("mandated "); 1304 } 1305 if ((accessFlags & Opcodes.ACC_ENUM) != 0) { 1306 stringBuilder.append("enum "); 1307 } 1308 } 1309 1310 /** 1311 * Appends the hexadecimal value of the given access flags to {@link #stringBuilder}. 1312 * 1313 * @param accessFlags some access flags. 1314 */ appendRawAccess(final int accessFlags)1315 private void appendRawAccess(final int accessFlags) { 1316 stringBuilder 1317 .append("// access flags 0x") 1318 .append(Integer.toHexString(accessFlags).toUpperCase()) 1319 .append('\n'); 1320 } 1321 1322 /** 1323 * Appends an internal name, a type descriptor or a type signature to {@link #stringBuilder}. 1324 * 1325 * @param type the type of 'value'. Must be one of {@link #INTERNAL_NAME}, {@link 1326 * #FIELD_DESCRIPTOR}, {@link #FIELD_SIGNATURE}, {@link #METHOD_DESCRIPTOR}, {@link 1327 * #METHOD_SIGNATURE}, {@link #CLASS_SIGNATURE} or {@link #HANDLE_DESCRIPTOR}. 1328 * @param value an internal name (see {@link Type#getInternalName()}), type descriptor or a type 1329 * signature. May be {@literal null}. 1330 */ appendDescriptor(final int type, final String value)1331 protected void appendDescriptor(final int type, final String value) { 1332 if (type == CLASS_SIGNATURE || type == FIELD_SIGNATURE || type == METHOD_SIGNATURE) { 1333 if (value != null) { 1334 stringBuilder.append("// signature ").append(value).append('\n'); 1335 } 1336 } else { 1337 stringBuilder.append(value); 1338 } 1339 } 1340 1341 /** 1342 * Appends the Java generic type declaration corresponding to the given signature. 1343 * 1344 * @param name a class, field or method name. 1345 * @param signature a class, field or method signature. 1346 */ appendJavaDeclaration(final String name, final String signature)1347 private void appendJavaDeclaration(final String name, final String signature) { 1348 TraceSignatureVisitor traceSignatureVisitor = new TraceSignatureVisitor(access); 1349 new SignatureReader(signature).accept(traceSignatureVisitor); 1350 stringBuilder.append("// declaration: "); 1351 if (traceSignatureVisitor.getReturnType() != null) { 1352 stringBuilder.append(traceSignatureVisitor.getReturnType()); 1353 stringBuilder.append(' '); 1354 } 1355 stringBuilder.append(name); 1356 stringBuilder.append(traceSignatureVisitor.getDeclaration()); 1357 if (traceSignatureVisitor.getExceptions() != null) { 1358 stringBuilder.append(" throws ").append(traceSignatureVisitor.getExceptions()); 1359 } 1360 stringBuilder.append('\n'); 1361 } 1362 1363 /** 1364 * Appends the name of the given label to {@link #stringBuilder}. Constructs a new label name if 1365 * the given label does not yet have one. 1366 * 1367 * @param label a label. 1368 */ appendLabel(final Label label)1369 protected void appendLabel(final Label label) { 1370 if (labelNames == null) { 1371 labelNames = new HashMap<>(); 1372 } 1373 String name = labelNames.get(label); 1374 if (name == null) { 1375 name = "L" + labelNames.size(); 1376 labelNames.put(label, name); 1377 } 1378 stringBuilder.append(name); 1379 } 1380 1381 /** 1382 * Appends a string representation of the given handle to {@link #stringBuilder}. 1383 * 1384 * @param handle a handle. 1385 */ appendHandle(final Handle handle)1386 protected void appendHandle(final Handle handle) { 1387 int tag = handle.getTag(); 1388 stringBuilder.append("// handle kind 0x").append(Integer.toHexString(tag)).append(" : "); 1389 boolean isMethodHandle = false; 1390 switch (tag) { 1391 case Opcodes.H_GETFIELD: 1392 stringBuilder.append("GETFIELD"); 1393 break; 1394 case Opcodes.H_GETSTATIC: 1395 stringBuilder.append("GETSTATIC"); 1396 break; 1397 case Opcodes.H_PUTFIELD: 1398 stringBuilder.append("PUTFIELD"); 1399 break; 1400 case Opcodes.H_PUTSTATIC: 1401 stringBuilder.append("PUTSTATIC"); 1402 break; 1403 case Opcodes.H_INVOKEINTERFACE: 1404 stringBuilder.append("INVOKEINTERFACE"); 1405 isMethodHandle = true; 1406 break; 1407 case Opcodes.H_INVOKESPECIAL: 1408 stringBuilder.append("INVOKESPECIAL"); 1409 isMethodHandle = true; 1410 break; 1411 case Opcodes.H_INVOKESTATIC: 1412 stringBuilder.append("INVOKESTATIC"); 1413 isMethodHandle = true; 1414 break; 1415 case Opcodes.H_INVOKEVIRTUAL: 1416 stringBuilder.append("INVOKEVIRTUAL"); 1417 isMethodHandle = true; 1418 break; 1419 case Opcodes.H_NEWINVOKESPECIAL: 1420 stringBuilder.append("NEWINVOKESPECIAL"); 1421 isMethodHandle = true; 1422 break; 1423 default: 1424 throw new IllegalArgumentException(); 1425 } 1426 stringBuilder.append('\n'); 1427 stringBuilder.append(tab3); 1428 appendDescriptor(INTERNAL_NAME, handle.getOwner()); 1429 stringBuilder.append('.'); 1430 stringBuilder.append(handle.getName()); 1431 if (!isMethodHandle) { 1432 stringBuilder.append('('); 1433 } 1434 appendDescriptor(HANDLE_DESCRIPTOR, handle.getDesc()); 1435 if (!isMethodHandle) { 1436 stringBuilder.append(')'); 1437 } 1438 if (handle.isInterface()) { 1439 stringBuilder.append(" itf"); 1440 } 1441 } 1442 1443 /** 1444 * Appends a comma to {@link #stringBuilder} if the given number is strictly positive. 1445 * 1446 * @param numValues a number of 'values visited so far', for instance the number of annotation 1447 * values visited so far in an annotation visitor. 1448 */ maybeAppendComma(final int numValues)1449 private void maybeAppendComma(final int numValues) { 1450 if (numValues > 0) { 1451 stringBuilder.append(", "); 1452 } 1453 } 1454 1455 /** 1456 * Appends a string representation of the given type reference to {@link #stringBuilder}. 1457 * 1458 * @param typeRef a type reference. See {@link TypeReference}. 1459 */ appendTypeReference(final int typeRef)1460 private void appendTypeReference(final int typeRef) { 1461 TypeReference typeReference = new TypeReference(typeRef); 1462 switch (typeReference.getSort()) { 1463 case TypeReference.CLASS_TYPE_PARAMETER: 1464 stringBuilder.append("CLASS_TYPE_PARAMETER ").append(typeReference.getTypeParameterIndex()); 1465 break; 1466 case TypeReference.METHOD_TYPE_PARAMETER: 1467 stringBuilder 1468 .append("METHOD_TYPE_PARAMETER ") 1469 .append(typeReference.getTypeParameterIndex()); 1470 break; 1471 case TypeReference.CLASS_EXTENDS: 1472 stringBuilder.append("CLASS_EXTENDS ").append(typeReference.getSuperTypeIndex()); 1473 break; 1474 case TypeReference.CLASS_TYPE_PARAMETER_BOUND: 1475 stringBuilder 1476 .append("CLASS_TYPE_PARAMETER_BOUND ") 1477 .append(typeReference.getTypeParameterIndex()) 1478 .append(", ") 1479 .append(typeReference.getTypeParameterBoundIndex()); 1480 break; 1481 case TypeReference.METHOD_TYPE_PARAMETER_BOUND: 1482 stringBuilder 1483 .append("METHOD_TYPE_PARAMETER_BOUND ") 1484 .append(typeReference.getTypeParameterIndex()) 1485 .append(", ") 1486 .append(typeReference.getTypeParameterBoundIndex()); 1487 break; 1488 case TypeReference.FIELD: 1489 stringBuilder.append("FIELD"); 1490 break; 1491 case TypeReference.METHOD_RETURN: 1492 stringBuilder.append("METHOD_RETURN"); 1493 break; 1494 case TypeReference.METHOD_RECEIVER: 1495 stringBuilder.append("METHOD_RECEIVER"); 1496 break; 1497 case TypeReference.METHOD_FORMAL_PARAMETER: 1498 stringBuilder 1499 .append("METHOD_FORMAL_PARAMETER ") 1500 .append(typeReference.getFormalParameterIndex()); 1501 break; 1502 case TypeReference.THROWS: 1503 stringBuilder.append("THROWS ").append(typeReference.getExceptionIndex()); 1504 break; 1505 case TypeReference.LOCAL_VARIABLE: 1506 stringBuilder.append("LOCAL_VARIABLE"); 1507 break; 1508 case TypeReference.RESOURCE_VARIABLE: 1509 stringBuilder.append("RESOURCE_VARIABLE"); 1510 break; 1511 case TypeReference.EXCEPTION_PARAMETER: 1512 stringBuilder.append("EXCEPTION_PARAMETER ").append(typeReference.getTryCatchBlockIndex()); 1513 break; 1514 case TypeReference.INSTANCEOF: 1515 stringBuilder.append("INSTANCEOF"); 1516 break; 1517 case TypeReference.NEW: 1518 stringBuilder.append("NEW"); 1519 break; 1520 case TypeReference.CONSTRUCTOR_REFERENCE: 1521 stringBuilder.append("CONSTRUCTOR_REFERENCE"); 1522 break; 1523 case TypeReference.METHOD_REFERENCE: 1524 stringBuilder.append("METHOD_REFERENCE"); 1525 break; 1526 case TypeReference.CAST: 1527 stringBuilder.append("CAST ").append(typeReference.getTypeArgumentIndex()); 1528 break; 1529 case TypeReference.CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT: 1530 stringBuilder 1531 .append("CONSTRUCTOR_INVOCATION_TYPE_ARGUMENT ") 1532 .append(typeReference.getTypeArgumentIndex()); 1533 break; 1534 case TypeReference.METHOD_INVOCATION_TYPE_ARGUMENT: 1535 stringBuilder 1536 .append("METHOD_INVOCATION_TYPE_ARGUMENT ") 1537 .append(typeReference.getTypeArgumentIndex()); 1538 break; 1539 case TypeReference.CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT: 1540 stringBuilder 1541 .append("CONSTRUCTOR_REFERENCE_TYPE_ARGUMENT ") 1542 .append(typeReference.getTypeArgumentIndex()); 1543 break; 1544 case TypeReference.METHOD_REFERENCE_TYPE_ARGUMENT: 1545 stringBuilder 1546 .append("METHOD_REFERENCE_TYPE_ARGUMENT ") 1547 .append(typeReference.getTypeArgumentIndex()); 1548 break; 1549 default: 1550 throw new IllegalArgumentException(); 1551 } 1552 } 1553 1554 /** 1555 * Appends the given stack map frame types to {@link #stringBuilder}. 1556 * 1557 * @param numTypes the number of stack map frame types in 'frameTypes'. 1558 * @param frameTypes an array of stack map frame types, in the format described in {@link 1559 * org.objectweb.asm.MethodVisitor#visitFrame}. 1560 */ appendFrameTypes(final int numTypes, final Object[] frameTypes)1561 private void appendFrameTypes(final int numTypes, final Object[] frameTypes) { 1562 for (int i = 0; i < numTypes; ++i) { 1563 if (i > 0) { 1564 stringBuilder.append(' '); 1565 } 1566 if (frameTypes[i] instanceof String) { 1567 String descriptor = (String) frameTypes[i]; 1568 if (descriptor.charAt(0) == '[') { 1569 appendDescriptor(FIELD_DESCRIPTOR, descriptor); 1570 } else { 1571 appendDescriptor(INTERNAL_NAME, descriptor); 1572 } 1573 } else if (frameTypes[i] instanceof Integer) { 1574 stringBuilder.append(FRAME_TYPES.get(((Integer) frameTypes[i]).intValue())); 1575 } else { 1576 appendLabel((Label) frameTypes[i]); 1577 } 1578 } 1579 } 1580 1581 /** 1582 * Creates and adds to {@link #text} a new {@link Textifier}, followed by the given string. 1583 * 1584 * @param endText the text to add to {@link #text} after the textifier. May be {@literal null}. 1585 * @return the newly created {@link Textifier}. 1586 */ addNewTextifier(final String endText)1587 private Textifier addNewTextifier(final String endText) { 1588 Textifier textifier = createTextifier(); 1589 text.add(textifier.getText()); 1590 if (endText != null) { 1591 text.add(endText); 1592 } 1593 return textifier; 1594 } 1595 1596 /** 1597 * Creates a new {@link Textifier}. 1598 * 1599 * @return a new {@link Textifier}. 1600 */ createTextifier()1601 protected Textifier createTextifier() { 1602 return new Textifier(api); 1603 } 1604 } 1605