001 package railo.transformer.bytecode.util; 002 003 import java.io.ByteArrayInputStream; 004 import java.io.IOException; 005 import java.lang.reflect.Constructor; 006 import java.lang.reflect.InvocationTargetException; 007 import java.lang.reflect.Method; 008 import java.util.Arrays; 009 import java.util.HashMap; 010 import java.util.HashSet; 011 import java.util.Map; 012 import java.util.Set; 013 014 import org.objectweb.asm.ClassWriter; 015 import org.objectweb.asm.FieldVisitor; 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.util.ResourceUtil; 024 import railo.commons.lang.KeyGenerator; 025 import railo.commons.lang.PhysicalClassLoader; 026 import railo.runtime.Component; 027 import railo.runtime.Mapping; 028 import railo.runtime.PageContext; 029 import railo.runtime.PageContextImpl; 030 import railo.runtime.config.ConfigWeb; 031 import railo.runtime.exp.PageException; 032 import railo.runtime.java.JavaProxy; 033 import railo.runtime.listener.JavaSettingsImpl; 034 import railo.runtime.op.Caster; 035 import railo.runtime.reflection.Reflector; 036 import railo.transformer.bytecode.visitor.ArrayVisitor; 037 038 /** 039 * creates a Java Proxy for components, so you can use componets as java classes following a certain interface or class 040 */ 041 public class JavaProxyFactory { 042 043 044 private static final String COMPONENT_NAME="L"+Types.COMPONENT.getInternalName()+";"; 045 private static final String CONFIG_WEB_NAME="L"+Types.CONFIG_WEB.getInternalName()+";"; 046 047 private static final Type JAVA_PROXY = Type.getType(JavaProxy.class); 048 049 050 private static final org.objectweb.asm.commons.Method CALL = new org.objectweb.asm.commons.Method( 051 "call", 052 Types.OBJECT, 053 new Type[]{Types.CONFIG_WEB,Types.COMPONENT,Types.STRING,Types.OBJECT_ARRAY}); 054 055 private static final org.objectweb.asm.commons.Method CONSTRUCTOR = new org.objectweb.asm.commons.Method( 056 "<init>", 057 Types.VOID, 058 new Type[]{ 059 Types.CONFIG_WEB, 060 Types.COMPONENT 061 } 062 ); 063 private static final org.objectweb.asm.commons.Method SUPER_CONSTRUCTOR = new org.objectweb.asm.commons.Method( 064 "<init>", 065 Types.VOID, 066 new Type[]{} 067 ); 068 069 private static final org.objectweb.asm.commons.Method TO_BOOLEAN = new org.objectweb.asm.commons.Method( 070 "toBoolean", 071 Types.BOOLEAN_VALUE, 072 new Type[]{Types.OBJECT}); 073 private static final org.objectweb.asm.commons.Method TO_FLOAT = new org.objectweb.asm.commons.Method( 074 "toFloat", 075 Types.FLOAT_VALUE, 076 new Type[]{Types.OBJECT}); 077 private static final org.objectweb.asm.commons.Method TO_INT = new org.objectweb.asm.commons.Method( 078 "toInt", 079 Types.INT_VALUE, 080 new Type[]{Types.OBJECT}); 081 private static final org.objectweb.asm.commons.Method TO_DOUBLE = new org.objectweb.asm.commons.Method( 082 "toDouble", 083 Types.DOUBLE_VALUE, 084 new Type[]{Types.OBJECT}); 085 private static final org.objectweb.asm.commons.Method TO_LONG = new org.objectweb.asm.commons.Method( 086 "toLong", 087 Types.LONG_VALUE, 088 new Type[]{Types.OBJECT}); 089 private static final org.objectweb.asm.commons.Method TO_CHAR = new org.objectweb.asm.commons.Method( 090 "toChar", 091 Types.CHAR, 092 new Type[]{Types.OBJECT}); 093 private static final org.objectweb.asm.commons.Method TO_BYTE = new org.objectweb.asm.commons.Method( 094 "toByte", 095 Types.BYTE_VALUE, 096 new Type[]{Types.OBJECT}); 097 private static final org.objectweb.asm.commons.Method TO_SHORT = new org.objectweb.asm.commons.Method( 098 "toShort", 099 Types.SHORT, 100 new Type[]{Types.OBJECT}); 101 private static final org.objectweb.asm.commons.Method TO_ = new org.objectweb.asm.commons.Method( 102 "to", 103 Types.OBJECT, 104 new Type[]{Types.OBJECT,Types.CLASS}); 105 106 107 108 private static final org.objectweb.asm.commons.Method _BOOLEAN = new org.objectweb.asm.commons.Method( 109 "toCFML", 110 Types.OBJECT, 111 new Type[]{Types.BOOLEAN_VALUE}); 112 private static final org.objectweb.asm.commons.Method _FLOAT = new org.objectweb.asm.commons.Method( 113 "toCFML", 114 Types.OBJECT, 115 new Type[]{Types.FLOAT_VALUE}); 116 private static final org.objectweb.asm.commons.Method _INT = new org.objectweb.asm.commons.Method( 117 "toCFML", 118 Types.OBJECT, 119 new Type[]{Types.INT_VALUE}); 120 private static final org.objectweb.asm.commons.Method _DOUBLE = new org.objectweb.asm.commons.Method( 121 "toCFML", 122 Types.OBJECT, 123 new Type[]{Types.DOUBLE_VALUE}); 124 private static final org.objectweb.asm.commons.Method _LONG = new org.objectweb.asm.commons.Method( 125 "toCFML", 126 Types.OBJECT, 127 new Type[]{Types.LONG_VALUE}); 128 private static final org.objectweb.asm.commons.Method _CHAR = new org.objectweb.asm.commons.Method( 129 "toCFML", 130 Types.OBJECT, 131 new Type[]{Types.CHAR}); 132 private static final org.objectweb.asm.commons.Method _BYTE = new org.objectweb.asm.commons.Method( 133 "toCFML", 134 Types.OBJECT, 135 new Type[]{Types.BYTE_VALUE}); 136 private static final org.objectweb.asm.commons.Method _SHORT = new org.objectweb.asm.commons.Method( 137 "toCFML", 138 Types.OBJECT, 139 new Type[]{Types.SHORT}); 140 private static final org.objectweb.asm.commons.Method _OBJECT = new org.objectweb.asm.commons.Method( 141 "toCFML", 142 Types.OBJECT, 143 new Type[]{Types.OBJECT}); 144 145 146 147 148 149 150 /* 151 152 public static Object to(Object obj, Class clazz) { 153 return obj; 154 }*/ 155 156 157 158 159 /*public static Object createProxy(Config config,Component cfc, String className) throws PageException, IOException { 160 return createProxy(cfc, null, ClassUtil.loadClass(config.getClassLoader(), className)); 161 }*/ 162 163 public static Object createProxy(PageContext pc, Component cfc, Class extendz,Class... interfaces) throws PageException, IOException { 164 PageContextImpl pci=(PageContextImpl) pc; 165 if(extendz==null) extendz=Object.class; 166 if(interfaces==null) interfaces=new Class[0]; 167 else { 168 for(int i=0;i<interfaces.length;i++){ 169 if(!interfaces[i].isInterface()) 170 throw new IOException("definition ["+interfaces[i].getName()+"] is a class and not a interface"); 171 } 172 } 173 174 175 176 Type typeExtends = Type.getType(extendz); 177 Type[] typeInterfaces = ASMUtil.toTypes(interfaces); 178 String[] strInterfaces=new String[typeInterfaces.length]; 179 for(int i=0;i<strInterfaces.length;i++){ 180 strInterfaces[i]=typeInterfaces[i].getInternalName(); 181 } 182 183 184 String className=createClassName(extendz,interfaces); 185 //Mapping mapping = cfc.getPageSource().getMapping(); 186 187 // get ClassLoader 188 PhysicalClassLoader cl=null; 189 try { 190 cl = (PhysicalClassLoader) pci.getRPCClassLoader(false);// mapping.getConfig().getRPCClassLoader(false) 191 } catch (IOException e) { 192 throw Caster.toPageException(e); 193 } 194 Resource classFile = cl.getDirectory().getRealResource(className.concat(".class")); 195 196 // check if already exists, if yes return 197 if(classFile.exists()) { 198 //Object obj=newInstance(cl,className,cfc); 199 // if(obj!=null) return obj; 200 } 201 202 /* 203 String classNameOriginal=component.getPageSource().getFullClassName(); 204 String realOriginal=classNameOriginal.replace('.','/'); 205 Resource classFileOriginal = mapping.getClassRootDirectory().getRealResource(realOriginal.concat(".class")); 206 */ 207 ClassWriter cw = ASMUtil.getClassWriter(); 208 209 cw.visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC, className, null, typeExtends.getInternalName(), strInterfaces); 210 //BytecodeContext statConstr = null;//new BytecodeContext(null,null,null,cw,real,ga,Page.STATIC_CONSTRUCTOR); 211 //BytecodeContext constr = null;//new BytecodeContext(null,null,null,cw,real,ga,Page.CONSTRUCTOR); 212 213 214 // field Component 215 FieldVisitor _fv = cw.visitField(Opcodes.ACC_PRIVATE, "cfc", COMPONENT_NAME, null, null); 216 _fv.visitEnd(); 217 _fv = cw.visitField(Opcodes.ACC_PRIVATE, "config", CONFIG_WEB_NAME, null, null); 218 _fv.visitEnd(); 219 220 // Constructor 221 GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC,CONSTRUCTOR,null,null,cw); 222 Label begin = new Label(); 223 adapter.visitLabel(begin); 224 adapter.loadThis(); 225 adapter.invokeConstructor(Types.OBJECT, SUPER_CONSTRUCTOR); 226 227 //adapter.putField(JAVA_PROXY, arg1, arg2) 228 229 adapter.visitVarInsn(Opcodes.ALOAD, 0); 230 adapter.visitVarInsn(Opcodes.ALOAD, 1); 231 adapter.visitFieldInsn(Opcodes.PUTFIELD, className, "config", CONFIG_WEB_NAME); 232 233 adapter.visitVarInsn(Opcodes.ALOAD, 0); 234 adapter.visitVarInsn(Opcodes.ALOAD, 2); 235 adapter.visitFieldInsn(Opcodes.PUTFIELD, className, "cfc", COMPONENT_NAME); 236 237 adapter.visitInsn(Opcodes.RETURN); 238 Label end = new Label(); 239 adapter.visitLabel(end); 240 adapter.visitLocalVariable("config",CONFIG_WEB_NAME, null, begin, end, 1); 241 adapter.visitLocalVariable("cfc",COMPONENT_NAME, null, begin, end, 2); 242 243 //adapter.returnValue(); 244 adapter.endMethod(); 245 246 247 // create methods 248 Set<Class> cDone=new HashSet<Class>(); 249 Map<String,Class> mDone=new HashMap<String,Class>(); 250 for(int i=0;i<interfaces.length;i++){ 251 _createProxy(cw,cDone,mDone, cfc, interfaces[i],className); 252 } 253 cw.visitEnd(); 254 255 256 // create class file 257 byte[] barr = cw.toByteArray(); 258 259 try { 260 ResourceUtil.touch(classFile); 261 IOUtil.copy(new ByteArrayInputStream(barr), classFile,true); 262 263 cl = (PhysicalClassLoader) pci.getRPCClassLoader(true); 264 Class<?> clazz = cl.loadClass(className, barr); 265 return newInstance(clazz, pc.getConfig(),cfc); 266 } 267 catch(Throwable t) { 268 throw Caster.toPageException(t); 269 } 270 } 271 272 private static void _createProxy(ClassWriter cw, Set<Class> cDone,Map<String,Class> mDone, Component cfc, Class clazz, String className) throws IOException { 273 if(cDone.contains(clazz)) return; 274 275 cDone.add(clazz); 276 277 // super class 278 Class superClass = clazz.getSuperclass(); 279 if(superClass!=null)_createProxy(cw, cDone,mDone, cfc, superClass,className); 280 281 // interfaces 282 Class[] interfaces = clazz.getInterfaces(); 283 if(interfaces!=null)for(int i=0;i<interfaces.length;i++){ 284 _createProxy(cw,cDone,mDone, cfc, interfaces[i],className); 285 } 286 287 Method[] methods = clazz.getMethods(); 288 if(methods!=null)for(int i=0;i<methods.length;i++){ 289 _createMethod(cw,mDone,methods[i],className); 290 } 291 } 292 293 private static void _createMethod(ClassWriter cw, Map<String,Class> mDone, Method src, String className) throws IOException { 294 Class<?>[] classArgs = src.getParameterTypes(); 295 Class<?> classRtn = src.getReturnType(); 296 297 String str=src.getName()+"("+Reflector.getDspMethods(classArgs)+")"; 298 Class rtnClass = mDone.get(str); 299 if(rtnClass!=null) { 300 if(rtnClass!=classRtn) throw new IOException("there is a conflict with method ["+str+"], this method is declared more than once with different return types."); 301 return; 302 } 303 mDone.put(str,classRtn); 304 305 Type[] typeArgs = ASMUtil.toTypes(classArgs); 306 Type typeRtn = Type.getType(classRtn); 307 308 org.objectweb.asm.commons.Method method = new org.objectweb.asm.commons.Method( 309 src.getName(), 310 typeRtn, 311 typeArgs 312 ); 313 GeneratorAdapter adapter = new GeneratorAdapter(Opcodes.ACC_PUBLIC+Opcodes.ACC_FINAL , method, null, null, cw); 314 //BytecodeContext bc = new BytecodeContext(statConstr,constr,null,null,keys,cw,className,adapter,method,writeLog); 315 Label start=adapter.newLabel(); 316 adapter.visitLabel(start); 317 318 319 //JavaProxy.call(cfc,"add",new Object[]{arg0}) 320 // config 321 adapter.visitVarInsn(Opcodes.ALOAD, 0); 322 adapter.visitFieldInsn(Opcodes.GETFIELD, className, "config", CONFIG_WEB_NAME); 323 324 // cfc 325 adapter.visitVarInsn(Opcodes.ALOAD, 0); 326 adapter.visitFieldInsn(Opcodes.GETFIELD, className, "cfc", COMPONENT_NAME); 327 328 // name 329 adapter.push(src.getName()); 330 331 // arguments 332 ArrayVisitor av=new ArrayVisitor(); 333 av.visitBegin(adapter,Types.OBJECT,typeArgs.length); 334 for(int y=0;y<typeArgs.length;y++){ 335 av.visitBeginItem(adapter, y); 336 adapter.loadArg(y); 337 if(classArgs[y]==boolean.class) adapter.invokeStatic(JAVA_PROXY, _BOOLEAN); 338 else if(classArgs[y]==byte.class) adapter.invokeStatic(JAVA_PROXY, _BYTE); 339 else if(classArgs[y]==char.class) adapter.invokeStatic(JAVA_PROXY, _CHAR); 340 else if(classArgs[y]==double.class) adapter.invokeStatic(JAVA_PROXY, _DOUBLE); 341 else if(classArgs[y]==float.class) adapter.invokeStatic(JAVA_PROXY, _FLOAT); 342 else if(classArgs[y]==int.class) adapter.invokeStatic(JAVA_PROXY, _INT); 343 else if(classArgs[y]==long.class) adapter.invokeStatic(JAVA_PROXY, _LONG); 344 else if(classArgs[y]==short.class) adapter.invokeStatic(JAVA_PROXY, _SHORT); 345 else { 346 adapter.invokeStatic(JAVA_PROXY, _OBJECT); 347 } 348 349 350 av.visitEndItem(adapter); 351 } 352 av.visitEnd(); 353 adapter.invokeStatic(JAVA_PROXY, CALL); 354 355 //JavaProxy.to...(...); 356 int rtn=Opcodes.IRETURN; 357 if(classRtn==boolean.class) adapter.invokeStatic(JAVA_PROXY, TO_BOOLEAN); 358 else if(classRtn==byte.class) adapter.invokeStatic(JAVA_PROXY, TO_BYTE); 359 else if(classRtn==char.class) adapter.invokeStatic(JAVA_PROXY, TO_CHAR); 360 else if(classRtn==double.class){ 361 rtn=Opcodes.DRETURN; 362 adapter.invokeStatic(JAVA_PROXY, TO_DOUBLE); 363 } 364 else if(classRtn==float.class) { 365 rtn=Opcodes.FRETURN; 366 adapter.invokeStatic(JAVA_PROXY, TO_FLOAT); 367 } 368 else if(classRtn==int.class) adapter.invokeStatic(JAVA_PROXY, TO_INT); 369 else if(classRtn==long.class) { 370 rtn=Opcodes.LRETURN; 371 adapter.invokeStatic(JAVA_PROXY, TO_LONG); 372 } 373 else if(classRtn==short.class) adapter.invokeStatic(JAVA_PROXY, TO_SHORT); 374 else if(classRtn==void.class){ 375 rtn=Opcodes.RETURN; 376 adapter.pop(); 377 } 378 else { 379 rtn=Opcodes.ARETURN; 380 adapter.checkCast(typeRtn); 381 } 382 383 384 385 /*mv = cw.visitMethod(ACC_PUBLIC, "add", "(Ljava/lang/Object;)Z", null, null); 386 mv.visitCode(); 387 Label l0 = new Label(); 388 mv.visitLabel(l0); 389 mv.visitLineNumber(20, l0); 390 mv.visitVarInsn(ALOAD, 0); 391 mv.visitFieldInsn(GETFIELD, "Test", "cfc", "Ljava/lang/Object;"); 392 mv.visitLdcInsn("add"); 393 mv.visitInsn(ICONST_1); 394 mv.visitTypeInsn(ANEWARRAY, "java/lang/Object"); 395 mv.visitInsn(DUP); 396 mv.visitInsn(ICONST_0); 397 mv.visitVarInsn(ALOAD, 1); 398 mv.visitInsn(AASTORE); 399 mv.visitMethodInsn(INVOKESTATIC, "JavaProxy", "call", "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)Ljava/lang/Object;"); 400 mv.visitMethodInsn(INVOKESTATIC, "JavaProxy", "toBoolean", "(Ljava/lang/Object;)Z"); 401 mv.visitInsn(IRETURN); 402 Label l1 = new Label(); 403 mv.visitLabel(l1); 404 mv.visitLocalVariable("this", "LTest;", null, l0, l1, 0); 405 mv.visitLocalVariable("arg0", "Ljava/lang/Object;", null, l0, l1, 1); 406 mv.visitMaxs(6, 2); 407 mv.visitEnd();*/ 408 409 410 411 adapter.visitInsn(rtn); 412 adapter.endMethod(); 413 414 415 } 416 417 private static Object newInstance(PhysicalClassLoader cl, String className, ConfigWeb config,Component cfc) 418 throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException, ClassNotFoundException { 419 return newInstance(cl.loadClass(className),config,cfc); 420 } 421 private static Object newInstance(Class<?> _clazz,ConfigWeb config, Component cfc) 422 throws IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException, SecurityException, NoSuchMethodException { 423 Constructor<?> constr = _clazz.getConstructor(new Class[]{ConfigWeb.class,Component.class}); 424 return constr.newInstance(new Object[]{config,cfc}); 425 } 426 427 private static String createClassName(Class extendz, Class[] interfaces) throws IOException { 428 if(extendz==null) extendz=Object.class; 429 430 431 432 StringBuilder sb=new StringBuilder(extendz.getName()); 433 if(interfaces!=null && interfaces.length>0){ 434 sb.append(';'); 435 436 String[] arr=new String[interfaces.length]; 437 for(int i=0;i<interfaces.length;i++){ 438 arr[i]=interfaces[i].getName(); 439 } 440 Arrays.sort(arr); 441 442 sb.append(railo.runtime.type.util.ListUtil.arrayToList(arr, ";")); 443 } 444 445 String key = KeyGenerator.createVariable(sb.toString()); 446 447 448 return key; 449 } 450 451 452 453 }