001    package railo.transformer.bytecode.reflection;
002    
003    import java.io.ByteArrayInputStream;
004    import java.io.File;
005    import java.io.IOException;
006    import java.lang.reflect.Constructor;
007    import java.lang.reflect.Field;
008    import java.lang.reflect.InvocationTargetException;
009    import java.lang.reflect.Method;
010    import java.lang.reflect.Modifier;
011    import java.util.HashMap;
012    import java.util.Map;
013    
014    import org.apache.commons.collections.map.ReferenceMap;
015    import org.objectweb.asm.ClassWriter;
016    import org.objectweb.asm.Label;
017    import org.objectweb.asm.Opcodes;
018    import org.objectweb.asm.Type;
019    import org.objectweb.asm.commons.GeneratorAdapter;
020    
021    import railo.commons.io.IOUtil;
022    import railo.commons.io.res.Resource;
023    import railo.commons.io.res.ResourceProvider;
024    import railo.commons.io.res.ResourcesImpl;
025    import railo.commons.io.res.util.ResourceUtil;
026    import railo.commons.lang.ClassUtil;
027    import railo.commons.lang.ExtendableClassLoader;
028    import railo.commons.lang.PhysicalClassLoader;
029    import railo.commons.lang.StringUtil;
030    import railo.runtime.PageContext;
031    import railo.runtime.config.ConfigWeb;
032    import railo.runtime.engine.ThreadLocalPageContext;
033    import railo.runtime.exp.ExpressionException;
034    import railo.runtime.functions.arrays.ArrayAppend;
035    import railo.runtime.functions.arrays.ArrayNew;
036    import railo.runtime.op.Caster;
037    import railo.runtime.type.Array;
038    import railo.runtime.type.util.ArrayUtil;
039    import railo.transformer.bytecode.BytecodeContext;
040    import railo.transformer.bytecode.util.ASMConstants;
041    import railo.transformer.bytecode.util.ASMUtil;
042    import railo.transformer.bytecode.util.Types;
043    import railo.transformer.bytecode.visitor.ArrayVisitor;
044    import railo.transformer.bytecode.visitor.OnFinally;
045    import railo.transformer.bytecode.visitor.TryCatchFinallyVisitor;
046    
047    public class ASMProxyFactory {
048    
049            public static final Type ASM_METHOD=Type.getType(ASMMethod.class);
050            public static final Type CLASS404=Type.getType(ClassNotFoundException.class);
051            public static final Type CLASS_UTIL=Type.getType(ClassUtil.class);
052            
053            
054            
055            //private static final org.objectweb.asm.commons.Method CONSTRUCTOR = 
056        //  new org.objectweb.asm.commons.Method("<init>",Types.VOID,new Type[]{Types.CLASS_LOADER,Types.CLASS});
057            private static final org.objectweb.asm.commons.Method CONSTRUCTOR = 
058            new org.objectweb.asm.commons.Method("<init>",Types.VOID,new Type[]{Types.CLASS,Types.CLASS_ARRAY});
059    
060            private static final org.objectweb.asm.commons.Method LOAD_CLASS = new org.objectweb.asm.commons.Method(
061                            "loadClass",
062                            Types.CLASS,
063                            new Type[]{Types.STRING});
064            
065    
066            // public static Class loadClass(String className, Class defaultValue) {
067            private static final org.objectweb.asm.commons.Method LOAD_CLASS_EL = new org.objectweb.asm.commons.Method(
068                            "loadClass",
069                            Types.CLASS,
070                            new Type[]{Types.STRING,Types.CLASS});
071            
072    
073            // public String getName();
074            private static final org.objectweb.asm.commons.Method GET_NAME = new org.objectweb.asm.commons.Method(
075                            "getName",
076                            Types.STRING,
077                            new Type[]{});
078            
079            // public int getModifiers();
080            private static final org.objectweb.asm.commons.Method GET_MODIFIERS = new org.objectweb.asm.commons.Method(
081                            "getModifiers",
082                            Types.INT_VALUE,
083                            new Type[]{});
084    
085            // public Class getReturnType();
086            private static final org.objectweb.asm.commons.Method GET_RETURN_TYPE_AS_STRING = new org.objectweb.asm.commons.Method(
087                            "getReturnTypeAsString",
088                            Types.STRING,
089                            new Type[]{});
090    
091            
092            
093            
094            private static final org.objectweb.asm.commons.Method INVOKE = new org.objectweb.asm.commons.Method(
095                            "invoke",
096                            Types.OBJECT,
097                            new Type[]{Types.OBJECT,Types.OBJECT_ARRAY});
098            
099            
100            // primitive to reference type
101            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});
102            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});
103            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});
104            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});
105            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});
106            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});
107            private static final org.objectweb.asm.commons.Method CHR_VALUE_OF = new org.objectweb.asm.commons.Method("valueOf",Types.CHARACTER,new Type[]{Types.CHARACTER});
108            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});
109            
110            // reference type to primitive
111            private static final org.objectweb.asm.commons.Method BOOL_VALUE = new org.objectweb.asm.commons.Method("booleanValue",Types.BOOLEAN_VALUE,new Type[]{});
112            private static final org.objectweb.asm.commons.Method SHORT_VALUE = new org.objectweb.asm.commons.Method("shortValue",Types.SHORT_VALUE,new Type[]{});
113            private static final org.objectweb.asm.commons.Method INT_VALUE = new org.objectweb.asm.commons.Method("intValue",Types.INT_VALUE,new Type[]{});
114            private static final org.objectweb.asm.commons.Method LONG_VALUE = new org.objectweb.asm.commons.Method("longValue",Types.LONG_VALUE,new Type[]{});
115            private static final org.objectweb.asm.commons.Method FLT_VALUE = new org.objectweb.asm.commons.Method("floatValue",Types.FLOAT_VALUE,new Type[]{});
116            private static final org.objectweb.asm.commons.Method DBL_VALUE = new org.objectweb.asm.commons.Method("doubleValue",Types.DOUBLE_VALUE,new Type[]{});
117            private static final org.objectweb.asm.commons.Method CHR_VALUE = new org.objectweb.asm.commons.Method("charValue",Types.CHAR,new Type[]{});
118            private static final org.objectweb.asm.commons.Method BYT_VALUE = new org.objectweb.asm.commons.Method("byteValue",Types.BYTE_VALUE,new Type[]{});
119            
120            private static final org.objectweb.asm.commons.Method ASM_METHOD_CONSTRUCTOR = new org.objectweb.asm.commons.Method(
121                            "<init>",
122                            Types.VOID,
123                            new Type[]{Types.CLASS,Types.CLASS_ARRAY}
124                    );
125            
126            
127            private static final Map<String,ASMMethod>methods=new ReferenceMap();
128    
129            public static void main(String[] args) throws Throwable {
130                    ResourceProvider frp = ResourcesImpl.getFileResourceProvider();
131                    Resource root = frp.getResource("/Users/mic/Projects/Railo/webroot/WEB-INF/railo/cfclasses/wrappers/");
132                    root.mkdir();
133                    PhysicalClassLoader pcl = new PhysicalClassLoader(root);
134                    //PhysicalClassLoader pcl = (PhysicalClassLoader)ThreadLocalPageContext.getConfig().getRPCClassLoader(false);
135    
136                    ASMProxyFactory.getClass(pcl, root, ArrayNew.class);
137                    
138                    
139                    ASMMethod method = ASMProxyFactory.getMethod(pcl, root, ArrayNew.class, "call", new Class[]{PageContext.class});
140                    //print.e(method.invoke(null, new Object[]{null}));
141                    
142                    
143                    
144                    
145            }
146            
147            public static ASMClass getClass(ExtendableClassLoader pcl,Resource classRoot,Class clazz) throws IOException, InstantiationException, IllegalAccessException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{
148                    Type type = Type.getType(clazz); 
149    
150                // Fields
151                Field[] fields = clazz.getFields();
152                for(int i=0;i<fields.length;i++){
153                    if(Modifier.isPrivate(fields[i].getModifiers())) continue;
154                    createField(type,fields[i]);
155                }
156                
157                // Methods
158                Method[] methods = clazz.getMethods();
159                Map<String,ASMMethod> amethods=new HashMap<String, ASMMethod>();
160                for(int i=0;i<methods.length;i++){
161                    if(Modifier.isPrivate(methods[i].getModifiers())) continue;
162                    amethods.put(methods[i].getName(), getMethod(pcl,classRoot,type,clazz,methods[i]));
163                }
164                
165                return new ASMClass(clazz.getName(),amethods);
166                
167            }
168            
169            private static void createField(Type type, Field field) {
170                    // TODO Auto-generated method stub
171                    
172            }
173    
174            public static ASMMethod getMethod(ExtendableClassLoader pcl, Resource classRoot, Class clazz, String methodName, Class[] parameters) throws IOException, InstantiationException, IllegalAccessException, SecurityException, NoSuchMethodException, IllegalArgumentException, InvocationTargetException {
175                    String className = createMethodName(clazz,methodName,parameters);
176                    
177                    // check if already in memory cache
178                    ASMMethod asmm = methods.get(className);
179                    if(asmm!=null){
180                            //print.e("use loaded from memory");
181                            return asmm;
182                    }
183                    
184                    // try to load existing ASM Class
185                    Class<?> asmClass;
186                    try {
187                            asmClass = pcl.loadClass(className);
188                            //print.e("use existing class");
189                    }
190                    catch (ClassNotFoundException cnfe) {
191                            Type type = Type.getType(clazz);
192                            Method method = clazz.getMethod(methodName, parameters);
193                            byte[] barr = _createMethod(type, clazz, method, classRoot, className);
194                            asmClass=pcl.loadClass(className, barr);
195                            //print.e("create class");
196                    }
197                    asmm = newInstance(asmClass,clazz,parameters);
198                    //methods.put(className, asmm);
199                    return asmm;
200            }
201    
202            private static ASMMethod getMethod(ExtendableClassLoader pcl, Resource classRoot, Type type,Class clazz, Method method) throws IOException, InstantiationException, IllegalAccessException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException {
203                    String className = createMethodName(clazz,method.getName(),method.getParameterTypes());
204                    
205                    // check if already in memory cache
206                    ASMMethod asmm = methods.get(className);
207                    if(asmm!=null)return asmm;
208                    
209                    // try to load existing ASM Class
210                    Class<?> asmClass;
211                    try {
212                            asmClass = pcl.loadClass(className);
213                    }
214                    catch (ClassNotFoundException cnfe) {
215                            byte[] barr = _createMethod(type, clazz, method, classRoot, className);
216                            asmClass=pcl.loadClass(className, barr);
217                    }
218                    
219                    asmm = newInstance(asmClass,clazz,method.getParameterTypes());
220                    methods.put(className, asmm);
221                    return asmm;
222            }
223    
224            private static ASMMethod newInstance(Class<?> asmClass, Class<?> decClass, Class[] params) throws InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, SecurityException, NoSuchMethodException {
225                    Constructor<ASMMethod> constr = (Constructor<ASMMethod>) asmClass.getConstructor(
226                                    new Class[]{
227                                            Class.class,
228                                            Class[].class
229                                    }
230                    );
231                    return constr.newInstance(new Object[]{
232                                    decClass,
233                                    params
234                            });
235                     
236                    //return (ASMMethod) asmClass.newInstance();
237            }
238            
239            
240            private static String createMethodName(Class clazz,String methodName,Class[] paramTypes) {
241                    StringBuilder sb = new StringBuilder("method.")
242                    .append(clazz.getName())
243                    .append(methodName);
244                    
245                    paramNames(sb,paramTypes);
246                    
247                    return sb.toString();
248            }
249    
250            private static byte[] _createMethod(Type type,Class clazz, Method method,Resource classRoot, String className) throws IOException {
251                    Class<?> rtn = method.getReturnType();
252                Type rtnType = Type.getType(rtn);
253                
254                    className=className.replace('.',File.separatorChar);
255                    ClassWriter cw = ASMUtil.getClassWriter();
256                cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, className, null, ASM_METHOD.getInternalName(), null);
257                    
258    
259    // CONSTRUCTOR
260    
261                GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR,null,null,cw);
262                
263                Label begin = new Label();
264            adapter.visitLabel(begin);
265                    adapter.loadThis();
266            
267                adapter.visitVarInsn(Opcodes.ALOAD, 1);
268                    adapter.visitVarInsn(Opcodes.ALOAD, 2);
269            
270                    adapter.invokeConstructor(ASM_METHOD, CONSTRUCTOR);
271                    adapter.visitInsn(Opcodes.RETURN);
272                    
273                    Label end = new Label();
274                    adapter.visitLabel(end);
275                    
276                    adapter.endMethod();
277                    
278            /*
279             
280                GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR,null,null,cw);
281                
282                Label begin = new Label();
283            adapter.visitLabel(begin);
284                    adapter.loadThis();
285            
286                // clazz
287            adapter.visitVarInsn(Opcodes.ALOAD, 2);
288            
289            // parameterTypes 
290            Class<?>[] params = method.getParameterTypes();
291            Type[] paramTypes = new Type[params.length];
292                ArrayVisitor av=new ArrayVisitor();
293                av.visitBegin(adapter, Types.CLASS, params.length);
294                for(int i=0;i<params.length;i++){
295                    paramTypes[i]=Type.getType(params[i]);
296                    av.visitBeginItem(adapter, i);
297                            loadClass(adapter,params[i]);
298                    av.visitEndItem(adapter);
299                }
300                av.visitEnd();
301                
302                    adapter.invokeConstructor(ASM_METHOD, ASM_METHOD_CONSTRUCTOR);
303                    adapter.visitInsn(Opcodes.RETURN);
304                    
305                    Label end = new Label();
306                    adapter.visitLabel(end);
307                    
308                    adapter.endMethod();
309             */
310                    
311                    
312                    
313            // METHOD getName();
314                    adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC , GET_NAME, null, null, cw);
315                    adapter.push(method.getName());
316                    adapter.visitInsn(Opcodes.ARETURN);
317            adapter.endMethod();
318                    
319        // METHOD getModifiers();
320                    adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC , GET_MODIFIERS, null, null, cw);
321                    adapter.push(method.getModifiers());
322                    adapter.visitInsn(Opcodes.IRETURN);
323            adapter.endMethod();
324            
325    
326                    
327        // METHOD getReturnType();
328            adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC , GET_RETURN_TYPE_AS_STRING, null, null, cw);
329                    
330                    adapter.push(method.getReturnType().getName());
331                    adapter.visitInsn(Opcodes.ARETURN);
332            
333                    adapter.endMethod();
334            
335                    
336            
337            
338            // METHOD INVOKE
339                    boolean isStatic = Modifier.isStatic(method.getModifiers());
340                    adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC , INVOKE, null, null, cw);
341            Label start=adapter.newLabel();
342            adapter.visitLabel(start);
343            
344            // load Object
345            if(!isStatic) {
346                    adapter.visitVarInsn(Opcodes.ALOAD, 1);
347                    adapter.checkCast(type);
348                    }
349                    
350            // load params
351            Class<?>[] params = method.getParameterTypes();
352    
353            Type[] paramTypes = new Type[params.length];
354                for(int i=0;i<params.length;i++){
355                    paramTypes[i]=Type.getType(params[i]);
356                }
357    
358            for(int i=0;i<params.length;i++){
359                    adapter.visitVarInsn(Opcodes.ALOAD, 2);
360                    adapter.push(i);
361                    //adapter.visitInsn(Opcodes.ICONST_0);
362                    adapter.visitInsn(Opcodes.AALOAD);
363                    
364                    adapter.checkCast(toReferenceType(params[i],paramTypes[i]));
365                    
366                    // cast
367                    if(params[i]==boolean.class) adapter.invokeVirtual(Types.BOOLEAN, BOOL_VALUE);
368                    else if(params[i]==short.class) adapter.invokeVirtual(Types.SHORT, SHORT_VALUE);
369                    else if(params[i]==int.class) adapter.invokeVirtual(Types.INTEGER, INT_VALUE);
370                    else if(params[i]==float.class) adapter.invokeVirtual(Types.FLOAT, FLT_VALUE);
371                    else if(params[i]==long.class) adapter.invokeVirtual(Types.LONG, LONG_VALUE);
372                    else if(params[i]==double.class) adapter.invokeVirtual(Types.DOUBLE, DBL_VALUE);
373                    else if(params[i]==char.class) adapter.invokeVirtual(Types.CHARACTER, CHR_VALUE);
374                    else if(params[i]==byte.class) adapter.invokeVirtual(Types.BYTE, BYT_VALUE);
375                    //else adapter.checkCast(paramTypes[i]);
376                    
377            }
378            
379            
380            // call method
381            final org.objectweb.asm.commons.Method m = new org.objectweb.asm.commons.Method(method.getName(),rtnType,paramTypes);
382            if(isStatic)adapter.invokeStatic(type, m);
383            else adapter.invokeVirtual(type, m);
384             
385            
386            // return
387            if(rtn==void.class) ASMConstants.NULL(adapter);
388            
389            
390            // cast result to object
391            if(rtn==boolean.class) adapter.invokeStatic(Types.BOOLEAN, BOOL_VALUE_OF);
392            else if(rtn==short.class) adapter.invokeStatic(Types.SHORT, SHORT_VALUE_OF);
393            else if(rtn==int.class) adapter.invokeStatic(Types.INTEGER, INT_VALUE_OF);
394            else if(rtn==long.class) adapter.invokeStatic(Types.LONG, LONG_VALUE_OF);
395            else if(rtn==float.class) adapter.invokeStatic(Types.FLOAT, FLT_VALUE_OF);
396            else if(rtn==double.class) adapter.invokeStatic(Types.DOUBLE, DBL_VALUE_OF);
397            else if(rtn==char.class) adapter.invokeStatic(Types.CHARACTER, CHR_VALUE_OF);
398            else if(rtn==byte.class) adapter.invokeStatic(Types.BYTE, BYT_VALUE_OF);
399            
400            adapter.visitInsn(Opcodes.ARETURN);
401            
402            adapter.endMethod();
403                    
404            
405                    
406            if(classRoot!=null) {
407                    Resource classFile=classRoot.getRealResource(className+".class");
408                    return store(cw.toByteArray(),classFile);
409            }
410            return cw.toByteArray();
411            }
412            
413    
414            private static Type toReferenceType(Class<?> clazz, Type defaultValue) {
415                    if(int.class==clazz) return Types.INTEGER;
416                    else if(long.class==clazz) return Types.LONG;
417                    else if(char.class==clazz) return Types.CHARACTER;
418                    else if(byte.class==clazz) return Types.BYTE;
419                    else if(float.class==clazz) return Types.FLOAT;
420                    else if(double.class==clazz) return Types.DOUBLE;
421                    else if(boolean.class==clazz) return Types.BOOLEAN;
422                    else if(short.class==clazz) return Types.SHORT;
423                    return defaultValue;
424            }
425    
426            private static void loadClass(GeneratorAdapter adapter, Class<?> clazz) {
427                    if(void.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Void", "TYPE", "Ljava/lang/Class;");
428                    
429                    // primitive types
430                    else if(int.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Integer", "TYPE", "Ljava/lang/Class;");
431                    else if(long.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Long", "TYPE", "Ljava/lang/Class;");
432                    else if(char.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Character", "TYPE", "Ljava/lang/Class;");
433                    else if(byte.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Byte", "TYPE", "Ljava/lang/Class;");
434                    else if(float.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Float", "TYPE", "Ljava/lang/Class;");
435                    else if(double.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Double", "TYPE", "Ljava/lang/Class;");
436                    else if(boolean.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Boolean", "TYPE", "Ljava/lang/Class;");
437                    else if(short.class==clazz) adapter.visitFieldInsn(Opcodes.GETSTATIC, "java/lang/Short", "TYPE", "Ljava/lang/Class;");
438                    
439                    // TODO ref types
440                    
441                    else {
442                        adapter.visitVarInsn(Opcodes.ALOAD, 1);
443                    adapter.push(clazz.getName());
444                            adapter.invokeVirtual(Types.CLASS_LOADER,LOAD_CLASS );
445                    }
446            }
447    
448            private static void paramNames(StringBuilder sb, Class<?>[] params) {
449                    if(ArrayUtil.isEmpty(params)) return;
450                    
451                    for(int i=0;i<params.length;i++){
452                            sb.append('$');
453                            if(params[i].isArray())
454                                    sb.append(StringUtil.replace(Caster.toClassName(params[i]).replace('.', '_'),"[]","_arr",false));
455                            else
456                                    sb.append(params[i].getName().replace('.', '_'));
457                    }
458            }
459    
460            private static byte[] store(byte[] barr,Resource classFile) throws IOException {
461                    // create class file
462            ResourceUtil.touch(classFile);
463            //print.e(classFile);
464            IOUtil.copy(new ByteArrayInputStream(barr), classFile,true);
465           return barr;
466            }
467            /*private void store(ClassWriter cw) {
468                    // create class file
469            byte[] barr = cw.toByteArray();
470            
471            try {
472                    ResourceUtil.touch(classFile);
473                    IOUtil.copy(new ByteArrayInputStream(barr), classFile,true);
474                    
475                    cl = (PhysicalClassLoader) mapping.getConfig().getRPCClassLoader(true);
476                    Class<?> clazz = cl.loadClass(className, barr);
477                    return newInstance(clazz, config,cfc);
478            }
479            catch(Throwable t) {
480                    throw Caster.toPageException(t);
481            }
482            }*/
483            
484    }