package railo.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 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;
import railo.commons.io.IOUtil;
import railo.commons.io.res.Resource;
import railo.commons.io.res.ResourceProvider;
import railo.commons.io.res.ResourcesImpl;
import railo.commons.io.res.util.ResourceUtil;
import railo.commons.lang.ClassUtil;
import railo.commons.lang.ExtendableClassLoader;
import railo.commons.lang.PhysicalClassLoader;
import railo.commons.lang.StringUtil;
import railo.runtime.PageContext;
import railo.runtime.functions.arrays.ArrayNew;
import railo.runtime.op.Caster;
import railo.runtime.type.util.ArrayUtil;
import railo.transformer.bytecode.util.ASMConstants;
import railo.transformer.bytecode.util.ASMUtil;
import railo.transformer.bytecode.util.Types;
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/Railo/webroot/WEB-INF/railo/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);
}
}*/
}