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