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 */ 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 */ 200 public ASMifierClassVisitor(final PrintWriter pw) { 201 super("cw"); 202 this.pw = pw; 203 } 204 205 // ------------------------------------------------------------------------ 206 // Implementation of the ClassVisitor interface 207 // ------------------------------------------------------------------------ 208 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 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 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 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 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 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 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 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 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 */ 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 */ 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