classpath
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

a faster rmic


From: Archit Shah
Subject: a faster rmic
Date: Mon, 14 Feb 2005 15:01:57 -0500
User-agent: Mozilla Thunderbird 0.9 (X11/20041127)

Currently classpath's rmic works by generating stub and skeleton java files and then invoking a java compiler on the generated java files. Directly generating bytecode for the stub and skeleton class files would be much faster and not significantly more complicated as the generated files are very simple. I have implemented and attached such an rmi compiler based on classpath's RMIC.java. It is basically a direct translation of RMIC.java to generate bytecode instead of a giant Java string. To actually generate the bytecode, I am using asm [1], a BSD-licensed Java bytecode manipulation framework. Along the way, I found (and fixed) a few bugs and unimplemented features in classpath's rmic. It is not quite complete yet, but is mostly working.

Is this bytecode-generating rmi compiler suitable for introduction into classpath? I'm hoping that this new rmic would replace the old one. Reservations about this option are understandable as the new code is about two-thirds longer than the original. To mitigate this concern, I've written a few tests that can hopefully be contributed to mauve [2] or some similar test suite.

If there is concern about the dependency on asm, I am open to suggestions for an alternate bytecode generating facility. I understand there is a gnu.bytecode in kawa [3]. I'm perfectly happy to switch from asm to something else if it will ease adoption of my code.

The motivation for a faster rmi compiler is to speed build times for developers using jonas [4], an open source implementation of the J2EE specification. In the process of building enterprise java beans, rmi compilation is the slowest step. My initial tests show that using direct bytecode generation will speed build times by about 40%.

 -- Archit

Footnotes

1. http://asm.objectweb.org/
2. http://sources.redhat.com/mauve/
3. http://www.gnu.org/software/kawa/
4. http://jonas.objectweb.org/
/* 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.

   Linking this library statically or dynamically with other modules is
   making a combined work based on this library.  Thus, the terms and
   conditions of the GNU General Public License cover the whole
   combination.

   As a special exception, the copyright holders of this library give you
   permission to link this library with independent modules to produce an
   executable, regardless of the license terms of these independent
   modules, and to copy and distribute the resulting executable under
   terms of your choice, provided that you also meet, for each linked
   independent module, the terms and conditions of the license of that
   module.  An independent module is a module which is not derived from
   or based on this library.  If you modify this library, you may extend
   this exception to your version of the library, but you are not
   obligated to do so.  If you do not wish to do so, delete this
   exception statement from your version. */

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.rmi.MarshalException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.rmi.UnexpectedException;
import java.rmi.UnmarshalException;
import java.rmi.server.Operation;
import java.rmi.server.RemoteCall;
import java.rmi.server.RemoteObject;
import java.rmi.server.RemoteRef;
import java.rmi.server.RemoteStub;
import java.rmi.server.Skeleton;
import java.rmi.server.SkeletonMismatchException;
import java.security.MessageDigest;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.CodeVisitor;
import org.objectweb.asm.Constants;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;

public class RMIC {
  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 PrintWriter out;
  private PrintWriter ctrl;
  private Class clazz;
  private String classname;
  private String classInternalName;
  private String fullclassname;
  private MethodRef[] remotemethods;
  private String stubname;
  private String skelname;
  private int errorCount = 0;
  private List mRemoteInterfaces = new ArrayList();
  private ClassLoader loader = null;
  private String classpath;


