165 * The label names. This map associate String values to Label keys.
166 */
167 protected Map<Label, String> labelNames;
168
169 /**
170 * Class access flags
171 */
172 private int access;
173
174 private int valueNumber = 0;
175
176 /**
177 * Constructs a new {@link Textifier}. <i>Subclasses must not use this
178 * constructor</i>. Instead, they must use the {@link #Textifier(int)}
179 * version.
180 *
181 * @throws IllegalStateException
182 * If a subclass calls this constructor.
183 */
184 public Textifier() {
185 this(Opcodes.ASM5);
186 if (getClass() != Textifier.class) {
187 throw new IllegalStateException();
188 }
189 }
190
191 /**
192 * Constructs a new {@link Textifier}.
193 *
194 * @param api
195 * the ASM API version implemented by this visitor. Must be one
196 * of {@link Opcodes#ASM4} or {@link Opcodes#ASM5}.
197 */
198 protected Textifier(final int api) {
199 super(api);
200 }
201
202 /**
203 * Prints a disassembled view of the given class to the standard output.
204 * <p>
205 * Usage: Textifier [-debug] <binary class name or class file name >
206 *
207 * @param args
208 * the command line arguments.
209 *
210 * @throws Exception
211 * if the class cannot be found, or if an IO exception occurs.
212 */
213 public static void main(final String[] args) throws Exception {
214 int i = 0;
215 int flags = ClassReader.SKIP_DEBUG;
216
233 return;
234 }
235 ClassReader cr;
236 if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
237 || args[i].indexOf('/') > -1) {
238 cr = new ClassReader(new FileInputStream(args[i]));
239 } else {
240 cr = new ClassReader(args[i]);
241 }
242 cr.accept(new TraceClassVisitor(new PrintWriter(System.out)), flags);
243 }
244
245 // ------------------------------------------------------------------------
246 // Classes
247 // ------------------------------------------------------------------------
248
249 @Override
250 public void visit(final int version, final int access, final String name,
251 final String signature, final String superName,
252 final String[] interfaces) {
253 this.access = access;
254 int major = version & 0xFFFF;
255 int minor = version >>> 16;
256 buf.setLength(0);
257 buf.append("// class version ").append(major).append('.').append(minor)
258 .append(" (").append(version).append(")\n");
259 if ((access & Opcodes.ACC_DEPRECATED) != 0) {
260 buf.append("// DEPRECATED\n");
261 }
262 buf.append("// access flags 0x")
263 .append(Integer.toHexString(access).toUpperCase()).append('\n');
264
265 appendDescriptor(CLASS_SIGNATURE, signature);
266 if (signature != null) {
267 TraceSignatureVisitor sv = new TraceSignatureVisitor(access);
268 SignatureReader r = new SignatureReader(signature);
269 r.accept(sv);
270 buf.append("// declaration: ").append(name)
271 .append(sv.getDeclaration()).append('\n');
272 }
273
274 appendAccess(access & ~Opcodes.ACC_SUPER);
275 if ((access & Opcodes.ACC_ANNOTATION) != 0) {
276 buf.append("@interface ");
277 } else if ((access & Opcodes.ACC_INTERFACE) != 0) {
278 buf.append("interface ");
279 } else if ((access & Opcodes.ACC_ENUM) == 0) {
280 buf.append("class ");
281 }
282 appendDescriptor(INTERNAL_NAME, name);
283
284 if (superName != null && !"java/lang/Object".equals(superName)) {
285 buf.append(" extends ");
286 appendDescriptor(INTERNAL_NAME, superName);
287 buf.append(' ');
288 }
289 if (interfaces != null && interfaces.length > 0) {
290 buf.append(" implements ");
291 for (int i = 0; i < interfaces.length; ++i) {
292 appendDescriptor(INTERNAL_NAME, interfaces[i]);
293 buf.append(' ');
294 }
298 text.add(buf.toString());
299 }
300
301 @Override
302 public void visitSource(final String file, final String debug) {
303 buf.setLength(0);
304 if (file != null) {
305 buf.append(tab).append("// compiled from: ").append(file)
306 .append('\n');
307 }
308 if (debug != null) {
309 buf.append(tab).append("// debug info: ").append(debug)
310 .append('\n');
311 }
312 if (buf.length() > 0) {
313 text.add(buf.toString());
314 }
315 }
316
317 @Override
318 public void visitOuterClass(final String owner, final String name,
319 final String desc) {
320 buf.setLength(0);
321 buf.append(tab).append("OUTERCLASS ");
322 appendDescriptor(INTERNAL_NAME, owner);
323 buf.append(' ');
324 if (name != null) {
325 buf.append(name).append(' ');
326 }
327 appendDescriptor(METHOD_DESCRIPTOR, desc);
328 buf.append('\n');
329 text.add(buf.toString());
330 }
331
332 @Override
333 public Textifier visitClassAnnotation(final String desc,
334 final boolean visible) {
335 text.add("\n");
336 return visitAnnotation(desc, visible);
337 }
426 if (signature != null) {
427 buf.append(tab);
428 appendDescriptor(METHOD_SIGNATURE, signature);
429
430 TraceSignatureVisitor v = new TraceSignatureVisitor(0);
431 SignatureReader r = new SignatureReader(signature);
432 r.accept(v);
433 String genericDecl = v.getDeclaration();
434 String genericReturn = v.getReturnType();
435 String genericExceptions = v.getExceptions();
436
437 buf.append(tab).append("// declaration: ").append(genericReturn)
438 .append(' ').append(name).append(genericDecl);
439 if (genericExceptions != null) {
440 buf.append(" throws ").append(genericExceptions);
441 }
442 buf.append('\n');
443 }
444
445 buf.append(tab);
446 appendAccess(access & ~Opcodes.ACC_VOLATILE);
447 if ((access & Opcodes.ACC_NATIVE) != 0) {
448 buf.append("native ");
449 }
450 if ((access & Opcodes.ACC_VARARGS) != 0) {
451 buf.append("varargs ");
452 }
453 if ((access & Opcodes.ACC_BRIDGE) != 0) {
454 buf.append("bridge ");
455 }
456 if ((this.access & Opcodes.ACC_INTERFACE) != 0
457 && (access & Opcodes.ACC_ABSTRACT) == 0
458 && (access & Opcodes.ACC_STATIC) == 0) {
459 buf.append("default ");
460 }
461
462 buf.append(name);
463 appendDescriptor(METHOD_DESCRIPTOR, desc);
464 if (exceptions != null && exceptions.length > 0) {
465 buf.append(" throws ");
466 for (int i = 0; i < exceptions.length; ++i) {
467 appendDescriptor(INTERNAL_NAME, exceptions[i]);
468 buf.append(' ');
469 }
470 }
471
472 buf.append('\n');
473 text.add(buf.toString());
474
475 Textifier t = createTextifier();
476 text.add(t.getText());
477 return t;
478 }
479
480 @Override
481 public void visitClassEnd() {
482 text.add("}\n");
483 }
484
485 // ------------------------------------------------------------------------
486 // Annotations
487 // ------------------------------------------------------------------------
488
489 @Override
490 public void visit(final String name, final Object value) {
491 buf.setLength(0);
492 appendComa(valueNumber++);
493
494 if (name != null) {
495 buf.append(name).append('=');
496 }
497
498 if (value instanceof String) {
499 visitString((String) value);
500 } else if (value instanceof Type) {
501 visitType((Type) value);
502 } else if (value instanceof Byte) {
503 visitByte(((Byte) value).byteValue());
504 } else if (value instanceof Boolean) {
505 visitBoolean(((Boolean) value).booleanValue());
1279 isMethodHandle = true;
1280 break;
1281 case Opcodes.H_INVOKESTATIC:
1282 buf.append("INVOKESTATIC");
1283 isMethodHandle = true;
1284 break;
1285 case Opcodes.H_INVOKEVIRTUAL:
1286 buf.append("INVOKEVIRTUAL");
1287 isMethodHandle = true;
1288 break;
1289 case Opcodes.H_NEWINVOKESPECIAL:
1290 buf.append("NEWINVOKESPECIAL");
1291 isMethodHandle = true;
1292 break;
1293 }
1294 buf.append('\n');
1295 buf.append(tab3);
1296 appendDescriptor(INTERNAL_NAME, h.getOwner());
1297 buf.append('.');
1298 buf.append(h.getName());
1299 if(!isMethodHandle){
1300 buf.append('(');
1301 }
1302 appendDescriptor(HANDLE_DESCRIPTOR, h.getDesc());
1303 if(!isMethodHandle){
1304 buf.append(')');
1305 }
1306 }
1307
1308 /**
1309 * Appends a string representation of the given access modifiers to
1310 * {@link #buf buf}.
1311 *
1312 * @param access
1313 * some access modifiers.
1314 */
1315 private void appendAccess(final int access) {
1316 if ((access & Opcodes.ACC_PUBLIC) != 0) {
1317 buf.append("public ");
1318 }
1319 if ((access & Opcodes.ACC_PRIVATE) != 0) {
1320 buf.append("private ");
1321 }
1322 if ((access & Opcodes.ACC_PROTECTED) != 0) {
1323 buf.append("protected ");
1324 }
1325 if ((access & Opcodes.ACC_FINAL) != 0) {
1326 buf.append("final ");
|
165 * The label names. This map associate String values to Label keys.
166 */
167 protected Map<Label, String> labelNames;
168
169 /**
170 * Class access flags
171 */
172 private int access;
173
174 private int valueNumber = 0;
175
176 /**
177 * Constructs a new {@link Textifier}. <i>Subclasses must not use this
178 * constructor</i>. Instead, they must use the {@link #Textifier(int)}
179 * version.
180 *
181 * @throws IllegalStateException
182 * If a subclass calls this constructor.
183 */
184 public Textifier() {
185 this(Opcodes.ASM6);
186 if (getClass() != Textifier.class) {
187 throw new IllegalStateException();
188 }
189 }
190
191 /**
192 * Constructs a new {@link Textifier}.
193 *
194 * @param api
195 * the ASM API version implemented by this visitor. Must be one
196 * of {@link Opcodes#ASM4}, {@link Opcodes#ASM5} or {@link Opcodes#ASM6}.
197 */
198 protected Textifier(final int api) {
199 super(api);
200 }
201
202 /**
203 * Prints a disassembled view of the given class to the standard output.
204 * <p>
205 * Usage: Textifier [-debug] <binary class name or class file name >
206 *
207 * @param args
208 * the command line arguments.
209 *
210 * @throws Exception
211 * if the class cannot be found, or if an IO exception occurs.
212 */
213 public static void main(final String[] args) throws Exception {
214 int i = 0;
215 int flags = ClassReader.SKIP_DEBUG;
216
233 return;
234 }
235 ClassReader cr;
236 if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1
237 || args[i].indexOf('/') > -1) {
238 cr = new ClassReader(new FileInputStream(args[i]));
239 } else {
240 cr = new ClassReader(args[i]);
241 }
242 cr.accept(new TraceClassVisitor(new PrintWriter(System.out)), flags);
243 }
244
245 // ------------------------------------------------------------------------
246 // Classes
247 // ------------------------------------------------------------------------
248
249 @Override
250 public void visit(final int version, final int access, final String name,
251 final String signature, final String superName,
252 final String[] interfaces) {
253 if ((access & Opcodes.ACC_MODULE) != 0) {
254 // visitModule will print the module
255 return;
256 }
257 this.access = access;
258 int major = version & 0xFFFF;
259 int minor = version >>> 16;
260 buf.setLength(0);
261 buf.append("// class version ").append(major).append('.').append(minor)
262 .append(" (").append(version).append(")\n");
263 if ((access & Opcodes.ACC_DEPRECATED) != 0) {
264 buf.append("// DEPRECATED\n");
265 }
266 buf.append("// access flags 0x")
267 .append(Integer.toHexString(access).toUpperCase()).append('\n');
268
269 appendDescriptor(CLASS_SIGNATURE, signature);
270 if (signature != null) {
271 TraceSignatureVisitor sv = new TraceSignatureVisitor(access);
272 SignatureReader r = new SignatureReader(signature);
273 r.accept(sv);
274 buf.append("// declaration: ").append(name)
275 .append(sv.getDeclaration()).append('\n');
276 }
277
278 appendAccess(access & ~(Opcodes.ACC_SUPER | Opcodes.ACC_MODULE));
279 if ((access & Opcodes.ACC_ANNOTATION) != 0) {
280 buf.append("@interface ");
281 } else if ((access & Opcodes.ACC_INTERFACE) != 0) {
282 buf.append("interface ");
283 } else if ((access & Opcodes.ACC_ENUM) == 0) {
284 buf.append("class ");
285 }
286 appendDescriptor(INTERNAL_NAME, name);
287
288 if (superName != null && !"java/lang/Object".equals(superName)) {
289 buf.append(" extends ");
290 appendDescriptor(INTERNAL_NAME, superName);
291 buf.append(' ');
292 }
293 if (interfaces != null && interfaces.length > 0) {
294 buf.append(" implements ");
295 for (int i = 0; i < interfaces.length; ++i) {
296 appendDescriptor(INTERNAL_NAME, interfaces[i]);
297 buf.append(' ');
298 }
302 text.add(buf.toString());
303 }
304
305 @Override
306 public void visitSource(final String file, final String debug) {
307 buf.setLength(0);
308 if (file != null) {
309 buf.append(tab).append("// compiled from: ").append(file)
310 .append('\n');
311 }
312 if (debug != null) {
313 buf.append(tab).append("// debug info: ").append(debug)
314 .append('\n');
315 }
316 if (buf.length() > 0) {
317 text.add(buf.toString());
318 }
319 }
320
321 @Override
322 public Printer visitModule(final String name, final int access,
323 final String version) {
324 buf.setLength(0);
325 if ((access & Opcodes.ACC_OPEN) != 0) {
326 buf.append("open ");
327 }
328 buf.append("module ")
329 .append(name)
330 .append(" { ")
331 .append(version == null ? "" : "// " + version)
332 .append("\n\n");
333 text.add(buf.toString());
334 Textifier t = createTextifier();
335 text.add(t.getText());
336 return t;
337 }
338
339 @Override
340 public void visitOuterClass(final String owner, final String name,
341 final String desc) {
342 buf.setLength(0);
343 buf.append(tab).append("OUTERCLASS ");
344 appendDescriptor(INTERNAL_NAME, owner);
345 buf.append(' ');
346 if (name != null) {
347 buf.append(name).append(' ');
348 }
349 appendDescriptor(METHOD_DESCRIPTOR, desc);
350 buf.append('\n');
351 text.add(buf.toString());
352 }
353
354 @Override
355 public Textifier visitClassAnnotation(final String desc,
356 final boolean visible) {
357 text.add("\n");
358 return visitAnnotation(desc, visible);
359 }
448 if (signature != null) {
449 buf.append(tab);
450 appendDescriptor(METHOD_SIGNATURE, signature);
451
452 TraceSignatureVisitor v = new TraceSignatureVisitor(0);
453 SignatureReader r = new SignatureReader(signature);
454 r.accept(v);
455 String genericDecl = v.getDeclaration();
456 String genericReturn = v.getReturnType();
457 String genericExceptions = v.getExceptions();
458
459 buf.append(tab).append("// declaration: ").append(genericReturn)
460 .append(' ').append(name).append(genericDecl);
461 if (genericExceptions != null) {
462 buf.append(" throws ").append(genericExceptions);
463 }
464 buf.append('\n');
465 }
466
467 buf.append(tab);
468 appendAccess(access & ~(Opcodes.ACC_VOLATILE | Opcodes.ACC_TRANSIENT));
469 if ((access & Opcodes.ACC_NATIVE) != 0) {
470 buf.append("native ");
471 }
472 if ((access & Opcodes.ACC_VARARGS) != 0) {
473 buf.append("varargs ");
474 }
475 if ((access & Opcodes.ACC_BRIDGE) != 0) {
476 buf.append("bridge ");
477 }
478 if ((this.access & Opcodes.ACC_INTERFACE) != 0
479 && (access & Opcodes.ACC_ABSTRACT) == 0
480 && (access & Opcodes.ACC_STATIC) == 0) {
481 buf.append("default ");
482 }
483
484 buf.append(name);
485 appendDescriptor(METHOD_DESCRIPTOR, desc);
486 if (exceptions != null && exceptions.length > 0) {
487 buf.append(" throws ");
488 for (int i = 0; i < exceptions.length; ++i) {
489 appendDescriptor(INTERNAL_NAME, exceptions[i]);
490 buf.append(' ');
491 }
492 }
493
494 buf.append('\n');
495 text.add(buf.toString());
496
497 Textifier t = createTextifier();
498 text.add(t.getText());
499 return t;
500 }
501
502 @Override
503 public void visitClassEnd() {
504 text.add("}\n");
505 }
506
507 // ------------------------------------------------------------------------
508 // Module
509 // ------------------------------------------------------------------------
510
511 @Override
512 public void visitMainClass(String mainClass) {
513 buf.setLength(0);
514 buf.append(" // main class ").append(mainClass).append('\n');
515 text.add(buf.toString());
516 }
517
518 @Override
519 public void visitPackage(String packaze) {
520 buf.setLength(0);
521 buf.append(" // package ").append(packaze).append('\n');
522 text.add(buf.toString());
523 }
524
525 @Override
526 public void visitRequire(String require, int access, String version) {
527 buf.setLength(0);
528 buf.append(tab).append("requires ");
529 if ((access & Opcodes.ACC_TRANSITIVE) != 0) {
530 buf.append("transitive ");
531 }
532 if ((access & Opcodes.ACC_STATIC_PHASE) != 0) {
533 buf.append("static ");
534 }
535 buf.append(require)
536 .append("; // access flags 0x")
537 .append(Integer.toHexString(access).toUpperCase())
538 .append('\n');
539 if (version != null) {
540 buf.append(" // version ")
541 .append(version)
542 .append('\n');
543 }
544 text.add(buf.toString());
545 }
546
547 @Override
548 public void visitExport(String export, int access, String... modules) {
549 buf.setLength(0);
550 buf.append(tab).append("exports ");
551 buf.append(export);
552 if (modules != null && modules.length > 0) {
553 buf.append(" to");
554 } else {
555 buf.append(';');
556 }
557 buf.append(" // access flags 0x")
558 .append(Integer.toHexString(access).toUpperCase())
559 .append('\n');
560 if (modules != null && modules.length > 0) {
561 for (int i = 0; i < modules.length; ++i) {
562 buf.append(tab2).append(modules[i]);
563 buf.append(i != modules.length - 1 ? ",\n": ";\n");
564 }
565 }
566 text.add(buf.toString());
567 }
568
569 @Override
570 public void visitOpen(String export, int access, String... modules) {
571 buf.setLength(0);
572 buf.append(tab).append("opens ");
573 buf.append(export);
574 if (modules != null && modules.length > 0) {
575 buf.append(" to");
576 } else {
577 buf.append(';');
578 }
579 buf.append(" // access flags 0x")
580 .append(Integer.toHexString(access).toUpperCase())
581 .append('\n');
582 if (modules != null && modules.length > 0) {
583 for (int i = 0; i < modules.length; ++i) {
584 buf.append(tab2).append(modules[i]);
585 buf.append(i != modules.length - 1 ? ",\n": ";\n");
586 }
587 }
588 text.add(buf.toString());
589 }
590
591 @Override
592 public void visitUse(String use) {
593 buf.setLength(0);
594 buf.append(tab).append("uses ");
595 appendDescriptor(INTERNAL_NAME, use);
596 buf.append(";\n");
597 text.add(buf.toString());
598 }
599
600 @Override
601 public void visitProvide(String provide, String... providers) {
602 buf.setLength(0);
603 buf.append(tab).append("provides ");
604 appendDescriptor(INTERNAL_NAME, provide);
605 buf.append(" with\n");
606 for (int i = 0; i < providers.length; ++i) {
607 buf.append(tab2);
608 appendDescriptor(INTERNAL_NAME, providers[i]);
609 buf.append(i != providers.length - 1 ? ",\n": ";\n");
610 }
611 text.add(buf.toString());
612 }
613
614 @Override
615 public void visitModuleEnd() {
616 // empty
617 }
618
619 // ------------------------------------------------------------------------
620 // Annotations
621 // ------------------------------------------------------------------------
622
623 @Override
624 public void visit(final String name, final Object value) {
625 buf.setLength(0);
626 appendComa(valueNumber++);
627
628 if (name != null) {
629 buf.append(name).append('=');
630 }
631
632 if (value instanceof String) {
633 visitString((String) value);
634 } else if (value instanceof Type) {
635 visitType((Type) value);
636 } else if (value instanceof Byte) {
637 visitByte(((Byte) value).byteValue());
638 } else if (value instanceof Boolean) {
639 visitBoolean(((Boolean) value).booleanValue());
1413 isMethodHandle = true;
1414 break;
1415 case Opcodes.H_INVOKESTATIC:
1416 buf.append("INVOKESTATIC");
1417 isMethodHandle = true;
1418 break;
1419 case Opcodes.H_INVOKEVIRTUAL:
1420 buf.append("INVOKEVIRTUAL");
1421 isMethodHandle = true;
1422 break;
1423 case Opcodes.H_NEWINVOKESPECIAL:
1424 buf.append("NEWINVOKESPECIAL");
1425 isMethodHandle = true;
1426 break;
1427 }
1428 buf.append('\n');
1429 buf.append(tab3);
1430 appendDescriptor(INTERNAL_NAME, h.getOwner());
1431 buf.append('.');
1432 buf.append(h.getName());
1433 if (!isMethodHandle) {
1434 buf.append('(');
1435 }
1436 appendDescriptor(HANDLE_DESCRIPTOR, h.getDesc());
1437 if (!isMethodHandle) {
1438 buf.append(')');
1439 }
1440 if (h.isInterface()) {
1441 buf.append(" itf");
1442 }
1443 }
1444
1445 /**
1446 * Appends a string representation of the given access modifiers to
1447 * {@link #buf buf}.
1448 *
1449 * @param access
1450 * some access modifiers.
1451 */
1452 private void appendAccess(final int access) {
1453 if ((access & Opcodes.ACC_PUBLIC) != 0) {
1454 buf.append("public ");
1455 }
1456 if ((access & Opcodes.ACC_PRIVATE) != 0) {
1457 buf.append("private ");
1458 }
1459 if ((access & Opcodes.ACC_PROTECTED) != 0) {
1460 buf.append("protected ");
1461 }
1462 if ((access & Opcodes.ACC_FINAL) != 0) {
1463 buf.append("final ");
|