/************************************************************************************** * Copyright (c) Jonas Bon�r, Alexandre Vasseur. All rights reserved. * * http://aspectwerkz.codehaus.org * * ---------------------------------------------------------------------------------- * * The software in this package is published under the terms of the LGPL license * * a copy of which has been included with this distribution in the license.txt file. * **************************************************************************************/ package org.codehaus.aspectwerkz.transform.inlining; import org.codehaus.aspectwerkz.util.ContextClassLoader; import org.codehaus.aspectwerkz.reflect.ClassInfo; import org.codehaus.aspectwerkz.reflect.MethodInfo; import org.codehaus.aspectwerkz.reflect.ConstructorInfo; import org.codehaus.aspectwerkz.reflect.FieldInfo; import org.codehaus.aspectwerkz.reflect.impl.java.JavaClassInfo; import org.codehaus.aspectwerkz.transform.TransformationConstants; import org.codehaus.aspectwerkz.transform.AspectWerkzPreProcessor; import org.codehaus.aspectwerkz.exception.WrappedRuntimeException; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; import org.objectweb.asm.Label; import org.objectweb.asm.ClassReader; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.lang.reflect.InvocationTargetException; import java.security.ProtectionDomain; import java.security.AccessController; import java.security.PrivilegedAction; /** * Helper class with utility methods for the ASM library. * * @author <a href="mailto:jboner@codehaus.org">Jonas Bon�r </a> * @author <a href="mailto:alex@gnilux.com">Alexandre Vasseur </a> */ public class AsmHelper implements TransformationConstants { public final static ClassInfo INTEGER = JavaClassInfo.getClassInfo(Integer.TYPE); public final static ClassInfo VOID = JavaClassInfo.getClassInfo(Void.TYPE); public final static ClassInfo BOOLEAN = JavaClassInfo.getClassInfo(Boolean.TYPE); public final static ClassInfo BYTE = JavaClassInfo.getClassInfo(Byte.TYPE); public final static ClassInfo CHARACTER = JavaClassInfo.getClassInfo(Character.TYPE); public final static ClassInfo SHORT = JavaClassInfo.getClassInfo(Short.TYPE); public final static ClassInfo DOUBLE = JavaClassInfo.getClassInfo(Double.TYPE); public final static ClassInfo FLOAT = JavaClassInfo.getClassInfo(Float.TYPE); public final static ClassInfo LONG = JavaClassInfo.getClassInfo(Long.TYPE); private static Class CLASS_LOADER; private static Method CLASS_LOADER_DEFINE; private static final ProtectionDomain PROTECTION_DOMAIN; static { PROTECTION_DOMAIN = (ProtectionDomain)AccessController.doPrivileged(new PrivilegedAction() { public Object run() { return AsmHelper.class.getProtectionDomain(); } }); AccessController.doPrivileged(new PrivilegedAction() { public Object run() { try { CLASS_LOADER = Class.forName(CLASS_LOADER_REFLECT_CLASS_NAME); CLASS_LOADER_DEFINE = CLASS_LOADER.getDeclaredMethod( DEFINE_CLASS_METHOD_NAME, new Class[]{ String.class, byte[].class, int.class, int.class, ProtectionDomain.class } ); CLASS_LOADER_DEFINE.setAccessible(true); } catch (Throwable t) { throw new Error(t.toString()); } return null; } }); } /** * A boolean to check if we have a J2SE 5 support */ public final static boolean IS_JAVA_5; public static int JAVA_VERSION = V1_3; static { Class annotation = null; try { annotation = Class.forName("java.lang.annotation.Annotation"); ClassReader cr = new ClassReader("java.lang.annotation.Annotation"); JAVA_VERSION = V1_5; } catch (Throwable e) { annotation = null; } if (annotation == null) { IS_JAVA_5 = false; } else { IS_JAVA_5 = true; } } /** * Factory method for ASM ClassWriter and J2SE 5 support * See http://www.objectweb.org/wws/arc/asm/2004-08/msg00005.html * * @param computeMax * @return */ public static ClassWriter newClassWriter(boolean computeMax) { return new ClassWriter(computeMax, true); } /** * Gets the argument types for a constructor. <p/>Parts of code in this method is taken from the ASM codebase. * * @param constructor * @return the ASM argument types for the constructor */ public static Type[] getArgumentTypes(final Constructor constructor) { Class[] classes = constructor.getParameterTypes(); Type[] types = new Type[classes.length]; for (int i = classes.length - 1; i >= 0; --i) { types[i] = Type.getType(classes[i]); } return types; } /** * Dumps an ASM class to disk. * * @param dumpDir * @param className * @param bytes * @throws java.io.IOException */ public static void dumpClass(final String dumpDir, final String className, final byte[] bytes) throws IOException { final File dir; if (className.lastIndexOf('/')>0) { dir = new File(dumpDir + File.separator + className.substring(0, className.lastIndexOf('/'))); } else { dir = new File(dumpDir); } dir.mkdirs(); String fileName = dumpDir + File.separator + className + ".class"; if (AspectWerkzPreProcessor.VERBOSE) { System.out.println("AW INFO: dumping class " + className + " to " + dumpDir); } FileOutputStream os = new FileOutputStream(fileName); os.write(bytes); os.close(); } /** * Dumps an ASM class to disk. * * @param dumpDir * @param className * @param cw * @throws java.io.IOException */ public static void dumpClass(final String dumpDir, final String className, final ClassWriter cw) throws IOException { String base = ""; if (className.lastIndexOf('/')>0) { base = className.substring(0, className.lastIndexOf('/')); } File dir = new File(dumpDir + File.separator + base); dir.mkdirs(); String fileName = dumpDir + File.separator + className + ".class"; if (AspectWerkzPreProcessor.VERBOSE) { System.out.println("AW INFO: dumping class " + className + " to " + dumpDir); } FileOutputStream os = new FileOutputStream(fileName); os.write(cw.toByteArray()); os.close(); } /** * Adds a class to a class loader and loads it. * * @param loader the class loader (if null the context class loader will be used) * @param bytes the bytes for the class * @param name the name of the class * @return the class */ public static Class defineClass(ClassLoader loader, final byte[] bytes, final String name) { String className = name.replace('/', '.'); try { if (loader == null) { loader = ContextClassLoader.getLoader(); } // TODO: what if we don't have rights to set this method to // accessible on this specific CL? Load it in System CL? //CLASS_LOADER_DEFINE.setAccessible(true); Object[] args = new Object[]{ className, bytes, new Integer(0), new Integer(bytes.length), PROTECTION_DOMAIN }; Class clazz = (Class) CLASS_LOADER_DEFINE.invoke(loader, args); //CLASS_LOADER_DEFINE.setAccessible(false); return clazz; } catch (InvocationTargetException e) { // JIT failovering for Thread concurrency // AW-222 (Tomcat and WLS were reported for AW-222) if (e.getTargetException() instanceof LinkageError) { Class failoverJoinpointClass = forName(loader, className); if (failoverJoinpointClass != null) { return failoverJoinpointClass; } } throw new WrappedRuntimeException(e); } catch (Exception e) { throw new WrappedRuntimeException(e); } } /** * Tries to load a class if unsuccessful returns null. * * @param loader the class loader * @param name the name of the class * @return the class */ public static Class forName(ClassLoader loader, final String name) { String className = name.replace('/', '.'); try { if (loader == null) { loader = ContextClassLoader.getLoader(); } // Use Class.forName since loader.loadClass fails on JBoss UCL return Class.forName(className, false, loader); } catch (Exception e) { return null; } } /** * Calculates the method hash. The computation MUST be the same as in ReflectHelper, thus we switch back the names * to Java style. Note that for array type, Java.reflect is using "[Lpack.foo;" style unless primitive. * * @param name * @param desc * @return */ public static int calculateMethodHash(final String name, final String desc) { int hash = 17; hash = (37 * hash) + name.replace('/', '.').hashCode(); Type[] argumentTypes = Type.getArgumentTypes(desc); for (int i = 0; i < argumentTypes.length; i++) { hash = (37 * hash) + AsmHelper.convertTypeDescToReflectDesc(argumentTypes[i].getDescriptor()).hashCode(); } return hash; } /** * Calculates the constructor hash. * * @param desc * @return */ public static int calculateConstructorHash(final String desc) { return AsmHelper.calculateMethodHash(INIT_METHOD_NAME, desc); } // /** // * Calculates the joinpoint constructor call/execution hash. // * It depends on the callee class name else we won't be able to distinguish joinpoint on different targets. // * // * @param declaringClassName // * @param desc // * @return // */ // public static int calculateConstructorJoinPointHash(final String declaringClassName, final String desc) { // int hash = 17; // hash = (37 * hash) + AsmHelper.calculateClassHash("L" + declaringClassName + ";"); // hash = (37 * hash) + AsmHelper.calculateMethodHash(INIT_METHOD_NAME, desc); // return hash; // } /** * Calculates the field hash. * * @param name * @param desc * @return */ public static int calculateFieldHash(final String name, final String desc) { int hash = 17; hash = (37 * hash) + name.hashCode(); Type type = Type.getType(desc); hash = (37 * hash) + AsmHelper.convertTypeDescToReflectDesc(type.getDescriptor()).hashCode(); return hash; } /** * Calculates the class hash. * * @param declaringType * @return */ public static int calculateClassHash(final String declaringType) { return AsmHelper.convertTypeDescToReflectDesc(declaringType).hashCode(); } /** * Converts an internal Java array type name ([Lblabla) to the a the format used by the expression matcher * (blabla[]) * * @param typeName is type name * @return */ public static String convertArrayTypeName(final String typeName) { int index = typeName.lastIndexOf('['); if (index != -1) { StringBuffer arrayType = new StringBuffer(); if (typeName.endsWith("I")) { arrayType.append("int"); } else if (typeName.endsWith("J")) { arrayType.append("long"); } else if (typeName.endsWith("S")) { arrayType.append("short"); } else if (typeName.endsWith("F")) { arrayType.append("float"); } else if (typeName.endsWith("D")) { arrayType.append("double"); } else if (typeName.endsWith("Z")) { arrayType.append("boolean"); } else if (typeName.endsWith("C")) { arrayType.append("char"); } else if (typeName.endsWith("B")) { arrayType.append("byte"); } else { arrayType.append(typeName.substring(index + 2, typeName.length() - 1)); } for (int i = 0; i < (index + 1); i++) { arrayType.append("[]"); } return arrayType.toString(); } else { return typeName; } } /** * Converts an ASM type descriptor" (I, [I, [Ljava/lang/String;, Ljava/lang/String;) to a Java.reflect one (int, [I, * [Ljava.lang.String;, java.lang.String) * * @param typeDesc * @return the Java.reflect string representation */ public static String convertTypeDescToReflectDesc(final String typeDesc) { if (typeDesc == null) { return null; } String result = null; // change needed for array types only if (typeDesc.startsWith("[")) { result = typeDesc; } else { // support for single dimension type if (typeDesc.startsWith("L") && typeDesc.endsWith(";")) { result = typeDesc.substring(1, typeDesc.length() - 1); } else { // primitive type, single dimension if (typeDesc.equals("I")) { result = "int"; } else if (typeDesc.equals("J")) { result = "long"; } else if (typeDesc.equals("S")) { result = "short"; } else if (typeDesc.equals("F")) { result = "float"; } else if (typeDesc.equals("D")) { result = "double"; } else if (typeDesc.equals("Z")) { result = "boolean"; } else if (typeDesc.equals("C")) { result = "char"; } else if (typeDesc.equals("B")) { result = "byte"; } else { throw new RuntimeException("unknown primitive type " + typeDesc); } } } return result.replace('/', '.'); } /** * Converts a java reflect type desc to ASM type desc. * * @param desc * @return */ public static String convertReflectDescToTypeDesc(final String desc) { if (desc == null) { return null; } String typeDesc = desc; int dimension = 0; char[] arr = desc.toCharArray(); for (int i = 0; i < arr.length; i++) { if (arr[i] == ']') { dimension++; } } typeDesc = desc.substring(0, desc.length() - dimension * 2); if (typeDesc.equals("int")) { typeDesc = "I"; } else if (typeDesc.equals("short")) { typeDesc = "S"; } else if (typeDesc.equals("long")) { typeDesc = "J"; } else if (typeDesc.equals("float")) { typeDesc = "F"; } else if (typeDesc.equals("double")) { typeDesc = "D"; } else if (typeDesc.equals("byte")) { typeDesc = "B"; } else if (typeDesc.equals("char")) { typeDesc = "C"; } else if (typeDesc.equals("boolean")) { typeDesc = "Z"; } else { typeDesc = 'L' + typeDesc + ';'; } for (int i = 0; i < dimension; i++) { typeDesc = '[' + typeDesc; } return typeDesc.replace('.', '/'); } /** * Adds the correct return statement. * * @param mv * @param type */ public static void addReturnStatement(final MethodVisitor mv, final Type type) { switch (type.getSort()) { case Type.VOID: mv.visitInsn(RETURN); break; case Type.LONG: mv.visitInsn(LRETURN); break; case Type.INT: mv.visitInsn(IRETURN); break; case Type.SHORT: mv.visitInsn(IRETURN); break; case Type.DOUBLE: mv.visitInsn(DRETURN); break; case Type.FLOAT: mv.visitInsn(FRETURN); break; case Type.BYTE: mv.visitInsn(IRETURN); break; case Type.BOOLEAN: mv.visitInsn(IRETURN); break; case Type.CHAR: mv.visitInsn(IRETURN); break; case Type.ARRAY: mv.visitInsn(ARETURN); break; case Type.OBJECT: mv.visitInsn(ARETURN); break; } } /** * Loads argument types. * * @param mv * @param argumentTypes */ public static void loadArgumentTypes(final MethodVisitor mv, final Type[] argumentTypes, final boolean staticMethod) { int index; if (staticMethod) { index = 0; } else { index = 1; } for (int i = 0; i < argumentTypes.length; i++) { index = loadType(mv, index, argumentTypes[i]); } } /** * Loads a type. * * @param cv * @param index * @param type * @return the incremented index */ public static int loadType(final MethodVisitor cv, int index, final Type type) { switch (type.getSort()) { case Type.LONG: cv.visitVarInsn(LLOAD, index++); index++; break; case Type.INT: cv.visitVarInsn(ILOAD, index++); break; case Type.SHORT: cv.visitVarInsn(ILOAD, index++); break; case Type.DOUBLE: cv.visitVarInsn(DLOAD, index++); index++; break; case Type.FLOAT: cv.visitVarInsn(FLOAD, index++); break; case Type.BYTE: cv.visitVarInsn(ILOAD, index++); break; case Type.BOOLEAN: cv.visitVarInsn(ILOAD, index++); break; case Type.CHAR: cv.visitVarInsn(ILOAD, index++); break; case Type.ARRAY: cv.visitVarInsn(ALOAD, index++); break; case Type.OBJECT: cv.visitVarInsn(ALOAD, index++); break; } return index; } /** * Stores a type. * * @param cv * @param index * @param type * @return the incremented index */ public static int storeType(final MethodVisitor cv, int index, final Type type) { switch (type.getSort()) { case Type.VOID: break; case Type.LONG: cv.visitVarInsn(LSTORE, index++); index++; break; case Type.INT: cv.visitVarInsn(ISTORE, index++); break; case Type.SHORT: cv.visitVarInsn(ISTORE, index++); break; case Type.DOUBLE: cv.visitVarInsn(DSTORE, index++); index++; break; case Type.FLOAT: cv.visitVarInsn(FSTORE, index++); break; case Type.BYTE: cv.visitVarInsn(ISTORE, index++); break; case Type.BOOLEAN: cv.visitVarInsn(ISTORE, index++); break; case Type.CHAR: cv.visitVarInsn(ISTORE, index++); break; case Type.ARRAY: cv.visitVarInsn(ASTORE, index++); break; case Type.OBJECT: cv.visitVarInsn(ASTORE, index++); break; } return index; } /** * Push the string on the stack. Deal when case where string is null. * * @param cv * @param s */ public static void loadStringConstant(final MethodVisitor cv, final String s) { if (s != null) { cv.visitLdcInsn(s); } else { cv.visitInsn(ACONST_NULL); } } /** * Creates and adds the correct parameter index for integer types. * * @param cv * @param index */ public static void loadIntegerConstant(final MethodVisitor cv, final int index) { switch (index) { case 0: cv.visitInsn(ICONST_0); break; case 1: cv.visitInsn(ICONST_1); break; case 2: cv.visitInsn(ICONST_2); break; case 3: cv.visitInsn(ICONST_3); break; case 4: cv.visitInsn(ICONST_4); break; case 5: cv.visitInsn(ICONST_5); break; default: cv.visitIntInsn(BIPUSH, index); break; } } /** * Prepares the wrapping or a primitive type. * * @param cv * @param type */ public static void prepareWrappingOfPrimitiveType(final MethodVisitor cv, final Type type) { switch (type.getSort()) { case Type.SHORT: cv.visitTypeInsn(NEW, SHORT_CLASS_NAME); cv.visitInsn(DUP); break; case Type.INT: cv.visitTypeInsn(NEW, INTEGER_CLASS_NAME); cv.visitInsn(DUP); break; case Type.LONG: cv.visitTypeInsn(NEW, LONG_CLASS_NAME); cv.visitInsn(DUP); break; case Type.FLOAT: cv.visitTypeInsn(NEW, FLOAT_CLASS_NAME); cv.visitInsn(DUP); break; case Type.DOUBLE: cv.visitTypeInsn(NEW, DOUBLE_CLASS_NAME); cv.visitInsn(DUP); break; case Type.BYTE: cv.visitTypeInsn(NEW, BYTE_CLASS_NAME); cv.visitInsn(DUP); break; case Type.BOOLEAN: cv.visitTypeInsn(NEW, BOOLEAN_CLASS_NAME); cv.visitInsn(DUP); break; case Type.CHAR: cv.visitTypeInsn(NEW, CHARACTER_CLASS_NAME); cv.visitInsn(DUP); break; } } /** * Handles the wrapping of a primitive type. * * @param cv * @param type */ public static void wrapPrimitiveType(final MethodVisitor cv, final Type type) { switch (type.getSort()) { case Type.VOID: cv.visitInsn(ACONST_NULL); break; case Type.SHORT: cv.visitMethodInsn( INVOKESPECIAL, SHORT_CLASS_NAME, INIT_METHOD_NAME, SHORT_CLASS_INIT_METHOD_SIGNATURE ); break; case Type.INT: cv.visitMethodInsn( INVOKESPECIAL, INTEGER_CLASS_NAME, INIT_METHOD_NAME, INTEGER_CLASS_INIT_METHOD_SIGNATURE ); break; case Type.LONG: cv.visitMethodInsn(INVOKESPECIAL, LONG_CLASS_NAME, INIT_METHOD_NAME, LONG_CLASS_INIT_METHOD_SIGNATURE); break; case Type.FLOAT: cv.visitMethodInsn( INVOKESPECIAL, FLOAT_CLASS_NAME, INIT_METHOD_NAME, FLOAT_CLASS_INIT_METHOD_SIGNATURE ); break; case Type.DOUBLE: cv.visitMethodInsn( INVOKESPECIAL, DOUBLE_CLASS_NAME, INIT_METHOD_NAME, DOUBLE_CLASS_INIT_METHOD_SIGNATURE ); break; case Type.BYTE: cv.visitMethodInsn(INVOKESPECIAL, BYTE_CLASS_NAME, INIT_METHOD_NAME, BYTE_CLASS_INIT_METHOD_SIGNATURE); break; case Type.BOOLEAN: cv.visitMethodInsn( INVOKESPECIAL, BOOLEAN_CLASS_NAME, INIT_METHOD_NAME, BOOLEAN_CLASS_INIT_METHOD_SIGNATURE ); break; case Type.CHAR: cv.visitMethodInsn( INVOKESPECIAL, CHARACTER_CLASS_NAME, INIT_METHOD_NAME, CHARACTER_CLASS_INIT_METHOD_SIGNATURE ); break; } } /** * Handles the unwrapping of a type, unboxing of primitives and casting to the correct object type. * Takes care of null value replaced by default primitive value. * <pre>(obj==null)?0L:((Long)obj).longValue();</pre> * * @param cv * @param type */ public static void unwrapType(final MethodVisitor cv, final Type type) { // void, object and array type handling switch (type.getSort()) { case Type.OBJECT: String objectTypeName = type.getClassName().replace('.', '/'); cv.visitTypeInsn(CHECKCAST, objectTypeName); return; case Type.ARRAY: cv.visitTypeInsn(CHECKCAST, type.getDescriptor()); return; case Type.VOID: return; } // primitive type handling Label l0If = new Label(); Label l1End = new Label(); // if != null cv.visitInsn(DUP); cv.visitJumpInsn(IFNONNULL, l0If); // else, default value cv.visitInsn(POP); addDefaultValue(cv, type); // end cv.visitJumpInsn(GOTO, l1End); // if body cv.visitLabel(l0If); switch (type.getSort()) { case Type.SHORT: cv.visitTypeInsn(CHECKCAST, SHORT_CLASS_NAME); cv.visitMethodInsn( INVOKEVIRTUAL, SHORT_CLASS_NAME, SHORT_VALUE_METHOD_NAME, SHORT_VALUE_METHOD_SIGNATURE ); break; case Type.INT: cv.visitTypeInsn(CHECKCAST, INTEGER_CLASS_NAME); cv.visitMethodInsn( INVOKEVIRTUAL, INTEGER_CLASS_NAME, INT_VALUE_METHOD_NAME, INT_VALUE_METHOD_SIGNATURE ); break; case Type.LONG: cv.visitTypeInsn(CHECKCAST, LONG_CLASS_NAME); cv.visitMethodInsn( INVOKEVIRTUAL, LONG_CLASS_NAME, LONG_VALUE_METHOD_NAME, LONG_VALUE_METHOD_SIGNATURE ); break; case Type.FLOAT: cv.visitTypeInsn(CHECKCAST, FLOAT_CLASS_NAME); cv.visitMethodInsn( INVOKEVIRTUAL, FLOAT_CLASS_NAME, FLOAT_VALUE_METHOD_NAME, FLOAT_VALUE_METHOD_SIGNATURE ); break; case Type.DOUBLE: cv.visitTypeInsn(CHECKCAST, DOUBLE_CLASS_NAME); cv.visitMethodInsn( INVOKEVIRTUAL, DOUBLE_CLASS_NAME, DOUBLE_VALUE_METHOD_NAME, DOUBLE_VALUE_METHOD_SIGNATURE ); break; case Type.BYTE: cv.visitTypeInsn(CHECKCAST, BYTE_CLASS_NAME); cv.visitMethodInsn( INVOKEVIRTUAL, BYTE_CLASS_NAME, BYTE_VALUE_METHOD_NAME, BYTE_VALUE_METHOD_SIGNATURE ); break; case Type.BOOLEAN: cv.visitTypeInsn(CHECKCAST, BOOLEAN_CLASS_NAME); cv.visitMethodInsn( INVOKEVIRTUAL, BOOLEAN_CLASS_NAME, BOOLEAN_VALUE_METHOD_NAME, BOOLEAN_VALUE_METHOD_SIGNATURE ); break; case Type.CHAR: cv.visitTypeInsn(CHECKCAST, CHARACTER_CLASS_NAME); cv.visitMethodInsn( INVOKEVIRTUAL, CHARACTER_CLASS_NAME, CHAR_VALUE_METHOD_NAME, CHAR_VALUE_METHOD_SIGNATURE ); break; } cv.visitLabel(l1End); } /** * Adds the default value for a type. * * @param cv * @param type */ public static void addDefaultValue(final MethodVisitor cv, final Type type) { switch (type.getSort()) { case Type.OBJECT: cv.visitInsn(ACONST_NULL); break; case Type.ARRAY: cv.visitInsn(ACONST_NULL); break; case Type.INT: cv.visitInsn(ICONST_0); break; case Type.LONG: cv.visitInsn(LCONST_0); break; case Type.SHORT: cv.visitInsn(ICONST_0); break; case Type.FLOAT: cv.visitInsn(FCONST_0); break; case Type.DOUBLE: cv.visitInsn(DCONST_0); break; case Type.BYTE: cv.visitInsn(ICONST_0); break; case Type.BOOLEAN: cv.visitInsn(ICONST_0); break; case Type.CHAR: cv.visitInsn(ICONST_0); break; } } /** * Adds a string and inserts null if the string is null. * * @param cv * @param value */ public static void addNullableString(final MethodVisitor cv, final String value) { if (value == null) { cv.visitInsn(ACONST_NULL); } else { cv.visitLdcInsn(value); } } /** * Compute the register depth, based on an array of types (long, double = 2 bytes address) * * @param typesOnStack * @return depth of the stack */ public static int getRegisterDepth(final Type[] typesOnStack) { int depth = 0; for (int i = 0; i < typesOnStack.length; i++) { Type type = typesOnStack[i]; depth++; switch (type.getSort()) { case Type.LONG: depth++; break; case Type.DOUBLE: depth++; break; } } return depth; } /** * Compute the index on the stack of a given argument based on its index in the signature * * @param typesOnStack * @param typeIndex * @return */ public static int getRegisterIndexOf(final Type[] typesOnStack, final int typeIndex) { int depth = 0; for (int i = 0; i < typeIndex; i++) { Type type = typesOnStack[i]; depth++; switch (type.getSort()) { case Type.LONG: depth++; break; case Type.DOUBLE: depth++; break; } } return depth; } /** * Compute the index on the signature of a given argument based on its index as if it was on the stack * where the stack would start at the first argument * * @param typesOnStack * @param registerIndex * @return */ public static int getTypeIndexOf(final Type[] typesOnStack, final int registerIndex) { for (int i = 0; i < typesOnStack.length; i++) { int presumedRegisterIndex = getRegisterIndexOf(typesOnStack, i); if (registerIndex == presumedRegisterIndex) { return i; } } return -1; } /** * Checks if the Type is a primitive. * * @param returnType * @return TRUE/FALSE */ public static boolean isPrimitive(final Type returnType) { if (returnType.getSort() == Type.INT || returnType.getSort() == Type.SHORT || returnType.getSort() == Type.LONG || returnType.getSort() == Type.FLOAT || returnType.getSort() == Type.DOUBLE || returnType.getSort() == Type.BYTE || returnType.getSort() == Type.CHAR || returnType.getSort() == Type.BOOLEAN || returnType.getSort() == Type.VOID) { return true; } else { return false; } } /** * Increments the index (takes doubles and longs in to account). * * @param index * @param type * @return the incremented index */ public static int incrementIndex(int index, final Type type) { if (type.getSort() == Type.LONG || type.getSort() == Type.DOUBLE) { index += 2; } else { index++; } return index; } /** * Returns the descriptor corresponding to the given method info. * Adapted from ASM Type.getMethodDescriptor(<Method>) * * @param method * @return the descriptor of the given method. */ public static String getMethodDescriptor(final MethodInfo method) { ClassInfo[] parameters = method.getParameterTypes(); StringBuffer buf = new StringBuffer(); buf.append('('); for (int i = 0; i < parameters.length; ++i) { getClassDescriptor(buf, parameters[i]); } buf.append(')'); getClassDescriptor(buf, method.getReturnType()); return buf.toString(); } /** * Returns the descriptor corresponding to the given constructor info. * * @param constructor * @return the descriptor of the given constructor. */ public static String getConstructorDescriptor(final ConstructorInfo constructor) { ClassInfo[] parameters = constructor.getParameterTypes(); StringBuffer buf = new StringBuffer(); buf.append('('); for (int i = 0; i < parameters.length; ++i) { getClassDescriptor(buf, parameters[i]); } buf.append(')'); getClassDescriptor(buf, VOID); return buf.toString(); } /** * Returns the descriptor corresponding to the given field info. * * @param field * @return the descriptor of the given field. */ public static String getFieldDescriptor(final FieldInfo field) { return getClassDescriptor(field.getType()); } /** * Returns the descriptor corresponding to the given Java type. * Adapted from ASM Type.getClassDescriptor(Class) * <p/> * TODO remove the delegation to private method * * @param c an object class, a primitive class or an array class. * @return the descriptor corresponding to the given class. */ public static String getClassDescriptor(final ClassInfo c) { StringBuffer buf = new StringBuffer(); getClassDescriptor(buf, c); return buf.toString(); } /** * Appends the descriptor of the given class to the given string buffer. * Adapted from ASM Type.getClassDescriptor(StringBuffer, Class) * * @param buf the string buffer to which the descriptor must be appended. * @param klass the class whose descriptor must be computed. */ private static void getClassDescriptor(final StringBuffer buf, final ClassInfo klass) { ClassInfo d = klass; while (true) { if (d.isPrimitive()) { char car; if (d.equals(INTEGER)) { car = 'I'; } else if (d.equals(VOID)) { car = 'V'; } else if (d.equals(BOOLEAN)) { car = 'Z'; } else if (d.equals(BYTE)) { car = 'B'; } else if (d.equals(CHARACTER)) { car = 'C'; } else if (d.equals(SHORT)) { car = 'S'; } else if (d.equals(DOUBLE)) { car = 'D'; } else if (d.equals(FLOAT)) { car = 'F'; } else if (d.equals(LONG)) { car = 'J'; } else { throw new Error("should not happen"); } buf.append(car); return; } else if (d.isArray()) { buf.append('['); d = d.getComponentType(); } else { buf.append('L'); String name = d.getName(); int len = name.length(); for (int i = 0; i < len; ++i) { char car = name.charAt(i); buf.append(car == '.' ? '/' : car); } buf.append(';'); return; } } } /** * Returns the Java types corresponding to the argument types of the given * method. * Adapted from ASM getArgumentTypes(Method) * * @param method a method. * @return the Java types corresponding to the argument types of the given * method. */ public static Type[] getArgumentTypes(final MethodInfo method) { ClassInfo[] classes = method.getParameterTypes(); Type[] types = new Type[classes.length]; for (int i = classes.length - 1; i >= 0; --i) { types[i] = getType(classes[i]); } return types; } /** * Returns the Java type corresponding to the given class. * Adapted from ASM getType(Class) * * @param c a class. * @return the Java type corresponding to the given class. */ public static Type getType(final ClassInfo c) { if (c.isPrimitive()) { if (c.equals(INTEGER)) { return Type.INT_TYPE; } else if (c.equals(VOID)) { return Type.VOID_TYPE; } else if (c.equals(BOOLEAN)) { return Type.BOOLEAN_TYPE; } else if (c.equals(BYTE)) { return Type.BYTE_TYPE; } else if (c.equals(CHARACTER)) { return Type.CHAR_TYPE; } else if (c.equals(SHORT)) { return Type.SHORT_TYPE; } else if (c.equals(DOUBLE)) { return Type.DOUBLE_TYPE; } else if (c.equals(FLOAT)) { return Type.FLOAT_TYPE; } else if (c.equals(LONG)) { return Type.LONG_TYPE; } else { throw new Error("should not happen"); } } else { return Type.getType(getClassDescriptor(c)); } } }