1 /***************************************************************************************
2 * Copyright (c) Jonas BonŽr, Alexandre Vasseur. All rights reserved. *
3 * http://aspectwerkz.codehaus.org *
4 * ---------------------------------------------------------------------------------- *
5 * The software in this package is published under the terms of the LGPL license *
6 * a copy of which has been included with this distribution in the license.txt file. *
7 **************************************************************************************/
8 package org.codehaus.aspectwerkz.transform.inlining;
9
10 import org.codehaus.aspectwerkz.util.ContextClassLoader;
11 import org.codehaus.aspectwerkz.reflect.ClassInfo;
12 import org.codehaus.aspectwerkz.reflect.MethodInfo;
13 import org.codehaus.aspectwerkz.reflect.ConstructorInfo;
14 import org.codehaus.aspectwerkz.reflect.FieldInfo;
15 import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo;
16 import org.codehaus.aspectwerkz.transform.TransformationConstants;
17 import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor;
18 import org.codehaus.aspectwerkz.exception.WrappedRuntimeException;
19 import org.objectweb.asm.ClassWriter;
20 import org.objectweb.asm.CodeVisitor;
21 import org.objectweb.asm.Type;
22 import org.objectweb.asm.Label;
23 import org.objectweb.asm.ClassReader;
24
25 import java.io.File;
26 import java.io.FileOutputStream;
27 import java.io.IOException;
28 import java.lang.reflect.Constructor;
29 import java.lang.reflect.Method;
30 import java.lang.reflect.InvocationTargetException;
31 import java.security.ProtectionDomain;
32 import java.security.AccessController;
33 import java.security.PrivilegedAction;
34
35 /***
36 * Helper class with utility methods for the ASM library.
37 *
38 * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
39 * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
40 */
41 public class AsmHelper implements TransformationConstants {
42
43 public final static ClassInfo INTEGER = JavaClassInfo.getClassInfo(Integer.TYPE);
44 public final static ClassInfo VOID = JavaClassInfo.getClassInfo(Void.TYPE);
45 public final static ClassInfo BOOLEAN = JavaClassInfo.getClassInfo(Boolean.TYPE);
46 public final static ClassInfo BYTE = JavaClassInfo.getClassInfo(Byte.TYPE);
47 public final static ClassInfo CHARACTER = JavaClassInfo.getClassInfo(Character.TYPE);
48 public final static ClassInfo SHORT = JavaClassInfo.getClassInfo(Short.TYPE);
49 public final static ClassInfo DOUBLE = JavaClassInfo.getClassInfo(Double.TYPE);
50 public final static ClassInfo FLOAT = JavaClassInfo.getClassInfo(Float.TYPE);
51 public final static ClassInfo LONG = JavaClassInfo.getClassInfo(Long.TYPE);
52
53 private static Class CLASS_LOADER;
54 private static Method CLASS_LOADER_DEFINE;
55 private static final ProtectionDomain PROTECTION_DOMAIN;
56
57 static {
58 PROTECTION_DOMAIN = (ProtectionDomain)AccessController.doPrivileged(new PrivilegedAction() {
59 public Object run() {
60 return AsmHelper.class.getProtectionDomain();
61 }
62 });
63
64 AccessController.doPrivileged(new PrivilegedAction() {
65 public Object run() {
66 try {
67 CLASS_LOADER = Class.forName(CLASS_LOADER_REFLECT_CLASS_NAME);
68 CLASS_LOADER_DEFINE = CLASS_LOADER.getDeclaredMethod(
69 DEFINE_CLASS_METHOD_NAME, new Class[]{
70 String.class, byte[].class, int.class, int.class, ProtectionDomain.class
71 }
72 );
73 CLASS_LOADER_DEFINE.setAccessible(true);
74 } catch (Throwable t) {
75 throw new Error(t.toString());
76 }
77 return null;
78 }
79 });
80 }
81
82
83
84 /***
85 * A boolean to check if we have a J2SE 5 support
86 */
87 public final static boolean IS_JAVA_5;
88 public static int JAVA_VERSION = V1_3;
89
90 static {
91 Class annotation = null;
92 try {
93 annotation = Class.forName("java.lang.annotation.Annotation");
94 ClassReader cr = new ClassReader("java.lang.annotation.Annotation");
95 JAVA_VERSION = V1_5;
96 } catch (Throwable e) {
97 annotation = null;
98 }
99 if (annotation == null) {
100 IS_JAVA_5 = false;
101 } else {
102 IS_JAVA_5 = true;
103 }
104 }
105
106 /***
107 * Factory method for ASM ClassWriter and J2SE 5 support
108 * See http://www.objectweb.org/wws/arc/asm/2004-08/msg00005.html
109 *
110 * @param computeMax
111 * @return
112 */
113 public static ClassWriter newClassWriter(boolean computeMax) {
114 return new ClassWriter(computeMax, true);
115 }
116
117 /***
118 * Gets the argument types for a constructor. <p/>Parts of code in this method is taken from the ASM codebase.
119 *
120 * @param constructor
121 * @return the ASM argument types for the constructor
122 */
123 public static Type[] getArgumentTypes(final Constructor constructor) {
124 Class[] classes = constructor.getParameterTypes();
125 Type[] types = new Type[classes.length];
126 for (int i = classes.length - 1; i >= 0; --i) {
127 types[i] = Type.getType(classes[i]);
128 }
129 return types;
130 }
131
132 /***
133 * Dumps an ASM class to disk.
134 *
135 * @param dumpDir
136 * @param className
137 * @param bytes
138 * @throws java.io.IOException
139 */
140 public static void dumpClass(final String dumpDir, final String className, final byte[] bytes)
141 throws IOException {
142 final File dir;
143 if (className.lastIndexOf('/')>0) {
144 dir = new File(dumpDir + File.separator + className.substring(0, className.lastIndexOf('/')));
145 } else {
146 dir = new File(dumpDir);
147 }
148 dir.mkdirs();
149 String fileName = dumpDir + File.separator + className + ".class";
150 if (AspectWerkzPreProcessor.VERBOSE) {
151 System.out.println("AW INFO: dumping class " + className + " to " + dumpDir);
152 }
153 FileOutputStream os = new FileOutputStream(fileName);
154 os.write(bytes);
155 os.close();
156 }
157
158 /***
159 * Dumps an ASM class to disk.
160 *
161 * @param dumpDir
162 * @param className
163 * @param cw
164 * @throws java.io.IOException
165 */
166 public static void dumpClass(final String dumpDir, final String className, final ClassWriter cw)
167 throws IOException {
168 File dir = new File(dumpDir + File.separator + className.substring(0, className.lastIndexOf('/')));
169 dir.mkdirs();
170 String fileName = dumpDir + File.separator + className + ".class";
171 if (AspectWerkzPreProcessor.VERBOSE) {
172 System.out.println("AW INFO: dumping class " + className + " to " + dumpDir);
173 }
174 FileOutputStream os = new FileOutputStream(fileName);
175 os.write(cw.toByteArray());
176 os.close();
177 }
178
179 /***
180 * Adds a class to a class loader and loads it.
181 *
182 * @param loader the class loader (if null the context class loader will be used)
183 * @param bytes the bytes for the class
184 * @param name the name of the class
185 * @return the class
186 */
187 public static Class defineClass(ClassLoader loader, final byte[] bytes, final String name) {
188 String className = name.replace('/', '.');
189 try {
190 if (loader == null) {
191 loader = ContextClassLoader.getLoader();
192 }
193
194
195
196
197 Object[] args = new Object[]{
198 className, bytes, new Integer(0), new Integer(bytes.length), PROTECTION_DOMAIN
199 };
200 Class clazz = (Class) CLASS_LOADER_DEFINE.invoke(loader, args);
201
202
203 return clazz;
204
205 } catch (InvocationTargetException e) {
206
207
208 if (e.getTargetException() instanceof LinkageError) {
209 Class failoverJoinpointClass = forName(loader, className);
210 if (failoverJoinpointClass != null) {
211 return failoverJoinpointClass;
212 }
213 }
214 throw new WrappedRuntimeException(e);
215 } catch (Exception e) {
216 throw new WrappedRuntimeException(e);
217 }
218 }
219
220 /***
221 * Tries to load a class if unsuccessful returns null.
222 *
223 * @param loader the class loader
224 * @param name the name of the class
225 * @return the class
226 */
227 public static Class forName(ClassLoader loader, final String name) {
228 String className = name.replace('/', '.');
229 try {
230 if (loader == null) {
231 loader = ContextClassLoader.getLoader();
232 }
233
234 return Class.forName(className, false, loader);
235 } catch (Exception e) {
236 return null;
237 }
238 }
239
240 /***
241 * Calculates the method hash. The computation MUST be the same as in ReflectHelper, thus we switch back the names
242 * to Java style. Note that for array type, Java.reflect is using "[Lpack.foo;" style unless primitive.
243 *
244 * @param name
245 * @param desc
246 * @return
247 */
248 public static int calculateMethodHash(final String name, final String desc) {
249 int hash = 17;
250 hash = (37 * hash) + name.replace('/', '.').hashCode();
251 Type[] argumentTypes = Type.getArgumentTypes(desc);
252 for (int i = 0; i < argumentTypes.length; i++) {
253 hash = (37 * hash)
254 + AsmHelper.convertTypeDescToReflectDesc(argumentTypes[i].getDescriptor()).hashCode();
255 }
256 return hash;
257 }
258
259 /***
260 * Calculates the constructor hash.
261 *
262 * @param desc
263 * @return
264 */
265 public static int calculateConstructorHash(final String desc) {
266 return AsmHelper.calculateMethodHash(INIT_METHOD_NAME, desc);
267 }
268
269 /***
270 // * Calculates the joinpoint constructor call/execution hash.
271 // * It depends on the callee class name else we won't be able to distinguish joinpoint on different targets.
272 // *
273 // * @param declaringClassName
274 // * @param desc
275 // * @return
276 // */
277
278
279
280
281
282
283
284 /***
285 * Calculates the field hash.
286 *
287 * @param name
288 * @param desc
289 * @return
290 */
291 public static int calculateFieldHash(final String name, final String desc) {
292 int hash = 17;
293 hash = (37 * hash) + name.hashCode();
294 Type type = Type.getType(desc);
295 hash = (37 * hash) + AsmHelper.convertTypeDescToReflectDesc(type.getDescriptor()).hashCode();
296 return hash;
297 }
298
299 /***
300 * Calculates the class hash.
301 *
302 * @param declaringType
303 * @return
304 */
305 public static int calculateClassHash(final String declaringType) {
306 return AsmHelper.convertTypeDescToReflectDesc(declaringType).hashCode();
307 }
308
309 /***
310 * Converts an internal Java array type name ([Lblabla) to the a the format used by the expression matcher
311 * (blabla[])
312 *
313 * @param typeName is type name
314 * @return
315 */
316 public static String convertArrayTypeName(final String typeName) {
317 int index = typeName.lastIndexOf('[');
318 if (index != -1) {
319 StringBuffer arrayType = new StringBuffer();
320 if (typeName.endsWith("I")) {
321 arrayType.append("int");
322 } else if (typeName.endsWith("J")) {
323 arrayType.append("long");
324 } else if (typeName.endsWith("S")) {
325 arrayType.append("short");
326 } else if (typeName.endsWith("F")) {
327 arrayType.append("float");
328 } else if (typeName.endsWith("D")) {
329 arrayType.append("double");
330 } else if (typeName.endsWith("Z")) {
331 arrayType.append("boolean");
332 } else if (typeName.endsWith("C")) {
333 arrayType.append("char");
334 } else if (typeName.endsWith("B")) {
335 arrayType.append("byte");
336 } else {
337 arrayType.append(typeName.substring(index + 2, typeName.length() - 1));
338 }
339 for (int i = 0; i < (index + 1); i++) {
340 arrayType.append("[]");
341 }
342 return arrayType.toString();
343 } else {
344 return typeName;
345 }
346 }
347
348 /***
349 * Converts an ASM type descriptor" (I, [I, [Ljava/lang/String;, Ljava/lang/String;) to a Java.reflect one (int, [I,
350 * [Ljava.lang.String;, java.lang.String)
351 *
352 * @param typeDesc
353 * @return the Java.reflect string representation
354 */
355 public static String convertTypeDescToReflectDesc(final String typeDesc) {
356 if (typeDesc == null) {
357 return null;
358 }
359 String result = null;
360
361 if (typeDesc.startsWith("[")) {
362 result = typeDesc;
363 } else {
364
365 if (typeDesc.startsWith("L") && typeDesc.endsWith(";")) {
366 result = typeDesc.substring(1, typeDesc.length() - 1);
367 } else {
368
369 if (typeDesc.equals("I")) {
370 result = "int";
371 } else if (typeDesc.equals("J")) {
372 result = "long";
373 } else if (typeDesc.equals("S")) {
374 result = "short";
375 } else if (typeDesc.equals("F")) {
376 result = "float";
377 } else if (typeDesc.equals("D")) {
378 result = "double";
379 } else if (typeDesc.equals("Z")) {
380 result = "boolean";
381 } else if (typeDesc.equals("C")) {
382 result = "char";
383 } else if (typeDesc.equals("B")) {
384 result = "byte";
385 } else {
386 throw new RuntimeException("unknown primitive type " + typeDesc);
387 }
388 }
389 }
390 return result.replace('/', '.');
391 }
392
393 /***
394 * Converts a java reflect type desc to ASM type desc.
395 *
396 * @param desc
397 * @return
398 */
399 public static String convertReflectDescToTypeDesc(final String desc) {
400 if (desc == null) {
401 return null;
402 }
403 String typeDesc = desc;
404 int dimension = 0;
405 char[] arr = desc.toCharArray();
406 for (int i = 0; i < arr.length; i++) {
407 if (arr[i] == ']') {
408 dimension++;
409 }
410 }
411 typeDesc = desc.substring(0, desc.length() - dimension * 2);
412 if (typeDesc.equals("int")) {
413 typeDesc = "I";
414 } else if (typeDesc.equals("short")) {
415 typeDesc = "S";
416 } else if (typeDesc.equals("long")) {
417 typeDesc = "J";
418 } else if (typeDesc.equals("float")) {
419 typeDesc = "F";
420 } else if (typeDesc.equals("double")) {
421 typeDesc = "D";
422 } else if (typeDesc.equals("byte")) {
423 typeDesc = "B";
424 } else if (typeDesc.equals("char")) {
425 typeDesc = "C";
426 } else if (typeDesc.equals("boolean")) {
427 typeDesc = "Z";
428 } else {
429 typeDesc = 'L' + typeDesc + ';';
430 }
431 for (int i = 0; i < dimension; i++) {
432 typeDesc = '[' + typeDesc;
433 }
434 return typeDesc.replace('.', '/');
435 }
436
437 /***
438 * Adds the correct return statement.
439 *
440 * @param mv
441 * @param type
442 */
443 public static void addReturnStatement(final CodeVisitor mv, final Type type) {
444 switch (type.getSort()) {
445 case Type.VOID:
446 mv.visitInsn(RETURN);
447 break;
448 case Type.LONG:
449 mv.visitInsn(LRETURN);
450 break;
451 case Type.INT:
452 mv.visitInsn(IRETURN);
453 break;
454 case Type.SHORT:
455 mv.visitInsn(IRETURN);
456 break;
457 case Type.DOUBLE:
458 mv.visitInsn(DRETURN);
459 break;
460 case Type.FLOAT:
461 mv.visitInsn(FRETURN);
462 break;
463 case Type.BYTE:
464 mv.visitInsn(IRETURN);
465 break;
466 case Type.BOOLEAN:
467 mv.visitInsn(IRETURN);
468 break;
469 case Type.CHAR:
470 mv.visitInsn(IRETURN);
471 break;
472 case Type.ARRAY:
473 mv.visitInsn(ARETURN);
474 break;
475 case Type.OBJECT:
476 mv.visitInsn(ARETURN);
477 break;
478 }
479 }
480
481 /***
482 * Loads argument types.
483 *
484 * @param mv
485 * @param argumentTypes
486 */
487 public static void loadArgumentTypes(final CodeVisitor mv, final Type[] argumentTypes, final boolean staticMethod) {
488 int index;
489 if (staticMethod) {
490 index = 0;
491 } else {
492 index = 1;
493 }
494 for (int i = 0; i < argumentTypes.length; i++) {
495 index = loadType(mv, index, argumentTypes[i]);
496 }
497 }
498
499 /***
500 * Loads a type.
501 *
502 * @param cv
503 * @param index
504 * @param type
505 * @return the incremented index
506 */
507 public static int loadType(final CodeVisitor cv, int index, final Type type) {
508 switch (type.getSort()) {
509 case Type.LONG:
510 cv.visitVarInsn(LLOAD, index++);
511 index++;
512 break;
513 case Type.INT:
514 cv.visitVarInsn(ILOAD, index++);
515 break;
516 case Type.SHORT:
517 cv.visitVarInsn(ILOAD, index++);
518 break;
519 case Type.DOUBLE:
520 cv.visitVarInsn(DLOAD, index++);
521 index++;
522 break;
523 case Type.FLOAT:
524 cv.visitVarInsn(FLOAD, index++);
525 break;
526 case Type.BYTE:
527 cv.visitVarInsn(ILOAD, index++);
528 break;
529 case Type.BOOLEAN:
530 cv.visitVarInsn(ILOAD, index++);
531 break;
532 case Type.CHAR:
533 cv.visitVarInsn(ILOAD, index++);
534 break;
535 case Type.ARRAY:
536 cv.visitVarInsn(ALOAD, index++);
537 break;
538 case Type.OBJECT:
539 cv.visitVarInsn(ALOAD, index++);
540 break;
541 }
542 return index;
543 }
544
545 /***
546 * Stores a type.
547 *
548 * @param cv
549 * @param index
550 * @param type
551 * @return the incremented index
552 */
553 public static int storeType(final CodeVisitor cv, int index, final Type type) {
554 switch (type.getSort()) {
555 case Type.VOID:
556 break;
557 case Type.LONG:
558 cv.visitVarInsn(LSTORE, index++);
559 index++;
560 break;
561 case Type.INT:
562 cv.visitVarInsn(ISTORE, index++);
563 break;
564 case Type.SHORT:
565 cv.visitVarInsn(ISTORE, index++);
566 break;
567 case Type.DOUBLE:
568 cv.visitVarInsn(DSTORE, index++);
569 index++;
570 break;
571 case Type.FLOAT:
572 cv.visitVarInsn(FSTORE, index++);
573 break;
574 case Type.BYTE:
575 cv.visitVarInsn(ISTORE, index++);
576 break;
577 case Type.BOOLEAN:
578 cv.visitVarInsn(ISTORE, index++);
579 break;
580 case Type.CHAR:
581 cv.visitVarInsn(ISTORE, index++);
582 break;
583 case Type.ARRAY:
584 cv.visitVarInsn(ASTORE, index++);
585 break;
586 case Type.OBJECT:
587 cv.visitVarInsn(ASTORE, index++);
588 break;
589 }
590 return index;
591 }
592
593 /***
594 * Creates and adds the correct parameter index for integer types.
595 *
596 * @param cv
597 * @param index
598 */
599 public static void loadIntegerConstant(final CodeVisitor cv, final int index) {
600 switch (index) {
601 case 0:
602 cv.visitInsn(ICONST_0);
603 break;
604 case 1:
605 cv.visitInsn(ICONST_1);
606 break;
607 case 2:
608 cv.visitInsn(ICONST_2);
609 break;
610 case 3:
611 cv.visitInsn(ICONST_3);
612 break;
613 case 4:
614 cv.visitInsn(ICONST_4);
615 break;
616 case 5:
617 cv.visitInsn(ICONST_5);
618 break;
619 default:
620 cv.visitIntInsn(BIPUSH, index);
621 break;
622 }
623 }
624
625 /***
626 * Prepares the wrapping or a primitive type.
627 *
628 * @param cv
629 * @param type
630 */
631 public static void prepareWrappingOfPrimitiveType(final CodeVisitor cv, final Type type) {
632 switch (type.getSort()) {
633 case Type.SHORT:
634 cv.visitTypeInsn(NEW, SHORT_CLASS_NAME);
635 cv.visitInsn(DUP);
636 break;
637 case Type.INT:
638 cv.visitTypeInsn(NEW, INTEGER_CLASS_NAME);
639 cv.visitInsn(DUP);
640 break;
641 case Type.LONG:
642 cv.visitTypeInsn(NEW, LONG_CLASS_NAME);
643 cv.visitInsn(DUP);
644 break;
645 case Type.FLOAT:
646 cv.visitTypeInsn(NEW, FLOAT_CLASS_NAME);
647 cv.visitInsn(DUP);
648 break;
649 case Type.DOUBLE:
650 cv.visitTypeInsn(NEW, DOUBLE_CLASS_NAME);
651 cv.visitInsn(DUP);
652 break;
653 case Type.BYTE:
654 cv.visitTypeInsn(NEW, BYTE_CLASS_NAME);
655 cv.visitInsn(DUP);
656 break;
657 case Type.BOOLEAN:
658 cv.visitTypeInsn(NEW, BOOLEAN_CLASS_NAME);
659 cv.visitInsn(DUP);
660 break;
661 case Type.CHAR:
662 cv.visitTypeInsn(NEW, CHARACTER_CLASS_NAME);
663 cv.visitInsn(DUP);
664 break;
665 }
666 }
667
668 /***
669 * Handles the wrapping of a primitive type.
670 *
671 * @param cv
672 * @param type
673 */
674 public static void wrapPrimitiveType(final CodeVisitor cv, final Type type) {
675 switch (type.getSort()) {
676 case Type.VOID:
677 cv.visitInsn(ACONST_NULL);
678 break;
679 case Type.SHORT:
680 cv.visitMethodInsn(
681 INVOKESPECIAL,
682 SHORT_CLASS_NAME,
683 INIT_METHOD_NAME,
684 SHORT_CLASS_INIT_METHOD_SIGNATURE
685 );
686 break;
687 case Type.INT:
688 cv.visitMethodInsn(
689 INVOKESPECIAL,
690 INTEGER_CLASS_NAME,
691 INIT_METHOD_NAME,
692 INTEGER_CLASS_INIT_METHOD_SIGNATURE
693 );
694 break;
695 case Type.LONG:
696 cv.visitMethodInsn(INVOKESPECIAL, LONG_CLASS_NAME, INIT_METHOD_NAME, LONG_CLASS_INIT_METHOD_SIGNATURE);
697 break;
698 case Type.FLOAT:
699 cv.visitMethodInsn(
700 INVOKESPECIAL,
701 FLOAT_CLASS_NAME,
702 INIT_METHOD_NAME,
703 FLOAT_CLASS_INIT_METHOD_SIGNATURE
704 );
705 break;
706 case Type.DOUBLE:
707 cv.visitMethodInsn(
708 INVOKESPECIAL,
709 DOUBLE_CLASS_NAME,
710 INIT_METHOD_NAME,
711 DOUBLE_CLASS_INIT_METHOD_SIGNATURE
712 );
713 break;
714 case Type.BYTE:
715 cv.visitMethodInsn(INVOKESPECIAL, BYTE_CLASS_NAME, INIT_METHOD_NAME, BYTE_CLASS_INIT_METHOD_SIGNATURE);
716 break;
717 case Type.BOOLEAN:
718 cv.visitMethodInsn(
719 INVOKESPECIAL,
720 BOOLEAN_CLASS_NAME,
721 INIT_METHOD_NAME,
722 BOOLEAN_CLASS_INIT_METHOD_SIGNATURE
723 );
724 break;
725 case Type.CHAR:
726 cv.visitMethodInsn(
727 INVOKESPECIAL,
728 CHARACTER_CLASS_NAME,
729 INIT_METHOD_NAME,
730 CHARACTER_CLASS_INIT_METHOD_SIGNATURE
731 );
732 break;
733 }
734 }
735
736 /***
737 * Handles the unwrapping of a type, unboxing of primitives and casting to the correct object type.
738 * Takes care of null value replaced by default primitive value.
739 * <pre>(obj==null)?0L:((Long)obj).longValue();</pre>
740 *
741 * @param cv
742 * @param type
743 */
744 public static void unwrapType(final CodeVisitor cv, final Type type) {
745
746 switch (type.getSort()) {
747 case Type.OBJECT:
748 String objectTypeName = type.getClassName().replace('.', '/');
749 cv.visitTypeInsn(CHECKCAST, objectTypeName);
750 return;
751 case Type.ARRAY:
752 cv.visitTypeInsn(CHECKCAST, type.getDescriptor());
753 return;
754 case Type.VOID:
755 return;
756 }
757
758 Label l0If = new Label();
759 Label l1End = new Label();
760
761 cv.visitInsn(DUP);
762 cv.visitJumpInsn(IFNONNULL, l0If);
763
764 cv.visitInsn(POP);
765 addDefaultValue(cv, type);
766
767 cv.visitJumpInsn(GOTO, l1End);
768
769 cv.visitLabel(l0If);
770 switch (type.getSort()) {
771 case Type.SHORT:
772 cv.visitTypeInsn(CHECKCAST, SHORT_CLASS_NAME);
773 cv.visitMethodInsn(
774 INVOKEVIRTUAL,
775 SHORT_CLASS_NAME,
776 SHORT_VALUE_METHOD_NAME,
777 SHORT_VALUE_METHOD_SIGNATURE
778 );
779 break;
780 case Type.INT:
781 cv.visitTypeInsn(CHECKCAST, INTEGER_CLASS_NAME);
782 cv.visitMethodInsn(
783 INVOKEVIRTUAL,
784 INTEGER_CLASS_NAME,
785 INT_VALUE_METHOD_NAME,
786 INT_VALUE_METHOD_SIGNATURE
787 );
788 break;
789 case Type.LONG:
790 cv.visitTypeInsn(CHECKCAST, LONG_CLASS_NAME);
791 cv.visitMethodInsn(
792 INVOKEVIRTUAL, LONG_CLASS_NAME, LONG_VALUE_METHOD_NAME, LONG_VALUE_METHOD_SIGNATURE
793 );
794 break;
795 case Type.FLOAT:
796 cv.visitTypeInsn(CHECKCAST, FLOAT_CLASS_NAME);
797 cv.visitMethodInsn(
798 INVOKEVIRTUAL,
799 FLOAT_CLASS_NAME,
800 FLOAT_VALUE_METHOD_NAME,
801 FLOAT_VALUE_METHOD_SIGNATURE
802 );
803 break;
804 case Type.DOUBLE:
805 cv.visitTypeInsn(CHECKCAST, DOUBLE_CLASS_NAME);
806 cv.visitMethodInsn(
807 INVOKEVIRTUAL,
808 DOUBLE_CLASS_NAME,
809 DOUBLE_VALUE_METHOD_NAME,
810 DOUBLE_VALUE_METHOD_SIGNATURE
811 );
812 break;
813 case Type.BYTE:
814 cv.visitTypeInsn(CHECKCAST, BYTE_CLASS_NAME);
815 cv.visitMethodInsn(
816 INVOKEVIRTUAL, BYTE_CLASS_NAME, BYTE_VALUE_METHOD_NAME, BYTE_VALUE_METHOD_SIGNATURE
817 );
818 break;
819 case Type.BOOLEAN:
820 cv.visitTypeInsn(CHECKCAST, BOOLEAN_CLASS_NAME);
821 cv.visitMethodInsn(
822 INVOKEVIRTUAL,
823 BOOLEAN_CLASS_NAME,
824 BOOLEAN_VALUE_METHOD_NAME,
825 BOOLEAN_VALUE_METHOD_SIGNATURE
826 );
827 break;
828 case Type.CHAR:
829 cv.visitTypeInsn(CHECKCAST, CHARACTER_CLASS_NAME);
830 cv.visitMethodInsn(
831 INVOKEVIRTUAL,
832 CHARACTER_CLASS_NAME,
833 CHAR_VALUE_METHOD_NAME,
834 CHAR_VALUE_METHOD_SIGNATURE
835 );
836 break;
837 }
838 cv.visitLabel(l1End);
839 }
840
841 /***
842 * Adds the default value for a type.
843 *
844 * @param cv
845 * @param type
846 */
847 public static void addDefaultValue(final CodeVisitor cv, final Type type) {
848 switch (type.getSort()) {
849 case Type.OBJECT:
850 cv.visitInsn(ACONST_NULL);
851 break;
852 case Type.ARRAY:
853 cv.visitInsn(ACONST_NULL);
854 break;
855 case Type.INT:
856 cv.visitInsn(ICONST_0);
857 break;
858 case Type.LONG:
859 cv.visitInsn(LCONST_0);
860 break;
861 case Type.SHORT:
862 cv.visitInsn(ICONST_0);
863 break;
864 case Type.FLOAT:
865 cv.visitInsn(FCONST_0);
866 break;
867 case Type.DOUBLE:
868 cv.visitInsn(DCONST_0);
869 break;
870 case Type.BYTE:
871 cv.visitInsn(ICONST_0);
872 break;
873 case Type.BOOLEAN:
874 cv.visitInsn(ICONST_0);
875 break;
876 case Type.CHAR:
877 cv.visitInsn(ICONST_0);
878 break;
879 }
880 }
881
882 /***
883 * Adds a string and inserts null if the string is null.
884 *
885 * @param cv
886 * @param value
887 */
888 public static void addNullableString(final CodeVisitor cv, final String value) {
889 if (value == null) {
890 cv.visitInsn(ACONST_NULL);
891 } else {
892 cv.visitLdcInsn(value);
893 }
894 }
895
896 /***
897 * Compute the register depth, based on an array of types (long, double = 2 bytes address)
898 *
899 * @param typesOnStack
900 * @return depth of the stack
901 */
902 public static int getRegisterDepth(final Type[] typesOnStack) {
903 int depth = 0;
904 for (int i = 0; i < typesOnStack.length; i++) {
905 Type type = typesOnStack[i];
906 depth++;
907 switch (type.getSort()) {
908 case Type.LONG:
909 depth++;
910 break;
911 case Type.DOUBLE:
912 depth++;
913 break;
914 }
915 }
916 return depth;
917 }
918
919 /***
920 * Compute the index on the stack of a given argument based on its index in the signature
921 *
922 * @param typesOnStack
923 * @param typeIndex
924 * @return
925 */
926 public static int getRegisterIndexOf(final Type[] typesOnStack, final int typeIndex) {
927 int depth = 0;
928 for (int i = 0; i < typeIndex; i++) {
929 Type type = typesOnStack[i];
930 depth++;
931 switch (type.getSort()) {
932 case Type.LONG:
933 depth++;
934 break;
935 case Type.DOUBLE:
936 depth++;
937 break;
938 }
939 }
940 return depth;
941 }
942
943 /***
944 * Compute the index on the signature of a given argument based on its index as if it was on the stack
945 * where the stack would start at the first argument
946 *
947 * @param typesOnStack
948 * @param registerIndex
949 * @return
950 */
951 public static int getTypeIndexOf(final Type[] typesOnStack, final int registerIndex) {
952 for (int i = 0; i < typesOnStack.length; i++) {
953 int presumedRegisterIndex = getRegisterIndexOf(typesOnStack, i);
954 if (registerIndex == presumedRegisterIndex) {
955 return i;
956 }
957 }
958 return -1;
959 }
960
961 /***
962 * Checks if the Type is a primitive.
963 *
964 * @param returnType
965 * @return TRUE/FALSE
966 */
967 public static boolean isPrimitive(final Type returnType) {
968 if (returnType.getSort() == Type.INT ||
969 returnType.getSort() == Type.SHORT ||
970 returnType.getSort() == Type.LONG ||
971 returnType.getSort() == Type.FLOAT ||
972 returnType.getSort() == Type.DOUBLE ||
973 returnType.getSort() == Type.BYTE ||
974 returnType.getSort() == Type.CHAR ||
975 returnType.getSort() == Type.BOOLEAN ||
976 returnType.getSort() == Type.VOID) {
977 return true;
978 } else {
979 return false;
980 }
981 }
982
983 /***
984 * Increments the index (takes doubles and longs in to account).
985 *
986 * @param index
987 * @param type
988 * @return the incremented index
989 */
990 public static int incrementIndex(int index, final Type type) {
991 if (type.getSort() == Type.LONG || type.getSort() == Type.DOUBLE) {
992 index += 2;
993 } else {
994 index++;
995 }
996 return index;
997 }
998
999 /***
1000 * Returns the descriptor corresponding to the given method info.
1001 * Adapted from ASM Type.getMethodDescriptor(<Method>)
1002 *
1003 * @param method
1004 * @return the descriptor of the given method.
1005 */
1006 public static String getMethodDescriptor(final MethodInfo method) {
1007 ClassInfo[] parameters = method.getParameterTypes();
1008 StringBuffer buf = new StringBuffer();
1009 buf.append('(');
1010 for (int i = 0; i < parameters.length; ++i) {
1011 getClassDescriptor(buf, parameters[i]);
1012 }
1013 buf.append(')');
1014 getClassDescriptor(buf, method.getReturnType());
1015 return buf.toString();
1016 }
1017
1018 /***
1019 * Returns the descriptor corresponding to the given constructor info.
1020 *
1021 * @param constructor
1022 * @return the descriptor of the given constructor.
1023 */
1024 public static String getConstructorDescriptor(final ConstructorInfo constructor) {
1025 ClassInfo[] parameters = constructor.getParameterTypes();
1026 StringBuffer buf = new StringBuffer();
1027 buf.append('(');
1028 for (int i = 0; i < parameters.length; ++i) {
1029 getClassDescriptor(buf, parameters[i]);
1030 }
1031 buf.append(')');
1032 getClassDescriptor(buf, VOID);
1033 return buf.toString();
1034 }
1035
1036 /***
1037 * Returns the descriptor corresponding to the given field info.
1038 *
1039 * @param field
1040 * @return the descriptor of the given field.
1041 */
1042 public static String getFieldDescriptor(final FieldInfo field) {
1043 return getClassDescriptor(field.getType());
1044 }
1045
1046 /***
1047 * Returns the descriptor corresponding to the given Java type.
1048 * Adapted from ASM Type.getClassDescriptor(Class)
1049 * <p/>
1050 * TODO remove the delegation to private method
1051 *
1052 * @param c an object class, a primitive class or an array class.
1053 * @return the descriptor corresponding to the given class.
1054 */
1055 public static String getClassDescriptor(final ClassInfo c) {
1056 StringBuffer buf = new StringBuffer();
1057 getClassDescriptor(buf, c);
1058 return buf.toString();
1059 }
1060
1061 /***
1062 * Appends the descriptor of the given class to the given string buffer.
1063 * Adapted from ASM Type.getClassDescriptor(StringBuffer, Class)
1064 *
1065 * @param buf the string buffer to which the descriptor must be appended.
1066 * @param klass the class whose descriptor must be computed.
1067 */
1068 private static void getClassDescriptor(final StringBuffer buf, final ClassInfo klass) {
1069 ClassInfo d = klass;
1070 while (true) {
1071 if (d.isPrimitive()) {
1072 char car;
1073 if (d.equals(INTEGER)) {
1074 car = 'I';
1075 } else if (d.equals(VOID)) {
1076 car = 'V';
1077 } else if (d.equals(BOOLEAN)) {
1078 car = 'Z';
1079 } else if (d.equals(BYTE)) {
1080 car = 'B';
1081 } else if (d.equals(CHARACTER)) {
1082 car = 'C';
1083 } else if (d.equals(SHORT)) {
1084 car = 'S';
1085 } else if (d.equals(DOUBLE)) {
1086 car = 'D';
1087 } else if (d.equals(FLOAT)) {
1088 car = 'F';
1089 } else if (d.equals(LONG)) {
1090 car = 'J';
1091 } else {
1092 throw new Error("should not happen");
1093 }
1094 buf.append(car);
1095 return;
1096 } else if (d.isArray()) {
1097 buf.append('[');
1098 d = d.getComponentType();
1099 } else {
1100 buf.append('L');
1101 String name = d.getName();
1102 int len = name.length();
1103 for (int i = 0; i < len; ++i) {
1104 char car = name.charAt(i);
1105 buf.append(car == '.' ? '/' : car);
1106 }
1107 buf.append(';');
1108 return;
1109 }
1110 }
1111 }
1112
1113 /***
1114 * Returns the Java types corresponding to the argument types of the given
1115 * method.
1116 * Adapted from ASM getArgumentTypes(Method)
1117 *
1118 * @param method a method.
1119 * @return the Java types corresponding to the argument types of the given
1120 * method.
1121 */
1122 public static Type[] getArgumentTypes(final MethodInfo method) {
1123 ClassInfo[] classes = method.getParameterTypes();
1124 Type[] types = new Type[classes.length];
1125 for (int i = classes.length - 1; i >= 0; --i) {
1126 types[i] = getType(classes[i]);
1127 }
1128 return types;
1129 }
1130
1131 /***
1132 * Returns the Java type corresponding to the given class.
1133 * Adapted from ASM getType(Class)
1134 *
1135 * @param c a class.
1136 * @return the Java type corresponding to the given class.
1137 */
1138 public static Type getType(final ClassInfo c) {
1139 if (c.isPrimitive()) {
1140 if (c.equals(INTEGER)) {
1141 return Type.INT_TYPE;
1142 } else if (c.equals(VOID)) {
1143 return Type.VOID_TYPE;
1144 } else if (c.equals(BOOLEAN)) {
1145 return Type.BOOLEAN_TYPE;
1146 } else if (c.equals(BYTE)) {
1147 return Type.BYTE_TYPE;
1148 } else if (c.equals(CHARACTER)) {
1149 return Type.CHAR_TYPE;
1150 } else if (c.equals(SHORT)) {
1151 return Type.SHORT_TYPE;
1152 } else if (c.equals(DOUBLE)) {
1153 return Type.DOUBLE_TYPE;
1154 } else if (c.equals(FLOAT)) {
1155 return Type.FLOAT_TYPE;
1156 } else if (c.equals(LONG)) {
1157 return Type.LONG_TYPE;
1158 } else {
1159 throw new Error("should not happen");
1160 }
1161 } else {
1162 return Type.getType(getClassDescriptor(c));
1163 }
1164 }
1165
1166 }