OSDN Git Service

Merged gcj-eclipse branch to trunk.
[pf3gnuchains/gcc-fork.git] / libjava / classpath / tools / gnu / classpath / tools / rmic / ClassRmicCompiler.java
1 /* ClassRmicCompiler.java --
2    Copyright (c) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005
3    Free Software Foundation, Inc.
4
5 This file is part of GNU Classpath.
6
7 GNU Classpath is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2, or (at your option)
10 any later version.
11
12 GNU Classpath is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GNU Classpath; see the file COPYING.  If not, write to the
19 Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307 USA. */
21
22 package gnu.classpath.tools.rmic;
23
24 import gnu.java.rmi.server.RMIHashes;
25 import java.io.ByteArrayOutputStream;
26 import java.io.DataOutputStream;
27 import java.io.File;
28 import java.io.FileOutputStream;
29 import java.io.FileWriter;
30 import java.io.IOException;
31 import java.io.ObjectInput;
32 import java.io.ObjectOutput;
33 import java.io.PrintWriter;
34 import java.lang.reflect.Method;
35 import java.net.URL;
36 import java.net.URLClassLoader;
37 import java.rmi.MarshalException;
38 import java.rmi.Remote;
39 import java.rmi.RemoteException;
40 import java.rmi.UnexpectedException;
41 import java.rmi.UnmarshalException;
42 import java.rmi.server.Operation;
43 import java.rmi.server.RemoteCall;
44 import java.rmi.server.RemoteObject;
45 import java.rmi.server.RemoteRef;
46 import java.rmi.server.RemoteStub;
47 import java.rmi.server.Skeleton;
48 import java.rmi.server.SkeletonMismatchException;
49 import java.security.MessageDigest;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.HashSet;
53 import java.util.Iterator;
54 import java.util.List;
55 import java.util.Set;
56 import java.util.StringTokenizer;
57 import org.objectweb.asm.ClassVisitor;
58 import org.objectweb.asm.ClassWriter;
59 import org.objectweb.asm.MethodVisitor;
60 import org.objectweb.asm.Opcodes;
61 import org.objectweb.asm.Label;
62 import org.objectweb.asm.Type;
63
64 public class ClassRmicCompiler
65   implements RmicBackend
66 {
67   private String[] args;
68   private int next;
69   private List errors = new ArrayList();
70   private boolean keep = false;
71   private boolean need11Stubs = true;
72   private boolean need12Stubs = true;
73   private boolean compile = true;
74   private boolean verbose;
75   private boolean noWrite;
76   private String destination;
77   private String classpath;
78   private ClassLoader loader;
79   private int errorCount = 0;
80
81   private Class clazz;
82   private String classname;
83   private String classInternalName;
84   private String fullclassname;
85   private MethodRef[] remotemethods;
86   private String stubname;
87   private String skelname;
88   private List mRemoteInterfaces;
89
90   /**
91    * @return true if run was successful
92    */
93   public boolean run(String[] inputFiles)
94   {
95     args = inputFiles;
96
97     if (next >= args.length)
98       return false;
99
100     for (int i = next; i < args.length; i++)
101       {
102         try
103           {
104             if (verbose)
105               System.out.println("[Processing class " + args[i] + ".class]");
106             processClass(args[i].replace(File.separatorChar, '.'));
107           }
108         catch (IOException e)
109           {
110             errors.add(e);
111           }
112         catch (RMICException e)
113           {
114             errors.add(e);
115           }
116       }
117     if (errors.size() > 0)
118       {
119         for (Iterator it = errors.iterator(); it.hasNext(); )
120           {
121             Exception ex = (Exception) it.next();
122             logError(ex);
123           }
124       }
125
126     return errorCount == 0;
127   }
128
129   private void processClass(String cls) throws IOException, RMICException
130   {
131     // reset class specific vars
132     clazz = null;
133     classname = null;
134     classInternalName = null;
135     fullclassname = null;
136     remotemethods = null;
137     stubname = null;
138     skelname = null;
139     mRemoteInterfaces = new ArrayList();
140
141     analyzeClass(cls);
142     generateStub();
143     if (need11Stubs)
144       generateSkel();
145   }
146
147   private void analyzeClass(String cname)
148     throws RMICException
149   {
150     if (verbose)
151       System.out.println("[analyze class " + cname + "]");
152     int p = cname.lastIndexOf('.');
153     if (p != -1)
154       classname = cname.substring(p + 1);
155     else
156       classname = cname;
157     fullclassname = cname;
158
159     findClass();
160     findRemoteMethods();
161   }
162
163   /**
164    * @deprecated
165    */
166   public Exception getException()
167   {
168     return errors.size() == 0 ? null : (Exception) errors.get(0);
169   }
170
171   private void findClass()
172     throws RMICException
173   {
174     ClassLoader cl = (loader == null
175                       ? ClassLoader.getSystemClassLoader()
176                       : loader);
177     try
178       {
179         clazz = Class.forName(fullclassname, false, cl);
180       }
181     catch (ClassNotFoundException cnfe)
182       {
183         throw new RMICException
184           ("Class " + fullclassname + " not found in classpath", cnfe);
185       }
186
187     if (! Remote.class.isAssignableFrom(clazz))
188       {
189         throw new RMICException
190           ("Class " + clazz.getName()
191            + " does not implement a remote interface.");
192       }
193   }
194
195   private static Type[] typeArray(Class[] cls)
196   {
197     Type[] t = new Type[cls.length];
198     for (int i = 0; i < cls.length; i++)
199       {
200         t[i] = Type.getType(cls[i]);
201       }
202
203     return t;
204   }
205
206   private static String[] internalNameArray(Type[] t)
207   {
208     String[] s = new String[t.length];
209     for (int i = 0; i < t.length; i++)
210       {
211         s[i] = t[i].getInternalName();
212       }
213
214     return s;
215   }
216
217   private static String[] internalNameArray(Class[] c)
218   {
219     return internalNameArray(typeArray(c));
220   }
221
222   private static final String forName = "class$";
223
224   private static Object param(Method m, int argIndex)
225   {
226     List l = new ArrayList();
227     l.add(m);
228     l.add(new Integer(argIndex));
229     return l;
230   }
231
232   private static void generateClassForNamer(ClassVisitor cls)
233   {
234     MethodVisitor cv =
235       cls.visitMethod
236       (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC, forName,
237        Type.getMethodDescriptor
238        (Type.getType(Class.class), new Type[] { Type.getType(String.class) }),
239        null, null);
240
241     Label start = new Label();
242     cv.visitLabel(start);
243     cv.visitVarInsn(Opcodes.ALOAD, 0);
244     cv.visitMethodInsn
245       (Opcodes.INVOKESTATIC,
246        Type.getInternalName(Class.class),
247        "forName",
248        Type.getMethodDescriptor
249        (Type.getType(Class.class), new Type[] { Type.getType(String.class) }));
250     cv.visitInsn(Opcodes.ARETURN);
251
252     Label handler = new Label();
253     cv.visitLabel(handler);
254     cv.visitVarInsn(Opcodes.ASTORE, 1);
255     cv.visitTypeInsn(Opcodes.NEW, typeArg(NoClassDefFoundError.class));
256     cv.visitInsn(Opcodes.DUP);
257     cv.visitVarInsn(Opcodes.ALOAD, 1);
258     cv.visitMethodInsn
259       (Opcodes.INVOKEVIRTUAL,
260        Type.getInternalName(ClassNotFoundException.class),
261        "getMessage",
262        Type.getMethodDescriptor(Type.getType(String.class), new Type[] {}));
263     cv.visitMethodInsn
264       (Opcodes.INVOKESPECIAL,
265        Type.getInternalName(NoClassDefFoundError.class),
266        "<init>",
267        Type.getMethodDescriptor
268        (Type.VOID_TYPE, new Type[] { Type.getType(String.class) }));
269     cv.visitInsn(Opcodes.ATHROW);
270     cv.visitTryCatchBlock
271       (start, handler, handler,
272        Type.getInternalName(ClassNotFoundException.class));
273     cv.visitMaxs(-1, -1);
274   }
275
276   private void generateClassConstant(MethodVisitor cv, Class cls) {
277     if (cls.isPrimitive())
278       {
279         Class boxCls;
280         if (cls.equals(Boolean.TYPE))
281           boxCls = Boolean.class;
282         else if (cls.equals(Character.TYPE))
283           boxCls = Character.class;
284         else if (cls.equals(Byte.TYPE))
285           boxCls = Byte.class;
286         else if (cls.equals(Short.TYPE))
287           boxCls = Short.class;
288         else if (cls.equals(Integer.TYPE))
289           boxCls = Integer.class;
290         else if (cls.equals(Long.TYPE))
291           boxCls = Long.class;
292         else if (cls.equals(Float.TYPE))
293           boxCls = Float.class;
294         else if (cls.equals(Double.TYPE))
295           boxCls = Double.class;
296         else if (cls.equals(Void.TYPE))
297           boxCls = Void.class;
298         else
299           throw new IllegalArgumentException("unknown primitive type " + cls);
300
301         cv.visitFieldInsn
302           (Opcodes.GETSTATIC, Type.getInternalName(boxCls), "TYPE",
303            Type.getDescriptor(Class.class));
304         return;
305       }
306     cv.visitLdcInsn(cls.getName());
307     cv.visitMethodInsn
308       (Opcodes.INVOKESTATIC, classInternalName, forName,
309        Type.getMethodDescriptor
310        (Type.getType(Class.class),
311         new Type[] { Type.getType(String.class) }));
312   }
313
314   private void generateClassArray(MethodVisitor code, Class[] classes)
315   {
316     code.visitLdcInsn(new Integer(classes.length));
317     code.visitTypeInsn(Opcodes.ANEWARRAY, typeArg(Class.class));
318     for (int i = 0; i < classes.length; i++)
319       {
320         code.visitInsn(Opcodes.DUP);
321         code.visitLdcInsn(new Integer(i));
322         generateClassConstant(code, classes[i]);
323         code.visitInsn(Opcodes.AASTORE);
324       }
325   }
326
327   private void fillOperationArray(MethodVisitor clinit)
328   {
329     // Operations array
330     clinit.visitLdcInsn(new Integer(remotemethods.length));
331     clinit.visitTypeInsn(Opcodes.ANEWARRAY, typeArg(Operation.class));
332     clinit.visitFieldInsn
333       (Opcodes.PUTSTATIC, classInternalName, "operations",
334        Type.getDescriptor(Operation[].class));
335
336     for (int i = 0; i < remotemethods.length; i++)
337       {
338         Method m = remotemethods[i].meth;
339
340         StringBuffer desc = new StringBuffer();
341         desc.append(getPrettyName(m.getReturnType()) + " ");
342         desc.append(m.getName() + "(");
343
344         // signature
345         Class[] sig = m.getParameterTypes();
346         for (int j = 0; j < sig.length; j++)
347           {
348             desc.append(getPrettyName(sig[j]));
349             if (j + 1 < sig.length)
350                 desc.append(", ");
351           }
352
353         // push operations array
354         clinit.visitFieldInsn
355           (Opcodes.GETSTATIC, classInternalName, "operations",
356            Type.getDescriptor(Operation[].class));
357
358         // push array index
359         clinit.visitLdcInsn(new Integer(i));
360
361         // instantiate operation and leave a copy on the stack
362         clinit.visitTypeInsn(Opcodes.NEW, typeArg(Operation.class));
363         clinit.visitInsn(Opcodes.DUP);
364         clinit.visitLdcInsn(desc.toString());
365         clinit.visitMethodInsn
366           (Opcodes.INVOKESPECIAL,
367            Type.getInternalName(Operation.class),
368            "<init>",
369            Type.getMethodDescriptor
370            (Type.VOID_TYPE, new Type[] { Type.getType(String.class) }));
371
372         // store in operations array
373         clinit.visitInsn(Opcodes.AASTORE);
374       }
375   }
376
377   private void generateStaticMethodObjs(MethodVisitor clinit)
378   {
379     for (int i = 0; i < remotemethods.length; i++)
380       {
381         Method m = remotemethods[i].meth;
382
383         /*
384          * $method_<i>m.getName()</i>_<i>i</i> =
385          *   <i>m.getDeclaringClass()</i>.class.getMethod
386          *     (m.getName(), m.getParameterType())
387          */
388         String methodVar = "$method_" + m.getName() + "_" + i;
389         generateClassConstant(clinit, m.getDeclaringClass());
390         clinit.visitLdcInsn(m.getName());
391         generateClassArray(clinit, m.getParameterTypes());
392         clinit.visitMethodInsn
393           (Opcodes.INVOKEVIRTUAL,
394            Type.getInternalName(Class.class),
395            "getMethod",
396            Type.getMethodDescriptor
397            (Type.getType(Method.class),
398             new Type[] { Type.getType(String.class),
399                          Type.getType(Class[].class) }));
400
401         clinit.visitFieldInsn
402           (Opcodes.PUTSTATIC, classInternalName, methodVar,
403            Type.getDescriptor(Method.class));
404       }
405   }
406
407   private void generateStub()
408     throws IOException
409   {
410     stubname = fullclassname + "_Stub";
411     String stubclassname = classname + "_Stub";
412     File file = new File((destination == null ? "." : destination)
413                          + File.separator
414                          + stubname.replace('.', File.separatorChar)
415                          + ".class");
416
417     if (verbose)
418       System.out.println("[Generating class " + stubname + "]");
419
420     final ClassWriter stub = new ClassWriter(true);
421     classInternalName = stubname.replace('.', '/');
422     final String superInternalName =
423       Type.getType(RemoteStub.class).getInternalName();
424
425     String[] remoteInternalNames =
426       internalNameArray((Class[]) mRemoteInterfaces.toArray(new Class[] {}));
427     stub.visit
428       (Opcodes.V1_2, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, classInternalName,
429        null, superInternalName, remoteInternalNames);
430
431     if (need12Stubs)
432       {
433         stub.visitField
434           (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "serialVersionUID",
435            Type.LONG_TYPE.getDescriptor(), null, new Long(2L));
436       }
437
438     if (need11Stubs)
439       {
440         stub.visitField
441           (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL,
442            "interfaceHash", Type.LONG_TYPE.getDescriptor(), null,
443            new Long(RMIHashes.getInterfaceHash(clazz)));
444
445         if (need12Stubs)
446           {
447             stub.visitField
448               (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, "useNewInvoke",
449                Type.BOOLEAN_TYPE.getDescriptor(), null, null);
450           }
451
452         stub.visitField
453           (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL,
454            "operations", Type.getDescriptor(Operation[].class), null, null);
455       }
456
457     // Set of method references.
458     if (need12Stubs)
459       {
460         for (int i = 0; i < remotemethods.length; i++)
461           {
462             Method m = remotemethods[i].meth;
463             String slotName = "$method_" + m.getName() + "_" + i;
464             stub.visitField
465               (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, slotName,
466                Type.getDescriptor(Method.class), null, null);
467           }
468       }
469
470     MethodVisitor clinit = stub.visitMethod
471       (Opcodes.ACC_STATIC, "<clinit>",
472        Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null, null);
473
474     if (need11Stubs)
475       {
476         fillOperationArray(clinit);
477         if (! need12Stubs)
478           clinit.visitInsn(Opcodes.RETURN);
479       }
480
481     if (need12Stubs)
482       {
483         // begin of try
484         Label begin = new Label();
485
486         // beginning of catch
487         Label handler = new Label();
488         clinit.visitLabel(begin);
489
490         // Initialize the methods references.
491         if (need11Stubs)
492           {
493             /*
494              * RemoteRef.class.getMethod("invoke", new Class[] {
495              *   Remote.class, Method.class, Object[].class, long.class })
496              */
497             generateClassConstant(clinit, RemoteRef.class);
498             clinit.visitLdcInsn("invoke");
499             generateClassArray
500               (clinit, new Class[] { Remote.class, Method.class,
501                                      Object[].class, long.class });
502             clinit.visitMethodInsn
503               (Opcodes.INVOKEVIRTUAL,
504                Type.getInternalName(Class.class),
505                "getMethod",
506                Type.getMethodDescriptor
507                (Type.getType(Method.class),
508                 new Type[] { Type.getType(String.class),
509                              Type.getType(Class[].class) }));
510
511             // useNewInvoke = true
512             clinit.visitInsn(Opcodes.ICONST_1);
513             clinit.visitFieldInsn
514               (Opcodes.PUTSTATIC, classInternalName, "useNewInvoke",
515                Type.BOOLEAN_TYPE.getDescriptor());
516           }
517
518         generateStaticMethodObjs(clinit);
519
520         // jump past handler
521         clinit.visitInsn(Opcodes.RETURN);
522         clinit.visitLabel(handler);
523         if (need11Stubs)
524           {
525             // useNewInvoke = false
526             clinit.visitInsn(Opcodes.ICONST_0);
527             clinit.visitFieldInsn
528               (Opcodes.PUTSTATIC, classInternalName, "useNewInvoke",
529                Type.BOOLEAN_TYPE.getDescriptor());
530             clinit.visitInsn(Opcodes.RETURN);
531           }
532         else
533           {
534             // throw NoSuchMethodError
535             clinit.visitTypeInsn(Opcodes.NEW, typeArg(NoSuchMethodError.class));
536             clinit.visitInsn(Opcodes.DUP);
537             clinit.visitLdcInsn("stub class initialization failed");
538             clinit.visitMethodInsn
539               (Opcodes.INVOKESPECIAL,
540                Type.getInternalName(NoSuchMethodError.class),
541                "<init>",
542                Type.getMethodDescriptor
543                (Type.VOID_TYPE,
544                 new Type[] { Type.getType(String.class) }));
545             clinit.visitInsn(Opcodes.ATHROW);
546           }
547
548         clinit.visitTryCatchBlock
549           (begin, handler, handler,
550            Type.getInternalName(NoSuchMethodException.class));
551
552       }
553
554     clinit.visitMaxs(-1, -1);
555
556     generateClassForNamer(stub);
557
558     // Constructors
559     if (need11Stubs)
560       {
561         // no arg public constructor
562         MethodVisitor code = stub.visitMethod
563           (Opcodes.ACC_PUBLIC, "<init>",
564            Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}),
565            null, null);
566         code.visitVarInsn(Opcodes.ALOAD, 0);
567         code.visitMethodInsn
568           (Opcodes.INVOKESPECIAL, superInternalName, "<init>",
569            Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
570         code.visitInsn(Opcodes.RETURN);
571
572         code.visitMaxs(-1, -1);
573       }
574
575     // public RemoteRef constructor
576     MethodVisitor constructor = stub.visitMethod
577       (Opcodes.ACC_PUBLIC, "<init>",
578        Type.getMethodDescriptor
579        (Type.VOID_TYPE, new Type[] {Type.getType(RemoteRef.class)}),
580        null, null);
581     constructor.visitVarInsn(Opcodes.ALOAD, 0);
582     constructor.visitVarInsn(Opcodes.ALOAD, 1);
583     constructor.visitMethodInsn
584       (Opcodes.INVOKESPECIAL, superInternalName, "<init>",
585        Type.getMethodDescriptor
586        (Type.VOID_TYPE, new Type[] {Type.getType(RemoteRef.class)}));
587     constructor.visitInsn(Opcodes.RETURN);
588     constructor.visitMaxs(-1, -1);
589
590     // Method implementations
591     for (int i = 0; i < remotemethods.length; i++)
592       {
593         Method m = remotemethods[i].meth;
594         Class[] sig = m.getParameterTypes();
595         Class returntype = m.getReturnType();
596         Class[] except = sortExceptions
597           ((Class[]) remotemethods[i].exceptions.toArray(new Class[0]));
598
599         MethodVisitor code = stub.visitMethod
600           (Opcodes.ACC_PUBLIC,
601            m.getName(),
602            Type.getMethodDescriptor(Type.getType(returntype), typeArray(sig)),
603            null,
604            internalNameArray(typeArray(except)));
605
606         final Variables var = new Variables();
607
608         // this and parameters are the declared vars
609         var.declare("this");
610         for (int j = 0; j < sig.length; j++)
611           var.declare(param(m, j), size(sig[j]));
612
613         Label methodTryBegin = new Label();
614         code.visitLabel(methodTryBegin);
615
616         if (need12Stubs)
617           {
618             Label oldInvoke = new Label();
619             if (need11Stubs)
620               {
621                 // if not useNewInvoke jump to old invoke
622                 code.visitFieldInsn
623                   (Opcodes.GETSTATIC, classInternalName, "useNewInvoke",
624                    Type.getDescriptor(boolean.class));
625                 code.visitJumpInsn(Opcodes.IFEQ, oldInvoke);
626               }
627
628             // this.ref
629             code.visitVarInsn(Opcodes.ALOAD, var.get("this"));
630             code.visitFieldInsn
631               (Opcodes.GETFIELD, Type.getInternalName(RemoteObject.class),
632                "ref", Type.getDescriptor(RemoteRef.class));
633
634             // "this" is first arg to invoke
635             code.visitVarInsn(Opcodes.ALOAD, var.get("this"));
636
637             // method object is second arg to invoke
638             String methName = "$method_" + m.getName() + "_" + i;
639             code.visitFieldInsn
640               (Opcodes.GETSTATIC, classInternalName, methName,
641                Type.getDescriptor(Method.class));
642
643             // args to remote method are third arg to invoke
644             if (sig.length == 0)
645               code.visitInsn(Opcodes.ACONST_NULL);
646             else
647               {
648                 // create arg Object[] (with boxed primitives) and push it
649                 code.visitLdcInsn(new Integer(sig.length));
650                 code.visitTypeInsn(Opcodes.ANEWARRAY, typeArg(Object.class));
651
652                 var.allocate("argArray");
653                 code.visitVarInsn(Opcodes.ASTORE, var.get("argArray"));
654
655                 for (int j = 0; j < sig.length; j++)
656                   {
657                     int size = size(sig[j]);
658                     int insn = loadOpcode(sig[j]);
659                     Class box = sig[j].isPrimitive() ? box(sig[j]) : null;
660
661                     code.visitVarInsn(Opcodes.ALOAD, var.get("argArray"));
662                     code.visitLdcInsn(new Integer(j));
663
664                     // put argument on stack
665                     if (box != null)
666                       {
667                         code.visitTypeInsn(Opcodes.NEW, typeArg(box));
668                         code.visitInsn(Opcodes.DUP);
669                         code.visitVarInsn(insn, var.get(param(m, j)));
670                         code.visitMethodInsn
671                           (Opcodes.INVOKESPECIAL,
672                            Type.getInternalName(box),
673                            "<init>",
674                            Type.getMethodDescriptor
675                            (Type.VOID_TYPE,
676                             new Type[] { Type.getType(sig[j]) }));
677                       }
678                     else
679                       code.visitVarInsn(insn, var.get(param(m, j)));
680
681                     code.visitInsn(Opcodes.AASTORE);
682                   }
683
684                 code.visitVarInsn(Opcodes.ALOAD, var.deallocate("argArray"));
685               }
686
687             // push remote operation opcode
688             code.visitLdcInsn(new Long(remotemethods[i].hash));
689             code.visitMethodInsn
690               (Opcodes.INVOKEINTERFACE,
691                Type.getInternalName(RemoteRef.class),
692                "invoke",
693                Type.getMethodDescriptor
694                (Type.getType(Object.class),
695                 new Type[] { Type.getType(Remote.class),
696                              Type.getType(Method.class),
697                              Type.getType(Object[].class),
698                              Type.LONG_TYPE }));
699
700             if (! returntype.equals(Void.TYPE))
701               {
702                 int retcode = returnOpcode(returntype);
703                 Class boxCls =
704                   returntype.isPrimitive() ? box(returntype) : null;
705                 code.visitTypeInsn
706                   (Opcodes.CHECKCAST, typeArg(boxCls == null ? returntype : boxCls));
707                 if (returntype.isPrimitive())
708                   {
709                     // unbox
710                     code.visitMethodInsn
711                       (Opcodes.INVOKEVIRTUAL,
712                        Type.getType(boxCls).getInternalName(),
713                        unboxMethod(returntype),
714                        Type.getMethodDescriptor
715                        (Type.getType(returntype), new Type[] {}));
716                   }
717
718                 code.visitInsn(retcode);
719               }
720             else
721               code.visitInsn(Opcodes.RETURN);
722
723
724             if (need11Stubs)
725               code.visitLabel(oldInvoke);
726           }
727
728         if (need11Stubs)
729           {
730
731             // this.ref.newCall(this, operations, index, interfaceHash)
732             code.visitVarInsn(Opcodes.ALOAD, var.get("this"));
733             code.visitFieldInsn
734               (Opcodes.GETFIELD,
735                Type.getInternalName(RemoteObject.class),
736                "ref",
737                Type.getDescriptor(RemoteRef.class));
738
739             // "this" is first arg to newCall
740             code.visitVarInsn(Opcodes.ALOAD, var.get("this"));
741
742             // operations is second arg to newCall
743             code.visitFieldInsn
744               (Opcodes.GETSTATIC, classInternalName, "operations",
745                Type.getDescriptor(Operation[].class));
746
747             // method index is third arg
748             code.visitLdcInsn(new Integer(i));
749
750             // interface hash is fourth arg
751             code.visitFieldInsn
752               (Opcodes.GETSTATIC, classInternalName, "interfaceHash",
753                Type.LONG_TYPE.getDescriptor());
754
755             code.visitMethodInsn
756               (Opcodes.INVOKEINTERFACE,
757                Type.getInternalName(RemoteRef.class),
758                "newCall",
759                Type.getMethodDescriptor
760                (Type.getType(RemoteCall.class),
761                 new Type[] { Type.getType(RemoteObject.class),
762                              Type.getType(Operation[].class),
763                              Type.INT_TYPE,
764                              Type.LONG_TYPE }));
765
766             // store call object on stack and leave copy on stack
767             var.allocate("call");
768             code.visitInsn(Opcodes.DUP);
769             code.visitVarInsn(Opcodes.ASTORE, var.get("call"));
770
771             Label beginArgumentTryBlock = new Label();
772             code.visitLabel(beginArgumentTryBlock);
773
774             // ObjectOutput out = call.getOutputStream();
775             code.visitMethodInsn
776               (Opcodes.INVOKEINTERFACE,
777                Type.getInternalName(RemoteCall.class),
778                "getOutputStream",
779                Type.getMethodDescriptor
780                (Type.getType(ObjectOutput.class), new Type[] {}));
781
782             for (int j = 0; j < sig.length; j++)
783               {
784                 // dup the ObjectOutput
785                 code.visitInsn(Opcodes.DUP);
786
787                 // get j'th arg to remote method
788                 code.visitVarInsn(loadOpcode(sig[j]), var.get(param(m, j)));
789
790                 Class argCls =
791                   sig[j].isPrimitive() ? sig[j] : Object.class;
792
793                 // out.writeFoo
794                 code.visitMethodInsn
795                   (Opcodes.INVOKEINTERFACE,
796                    Type.getInternalName(ObjectOutput.class),
797                    writeMethod(sig[j]),
798                    Type.getMethodDescriptor
799                    (Type.VOID_TYPE,
800                     new Type[] { Type.getType(argCls) }));
801               }
802
803             // pop ObjectOutput
804             code.visitInsn(Opcodes.POP);
805
806             Label iohandler = new Label();
807             Label endArgumentTryBlock = new Label();
808             code.visitJumpInsn(Opcodes.GOTO, endArgumentTryBlock);
809             code.visitLabel(iohandler);
810
811             // throw new MarshalException(msg, ioexception);
812             code.visitVarInsn(Opcodes.ASTORE, var.allocate("exception"));
813             code.visitTypeInsn(Opcodes.NEW, typeArg(MarshalException.class));
814             code.visitInsn(Opcodes.DUP);
815             code.visitLdcInsn("error marshalling arguments");
816             code.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception"));
817             code.visitMethodInsn
818               (Opcodes.INVOKESPECIAL,
819                Type.getInternalName(MarshalException.class),
820                "<init>",
821                Type.getMethodDescriptor
822                (Type.VOID_TYPE,
823                 new Type[] { Type.getType(String.class),
824                              Type.getType(Exception.class) }));
825             code.visitInsn(Opcodes.ATHROW);
826
827             code.visitLabel(endArgumentTryBlock);
828             code.visitTryCatchBlock
829               (beginArgumentTryBlock, iohandler, iohandler,
830                Type.getInternalName(IOException.class));
831
832             // this.ref.invoke(call)
833             code.visitVarInsn(Opcodes.ALOAD, var.get("this"));
834             code.visitFieldInsn
835               (Opcodes.GETFIELD, Type.getInternalName(RemoteObject.class),
836                "ref", Type.getDescriptor(RemoteRef.class));
837             code.visitVarInsn(Opcodes.ALOAD, var.get("call"));
838             code.visitMethodInsn
839               (Opcodes.INVOKEINTERFACE,
840                Type.getInternalName(RemoteRef.class),
841                "invoke",
842                Type.getMethodDescriptor
843                (Type.VOID_TYPE,
844                 new Type[] { Type.getType(RemoteCall.class) }));
845
846             // handle return value
847             boolean needcastcheck = false;
848
849             Label beginReturnTryCatch = new Label();
850             code.visitLabel(beginReturnTryCatch);
851
852             int returncode = returnOpcode(returntype);
853
854             if (! returntype.equals(Void.TYPE))
855               {
856                 // call.getInputStream()
857                 code.visitVarInsn(Opcodes.ALOAD, var.get("call"));
858                 code.visitMethodInsn
859                   (Opcodes.INVOKEINTERFACE,
860                    Type.getInternalName(RemoteCall.class),
861                    "getInputStream",
862                    Type.getMethodDescriptor
863                    (Type.getType(ObjectInput.class), new Type[] {}));
864
865                 Class readCls =
866                   returntype.isPrimitive() ? returntype : Object.class;
867                 code.visitMethodInsn
868                   (Opcodes.INVOKEINTERFACE,
869                    Type.getInternalName(ObjectInput.class),
870                    readMethod(returntype),
871                    Type.getMethodDescriptor
872                    (Type.getType(readCls), new Type[] {}));
873
874                 boolean castresult = false;
875
876                 if (! returntype.isPrimitive())
877                   {
878                     if (! returntype.equals(Object.class))
879                       castresult = true;
880                     else
881                       needcastcheck = true;
882                   }
883
884                 if (castresult)
885                   code.visitTypeInsn(Opcodes.CHECKCAST, typeArg(returntype));
886
887                 // leave result on stack for return
888               }
889
890             // this.ref.done(call)
891             code.visitVarInsn(Opcodes.ALOAD, var.get("this"));
892             code.visitFieldInsn
893               (Opcodes.GETFIELD,
894                Type.getInternalName(RemoteObject.class),
895                "ref",
896                Type.getDescriptor(RemoteRef.class));
897             code.visitVarInsn(Opcodes.ALOAD, var.deallocate("call"));
898             code.visitMethodInsn
899               (Opcodes.INVOKEINTERFACE,
900                Type.getInternalName(RemoteRef.class),
901                "done",
902                Type.getMethodDescriptor
903                (Type.VOID_TYPE,
904                 new Type[] { Type.getType(RemoteCall.class) }));
905
906             // return; or return result;
907             code.visitInsn(returncode);
908
909             // exception handler
910             Label handler = new Label();
911             code.visitLabel(handler);
912             code.visitVarInsn(Opcodes.ASTORE, var.allocate("exception"));
913
914             // throw new UnmarshalException(msg, e)
915             code.visitTypeInsn(Opcodes.NEW, typeArg(UnmarshalException.class));
916             code.visitInsn(Opcodes.DUP);
917             code.visitLdcInsn("error unmarshalling return");
918             code.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception"));
919             code.visitMethodInsn
920               (Opcodes.INVOKESPECIAL,
921                Type.getInternalName(UnmarshalException.class),
922                "<init>",
923                Type.getMethodDescriptor
924                (Type.VOID_TYPE,
925                 new Type[] { Type.getType(String.class),
926                              Type.getType(Exception.class) }));
927             code.visitInsn(Opcodes.ATHROW);
928
929             Label endReturnTryCatch = new Label();
930
931             // catch IOException
932             code.visitTryCatchBlock
933               (beginReturnTryCatch, handler, handler,
934                Type.getInternalName(IOException.class));
935
936             if (needcastcheck)
937               {
938                 // catch ClassNotFoundException
939                 code.visitTryCatchBlock
940                   (beginReturnTryCatch, handler, handler,
941                    Type.getInternalName(ClassNotFoundException.class));
942               }
943           }
944
945         Label rethrowHandler = new Label();
946         code.visitLabel(rethrowHandler);
947         // rethrow declared exceptions
948         code.visitInsn(Opcodes.ATHROW);
949
950         boolean needgeneral = true;
951         for (int j = 0; j < except.length; j++)
952           {
953             if (except[j] == Exception.class)
954               needgeneral = false;
955           }
956
957         for (int j = 0; j < except.length; j++)
958           {
959             code.visitTryCatchBlock
960               (methodTryBegin, rethrowHandler, rethrowHandler,
961                Type.getInternalName(except[j]));
962           }
963
964         if (needgeneral)
965           {
966             // rethrow unchecked exceptions
967             code.visitTryCatchBlock
968               (methodTryBegin, rethrowHandler, rethrowHandler,
969                Type.getInternalName(RuntimeException.class));
970
971             Label generalHandler = new Label();
972             code.visitLabel(generalHandler);
973             String msg = "undeclared checked exception";
974
975             // throw new java.rmi.UnexpectedException(msg, e)
976             code.visitVarInsn(Opcodes.ASTORE, var.allocate("exception"));
977             code.visitTypeInsn(Opcodes.NEW, typeArg(UnexpectedException.class));
978             code.visitInsn(Opcodes.DUP);
979             code.visitLdcInsn(msg);
980             code.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception"));
981             code.visitMethodInsn
982               (Opcodes.INVOKESPECIAL,
983                Type.getInternalName(UnexpectedException.class),
984                "<init>",
985                Type.getMethodDescriptor
986                (Type.VOID_TYPE,
987                 new Type [] { Type.getType(String.class),
988                               Type.getType(Exception.class) }));
989             code.visitInsn(Opcodes.ATHROW);
990
991             code.visitTryCatchBlock
992               (methodTryBegin, rethrowHandler, generalHandler,
993                Type.getInternalName(Exception.class));
994           }
995
996         code.visitMaxs(-1, -1);
997       }
998
999     stub.visitEnd();
1000     byte[] classData = stub.toByteArray();
1001     if (!noWrite)
1002       {
1003         if (file.exists())
1004           file.delete();
1005         if (file.getParentFile() != null)
1006           file.getParentFile().mkdirs();
1007         FileOutputStream fos = new FileOutputStream(file);
1008         fos.write(classData);
1009         fos.flush();
1010         fos.close();
1011       }
1012   }
1013
1014   private void generateSkel() throws IOException
1015   {
1016     skelname = fullclassname + "_Skel";
1017     String skelclassname = classname + "_Skel";
1018     File file = new File(destination == null ? "" : destination
1019                          + File.separator
1020                          + skelname.replace('.', File.separatorChar)
1021                          + ".class");
1022     if (verbose)
1023       System.out.println("[Generating class " + skelname + "]");
1024
1025     final ClassWriter skel = new ClassWriter(true);
1026     classInternalName = skelname.replace('.', '/');
1027     skel.visit
1028       (Opcodes.V1_1, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL,
1029        classInternalName, Type.getInternalName(Object.class), null,
1030        new String[] { Type.getType(Skeleton.class).getInternalName() });
1031
1032     skel.visitField
1033       (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "interfaceHash",
1034        Type.LONG_TYPE.getDescriptor(), null,
1035        new Long(RMIHashes.getInterfaceHash(clazz)));
1036
1037     skel.visitField
1038       (Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "operations",
1039        Type.getDescriptor(Operation[].class), null, null);
1040
1041     MethodVisitor clinit = skel.visitMethod
1042       (Opcodes.ACC_STATIC, "<clinit>",
1043        Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null, null);
1044
1045     fillOperationArray(clinit);
1046     clinit.visitInsn(Opcodes.RETURN);
1047
1048     clinit.visitMaxs(-1, -1);
1049
1050     // no arg public constructor
1051     MethodVisitor init = skel.visitMethod
1052       (Opcodes.ACC_PUBLIC, "<init>",
1053        Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null, null);
1054     init.visitVarInsn(Opcodes.ALOAD, 0);
1055     init.visitMethodInsn
1056       (Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>",
1057        Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
1058     init.visitInsn(Opcodes.RETURN);
1059     init.visitMaxs(-1, -1);
1060
1061     /*
1062      * public Operation[] getOperations()
1063      * returns a clone of the operations array
1064      */
1065     MethodVisitor getOp = skel.visitMethod
1066       (Opcodes.ACC_PUBLIC, "getOperations",
1067        Type.getMethodDescriptor
1068        (Type.getType(Operation[].class), new Type[] {}),
1069        null, null);
1070     getOp.visitFieldInsn
1071       (Opcodes.GETSTATIC, classInternalName, "operations",
1072        Type.getDescriptor(Operation[].class));
1073     getOp.visitMethodInsn
1074       (Opcodes.INVOKEVIRTUAL, Type.getInternalName(Object.class),
1075        "clone", Type.getMethodDescriptor(Type.getType(Object.class),
1076                                          new Type[] {}));
1077     getOp.visitTypeInsn(Opcodes.CHECKCAST, typeArg(Operation[].class));
1078     getOp.visitInsn(Opcodes.ARETURN);
1079     getOp.visitMaxs(-1, -1);
1080
1081     // public void dispatch(Remote, RemoteCall, int opnum, long hash)
1082     MethodVisitor dispatch = skel.visitMethod
1083       (Opcodes.ACC_PUBLIC,
1084        "dispatch",
1085        Type.getMethodDescriptor
1086        (Type.VOID_TYPE,
1087         new Type[] { Type.getType(Remote.class),
1088                      Type.getType(RemoteCall.class),
1089                      Type.INT_TYPE, Type.LONG_TYPE }), null,
1090        new String[] { Type.getInternalName(Exception.class) });
1091
1092     Variables var = new Variables();
1093     var.declare("this");
1094     var.declare("remoteobj");
1095     var.declare("remotecall");
1096     var.declare("opnum");
1097     var.declareWide("hash");
1098
1099     /*
1100      * if opnum >= 0
1101      * XXX it is unclear why there is handling of negative opnums
1102      */
1103     dispatch.visitVarInsn(Opcodes.ILOAD, var.get("opnum"));
1104     Label nonNegativeOpnum = new Label();
1105     Label opnumSet = new Label();
1106     dispatch.visitJumpInsn(Opcodes.IFGE, nonNegativeOpnum);
1107
1108     for (int i = 0; i < remotemethods.length; i++)
1109       {
1110         // assign opnum if hash matches supplied hash
1111         dispatch.visitVarInsn(Opcodes.LLOAD, var.get("hash"));
1112         dispatch.visitLdcInsn(new Long(remotemethods[i].hash));
1113         Label notIt = new Label();
1114         dispatch.visitInsn(Opcodes.LCMP);
1115         dispatch.visitJumpInsn(Opcodes.IFNE, notIt);
1116
1117         // opnum = <opnum>
1118         dispatch.visitLdcInsn(new Integer(i));
1119         dispatch.visitVarInsn(Opcodes.ISTORE, var.get("opnum"));
1120         dispatch.visitJumpInsn(Opcodes.GOTO, opnumSet);
1121         dispatch.visitLabel(notIt);
1122       }
1123
1124     // throw new SkeletonMismatchException
1125     Label mismatch = new Label();
1126     dispatch.visitJumpInsn(Opcodes.GOTO, mismatch);
1127
1128     dispatch.visitLabel(nonNegativeOpnum);
1129
1130     // if opnum is already set, check that the hash matches the interface
1131     dispatch.visitVarInsn(Opcodes.LLOAD, var.get("hash"));
1132     dispatch.visitFieldInsn
1133       (Opcodes.GETSTATIC, classInternalName,
1134        "interfaceHash", Type.LONG_TYPE.getDescriptor());
1135     dispatch.visitInsn(Opcodes.LCMP);
1136     dispatch.visitJumpInsn(Opcodes.IFEQ, opnumSet);
1137
1138     dispatch.visitLabel(mismatch);
1139     dispatch.visitTypeInsn
1140       (Opcodes.NEW, typeArg(SkeletonMismatchException.class));
1141     dispatch.visitInsn(Opcodes.DUP);
1142     dispatch.visitLdcInsn("interface hash mismatch");
1143     dispatch.visitMethodInsn
1144       (Opcodes.INVOKESPECIAL,
1145        Type.getInternalName(SkeletonMismatchException.class),
1146        "<init>",
1147        Type.getMethodDescriptor
1148        (Type.VOID_TYPE, new Type[] { Type.getType(String.class) }));
1149     dispatch.visitInsn(Opcodes.ATHROW);
1150
1151     // opnum has been set
1152     dispatch.visitLabel(opnumSet);
1153
1154     dispatch.visitVarInsn(Opcodes.ALOAD, var.get("remoteobj"));
1155     dispatch.visitTypeInsn(Opcodes.CHECKCAST, typeArg(clazz));
1156     dispatch.visitVarInsn(Opcodes.ASTORE, var.get("remoteobj"));
1157
1158     Label deflt = new Label();
1159     Label[] methLabels = new Label[remotemethods.length];
1160     for (int i = 0; i < methLabels.length; i++)
1161       methLabels[i] = new Label();
1162
1163     // switch on opnum
1164     dispatch.visitVarInsn(Opcodes.ILOAD, var.get("opnum"));
1165     dispatch.visitTableSwitchInsn
1166       (0, remotemethods.length - 1, deflt, methLabels);
1167
1168     // Method dispatch
1169     for (int i = 0; i < remotemethods.length; i++)
1170       {
1171         dispatch.visitLabel(methLabels[i]);
1172         Method m = remotemethods[i].meth;
1173         generateMethodSkel(dispatch, m, var);
1174       }
1175
1176     dispatch.visitLabel(deflt);
1177     dispatch.visitTypeInsn(Opcodes.NEW, typeArg(UnmarshalException.class));
1178     dispatch.visitInsn(Opcodes.DUP);
1179     dispatch.visitLdcInsn("invalid method number");
1180     dispatch.visitMethodInsn
1181       (Opcodes.INVOKESPECIAL,
1182        Type.getInternalName(UnmarshalException.class),
1183        "<init>",
1184        Type.getMethodDescriptor
1185        (Type.VOID_TYPE, new Type[] { Type.getType(String.class) }));
1186     dispatch.visitInsn(Opcodes.ATHROW);
1187
1188     dispatch.visitMaxs(-1, -1);
1189
1190     skel.visitEnd();
1191     byte[] classData = skel.toByteArray();
1192     if (!noWrite)
1193       {
1194         if (file.exists())
1195           file.delete();
1196         if (file.getParentFile() != null)
1197           file.getParentFile().mkdirs();
1198         FileOutputStream fos = new FileOutputStream(file);
1199         fos.write(classData);
1200         fos.flush();
1201         fos.close();
1202       }
1203   }
1204
1205   private void generateMethodSkel(MethodVisitor cv, Method m, Variables var)
1206   {
1207     Class[] sig = m.getParameterTypes();
1208
1209     Label readArgs = new Label();
1210     cv.visitLabel(readArgs);
1211
1212     boolean needcastcheck = false;
1213
1214     // ObjectInput in = call.getInputStream();
1215     cv.visitVarInsn(Opcodes.ALOAD, var.get("remotecall"));
1216     cv.visitMethodInsn
1217       (Opcodes.INVOKEINTERFACE,
1218        Type.getInternalName(RemoteCall.class), "getInputStream",
1219        Type.getMethodDescriptor
1220        (Type.getType(ObjectInput.class), new Type[] {}));
1221     cv.visitVarInsn(Opcodes.ASTORE, var.allocate("objectinput"));
1222
1223     for (int i = 0; i < sig.length; i++)
1224       {
1225         // dup input stream
1226         cv.visitVarInsn(Opcodes.ALOAD, var.get("objectinput"));
1227
1228         Class readCls = sig[i].isPrimitive() ? sig[i] : Object.class;
1229
1230         // in.readFoo()
1231         cv.visitMethodInsn
1232           (Opcodes.INVOKEINTERFACE,
1233            Type.getInternalName(ObjectInput.class),
1234            readMethod(sig[i]),
1235            Type.getMethodDescriptor
1236            (Type.getType(readCls), new Type [] {}));
1237
1238         if (! sig[i].isPrimitive() && ! sig[i].equals(Object.class))
1239           {
1240             needcastcheck = true;
1241             cv.visitTypeInsn(Opcodes.CHECKCAST, typeArg(sig[i]));
1242           }
1243
1244         // store arg in variable
1245         cv.visitVarInsn
1246           (storeOpcode(sig[i]), var.allocate(param(m, i), size(sig[i])));
1247       }
1248
1249     var.deallocate("objectinput");
1250
1251     Label doCall = new Label();
1252     Label closeInput = new Label();
1253
1254     cv.visitJumpInsn(Opcodes.JSR, closeInput);
1255     cv.visitJumpInsn(Opcodes.GOTO, doCall);
1256
1257     // throw new UnmarshalException
1258     Label handler = new Label();
1259     cv.visitLabel(handler);
1260     cv.visitVarInsn(Opcodes.ASTORE, var.allocate("exception"));
1261     cv.visitTypeInsn(Opcodes.NEW, typeArg(UnmarshalException.class));
1262     cv.visitInsn(Opcodes.DUP);
1263     cv.visitLdcInsn("error unmarshalling arguments");
1264     cv.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception"));
1265     cv.visitMethodInsn
1266       (Opcodes.INVOKESPECIAL,
1267        Type.getInternalName(UnmarshalException.class),
1268        "<init>",
1269        Type.getMethodDescriptor
1270        (Type.VOID_TYPE, new Type[] { Type.getType(String.class),
1271                                      Type.getType(Exception.class) }));
1272     cv.visitVarInsn(Opcodes.ASTORE, var.allocate("toThrow"));
1273     cv.visitJumpInsn(Opcodes.JSR, closeInput);
1274     cv.visitVarInsn(Opcodes.ALOAD, var.get("toThrow"));
1275     cv.visitInsn(Opcodes.ATHROW);
1276
1277     cv.visitTryCatchBlock
1278       (readArgs, handler, handler, Type.getInternalName(IOException.class));
1279     if (needcastcheck)
1280       {
1281         cv.visitTryCatchBlock
1282           (readArgs, handler, handler,
1283            Type.getInternalName(ClassCastException.class));
1284       }
1285
1286     // finally block
1287     cv.visitLabel(closeInput);
1288     cv.visitVarInsn(Opcodes.ASTORE, var.allocate("retAddress"));
1289     cv.visitVarInsn(Opcodes.ALOAD, var.get("remotecall"));
1290     cv.visitMethodInsn
1291       (Opcodes.INVOKEINTERFACE,
1292        Type.getInternalName(RemoteCall.class),
1293        "releaseInputStream",
1294        Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
1295     cv.visitVarInsn(Opcodes.RET, var.deallocate("retAddress"));
1296     var.deallocate("toThrow");
1297
1298     // do the call using args stored as variables
1299     cv.visitLabel(doCall);
1300     cv.visitVarInsn(Opcodes.ALOAD, var.get("remoteobj"));
1301     for (int i = 0; i < sig.length; i++)
1302       cv.visitVarInsn(loadOpcode(sig[i]), var.deallocate(param(m, i)));
1303     cv.visitMethodInsn
1304       (Opcodes.INVOKEVIRTUAL, Type.getInternalName(clazz), m.getName(),
1305        Type.getMethodDescriptor(m));
1306
1307     Class returntype = m.getReturnType();
1308     if (! returntype.equals(Void.TYPE))
1309       {
1310         cv.visitVarInsn
1311           (storeOpcode(returntype), var.allocate("result", size(returntype)));
1312       }
1313
1314     // write result to result stream
1315     Label writeResult = new Label();
1316     cv.visitLabel(writeResult);
1317     cv.visitVarInsn(Opcodes.ALOAD, var.get("remotecall"));
1318     cv.visitInsn(Opcodes.ICONST_1);
1319     cv.visitMethodInsn
1320       (Opcodes.INVOKEINTERFACE,
1321        Type.getInternalName(RemoteCall.class),
1322        "getResultStream",
1323        Type.getMethodDescriptor
1324        (Type.getType(ObjectOutput.class),
1325         new Type[] { Type.BOOLEAN_TYPE }));
1326
1327     if (! returntype.equals(Void.TYPE))
1328       {
1329         // out.writeFoo(result)
1330         cv.visitVarInsn(loadOpcode(returntype), var.deallocate("result"));
1331         Class writeCls = returntype.isPrimitive() ? returntype : Object.class;
1332         cv.visitMethodInsn
1333           (Opcodes.INVOKEINTERFACE,
1334            Type.getInternalName(ObjectOutput.class),
1335            writeMethod(returntype),
1336            Type.getMethodDescriptor
1337            (Type.VOID_TYPE, new Type[] { Type.getType(writeCls) }));
1338       }
1339
1340     cv.visitInsn(Opcodes.RETURN);
1341
1342     // throw new MarshalException
1343     Label marshalHandler = new Label();
1344     cv.visitLabel(marshalHandler);
1345     cv.visitVarInsn(Opcodes.ASTORE, var.allocate("exception"));
1346     cv.visitTypeInsn(Opcodes.NEW, typeArg(MarshalException.class));
1347     cv.visitInsn(Opcodes.DUP);
1348     cv.visitLdcInsn("error marshalling return");
1349     cv.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception"));
1350     cv.visitMethodInsn
1351       (Opcodes.INVOKESPECIAL,
1352        Type.getInternalName(MarshalException.class),
1353        "<init>",
1354        Type.getMethodDescriptor
1355        (Type.VOID_TYPE, new Type[] { Type.getType(String.class),
1356                                      Type.getType(Exception.class) }));
1357     cv.visitInsn(Opcodes.ATHROW);
1358     cv.visitTryCatchBlock
1359       (writeResult, marshalHandler, marshalHandler,
1360        Type.getInternalName(IOException.class));
1361   }
1362
1363   private static String typeArg(Class cls)
1364   {
1365     if (cls.isArray())
1366       return Type.getDescriptor(cls);
1367
1368     return Type.getInternalName(cls);
1369   }
1370
1371   private static String readMethod(Class cls)
1372   {
1373     if (cls.equals(Void.TYPE))
1374       throw new IllegalArgumentException("can not read void");
1375
1376     String method;
1377     if (cls.equals(Boolean.TYPE))
1378       method = "readBoolean";
1379     else if (cls.equals(Byte.TYPE))
1380       method = "readByte";
1381     else if (cls.equals(Character.TYPE))
1382       method = "readChar";
1383     else if (cls.equals(Short.TYPE))
1384       method = "readShort";
1385     else if (cls.equals(Integer.TYPE))
1386       method = "readInt";
1387     else if (cls.equals(Long.TYPE))
1388       method = "readLong";
1389     else if (cls.equals(Float.TYPE))
1390       method = "readFloat";
1391     else if (cls.equals(Double.TYPE))
1392       method = "readDouble";
1393     else
1394       method = "readObject";
1395
1396     return method;
1397   }
1398
1399   private static String writeMethod(Class cls)
1400   {
1401     if (cls.equals(Void.TYPE))
1402       throw new IllegalArgumentException("can not read void");
1403
1404     String method;
1405     if (cls.equals(Boolean.TYPE))
1406       method = "writeBoolean";
1407     else if (cls.equals(Byte.TYPE))
1408       method = "writeByte";
1409     else if (cls.equals(Character.TYPE))
1410       method = "writeChar";
1411     else if (cls.equals(Short.TYPE))
1412       method = "writeShort";
1413     else if (cls.equals(Integer.TYPE))
1414       method = "writeInt";
1415     else if (cls.equals(Long.TYPE))
1416       method = "writeLong";
1417     else if (cls.equals(Float.TYPE))
1418       method = "writeFloat";
1419     else if (cls.equals(Double.TYPE))
1420       method = "writeDouble";
1421     else
1422       method = "writeObject";
1423
1424     return method;
1425   }
1426
1427   private static int returnOpcode(Class cls)
1428   {
1429     int returncode;
1430     if (cls.equals(Boolean.TYPE))
1431       returncode = Opcodes.IRETURN;
1432     else if (cls.equals(Byte.TYPE))
1433       returncode = Opcodes.IRETURN;
1434     else if (cls.equals(Character.TYPE))
1435       returncode = Opcodes.IRETURN;
1436     else if (cls.equals(Short.TYPE))
1437       returncode = Opcodes.IRETURN;
1438     else if (cls.equals(Integer.TYPE))
1439       returncode = Opcodes.IRETURN;
1440     else if (cls.equals(Long.TYPE))
1441       returncode = Opcodes.LRETURN;
1442     else if (cls.equals(Float.TYPE))
1443       returncode = Opcodes.FRETURN;
1444     else if (cls.equals(Double.TYPE))
1445       returncode = Opcodes.DRETURN;
1446     else if (cls.equals(Void.TYPE))
1447       returncode = Opcodes.RETURN;
1448     else
1449       returncode = Opcodes.ARETURN;
1450
1451     return returncode;
1452   }
1453
1454   private static int loadOpcode(Class cls)
1455   {
1456     if (cls.equals(Void.TYPE))
1457       throw new IllegalArgumentException("can not load void");
1458
1459     int loadcode;
1460     if (cls.equals(Boolean.TYPE))
1461       loadcode = Opcodes.ILOAD;
1462     else if (cls.equals(Byte.TYPE))
1463       loadcode = Opcodes.ILOAD;
1464     else if (cls.equals(Character.TYPE))
1465       loadcode = Opcodes.ILOAD;
1466     else if (cls.equals(Short.TYPE))
1467       loadcode = Opcodes.ILOAD;
1468     else if (cls.equals(Integer.TYPE))
1469       loadcode = Opcodes.ILOAD;
1470     else if (cls.equals(Long.TYPE))
1471       loadcode = Opcodes.LLOAD;
1472     else if (cls.equals(Float.TYPE))
1473       loadcode = Opcodes.FLOAD;
1474     else if (cls.equals(Double.TYPE))
1475       loadcode = Opcodes.DLOAD;
1476     else
1477       loadcode = Opcodes.ALOAD;
1478
1479     return loadcode;
1480   }
1481
1482   private static int storeOpcode(Class cls)
1483   {
1484     if (cls.equals(Void.TYPE))
1485       throw new IllegalArgumentException("can not load void");
1486
1487     int storecode;
1488     if (cls.equals(Boolean.TYPE))
1489       storecode = Opcodes.ISTORE;
1490     else if (cls.equals(Byte.TYPE))
1491       storecode = Opcodes.ISTORE;
1492     else if (cls.equals(Character.TYPE))
1493       storecode = Opcodes.ISTORE;
1494     else if (cls.equals(Short.TYPE))
1495       storecode = Opcodes.ISTORE;
1496     else if (cls.equals(Integer.TYPE))
1497       storecode = Opcodes.ISTORE;
1498     else if (cls.equals(Long.TYPE))
1499       storecode = Opcodes.LSTORE;
1500     else if (cls.equals(Float.TYPE))
1501       storecode = Opcodes.FSTORE;
1502     else if (cls.equals(Double.TYPE))
1503       storecode = Opcodes.DSTORE;
1504     else
1505       storecode = Opcodes.ASTORE;
1506
1507     return storecode;
1508   }
1509
1510   private static String unboxMethod(Class primitive)
1511   {
1512     if (! primitive.isPrimitive())
1513       throw new IllegalArgumentException("can not unbox nonprimitive");
1514
1515     String method;
1516     if (primitive.equals(Boolean.TYPE))
1517       method = "booleanValue";
1518     else if (primitive.equals(Byte.TYPE))
1519       method = "byteValue";
1520     else if (primitive.equals(Character.TYPE))
1521       method = "charValue";
1522     else if (primitive.equals(Short.TYPE))
1523       method = "shortValue";
1524     else if (primitive.equals(Integer.TYPE))
1525       method = "intValue";
1526     else if (primitive.equals(Long.TYPE))
1527       method = "longValue";
1528     else if (primitive.equals(Float.TYPE))
1529       method = "floatValue";
1530     else if (primitive.equals(Double.TYPE))
1531       method = "doubleValue";
1532     else
1533       throw new IllegalStateException("unknown primitive class " + primitive);
1534
1535     return method;
1536   }
1537
1538   public static Class box(Class cls)
1539   {
1540     if (! cls.isPrimitive())
1541       throw new IllegalArgumentException("can only box primitive");
1542
1543     Class box;
1544     if (cls.equals(Boolean.TYPE))
1545       box = Boolean.class;
1546     else if (cls.equals(Byte.TYPE))
1547       box = Byte.class;
1548     else if (cls.equals(Character.TYPE))
1549       box = Character.class;
1550     else if (cls.equals(Short.TYPE))
1551       box = Short.class;
1552     else if (cls.equals(Integer.TYPE))
1553       box = Integer.class;
1554     else if (cls.equals(Long.TYPE))
1555       box = Long.class;
1556     else if (cls.equals(Float.TYPE))
1557       box = Float.class;
1558     else if (cls.equals(Double.TYPE))
1559       box = Double.class;
1560     else
1561       throw new IllegalStateException("unknown primitive type " + cls);
1562
1563     return box;
1564   }
1565
1566   private static int size(Class cls) {
1567     if (cls.equals(Long.TYPE) || cls.equals(Double.TYPE))
1568       return 2;
1569     else
1570       return 1;
1571   }
1572
1573   /**
1574    * Sort exceptions so the most general go last.
1575    */
1576   private Class[] sortExceptions(Class[] except)
1577   {
1578     for (int i = 0; i < except.length; i++)
1579       {
1580         for (int j = i + 1; j < except.length; j++)
1581           {
1582             if (except[i].isAssignableFrom(except[j]))
1583               {
1584                 Class tmp = except[i];
1585                 except[i] = except[j];
1586                 except[j] = tmp;
1587               }
1588           }
1589       }
1590     return (except);
1591   }
1592
1593   public void setup(boolean keep, boolean need11Stubs, boolean need12Stubs,
1594                     boolean iiop, boolean poa, boolean debug, boolean warnings,
1595                     boolean noWrite, boolean verbose, boolean force, String classpath,
1596                     String bootclasspath, String extdirs, String outputDirectory)
1597   {
1598     this.keep = keep;
1599     this.need11Stubs = need11Stubs;
1600     this.need12Stubs = need12Stubs;
1601     this.verbose = verbose;
1602     this.noWrite = noWrite;
1603
1604     // Set up classpath.
1605     this.classpath = classpath;
1606     StringTokenizer st =
1607       new StringTokenizer(classpath, File.pathSeparator);
1608     URL[] u = new URL[st.countTokens()];
1609     for (int i = 0; i < u.length; i++)
1610       {
1611         String path = st.nextToken();
1612         File f = new File(path);
1613         try
1614           {
1615             u[i] = f.toURL();
1616           }
1617         catch (java.net.MalformedURLException mue)
1618           {
1619             logError("malformed classpath component " + path);
1620             return;
1621           }
1622       }
1623     loader = new URLClassLoader(u);
1624
1625     destination = outputDirectory;
1626   }
1627
1628   private void findRemoteMethods()
1629     throws RMICException
1630   {
1631     List rmeths = new ArrayList();
1632     for (Class cur = clazz; cur != null; cur = cur.getSuperclass())
1633       {
1634         Class[] interfaces = cur.getInterfaces();
1635         for (int i = 0; i < interfaces.length; i++)
1636           {
1637             if (java.rmi.Remote.class.isAssignableFrom(interfaces[i]))
1638               {
1639                 Class remoteInterface = interfaces[i];
1640                 if (verbose)
1641                   System.out.println
1642                     ("[implements " + remoteInterface.getName() + "]");
1643
1644                 // check if the methods declare RemoteExceptions
1645                 Method[] meths = remoteInterface.getMethods();
1646                 for (int j = 0; j < meths.length; j++)
1647                   {
1648                     Method m = meths[j];
1649                     Class[] exs = m.getExceptionTypes();
1650
1651                     boolean throwsRemote = false;
1652                     for (int k = 0; k < exs.length; k++)
1653                       {
1654                         if (exs[k].isAssignableFrom(RemoteException.class))
1655                           throwsRemote = true;
1656                       }
1657
1658                     if (! throwsRemote)
1659                       {
1660                         throw new RMICException
1661                           ("Method " + m + " in interface " + remoteInterface
1662                            + " does not throw a RemoteException");
1663                       }
1664
1665                     rmeths.add(m);
1666                   }
1667
1668                 mRemoteInterfaces.add(remoteInterface);
1669               }
1670           }
1671       }
1672
1673     // intersect exceptions for doubly inherited methods
1674     boolean[] skip = new boolean[rmeths.size()];
1675     for (int i = 0; i < skip.length; i++)
1676       skip[i] = false;
1677     List methrefs = new ArrayList();
1678     for (int i = 0; i < rmeths.size(); i++)
1679       {
1680         if (skip[i]) continue;
1681         Method current = (Method) rmeths.get(i);
1682         MethodRef ref = new MethodRef(current);
1683         for (int j = i+1; j < rmeths.size(); j++)
1684           {
1685             Method other = (Method) rmeths.get(j);
1686             if (ref.isMatch(other))
1687               {
1688                 ref.intersectExceptions(other);
1689                 skip[j] = true;
1690               }
1691           }
1692         methrefs.add(ref);
1693       }
1694
1695     // Convert into a MethodRef array and sort them
1696     remotemethods = (MethodRef[])
1697       methrefs.toArray(new MethodRef[methrefs.size()]);
1698     Arrays.sort(remotemethods);
1699   }
1700
1701   /**
1702    * Prints an error to System.err and increases the error count.
1703    */
1704   private void logError(Exception theError)
1705   {
1706     logError(theError.getMessage());
1707     if (verbose)
1708       theError.printStackTrace(System.err);
1709   }
1710
1711   /**
1712    * Prints an error to System.err and increases the error count.
1713    */
1714   private void logError(String theError)
1715   {
1716     errorCount++;
1717     System.err.println("error: " + theError);
1718   }
1719
1720   private static String getPrettyName(Class cls)
1721   {
1722     StringBuffer str = new StringBuffer();
1723     for (int count = 0;; count++)
1724       {
1725         if (! cls.isArray())
1726           {
1727             str.append(cls.getName());
1728             for (; count > 0; count--)
1729               str.append("[]");
1730             return (str.toString());
1731           }
1732         cls = cls.getComponentType();
1733       }
1734   }
1735
1736   private static class MethodRef
1737     implements Comparable
1738   {
1739     Method meth;
1740     long hash;
1741     List exceptions;
1742     private String sig;
1743
1744     MethodRef(Method m) {
1745       meth = m;
1746       sig = Type.getMethodDescriptor(meth);
1747       hash = RMIHashes.getMethodHash(m);
1748       // add exceptions removing subclasses
1749       exceptions = removeSubclasses(m.getExceptionTypes());
1750     }
1751
1752     public int compareTo(Object obj) {
1753       MethodRef that = (MethodRef) obj;
1754       int name = this.meth.getName().compareTo(that.meth.getName());
1755       if (name == 0) {
1756         return this.sig.compareTo(that.sig);
1757       }
1758       return name;
1759     }
1760
1761     public boolean isMatch(Method m)
1762     {
1763       if (!meth.getName().equals(m.getName()))
1764         return false;
1765
1766       Class[] params1 = meth.getParameterTypes();
1767       Class[] params2 = m.getParameterTypes();
1768       if (params1.length != params2.length)
1769         return false;
1770
1771       for (int i = 0; i < params1.length; i++)
1772         if (!params1[i].equals(params2[i])) return false;
1773
1774       return true;
1775     }
1776
1777     private static List removeSubclasses(Class[] classes)
1778     {
1779       List list = new ArrayList();
1780       for (int i = 0; i < classes.length; i++)
1781         {
1782           Class candidate = classes[i];
1783           boolean add = true;
1784           for (int j = 0; j < classes.length; j++)
1785             {
1786               if (classes[j].equals(candidate))
1787                 continue;
1788               else if (classes[j].isAssignableFrom(candidate))
1789                 add = false;
1790             }
1791           if (add) list.add(candidate);
1792         }
1793
1794       return list;
1795     }
1796
1797     public void intersectExceptions(Method m)
1798     {
1799       List incoming = removeSubclasses(m.getExceptionTypes());
1800
1801       List updated = new ArrayList();
1802
1803       for (int i = 0; i < exceptions.size(); i++)
1804         {
1805           Class outer = (Class) exceptions.get(i);
1806           boolean addOuter = false;
1807           for (int j = 0; j < incoming.size(); j++)
1808             {
1809               Class inner = (Class) incoming.get(j);
1810
1811               if (inner.equals(outer) || inner.isAssignableFrom(outer))
1812                 addOuter = true;
1813               else if (outer.isAssignableFrom(inner))
1814                 updated.add(inner);
1815             }
1816
1817           if (addOuter)
1818             updated.add(outer);
1819         }
1820
1821       exceptions = updated;
1822     }
1823   }
1824 }