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