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;


  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(", ");
          }

        // 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 = new ClassWriter(ClassWriter.COMPUTE_MAXS);
    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(-1, -1);

    // 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(-1, -1);

    /*
     * 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(-1, -1);

    // 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(-1, -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