public Assembler sub(Type t) { int opcode = -1; switch (t.getSort()) { case Type.SHORT: case Type.BYTE: case Type.INT: { opcode = ISUB; break; } case Type.LONG: { opcode = LSUB; break; } case Type.FLOAT: { opcode = FSUB; break; } case Type.DOUBLE: { opcode = DSUB; break; } } if (opcode != -1) { mv.visitInsn(opcode); } return this; }
/** Generate class bytes for java.lang.Runnable implementation and return the same. */ public byte[] generate(Method method, String className) { int modifiers = method.getModifiers(); // make sure that the method is public static // and accepts no arguments if (!Modifier.isStatic(modifiers) || !Modifier.isPublic(modifiers) || method.getParameterTypes().length != 0) { throw new IllegalArgumentException(); } Class clazz = method.getDeclaringClass(); modifiers = clazz.getModifiers(); // make sure that the class is public as well if (!Modifier.isPublic(modifiers)) { throw new IllegalArgumentException(); } ClassWriter cw = InstrumentUtils.newClassWriter(); cw.visit( V1_1, ACC_PUBLIC, className, null, "java/lang/Object", new String[] {"java/lang/Runnable"}); // creates a MethodWriter for the (implicit) constructor MethodVisitor mw = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null); // pushes the 'this' variable mw.visitVarInsn(ALOAD, 0); // invokes the super class constructor mw.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V"); mw.visitInsn(RETURN); mw.visitMaxs(1, 1); mw.visitEnd(); // creates a MethodWriter for the 'main' method mw = cw.visitMethod(ACC_PUBLIC, "run", "()V", null, null); // invokes the given method mw.visitMethodInsn( INVOKESTATIC, Type.getInternalName(method.getDeclaringClass()), method.getName(), Type.getMethodDescriptor(method)); mw.visitInsn(RETURN); mw.visitMaxs(1, 1); mw.visitEnd(); return cw.toByteArray(); }
public Assembler dupValue(Type type) { switch (type.getSize()) { case 1: dup(); break; case 2: dup2(); break; } return this; }
/** * Checks the type compatibility<br> * Two types are compatible when and only when: * * <ol> * <li>They are exactly the same * <li>The left parameter is {@linkplain Object} or {@linkplain AnyType} and the right parameter * is either {@linkplain Object} or an array * <li>The left parameter is assignable from the right one (is a superclass of the right one) * </ol> * * @param left The left type parameter * @param right The right type parameter * @return Returns true if a value of the left {@linkplain Type} can hold the value of the right * {@linkplain Type} */ public static boolean isCompatible(Type left, Type right) { if (left.equals(right)) { return true; } else if (isArray(left)) { return false; } else if (isObjectOrAnyType(left)) { int sort2 = right.getSort(); return (sort2 == Type.OBJECT || sort2 == Type.ARRAY); } else if (isPrimitive(left)) { // a primitive type requires strict equality return left.equals(right); } else { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = ClassLoader.getSystemClassLoader(); } // those classes should already have been loaded at this point Class clzLeft, clzRight; try { clzLeft = cl.loadClass(left.getClassName()); } catch (Throwable e) { clzLeft = Object.class; } // anything is assignable to Object if (clzLeft == Object.class) { return true; } try { clzRight = cl.loadClass(right.getClassName()); } catch (Throwable e) { clzRight = Object.class; } return (clzLeft.isAssignableFrom(clzRight)); } }
public Assembler unbox(String desc) { int typeCode = desc.charAt(0); switch (typeCode) { case '[': case 'L': mv.visitTypeInsn(CHECKCAST, Type.getType(desc).getInternalName()); break; case 'Z': mv.visitTypeInsn(CHECKCAST, JAVA_LANG_BOOLEAN); invokeVirtual(JAVA_LANG_BOOLEAN, BOOLEAN_VALUE, BOOLEAN_VALUE_DESC); break; case 'C': mv.visitTypeInsn(CHECKCAST, JAVA_LANG_CHARACTER); invokeVirtual(JAVA_LANG_CHARACTER, CHAR_VALUE, CHAR_VALUE_DESC); break; case 'B': mv.visitTypeInsn(CHECKCAST, JAVA_LANG_NUMBER); invokeVirtual(JAVA_LANG_NUMBER, BYTE_VALUE, BYTE_VALUE_DESC); break; case 'S': mv.visitTypeInsn(CHECKCAST, JAVA_LANG_NUMBER); invokeVirtual(JAVA_LANG_NUMBER, SHORT_VALUE, SHORT_VALUE_DESC); break; case 'I': mv.visitTypeInsn(CHECKCAST, JAVA_LANG_NUMBER); invokeVirtual(JAVA_LANG_NUMBER, INT_VALUE, INT_VALUE_DESC); break; case 'J': mv.visitTypeInsn(CHECKCAST, JAVA_LANG_NUMBER); invokeVirtual(JAVA_LANG_NUMBER, LONG_VALUE, LONG_VALUE_DESC); break; case 'F': mv.visitTypeInsn(CHECKCAST, JAVA_LANG_NUMBER); invokeVirtual(JAVA_LANG_NUMBER, FLOAT_VALUE, FLOAT_VALUE_DESC); break; case 'D': mv.visitTypeInsn(CHECKCAST, JAVA_LANG_NUMBER); invokeVirtual(JAVA_LANG_NUMBER, DOUBLE_VALUE, DOUBLE_VALUE_DESC); break; } return this; }
public static Type getArrayType(int arrayOpcode) { switch (arrayOpcode) { case IALOAD: case IASTORE: return Type.getType("[I"); case BALOAD: case BASTORE: return Type.getType("[B"); case AALOAD: case AASTORE: return objectArrayType; case CALOAD: case CASTORE: return Type.getType("[C"); case FALOAD: case FASTORE: return Type.getType("[F"); case SALOAD: case SASTORE: return Type.getType("[S"); case LALOAD: case LASTORE: return Type.getType("[J"); case DALOAD: case DASTORE: return Type.getType("[D"); default: throw new RuntimeException("invalid array opcode"); } }
public static boolean isString(Type t) { return t.equals(stringType); }
public static boolean isObject(Type t) { return t.equals(objectType); }
public static boolean isAnyTypeArray(Type t) { return t.equals(anyTypeArray); }
public static boolean isAnyType(Type t) { return t.equals(anyType); }
class TypeUtils { private TypeUtils() {} public static final Type objectType = Type.getType(Object.class); public static final Type stringType = Type.getType(String.class); public static final Type throwableType = Type.getType(Throwable.class); public static final Type objectArrayType = Type.getType(Object[].class); public static final Type anyType = Type.getType(AnyType.class); public static final Type anyTypeArray = Type.getType(AnyType[].class); public static boolean isPrimitive(Type t) { return t == Type.BOOLEAN_TYPE || t == Type.BYTE_TYPE || t == Type.CHAR_TYPE || t == Type.DOUBLE_TYPE || t == Type.FLOAT_TYPE || t == Type.INT_TYPE || t == Type.LONG_TYPE || t == Type.SHORT_TYPE; } public static boolean isAnyType(Type t) { return t.equals(anyType); } public static boolean isAnyTypeArray(Type t) { return t.equals(anyTypeArray); } public static boolean isObject(Type t) { return t.equals(objectType); } public static boolean isObjectOrAnyType(Type t) { return isObject(t) || isAnyType(t); } public static boolean isString(Type t) { return t.equals(stringType); } public static boolean isArray(Type t) { return t.getSort() == Type.ARRAY; } public static boolean isThrowable(Type t) { return t.equals(throwableType); } /** * Checks the type compatibility<br> * Two types are compatible when and only when: * * <ol> * <li>They are exactly the same * <li>The left parameter is {@linkplain Object} or {@linkplain AnyType} and the right parameter * is either {@linkplain Object} or an array * <li>The left parameter is assignable from the right one (is a superclass of the right one) * </ol> * * @param left The left type parameter * @param right The right type parameter * @return Returns true if a value of the left {@linkplain Type} can hold the value of the right * {@linkplain Type} */ public static boolean isCompatible(Type left, Type right) { if (left.equals(right)) { return true; } else if (isArray(left)) { return false; } else if (isObjectOrAnyType(left)) { int sort2 = right.getSort(); return (sort2 == Type.OBJECT || sort2 == Type.ARRAY); } else if (isPrimitive(left)) { // a primitive type requires strict equality return left.equals(right); } else { ClassLoader cl = Thread.currentThread().getContextClassLoader(); if (cl == null) { cl = ClassLoader.getSystemClassLoader(); } // those classes should already have been loaded at this point Class clzLeft, clzRight; try { clzLeft = cl.loadClass(left.getClassName()); } catch (Throwable e) { clzLeft = Object.class; } // anything is assignable to Object if (clzLeft == Object.class) { return true; } try { clzRight = cl.loadClass(right.getClassName()); } catch (Throwable e) { clzRight = Object.class; } return (clzLeft.isAssignableFrom(clzRight)); } } public static boolean isCompatible(Type[] args1, Type[] args2) { if (args1.length != args2.length) { return false; } for (int i = 0; i < args1.length; i++) { if (!args1[i].equals(args2[i])) { int sort2 = args2[i].getSort(); /* * if destination is AnyType and right side is * Object or Array (i.e., any reference type) * then we allow it - because AnyType is mapped to * java.lang.Object. */ if (isAnyType(args1[i]) && (sort2 == Type.OBJECT || sort2 == Type.ARRAY)) { continue; } else { return false; } } } return true; } public static Type getArrayType(int arrayOpcode) { switch (arrayOpcode) { case IALOAD: case IASTORE: return Type.getType("[I"); case BALOAD: case BASTORE: return Type.getType("[B"); case AALOAD: case AASTORE: return objectArrayType; case CALOAD: case CASTORE: return Type.getType("[C"); case FALOAD: case FASTORE: return Type.getType("[F"); case SALOAD: case SASTORE: return Type.getType("[S"); case LALOAD: case LASTORE: return Type.getType("[J"); case DALOAD: case DASTORE: return Type.getType("[D"); default: throw new RuntimeException("invalid array opcode"); } } public static Type getElementType(int arrayOpcode) { switch (arrayOpcode) { case IALOAD: case IASTORE: return Type.INT_TYPE; case BALOAD: case BASTORE: return Type.BYTE_TYPE; case AALOAD: case AASTORE: return objectType; case CALOAD: case CASTORE: return Type.CHAR_TYPE; case FALOAD: case FASTORE: return Type.FLOAT_TYPE; case SALOAD: case SASTORE: return Type.SHORT_TYPE; case LALOAD: case LASTORE: return Type.LONG_TYPE; case DALOAD: case DASTORE: return Type.DOUBLE_TYPE; default: throw new RuntimeException("invalid array opcode"); } } private static final Map<String, String> primitives; static { primitives = new HashMap<String, String>(); primitives.put("void", "V"); primitives.put("byte", "B"); primitives.put("char", "C"); primitives.put("double", "D"); primitives.put("float", "F"); primitives.put("int", "I"); primitives.put("long", "J"); primitives.put("short", "S"); primitives.put("boolean", "Z"); } public static String declarationToDescriptor(String decl) { int leftParen = decl.indexOf('('); int rightParen = decl.indexOf(')'); if (leftParen == -1 || rightParen == -1) { throw new IllegalArgumentException(); } StringBuilder buf = new StringBuilder(); String descriptor; buf.append('('); String args = decl.substring(leftParen + 1, rightParen); StringTokenizer st = new StringTokenizer(args, ","); while (st.hasMoreTokens()) { String arg = st.nextToken().trim(); descriptor = primitives.get(arg); if (arg.length() == 0) { throw new IllegalArgumentException(); } if (descriptor == null) { descriptor = objectOrArrayType(arg); } buf.append(descriptor); } buf.append(')'); String returnType = decl.substring(0, leftParen).trim(); descriptor = primitives.get(returnType); if (returnType.length() == 0) { throw new IllegalArgumentException(); } if (descriptor == null) { descriptor = objectOrArrayType(returnType); } buf.append(descriptor); return buf.toString(); } public static String getJavaType(String desc) { int arrIndex = desc.lastIndexOf("[") + 1; desc = desc.substring(arrIndex); if (desc.startsWith("L")) { desc = desc.substring(1, desc.length() - 1).replace('/', '.'); } else { for (Map.Entry<String, String> entry : primitives.entrySet()) { if (entry.getValue().equals(desc)) { desc = entry.getKey(); break; } } } StringBuilder sb = new StringBuilder(desc); for (int i = 0; i < arrIndex; i++) { sb.append("[]"); } return sb.toString(); } public static String objectOrArrayType(String type) { StringBuilder buf = new StringBuilder(); int index = 0; while ((index = type.indexOf("[]", index) + 1) > 0) { buf.append('['); } String t = type.substring(0, type.length() - buf.length() * 2); String desc = primitives.get(t); if (desc != null) { buf.append(desc); } else { buf.append('L'); if (t.indexOf('.') < 0) { buf.append(t); } else { buf.append(t.replace('.', '/')); } buf.append(';'); } return buf.toString(); } }
public Assembler storeField(Type owner, String name, Type t) { mv.visitFieldInsn(Opcodes.PUTFIELD, owner.getInternalName(), name, t.getDescriptor()); return this; }
public Assembler arrayLoad(Type type) { mv.visitInsn(type.getOpcode(IALOAD)); return this; }
public Assembler unbox(Type type) { return unbox(type.getDescriptor()); }
public Assembler newArray(Type t) { mv.visitTypeInsn(ANEWARRAY, t.getInternalName()); return this; }
public Assembler newInstance(Type t) { mv.visitTypeInsn(NEW, t.getInternalName()); return this; }
public Assembler loadStaticField(Type owner, String name, Type t) { mv.visitFieldInsn(Opcodes.GETSTATIC, owner.getInternalName(), name, t.getDescriptor()); return this; }
public static boolean isArray(Type t) { return t.getSort() == Type.ARRAY; }
public static boolean isThrowable(Type t) { return t.equals(throwableType); }
public Assembler loadLocal(Type type, int index) { mv.visitVarInsn(type.getOpcode(ILOAD), index); return this; }
public Assembler arrayStore(Type type) { mv.visitInsn(type.getOpcode(IASTORE)); return this; }
public Assembler storeLocal(Type type, int index) { mv.visitVarInsn(type.getOpcode(ISTORE), index); return this; }