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 }