Java tutorial
/** * * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see <http://www.gnu.org/licenses/>. * **/ package lucee.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 lucee.commons.io.IOUtil; import lucee.commons.io.res.Resource; import lucee.commons.io.res.ResourceProvider; import lucee.commons.io.res.ResourcesImpl; import lucee.commons.io.res.util.ResourceUtil; import lucee.commons.lang.ClassUtil; import lucee.commons.lang.ExtendableClassLoader; import lucee.commons.lang.PhysicalClassLoader; import lucee.commons.lang.StringUtil; import lucee.runtime.PageContext; import lucee.runtime.functions.arrays.ArrayNew; import lucee.runtime.op.Caster; import lucee.runtime.type.util.ArrayUtil; import lucee.transformer.bytecode.util.ASMConstants; import lucee.transformer.bytecode.util.ASMUtil; import lucee.transformer.bytecode.util.Types; 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; 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/Lucee/webroot/WEB-INF/lucee/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); } }*/ }