  private static class C
    implements Constants
  {
  }

  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 classname) throws Exception
  {
    errorCount = 0;
    analyzeClass(classname);
    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
      {
        clazz = ((loader == null)
                 ? Class.forName(fullclassname)
                 : Class.forName(fullclassname, true, loader));
      }
    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 void generateClassForNamer(ClassVisitor cls)
  {
    CodeVisitor cv =
      cls.visitMethod
      (C.ACC_PRIVATE + C.ACC_STATIC + C.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(C.ALOAD, 0);
    cv.visitMethodInsn
      (C.INVOKESTATIC,
       Type.getInternalName(Class.class),
       "forName",
       Type.getMethodDescriptor
       (Type.getType(Class.class), new Type[] { Type.getType(String.class) }));
    cv.visitInsn(C.ARETURN);

    Label handler = new Label();
    cv.visitLabel(handler);
    cv.visitVarInsn(C.ASTORE, 1);
    cv.visitTypeInsn(C.NEW, typeArg(NoClassDefFoundError.class));
    cv.visitInsn(C.DUP);
    cv.visitVarInsn(C.ALOAD, 1);
    cv.visitMethodInsn
      (C.INVOKEVIRTUAL,
       Type.getInternalName(ClassNotFoundException.class),
       "getMessage",
       Type.getMethodDescriptor(Type.getType(String.class), new Type[] {}));
    cv.visitMethodInsn
      (C.INVOKESPECIAL,
       Type.getInternalName(NoClassDefFoundError.class),
       "<init>",
       Type.getMethodDescriptor
       (Type.VOID_TYPE, new Type[] { Type.getType(String.class) }));
    cv.visitInsn(C.ATHROW);
    cv.visitTryCatchBlock
      (start, handler, handler,
       Type.getInternalName(ClassNotFoundException.class));
    cv.visitMaxs(4, 2);
  }

  private void generateClassConstant(CodeVisitor 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
          (C.GETSTATIC, Type.getInternalName(boxCls), "TYPE",
           Type.getDescriptor(Class.class));
        return;
      }
    cv.visitLdcInsn(cls.getName());
    cv.visitMethodInsn
      (C.INVOKESTATIC, classInternalName, forName,
       Type.getMethodDescriptor
       (Type.getType(Class.class),
        new Type[] { Type.getType(String.class) }));
  }

  private void generateClassArray(CodeVisitor code, Class[] classes)
  {
    code.visitLdcInsn(new Integer(classes.length));
    code.visitTypeInsn(C.ANEWARRAY, typeArg(Class.class));
    for (int i = 0; i < classes.length; i++)
      {
        code.visitInsn(C.DUP);
        code.visitLdcInsn(new Integer(i));
        generateClassConstant(code, classes[i]);
        code.visitInsn(C.AASTORE);
      }
  }

  private void fillOperationArray(CodeVisitor clinit)
  {
    // Operations array
    clinit.visitLdcInsn(new Integer(remotemethods.length));
    clinit.visitTypeInsn(C.ANEWARRAY, typeArg(Operation.class));
    clinit.visitFieldInsn
      (C.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
          (C.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(C.NEW, typeArg(Operation.class));
        clinit.visitInsn(C.DUP);
        clinit.visitLdcInsn(desc.toString());
        clinit.visitMethodInsn
          (C.INVOKESPECIAL,
           Type.getInternalName(Operation.class),
           "<init>",
           Type.getMethodDescriptor
           (Type.VOID_TYPE, new Type[] { Type.getType(String.class) }));

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

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

        /*
         * $method_<i>m.getName()</i>_<i>i</i> =
         *   <i>m.getDeclaringClass()</i>.class.getMethod
         *     (m.getName(), m.getParameterType())
         */
        String methodVar = "$method_" + m.getName() + "_" + i;
        generateClassConstant(clinit, m.getDeclaringClass());
        clinit.visitLdcInsn(m.getName());
        generateClassArray(clinit, m.getParameterTypes());
        clinit.visitMethodInsn
          (C.INVOKEVIRTUAL,
           Type.getInternalName(Class.class),
           "getMethod",
           Type.getMethodDescriptor
           (Type.getType(Method.class),
            new Type[] { Type.getType(String.class),
                         Type.getType(Class[].class) }));

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

  private void generateStub() throws IOException
  {
    stubname = fullclassname + "_Stub";
    String stubclassname = classname + "_Stub";
    File file = new File((destination == null ? "." : destination)
                         + File.separator
                         + stubname.replace('.', File.separatorChar)
                         + ".class");

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

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

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

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

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

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

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

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

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

        generateStaticMethodObjs(clinit);

        // jump past handler
        clinit.visitInsn(C.RETURN);
        clinit.visitLabel(handler);
        if (need11Stubs)
          {
            // useNewInvoke = false
            clinit.visitIntInsn(C.BIPUSH, 0);
            clinit.visitFieldInsn
              (C.PUTSTATIC, classInternalName, "useNewInvoke",
               Type.BOOLEAN_TYPE.getDescriptor());
            clinit.visitInsn(C.RETURN);
          }
        else
          {
            // throw NoSuchMethodError
            clinit.visitTypeInsn(C.NEW, typeArg(NoSuchMethodError.class));
            clinit.visitInsn(C.DUP);
            clinit.visitLdcInsn("stub class initialization failed");
            clinit.visitMethodInsn
              (C.INVOKESPECIAL,
               Type.getInternalName(NoSuchMethodError.class),
               "<init>",
               Type.getMethodDescriptor
               (Type.VOID_TYPE,
                new Type[] { Type.getType(String.class) }));
            clinit.visitInsn(C.ATHROW);
          }

        clinit.visitTryCatchBlock
          (begin, handler, handler,
           Type.getInternalName(NoSuchMethodException.class));

      }

    clinit.visitMaxs(8, 0);

    generateClassForNamer(stub);

    // Constructors
    if (need11Stubs)
      {
        // no arg public constructor
        CodeVisitor code = stub.visitMethod
          (C.ACC_PUBLIC, "<init>",
           Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}),
           null, null);
        code.visitVarInsn(C.ALOAD, 0);
        code.visitMethodInsn
          (C.INVOKESPECIAL, superInternalName, "<init>",
           Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
        code.visitInsn(C.RETURN);

        code.visitMaxs(1, 1);
      }

    // public RemoteRef constructor
    CodeVisitor constructor = stub.visitMethod
      (C.ACC_PUBLIC, "<init>",
       Type.getMethodDescriptor
       (Type.VOID_TYPE, new Type[] {Type.getType(RemoteRef.class)}),
       null, null);
    constructor.visitVarInsn(C.ALOAD, 0);
    constructor.visitVarInsn(C.ALOAD, 1);
    constructor.visitMethodInsn
      (C.INVOKESPECIAL, superInternalName, "<init>",
       Type.getMethodDescriptor
       (Type.VOID_TYPE, new Type[] {Type.getType(RemoteRef.class)}));
    constructor.visitInsn(C.RETURN);
    constructor.visitMaxs(2, 2);

    // 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(m.getExceptionTypes());

        CodeVisitor code = stub.visitMethod
          (C.ACC_PUBLIC,
           m.getName(),
           Type.getMethodDescriptor(Type.getType(returntype), typeArray(sig)),
           internalNameArray(typeArray(except)),
           null);

        // this and parameters are the initial vars
        int varIndex = 1;
        for (int j = 0; j < sig.length; j++)
          {
            if (sig[j].equals(Double.TYPE) || sig[j].equals(Long.TYPE))
              varIndex += 2;
            else
              varIndex += 1;
          }

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

        if (need12Stubs)
          {
            Label oldInvoke = new Label();
            if (need11Stubs)
              {
                // if not useNewInvoke jump to old invoke
                code.visitFieldInsn
                  (C.GETSTATIC, classInternalName, "useNewInvoke",
                   Type.getDescriptor(boolean.class));
                code.visitJumpInsn(C.IFNE, oldInvoke);
              }

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

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

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

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

                final int argArray = varIndex++;
                code.visitVarInsn(C.ASTORE, argArray);

                int argIndex = 1;
                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(C.ALOAD, argArray);
                    code.visitLdcInsn(new Integer(j));

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

                    code.visitInsn(C.AASTORE);
                    argIndex += size;
                  }

                code.visitVarInsn(C.ALOAD, argArray);
                // XXX varIndex--;
              }

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

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


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

        if (need11Stubs)
          {

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

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

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

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

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

            code.visitMethodInsn
              (C.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
            final int callVar = varIndex++;
            code.visitInsn(C.DUP);
            code.visitVarInsn(C.ASTORE, callVar);

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

            // ObjectOutput out = call.getOutputStream();
            code.visitMethodInsn
              (C.INVOKEINTERFACE,
               Type.getInternalName(RemoteCall.class),
               "getOutputStream",
               Type.getMethodDescriptor
               (Type.getType(ObjectOutput.class), new Type[] {}));

            int paramIndex = 1;
            for (int j = 0; j < sig.length; j++)
              {
                // dup the ObjectOutput
                code.visitInsn(C.DUP);

                // get j'th arg to remote method
                code.visitVarInsn(loadOpcode(sig[j]), paramIndex);
                paramIndex += size(sig[j]);

                Class argCls =
                  sig[j].isPrimitive() ? sig[j] : Object.class;

                // out.writeFoo
                code.visitMethodInsn
                  (C.INVOKEINTERFACE,
                   Type.getInternalName(ObjectOutput.class),
                   writeMethod(sig[j]),
                   Type.getMethodDescriptor
                   (Type.VOID_TYPE,
                    new Type[] { Type.getType(argCls) }));
              }

            // pop ObjectOutput
            code.visitInsn(C.POP);

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

            // throw new MarshalException(msg, ioexception);
            int caughtException = varIndex++;
            code.visitVarInsn(C.ASTORE, caughtException);
            code.visitTypeInsn(C.NEW, typeArg(MarshalException.class));
            code.visitInsn(C.DUP);
            code.visitLdcInsn("error marshalling arguments");
            code.visitVarInsn(C.ALOAD, caughtException);
            // XXX varIndex--;
            code.visitMethodInsn
              (C.INVOKESPECIAL,
               Type.getInternalName(MarshalException.class),
               "<init>",
               Type.getMethodDescriptor
               (Type.VOID_TYPE,
                new Type[] { Type.getType(String.class),
                             Type.getType(Exception.class) }));
            code.visitInsn(C.ATHROW);

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

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

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

                // leave result on stack for return
              }

            // this.ref.done(call)
            code.visitVarInsn(C.ALOAD, 0);
            code.visitFieldInsn
              (C.GETFIELD,
               Type.getInternalName(RemoteObject.class),
               "ref",
               Type.getDescriptor(RemoteRef.class));
            code.visitVarInsn(C.ALOAD, callVar);
            code.visitMethodInsn
              (C.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);
            int exception = varIndex++;
            code.visitVarInsn(C.ASTORE, exception);

            // throw new UnmarshalException(msg, e)
            code.visitTypeInsn(C.NEW, typeArg(UnmarshalException.class));
            code.visitInsn(C.DUP);
            code.visitLdcInsn("error unmarshalling return");
            code.visitVarInsn(C.ALOAD, exception);
            // XXX varIndex--;
            code.visitMethodInsn
              (C.INVOKESPECIAL,
               Type.getInternalName(UnmarshalException.class),
               "<init>",
               Type.getMethodDescriptor
               (Type.VOID_TYPE,
                new Type[] { Type.getType(String.class),
                             Type.getType(Exception.class) }));
            code.visitInsn(C.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(C.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++) {
          Class exception = except[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";
            int exception = varIndex++;

            // throw new java.rmi.UnexpectedException(msg, e)
            code.visitVarInsn(C.ASTORE, exception);
            code.visitTypeInsn(C.NEW, typeArg(UnexpectedException.class));
            code.visitInsn(C.DUP);
            code.visitLdcInsn(msg);
            code.visitVarInsn(C.ALOAD, exception);
            // XXX varIndex--;
            code.visitMethodInsn
              (C.INVOKESPECIAL,
               Type.getInternalName(UnexpectedException.class),
               "<init>",
               Type.getMethodDescriptor
               (Type.VOID_TYPE,
                new Type [] { Type.getType(String.class),
                              Type.getType(Exception.class) }));
            code.visitInsn(C.ATHROW);

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

        code.visitMaxs(10, varIndex);
      }

    stub.visitEnd();
    byte[] classData = stub.toByteArray();
    if (file.exists())
      file.delete();
    FileOutputStream fos = new FileOutputStream(file);
    fos.write(classData);
    fos.flush();
    fos.close();
  }

  private void generateSkel() throws IOException
  {
    skelname = fullclassname + "_Skel";
    String skelclassname = classname + "_Skel";
    File file = new File(destination == null ? "" : destination
                         + File.separator
                         + skelname.replace('.', File.separatorChar)
                         + ".class");
    if (verbose)
      System.out.println("[Generating class " + skelname + "]");

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

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

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

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

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

    clinit.visitMaxs(6, 0);

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

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

    // public void dispatch(Remote, RemoteCall, int opnum, long hash)
    CodeVisitor dispatch = skel.visitMethod
      (C.ACC_PUBLIC,
       "dispatch",
       Type.getMethodDescriptor
       (Type.VOID_TYPE,
        new Type[] { Type.getType(Remote.class),
                     Type.getType(RemoteCall.class),
                     Type.INT_TYPE, Type.LONG_TYPE }),
       new String[] { Type.getInternalName(Exception.class) },
       null);

    final int remoteobj = 1;
    final int opnum = 3;
    final int hash = 4;

    /*
     * if opnum >= 0
     * XXX it is unclear why there is handling of negative opnums
     */
    dispatch.visitVarInsn(C.ILOAD, opnum);
    Label nonNegativeOpnum = new Label();
    Label opnumSet = new Label();
    dispatch.visitJumpInsn(C.IFGE, nonNegativeOpnum);

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

        // opnum = <opnum>
        dispatch.visitLdcInsn(new Integer(i));
        dispatch.visitVarInsn(C.ISTORE, opnum);
        dispatch.visitJumpInsn(C.GOTO, opnumSet);
        dispatch.visitLabel(notit);
      }

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

    dispatch.visitLabel(nonNegativeOpnum);

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

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

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

    dispatch.visitVarInsn(C.ALOAD, remoteobj);
    dispatch.visitTypeInsn(C.CHECKCAST, typeArg(clazz));
    dispatch.visitVarInsn(C.ASTORE, 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(C.ILOAD, 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);
      }

    dispatch.visitLabel(deflt);
    dispatch.visitTypeInsn(C.NEW, typeArg(UnmarshalException.class));
    dispatch.visitInsn(C.DUP);
    dispatch.visitLdcInsn("invalid method number");
    dispatch.visitMethodInsn
      (C.INVOKESPECIAL,
       Type.getInternalName(UnmarshalException.class),
       "<init>",
       Type.getMethodDescriptor
       (Type.VOID_TYPE, new Type[] { Type.getType(String.class) }));
    dispatch.visitInsn(C.ATHROW);

    int maxSize = 0;
    for (int i = 0; i < remotemethods.length; i++)
      {
        Method m = remotemethods[i].meth;
        Class[] sig = m.getParameterTypes();
        int size = 0;
        for (int j = 0; j < sig.length; j++)
          size += size(sig[j]);
        maxSize = (size > maxSize) ? size : maxSize;
      }

    dispatch.visitMaxs(8 + maxSize, 9);

    skel.visitEnd();
    byte[] classData = skel.toByteArray();
    if (file.exists()) file.delete();
    FileOutputStream fos = new FileOutputStream(file);
    fos.write(classData);
    fos.flush();
    fos.close();
  }

  private void generateMethodSkel(CodeVisitor cv, Method m)
  {
    // XXX constant duplication
    final int remoteobj = 1;
    final int remotecall = 2;
    // XXX counting vars by hand
    final int objectinput = 6;
    final int result = 7;

    // push remoteobj on the stack as we will invoke method later
    cv.visitVarInsn(C.ALOAD, remoteobj);

    Label readArgs = new Label();
    cv.visitLabel(readArgs);

    boolean needcastcheck = false;

    // ObjectInput in = call.getInputStream();
    cv.visitVarInsn(C.ALOAD, remotecall);
    cv.visitMethodInsn
      (C.INVOKEINTERFACE,
       Type.getInternalName(RemoteCall.class), "getInputStream",
       Type.getMethodDescriptor
       (Type.getType(ObjectInput.class), new Type[] {}));
    cv.visitVarInsn(C.ASTORE, objectinput);

    Class[] sig = m.getParameterTypes();
    for (int i = 0; i < sig.length; i++)
      {
        // dup input stream
        cv.visitVarInsn(C.ALOAD, objectinput);

        Class readCls = sig[i].isPrimitive() ? sig[i] : Object.class;
        // in.readFoo()
        cv.visitMethodInsn
          (C.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(C.CHECKCAST, typeArg(sig[i]));
          }
      }

    Label doCall = new Label();
    Label closeInput = new Label();

    // XXX finally
    // cv.visitJumpInsn(C.JSR, closeInput);
    cv.visitJumpInsn(C.GOTO, doCall);

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

    // object and args are on the stack, ready for invocation
    cv.visitLabel(doCall);
    cv.visitMethodInsn
      (C.INVOKEVIRTUAL, Type.getInternalName(clazz),
       m.getName(), Type.getMethodDescriptor(m));

    Class returntype = m.getReturnType();
    if (! returntype.equals(Void.TYPE))
      cv.visitVarInsn(storeOpcode(returntype), result);

    // write result to result stream
    Label writeResult = new Label();
    cv.visitLabel(writeResult);
    cv.visitVarInsn(C.ALOAD, remotecall);
    cv.visitIntInsn(C.BIPUSH, 1);
    cv.visitMethodInsn
      (C.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), result);
        Class writeCls = returntype.isPrimitive() ? returntype : Object.class;
        cv.visitMethodInsn
          (C.INVOKEINTERFACE,
           Type.getInternalName(ObjectOutput.class),
           writeMethod(returntype),
           Type.getMethodDescriptor
           (Type.VOID_TYPE, new Type[] { Type.getType(writeCls) }));
      }

    cv.visitInsn(C.RETURN);

    // throw new MarshalException
    Label marshalHandler = new Label();
    cv.visitLabel(marshalHandler);
    cv.visitVarInsn(C.ASTORE, objectinput); // XXX reusing var
    cv.visitTypeInsn(C.NEW, typeArg(MarshalException.class));
    cv.visitInsn(C.DUP);
    cv.visitLdcInsn("error marshalling return");
    cv.visitVarInsn(C.ALOAD, objectinput);
    cv.visitMethodInsn
      (C.INVOKESPECIAL,
       Type.getInternalName(MarshalException.class),
       "<init>",
       Type.getMethodDescriptor
       (Type.VOID_TYPE, new Type[] { Type.getType(String.class),
                                     Type.getType(Exception.class) }));
    cv.visitInsn(C.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 = C.IRETURN;
    else if (cls.equals(Byte.TYPE))
      returncode = C.IRETURN;
    else if (cls.equals(Character.TYPE))
      returncode = C.IRETURN;
    else if (cls.equals(Short.TYPE))
      returncode = C.IRETURN;
    else if (cls.equals(Integer.TYPE))
      returncode = C.IRETURN;
    else if (cls.equals(Long.TYPE))
      returncode = C.LRETURN;
    else if (cls.equals(Float.TYPE))
      returncode = C.FRETURN;
    else if (cls.equals(Double.TYPE))
      returncode = C.DRETURN;
    else if (cls.equals(Void.TYPE))
      returncode = C.RETURN;
    else
      returncode = C.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 = C.ILOAD;
    else if (cls.equals(Byte.TYPE))
      loadcode = C.ILOAD;
    else if (cls.equals(Character.TYPE))
      loadcode = C.ILOAD;
    else if (cls.equals(Short.TYPE))
      loadcode = C.ILOAD;
    else if (cls.equals(Integer.TYPE))
      loadcode = C.ILOAD;
    else if (cls.equals(Long.TYPE))
      loadcode = C.LLOAD;
    else if (cls.equals(Float.TYPE))
      loadcode = C.FLOAD;
    else if (cls.equals(Double.TYPE))
      loadcode = C.DLOAD;
    else
      loadcode = C.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 = C.ISTORE;
    else if (cls.equals(Byte.TYPE))
      storecode = C.ISTORE;
    else if (cls.equals(Character.TYPE))
      storecode = C.ISTORE;
    else if (cls.equals(Short.TYPE))
      storecode = C.ISTORE;
    else if (cls.equals(Integer.TYPE))
      storecode = C.ISTORE;
    else if (cls.equals(Long.TYPE))
      storecode = C.LSTORE;
    else if (cls.equals(Float.TYPE))
      storecode = C.FSTORE;
    else if (cls.equals(Double.TYPE))
      storecode = C.DSTORE;
    else
      storecode = C.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];
            String[] cp = classpath.split(File.pathSeparator);
            URL[] u = new URL[cp.length];
            for (int i = 0; i < cp.length; i++)
              {
                File f = new File(cp[i]);
                try
                  {
                    u[i] = f.toURL();
                  }
                catch (java.net.MalformedURLException mue)
                  {
                    System.out.println
                      ("malformed classpath component " + cp[i]);
                    System.exit(-1);
                  }
              }
            loader = new URLClassLoader(u);
            next++;
          }
        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() {
    Class[] interfaces = clazz.getInterfaces();
    Set rmeths = new HashSet();
    for (int i = 0; i < interfaces.length; i++)
      {
        if (java.rmi.Remote.class.isAssignableFrom(interfaces[i]))
          {
            Class remoteInterface = interfaces[i];
            if (verbose)
              System.out.println
                ("[implements " + remoteInterface.getName() + "]");

            // check if the methods declare RemoteExceptions
            Method[] meths = remoteInterface.getMethods();
            for (int j = 0; j < meths.length; j++)
              {
                Method m = meths[j];
                Class[] exs = m.getExceptionTypes();

                boolean throwsRemote = false;
                for (int k = 0; k < exs.length; k++)
                  {
                    if (exs[k].isAssignableFrom(RemoteException.class))
                      throwsRemote = true;
                  }

                if (! throwsRemote)
                  {
                    logError("Method " + m
                             + " does not throw a java.rmi.RemoteException");
                    continue;
                  }

                rmeths.add(m);
              }

            mRemoteInterfaces.add(remoteInterface);
          }
      }

    // Convert into a MethodRef array and sort them
    remotemethods = new MethodRef[rmeths.size()];
    int c = 0;
    for (Iterator i = rmeths.iterator(); i.hasNext();)
      {
        remotemethods[c++] = new MethodRef((Method) i.next());
      }
    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 <path>       Use given path as 
classpath\n"
                       + "      -d <directory>          Specify where to place 
generated classes\n"
                       + "      -J<flag> *              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
  {
    private Method meth;
    private String sig;
    private long hash;

    MethodRef(Method m) {
      meth = m;
      sig = Type.getMethodDescriptor(meth);
      hash = getMethodHash(m);
    }

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

  }

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

    // XXX 8.3 of RMI spec describes correct implementation
    /*
     * # (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 (unmodified)
  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('(');
        Class params[] = meth.getParameterTypes();
        for(int i = 0; i < params.length; i++)
          // sbuf.append(TypeSignature.getEncodingOfClass(params[i]));
          sbuf.append(Type.getDescriptor(params[i]));
        sbuf.append(')');
        Class rcls = meth.getReturnType();
        if(rcls != Void.TYPE)
          // sbuf.append(TypeSignature.getEncodingOfClass(rcls));
          sbuf.append(Type.getDescriptor(rcls));
        else
          sbuf.append('V');
        
        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;
      }
  }

}

reply via email to

[Prev in Thread] Current Thread [Next in Thread]