1/*** 2 * ASM: a very small and fast Java bytecode manipulation framework 3 * Copyright (c) 2000-2007 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 */ 30package org.mockito.asm.util; 31 32import java.io.FileInputStream; 33import java.io.PrintWriter; 34 35import org.mockito.asm.AnnotationVisitor; 36import org.mockito.asm.ClassReader; 37import org.mockito.asm.ClassVisitor; 38import org.mockito.asm.FieldVisitor; 39import org.mockito.asm.MethodVisitor; 40import org.mockito.asm.Opcodes; 41 42/** 43 * A {@link ClassVisitor} that prints the ASM code that generates the classes it 44 * visits. This class visitor can be used to quickly write ASM code to generate 45 * some given bytecode: <ul> <li>write the Java source code equivalent to the 46 * bytecode you want to generate;</li> <li>compile it with <tt>javac</tt>;</li> 47 * <li>make a {@link ASMifierClassVisitor} visit this compiled class (see the 48 * {@link #main main} method);</li> <li>edit the generated source code, if 49 * necessary.</li> </ul> The source code printed when visiting the 50 * <tt>Hello</tt> class is the following: <p> <blockquote> 51 * 52 * <pre> 53 * import org.mockito.asm.*; 54 * 55 * public class HelloDump implements Opcodes { 56 * 57 * public static byte[] dump() throws Exception { 58 * 59 * ClassWriter cw = new ClassWriter(0); 60 * FieldVisitor fv; 61 * MethodVisitor mv; 62 * AnnotationVisitor av0; 63 * 64 * cw.visit(49, 65 * ACC_PUBLIC + ACC_SUPER, 66 * "Hello", 67 * null, 68 * "java/lang/Object", 69 * null); 70 * 71 * cw.visitSource("Hello.java", null); 72 * 73 * { 74 * mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); 75 * mv.visitVarInsn(ALOAD, 0); 76 * mv.visitMethodInsn(INVOKESPECIAL, 77 * "java/lang/Object", 78 * "<init>", 79 * "()V"); 80 * mv.visitInsn(RETURN); 81 * mv.visitMaxs(1, 1); 82 * mv.visitEnd(); 83 * } 84 * { 85 * mv = cw.visitMethod(ACC_PUBLIC + ACC_STATIC, 86 * "main", 87 * "([Ljava/lang/String;)V", 88 * null, 89 * null); 90 * mv.visitFieldInsn(GETSTATIC, 91 * "java/lang/System", 92 * "out", 93 * "Ljava/io/PrintStream;"); 94 * mv.visitLdcInsn("hello"); 95 * mv.visitMethodInsn(INVOKEVIRTUAL, 96 * "java/io/PrintStream", 97 * "println", 98 * "(Ljava/lang/String;)V"); 99 * mv.visitInsn(RETURN); 100 * mv.visitMaxs(2, 1); 101 * mv.visitEnd(); 102 * } 103 * cw.visitEnd(); 104 * 105 * return cw.toByteArray(); 106 * } 107 * } 108 * 109 * </pre> 110 * 111 * </blockquote> where <tt>Hello</tt> is defined by: <p> <blockquote> 112 * 113 * <pre> 114 * public class Hello { 115 * 116 * public static void main(String[] args) { 117 * System.out.println("hello"); 118 * } 119 * } 120 * </pre> 121 * 122 * </blockquote> 123 * 124 * @author Eric Bruneton 125 * @author Eugene Kuleshov 126 */ 127public class ASMifierClassVisitor extends ASMifierAbstractVisitor implements 128 ClassVisitor 129{ 130 131 /** 132 * Pseudo access flag used to distinguish class access flags. 133 */ 134 private static final int ACCESS_CLASS = 262144; 135 136 /** 137 * Pseudo access flag used to distinguish field access flags. 138 */ 139 private static final int ACCESS_FIELD = 524288; 140 141 /** 142 * Pseudo access flag used to distinguish inner class flags. 143 */ 144 private static final int ACCESS_INNER = 1048576; 145 146 /** 147 * The print writer to be used to print the class. 148 */ 149 protected final PrintWriter pw; 150 151 /** 152 * Prints the ASM source code to generate the given class to the standard 153 * output. <p> Usage: ASMifierClassVisitor [-debug] <fully qualified 154 * class name or class file name> 155 * 156 * @param args the command line arguments. 157 * 158 * @throws Exception if the class cannot be found, or if an IO exception 159 * occurs. 160 */ 161 public static void main(final String[] args) throws Exception { 162 int i = 0; 163 int flags = ClassReader.SKIP_DEBUG; 164 165 boolean ok = true; 166 if (args.length < 1 || args.length > 2) { 167 ok = false; 168 } 169 if (ok && "-debug".equals(args[0])) { 170 i = 1; 171 flags = 0; 172 if (args.length != 2) { 173 ok = false; 174 } 175 } 176 if (!ok) { 177 System.err.println("Prints the ASM code to generate the given class."); 178 System.err.println("Usage: ASMifierClassVisitor [-debug] " 179 + "<fully qualified class name or class file name>"); 180 return; 181 } 182 ClassReader cr; 183 if (args[i].endsWith(".class") || args[i].indexOf('\\') > -1 184 || args[i].indexOf('/') > -1) 185 { 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 flags); 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 simpleName = name; 221 } else { 222 text.add("package asm." + name.substring(0, n).replace('/', '.') 223 + ";\n"); 224 simpleName = name.substring(n + 1); 225 } 226 text.add("import java.util.*;\n"); 227 text.add("import org.mockito.asm.*;\n"); 228 text.add("import org.mockito.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(0);\n"); 232 text.add("FieldVisitor fv;\n"); 233 text.add("MethodVisitor mv;\n"); 234 text.add("AnnotationVisitor av0;\n\n"); 235 236 buf.setLength(0); 237 buf.append("cw.visit("); 238 switch (version) { 239 case Opcodes.V1_1: 240 buf.append("V1_1"); 241 break; 242 case Opcodes.V1_2: 243 buf.append("V1_2"); 244 break; 245 case Opcodes.V1_3: 246 buf.append("V1_3"); 247 break; 248 case Opcodes.V1_4: 249 buf.append("V1_4"); 250 break; 251 case Opcodes.V1_5: 252 buf.append("V1_5"); 253 break; 254 case Opcodes.V1_6: 255 buf.append("V1_6"); 256 break; 257 default: 258 buf.append(version); 259 break; 260 } 261 buf.append(", "); 262 appendAccess(access | ACCESS_CLASS); 263 buf.append(", "); 264 appendConstant(name); 265 buf.append(", "); 266 appendConstant(signature); 267 buf.append(", "); 268 appendConstant(superName); 269 buf.append(", "); 270 if (interfaces != null && interfaces.length > 0) { 271 buf.append("new String[] {"); 272 for (int i = 0; i < interfaces.length; ++i) { 273 buf.append(i == 0 ? " " : ", "); 274 appendConstant(interfaces[i]); 275 } 276 buf.append(" }"); 277 } else { 278 buf.append("null"); 279 } 280 buf.append(");\n\n"); 281 text.add(buf.toString()); 282 } 283 284 public void visitSource(final String file, final String debug) { 285 buf.setLength(0); 286 buf.append("cw.visitSource("); 287 appendConstant(file); 288 buf.append(", "); 289 appendConstant(debug); 290 buf.append(");\n\n"); 291 text.add(buf.toString()); 292 } 293 294 public void visitOuterClass( 295 final String owner, 296 final String name, 297 final String desc) 298 { 299 buf.setLength(0); 300 buf.append("cw.visitOuterClass("); 301 appendConstant(owner); 302 buf.append(", "); 303 appendConstant(name); 304 buf.append(", "); 305 appendConstant(desc); 306 buf.append(");\n\n"); 307 text.add(buf.toString()); 308 } 309 310 public void visitInnerClass( 311 final String name, 312 final String outerName, 313 final String innerName, 314 final int access) 315 { 316 buf.setLength(0); 317 buf.append("cw.visitInnerClass("); 318 appendConstant(name); 319 buf.append(", "); 320 appendConstant(outerName); 321 buf.append(", "); 322 appendConstant(innerName); 323 buf.append(", "); 324 appendAccess(access | ACCESS_INNER); 325 buf.append(");\n\n"); 326 text.add(buf.toString()); 327 } 328 329 public FieldVisitor visitField( 330 final int access, 331 final String name, 332 final String desc, 333 final String signature, 334 final Object value) 335 { 336 buf.setLength(0); 337 buf.append("{\n"); 338 buf.append("fv = cw.visitField("); 339 appendAccess(access | ACCESS_FIELD); 340 buf.append(", "); 341 appendConstant(name); 342 buf.append(", "); 343 appendConstant(desc); 344 buf.append(", "); 345 appendConstant(signature); 346 buf.append(", "); 347 appendConstant(value); 348 buf.append(");\n"); 349 text.add(buf.toString()); 350 ASMifierFieldVisitor aav = new ASMifierFieldVisitor(); 351 text.add(aav.getText()); 352 text.add("}\n"); 353 return aav; 354 } 355 356 public MethodVisitor visitMethod( 357 final int access, 358 final String name, 359 final String desc, 360 final String signature, 361 final String[] exceptions) 362 { 363 buf.setLength(0); 364 buf.append("{\n"); 365 buf.append("mv = cw.visitMethod("); 366 appendAccess(access); 367 buf.append(", "); 368 appendConstant(name); 369 buf.append(", "); 370 appendConstant(desc); 371 buf.append(", "); 372 appendConstant(signature); 373 buf.append(", "); 374 if (exceptions != null && exceptions.length > 0) { 375 buf.append("new String[] {"); 376 for (int i = 0; i < exceptions.length; ++i) { 377 buf.append(i == 0 ? " " : ", "); 378 appendConstant(exceptions[i]); 379 } 380 buf.append(" }"); 381 } else { 382 buf.append("null"); 383 } 384 buf.append(");\n"); 385 text.add(buf.toString()); 386 ASMifierMethodVisitor acv = createASMifierMethodVisitor(); 387 text.add(acv.getText()); 388 text.add("}\n"); 389 return acv; 390 } 391 392 protected ASMifierMethodVisitor createASMifierMethodVisitor() { 393 return new ASMifierMethodVisitor(); 394 } 395 396 public AnnotationVisitor visitAnnotation( 397 final String desc, 398 final boolean visible) 399 { 400 buf.setLength(0); 401 buf.append("{\n"); 402 buf.append("av0 = cw.visitAnnotation("); 403 appendConstant(desc); 404 buf.append(", "); 405 buf.append(visible); 406 buf.append(");\n"); 407 text.add(buf.toString()); 408 ASMifierAnnotationVisitor av = new ASMifierAnnotationVisitor(0); 409 text.add(av.getText()); 410 text.add("}\n"); 411 return av; 412 } 413 414 public void visitEnd() { 415 text.add("cw.visitEnd();\n\n"); 416 text.add("return cw.toByteArray();\n"); 417 text.add("}\n"); 418 text.add("}\n"); 419 printList(pw, text); 420 pw.flush(); 421 } 422 423 // ------------------------------------------------------------------------ 424 // Utility methods 425 // ------------------------------------------------------------------------ 426 427 /** 428 * Appends a string representation of the given access modifiers to {@link 429 * #buf buf}. 430 * 431 * @param access some access modifiers. 432 */ 433 void appendAccess(final int access) { 434 boolean first = true; 435 if ((access & Opcodes.ACC_PUBLIC) != 0) { 436 buf.append("ACC_PUBLIC"); 437 first = false; 438 } 439 if ((access & Opcodes.ACC_PRIVATE) != 0) { 440 buf.append("ACC_PRIVATE"); 441 first = false; 442 } 443 if ((access & Opcodes.ACC_PROTECTED) != 0) { 444 buf.append("ACC_PROTECTED"); 445 first = false; 446 } 447 if ((access & Opcodes.ACC_FINAL) != 0) { 448 if (!first) { 449 buf.append(" + "); 450 } 451 buf.append("ACC_FINAL"); 452 first = false; 453 } 454 if ((access & Opcodes.ACC_STATIC) != 0) { 455 if (!first) { 456 buf.append(" + "); 457 } 458 buf.append("ACC_STATIC"); 459 first = false; 460 } 461 if ((access & Opcodes.ACC_SYNCHRONIZED) != 0) { 462 if (!first) { 463 buf.append(" + "); 464 } 465 if ((access & ACCESS_CLASS) == 0) { 466 buf.append("ACC_SYNCHRONIZED"); 467 } else { 468 buf.append("ACC_SUPER"); 469 } 470 first = false; 471 } 472 if ((access & Opcodes.ACC_VOLATILE) != 0 473 && (access & ACCESS_FIELD) != 0) 474 { 475 if (!first) { 476 buf.append(" + "); 477 } 478 buf.append("ACC_VOLATILE"); 479 first = false; 480 } 481 if ((access & Opcodes.ACC_BRIDGE) != 0 && (access & ACCESS_CLASS) == 0 482 && (access & ACCESS_FIELD) == 0) 483 { 484 if (!first) { 485 buf.append(" + "); 486 } 487 buf.append("ACC_BRIDGE"); 488 first = false; 489 } 490 if ((access & Opcodes.ACC_VARARGS) != 0 && (access & ACCESS_CLASS) == 0 491 && (access & ACCESS_FIELD) == 0) 492 { 493 if (!first) { 494 buf.append(" + "); 495 } 496 buf.append("ACC_VARARGS"); 497 first = false; 498 } 499 if ((access & Opcodes.ACC_TRANSIENT) != 0 500 && (access & ACCESS_FIELD) != 0) 501 { 502 if (!first) { 503 buf.append(" + "); 504 } 505 buf.append("ACC_TRANSIENT"); 506 first = false; 507 } 508 if ((access & Opcodes.ACC_NATIVE) != 0 && (access & ACCESS_CLASS) == 0 509 && (access & ACCESS_FIELD) == 0) 510 { 511 if (!first) { 512 buf.append(" + "); 513 } 514 buf.append("ACC_NATIVE"); 515 first = false; 516 } 517 if ((access & Opcodes.ACC_ENUM) != 0 518 && ((access & ACCESS_CLASS) != 0 519 || (access & ACCESS_FIELD) != 0 || (access & ACCESS_INNER) != 0)) 520 { 521 if (!first) { 522 buf.append(" + "); 523 } 524 buf.append("ACC_ENUM"); 525 first = false; 526 } 527 if ((access & Opcodes.ACC_ANNOTATION) != 0 528 && (access & ACCESS_CLASS) != 0) 529 { 530 if (!first) { 531 buf.append(" + "); 532 } 533 buf.append("ACC_ANNOTATION"); 534 first = false; 535 } 536 if ((access & Opcodes.ACC_ABSTRACT) != 0) { 537 if (!first) { 538 buf.append(" + "); 539 } 540 buf.append("ACC_ABSTRACT"); 541 first = false; 542 } 543 if ((access & Opcodes.ACC_INTERFACE) != 0) { 544 if (!first) { 545 buf.append(" + "); 546 } 547 buf.append("ACC_INTERFACE"); 548 first = false; 549 } 550 if ((access & Opcodes.ACC_STRICT) != 0) { 551 if (!first) { 552 buf.append(" + "); 553 } 554 buf.append("ACC_STRICT"); 555 first = false; 556 } 557 if ((access & Opcodes.ACC_SYNTHETIC) != 0) { 558 if (!first) { 559 buf.append(" + "); 560 } 561 buf.append("ACC_SYNTHETIC"); 562 first = false; 563 } 564 if ((access & Opcodes.ACC_DEPRECATED) != 0) { 565 if (!first) { 566 buf.append(" + "); 567 } 568 buf.append("ACC_DEPRECATED"); 569 first = false; 570 } 571 if (first) { 572 buf.append('0'); 573 } 574 } 575} 576