diff options
Diffstat (limited to 'tools/gnu/classpath/tools/rmic/ClassRmicCompiler.java')
-rw-r--r-- | tools/gnu/classpath/tools/rmic/ClassRmicCompiler.java | 1824 |
1 files changed, 1824 insertions, 0 deletions
diff --git a/tools/gnu/classpath/tools/rmic/ClassRmicCompiler.java b/tools/gnu/classpath/tools/rmic/ClassRmicCompiler.java new file mode 100644 index 000000000..9c50efa8f --- /dev/null +++ b/tools/gnu/classpath/tools/rmic/ClassRmicCompiler.java @@ -0,0 +1,1824 @@ +/* ClassRmicCompiler.java -- + Copyright (c) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 +General Public License for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA +02111-1307 USA. */ + +package gnu.classpath.tools.rmic; + +import gnu.java.rmi.server.RMIHashes; +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.io.ObjectInput; +import java.io.ObjectOutput; +import java.io.PrintWriter; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.rmi.MarshalException; +import java.rmi.Remote; +import java.rmi.RemoteException; +import java.rmi.UnexpectedException; +import java.rmi.UnmarshalException; +import java.rmi.server.Operation; +import java.rmi.server.RemoteCall; +import java.rmi.server.RemoteObject; +import java.rmi.server.RemoteRef; +import java.rmi.server.RemoteStub; +import java.rmi.server.Skeleton; +import java.rmi.server.SkeletonMismatchException; +import java.security.MessageDigest; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.StringTokenizer; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Label; +import org.objectweb.asm.Type; + +public class ClassRmicCompiler + implements RmicBackend +{ + private String[] args; + private int next; + private List errors = new ArrayList(); + private boolean keep = false; + private boolean need11Stubs = true; + private boolean need12Stubs = true; + private boolean compile = true; + private boolean verbose; + private boolean noWrite; + private String destination; + private String classpath; + private ClassLoader loader; + private int errorCount = 0; + + private Class clazz; + private String classname; + private String classInternalName; + private String fullclassname; + private MethodRef[] remotemethods; + private String stubname; + private String skelname; + private List mRemoteInterfaces; + + /** + * @return true if run was successful + */ + public boolean run(String[] inputFiles) + { + args = inputFiles; + + if (next >= args.length) + return false; + + for (int i = next; i < args.length; i++) + { + try + { + if (verbose) + System.out.println("[Processing class " + args[i] + ".class]"); + processClass(args[i].replace(File.separatorChar, '.')); + } + catch (IOException e) + { + errors.add(e); + } + catch (RMICException e) + { + errors.add(e); + } + } + if (errors.size() > 0) + { + for (Iterator it = errors.iterator(); it.hasNext(); ) + { + Exception ex = (Exception) it.next(); + logError(ex); + } + } + + return errorCount == 0; + } + + private void processClass(String cls) throws IOException, RMICException + { + // reset class specific vars + clazz = null; + classname = null; + classInternalName = null; + fullclassname = null; + remotemethods = null; + stubname = null; + skelname = null; + mRemoteInterfaces = new ArrayList(); + + analyzeClass(cls); + generateStub(); + if (need11Stubs) + generateSkel(); + } + + private void analyzeClass(String cname) + throws RMICException + { + if (verbose) + System.out.println("[analyze class " + cname + "]"); + int p = cname.lastIndexOf('.'); + if (p != -1) + classname = cname.substring(p + 1); + else + classname = cname; + fullclassname = cname; + + findClass(); + findRemoteMethods(); + } + + /** + * @deprecated + */ + public Exception getException() + { + return errors.size() == 0 ? null : (Exception) errors.get(0); + } + + private void findClass() + throws RMICException + { + ClassLoader cl = (loader == null + ? ClassLoader.getSystemClassLoader() + : loader); + try + { + clazz = Class.forName(fullclassname, false, cl); + } + catch (ClassNotFoundException cnfe) + { + throw new RMICException + ("Class " + fullclassname + " not found in classpath", cnfe); + } + + if (! Remote.class.isAssignableFrom(clazz)) + { + throw new RMICException + ("Class " + clazz.getName() + + " does not implement a remote interface."); + } + } + + private static Type[] typeArray(Class[] cls) + { + Type[] t = new Type[cls.length]; + for (int i = 0; i < cls.length; i++) + { + t[i] = Type.getType(cls[i]); + } + + return t; + } + + private static String[] internalNameArray(Type[] t) + { + String[] s = new String[t.length]; + for (int i = 0; i < t.length; i++) + { + s[i] = t[i].getInternalName(); + } + + return s; + } + + private static String[] internalNameArray(Class[] c) + { + return internalNameArray(typeArray(c)); + } + + private static final String forName = "class$"; + + private static Object param(Method m, int argIndex) + { + List l = new ArrayList(); + l.add(m); + l.add(new Integer(argIndex)); + return l; + } + + private static void generateClassForNamer(ClassVisitor cls) + { + MethodVisitor cv = + cls.visitMethod + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC, forName, + Type.getMethodDescriptor + (Type.getType(Class.class), new Type[] { Type.getType(String.class) }), + null, null); + + Label start = new Label(); + cv.visitLabel(start); + cv.visitVarInsn(Opcodes.ALOAD, 0); + cv.visitMethodInsn + (Opcodes.INVOKESTATIC, + Type.getInternalName(Class.class), + "forName", + Type.getMethodDescriptor + (Type.getType(Class.class), new Type[] { Type.getType(String.class) })); + cv.visitInsn(Opcodes.ARETURN); + + Label handler = new Label(); + cv.visitLabel(handler); + cv.visitVarInsn(Opcodes.ASTORE, 1); + cv.visitTypeInsn(Opcodes.NEW, typeArg(NoClassDefFoundError.class)); + cv.visitInsn(Opcodes.DUP); + cv.visitVarInsn(Opcodes.ALOAD, 1); + cv.visitMethodInsn + (Opcodes.INVOKEVIRTUAL, + Type.getInternalName(ClassNotFoundException.class), + "getMessage", + Type.getMethodDescriptor(Type.getType(String.class), new Type[] {})); + cv.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(NoClassDefFoundError.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(String.class) })); + cv.visitInsn(Opcodes.ATHROW); + cv.visitTryCatchBlock + (start, handler, handler, + Type.getInternalName(ClassNotFoundException.class)); + cv.visitMaxs(-1, -1); + } + + private void generateClassConstant(MethodVisitor cv, Class cls) { + if (cls.isPrimitive()) + { + Class boxCls; + if (cls.equals(Boolean.TYPE)) + boxCls = Boolean.class; + else if (cls.equals(Character.TYPE)) + boxCls = Character.class; + else if (cls.equals(Byte.TYPE)) + boxCls = Byte.class; + else if (cls.equals(Short.TYPE)) + boxCls = Short.class; + else if (cls.equals(Integer.TYPE)) + boxCls = Integer.class; + else if (cls.equals(Long.TYPE)) + boxCls = Long.class; + else if (cls.equals(Float.TYPE)) + boxCls = Float.class; + else if (cls.equals(Double.TYPE)) + boxCls = Double.class; + else if (cls.equals(Void.TYPE)) + boxCls = Void.class; + else + throw new IllegalArgumentException("unknown primitive type " + cls); + + cv.visitFieldInsn + (Opcodes.GETSTATIC, Type.getInternalName(boxCls), "TYPE", + Type.getDescriptor(Class.class)); + return; + } + cv.visitLdcInsn(cls.getName()); + cv.visitMethodInsn + (Opcodes.INVOKESTATIC, classInternalName, forName, + Type.getMethodDescriptor + (Type.getType(Class.class), + new Type[] { Type.getType(String.class) })); + } + + private void generateClassArray(MethodVisitor code, Class[] classes) + { + code.visitLdcInsn(new Integer(classes.length)); + code.visitTypeInsn(Opcodes.ANEWARRAY, typeArg(Class.class)); + for (int i = 0; i < classes.length; i++) + { + code.visitInsn(Opcodes.DUP); + code.visitLdcInsn(new Integer(i)); + generateClassConstant(code, classes[i]); + code.visitInsn(Opcodes.AASTORE); + } + } + + private void fillOperationArray(MethodVisitor clinit) + { + // Operations array + clinit.visitLdcInsn(new Integer(remotemethods.length)); + clinit.visitTypeInsn(Opcodes.ANEWARRAY, typeArg(Operation.class)); + clinit.visitFieldInsn + (Opcodes.PUTSTATIC, classInternalName, "operations", + Type.getDescriptor(Operation[].class)); + + for (int i = 0; i < remotemethods.length; i++) + { + Method m = remotemethods[i].meth; + + StringBuffer desc = new StringBuffer(); + desc.append(getPrettyName(m.getReturnType()) + " "); + desc.append(m.getName() + "("); + + // signature + Class[] sig = m.getParameterTypes(); + for (int j = 0; j < sig.length; j++) + { + desc.append(getPrettyName(sig[j])); + if (j + 1 < sig.length) + desc.append(", "); + } + + // push operations array + clinit.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, "operations", + Type.getDescriptor(Operation[].class)); + + // push array index + clinit.visitLdcInsn(new Integer(i)); + + // instantiate operation and leave a copy on the stack + clinit.visitTypeInsn(Opcodes.NEW, typeArg(Operation.class)); + clinit.visitInsn(Opcodes.DUP); + clinit.visitLdcInsn(desc.toString()); + clinit.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(Operation.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(String.class) })); + + // store in operations array + clinit.visitInsn(Opcodes.AASTORE); + } + } + + private void generateStaticMethodObjs(MethodVisitor clinit) + { + for (int i = 0; i < remotemethods.length; i++) + { + Method m = remotemethods[i].meth; + + /* + * $method_<i>m.getName()</i>_<i>i</i> = + * <i>m.getDeclaringClass()</i>.class.getMethod + * (m.getName(), m.getParameterType()) + */ + String methodVar = "$method_" + m.getName() + "_" + i; + generateClassConstant(clinit, m.getDeclaringClass()); + clinit.visitLdcInsn(m.getName()); + generateClassArray(clinit, m.getParameterTypes()); + clinit.visitMethodInsn + (Opcodes.INVOKEVIRTUAL, + Type.getInternalName(Class.class), + "getMethod", + Type.getMethodDescriptor + (Type.getType(Method.class), + new Type[] { Type.getType(String.class), + Type.getType(Class[].class) })); + + clinit.visitFieldInsn + (Opcodes.PUTSTATIC, classInternalName, methodVar, + Type.getDescriptor(Method.class)); + } + } + + private void generateStub() + throws IOException + { + stubname = fullclassname + "_Stub"; + String stubclassname = classname + "_Stub"; + File file = new File((destination == null ? "." : destination) + + File.separator + + stubname.replace('.', File.separatorChar) + + ".class"); + + if (verbose) + System.out.println("[Generating class " + stubname + "]"); + + final ClassWriter stub = new ClassWriter(true); + classInternalName = stubname.replace('.', '/'); + final String superInternalName = + Type.getType(RemoteStub.class).getInternalName(); + + String[] remoteInternalNames = + internalNameArray((Class[]) mRemoteInterfaces.toArray(new Class[] {})); + stub.visit + (Opcodes.V1_2, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, classInternalName, + null, superInternalName, remoteInternalNames); + + if (need12Stubs) + { + stub.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "serialVersionUID", + Type.LONG_TYPE.getDescriptor(), null, new Long(2L)); + } + + if (need11Stubs) + { + stub.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, + "interfaceHash", Type.LONG_TYPE.getDescriptor(), null, + new Long(RMIHashes.getInterfaceHash(clazz))); + + if (need12Stubs) + { + stub.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, "useNewInvoke", + Type.BOOLEAN_TYPE.getDescriptor(), null, null); + } + + stub.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, + "operations", Type.getDescriptor(Operation[].class), null, null); + } + + // Set of method references. + if (need12Stubs) + { + for (int i = 0; i < remotemethods.length; i++) + { + Method m = remotemethods[i].meth; + String slotName = "$method_" + m.getName() + "_" + i; + stub.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, slotName, + Type.getDescriptor(Method.class), null, null); + } + } + + MethodVisitor clinit = stub.visitMethod + (Opcodes.ACC_STATIC, "<clinit>", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null, null); + + if (need11Stubs) + { + fillOperationArray(clinit); + if (! need12Stubs) + clinit.visitInsn(Opcodes.RETURN); + } + + if (need12Stubs) + { + // begin of try + Label begin = new Label(); + + // beginning of catch + Label handler = new Label(); + clinit.visitLabel(begin); + + // Initialize the methods references. + if (need11Stubs) + { + /* + * RemoteRef.class.getMethod("invoke", new Class[] { + * Remote.class, Method.class, Object[].class, long.class }) + */ + generateClassConstant(clinit, RemoteRef.class); + clinit.visitLdcInsn("invoke"); + generateClassArray + (clinit, new Class[] { Remote.class, Method.class, + Object[].class, long.class }); + clinit.visitMethodInsn + (Opcodes.INVOKEVIRTUAL, + Type.getInternalName(Class.class), + "getMethod", + Type.getMethodDescriptor + (Type.getType(Method.class), + new Type[] { Type.getType(String.class), + Type.getType(Class[].class) })); + + // useNewInvoke = true + clinit.visitInsn(Opcodes.ICONST_1); + clinit.visitFieldInsn + (Opcodes.PUTSTATIC, classInternalName, "useNewInvoke", + Type.BOOLEAN_TYPE.getDescriptor()); + } + + generateStaticMethodObjs(clinit); + + // jump past handler + clinit.visitInsn(Opcodes.RETURN); + clinit.visitLabel(handler); + if (need11Stubs) + { + // useNewInvoke = false + clinit.visitInsn(Opcodes.ICONST_0); + clinit.visitFieldInsn + (Opcodes.PUTSTATIC, classInternalName, "useNewInvoke", + Type.BOOLEAN_TYPE.getDescriptor()); + clinit.visitInsn(Opcodes.RETURN); + } + else + { + // throw NoSuchMethodError + clinit.visitTypeInsn(Opcodes.NEW, typeArg(NoSuchMethodError.class)); + clinit.visitInsn(Opcodes.DUP); + clinit.visitLdcInsn("stub class initialization failed"); + clinit.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(NoSuchMethodError.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(String.class) })); + clinit.visitInsn(Opcodes.ATHROW); + } + + clinit.visitTryCatchBlock + (begin, handler, handler, + Type.getInternalName(NoSuchMethodException.class)); + + } + + clinit.visitMaxs(-1, -1); + + generateClassForNamer(stub); + + // Constructors + if (need11Stubs) + { + // no arg public constructor + MethodVisitor code = stub.visitMethod + (Opcodes.ACC_PUBLIC, "<init>", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), + null, null); + code.visitVarInsn(Opcodes.ALOAD, 0); + code.visitMethodInsn + (Opcodes.INVOKESPECIAL, superInternalName, "<init>", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {})); + code.visitInsn(Opcodes.RETURN); + + code.visitMaxs(-1, -1); + } + + // public RemoteRef constructor + MethodVisitor constructor = stub.visitMethod + (Opcodes.ACC_PUBLIC, "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] {Type.getType(RemoteRef.class)}), + null, null); + constructor.visitVarInsn(Opcodes.ALOAD, 0); + constructor.visitVarInsn(Opcodes.ALOAD, 1); + constructor.visitMethodInsn + (Opcodes.INVOKESPECIAL, superInternalName, "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] {Type.getType(RemoteRef.class)})); + constructor.visitInsn(Opcodes.RETURN); + constructor.visitMaxs(-1, -1); + + // Method implementations + for (int i = 0; i < remotemethods.length; i++) + { + Method m = remotemethods[i].meth; + Class[] sig = m.getParameterTypes(); + Class returntype = m.getReturnType(); + Class[] except = sortExceptions + ((Class[]) remotemethods[i].exceptions.toArray(new Class[0])); + + MethodVisitor code = stub.visitMethod + (Opcodes.ACC_PUBLIC, + m.getName(), + Type.getMethodDescriptor(Type.getType(returntype), typeArray(sig)), + null, + internalNameArray(typeArray(except))); + + final Variables var = new Variables(); + + // this and parameters are the declared vars + var.declare("this"); + for (int j = 0; j < sig.length; j++) + var.declare(param(m, j), size(sig[j])); + + Label methodTryBegin = new Label(); + code.visitLabel(methodTryBegin); + + if (need12Stubs) + { + Label oldInvoke = new Label(); + if (need11Stubs) + { + // if not useNewInvoke jump to old invoke + code.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, "useNewInvoke", + Type.getDescriptor(boolean.class)); + code.visitJumpInsn(Opcodes.IFEQ, oldInvoke); + } + + // this.ref + code.visitVarInsn(Opcodes.ALOAD, var.get("this")); + code.visitFieldInsn + (Opcodes.GETFIELD, Type.getInternalName(RemoteObject.class), + "ref", Type.getDescriptor(RemoteRef.class)); + + // "this" is first arg to invoke + code.visitVarInsn(Opcodes.ALOAD, var.get("this")); + + // method object is second arg to invoke + String methName = "$method_" + m.getName() + "_" + i; + code.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, methName, + Type.getDescriptor(Method.class)); + + // args to remote method are third arg to invoke + if (sig.length == 0) + code.visitInsn(Opcodes.ACONST_NULL); + else + { + // create arg Object[] (with boxed primitives) and push it + code.visitLdcInsn(new Integer(sig.length)); + code.visitTypeInsn(Opcodes.ANEWARRAY, typeArg(Object.class)); + + var.allocate("argArray"); + code.visitVarInsn(Opcodes.ASTORE, var.get("argArray")); + + for (int j = 0; j < sig.length; j++) + { + int size = size(sig[j]); + int insn = loadOpcode(sig[j]); + Class box = sig[j].isPrimitive() ? box(sig[j]) : null; + + code.visitVarInsn(Opcodes.ALOAD, var.get("argArray")); + code.visitLdcInsn(new Integer(j)); + + // put argument on stack + if (box != null) + { + code.visitTypeInsn(Opcodes.NEW, typeArg(box)); + code.visitInsn(Opcodes.DUP); + code.visitVarInsn(insn, var.get(param(m, j))); + code.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(box), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(sig[j]) })); + } + else + code.visitVarInsn(insn, var.get(param(m, j))); + + code.visitInsn(Opcodes.AASTORE); + } + + code.visitVarInsn(Opcodes.ALOAD, var.deallocate("argArray")); + } + + // push remote operation opcode + code.visitLdcInsn(new Long(remotemethods[i].hash)); + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteRef.class), + "invoke", + Type.getMethodDescriptor + (Type.getType(Object.class), + new Type[] { Type.getType(Remote.class), + Type.getType(Method.class), + Type.getType(Object[].class), + Type.LONG_TYPE })); + + if (! returntype.equals(Void.TYPE)) + { + int retcode = returnOpcode(returntype); + Class boxCls = + returntype.isPrimitive() ? box(returntype) : null; + code.visitTypeInsn + (Opcodes.CHECKCAST, typeArg(boxCls == null ? returntype : boxCls)); + if (returntype.isPrimitive()) + { + // unbox + code.visitMethodInsn + (Opcodes.INVOKEVIRTUAL, + Type.getType(boxCls).getInternalName(), + unboxMethod(returntype), + Type.getMethodDescriptor + (Type.getType(returntype), new Type[] {})); + } + + code.visitInsn(retcode); + } + else + code.visitInsn(Opcodes.RETURN); + + + if (need11Stubs) + code.visitLabel(oldInvoke); + } + + if (need11Stubs) + { + + // this.ref.newCall(this, operations, index, interfaceHash) + code.visitVarInsn(Opcodes.ALOAD, var.get("this")); + code.visitFieldInsn + (Opcodes.GETFIELD, + Type.getInternalName(RemoteObject.class), + "ref", + Type.getDescriptor(RemoteRef.class)); + + // "this" is first arg to newCall + code.visitVarInsn(Opcodes.ALOAD, var.get("this")); + + // operations is second arg to newCall + code.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, "operations", + Type.getDescriptor(Operation[].class)); + + // method index is third arg + code.visitLdcInsn(new Integer(i)); + + // interface hash is fourth arg + code.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, "interfaceHash", + Type.LONG_TYPE.getDescriptor()); + + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteRef.class), + "newCall", + Type.getMethodDescriptor + (Type.getType(RemoteCall.class), + new Type[] { Type.getType(RemoteObject.class), + Type.getType(Operation[].class), + Type.INT_TYPE, + Type.LONG_TYPE })); + + // store call object on stack and leave copy on stack + var.allocate("call"); + code.visitInsn(Opcodes.DUP); + code.visitVarInsn(Opcodes.ASTORE, var.get("call")); + + Label beginArgumentTryBlock = new Label(); + code.visitLabel(beginArgumentTryBlock); + + // ObjectOutput out = call.getOutputStream(); + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteCall.class), + "getOutputStream", + Type.getMethodDescriptor + (Type.getType(ObjectOutput.class), new Type[] {})); + + for (int j = 0; j < sig.length; j++) + { + // dup the ObjectOutput + code.visitInsn(Opcodes.DUP); + + // get j'th arg to remote method + code.visitVarInsn(loadOpcode(sig[j]), var.get(param(m, j))); + + Class argCls = + sig[j].isPrimitive() ? sig[j] : Object.class; + + // out.writeFoo + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(ObjectOutput.class), + writeMethod(sig[j]), + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(argCls) })); + } + + // pop ObjectOutput + code.visitInsn(Opcodes.POP); + + Label iohandler = new Label(); + Label endArgumentTryBlock = new Label(); + code.visitJumpInsn(Opcodes.GOTO, endArgumentTryBlock); + code.visitLabel(iohandler); + + // throw new MarshalException(msg, ioexception); + code.visitVarInsn(Opcodes.ASTORE, var.allocate("exception")); + code.visitTypeInsn(Opcodes.NEW, typeArg(MarshalException.class)); + code.visitInsn(Opcodes.DUP); + code.visitLdcInsn("error marshalling arguments"); + code.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception")); + code.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(MarshalException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(String.class), + Type.getType(Exception.class) })); + code.visitInsn(Opcodes.ATHROW); + + code.visitLabel(endArgumentTryBlock); + code.visitTryCatchBlock + (beginArgumentTryBlock, iohandler, iohandler, + Type.getInternalName(IOException.class)); + + // this.ref.invoke(call) + code.visitVarInsn(Opcodes.ALOAD, var.get("this")); + code.visitFieldInsn + (Opcodes.GETFIELD, Type.getInternalName(RemoteObject.class), + "ref", Type.getDescriptor(RemoteRef.class)); + code.visitVarInsn(Opcodes.ALOAD, var.get("call")); + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteRef.class), + "invoke", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(RemoteCall.class) })); + + // handle return value + boolean needcastcheck = false; + + Label beginReturnTryCatch = new Label(); + code.visitLabel(beginReturnTryCatch); + + int returncode = returnOpcode(returntype); + + if (! returntype.equals(Void.TYPE)) + { + // call.getInputStream() + code.visitVarInsn(Opcodes.ALOAD, var.get("call")); + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteCall.class), + "getInputStream", + Type.getMethodDescriptor + (Type.getType(ObjectInput.class), new Type[] {})); + + Class readCls = + returntype.isPrimitive() ? returntype : Object.class; + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(ObjectInput.class), + readMethod(returntype), + Type.getMethodDescriptor + (Type.getType(readCls), new Type[] {})); + + boolean castresult = false; + + if (! returntype.isPrimitive()) + { + if (! returntype.equals(Object.class)) + castresult = true; + else + needcastcheck = true; + } + + if (castresult) + code.visitTypeInsn(Opcodes.CHECKCAST, typeArg(returntype)); + + // leave result on stack for return + } + + // this.ref.done(call) + code.visitVarInsn(Opcodes.ALOAD, var.get("this")); + code.visitFieldInsn + (Opcodes.GETFIELD, + Type.getInternalName(RemoteObject.class), + "ref", + Type.getDescriptor(RemoteRef.class)); + code.visitVarInsn(Opcodes.ALOAD, var.deallocate("call")); + code.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteRef.class), + "done", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(RemoteCall.class) })); + + // return; or return result; + code.visitInsn(returncode); + + // exception handler + Label handler = new Label(); + code.visitLabel(handler); + code.visitVarInsn(Opcodes.ASTORE, var.allocate("exception")); + + // throw new UnmarshalException(msg, e) + code.visitTypeInsn(Opcodes.NEW, typeArg(UnmarshalException.class)); + code.visitInsn(Opcodes.DUP); + code.visitLdcInsn("error unmarshalling return"); + code.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception")); + code.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(UnmarshalException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(String.class), + Type.getType(Exception.class) })); + code.visitInsn(Opcodes.ATHROW); + + Label endReturnTryCatch = new Label(); + + // catch IOException + code.visitTryCatchBlock + (beginReturnTryCatch, handler, handler, + Type.getInternalName(IOException.class)); + + if (needcastcheck) + { + // catch ClassNotFoundException + code.visitTryCatchBlock + (beginReturnTryCatch, handler, handler, + Type.getInternalName(ClassNotFoundException.class)); + } + } + + Label rethrowHandler = new Label(); + code.visitLabel(rethrowHandler); + // rethrow declared exceptions + code.visitInsn(Opcodes.ATHROW); + + boolean needgeneral = true; + for (int j = 0; j < except.length; j++) + { + if (except[j] == Exception.class) + needgeneral = false; + } + + for (int j = 0; j < except.length; j++) + { + code.visitTryCatchBlock + (methodTryBegin, rethrowHandler, rethrowHandler, + Type.getInternalName(except[j])); + } + + if (needgeneral) + { + // rethrow unchecked exceptions + code.visitTryCatchBlock + (methodTryBegin, rethrowHandler, rethrowHandler, + Type.getInternalName(RuntimeException.class)); + + Label generalHandler = new Label(); + code.visitLabel(generalHandler); + String msg = "undeclared checked exception"; + + // throw new java.rmi.UnexpectedException(msg, e) + code.visitVarInsn(Opcodes.ASTORE, var.allocate("exception")); + code.visitTypeInsn(Opcodes.NEW, typeArg(UnexpectedException.class)); + code.visitInsn(Opcodes.DUP); + code.visitLdcInsn(msg); + code.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception")); + code.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(UnexpectedException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type [] { Type.getType(String.class), + Type.getType(Exception.class) })); + code.visitInsn(Opcodes.ATHROW); + + code.visitTryCatchBlock + (methodTryBegin, rethrowHandler, generalHandler, + Type.getInternalName(Exception.class)); + } + + code.visitMaxs(-1, -1); + } + + stub.visitEnd(); + byte[] classData = stub.toByteArray(); + if (!noWrite) + { + if (file.exists()) + file.delete(); + if (file.getParentFile() != null) + file.getParentFile().mkdirs(); + FileOutputStream fos = new FileOutputStream(file); + fos.write(classData); + fos.flush(); + fos.close(); + } + } + + private void generateSkel() throws IOException + { + skelname = fullclassname + "_Skel"; + String skelclassname = classname + "_Skel"; + File file = new File(destination == null ? "" : destination + + File.separator + + skelname.replace('.', File.separatorChar) + + ".class"); + if (verbose) + System.out.println("[Generating class " + skelname + "]"); + + final ClassWriter skel = new ClassWriter(true); + classInternalName = skelname.replace('.', '/'); + skel.visit + (Opcodes.V1_1, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, + classInternalName, Type.getInternalName(Object.class), null, + new String[] { Type.getType(Skeleton.class).getInternalName() }); + + skel.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "interfaceHash", + Type.LONG_TYPE.getDescriptor(), null, + new Long(RMIHashes.getInterfaceHash(clazz))); + + skel.visitField + (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "operations", + Type.getDescriptor(Operation[].class), null, null); + + MethodVisitor clinit = skel.visitMethod + (Opcodes.ACC_STATIC, "<clinit>", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null, null); + + fillOperationArray(clinit); + clinit.visitInsn(Opcodes.RETURN); + + clinit.visitMaxs(-1, -1); + + // no arg public constructor + MethodVisitor init = skel.visitMethod + (Opcodes.ACC_PUBLIC, "<init>", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null, null); + init.visitVarInsn(Opcodes.ALOAD, 0); + init.visitMethodInsn + (Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {})); + init.visitInsn(Opcodes.RETURN); + init.visitMaxs(-1, -1); + + /* + * public Operation[] getOperations() + * returns a clone of the operations array + */ + MethodVisitor getOp = skel.visitMethod + (Opcodes.ACC_PUBLIC, "getOperations", + Type.getMethodDescriptor + (Type.getType(Operation[].class), new Type[] {}), + null, null); + getOp.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, "operations", + Type.getDescriptor(Operation[].class)); + getOp.visitMethodInsn + (Opcodes.INVOKEVIRTUAL, Type.getInternalName(Object.class), + "clone", Type.getMethodDescriptor(Type.getType(Object.class), + new Type[] {})); + getOp.visitTypeInsn(Opcodes.CHECKCAST, typeArg(Operation[].class)); + getOp.visitInsn(Opcodes.ARETURN); + getOp.visitMaxs(-1, -1); + + // public void dispatch(Remote, RemoteCall, int opnum, long hash) + MethodVisitor dispatch = skel.visitMethod + (Opcodes.ACC_PUBLIC, + "dispatch", + Type.getMethodDescriptor + (Type.VOID_TYPE, + new Type[] { Type.getType(Remote.class), + Type.getType(RemoteCall.class), + Type.INT_TYPE, Type.LONG_TYPE }), null, + new String[] { Type.getInternalName(Exception.class) }); + + Variables var = new Variables(); + var.declare("this"); + var.declare("remoteobj"); + var.declare("remotecall"); + var.declare("opnum"); + var.declareWide("hash"); + + /* + * if opnum >= 0 + * XXX it is unclear why there is handling of negative opnums + */ + dispatch.visitVarInsn(Opcodes.ILOAD, var.get("opnum")); + Label nonNegativeOpnum = new Label(); + Label opnumSet = new Label(); + dispatch.visitJumpInsn(Opcodes.IFGE, nonNegativeOpnum); + + for (int i = 0; i < remotemethods.length; i++) + { + // assign opnum if hash matches supplied hash + dispatch.visitVarInsn(Opcodes.LLOAD, var.get("hash")); + dispatch.visitLdcInsn(new Long(remotemethods[i].hash)); + Label notIt = new Label(); + dispatch.visitInsn(Opcodes.LCMP); + dispatch.visitJumpInsn(Opcodes.IFNE, notIt); + + // opnum = <opnum> + dispatch.visitLdcInsn(new Integer(i)); + dispatch.visitVarInsn(Opcodes.ISTORE, var.get("opnum")); + dispatch.visitJumpInsn(Opcodes.GOTO, opnumSet); + dispatch.visitLabel(notIt); + } + + // throw new SkeletonMismatchException + Label mismatch = new Label(); + dispatch.visitJumpInsn(Opcodes.GOTO, mismatch); + + dispatch.visitLabel(nonNegativeOpnum); + + // if opnum is already set, check that the hash matches the interface + dispatch.visitVarInsn(Opcodes.LLOAD, var.get("hash")); + dispatch.visitFieldInsn + (Opcodes.GETSTATIC, classInternalName, + "interfaceHash", Type.LONG_TYPE.getDescriptor()); + dispatch.visitInsn(Opcodes.LCMP); + dispatch.visitJumpInsn(Opcodes.IFEQ, opnumSet); + + dispatch.visitLabel(mismatch); + dispatch.visitTypeInsn + (Opcodes.NEW, typeArg(SkeletonMismatchException.class)); + dispatch.visitInsn(Opcodes.DUP); + dispatch.visitLdcInsn("interface hash mismatch"); + dispatch.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(SkeletonMismatchException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(String.class) })); + dispatch.visitInsn(Opcodes.ATHROW); + + // opnum has been set + dispatch.visitLabel(opnumSet); + + dispatch.visitVarInsn(Opcodes.ALOAD, var.get("remoteobj")); + dispatch.visitTypeInsn(Opcodes.CHECKCAST, typeArg(clazz)); + dispatch.visitVarInsn(Opcodes.ASTORE, var.get("remoteobj")); + + Label deflt = new Label(); + Label[] methLabels = new Label[remotemethods.length]; + for (int i = 0; i < methLabels.length; i++) + methLabels[i] = new Label(); + + // switch on opnum + dispatch.visitVarInsn(Opcodes.ILOAD, var.get("opnum")); + dispatch.visitTableSwitchInsn + (0, remotemethods.length - 1, deflt, methLabels); + + // Method dispatch + for (int i = 0; i < remotemethods.length; i++) + { + dispatch.visitLabel(methLabels[i]); + Method m = remotemethods[i].meth; + generateMethodSkel(dispatch, m, var); + } + + dispatch.visitLabel(deflt); + dispatch.visitTypeInsn(Opcodes.NEW, typeArg(UnmarshalException.class)); + dispatch.visitInsn(Opcodes.DUP); + dispatch.visitLdcInsn("invalid method number"); + dispatch.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(UnmarshalException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(String.class) })); + dispatch.visitInsn(Opcodes.ATHROW); + + dispatch.visitMaxs(-1, -1); + + skel.visitEnd(); + byte[] classData = skel.toByteArray(); + if (!noWrite) + { + if (file.exists()) + file.delete(); + if (file.getParentFile() != null) + file.getParentFile().mkdirs(); + FileOutputStream fos = new FileOutputStream(file); + fos.write(classData); + fos.flush(); + fos.close(); + } + } + + private void generateMethodSkel(MethodVisitor cv, Method m, Variables var) + { + Class[] sig = m.getParameterTypes(); + + Label readArgs = new Label(); + cv.visitLabel(readArgs); + + boolean needcastcheck = false; + + // ObjectInput in = call.getInputStream(); + cv.visitVarInsn(Opcodes.ALOAD, var.get("remotecall")); + cv.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteCall.class), "getInputStream", + Type.getMethodDescriptor + (Type.getType(ObjectInput.class), new Type[] {})); + cv.visitVarInsn(Opcodes.ASTORE, var.allocate("objectinput")); + + for (int i = 0; i < sig.length; i++) + { + // dup input stream + cv.visitVarInsn(Opcodes.ALOAD, var.get("objectinput")); + + Class readCls = sig[i].isPrimitive() ? sig[i] : Object.class; + + // in.readFoo() + cv.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(ObjectInput.class), + readMethod(sig[i]), + Type.getMethodDescriptor + (Type.getType(readCls), new Type [] {})); + + if (! sig[i].isPrimitive() && ! sig[i].equals(Object.class)) + { + needcastcheck = true; + cv.visitTypeInsn(Opcodes.CHECKCAST, typeArg(sig[i])); + } + + // store arg in variable + cv.visitVarInsn + (storeOpcode(sig[i]), var.allocate(param(m, i), size(sig[i]))); + } + + var.deallocate("objectinput"); + + Label doCall = new Label(); + Label closeInput = new Label(); + + cv.visitJumpInsn(Opcodes.JSR, closeInput); + cv.visitJumpInsn(Opcodes.GOTO, doCall); + + // throw new UnmarshalException + Label handler = new Label(); + cv.visitLabel(handler); + cv.visitVarInsn(Opcodes.ASTORE, var.allocate("exception")); + cv.visitTypeInsn(Opcodes.NEW, typeArg(UnmarshalException.class)); + cv.visitInsn(Opcodes.DUP); + cv.visitLdcInsn("error unmarshalling arguments"); + cv.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception")); + cv.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(UnmarshalException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(String.class), + Type.getType(Exception.class) })); + cv.visitVarInsn(Opcodes.ASTORE, var.allocate("toThrow")); + cv.visitJumpInsn(Opcodes.JSR, closeInput); + cv.visitVarInsn(Opcodes.ALOAD, var.get("toThrow")); + cv.visitInsn(Opcodes.ATHROW); + + cv.visitTryCatchBlock + (readArgs, handler, handler, Type.getInternalName(IOException.class)); + if (needcastcheck) + { + cv.visitTryCatchBlock + (readArgs, handler, handler, + Type.getInternalName(ClassCastException.class)); + } + + // finally block + cv.visitLabel(closeInput); + cv.visitVarInsn(Opcodes.ASTORE, var.allocate("retAddress")); + cv.visitVarInsn(Opcodes.ALOAD, var.get("remotecall")); + cv.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteCall.class), + "releaseInputStream", + Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {})); + cv.visitVarInsn(Opcodes.RET, var.deallocate("retAddress")); + var.deallocate("toThrow"); + + // do the call using args stored as variables + cv.visitLabel(doCall); + cv.visitVarInsn(Opcodes.ALOAD, var.get("remoteobj")); + for (int i = 0; i < sig.length; i++) + cv.visitVarInsn(loadOpcode(sig[i]), var.deallocate(param(m, i))); + cv.visitMethodInsn + (Opcodes.INVOKEVIRTUAL, Type.getInternalName(clazz), m.getName(), + Type.getMethodDescriptor(m)); + + Class returntype = m.getReturnType(); + if (! returntype.equals(Void.TYPE)) + { + cv.visitVarInsn + (storeOpcode(returntype), var.allocate("result", size(returntype))); + } + + // write result to result stream + Label writeResult = new Label(); + cv.visitLabel(writeResult); + cv.visitVarInsn(Opcodes.ALOAD, var.get("remotecall")); + cv.visitInsn(Opcodes.ICONST_1); + cv.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(RemoteCall.class), + "getResultStream", + Type.getMethodDescriptor + (Type.getType(ObjectOutput.class), + new Type[] { Type.BOOLEAN_TYPE })); + + if (! returntype.equals(Void.TYPE)) + { + // out.writeFoo(result) + cv.visitVarInsn(loadOpcode(returntype), var.deallocate("result")); + Class writeCls = returntype.isPrimitive() ? returntype : Object.class; + cv.visitMethodInsn + (Opcodes.INVOKEINTERFACE, + Type.getInternalName(ObjectOutput.class), + writeMethod(returntype), + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(writeCls) })); + } + + cv.visitInsn(Opcodes.RETURN); + + // throw new MarshalException + Label marshalHandler = new Label(); + cv.visitLabel(marshalHandler); + cv.visitVarInsn(Opcodes.ASTORE, var.allocate("exception")); + cv.visitTypeInsn(Opcodes.NEW, typeArg(MarshalException.class)); + cv.visitInsn(Opcodes.DUP); + cv.visitLdcInsn("error marshalling return"); + cv.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception")); + cv.visitMethodInsn + (Opcodes.INVOKESPECIAL, + Type.getInternalName(MarshalException.class), + "<init>", + Type.getMethodDescriptor + (Type.VOID_TYPE, new Type[] { Type.getType(String.class), + Type.getType(Exception.class) })); + cv.visitInsn(Opcodes.ATHROW); + cv.visitTryCatchBlock + (writeResult, marshalHandler, marshalHandler, + Type.getInternalName(IOException.class)); + } + + private static String typeArg(Class cls) + { + if (cls.isArray()) + return Type.getDescriptor(cls); + + return Type.getInternalName(cls); + } + + private static String readMethod(Class cls) + { + if (cls.equals(Void.TYPE)) + throw new IllegalArgumentException("can not read void"); + + String method; + if (cls.equals(Boolean.TYPE)) + method = "readBoolean"; + else if (cls.equals(Byte.TYPE)) + method = "readByte"; + else if (cls.equals(Character.TYPE)) + method = "readChar"; + else if (cls.equals(Short.TYPE)) + method = "readShort"; + else if (cls.equals(Integer.TYPE)) + method = "readInt"; + else if (cls.equals(Long.TYPE)) + method = "readLong"; + else if (cls.equals(Float.TYPE)) + method = "readFloat"; + else if (cls.equals(Double.TYPE)) + method = "readDouble"; + else + method = "readObject"; + + return method; + } + + private static String writeMethod(Class cls) + { + if (cls.equals(Void.TYPE)) + throw new IllegalArgumentException("can not read void"); + + String method; + if (cls.equals(Boolean.TYPE)) + method = "writeBoolean"; + else if (cls.equals(Byte.TYPE)) + method = "writeByte"; + else if (cls.equals(Character.TYPE)) + method = "writeChar"; + else if (cls.equals(Short.TYPE)) + method = "writeShort"; + else if (cls.equals(Integer.TYPE)) + method = "writeInt"; + else if (cls.equals(Long.TYPE)) + method = "writeLong"; + else if (cls.equals(Float.TYPE)) + method = "writeFloat"; + else if (cls.equals(Double.TYPE)) + method = "writeDouble"; + else + method = "writeObject"; + + return method; + } + + private static int returnOpcode(Class cls) + { + int returncode; + if (cls.equals(Boolean.TYPE)) + returncode = Opcodes.IRETURN; + else if (cls.equals(Byte.TYPE)) + returncode = Opcodes.IRETURN; + else if (cls.equals(Character.TYPE)) + returncode = Opcodes.IRETURN; + else if (cls.equals(Short.TYPE)) + returncode = Opcodes.IRETURN; + else if (cls.equals(Integer.TYPE)) + returncode = Opcodes.IRETURN; + else if (cls.equals(Long.TYPE)) + returncode = Opcodes.LRETURN; + else if (cls.equals(Float.TYPE)) + returncode = Opcodes.FRETURN; + else if (cls.equals(Double.TYPE)) + returncode = Opcodes.DRETURN; + else if (cls.equals(Void.TYPE)) + returncode = Opcodes.RETURN; + else + returncode = Opcodes.ARETURN; + + return returncode; + } + + private static int loadOpcode(Class cls) + { + if (cls.equals(Void.TYPE)) + throw new IllegalArgumentException("can not load void"); + + int loadcode; + if (cls.equals(Boolean.TYPE)) + loadcode = Opcodes.ILOAD; + else if (cls.equals(Byte.TYPE)) + loadcode = Opcodes.ILOAD; + else if (cls.equals(Character.TYPE)) + loadcode = Opcodes.ILOAD; + else if (cls.equals(Short.TYPE)) + loadcode = Opcodes.ILOAD; + else if (cls.equals(Integer.TYPE)) + loadcode = Opcodes.ILOAD; + else if (cls.equals(Long.TYPE)) + loadcode = Opcodes.LLOAD; + else if (cls.equals(Float.TYPE)) + loadcode = Opcodes.FLOAD; + else if (cls.equals(Double.TYPE)) + loadcode = Opcodes.DLOAD; + else + loadcode = Opcodes.ALOAD; + + return loadcode; + } + + private static int storeOpcode(Class cls) + { + if (cls.equals(Void.TYPE)) + throw new IllegalArgumentException("can not load void"); + + int storecode; + if (cls.equals(Boolean.TYPE)) + storecode = Opcodes.ISTORE; + else if (cls.equals(Byte.TYPE)) + storecode = Opcodes.ISTORE; + else if (cls.equals(Character.TYPE)) + storecode = Opcodes.ISTORE; + else if (cls.equals(Short.TYPE)) + storecode = Opcodes.ISTORE; + else if (cls.equals(Integer.TYPE)) + storecode = Opcodes.ISTORE; + else if (cls.equals(Long.TYPE)) + storecode = Opcodes.LSTORE; + else if (cls.equals(Float.TYPE)) + storecode = Opcodes.FSTORE; + else if (cls.equals(Double.TYPE)) + storecode = Opcodes.DSTORE; + else + storecode = Opcodes.ASTORE; + + return storecode; + } + + private static String unboxMethod(Class primitive) + { + if (! primitive.isPrimitive()) + throw new IllegalArgumentException("can not unbox nonprimitive"); + + String method; + if (primitive.equals(Boolean.TYPE)) + method = "booleanValue"; + else if (primitive.equals(Byte.TYPE)) + method = "byteValue"; + else if (primitive.equals(Character.TYPE)) + method = "charValue"; + else if (primitive.equals(Short.TYPE)) + method = "shortValue"; + else if (primitive.equals(Integer.TYPE)) + method = "intValue"; + else if (primitive.equals(Long.TYPE)) + method = "longValue"; + else if (primitive.equals(Float.TYPE)) + method = "floatValue"; + else if (primitive.equals(Double.TYPE)) + method = "doubleValue"; + else + throw new IllegalStateException("unknown primitive class " + primitive); + + return method; + } + + public static Class box(Class cls) + { + if (! cls.isPrimitive()) + throw new IllegalArgumentException("can only box primitive"); + + Class box; + if (cls.equals(Boolean.TYPE)) + box = Boolean.class; + else if (cls.equals(Byte.TYPE)) + box = Byte.class; + else if (cls.equals(Character.TYPE)) + box = Character.class; + else if (cls.equals(Short.TYPE)) + box = Short.class; + else if (cls.equals(Integer.TYPE)) + box = Integer.class; + else if (cls.equals(Long.TYPE)) + box = Long.class; + else if (cls.equals(Float.TYPE)) + box = Float.class; + else if (cls.equals(Double.TYPE)) + box = Double.class; + else + throw new IllegalStateException("unknown primitive type " + cls); + + return box; + } + + private static int size(Class cls) { + if (cls.equals(Long.TYPE) || cls.equals(Double.TYPE)) + return 2; + else + return 1; + } + + /** + * Sort exceptions so the most general go last. + */ + private Class[] sortExceptions(Class[] except) + { + for (int i = 0; i < except.length; i++) + { + for (int j = i + 1; j < except.length; j++) + { + if (except[i].isAssignableFrom(except[j])) + { + Class tmp = except[i]; + except[i] = except[j]; + except[j] = tmp; + } + } + } + return (except); + } + + public void setup(boolean keep, boolean need11Stubs, boolean need12Stubs, + boolean iiop, boolean poa, boolean debug, boolean warnings, + boolean noWrite, boolean verbose, boolean force, String classpath, + String bootclasspath, String extdirs, String outputDirectory) + { + this.keep = keep; + this.need11Stubs = need11Stubs; + this.need12Stubs = need12Stubs; + this.verbose = verbose; + this.noWrite = noWrite; + + // Set up classpath. + this.classpath = classpath; + StringTokenizer st = + new StringTokenizer(classpath, File.pathSeparator); + URL[] u = new URL[st.countTokens()]; + for (int i = 0; i < u.length; i++) + { + String path = st.nextToken(); + File f = new File(path); + try + { + u[i] = f.toURL(); + } + catch (java.net.MalformedURLException mue) + { + logError("malformed classpath component " + path); + return; + } + } + loader = new URLClassLoader(u); + + destination = outputDirectory; + } + + private void findRemoteMethods() + throws RMICException + { + List rmeths = new ArrayList(); + for (Class cur = clazz; cur != null; cur = cur.getSuperclass()) + { + Class[] interfaces = cur.getInterfaces(); + for (int i = 0; i < interfaces.length; i++) + { + if (java.rmi.Remote.class.isAssignableFrom(interfaces[i])) + { + Class remoteInterface = interfaces[i]; + if (verbose) + System.out.println + ("[implements " + remoteInterface.getName() + "]"); + + // check if the methods declare RemoteExceptions + Method[] meths = remoteInterface.getMethods(); + for (int j = 0; j < meths.length; j++) + { + Method m = meths[j]; + Class[] exs = m.getExceptionTypes(); + + boolean throwsRemote = false; + for (int k = 0; k < exs.length; k++) + { + if (exs[k].isAssignableFrom(RemoteException.class)) + throwsRemote = true; + } + + if (! throwsRemote) + { + throw new RMICException + ("Method " + m + " in interface " + remoteInterface + + " does not throw a RemoteException"); + } + + rmeths.add(m); + } + + mRemoteInterfaces.add(remoteInterface); + } + } + } + + // intersect exceptions for doubly inherited methods + boolean[] skip = new boolean[rmeths.size()]; + for (int i = 0; i < skip.length; i++) + skip[i] = false; + List methrefs = new ArrayList(); + for (int i = 0; i < rmeths.size(); i++) + { + if (skip[i]) continue; + Method current = (Method) rmeths.get(i); + MethodRef ref = new MethodRef(current); + for (int j = i+1; j < rmeths.size(); j++) + { + Method other = (Method) rmeths.get(j); + if (ref.isMatch(other)) + { + ref.intersectExceptions(other); + skip[j] = true; + } + } + methrefs.add(ref); + } + + // Convert into a MethodRef array and sort them + remotemethods = (MethodRef[]) + methrefs.toArray(new MethodRef[methrefs.size()]); + Arrays.sort(remotemethods); + } + + /** + * Prints an error to System.err and increases the error count. + */ + private void logError(Exception theError) + { + logError(theError.getMessage()); + if (verbose) + theError.printStackTrace(System.err); + } + + /** + * Prints an error to System.err and increases the error count. + */ + private void logError(String theError) + { + errorCount++; + System.err.println("error: " + theError); + } + + private static String getPrettyName(Class cls) + { + StringBuffer str = new StringBuffer(); + for (int count = 0;; count++) + { + if (! cls.isArray()) + { + str.append(cls.getName()); + for (; count > 0; count--) + str.append("[]"); + return (str.toString()); + } + cls = cls.getComponentType(); + } + } + + private static class MethodRef + implements Comparable + { + Method meth; + long hash; + List exceptions; + private String sig; + + MethodRef(Method m) { + meth = m; + sig = Type.getMethodDescriptor(meth); + hash = RMIHashes.getMethodHash(m); + // add exceptions removing subclasses + exceptions = removeSubclasses(m.getExceptionTypes()); + } + + public int compareTo(Object obj) { + MethodRef that = (MethodRef) obj; + int name = this.meth.getName().compareTo(that.meth.getName()); + if (name == 0) { + return this.sig.compareTo(that.sig); + } + return name; + } + + public boolean isMatch(Method m) + { + if (!meth.getName().equals(m.getName())) + return false; + + Class[] params1 = meth.getParameterTypes(); + Class[] params2 = m.getParameterTypes(); + if (params1.length != params2.length) + return false; + + for (int i = 0; i < params1.length; i++) + if (!params1[i].equals(params2[i])) return false; + + return true; + } + + private static List removeSubclasses(Class[] classes) + { + List list = new ArrayList(); + for (int i = 0; i < classes.length; i++) + { + Class candidate = classes[i]; + boolean add = true; + for (int j = 0; j < classes.length; j++) + { + if (classes[j].equals(candidate)) + continue; + else if (classes[j].isAssignableFrom(candidate)) + add = false; + } + if (add) list.add(candidate); + } + + return list; + } + + public void intersectExceptions(Method m) + { + List incoming = removeSubclasses(m.getExceptionTypes()); + + List updated = new ArrayList(); + + for (int i = 0; i < exceptions.size(); i++) + { + Class outer = (Class) exceptions.get(i); + boolean addOuter = false; + for (int j = 0; j < incoming.size(); j++) + { + Class inner = (Class) incoming.get(j); + + if (inner.equals(outer) || inner.isAssignableFrom(outer)) + addOuter = true; + else if (outer.isAssignableFrom(inner)) + updated.add(inner); + } + + if (addOuter) + updated.add(outer); + } + + exceptions = updated; + } + } +} |