package org.jboss.proxy.compiler;
import org.apache.bcel.Constants;
import org.apache.bcel.classfile.Field;
import org.apache.bcel.classfile.Method;
import org.apache.bcel.generic.ArrayType;
import org.apache.bcel.generic.BasicType;
import org.apache.bcel.generic.ConstantPoolGen;
import org.apache.bcel.generic.ClassGen;
import org.apache.bcel.generic.FieldGen;
import org.apache.bcel.generic.Instruction;
import org.apache.bcel.generic.InstructionFactory;
import org.apache.bcel.generic.InstructionList;
import org.apache.bcel.generic.MethodGen;
import org.apache.bcel.generic.ObjectType;
import org.apache.bcel.generic.PUSH;
import org.apache.bcel.generic.ReferenceType;
import org.apache.bcel.generic.Type;
public class ProxyImplementationFactory
{
private final static String RUNTIME_CN = Runtime.class.getName();
private final static String INVOCATION_HANDLER_CN = InvocationHandler.class.getName();
private final static String STRING_BUFFER_CN = StringBuffer.class.getName();
private final static ObjectType RUNTIME_T = (ObjectType)Utility.getType(Runtime.class);
private final static ObjectType INVOCATION_HANDLER_T = (ObjectType)Utility.getType(InvocationHandler.class);
private final static ArrayType ARRAY_OF_CLASS_T = new ArrayType("java.lang.Class", 1);
private final static ObjectType OBJECT_T = new ObjectType("java.lang.Object");
private final static ArrayType ARRAY_OF_OBJECT_T = new ArrayType("java.lang.Object", 1);
private final static ObjectType STRING_T = new ObjectType("java.lang.String");
private final static ObjectType STRING_BUFFER_T = new ObjectType("java.lang.StringBuffer");
private final static ObjectType PROXY_TARGET_T = new ObjectType(Proxies.ProxyTarget.class.getName());
private final static Type[] INVOKE_ARGS = { INVOCATION_HANDLER_T,
Type.INT,
ARRAY_OF_OBJECT_T };
private final static String GET_INVOCATION_HANDLER_MN = "getInvocationHandler";
private final static String GET_TARGET_TYPES_MN = "getTargetTypes";
private final static String TO_STRING_MN = "toString";
private final static String APPEND_MN = "append";
private final static String CTOR_MN = "<init>";
private final static String INVOCATION_HANDLER_FN = "invocationHandler";
private static Type PROXY_CLASS_T;
private InstructionList il = new InstructionList();
private String proxyClassName;
private String superClassName;
private ConstantPoolGen constPool;
private InstructionFactory iFactory;
public ProxyImplementationFactory(final String superClassName,
final String proxyClassName,
final ClassGen cg)
{
this.superClassName = superClassName;
this.proxyClassName = proxyClassName;
PROXY_CLASS_T = new ObjectType(proxyClassName);
constPool = cg.getConstantPool();
iFactory = new InstructionFactory(cg, constPool);
}
public Method createGetInvocationHandler()
{
MethodGen mg = new MethodGen(Constants.ACC_PUBLIC,
INVOCATION_HANDLER_T,
Type.NO_ARGS,
null, GET_INVOCATION_HANDLER_MN, proxyClassName, il, constPool);
il.append(iFactory.createLoad(PROXY_CLASS_T, 0));
il.append(iFactory.createGetField(proxyClassName, INVOCATION_HANDLER_FN, INVOCATION_HANDLER_T));
il.append(iFactory.createReturn(INVOCATION_HANDLER_T));
mg.stripAttributes(true);
mg.setMaxStack();
mg.setMaxLocals();
return getMethodAndTidyup(mg);
}
public Method createGetTargetTypes()
{
MethodGen mg = new MethodGen(Constants.ACC_PUBLIC,
ARRAY_OF_CLASS_T,
Type.NO_ARGS,
null,
GET_TARGET_TYPES_MN,
proxyClassName,
il,
constPool);
il.append(iFactory.createLoad(PROXY_CLASS_T, 0));
il.append(iFactory.createGetField(proxyClassName, Runtime.RUNTIME_FN, RUNTIME_T));
il.append(iFactory.createInvoke(RUNTIME_CN,
"copyTargetTypes",
ARRAY_OF_CLASS_T,
Type.NO_ARGS,
Constants.INVOKEVIRTUAL));
il.append(iFactory.createReturn(ARRAY_OF_CLASS_T));
mg.stripAttributes(true);
mg.setMaxStack(1);
mg.setMaxLocals();
return getMethodAndTidyup(mg);
}
public Method createToString()
{
MethodGen mg = new MethodGen(Constants.ACC_PUBLIC, STRING_T,
Type.NO_ARGS,
null, TO_STRING_MN, proxyClassName, il, constPool);
il.append(iFactory.createNew(STRING_BUFFER_T));
il.append(iFactory.createDup(1));
il.append(iFactory.createInvoke(STRING_BUFFER_CN,
CTOR_MN,
Type.VOID,
Type.NO_ARGS,
Constants.INVOKESPECIAL));
il.append(new PUSH(constPool, "ProxyTarget["));
il.append(iFactory.createInvoke(STRING_BUFFER_CN,
APPEND_MN,
STRING_BUFFER_T,
new Type[]{STRING_T},
Constants.INVOKEVIRTUAL));
il.append(iFactory.createLoad(PROXY_CLASS_T, 0));
il.append(iFactory.createGetField(proxyClassName, INVOCATION_HANDLER_FN, INVOCATION_HANDLER_T));
il.append(iFactory.createInvoke(STRING_BUFFER_CN,
APPEND_MN,
STRING_BUFFER_T,
new Type[]{OBJECT_T},
Constants.INVOKEVIRTUAL));
il.append(new PUSH(constPool, "]"));
il.append(iFactory.createInvoke(STRING_BUFFER_CN,
APPEND_MN,
STRING_BUFFER_T,
new Type[]{STRING_T},
Constants.INVOKEVIRTUAL));
il.append(iFactory.createInvoke(STRING_BUFFER_CN,
TO_STRING_MN,
STRING_T,
Type.NO_ARGS,
Constants.INVOKEVIRTUAL));
il.append(iFactory.createReturn(STRING_T));
mg.stripAttributes(true);
mg.setMaxStack();
mg.setMaxLocals();
return getMethodAndTidyup(mg);
}
public Method createConstructor()
{
MethodGen mg = new MethodGen(Constants.ACC_PUBLIC,
Type.VOID,
new Type[]{INVOCATION_HANDLER_T},
null,
CTOR_MN,
proxyClassName,
il,
constPool);
il.append(iFactory.createLoad(INVOCATION_HANDLER_T, 0));
il.append(iFactory.createInvoke(superClassName,
CTOR_MN,
Type.VOID,
Type.NO_ARGS,
Constants.INVOKESPECIAL));
il.append(iFactory.createLoad(PROXY_CLASS_T, 0));
il.append(iFactory.createLoad(INVOCATION_HANDLER_T, 1));
il.append(iFactory.createPutField(proxyClassName, INVOCATION_HANDLER_FN, INVOCATION_HANDLER_T));
il.append(iFactory.createReturn(Type.VOID));
mg.stripAttributes(true);
mg.setMaxStack();
mg.setMaxLocals();
return getMethodAndTidyup(mg);
}
public Method createProxyMethod(String name,
int methodNum,
Type rType,
Type[] pTypes,
String[] exceptionNames)
{
MethodGen mg = new MethodGen(Constants.ACC_PUBLIC,
rType,
pTypes,
null, name,
proxyClassName,
il,
constPool);
for (int j = 0; j < exceptionNames.length; j++) {
mg.addException(exceptionNames[j]);
}
il.append(iFactory.createGetStatic(proxyClassName, Runtime.RUNTIME_FN, RUNTIME_T));
il.append(iFactory.createLoad(RUNTIME_T, 0));
il.append(iFactory.createGetField(proxyClassName, INVOCATION_HANDLER_FN, INVOCATION_HANDLER_T));
il.append(new PUSH(constPool, methodNum));
il.append(new PUSH(constPool, pTypes.length));
il.append((Instruction)iFactory.createNewArray(OBJECT_T, (short)1));
if (pTypes.length > 0) {
int i = 1;
for (int j = 0; j < pTypes.length; j++) {
Type t = pTypes[j];
il.append(iFactory.createDup(1));
il.append(new PUSH(constPool, j));
if (t instanceof BasicType) {
String wrappedClassName = Utility.getObjectEquivalentClassName((BasicType)t);
ObjectType wrappedType = new ObjectType(wrappedClassName);
il.append(iFactory.createNew(wrappedType));
il.append(iFactory.createDup(1));
il.append(iFactory.createLoad(t, i));
il.append(iFactory.createInvoke(wrappedClassName,
CTOR_MN,
Type.VOID,
new Type[] { t },
Constants.INVOKESPECIAL));
switch (t.getType()) {
case Constants.T_DOUBLE: case Constants.T_LONG: i++;
}
t = wrappedType;
}
else {
il.append(iFactory.createLoad(t, i));
}
i++;
il.append(iFactory.createArrayStore(t));
}
}
il.append(iFactory.createInvoke(RUNTIME_CN,
"invoke",
Type.OBJECT,
INVOKE_ARGS,
Constants.INVOKEVIRTUAL));
if (rType instanceof ReferenceType) {
il.append(iFactory.createCheckCast((ReferenceType)rType));
}
else if (rType instanceof BasicType) {
if (rType == Type.VOID) {
il.append(iFactory.createPop(1));
}
else {
String wrappedClassName = Utility.getObjectEquivalentClassName((BasicType)rType);
ObjectType wrappedType = new ObjectType(wrappedClassName);
il.append(iFactory.createCheckCast((ReferenceType)wrappedType));
String methodName = Utility.signatureToString(rType.getSignature()) + "Value";
il.append(iFactory.createInvoke(wrappedClassName,
methodName,
rType,
Type.NO_ARGS,
Constants.INVOKEVIRTUAL));
}
}
il.append(iFactory.createReturn(rType));
mg.stripAttributes(true);
mg.setMaxStack();
mg.setMaxLocals();
return getMethodAndTidyup(mg);
}
public Field createInvocationHandlerField()
{
FieldGen fg = new FieldGen(Constants.ACC_PRIVATE,
INVOCATION_HANDLER_T,
INVOCATION_HANDLER_FN,
constPool);
return fg.getField();
}
public Field createRuntimeField()
{
FieldGen fg = new FieldGen(Constants.ACC_PUBLIC | Constants.ACC_STATIC,
RUNTIME_T,
Runtime.RUNTIME_FN,
constPool);
return fg.getField();
}
private Method getMethodAndTidyup(final MethodGen mg)
{
Method m = mg.getMethod();
il.dispose();
return m;
}
}