/* * JBoss, Home of Professional Open Source. * Copyright 2008, Red Hat Middleware LLC, and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This 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 software 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 software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ 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; /** * Factory to create the bytecode implementation of various methods * required by the ProxyCompiler. * * @version <tt>$Revision: 81030 $</tt> * @author <a href="mailto:neale@isismanor.co.uk">Neale Swinnerton</a> * @author <a href="mailto:jason@planet57.com">Jason Dillon</a> */ public class ProxyImplementationFactory { // Class Names 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(); // Types 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 }; // Method Names 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>"; // Field Names private final static String INVOCATION_HANDLER_FN = "invocationHandler"; /** The proxy class type (assigned in the ctor) */ private static Type PROXY_CLASS_T; private InstructionList il = new InstructionList(); private String proxyClassName; private String superClassName; private ConstantPoolGen constPool; private InstructionFactory iFactory; /** * Creates a new <code>ProxyImplementationFactory</code> instance. * * @param superClassName a <code>String</code> value * @param proxyClassName a <code>String</code> value * @param cg a <code>ClassGen</code> value */ 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); } /** * generate an implementation of * <pre> * * <code> * public InvocationHandler getInvocationHandler() { * return this.invocationHandler; * } * </code> * * </pre> */ 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); } /** * generate an implementation of * <pre> * * <code> * public Class[] getTargetTypes { * return this.invocationHandler.copyTargetTypes(); * } * </code> * * </pre> * * @return the method * */ 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); } /** * generate an implementation of * <pre> * * <code> * public String toString() { * return "ProxyTarget[" + invocationHandler + "]"; * } * </code> * * </pre> * * @return the method * */ 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); } /** * generate an implementation of * <pre> * * <xmp> * public <proxyClassName> (InvocationHandler h) { * this.invocationHandler = h; * } * </xmp> * * </pre> * * @return the method * */ 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); } /** * generate an implementation of... * <pre> * * <xmp> * public <return type> <method name>(<p0 type> p0, <p1 type> p1, ...) * throws e0, e1 ... * { * return runtme.invoke(invocatioHandler, <method index>, * new Object[]{boxed p0, boxed p1, ...)}; * } * </xmp> * * </pre> * * @return the method */ public Method createProxyMethod(String name, int methodNum, Type rType, Type[] pTypes, String[] exceptionNames) { MethodGen mg = new MethodGen(Constants.ACC_PUBLIC, rType, pTypes, null, // argNames name, proxyClassName, il, constPool); for (int j = 0; j < exceptionNames.length; j++) { mg.addException(exceptionNames[j]); } // implementation of this.invocationHandler.invoke<Type>(InvocationHandler, i, new Object[]{ ... }) il.append(iFactory.createGetStatic(proxyClassName, Runtime.RUNTIME_FN, RUNTIME_T)); il.append(iFactory.createLoad(RUNTIME_T, 0)); // load the first method param (the ih) il.append(iFactory.createGetField(proxyClassName, INVOCATION_HANDLER_FN, INVOCATION_HANDLER_T)); // load the second method param (the method id) il.append(new PUSH(constPool, methodNum)); // create a new array to hold param values il.append(new PUSH(constPool, pTypes.length)); il.append((Instruction)iFactory.createNewArray(OBJECT_T, (short)1)); if (pTypes.length > 0) { // the register index int i = 1; // register 0 loaded with runtime ? for (int j = 0; j < pTypes.length; j++) { Type t = pTypes[j]; // not sure what this does il.append(iFactory.createDup(1)); // load the index of the array element il.append(new PUSH(constPool, j)); // box basic types into wrapped versions if (t instanceof BasicType) { // do a e.g new Boolean(b) String wrappedClassName = Utility.getObjectEquivalentClassName((BasicType)t); ObjectType wrappedType = new ObjectType(wrappedClassName); il.append(iFactory.createNew(wrappedType)); // again, what does this do? il.append(iFactory.createDup(1)); // load the parameter value from the register index il.append(iFactory.createLoad(t, i)); il.append(iFactory.createInvoke(wrappedClassName, CTOR_MN, Type.VOID, new Type[] { t }, Constants.INVOKESPECIAL)); // increment register index for long & double switch (t.getType()) { case Constants.T_DOUBLE: // 7 case Constants.T_LONG: // 11 i++; } // type is now wrapped type t = wrappedType; } else { // just load the value in to the register slot il.append(iFactory.createLoad(t, i)); } // increment register index for everything // (makes += 2 for long & double) with above ++ i++; // store the value into the array il.append(iFactory.createArrayStore(t)); } } il.append(iFactory.createInvoke(RUNTIME_CN, "invoke", Type.OBJECT, INVOKE_ARGS, Constants.INVOKEVIRTUAL)); // handle the return value if (rType instanceof ReferenceType) { il.append(iFactory.createCheckCast((ReferenceType)rType)); } else if (rType instanceof BasicType) { if (rType == Type.VOID) { // Chuck away returned value if it's void il.append(iFactory.createPop(1)); } else { // unbox the return value of a primitive wrapper... // we've got an Object and need the equivalent primitive // do a e.g. (Boolean)obj.booleanValue(); 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); } /** * generate a field declaration of the form... * <pre> * * <code> * private InvocationHandler invocationHandler; * </code> * * </pre> * * @return the method * */ public Field createInvocationHandlerField() { FieldGen fg = new FieldGen(Constants.ACC_PRIVATE, INVOCATION_HANDLER_T, INVOCATION_HANDLER_FN, constPool); return fg.getField(); } /** * generate a field declaration of the form... * <pre> * * <code> * public static Runtime runtime; * </code> * * </pre> * * @return the method * */ public Field createRuntimeField() { FieldGen fg = new FieldGen(Constants.ACC_PUBLIC | Constants.ACC_STATIC, RUNTIME_T, Runtime.RUNTIME_FN, constPool); return fg.getField(); } /** * A helper to return the method from MethodGen and clean up the * instruction list. */ private Method getMethodAndTidyup(final MethodGen mg) { Method m = mg.getMethod(); il.dispose(); return m; } }