/* 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.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; 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.util.ArrayList; import java.util.Arrays; import java.util.Iterator; import java.util.List; 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; StringBuilder desc = new StringBuilder(); 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) { StringBuilder str = new StringBuilder(); 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; } } }