0001: /*
0002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
0003: */
0004: package com.tc.aspectwerkz.transform.inlining;
0005:
0006: import com.tc.asm.ClassWriter;
0007: import com.tc.asm.MethodVisitor;
0008: import com.tc.asm.Type;
0009: import com.tc.asm.Label;
0010: import com.tc.asm.ClassReader;
0011:
0012: import com.tc.aspectwerkz.exception.WrappedRuntimeException;
0013: import com.tc.aspectwerkz.reflect.impl.java.JavaClassInfo;
0014: import com.tc.aspectwerkz.reflect.ClassInfo;
0015: import com.tc.aspectwerkz.reflect.FieldInfo;
0016: import com.tc.aspectwerkz.reflect.ConstructorInfo;
0017: import com.tc.aspectwerkz.reflect.MethodInfo;
0018: import com.tc.aspectwerkz.transform.Properties;
0019: import com.tc.aspectwerkz.transform.TransformationConstants;
0020: import com.tc.aspectwerkz.util.ContextClassLoader;
0021:
0022: import java.io.File;
0023: import java.io.FileOutputStream;
0024: import java.io.IOException;
0025: import java.lang.reflect.Constructor;
0026: import java.lang.reflect.Method;
0027: import java.lang.reflect.InvocationTargetException;
0028: import java.security.ProtectionDomain;
0029: import java.security.AccessController;
0030: import java.security.PrivilegedAction;
0031:
0032: /**
0033: * Helper class with utility methods for the ASM library.
0034: *
0035: * @author <a href="mailto:jboner@codehaus.org">Jonas BonŽr </a>
0036: * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a>
0037: */
0038: public class AsmHelper implements TransformationConstants {
0039:
0040: public final static ClassInfo INTEGER = JavaClassInfo
0041: .getClassInfo(Integer.TYPE);
0042: public final static ClassInfo VOID = JavaClassInfo
0043: .getClassInfo(Void.TYPE);
0044: public final static ClassInfo BOOLEAN = JavaClassInfo
0045: .getClassInfo(Boolean.TYPE);
0046: public final static ClassInfo BYTE = JavaClassInfo
0047: .getClassInfo(Byte.TYPE);
0048: public final static ClassInfo CHARACTER = JavaClassInfo
0049: .getClassInfo(Character.TYPE);
0050: public final static ClassInfo SHORT = JavaClassInfo
0051: .getClassInfo(Short.TYPE);
0052: public final static ClassInfo DOUBLE = JavaClassInfo
0053: .getClassInfo(Double.TYPE);
0054: public final static ClassInfo FLOAT = JavaClassInfo
0055: .getClassInfo(Float.TYPE);
0056: public final static ClassInfo LONG = JavaClassInfo
0057: .getClassInfo(Long.TYPE);
0058:
0059: private static Class CLASS_LOADER;
0060: private static Method CLASS_LOADER_DEFINE;
0061: private static final ProtectionDomain PROTECTION_DOMAIN;
0062:
0063: static {
0064: PROTECTION_DOMAIN = (ProtectionDomain) AccessController
0065: .doPrivileged(new PrivilegedAction() {
0066: public Object run() {
0067: return AsmHelper.class.getProtectionDomain();
0068: }
0069: });
0070:
0071: AccessController.doPrivileged(new PrivilegedAction() {
0072: public Object run() {
0073: try {
0074: CLASS_LOADER = Class
0075: .forName(CLASS_LOADER_REFLECT_CLASS_NAME);
0076: CLASS_LOADER_DEFINE = CLASS_LOADER
0077: .getDeclaredMethod(
0078: DEFINE_CLASS_METHOD_NAME,
0079: new Class[] { String.class,
0080: byte[].class, int.class,
0081: int.class,
0082: ProtectionDomain.class });
0083: CLASS_LOADER_DEFINE.setAccessible(true);
0084: } catch (Throwable t) {
0085: throw new Error(t.toString());
0086: }
0087: return null;
0088: }
0089: });
0090: }
0091:
0092: /**
0093: * A boolean to check if we have a J2SE 5 support
0094: */
0095: public final static boolean IS_JAVA_5;
0096: public static int JAVA_VERSION = V1_3;
0097:
0098: static {
0099: Class annotation = null;
0100: try {
0101: annotation = Class
0102: .forName("java.lang.annotation.Annotation");
0103: new ClassReader("java.lang.annotation.Annotation");
0104: JAVA_VERSION = V1_5;
0105: } catch (Throwable e) {
0106: annotation = null;
0107: }
0108: if (annotation == null) {
0109: IS_JAVA_5 = false;
0110: } else {
0111: IS_JAVA_5 = true;
0112: }
0113: }
0114:
0115: /**
0116: * Factory method for ASM ClassWriter and J2SE 5 support
0117: * See http://www.objectweb.org/wws/arc/asm/2004-08/msg00005.html
0118: *
0119: * @param computeMax
0120: * @return
0121: */
0122: public static ClassWriter newClassWriter(boolean computeMax) {
0123: return new ClassWriter(computeMax ? ClassWriter.COMPUTE_MAXS
0124: : 0);
0125: }
0126:
0127: /**
0128: * Gets the argument types for a constructor. <p/>Parts of code in this method is taken from the ASM codebase.
0129: *
0130: * @param constructor
0131: * @return the ASM argument types for the constructor
0132: */
0133: public static Type[] getArgumentTypes(final Constructor constructor) {
0134: Class[] classes = constructor.getParameterTypes();
0135: Type[] types = new Type[classes.length];
0136: for (int i = classes.length - 1; i >= 0; --i) {
0137: types[i] = Type.getType(classes[i]);
0138: }
0139: return types;
0140: }
0141:
0142: /**
0143: * Dumps an ASM class to disk.
0144: *
0145: * @param dumpDir
0146: * @param className
0147: * @param bytes
0148: * @throws java.io.IOException
0149: */
0150: public static void dumpClass(final String dumpDir,
0151: final String className, final byte[] bytes)
0152: throws IOException {
0153: final File dir;
0154: if (className.lastIndexOf('/') > 0) {
0155: dir = new File(dumpDir
0156: + File.separator
0157: + className
0158: .substring(0, className.lastIndexOf('/')));
0159: } else {
0160: dir = new File(dumpDir);
0161: }
0162: dir.mkdirs();
0163: String fileName = dumpDir + File.separator + className
0164: + ".class";
0165: if (Properties.PRINT_DEPLOYMENT_INFO) {
0166: System.out.println("AW INFO: dumping class " + className
0167: + " to " + dumpDir);
0168: }
0169: FileOutputStream os = new FileOutputStream(fileName);
0170: os.write(bytes);
0171: os.close();
0172: }
0173:
0174: /**
0175: * Dumps an ASM class to disk.
0176: *
0177: * @param dumpDir
0178: * @param className
0179: * @param cw
0180: * @throws java.io.IOException
0181: */
0182: public static void dumpClass(final String dumpDir,
0183: final String className, final ClassWriter cw)
0184: throws IOException {
0185: String base = "";
0186: if (className.lastIndexOf('/') > 0) {
0187: base = className.substring(0, className.lastIndexOf('/'));
0188: }
0189: File dir = new File(dumpDir + File.separator + base);
0190: dir.mkdirs();
0191: String fileName = dumpDir + File.separator + className
0192: + ".class";
0193: if (Properties.PRINT_DEPLOYMENT_INFO) {
0194: System.out.println("AW INFO: dumping class " + className
0195: + " to " + dumpDir);
0196: }
0197: FileOutputStream os = new FileOutputStream(fileName);
0198: os.write(cw.toByteArray());
0199: os.close();
0200: }
0201:
0202: /**
0203: * Adds a class to a class loader and loads it.
0204: *
0205: * @param loader the class loader (if null the context class loader will be used)
0206: * @param bytes the bytes for the class
0207: * @param name the name of the class
0208: * @return the class
0209: */
0210: public static Class defineClass(ClassLoader loader,
0211: final byte[] bytes, final String name) {
0212: String className = name.replace('/', '.');
0213: try {
0214: if (loader == null) {
0215: loader = ContextClassLoader.getLoader();
0216: }
0217:
0218: Object[] args = new Object[] { className, bytes,
0219: new Integer(0), new Integer(bytes.length),
0220: PROTECTION_DOMAIN };
0221: Class klass = (Class) CLASS_LOADER_DEFINE.invoke(loader,
0222: args);
0223: return klass;
0224:
0225: } catch (InvocationTargetException e) {
0226: // JIT failovering for Thread concurrency
0227: // AW-222 (Tomcat and WLS were reported for AW-222)
0228: if (e.getTargetException() instanceof LinkageError) {
0229: Class failoverJoinpointClass = forName(loader,
0230: className);
0231: if (failoverJoinpointClass != null) {
0232: return failoverJoinpointClass;
0233: }
0234: }
0235: throw new WrappedRuntimeException(e);
0236: } catch (Exception e) {
0237: throw new WrappedRuntimeException(e);
0238: }
0239: }
0240:
0241: /**
0242: * Tries to load a class if unsuccessful returns null.
0243: *
0244: * @param loader the class loader
0245: * @param name the name of the class
0246: * @return the class
0247: */
0248: public static Class forName(ClassLoader loader, final String name) {
0249: String className = name.replace('/', '.');
0250: try {
0251: if (loader == null) {
0252: loader = ContextClassLoader.getLoader();
0253: }
0254: // Use Class.forName since loader.loadClass fails on JBoss UCL
0255: return Class.forName(className, false, loader);
0256: } catch (Exception e) {
0257: return null;
0258: }
0259: }
0260:
0261: /**
0262: * Calculates the method hash. The computation MUST be the same as in ReflectHelper, thus we switch back the names
0263: * to Java style. Note that for array type, Java.reflect is using "[Lpack.foo;" style unless primitive.
0264: *
0265: * @param name
0266: * @param desc
0267: * @return
0268: */
0269: public static int calculateMethodHash(final String name,
0270: final String desc) {
0271: int hash = 17;
0272: hash = (37 * hash) + name.replace('/', '.').hashCode();
0273: Type[] argumentTypes = Type.getArgumentTypes(desc);
0274: for (int i = 0; i < argumentTypes.length; i++) {
0275: hash = (37 * hash)
0276: + AsmHelper.convertTypeDescToReflectDesc(
0277: argumentTypes[i].getDescriptor())
0278: .hashCode();
0279: }
0280: return hash;
0281: }
0282:
0283: /**
0284: * Calculates the constructor hash.
0285: *
0286: * @param desc
0287: * @return
0288: */
0289: public static int calculateConstructorHash(final String desc) {
0290: return AsmHelper.calculateMethodHash(INIT_METHOD_NAME, desc);
0291: }
0292:
0293: /**
0294: * Calculates the field hash.
0295: *
0296: * @param name
0297: * @param desc
0298: * @return
0299: */
0300: public static int calculateFieldHash(final String name,
0301: final String desc) {
0302: int hash = 17;
0303: hash = (37 * hash) + name.hashCode();
0304: Type type = Type.getType(desc);
0305: hash = (37 * hash)
0306: + AsmHelper.convertTypeDescToReflectDesc(
0307: type.getDescriptor()).hashCode();
0308: return hash;
0309: }
0310:
0311: /**
0312: * Calculates the class hash.
0313: *
0314: * @param declaringType
0315: * @return
0316: */
0317: public static int calculateClassHash(final String declaringType) {
0318: return AsmHelper.convertTypeDescToReflectDesc(declaringType)
0319: .hashCode();
0320: }
0321:
0322: /**
0323: * Converts an internal Java array type name ([Lblabla) to the a the format used by the expression matcher
0324: * (blabla[])
0325: *
0326: * @param typeName is type name
0327: * @return
0328: */
0329: public static String convertArrayTypeName(final String typeName) {
0330: int index = typeName.lastIndexOf('[');
0331: if (index != -1) {
0332: StringBuffer arrayType = new StringBuffer();
0333: if (typeName.endsWith("I")) {
0334: arrayType.append("int");
0335: } else if (typeName.endsWith("J")) {
0336: arrayType.append("long");
0337: } else if (typeName.endsWith("S")) {
0338: arrayType.append("short");
0339: } else if (typeName.endsWith("F")) {
0340: arrayType.append("float");
0341: } else if (typeName.endsWith("D")) {
0342: arrayType.append("double");
0343: } else if (typeName.endsWith("Z")) {
0344: arrayType.append("boolean");
0345: } else if (typeName.endsWith("C")) {
0346: arrayType.append("char");
0347: } else if (typeName.endsWith("B")) {
0348: arrayType.append("byte");
0349: } else {
0350: arrayType.append(typeName.substring(index + 2, typeName
0351: .length() - 1));
0352: }
0353: for (int i = 0; i < (index + 1); i++) {
0354: arrayType.append("[]");
0355: }
0356: return arrayType.toString();
0357: } else {
0358: return typeName;
0359: }
0360: }
0361:
0362: /**
0363: * Converts an ASM type descriptor" (I, [I, [Ljava/lang/String;, Ljava/lang/String;) to a Java.reflect one (int, [I,
0364: * [Ljava.lang.String;, java.lang.String)
0365: *
0366: * @param typeDesc
0367: * @return the Java.reflect string representation
0368: */
0369: public static String convertTypeDescToReflectDesc(
0370: final String typeDesc) {
0371: if (typeDesc == null) {
0372: return null;
0373: }
0374: String result = null;
0375: // change needed for array types only
0376: if (typeDesc.startsWith("[")) {
0377: result = typeDesc;
0378: } else {
0379: // support for single dimension type
0380: if (typeDesc.startsWith("L") && typeDesc.endsWith(";")) {
0381: result = typeDesc.substring(1, typeDesc.length() - 1);
0382: } else {
0383: // primitive type, single dimension
0384: if (typeDesc.equals("I")) {
0385: result = "int";
0386: } else if (typeDesc.equals("J")) {
0387: result = "long";
0388: } else if (typeDesc.equals("S")) {
0389: result = "short";
0390: } else if (typeDesc.equals("F")) {
0391: result = "float";
0392: } else if (typeDesc.equals("D")) {
0393: result = "double";
0394: } else if (typeDesc.equals("Z")) {
0395: result = "boolean";
0396: } else if (typeDesc.equals("C")) {
0397: result = "char";
0398: } else if (typeDesc.equals("B")) {
0399: result = "byte";
0400: } else {
0401: throw new RuntimeException(
0402: "unknown primitive type " + typeDesc);
0403: }
0404: }
0405: }
0406: return result.replace('/', '.');
0407: }
0408:
0409: /**
0410: * Converts a java reflect type desc to ASM type desc.
0411: *
0412: * @param desc
0413: * @return
0414: */
0415: public static String convertReflectDescToTypeDesc(final String desc) {
0416: if (desc == null) {
0417: return null;
0418: }
0419: String typeDesc = desc;
0420: int dimension = 0;
0421: char[] arr = desc.toCharArray();
0422: for (int i = 0; i < arr.length; i++) {
0423: if (arr[i] == ']') {
0424: dimension++;
0425: }
0426: }
0427: typeDesc = desc.substring(0, desc.length() - dimension * 2);
0428: if (typeDesc.equals("int")) {
0429: typeDesc = "I";
0430: } else if (typeDesc.equals("short")) {
0431: typeDesc = "S";
0432: } else if (typeDesc.equals("long")) {
0433: typeDesc = "J";
0434: } else if (typeDesc.equals("float")) {
0435: typeDesc = "F";
0436: } else if (typeDesc.equals("double")) {
0437: typeDesc = "D";
0438: } else if (typeDesc.equals("byte")) {
0439: typeDesc = "B";
0440: } else if (typeDesc.equals("char")) {
0441: typeDesc = "C";
0442: } else if (typeDesc.equals("boolean")) {
0443: typeDesc = "Z";
0444: } else {
0445: typeDesc = 'L' + typeDesc + ';';
0446: }
0447: for (int i = 0; i < dimension; i++) {
0448: typeDesc = '[' + typeDesc;
0449: }
0450: return typeDesc.replace('.', '/');
0451: }
0452:
0453: /**
0454: * Adds the correct return statement.
0455: *
0456: * @param mv
0457: * @param type
0458: */
0459: public static void addReturnStatement(final MethodVisitor mv,
0460: final Type type) {
0461: switch (type.getSort()) {
0462: case Type.VOID:
0463: mv.visitInsn(RETURN);
0464: break;
0465: case Type.LONG:
0466: mv.visitInsn(LRETURN);
0467: break;
0468: case Type.INT:
0469: mv.visitInsn(IRETURN);
0470: break;
0471: case Type.SHORT:
0472: mv.visitInsn(IRETURN);
0473: break;
0474: case Type.DOUBLE:
0475: mv.visitInsn(DRETURN);
0476: break;
0477: case Type.FLOAT:
0478: mv.visitInsn(FRETURN);
0479: break;
0480: case Type.BYTE:
0481: mv.visitInsn(IRETURN);
0482: break;
0483: case Type.BOOLEAN:
0484: mv.visitInsn(IRETURN);
0485: break;
0486: case Type.CHAR:
0487: mv.visitInsn(IRETURN);
0488: break;
0489: case Type.ARRAY:
0490: mv.visitInsn(ARETURN);
0491: break;
0492: case Type.OBJECT:
0493: mv.visitInsn(ARETURN);
0494: break;
0495: }
0496: }
0497:
0498: /**
0499: * Loads argument types.
0500: *
0501: * @param mv
0502: * @param argumentTypes
0503: */
0504: public static void loadArgumentTypes(final MethodVisitor mv,
0505: final Type[] argumentTypes, final boolean staticMethod) {
0506: int index;
0507: if (staticMethod) {
0508: index = 0;
0509: } else {
0510: index = 1;
0511: }
0512: for (int i = 0; i < argumentTypes.length; i++) {
0513: index = loadType(mv, index, argumentTypes[i]);
0514: }
0515: }
0516:
0517: /**
0518: * Loads a type.
0519: *
0520: * @param cv
0521: * @param index
0522: * @param type
0523: * @return the incremented index
0524: */
0525: public static int loadType(final MethodVisitor cv, int index,
0526: final Type type) {
0527: switch (type.getSort()) {
0528: case Type.LONG:
0529: cv.visitVarInsn(LLOAD, index++);
0530: index++;
0531: break;
0532: case Type.INT:
0533: cv.visitVarInsn(ILOAD, index++);
0534: break;
0535: case Type.SHORT:
0536: cv.visitVarInsn(ILOAD, index++);
0537: break;
0538: case Type.DOUBLE:
0539: cv.visitVarInsn(DLOAD, index++);
0540: index++;
0541: break;
0542: case Type.FLOAT:
0543: cv.visitVarInsn(FLOAD, index++);
0544: break;
0545: case Type.BYTE:
0546: cv.visitVarInsn(ILOAD, index++);
0547: break;
0548: case Type.BOOLEAN:
0549: cv.visitVarInsn(ILOAD, index++);
0550: break;
0551: case Type.CHAR:
0552: cv.visitVarInsn(ILOAD, index++);
0553: break;
0554: case Type.ARRAY:
0555: cv.visitVarInsn(ALOAD, index++);
0556: break;
0557: case Type.OBJECT:
0558: cv.visitVarInsn(ALOAD, index++);
0559: break;
0560: }
0561: return index;
0562: }
0563:
0564: /**
0565: * Stores a type.
0566: *
0567: * @param cv
0568: * @param index
0569: * @param type
0570: * @return the incremented index
0571: */
0572: public static int storeType(final MethodVisitor cv, int index,
0573: final Type type) {
0574: switch (type.getSort()) {
0575: case Type.VOID:
0576: break;
0577: case Type.LONG:
0578: cv.visitVarInsn(LSTORE, index++);
0579: index++;
0580: break;
0581: case Type.INT:
0582: cv.visitVarInsn(ISTORE, index++);
0583: break;
0584: case Type.SHORT:
0585: cv.visitVarInsn(ISTORE, index++);
0586: break;
0587: case Type.DOUBLE:
0588: cv.visitVarInsn(DSTORE, index++);
0589: index++;
0590: break;
0591: case Type.FLOAT:
0592: cv.visitVarInsn(FSTORE, index++);
0593: break;
0594: case Type.BYTE:
0595: cv.visitVarInsn(ISTORE, index++);
0596: break;
0597: case Type.BOOLEAN:
0598: cv.visitVarInsn(ISTORE, index++);
0599: break;
0600: case Type.CHAR:
0601: cv.visitVarInsn(ISTORE, index++);
0602: break;
0603: case Type.ARRAY:
0604: cv.visitVarInsn(ASTORE, index++);
0605: break;
0606: case Type.OBJECT:
0607: cv.visitVarInsn(ASTORE, index++);
0608: break;
0609: }
0610: return index;
0611: }
0612:
0613: /**
0614: * Push the string on the stack. Deal when case where string is null.
0615: *
0616: * @param cv
0617: * @param s
0618: */
0619: public static void loadStringConstant(final MethodVisitor cv,
0620: final String s) {
0621: if (s != null) {
0622: cv.visitLdcInsn(s);
0623: } else {
0624: cv.visitInsn(ACONST_NULL);
0625: }
0626: }
0627:
0628: /**
0629: * Creates and adds the correct parameter index for integer types.
0630: *
0631: * @param cv
0632: * @param index
0633: */
0634: public static void loadIntegerConstant(final MethodVisitor cv,
0635: final int index) {
0636: switch (index) {
0637: case 0:
0638: cv.visitInsn(ICONST_0);
0639: break;
0640: case 1:
0641: cv.visitInsn(ICONST_1);
0642: break;
0643: case 2:
0644: cv.visitInsn(ICONST_2);
0645: break;
0646: case 3:
0647: cv.visitInsn(ICONST_3);
0648: break;
0649: case 4:
0650: cv.visitInsn(ICONST_4);
0651: break;
0652: case 5:
0653: cv.visitInsn(ICONST_5);
0654: break;
0655: default:
0656: cv.visitIntInsn(BIPUSH, index);
0657: break;
0658: }
0659: }
0660:
0661: /**
0662: * Prepares the wrapping or a primitive type.
0663: *
0664: * @param cv
0665: * @param type
0666: */
0667: public static void prepareWrappingOfPrimitiveType(
0668: final MethodVisitor cv, final Type type) {
0669: switch (type.getSort()) {
0670: case Type.SHORT:
0671: cv.visitTypeInsn(NEW, SHORT_CLASS_NAME);
0672: cv.visitInsn(DUP);
0673: break;
0674: case Type.INT:
0675: cv.visitTypeInsn(NEW, INTEGER_CLASS_NAME);
0676: cv.visitInsn(DUP);
0677: break;
0678: case Type.LONG:
0679: cv.visitTypeInsn(NEW, LONG_CLASS_NAME);
0680: cv.visitInsn(DUP);
0681: break;
0682: case Type.FLOAT:
0683: cv.visitTypeInsn(NEW, FLOAT_CLASS_NAME);
0684: cv.visitInsn(DUP);
0685: break;
0686: case Type.DOUBLE:
0687: cv.visitTypeInsn(NEW, DOUBLE_CLASS_NAME);
0688: cv.visitInsn(DUP);
0689: break;
0690: case Type.BYTE:
0691: cv.visitTypeInsn(NEW, BYTE_CLASS_NAME);
0692: cv.visitInsn(DUP);
0693: break;
0694: case Type.BOOLEAN:
0695: cv.visitTypeInsn(NEW, BOOLEAN_CLASS_NAME);
0696: cv.visitInsn(DUP);
0697: break;
0698: case Type.CHAR:
0699: cv.visitTypeInsn(NEW, CHARACTER_CLASS_NAME);
0700: cv.visitInsn(DUP);
0701: break;
0702: }
0703: }
0704:
0705: /**
0706: * Handles the wrapping of a primitive type.
0707: *
0708: * @param cv
0709: * @param type
0710: */
0711: public static void wrapPrimitiveType(final MethodVisitor cv,
0712: final Type type) {
0713: switch (type.getSort()) {
0714: case Type.VOID:
0715: cv.visitInsn(ACONST_NULL);
0716: break;
0717: case Type.SHORT:
0718: cv
0719: .visitMethodInsn(INVOKESPECIAL, SHORT_CLASS_NAME,
0720: INIT_METHOD_NAME,
0721: SHORT_CLASS_INIT_METHOD_SIGNATURE);
0722: break;
0723: case Type.INT:
0724: cv.visitMethodInsn(INVOKESPECIAL, INTEGER_CLASS_NAME,
0725: INIT_METHOD_NAME,
0726: INTEGER_CLASS_INIT_METHOD_SIGNATURE);
0727: break;
0728: case Type.LONG:
0729: cv.visitMethodInsn(INVOKESPECIAL, LONG_CLASS_NAME,
0730: INIT_METHOD_NAME, LONG_CLASS_INIT_METHOD_SIGNATURE);
0731: break;
0732: case Type.FLOAT:
0733: cv
0734: .visitMethodInsn(INVOKESPECIAL, FLOAT_CLASS_NAME,
0735: INIT_METHOD_NAME,
0736: FLOAT_CLASS_INIT_METHOD_SIGNATURE);
0737: break;
0738: case Type.DOUBLE:
0739: cv.visitMethodInsn(INVOKESPECIAL, DOUBLE_CLASS_NAME,
0740: INIT_METHOD_NAME,
0741: DOUBLE_CLASS_INIT_METHOD_SIGNATURE);
0742: break;
0743: case Type.BYTE:
0744: cv.visitMethodInsn(INVOKESPECIAL, BYTE_CLASS_NAME,
0745: INIT_METHOD_NAME, BYTE_CLASS_INIT_METHOD_SIGNATURE);
0746: break;
0747: case Type.BOOLEAN:
0748: cv.visitMethodInsn(INVOKESPECIAL, BOOLEAN_CLASS_NAME,
0749: INIT_METHOD_NAME,
0750: BOOLEAN_CLASS_INIT_METHOD_SIGNATURE);
0751: break;
0752: case Type.CHAR:
0753: cv.visitMethodInsn(INVOKESPECIAL, CHARACTER_CLASS_NAME,
0754: INIT_METHOD_NAME,
0755: CHARACTER_CLASS_INIT_METHOD_SIGNATURE);
0756: break;
0757: }
0758: }
0759:
0760: /**
0761: * Handles the unwrapping of a type, unboxing of primitives and casting to the correct object type.
0762: * Takes care of null value replaced by default primitive value.
0763: * <pre>(obj==null)?0L:((Long)obj).longValue();</pre>
0764: *
0765: * @param cv
0766: * @param type
0767: */
0768: public static void unwrapType(final MethodVisitor cv,
0769: final Type type) {
0770: // void, object and array type handling
0771: switch (type.getSort()) {
0772: case Type.OBJECT:
0773: String objectTypeName = type.getClassName().replace('.',
0774: '/');
0775: cv.visitTypeInsn(CHECKCAST, objectTypeName);
0776: return;
0777: case Type.ARRAY:
0778: cv.visitTypeInsn(CHECKCAST, type.getDescriptor());
0779: return;
0780: case Type.VOID:
0781: return;
0782: }
0783: // primitive type handling
0784: Label l0If = new Label();
0785: Label l1End = new Label();
0786: // if != null
0787: cv.visitInsn(DUP);
0788: cv.visitJumpInsn(IFNONNULL, l0If);
0789: // else, default value
0790: cv.visitInsn(POP);
0791: addDefaultValue(cv, type);
0792: // end
0793: cv.visitJumpInsn(GOTO, l1End);
0794: // if body
0795: cv.visitLabel(l0If);
0796: switch (type.getSort()) {
0797: case Type.SHORT:
0798: cv.visitTypeInsn(CHECKCAST, SHORT_CLASS_NAME);
0799: cv.visitMethodInsn(INVOKEVIRTUAL, SHORT_CLASS_NAME,
0800: SHORT_VALUE_METHOD_NAME,
0801: SHORT_VALUE_METHOD_SIGNATURE);
0802: break;
0803: case Type.INT:
0804: cv.visitTypeInsn(CHECKCAST, INTEGER_CLASS_NAME);
0805: cv.visitMethodInsn(INVOKEVIRTUAL, INTEGER_CLASS_NAME,
0806: INT_VALUE_METHOD_NAME, INT_VALUE_METHOD_SIGNATURE);
0807: break;
0808: case Type.LONG:
0809: cv.visitTypeInsn(CHECKCAST, LONG_CLASS_NAME);
0810: cv
0811: .visitMethodInsn(INVOKEVIRTUAL, LONG_CLASS_NAME,
0812: LONG_VALUE_METHOD_NAME,
0813: LONG_VALUE_METHOD_SIGNATURE);
0814: break;
0815: case Type.FLOAT:
0816: cv.visitTypeInsn(CHECKCAST, FLOAT_CLASS_NAME);
0817: cv.visitMethodInsn(INVOKEVIRTUAL, FLOAT_CLASS_NAME,
0818: FLOAT_VALUE_METHOD_NAME,
0819: FLOAT_VALUE_METHOD_SIGNATURE);
0820: break;
0821: case Type.DOUBLE:
0822: cv.visitTypeInsn(CHECKCAST, DOUBLE_CLASS_NAME);
0823: cv.visitMethodInsn(INVOKEVIRTUAL, DOUBLE_CLASS_NAME,
0824: DOUBLE_VALUE_METHOD_NAME,
0825: DOUBLE_VALUE_METHOD_SIGNATURE);
0826: break;
0827: case Type.BYTE:
0828: cv.visitTypeInsn(CHECKCAST, BYTE_CLASS_NAME);
0829: cv
0830: .visitMethodInsn(INVOKEVIRTUAL, BYTE_CLASS_NAME,
0831: BYTE_VALUE_METHOD_NAME,
0832: BYTE_VALUE_METHOD_SIGNATURE);
0833: break;
0834: case Type.BOOLEAN:
0835: cv.visitTypeInsn(CHECKCAST, BOOLEAN_CLASS_NAME);
0836: cv.visitMethodInsn(INVOKEVIRTUAL, BOOLEAN_CLASS_NAME,
0837: BOOLEAN_VALUE_METHOD_NAME,
0838: BOOLEAN_VALUE_METHOD_SIGNATURE);
0839: break;
0840: case Type.CHAR:
0841: cv.visitTypeInsn(CHECKCAST, CHARACTER_CLASS_NAME);
0842: cv
0843: .visitMethodInsn(INVOKEVIRTUAL,
0844: CHARACTER_CLASS_NAME,
0845: CHAR_VALUE_METHOD_NAME,
0846: CHAR_VALUE_METHOD_SIGNATURE);
0847: break;
0848: }
0849: cv.visitLabel(l1End);
0850: }
0851:
0852: /**
0853: * Adds the default value for a type.
0854: *
0855: * @param cv
0856: * @param type
0857: */
0858: public static void addDefaultValue(final MethodVisitor cv,
0859: final Type type) {
0860: switch (type.getSort()) {
0861: case Type.OBJECT:
0862: cv.visitInsn(ACONST_NULL);
0863: break;
0864: case Type.ARRAY:
0865: cv.visitInsn(ACONST_NULL);
0866: break;
0867: case Type.INT:
0868: cv.visitInsn(ICONST_0);
0869: break;
0870: case Type.LONG:
0871: cv.visitInsn(LCONST_0);
0872: break;
0873: case Type.SHORT:
0874: cv.visitInsn(ICONST_0);
0875: break;
0876: case Type.FLOAT:
0877: cv.visitInsn(FCONST_0);
0878: break;
0879: case Type.DOUBLE:
0880: cv.visitInsn(DCONST_0);
0881: break;
0882: case Type.BYTE:
0883: cv.visitInsn(ICONST_0);
0884: break;
0885: case Type.BOOLEAN:
0886: cv.visitInsn(ICONST_0);
0887: break;
0888: case Type.CHAR:
0889: cv.visitInsn(ICONST_0);
0890: break;
0891: }
0892: }
0893:
0894: /**
0895: * Adds a string and inserts null if the string is null.
0896: *
0897: * @param cv
0898: * @param value
0899: */
0900: public static void addNullableString(final MethodVisitor cv,
0901: final String value) {
0902: if (value == null) {
0903: cv.visitInsn(ACONST_NULL);
0904: } else {
0905: cv.visitLdcInsn(value);
0906: }
0907: }
0908:
0909: /**
0910: * Compute the register depth, based on an array of types (long, double = 2 bytes address)
0911: *
0912: * @param typesOnStack
0913: * @return depth of the stack
0914: */
0915: public static int getRegisterDepth(final Type[] typesOnStack) {
0916: int depth = 0;
0917: for (int i = 0; i < typesOnStack.length; i++) {
0918: depth += typesOnStack[i].getSize();
0919: }
0920: return depth;
0921: }
0922:
0923: /**
0924: * Compute the index on the stack of a given argument based on its index in the signature
0925: *
0926: * @param typesOnStack
0927: * @param typeIndex
0928: * @return
0929: */
0930: public static int getRegisterIndexOf(final Type[] typesOnStack,
0931: final int typeIndex) {
0932: int depth = 0;
0933: for (int i = 0; i < typeIndex; i++) {
0934: depth += typesOnStack[i].getSize();
0935: }
0936: return depth;
0937: }
0938:
0939: /**
0940: * Compute the index on the signature of a given argument based on its index as if it was on the stack
0941: * where the stack would start at the first argument
0942: *
0943: * @param typesOnStack
0944: * @param registerIndex
0945: * @return
0946: */
0947: public static int getTypeIndexOf(final Type[] typesOnStack,
0948: final int registerIndex) {
0949: for (int i = 0; i < typesOnStack.length; i++) {
0950: int presumedRegisterIndex = getRegisterIndexOf(
0951: typesOnStack, i);
0952: if (registerIndex == presumedRegisterIndex) {
0953: return i;
0954: }
0955: }
0956: return -1;
0957: }
0958:
0959: /**
0960: * Checks if the Type is a primitive.
0961: *
0962: * @param returnType
0963: * @return TRUE/FALSE
0964: */
0965: public static boolean isPrimitive(final Type returnType) {
0966: if (returnType.getSort() == Type.INT
0967: || returnType.getSort() == Type.SHORT
0968: || returnType.getSort() == Type.LONG
0969: || returnType.getSort() == Type.FLOAT
0970: || returnType.getSort() == Type.DOUBLE
0971: || returnType.getSort() == Type.BYTE
0972: || returnType.getSort() == Type.CHAR
0973: || returnType.getSort() == Type.BOOLEAN
0974: || returnType.getSort() == Type.VOID) {
0975: return true;
0976: } else {
0977: return false;
0978: }
0979: }
0980:
0981: /**
0982: * Increments the index (takes doubles and longs in to account).
0983: *
0984: * @param index
0985: * @param type
0986: * @return the incremented index
0987: */
0988: public static int incrementIndex(int index, final Type type) {
0989: return index += type.getSize();
0990: }
0991:
0992: /**
0993: * Returns the descriptor corresponding to the given method info.
0994: * Adapted from ASM Type.getMethodDescriptor(<Method>)
0995: *
0996: * @param method
0997: * @return the descriptor of the given method.
0998: */
0999: public static String getMethodDescriptor(final MethodInfo method) {
1000: ClassInfo[] parameters = method.getParameterTypes();
1001: StringBuffer buf = new StringBuffer();
1002: buf.append('(');
1003: for (int i = 0; i < parameters.length; ++i) {
1004: getClassDescriptor(buf, parameters[i]);
1005: }
1006: buf.append(')');
1007: getClassDescriptor(buf, method.getReturnType());
1008: return buf.toString();
1009: }
1010:
1011: /**
1012: * Returns the descriptor corresponding to the given constructor info.
1013: *
1014: * @param constructor
1015: * @return the descriptor of the given constructor.
1016: */
1017: public static String getConstructorDescriptor(
1018: final ConstructorInfo constructor) {
1019: ClassInfo[] parameters = constructor.getParameterTypes();
1020: StringBuffer buf = new StringBuffer();
1021: buf.append('(');
1022: for (int i = 0; i < parameters.length; ++i) {
1023: getClassDescriptor(buf, parameters[i]);
1024: }
1025: buf.append(')');
1026: getClassDescriptor(buf, VOID);
1027: return buf.toString();
1028: }
1029:
1030: /**
1031: * Returns the descriptor corresponding to the given field info.
1032: *
1033: * @param field
1034: * @return the descriptor of the given field.
1035: */
1036: public static String getFieldDescriptor(final FieldInfo field) {
1037: return getClassDescriptor(field.getType());
1038: }
1039:
1040: /**
1041: * Returns the descriptor corresponding to the given Java type.
1042: * Adapted from ASM Type.getClassDescriptor(Class)
1043: * <p/>
1044: * TODO remove the delegation to private method
1045: *
1046: * @param c an object class, a primitive class or an array class.
1047: * @return the descriptor corresponding to the given class.
1048: */
1049: public static String getClassDescriptor(final ClassInfo c) {
1050: StringBuffer buf = new StringBuffer();
1051: getClassDescriptor(buf, c);
1052: return buf.toString();
1053: }
1054:
1055: /**
1056: * Appends the descriptor of the given class to the given string buffer.
1057: * Adapted from ASM Type.getClassDescriptor(StringBuffer, Class)
1058: *
1059: * @param buf the string buffer to which the descriptor must be appended.
1060: * @param klass the class whose descriptor must be computed.
1061: */
1062: private static void getClassDescriptor(final StringBuffer buf,
1063: final ClassInfo klass) {
1064: ClassInfo d = klass;
1065: while (true) {
1066: if (d.isPrimitive()) {
1067: char car;
1068: if (d.equals(INTEGER)) {
1069: car = 'I';
1070: } else if (d.equals(VOID)) {
1071: car = 'V';
1072: } else if (d.equals(BOOLEAN)) {
1073: car = 'Z';
1074: } else if (d.equals(BYTE)) {
1075: car = 'B';
1076: } else if (d.equals(CHARACTER)) {
1077: car = 'C';
1078: } else if (d.equals(SHORT)) {
1079: car = 'S';
1080: } else if (d.equals(DOUBLE)) {
1081: car = 'D';
1082: } else if (d.equals(FLOAT)) {
1083: car = 'F';
1084: } else if (d.equals(LONG)) {
1085: car = 'J';
1086: } else {
1087: throw new Error("should not happen");
1088: }
1089: buf.append(car);
1090: return;
1091: } else if (d.isArray()) {
1092: buf.append('[');
1093: d = d.getComponentType();
1094: } else {
1095: buf.append('L');
1096: String name = d.getName();
1097: int len = name.length();
1098: for (int i = 0; i < len; ++i) {
1099: char car = name.charAt(i);
1100: buf.append(car == '.' ? '/' : car);
1101: }
1102: buf.append(';');
1103: return;
1104: }
1105: }
1106: }
1107:
1108: /**
1109: * Returns the Java types corresponding to the argument types of the given
1110: * method.
1111: * Adapted from ASM getArgumentTypes(Method)
1112: *
1113: * @param method a method.
1114: * @return the Java types corresponding to the argument types of the given
1115: * method.
1116: */
1117: public static Type[] getArgumentTypes(final MethodInfo method) {
1118: ClassInfo[] classes = method.getParameterTypes();
1119: Type[] types = new Type[classes.length];
1120: for (int i = classes.length - 1; i >= 0; --i) {
1121: types[i] = getType(classes[i]);
1122: }
1123: return types;
1124: }
1125:
1126: /**
1127: * Returns the Java type corresponding to the given class.
1128: * Adapted from ASM getType(Class)
1129: *
1130: * @param c a class.
1131: * @return the Java type corresponding to the given class.
1132: */
1133: public static Type getType(final ClassInfo c) {
1134: if (c.isPrimitive()) {
1135: if (c.equals(INTEGER)) {
1136: return Type.INT_TYPE;
1137: } else if (c.equals(VOID)) {
1138: return Type.VOID_TYPE;
1139: } else if (c.equals(BOOLEAN)) {
1140: return Type.BOOLEAN_TYPE;
1141: } else if (c.equals(BYTE)) {
1142: return Type.BYTE_TYPE;
1143: } else if (c.equals(CHARACTER)) {
1144: return Type.CHAR_TYPE;
1145: } else if (c.equals(SHORT)) {
1146: return Type.SHORT_TYPE;
1147: } else if (c.equals(DOUBLE)) {
1148: return Type.DOUBLE_TYPE;
1149: } else if (c.equals(FLOAT)) {
1150: return Type.FLOAT_TYPE;
1151: } else if (c.equals(LONG)) {
1152: return Type.LONG_TYPE;
1153: } else {
1154: throw new Error("should not happen");
1155: }
1156: } else {
1157: return Type.getType(getClassDescriptor(c));
1158: }
1159: }
1160: }
|