package railo.transformer.bytecode.reflection; import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Map; import org.apache.commons.collections.map.ReferenceMap; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.Label; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.GeneratorAdapter; import railo.commons.io.IOUtil; import railo.commons.io.res.Resource; import railo.commons.io.res.ResourceProvider; import railo.commons.io.res.ResourcesImpl; import railo.commons.io.res.util.ResourceUtil; import railo.commons.lang.ClassUtil; import railo.commons.lang.ExtendableClassLoader; import railo.commons.lang.PhysicalClassLoader; import railo.commons.lang.StringUtil; import railo.runtime.PageContext; import railo.runtime.functions.arrays.ArrayNew; import railo.runtime.op.Caster; import railo.runtime.type.util.ArrayUtil; import railo.transformer.bytecode.util.ASMConstants; import railo.transformer.bytecode.util.ASMUtil; import railo.transformer.bytecode.util.Types; public class ASMProxyFactory { public static final Type ASM_METHOD=Type.getType(ASMMethod.class); public static final Type CLASS404=Type.getType(ClassNotFoundException.class); public static final Type CLASS_UTIL=Type.getType(ClassUtil.class); //private static final org.objectweb.asm.commons.Method CONSTRUCTOR = // new org.objectweb.asm.commons.Method("<init>",Types.VOID,new Type[]{Types.CLASS_LOADER,Types.CLASS}); private static final org.objectweb.asm.commons.Method CONSTRUCTOR = new org.objectweb.asm.commons.Method("<init>",Types.VOID,new Type[]{Types.CLASS,Types.CLASS_ARRAY}); private static final org.objectweb.asm.commons.Method LOAD_CLASS = new org.objectweb.asm.commons.Method( "loadClass", Types.CLASS, new Type[]{Types.STRING}); // public static Class loadClass(String className, Class defaultValue) { private static final org.objectweb.asm.commons.Method LOAD_CLASS_EL = new org.objectweb.asm.commons.Method( "loadClass", Types.CLASS, new Type[]{Types.STRING,Types.CLASS}); // public String getName(); private static final org.objectweb.asm.commons.Method GET_NAME = new org.objectweb.asm.commons.Method( "getName", Types.STRING, new Type[]{}); // public int getModifiers(); private static final org.objectweb.asm.commons.Method GET_MODIFIERS = new org.objectweb.asm.commons.Method( "getModifiers", Types.INT_VALUE, new Type[]{}); // public Class getReturnType(); private static final org.objectweb.asm.commons.Method GET_RETURN_TYPE_AS_STRING = new org.objectweb.asm.commons.Method( "getReturnTypeAsString", Types.STRING, new Type[]{}); private static final org.objectweb.asm.commons.Method INVOKE = new org.objectweb.asm.commons.Method( "invoke", Types.OBJECT, new Type[]{Types.OBJECT,Types.OBJECT_ARRAY}); // primitive to reference type private static final org.objectweb.asm.commons.Method BOOL_VALUE_OF = new org.objectweb.asm.commons.Method("valueOf",Types.BOOLEAN,new Type[]{Types.BOOLEAN_VALUE}); private static final org.objectweb.asm.commons.Method SHORT_VALUE_OF = new org.objectweb.asm.commons.Method("valueOf",Types.SHORT,new Type[]{Types.SHORT_VALUE}); private static final org.objectweb.asm.commons.Method INT_VALUE_OF = new org.objectweb.asm.commons.Method("valueOf",Types.INTEGER,new Type[]{Types.INT_VALUE}); private static final org.objectweb.asm.commons.Method LONG_VALUE_OF = new org.objectweb.asm.commons.Method("valueOf",Types.LONG,new Type[]{Types.LONG_VALUE}); private static final org.objectweb.asm.commons.Method FLT_VALUE_OF = new org.objectweb.asm.commons.Method("valueOf",Types.FLOAT,new Type[]{Types.FLOAT_VALUE}); private static final org.objectweb.asm.commons.Method DBL_VALUE_OF = new org.objectweb.asm.commons.Method("valueOf",Types.DOUBLE,new Type[]{Types.DOUBLE_VALUE}); private static final org.objectweb.asm.commons.Method CHR_VALUE_OF = new org.objectweb.asm.commons.Method("valueOf",Types.CHARACTER,new Type[]{Types.CHARACTER}); private static final org.objectweb.asm.commons.Method BYT_VALUE_OF = new org.objectweb.asm.commons.Method("valueOf",Types.BYTE,new Type[]{Types.BYTE_VALUE}); // reference type to primitive private static final org.objectweb.asm.commons.Method BOOL_VALUE = new org.objectweb.asm.commons.Method("booleanValue",Types.BOOLEAN_VALUE,new Type[]{}); private static final org.objectweb.asm.commons.Method SHORT_VALUE = new org.objectweb.asm.commons.Method("shortValue",Types.SHORT_VALUE,new Type[]{}); private static final org.objectweb.asm.commons.Method INT_VALUE = new org.objectweb.asm.commons.Method("intValue",Types.INT_VALUE,new Type[]{}); private static final org.objectweb.asm.commons.Method LONG_VALUE = new org.objectweb.asm.commons.Method("longValue",Types.LONG_VALUE,new Type[]{}); private static final org.objectweb.asm.commons.Method FLT_VALUE = new org.objectweb.asm.commons.Method("floatValue",Types.FLOAT_VALUE,new Type[]{}); private static final org.objectweb.asm.commons.Method DBL_VALUE = new org.objectweb.asm.commons.Method("doubleValue",Types.DOUBLE_VALUE,new Type[]{}); private static final org.objectweb.asm.commons.Method CHR_VALUE = new org.objectweb.asm.commons.Method("charValue",Types.CHAR,new Type[]{}); private static final org.objectweb.asm.commons.Method BYT_VALUE = new org.objectweb.asm.commons.Method("byteValue",Types.BYTE_VALUE,new Type[]{}); private static final org.objectweb.asm.commons.Method ASM_METHOD_CONSTRUCTOR = new org.objectweb.asm.commons.Method( "<init>", Types.VOID, new Type[]{Types.CLASS,Types.CLASS_ARRAY} ); private static final Map<String,ASMMethod>methods=new ReferenceMap(); public static void main(String[] args) throws Throwable { ResourceProvider frp = ResourcesImpl.getFileResourceProvider(); Resource root = frp.getResource("/Users/mic/Projects/Railo/webroot/WEB-INF/railo/cfclasses/wrappers/"); root.mkdir(); PhysicalClassLoader pcl = new PhysicalClassLoader(root); //PhysicalClassLoader pcl = (PhysicalClassLoader)ThreadLocalPageContext.getConfig().getRPCClassLoader(false); ASMProxyFactory.getClass(pcl, root, ArrayNew.class); ASMMethod method = ASMProxyFactory.getMethod(pcl, root, ArrayNew.class, "call", new Class[]{PageContext.class}); //print.e(method.invoke(null, new Object[]{null})); } public static ASMClass getClass(ExtendableClassLoader pcl,Resource classRoot,Class clazz) throws IOException, InstantiationException, IllegalAccessException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{ Type type = Type.getType(clazz); // Fields Field[] fields = clazz.getFields(); for(int i=0;i<fields.length;i++){ if(Modifier.isPrivate(fields[i].getModifiers())) continue; createField(type,fields[i]); } // Methods Method[] methods = clazz.getMethods(); Map<String,ASMMethod> amethods=new HashMap<String, ASMMethod>(); for(int i=0;i<methods.length;i++){ if(Modifier.isPrivate(methods[i].getModifiers())) continue; amethods.put(methods[i].getName(), getMethod(pcl,classRoot,type,clazz,methods[i])); } return new ASMClass(clazz.getName(),amethods); } private static void createField(Type type, Field field) { // TODO Auto-generated method stub } public static ASMMethod getMethod(ExtendableClassLoader pcl, Resource classRoot, Class clazz, String methodName, Class[] parameters) throws IOException, InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException { String className = createMethodName(clazz,methodName,parameters); // check if already in memory cache ASMMethod asmm = methods.get(className); if(asmm!=null){ //print.e("use loaded from memory"); return asmm; } // try to load existing ASM Class Class<?> asmClass; try { asmClass = pcl.loadClass(className); //print.e("use existing class"); } catch (ClassNotFoundException cnfe) { Type type = Type.getType(clazz); Method method = clazz.getMethod(methodName, parameters); byte[] barr = _createMethod(type, clazz, method, classRoot, className); asmClass=pcl.loadClass(className, barr); //print.e("create class"); } asmm = newInstance(asmClass,clazz,parameters); //methods.put(className, asmm); return asmm; } private static ASMMethod getMethod(ExtendableClassLoader pcl, Resource classRoot, Type type,Class clazz, Method method) throws IOException, InstantiationException, IllegalAccessException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException { String className = createMethodName(clazz,method.getName(),method.getParameterTypes()); // check if already in memory cache ASMMethod asmm = methods.get(className); if(asmm!=null)return asmm; // try to load existing ASM Class Class<?> asmClass; try { asmClass = pcl.loadClass(className); } catch (ClassNotFoundException cnfe) { byte[] barr = _createMethod(type, clazz, method, classRoot, className); asmClass=pcl.loadClass(className, barr); } asmm = newInstance(asmClass,clazz,method.getParameterTypes()); methods.put(className, asmm); return asmm; } private static ASMMethod newInstance(Class<?> asmClass, Class<?> decClass, Class[] params) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchMethodException { Constructor<ASMMethod> constr = (Constructor<ASMMethod>) asmClass.getConstructor( new Class[]{ Class.class, Class[].class } ); return constr.newInstance(new Object[]{ decClass, params }); //return (ASMMethod) asmClass.newInstance(); } private static String createMethodName(Class clazz,String methodName,Class[] paramTypes) { StringBuilder sb = new StringBuilder("method.") .append(clazz.getName()) .append(methodName); paramNames(sb,paramTypes); return sb.toString(); } private static byte[] _createMethod(Type type,Class clazz, Method method,Resource classRoot, String className) throws IOException { Class<?> rtn = method.getReturnType(); Type rtnType = Type.getType(rtn); className=className.replace('.',File.separatorChar); ClassWriter cw = ASMUtil.getClassWriter(); cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, className, null, ASM_METHOD.getInternalName(), null); // CONSTRUCTOR GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR,null,null,cw); Label begin = new Label(); adapter.visitLabel(begin); adapter.loadThis(); adapter.visitVarInsn(Opcodes.ALOAD, 1); adapter.visitVarInsn(Opcodes.ALOAD, 2); adapter.invokeConstructor(ASM_METHOD, CONSTRUCTOR); adapter.visitInsn(Opcodes.RETURN); Label end = new Label(); adapter.visitLabel(end); adapter.endMethod(); /* GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR,null,null,cw); Label begin = new Label(); adapter.visitLabel(begin); adapter.loadThis(); // clazz adapter.visitVarInsn(Opcodes.ALOAD, 2); // parameterTypes Class<?>[] params = method.getParameterTypes(); Type[] paramTypes = new Type[params.length]; ArrayVisitor av=new ArrayVisitor(); av.visitBegin(adapter, Types.CLASS, params.length); for(int i=0;i<params.length;i++){ paramTypes[i]=Type.getType(params[i]); av.visitBeginItem(adapter, i); loadClass(adapter,params[i]); av.visitEndItem(adapter); } av.visitEnd(); adapter.invokeConstructor(ASM_METHOD, ASM_METHOD_CONSTRUCTOR); adapter.visitInsn(Opcodes.RETURN); Label end = new Label(); adapter.visitLabel(end); adapter.endMethod(); */ // METHOD getName(); adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC , GET_NAME, null, null, cw); adapter.push(method.getName()); adapter.visitInsn(Opcodes.ARETURN); adapter.endMethod(); // METHOD getModifiers(); adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC , GET_MODIFIERS, null, null, cw); adapter.push(method.getModifiers()); adapter.visitInsn(Opcodes.IRETURN); adapter.endMethod(); // METHOD getReturnType(); adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC , GET_RETURN_TYPE_AS_STRING, null, null, cw); adapter.push(method.getReturnType().getName()); adapter.visitInsn(Opcodes.ARETURN); adapter.endMethod(); // METHOD INVOKE boolean isStatic = Modifier.isStatic(method.getModifiers()); adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC , INVOKE, null, null, cw); Label start=adapter.newLabel(); adapter.visitLabel(start); // load Object if(!isStatic) { adapter.visitVarInsn(Opcodes.ALOAD, 1); adapter.checkCast(type); } // load params Class<?>[] params = method.getParameterTypes(); Type[] paramTypes = new Type[params.length]; for(int i=0;i<params.length;i++){ paramTypes[i]=Type.getType(params[i]); } for(int i=0;i<params.length;i++){ adapter.visitVarInsn(Opcodes.ALOAD, 2); adapter.push(i); //adapter.visitInsn(Opcodes.ICONST_0); adapter.visitInsn(Opcodes.AALOAD); adapter.checkCast(toReferenceType(params[i],paramTypes[i])); // cast if(params[i]==boolean.class) adapter.invokeVirtual(Types.BOOLEAN, BOOL_VALUE); else if(params[i]==short.class) adapter.invokeVirtual(Types.SHORT, SHORT_VALUE); else if(params[i]==int.class) adapter.invokeVirtual(Types.INTEGER, INT_VALUE); else if(params[i]==float.class) adapter.invokeVirtual(Types.FLOAT, FLT_VALUE); else if(params[i]==long.class) adapter.invokeVirtual(Types.LONG, LONG_VALUE); else if(params[i]==double.class) adapter.invokeVirtual(Types.DOUBLE, DBL_VALUE); else if(params[i]==char.class) adapter.invokeVirtual(Types.CHARACTER, CHR_VALUE); else if(params[i]==byte.class) adapter.invokeVirtual(Types.BYTE, BYT_VALUE); //else adapter.checkCast(paramTypes[i]); } // call method final org.objectweb.asm.commons.Method m = new org.objectweb.asm.commons.Method(method.getName(),rtnType,paramTypes); if(isStatic)adapter.invokeStatic(type, m); else adapter.invokeVirtual(type, m); // return if(rtn==void.class) ASMConstants.NULL(adapter); // cast result to object if(rtn==boolean.class) adapter.invokeStatic(Types.BOOLEAN, BOOL_VALUE_OF); else if(rtn==short.class) adapter.invokeStatic(Types.SHORT, SHORT_VALUE_OF); else if(rtn==int.class) adapter.invokeStatic(Types.INTEGER, INT_VALUE_OF); else if(rtn==long.class) adapter.invokeStatic(Types.LONG, LONG_VALUE_OF); else if(rtn==float.class) adapter.invokeStatic(Types.FLOAT, FLT_VALUE_OF); else if(rtn==double.class) adapter.invokeStatic(Types.DOUBLE, DBL_VALUE_OF); else if(rtn==char.class) adapter.invokeStatic(Types.CHARACTER, CHR_VALUE_OF); else if(rtn==byte.class) adapter.invokeStatic(Types.BYTE, BYT_VALUE_OF); adapter.visitInsn(Opcodes.ARETURN); adapter.endMethod(); if(classRoot!=null) { Resource classFile=classRoot.getRealResource(className+".class"); return store(cw.toByteArray(),classFile); } return cw.toByteArray(); } private static Type toReferenceType(Class<?> clazz, Type defaultValue) { if(int.class==clazz) return Types.INTEGER; else if(long.class==clazz) return Types.LONG; else if(char.class==clazz) return Types.CHARACTER; else if(byte.class==clazz) return Types.BYTE; else if(float.class==clazz) return Types.FLOAT; else if(double.class==clazz) return Types.DOUBLE; else if(boolean.class==clazz) return Types.BOOLEAN; else if(short.class==clazz) return Types.SHORT; return defaultValue; } private static void loadClass(GeneratorAdapter adapter, Class<?> clazz) { if(void.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;"); // primitive types else if(int.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;"); else if(long.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;"); else if(char.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;"); else if(byte.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;"); else if(float.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;"); else if(double.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;"); else if(boolean.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;"); else if(short.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;"); // TODO ref types else { adapter.visitVarInsn(Opcodes.ALOAD, 1); adapter.push(clazz.getName()); adapter.invokeVirtual(Types.CLASS_LOADER,LOAD_CLASS ); } } private static void paramNames(StringBuilder sb, Class<?>[] params) { if(ArrayUtil.isEmpty(params)) return; for(int i=0;i<params.length;i++){ sb.append('$'); if(params[i].isArray()) sb.append(StringUtil.replace(Caster.toClassName(params[i]).replace('.', '_'),"[]","_arr",false)); else sb.append(params[i].getName().replace('.', '_')); } } private static byte[] store(byte[] barr,Resource classFile) throws IOException { // create class file ResourceUtil.touch(classFile); //print.e(classFile); IOUtil.copy(new ByteArrayInputStream(barr), classFile,true); return barr; } /*private void store(ClassWriter cw) { // create class file byte[] barr = cw.toByteArray(); try { ResourceUtil.touch(classFile); IOUtil.copy(new ByteArrayInputStream(barr), classFile,true); cl = (PhysicalClassLoader) mapping.getConfig().getRPCClassLoader(true); Class<?> clazz = cl.loadClass(className, barr); return newInstance(clazz, config,cfc); } catch(Throwable t) { throw Caster.toPageException(t); } }*/ }