All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ow2.fastrmic.RMIC Maven / Gradle / Ivy

There is a newer version: 1.0.5
Show newest version
/**
   RMIC.java --
   Copyright (c) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004
   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 org.ow2.fastrmic;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
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.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
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.Label;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;

public class RMIC implements Opcodes {

    private String[] args;
    private int next;
    private Exception exception;
    private boolean keep = false;
    private boolean need11Stubs = true;
    private boolean need12Stubs = true;
    private boolean compile = true;
    private boolean verbose;
    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;

    /**
     * System property used to let ASM compute the max var.
     */
    private static final String COMPUTE_MAX = "fastrmic.asm.computemax";

    public RMIC(String[] a) {
        args = a;
    }

    public static void main(String[] args) {
        RMIC r = new RMIC(args);
        if (r.run() == false) {
            Exception e = r.getException();
            if (e != null)
                e.printStackTrace();
            else
                System.exit(1);
        }
    }

    public boolean run() {
        parseOptions();
        if (next >= args.length)
            error("no class names found");
        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 (Exception e) {
                exception = e;
                return (false);
            }
        }
        return (true);
    }

    private boolean processClass(String cls) throws Exception {
        // reset class specific vars
        clazz = null;
        classname = null;
        classInternalName = null;
        fullclassname = null;
        remotemethods = null;
        stubname = null;
        skelname = null;
        mRemoteInterfaces = new ArrayList();

        errorCount = 0;

        analyzeClass(cls);
        if (errorCount > 0)
            System.exit(1);
        generateStub();
        if (need11Stubs)
            generateSkel();
        return (true);
    }

    private void analyzeClass(String cname) throws Exception {
        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();
    }

    public Exception getException() {
        return (exception);
    }

    private void findClass() {
        try {
            ClassLoader cl = (loader == null ? ClassLoader
                    .getSystemClassLoader() : loader);
            clazz = Class.forName(fullclassname, false, cl);
        } catch (ClassNotFoundException cnfe) {
            System.err.println(fullclassname + " not found in " + classpath);
            throw new RuntimeException(cnfe);
        }

        if (!Remote.class.isAssignableFrom(clazz)) {
            logError("Class " + clazz.getName() + " is not a remote object. "
                    + "It does not implement an interface that is a "
                    + "java.rmi.Remote-interface.");
            throw new RuntimeException("Class " + clazz.getName()
                    + " is not a remote object. "
                    + "It does not implement an interface that is a "
                    + "java.rmi.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(ACC_PRIVATE + ACC_STATIC
                + 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(ALOAD, 0);
        cv.visitMethodInsn(INVOKESTATIC, Type.getInternalName(Class.class),
                "forName", Type.getMethodDescriptor(Type.getType(Class.class),
                        new Type[] { Type.getType(String.class) }));
        cv.visitInsn(ARETURN);

        Label handler = new Label();
        cv.visitLabel(handler);
        cv.visitVarInsn(ASTORE, 1);
        cv.visitTypeInsn(NEW, typeArg(NoClassDefFoundError.class));
        cv.visitInsn(DUP);
        cv.visitVarInsn(ALOAD, 1);
        cv.visitMethodInsn(INVOKEVIRTUAL, Type
                .getInternalName(ClassNotFoundException.class), "getMessage",
                Type.getMethodDescriptor(Type.getType(String.class),
                        new Type[] {}));
        cv.visitMethodInsn(INVOKESPECIAL, Type
                .getInternalName(NoClassDefFoundError.class), "", Type
                .getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type
                        .getType(String.class) }));
        cv.visitInsn(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(GETSTATIC, Type.getInternalName(boxCls), "TYPE",
                    Type.getDescriptor(Class.class));
            return;
        }
        cv.visitLdcInsn(cls.getName());
        cv.visitMethodInsn(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(ANEWARRAY, typeArg(Class.class));
        for (int i = 0; i < classes.length; i++) {
            code.visitInsn(DUP);
            code.visitLdcInsn(new Integer(i));
            generateClassConstant(code, classes[i]);
            code.visitInsn(AASTORE);
        }
    }

    private void fillOperationArray(MethodVisitor clinit) {
        // Operations array
        clinit.visitLdcInsn(new Integer(remotemethods.length));
        clinit.visitTypeInsn(ANEWARRAY, typeArg(Operation.class));
        clinit.visitFieldInsn(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(", ");
            }
            desc.append(")");

            // push operations array
            clinit.visitFieldInsn(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(NEW, typeArg(Operation.class));
            clinit.visitInsn(DUP);
            clinit.visitLdcInsn(desc.toString());
            clinit.visitMethodInsn(INVOKESPECIAL, Type
                    .getInternalName(Operation.class), "", Type
                    .getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type
                            .getType(String.class) }));

            // store in operations array
            clinit.visitInsn(AASTORE);
        }
    }

    private void generateStaticMethodObjs(MethodVisitor clinit) {
        for (int i = 0; i < remotemethods.length; i++) {
            Method m = remotemethods[i].meth;

            /*
             * $method_m.getName()_i =
             * m.getDeclaringClass().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(INVOKEVIRTUAL, Type
                    .getInternalName(Class.class), "getMethod", Type
                    .getMethodDescriptor(Type.getType(Method.class),
                            new Type[] { Type.getType(String.class),
                                    Type.getType(Class[].class) }));

            clinit.visitFieldInsn(PUTSTATIC, classInternalName, methodVar, Type
                    .getDescriptor(Method.class));
        }
    }

    private void generateStub() throws IOException {
        ClassInfo classInfo = generateStubData();

        File file = new File((destination == null ? "." : destination)
                + File.separator
                + classInfo.getName().replace('.', File.separatorChar)
                + ".class");

        if (file.exists())
            file.delete();
        if (file.getParentFile() != null)
            file.getParentFile().mkdirs();
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(classInfo.getBytecode());
        fos.flush();
        fos.close();
    }

    private ClassInfo generateStubData() throws IOException {
        ClassInfo classInfo = new ClassInfo();
        stubname = fullclassname + "_Stub";
        classInfo.setName(stubname);

        if (verbose)
            System.out.println("[Generating class " + stubname + "]");

        final ClassWriter stub = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        classInternalName = stubname.replace('.', '/');
        final String superInternalName = Type.getType(RemoteStub.class)
                .getInternalName();

        String[] remoteInternalNames = internalNameArray((Class[]) mRemoteInterfaces
                .toArray(new Class[] {}));
        stub.visit(V1_2, ACC_PUBLIC + ACC_FINAL, classInternalName, null,
                superInternalName, remoteInternalNames);

        if (need12Stubs) {
            stub.visitField(ACC_PRIVATE + ACC_STATIC + ACC_FINAL,
                    "serialVersionUID", Type.LONG_TYPE.getDescriptor(), null,
                    new Long(2L));
        }

        if (need11Stubs) {
            stub.visitField(ACC_PRIVATE + ACC_STATIC + ACC_FINAL,
                    "interfaceHash", Type.LONG_TYPE.getDescriptor(), null,
                    new Long(getInterfaceHash(clazz)));

            if (need12Stubs) {
                stub.visitField(ACC_PRIVATE + ACC_STATIC, "useNewInvoke",
                        Type.BOOLEAN_TYPE.getDescriptor(), null, null);
            }

            stub.visitField(ACC_PRIVATE + ACC_STATIC + 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(ACC_PRIVATE + ACC_STATIC, slotName, Type
                        .getDescriptor(Method.class), null, null);
            }
        }

        MethodVisitor clinit = stub
                .visitMethod(ACC_STATIC, "", Type.getMethodDescriptor(
                        Type.VOID_TYPE, new Type[] {}), null, null);

        if (need11Stubs) {
            fillOperationArray(clinit);
            if (!need12Stubs)
                clinit.visitInsn(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(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(ICONST_1);
                clinit.visitFieldInsn(PUTSTATIC, classInternalName,
                        "useNewInvoke", Type.BOOLEAN_TYPE.getDescriptor());
            }

            generateStaticMethodObjs(clinit);

            // jump past handler
            clinit.visitInsn(RETURN);
            clinit.visitLabel(handler);
            if (need11Stubs) {
                // useNewInvoke = false
                clinit.visitInsn(ICONST_0);
                clinit.visitFieldInsn(PUTSTATIC, classInternalName,
                        "useNewInvoke", Type.BOOLEAN_TYPE.getDescriptor());
                clinit.visitInsn(RETURN);
            } else {
                // throw NoSuchMethodError
                clinit.visitTypeInsn(NEW, typeArg(NoSuchMethodError.class));
                clinit.visitInsn(DUP);
                clinit.visitLdcInsn("stub class initialization failed");
                clinit.visitMethodInsn(INVOKESPECIAL, Type
                        .getInternalName(NoSuchMethodError.class), "",
                        Type.getMethodDescriptor(Type.VOID_TYPE,
                                new Type[] { Type.getType(String.class) }));
                clinit.visitInsn(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(ACC_PUBLIC, "", Type
                    .getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null,
                    null);
            code.visitVarInsn(ALOAD, 0);
            code.visitMethodInsn(INVOKESPECIAL, superInternalName, "",
                    Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
            code.visitInsn(RETURN);

            code.visitMaxs(-1, -1);
        }

        // public RemoteRef constructor
        MethodVisitor constructor = stub.visitMethod(ACC_PUBLIC, "", Type
                .getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type
                        .getType(RemoteRef.class) }), null, null);
        constructor.visitVarInsn(ALOAD, 0);
        constructor.visitVarInsn(ALOAD, 1);
        constructor.visitMethodInsn(INVOKESPECIAL, superInternalName, "",
                Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type
                        .getType(RemoteRef.class) }));
        constructor.visitInsn(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(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(GETSTATIC, classInternalName,
                            "useNewInvoke", Type.getDescriptor(boolean.class));
                    code.visitJumpInsn(IFEQ, oldInvoke);
                }

                // this.ref
                code.visitVarInsn(ALOAD, var.get("this"));
                code.visitFieldInsn(GETFIELD, Type
                        .getInternalName(RemoteObject.class), "ref", Type
                        .getDescriptor(RemoteRef.class));

                // "this" is first arg to invoke
                code.visitVarInsn(ALOAD, var.get("this"));

                // method object is second arg to invoke
                String methName = "$method_" + m.getName() + "_" + i;
                code.visitFieldInsn(GETSTATIC, classInternalName, methName,
                        Type.getDescriptor(Method.class));

                // args to remote method are third arg to invoke
                if (sig.length == 0)
                    code.visitInsn(ACONST_NULL);
                else {
                    // create arg Object[] (with boxed primitives) and push it
                    code.visitLdcInsn(new Integer(sig.length));
                    code.visitTypeInsn(ANEWARRAY, typeArg(Object.class));

                    var.allocate("argArray");
                    code.visitVarInsn(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(ALOAD, var.get("argArray"));
                        code.visitLdcInsn(new Integer(j));

                        // put argument on stack
                        if (box != null) {
                            code.visitTypeInsn(NEW, typeArg(box));
                            code.visitInsn(DUP);
                            code.visitVarInsn(insn, var.get(param(m, j)));
                            code
                                    .visitMethodInsn(INVOKESPECIAL, Type
                                            .getInternalName(box), "",
                                            Type.getMethodDescriptor(
                                                    Type.VOID_TYPE,
                                                    new Type[] { Type
                                                            .getType(sig[j]) }));
                        } else
                            code.visitVarInsn(insn, var.get(param(m, j)));

                        code.visitInsn(AASTORE);
                    }

                    code.visitVarInsn(ALOAD, var.deallocate("argArray"));
                }

                // push remote operation opcode
                code.visitLdcInsn(new Long(remotemethods[i].hash));
                code.visitMethodInsn(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(CHECKCAST,
                            typeArg(boxCls == null ? returntype : boxCls));
                    if (returntype.isPrimitive()) {
                        // unbox
                        code.visitMethodInsn(INVOKEVIRTUAL, Type
                                .getType(boxCls).getInternalName(),
                                unboxMethod(returntype), Type
                                        .getMethodDescriptor(Type
                                                .getType(returntype),
                                                new Type[] {}));
                    }

                    code.visitInsn(retcode);
                } else
                    code.visitInsn(RETURN);

                if (need11Stubs)
                    code.visitLabel(oldInvoke);
            }

            if (need11Stubs) {

                // this.ref.newCall(this, operations, index, interfaceHash)
                code.visitVarInsn(ALOAD, var.get("this"));
                code.visitFieldInsn(GETFIELD, Type
                        .getInternalName(RemoteObject.class), "ref", Type
                        .getDescriptor(RemoteRef.class));

                // "this" is first arg to newCall
                code.visitVarInsn(ALOAD, var.get("this"));

                // operations is second arg to newCall
                code.visitFieldInsn(GETSTATIC, classInternalName, "operations",
                        Type.getDescriptor(Operation[].class));

                // method index is third arg
                code.visitLdcInsn(new Integer(i));

                // interface hash is fourth arg
                code.visitFieldInsn(GETSTATIC, classInternalName,
                        "interfaceHash", Type.LONG_TYPE.getDescriptor());

                code.visitMethodInsn(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(DUP);
                code.visitVarInsn(ASTORE, var.get("call"));

                Label beginArgumentTryBlock = new Label();
                code.visitLabel(beginArgumentTryBlock);

                // ObjectOutput out = call.getOutputStream();
                code.visitMethodInsn(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(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(INVOKEINTERFACE, Type
                            .getInternalName(ObjectOutput.class),
                            writeMethod(sig[j]), Type.getMethodDescriptor(
                                    Type.VOID_TYPE, new Type[] { Type
                                            .getType(argCls) }));
                }

                // pop ObjectOutput
                code.visitInsn(POP);

                Label iohandler = new Label();
                Label endArgumentTryBlock = new Label();
                code.visitJumpInsn(GOTO, endArgumentTryBlock);
                code.visitLabel(iohandler);

                // throw new MarshalException(msg, ioexception);
                code.visitVarInsn(ASTORE, var.allocate("exception"));
                code.visitTypeInsn(NEW, typeArg(MarshalException.class));
                code.visitInsn(DUP);
                code.visitLdcInsn("error marshalling arguments");
                code.visitVarInsn(ALOAD, var.deallocate("exception"));
                code.visitMethodInsn(INVOKESPECIAL, Type
                        .getInternalName(MarshalException.class), "",
                        Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {
                                Type.getType(String.class),
                                Type.getType(Exception.class) }));
                code.visitInsn(ATHROW);

                code.visitLabel(endArgumentTryBlock);
                code.visitTryCatchBlock(beginArgumentTryBlock, iohandler,
                        iohandler, Type.getInternalName(IOException.class));

                // this.ref.invoke(call)
                code.visitVarInsn(ALOAD, var.get("this"));
                code.visitFieldInsn(GETFIELD, Type
                        .getInternalName(RemoteObject.class), "ref", Type
                        .getDescriptor(RemoteRef.class));
                code.visitVarInsn(ALOAD, var.get("call"));
                code.visitMethodInsn(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(ALOAD, var.get("call"));
                    code
                            .visitMethodInsn(INVOKEINTERFACE, Type
                                    .getInternalName(RemoteCall.class),
                                    "getInputStream", Type.getMethodDescriptor(
                                            Type.getType(ObjectInput.class),
                                            new Type[] {}));

                    Class readCls = returntype.isPrimitive() ? returntype
                            : Object.class;
                    code.visitMethodInsn(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(CHECKCAST, typeArg(returntype));

                    // leave result on stack for return
                }

                // this.ref.done(call)
                code.visitVarInsn(ALOAD, var.get("this"));
                code.visitFieldInsn(GETFIELD, Type
                        .getInternalName(RemoteObject.class), "ref", Type
                        .getDescriptor(RemoteRef.class));
                code.visitVarInsn(ALOAD, var.deallocate("call"));
                code.visitMethodInsn(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(ASTORE, var.allocate("exception"));

                // throw new UnmarshalException(msg, e)
                code.visitTypeInsn(NEW, typeArg(UnmarshalException.class));
                code.visitInsn(DUP);
                code.visitLdcInsn("error unmarshalling return");
                code.visitVarInsn(ALOAD, var.deallocate("exception"));
                code.visitMethodInsn(INVOKESPECIAL, Type
                        .getInternalName(UnmarshalException.class), "",
                        Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {
                                Type.getType(String.class),
                                Type.getType(Exception.class) }));
                code.visitInsn(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(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(ASTORE, var.allocate("exception"));
                code.visitTypeInsn(NEW, typeArg(UnexpectedException.class));
                code.visitInsn(DUP);
                code.visitLdcInsn(msg);
                code.visitVarInsn(ALOAD, var.deallocate("exception"));
                code.visitMethodInsn(INVOKESPECIAL, Type
                        .getInternalName(UnexpectedException.class), "",
                        Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {
                                Type.getType(String.class),
                                Type.getType(Exception.class) }));
                code.visitInsn(ATHROW);

                code.visitTryCatchBlock(methodTryBegin, rethrowHandler,
                        generalHandler, Type.getInternalName(Exception.class));
            }

            code.visitMaxs(-1, -1);
        }

        stub.visitEnd();
        byte[] classData = stub.toByteArray();
        classInfo.setBytecode(classData);

        return classInfo;
    }

    private void generateSkel() throws IOException {
        ClassInfo classInfo = generateSkelData();

        File file = new File(destination == null ? "" : destination
                + File.separator
                + classInfo.getName().replace('.', File.separatorChar)
                + ".class");

        if (file.exists())
            file.delete();
        if (file.getParentFile() != null)
            file.getParentFile().mkdirs();
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(classInfo.getBytecode());
        fos.flush();
        fos.close();
    }

    private ClassInfo generateSkelData() throws IOException {
        ClassInfo classInfo = new ClassInfo();
        skelname = fullclassname + "_Skel";
        classInfo.setName(skelname);

        if (verbose)
            System.out.println("[Generating class " + skelname + "]");

        final ClassWriter skel;
        if (Boolean.getBoolean(COMPUTE_MAX)) {
            // Let asm to compute the MAXs
            skel = new ClassWriter(ClassWriter.COMPUTE_MAXS);
        } else {
            skel = new ClassWriter(0);
        }

        classInternalName = skelname.replace('.', '/');
        skel.visit(V1_1, ACC_PUBLIC + ACC_FINAL, classInternalName, null, Type
                .getInternalName(Object.class), new String[] { Type.getType(
                Skeleton.class).getInternalName() });

        skel.visitField(ACC_PRIVATE + ACC_STATIC + ACC_FINAL, "interfaceHash",
                Type.LONG_TYPE.getDescriptor(), null, new Long(
                        getInterfaceHash(clazz)));

        skel.visitField(ACC_PRIVATE + ACC_STATIC + ACC_FINAL, "operations",
                Type.getDescriptor(Operation[].class), null, null);

        MethodVisitor clinit = skel
                .visitMethod(ACC_STATIC, "", Type.getMethodDescriptor(
                        Type.VOID_TYPE, new Type[] {}), null, null);

        fillOperationArray(clinit);
        clinit.visitInsn(RETURN);

        clinit.visitMaxs(10, 5);

        // no arg public constructor
        MethodVisitor init = skel
                .visitMethod(ACC_PUBLIC, "", Type.getMethodDescriptor(
                        Type.VOID_TYPE, new Type[] {}), null, null);
        init.visitVarInsn(ALOAD, 0);
        init.visitMethodInsn(INVOKESPECIAL, Type.getInternalName(Object.class),
                "", Type.getMethodDescriptor(Type.VOID_TYPE,
                        new Type[] {}));
        init.visitInsn(RETURN);
        init.visitMaxs(10, 5);

        /*
         * public Operation[] getOperations() returns a clone of the operations
         * array
         */
        MethodVisitor getOp = skel.visitMethod(ACC_PUBLIC, "getOperations",
                Type.getMethodDescriptor(Type.getType(Operation[].class),
                        new Type[] {}), null, null);
        getOp.visitFieldInsn(GETSTATIC, classInternalName, "operations", Type
                .getDescriptor(Operation[].class));
        getOp.visitMethodInsn(INVOKEVIRTUAL,
                Type.getInternalName(Object.class), "clone", Type
                        .getMethodDescriptor(Type.getType(Object.class),
                                new Type[] {}));
        getOp.visitTypeInsn(CHECKCAST, typeArg(Operation[].class));
        getOp.visitInsn(ARETURN);
        getOp.visitMaxs(10, 5);

        // public void dispatch(Remote, RemoteCall, int opnum, long hash)
        MethodVisitor dispatch = skel.visitMethod(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(ILOAD, var.get("opnum"));
        Label nonNegativeOpnum = new Label();
        Label opnumSet = new Label();
        dispatch.visitJumpInsn(IFGE, nonNegativeOpnum);

        for (int i = 0; i < remotemethods.length; i++) {
            // assign opnum if hash matches supplied hash
            dispatch.visitVarInsn(LLOAD, var.get("hash"));
            dispatch.visitLdcInsn(new Long(remotemethods[i].hash));
            Label notit = new Label();
            dispatch.visitInsn(LCMP);
            dispatch.visitJumpInsn(IFNE, notit);

            // opnum = 
            dispatch.visitLdcInsn(new Integer(i));
            dispatch.visitVarInsn(ISTORE, var.get("opnum"));
            dispatch.visitJumpInsn(GOTO, opnumSet);
            dispatch.visitLabel(notit);
        }

        // throw new SkeletonMismatchException
        Label mismatch = new Label();
        dispatch.visitJumpInsn(GOTO, mismatch);

        dispatch.visitLabel(nonNegativeOpnum);

        // if opnum is already set, check that the hash matches the interface
        dispatch.visitVarInsn(LLOAD, var.get("hash"));
        dispatch.visitFieldInsn(GETSTATIC, classInternalName, "interfaceHash",
                Type.LONG_TYPE.getDescriptor());
        dispatch.visitInsn(LCMP);
        dispatch.visitJumpInsn(IFEQ, opnumSet);

        dispatch.visitLabel(mismatch);
        dispatch.visitTypeInsn(NEW, typeArg(SkeletonMismatchException.class));
        dispatch.visitInsn(DUP);
        dispatch.visitLdcInsn("interface hash mismatch");
        dispatch.visitMethodInsn(INVOKESPECIAL, Type
                .getInternalName(SkeletonMismatchException.class), "",
                Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type
                        .getType(String.class) }));
        dispatch.visitInsn(ATHROW);

        // opnum has been set
        dispatch.visitLabel(opnumSet);

        dispatch.visitVarInsn(ALOAD, var.get("remoteobj"));
        dispatch.visitTypeInsn(CHECKCAST, typeArg(clazz));
        dispatch.visitVarInsn(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(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(NEW, typeArg(UnmarshalException.class));
        dispatch.visitInsn(DUP);
        dispatch.visitLdcInsn("invalid method number");
        dispatch.visitMethodInsn(INVOKESPECIAL, Type
                .getInternalName(UnmarshalException.class), "", Type
                .getMethodDescriptor(Type.VOID_TYPE, new Type[] { Type
                        .getType(String.class) }));
        dispatch.visitInsn(ATHROW);

        dispatch.visitMaxs(3 * remotemethods.length, var.max() + 1);

        skel.visitEnd();
        byte[] classData = skel.toByteArray();
        classInfo.setBytecode(classData);

        return classInfo;
    }

    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(ALOAD, var.get("remotecall"));
        cv.visitMethodInsn(INVOKEINTERFACE, Type
                .getInternalName(RemoteCall.class), "getInputStream", Type
                .getMethodDescriptor(Type.getType(ObjectInput.class),
                        new Type[] {}));
        cv.visitVarInsn(ASTORE, var.allocate("objectinput"));

        for (int i = 0; i < sig.length; i++) {
            // dup input stream
            cv.visitVarInsn(ALOAD, var.get("objectinput"));

            Class readCls = sig[i].isPrimitive() ? sig[i] : Object.class;

            // in.readFoo()
            cv.visitMethodInsn(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(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(JSR, closeInput);
        cv.visitJumpInsn(GOTO, doCall);

        // throw new UnmarshalException
        Label handler = new Label();
        cv.visitLabel(handler);
        cv.visitVarInsn(ASTORE, var.allocate("exception"));
        cv.visitTypeInsn(NEW, typeArg(UnmarshalException.class));
        cv.visitInsn(DUP);
        cv.visitLdcInsn("error unmarshalling arguments");
        cv.visitVarInsn(ALOAD, var.deallocate("exception"));
        cv.visitMethodInsn(INVOKESPECIAL, Type
                .getInternalName(UnmarshalException.class), "", Type
                .getMethodDescriptor(Type.VOID_TYPE, new Type[] {
                        Type.getType(String.class),
                        Type.getType(Exception.class) }));
        cv.visitVarInsn(ASTORE, var.allocate("toThrow"));
        cv.visitJumpInsn(JSR, closeInput);
        cv.visitVarInsn(ALOAD, var.get("toThrow"));
        cv.visitInsn(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(ASTORE, var.allocate("retAddress"));
        cv.visitVarInsn(ALOAD, var.get("remotecall"));
        cv.visitMethodInsn(INVOKEINTERFACE, Type
                .getInternalName(RemoteCall.class), "releaseInputStream", Type
                .getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
        cv.visitVarInsn(RET, var.deallocate("retAddress"));
        var.deallocate("toThrow");

        // do the call using args stored as variables
        cv.visitLabel(doCall);
        cv.visitVarInsn(ALOAD, var.get("remoteobj"));
        for (int i = 0; i < sig.length; i++)
            cv.visitVarInsn(loadOpcode(sig[i]), var.deallocate(param(m, i)));
        cv.visitMethodInsn(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(ALOAD, var.get("remotecall"));
        cv.visitInsn(ICONST_1);
        cv.visitMethodInsn(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(INVOKEINTERFACE, Type
                    .getInternalName(ObjectOutput.class),
                    writeMethod(returntype), Type.getMethodDescriptor(
                            Type.VOID_TYPE,
                            new Type[] { Type.getType(writeCls) }));
        }

        cv.visitInsn(RETURN);

        // throw new MarshalException
        Label marshalHandler = new Label();
        cv.visitLabel(marshalHandler);
        cv.visitVarInsn(ASTORE, var.allocate("exception"));
        cv.visitTypeInsn(NEW, typeArg(MarshalException.class));
        cv.visitInsn(DUP);
        cv.visitLdcInsn("error marshalling return");
        cv.visitVarInsn(ALOAD, var.deallocate("exception"));
        cv.visitMethodInsn(INVOKESPECIAL, Type
                .getInternalName(MarshalException.class), "", Type
                .getMethodDescriptor(Type.VOID_TYPE, new Type[] {
                        Type.getType(String.class),
                        Type.getType(Exception.class) }));
        cv.visitInsn(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 = IRETURN;
        else if (cls.equals(Byte.TYPE))
            returncode = IRETURN;
        else if (cls.equals(Character.TYPE))
            returncode = IRETURN;
        else if (cls.equals(Short.TYPE))
            returncode = IRETURN;
        else if (cls.equals(Integer.TYPE))
            returncode = IRETURN;
        else if (cls.equals(Long.TYPE))
            returncode = LRETURN;
        else if (cls.equals(Float.TYPE))
            returncode = FRETURN;
        else if (cls.equals(Double.TYPE))
            returncode = DRETURN;
        else if (cls.equals(Void.TYPE))
            returncode = RETURN;
        else
            returncode = 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 = ILOAD;
        else if (cls.equals(Byte.TYPE))
            loadcode = ILOAD;
        else if (cls.equals(Character.TYPE))
            loadcode = ILOAD;
        else if (cls.equals(Short.TYPE))
            loadcode = ILOAD;
        else if (cls.equals(Integer.TYPE))
            loadcode = ILOAD;
        else if (cls.equals(Long.TYPE))
            loadcode = LLOAD;
        else if (cls.equals(Float.TYPE))
            loadcode = FLOAD;
        else if (cls.equals(Double.TYPE))
            loadcode = DLOAD;
        else
            loadcode = 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 = ISTORE;
        else if (cls.equals(Byte.TYPE))
            storecode = ISTORE;
        else if (cls.equals(Character.TYPE))
            storecode = ISTORE;
        else if (cls.equals(Short.TYPE))
            storecode = ISTORE;
        else if (cls.equals(Integer.TYPE))
            storecode = ISTORE;
        else if (cls.equals(Long.TYPE))
            storecode = LSTORE;
        else if (cls.equals(Float.TYPE))
            storecode = FSTORE;
        else if (cls.equals(Double.TYPE))
            storecode = DSTORE;
        else
            storecode = 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);
    }

    /**
     * Process the options until we find the first argument.
     */
    private void parseOptions() {
        for (;;) {
            if (next >= args.length || args[next].charAt(0) != '-')
                break;
            String arg = args[next];
            next++;

            // Accept `--' options if they look long enough.
            if (arg.length() > 3 && arg.charAt(0) == '-'
                    && arg.charAt(1) == '-')
                arg = arg.substring(1);

            if (arg.equals("-keep"))
                keep = true;
            else if (arg.equals("-keepgenerated"))
                keep = true;
            else if (arg.equals("-v1.1")) {
                need11Stubs = true;
                need12Stubs = false;
            } else if (arg.equals("-vcompat")) {
                need11Stubs = true;
                need12Stubs = true;
            } else if (arg.equals("-v1.2")) {
                need11Stubs = false;
                need12Stubs = true;
            } else if (arg.equals("-g")) {
            } else if (arg.equals("-depend")) {
            } else if (arg.equals("-nowarn")) {
            } else if (arg.equals("-verbose"))
                verbose = true;
            else if (arg.equals("-nocompile"))
                compile = false;
            else if (arg.equals("-classpath")) {
                classpath = args[next];
                next++;
                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) {
                        error("malformed classpath component " + path);
                    }
                }
                // Explicitely set the parent.
                // AFAIK, by default, it's the system classloader
                ClassLoader parent = RMIC.class.getClassLoader();
                loader = new URLClassLoader(u, parent);
            } else if (arg.equals("-help"))
                usage();
            else if (arg.equals("-version")) {
                System.out.println("rmic ("
                        + System.getProperty("java.vm.name") + ") "
                        + System.getProperty("java.vm.version"));
                System.out.println();
                System.out
                        .println("Copyright 2002 Free Software Foundation, Inc.");
                System.out
                        .println("This is free software; see the source for copying conditions.  There is NO");
                System.out
                        .println("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.");
                System.exit(0);
            } else if (arg.equals("-d")) {
                destination = args[next];
                next++;
            } else if (arg.charAt(1) == 'J') {
            } else
                error("unrecognized option `" + arg + "'");
        }
    }

    private void findRemoteMethods() {
        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];

                    // Check if the interface was already added
                    if (mRemoteInterfaces.contains(remoteInterface))
                        continue;

                    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) {
                            logError("Method " + m
                                    + " does not throw a RemoteException");
                            continue;
                        }

                        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.
     * 
     * @param theError
     */
    private void logError(String theError) {
        errorCount++;
        System.err.println("error:" + theError);
    }

    private static void error(String message) {
        System.err.println("rmic: " + message);
        System.err.println("Try `rmic --help' for more information.");
        System.exit(1);
    }

    private static void usage() {
        System.out
                .println("Usage: rmic [OPTION]... CLASS...\n"
                        + "\n"
                        + "    -keep *            Don't delete any intermediate files\n"
                        + "    -keepgenerated *    Same as -keep\n"
                        + "    -v1.1            Java 1.1 style stubs only\n"
                        + "    -vcompat        Java 1.1 & Java 1.2 stubs\n"
                        + "    -v1.2            Java 1.2 style stubs only\n"
                        + "    -g *            Generated debugging information\n"
                        + "    -depend *        Recompile out-of-date files\n"
                        + "    -nowarn    *        Suppress warning messages\n"
                        + "    -nocompile *        Don't compile the generated files\n"
                        + "    -verbose         Output what's going on\n"
                        + "    -classpath      Use given path as classpath\n"
                        + "    -d          Specify where to place generated classes\n"
                        + "    -J *        Pass flag to Java\n"
                        + "    -help            Print this help, then exit\n"
                        + "    -version        Print version number, then exit\n"
                        + "\n"
                        + "  * Option currently ignored\n"
                        + "Long options can be used with `--option' form as well.");
        System.exit(0);
    }

    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 = 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;
        }
    }

    // XXX from RMIHashes (modified)
    public static long getInterfaceHash(Class cls) {
        return cls.hashCode();

        /*
         * 8.3 of RMI spec # (int) stub version number, always 1 # for each
         * remote method, in order of operation number: * (UTF-8) remote method
         * name * (UTF-8) remote method descriptor (see section 4.3.3 of JVMS) *
         * for each declared exception, in lexicographic order of binary name: o
         * (UTF-8) the name of the exception class
         */
    }

    // XXX from RMIHashes (modified)
    public static long getMethodHash(Method meth) {
        // Object Serialization Spec 8.3
        try {
            MessageDigest md = MessageDigest.getInstance("SHA");
            // or:remove this statement: DigestOutputStream digest_out = new
            // DigestOutputStream (nullOutputStream, md);
            ByteArrayOutputStream digest_out = new ByteArrayOutputStream();
            DataOutputStream data_out = new DataOutputStream(digest_out);

            StringBuffer sbuf = new StringBuffer();
            sbuf.append(meth.getName());
            sbuf.append(Type.getMethodDescriptor(meth));
            data_out.writeUTF(sbuf.toString());
            data_out.flush();
            data_out.close();

            md.update(digest_out.toByteArray()); // or:remove this statement
            byte[] sha = md.digest();
            long result = 0;
            int len = sha.length < 8 ? sha.length : 8;
            for (int i = 0; i < len; i++)
                result += (long) (sha[i] & 0xFF) << (8 * i);
            return result;
        } catch (Exception _) {
            return -1L;
        }
    }

    /**
     * Generate a stub for the given classname and the given classloader
     * 
     * @param className
     *            the name of the class
     * @param classLoader
     *            the classloader to use to find the class.
     * @return list of generated class with their name and associated bytecode.
     * @throws RMICException
     *             if it fails
     */
    public static List generateStubForClass(final String className,
            final ClassLoader classLoader) throws RMICException {
        List classInfos = new ArrayList();

        // New object
        RMIC rmic = new RMIC(null);

        // Sets the loader
        rmic.loader = classLoader;

        // Init list
        rmic.mRemoteInterfaces = new ArrayList();

        // Analyze the class
        try {
            rmic.analyzeClass(className);
        } catch (Exception e) {
            throw new RMICException("Unable to analyze the class '" + className
                    + "'", e);
        }

        // generate stub
        try {
            ClassInfo classInfo = rmic.generateStubData();
            // add result
            classInfos.add(classInfo);
        } catch (IOException e) {
            throw new RMICException("Unable to generate stub for the class '"
                    + className + "'", e);
        }

        if (rmic.need11Stubs) {
            try {
                ClassInfo classInfo = rmic.generateSkelData();
                // add result
                classInfos.add(classInfo);
            } catch (IOException e) {
                throw new RMICException(
                        "Unable to generate skel for the class '" + className
                                + "'", e);
            }
        }

        return classInfos;
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy