1 /*** 2 * ASM: a very small and fast Java bytecode manipulation framework 3 * Copyright (c) 2000-2005 INRIA, France Telecom 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 3. Neither the name of the copyright holders nor the names of its 15 * contributors may be used to endorse or promote products derived from 16 * this software without specific prior written permission. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28 * THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 package org.objectweb.asm.util; 31 32 import java.io.FileInputStream; 33 import java.io.PrintWriter; 34 35 import org.objectweb.asm.AnnotationVisitor; 36 import org.objectweb.asm.ClassReader; 37 import org.objectweb.asm.ClassVisitor; 38 import org.objectweb.asm.TypeAnnotationVisitor; 39 import org.objectweb.asm.FieldVisitor; 40 import org.objectweb.asm.MethodVisitor; 41 import org.objectweb.asm.Opcodes; 42 import org.objectweb.asm.Type; 43 44 /** 45 * A {@link ClassVisitor} that prints the ASM code that generates the classes it 46 * visits. This class visitor can be used to quickly write ASM code to generate 47 * some given bytecode: <ul> <li>write the Java source code equivalent to the 48 * bytecode you want to generate;</li> <li>compile it with <tt>javac</tt>;</li> 49 * <li>make a {@link ASMifierClassVisitor} visit this compiled class (see the 50 * {@link #main main} method);</li> <li>edit the generated source code, if 51 * necessary.</li> </ul> The source code printed when visiting the 52 * <tt>Hello</tt> class is the following: <p> <blockquote> 53 * 54 * <pre> 55 * import org.objectweb.asm.*; 56 * 57 * public class HelloDump implements Opcodes { 58 * 59 * public static byte[] dump() throws Exception { 60 * 61 * ClassWriter cw = new ClassWriter(false); 62 * FieldVisitor fv; 63 * MethodVisitor mv; 64 * AnnotationVisitor av0; 65 * 66 * cw.visit(49, 67 * ACC_PUBLIC + ACC_SUPER, 68 * "Hello", 69 * null, 70 * "java/lang/Object", 71 * null); 72 * 73 * cw.visitSource("Hello.java", null); 74 * 75 * { 76 * mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 77 * mv.visitVarInsn(ALOAD, 0); 78 * mv.visitMethodInsn(INVOKESPECIAL, 79 * "java/lang/Object", 80 * "<init>", 81 * "()V"); 82 * mv.visitInsn(RETURN); 83 * mv.visitMaxs(1, 1); 84 * mv.visitEnd(); 85 * } 86 * { 87 * mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, 88 * "main", 89 * "([Ljava/lang/String;)V", 90 * null, 91 * null); 92 * mv.visitFieldInsn(GETSTATIC, 93 * "java/lang/System", 94 * "out", 95 * "Ljava/io/PrintStream;"); 96 * mv.visitLdcInsn("hello"); 97 * mv.visitMethodInsn(INVOKEVIRTUAL, 98 * "java/io/PrintStream", 99 * "println", 100 * "(Ljava/lang/String;)V"); 101 * mv.visitInsn(RETURN); 102 * mv.visitMaxs(2, 1); 103 * mv.visitEnd(); 104 * } 105 * cw.visitEnd(); 106 * 107 * return cw.toByteArray(); 108 * } 109 * } 110 * 111 * </pre> 112 * 113 * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote> 114 * 115 * <pre> 116 * public class Hello { 117 * 118 * public static void main(String[] args) { 119 * System.out.println("hello"); 120 * } 121 * } 122 * </pre> 123 * 124 * </blockquote> 125 * 126 * @author Eric Bruneton 127 * @author Eugene Kuleshov 128 */ 129 public class ASMifierClassVisitor extends ASMifierAbstractVisitor implements 130 ClassVisitor 131 { 132 /** 133 * Pseudo access flag used to distinguish class access flags. 134 */ 135 private final static int ACCESS_CLASS = 262144; 136 137 /** 138 * Pseudo access flag used to distinguish field access flags. 139 */ 140 private final static int ACCESS_FIELD = 524288; 141 142 /** 143 * Pseudo access flag used to distinguish inner class flags. 144 */ 145 private static final int ACCESS_INNER = 1048576; 146 147 /** 148 * The print writer to be used to print the class. 149 */ 150 protected final PrintWriter pw; 151 152 /** 153 * Prints the ASM source code to generate the given class to the standard 154 * output. <p> Usage: ASMifierClassVisitor [-debug] <fully qualified 155 * class name or class file name> 156 * 157 * @param args the command line arguments. 158 * 159 * @throws Exception if the class cannot be found, or if an IO exception 160 * occurs. 161 */ main(final String[] args)162 public static void main(final String[] args) throws Exception { 163 int i = 0; 164 boolean skipDebug = true; 165 166 boolean ok = true; 167 if (args.length < 1 || args.length > 2) { 168 ok = false; 169 } 170 if (ok && args[0].equals("-debug")) { 171 i = 1; 172 skipDebug = false; 173 if (args.length != 2) { 174 ok = false; 175 } 176 } 177 if (!ok) { 178 System.err.println("Prints the ASM code to generate the given class."); 179 System.err.println("Usage: ASMifierClassVisitor [-debug] " 180 + "<fully qualified class name or class file name>"); 181 return; 182 } 183 ClassReader cr; 184 if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1 185 || args[i].indexOf('/') > -1) { 186 cr = new ClassReader(new FileInputStream(args[i])); 187 } else { 188 cr = new ClassReader(args[i]); 189 } 190 cr.accept(new ASMifierClassVisitor(new PrintWriter(System.out)), 191 getDefaultAttributes(), 192 skipDebug); 193 } 194 195 /** 196 * Constructs a new {@link ASMifierClassVisitor} object. 197 * 198 * @param pw the print writer to be used to print the class. 199 */ ASMifierClassVisitor(final PrintWriter pw)200 public ASMifierClassVisitor(final PrintWriter pw) { 201 super("cw"); 202 this.pw = pw; 203 } 204 205 // ------------------------------------------------------------------------ 206 // Implementation of the ClassVisitor interface 207 // ------------------------------------------------------------------------ 208 visit( final int version, final int access, final String name, final String signature, final String superName, final String[] interfaces)209 public void visit( 210 final int version, 211 final int access, 212 final String name, 213 final String signature, 214 final String superName, 215 final String[] interfaces) 216 { 217 String simpleName; 218 int n = name.lastIndexOf('/'); 219 if (n != -1) { 220 text.add("package asm." + name.substring(0, n).replace('/', '.') 221 + ";\n"); 222 simpleName = name.substring(n + 1); 223 } else { 224 simpleName = name; 225 } 226 text.add("import java.util.*;\n"); 227 text.add("import org.objectweb.asm.*;\n"); 228 text.add("import org.objectweb.asm.attrs.*;\n"); 229 text.add("public class " + simpleName + "Dump implements Opcodes {\n\n"); 230 text.add("public static byte[] dump () throws Exception {\n\n"); 231 text.add("ClassWriter cw = new ClassWriter(false);\n"); 232 text.add("FieldVisitor fv;\n"); 233 text.add("MethodVisitor mv;\n"); 234 text.add("AnnotationVisitor av0;\n"); 235 text.add("TypeAnnotationVisitor xav0;\n\n"); 236 237 buf.setLength(0); 238 buf.append("cw.visit("); 239 switch (version) { 240 case Opcodes.V1_1: 241 buf.append("V1_1"); 242 break; 243 case Opcodes.V1_2: 244 buf.append("V1_2"); 245 break; 246 case Opcodes.V1_3: 247 buf.append("V1_3"); 248 break; 249 case Opcodes.V1_4: 250 buf.append("V1_4"); 251 break; 252 case Opcodes.V1_5: 253 buf.append("V1_5"); 254 break; 255 case Opcodes.V1_6: 256 buf.append("V1_6"); 257 break; 258 default: 259 buf.append(version); 260 break; 261 } 262 buf.append(", "); 263 appendAccess(access | ACCESS_CLASS); 264 buf.append(", "); 265 appendConstant(name); 266 buf.append(", "); 267 appendConstant(signature); 268 buf.append(", "); 269 appendConstant(superName); 270 buf.append(", "); 271 if (interfaces != null && interfaces.length > 0) { 272 buf.append("new String[] {"); 273 for (int i = 0; i < interfaces.length; ++i) { 274 buf.append(i == 0 ? " " : ", "); 275 appendConstant(interfaces[i]); 276 } 277 buf.append(" }"); 278 } else { 279 buf.append("null"); 280 } 281 buf.append(");\n\n"); 282 text.add(buf.toString()); 283 } 284 visitSource(final String file, final String debug)285 public void visitSource(final String file, final String debug) { 286 buf.setLength(0); 287 buf.append("cw.visitSource("); 288 appendConstant(file); 289 buf.append(", "); 290 appendConstant(debug); 291 buf.append(");\n\n"); 292 text.add(buf.toString()); 293 } 294 visitOuterClass( final String owner, final String name, final String desc)295 public void visitOuterClass( 296 final String owner, 297 final String name, 298 final String desc) 299 { 300 buf.setLength(0); 301 buf.append("cw.visitOuterClass("); 302 appendConstant(owner); 303 buf.append(", "); 304 appendConstant(name); 305 buf.append(", "); 306 appendConstant(desc); 307 buf.append(");\n\n"); 308 text.add(buf.toString()); 309 } 310 visitInnerClass( final String name, final String outerName, final String innerName, final int access)311 public void visitInnerClass( 312 final String name, 313 final String outerName, 314 final String innerName, 315 final int access) 316 { 317 buf.setLength(0); 318 buf.append("cw.visitInnerClass("); 319 appendConstant(name); 320 buf.append(", "); 321 appendConstant(outerName); 322 buf.append(", "); 323 appendConstant(innerName); 324 buf.append(", "); 325 appendAccess(access | ACCESS_INNER); 326 buf.append(");\n\n"); 327 text.add(buf.toString()); 328 } 329 visitField( final int access, final String name, final String desc, final String signature, final Object value)330 public FieldVisitor visitField( 331 final int access, 332 final String name, 333 final String desc, 334 final String signature, 335 final Object value) 336 { 337 buf.setLength(0); 338 buf.append("{\n"); 339 buf.append("fv = cw.visitField("); 340 appendAccess(access | ACCESS_FIELD); 341 buf.append(", "); 342 appendConstant(name); 343 buf.append(", "); 344 appendConstant(desc); 345 buf.append(", "); 346 appendConstant(signature); 347 buf.append(", "); 348 appendConstant(value); 349 buf.append(");\n"); 350 text.add(buf.toString()); 351 ASMifierFieldVisitor aav = new ASMifierFieldVisitor(); 352 text.add(aav.getText()); 353 text.add("}\n"); 354 return aav; 355 } 356 visitMethod( final int access, final String name, final String desc, final String signature, final String[] exceptions)357 public MethodVisitor visitMethod( 358 final int access, 359 final String name, 360 final String desc, 361 final String signature, 362 final String[] exceptions) 363 { 364 buf.setLength(0); 365 buf.append("{\n"); 366 buf.append("mv = cw.visitMethod("); 367 appendAccess(access); 368 buf.append(", "); 369 appendConstant(name); 370 buf.append(", "); 371 appendConstant(desc); 372 buf.append(", "); 373 appendConstant(signature); 374 buf.append(", "); 375 if (exceptions != null && exceptions.length > 0) { 376 buf.append("new String[] {"); 377 for (int i = 0; i < exceptions.length; ++i) { 378 buf.append(i == 0 ? " " : ", "); 379 appendConstant(exceptions[i]); 380 } 381 buf.append(" }"); 382 } else { 383 buf.append("null"); 384 } 385 buf.append(");\n"); 386 text.add(buf.toString()); 387 ASMifierMethodVisitor acv = new ASMifierMethodVisitor(); 388 text.add(acv.getText()); 389 text.add("}\n"); 390 return acv; 391 } 392 visitAnnotation( final String desc, final boolean visible)393 public AnnotationVisitor visitAnnotation( 394 final String desc, 395 final boolean visible) 396 { 397 buf.setLength(0); 398 buf.append("{\n"); 399 buf.append("av0 = cw.visitAnnotation("); 400 appendConstant(desc); 401 buf.append(", "); 402 buf.append(visible); 403 buf.append(");\n"); 404 text.add(buf.toString()); 405 ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0); 406 text.add(av.getText()); 407 text.add("}\n"); 408 return av; 409 } 410 visitTypeAnnotation( final String desc, final boolean visible, final boolean inCode)411 public TypeAnnotationVisitor visitTypeAnnotation( 412 final String desc, 413 final boolean visible, 414 final boolean inCode) 415 { 416 buf.setLength(0); 417 buf.append("{\n"); 418 buf.append("xav0 = cw.visitTypeAnnotation("); 419 appendConstant(desc); 420 buf.append(", "); 421 buf.append(visible); 422 buf.append(", "); 423 buf.append(inCode); 424 buf.append(");\n"); 425 text.add(buf.toString()); 426 ASMifierTypeAnnotationVisitor xav = 427 new ASMifierTypeAnnotationVisitor(0); 428 text.add(xav.getText()); 429 text.add("}\n"); 430 return xav; 431 } 432 visitEnd()433 public void visitEnd() { 434 text.add("cw.visitEnd();\n\n"); 435 text.add("return cw.toByteArray();\n"); 436 text.add("}\n"); 437 text.add("}\n"); 438 printList(pw, text); 439 pw.flush(); 440 } 441 442 // ------------------------------------------------------------------------ 443 // Utility methods 444 // ------------------------------------------------------------------------ 445 446 /** 447 * Appends a string representation of the given access modifiers to {@link 448 * #buf buf}. 449 * 450 * @param access some access modifiers. 451 */ appendAccess(final int access)452 void appendAccess(final int access) { 453 boolean first = true; 454 if ((access & Opcodes.ACC_PUBLIC) != 0) { 455 buf.append("ACC_PUBLIC"); 456 first = false; 457 } 458 if ((access & Opcodes.ACC_PRIVATE) != 0) { 459 if (!first) { 460 buf.append(" + "); 461 } 462 buf.append("ACC_PRIVATE"); 463 first = false; 464 } 465 if ((access & Opcodes.ACC_PROTECTED) != 0) { 466 if (!first) { 467 buf.append(" + "); 468 } 469 buf.append("ACC_PROTECTED"); 470 first = false; 471 } 472 if ((access & Opcodes.ACC_FINAL) != 0) { 473 if (!first) { 474 buf.append(" + "); 475 } 476 buf.append("ACC_FINAL"); 477 first = false; 478 } 479 if ((access & Opcodes.ACC_STATIC) != 0) { 480 if (!first) { 481 buf.append(" + "); 482 } 483 buf.append("ACC_STATIC"); 484 first = false; 485 } 486 if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) { 487 if (!first) { 488 buf.append(" + "); 489 } 490 if ((access & ACCESS_CLASS) != 0) { 491 buf.append("ACC_SUPER"); 492 } else { 493 buf.append("ACC_SYNCHRONIZED"); 494 } 495 first = false; 496 } 497 if ((access & Opcodes.ACC_VOLATILE) != 0 498 && (access & ACCESS_FIELD) != 0) 499 { 500 if (!first) { 501 buf.append(" + "); 502 } 503 buf.append("ACC_VOLATILE"); 504 first = false; 505 } 506 if ((access & Opcodes.ACC_BRIDGE) != 0 && (access & ACCESS_CLASS) == 0 507 && (access & ACCESS_FIELD) == 0) 508 { 509 if (!first) { 510 buf.append(" + "); 511 } 512 buf.append("ACC_BRIDGE"); 513 first = false; 514 } 515 if ((access & Opcodes.ACC_VARARGS) != 0 && (access & ACCESS_CLASS) == 0 516 && (access & ACCESS_FIELD) == 0) 517 { 518 if (!first) { 519 buf.append(" + "); 520 } 521 buf.append("ACC_VARARGS"); 522 first = false; 523 } 524 if ((access & Opcodes.ACC_TRANSIENT) != 0 525 && (access & ACCESS_FIELD) != 0) 526 { 527 if (!first) { 528 buf.append(" + "); 529 } 530 buf.append("ACC_TRANSIENT"); 531 first = false; 532 } 533 if ((access & Opcodes.ACC_NATIVE) != 0 && (access & ACCESS_CLASS) == 0 534 && (access & ACCESS_FIELD) == 0) 535 { 536 if (!first) { 537 buf.append(" + "); 538 } 539 buf.append("ACC_NATIVE"); 540 first = false; 541 } 542 if ((access & Opcodes.ACC_ENUM) != 0 543 && ((access & ACCESS_CLASS) != 0 544 || (access & ACCESS_FIELD) != 0 || (access & ACCESS_INNER) != 0)) 545 { 546 if (!first) { 547 buf.append(" + "); 548 } 549 buf.append("ACC_ENUM"); 550 first = false; 551 } 552 if ((access & Opcodes.ACC_ANNOTATION) != 0 553 && ((access & ACCESS_CLASS) != 0)) 554 { 555 if (!first) { 556 buf.append(" + "); 557 } 558 buf.append("ACC_ANNOTATION"); 559 first = false; 560 } 561 if ((access & Opcodes.ACC_ABSTRACT) != 0) { 562 if (!first) { 563 buf.append(" + "); 564 } 565 buf.append("ACC_ABSTRACT"); 566 first = false; 567 } 568 if ((access & Opcodes.ACC_INTERFACE) != 0) { 569 if (!first) { 570 buf.append(" + "); 571 } 572 buf.append("ACC_INTERFACE"); 573 first = false; 574 } 575 if ((access & Opcodes.ACC_STRICT) != 0) { 576 if (!first) { 577 buf.append(" + "); 578 } 579 buf.append("ACC_STRICT"); 580 first = false; 581 } 582 if ((access & Opcodes.ACC_SYNTHETIC) != 0) { 583 if (!first) { 584 buf.append(" + "); 585 } 586 buf.append("ACC_SYNTHETIC"); 587 first = false; 588 } 589 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 590 if (!first) { 591 buf.append(" + "); 592 } 593 buf.append("ACC_DEPRECATED"); 594 first = false; 595 } 596 if (first) { 597 buf.append("0"); 598 } 599 } 600 601 /** 602 * Appends a string representation of the given constant to the given 603 * buffer. 604 * 605 * @param buf a string buffer. 606 * @param cst an {@link java.lang.Integer Integer}, {@link java.lang.Float 607 * Float}, {@link java.lang.Long Long}, 608 * {@link java.lang.Double Double} or {@link String String} object. 609 * May be <tt>null</tt>. 610 */ appendConstant(final StringBuffer buf, final Object cst)611 static void appendConstant(final StringBuffer buf, final Object cst) { 612 if (cst == null) { 613 buf.append("null"); 614 } else if (cst instanceof String) { 615 AbstractVisitor.appendString(buf, (String) cst); 616 } else if (cst instanceof Type) { 617 buf.append("Type.getType(\"") 618 .append(((Type) cst).getDescriptor()) 619 .append("\")"); 620 } else if (cst instanceof Integer) { 621 buf.append("new Integer(").append(cst).append(")"); 622 } else if (cst instanceof Float) { 623 buf.append("new Float(\"").append(cst).append("\")"); 624 } else if (cst instanceof Long) { 625 buf.append("new Long(").append(cst).append("L)"); 626 } else if (cst instanceof Double) { 627 buf.append("new Double(\"").append(cst).append("\")"); 628 } 629 } 630 } 631