blob: 9c50efa8ff9b32b86db475572912dc42c15bf070 [file] [log] [blame]
/* ClassRmicCompiler.java --
Copyright (c) 1996, 1997, 1998, 1999, 2001, 2002, 2003, 2004, 2005
Free Software Foundation, Inc.
This file is part of GNU Classpath.
GNU Classpath is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2, or (at your option)
any later version.
GNU Classpath is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Classpath; see the file COPYING. If not, write to the
Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
02111-1307 USA. */
package gnu.classpath.tools.rmic;
import gnu.java.rmi.server.RMIHashes;
import java.io.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.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Label;
import org.objectweb.asm.Type;
public class ClassRmicCompiler
implements RmicBackend
{
private String[] args;
private int next;
private List errors = new ArrayList();
private boolean keep = false;
private boolean need11Stubs = true;
private boolean need12Stubs = true;
private boolean compile = true;
private boolean verbose;
private boolean noWrite;
private String destination;
private String classpath;
private ClassLoader loader;
private int errorCount = 0;
private Class clazz;
private String classname;
private String classInternalName;
private String fullclassname;
private MethodRef[] remotemethods;
private String stubname;
private String skelname;
private List mRemoteInterfaces;
/**
* @return true if run was successful
*/
public boolean run(String[] inputFiles)
{
args = inputFiles;
if (next >= args.length)
return false;
for (int i = next; i < args.length; i++)
{
try
{
if (verbose)
System.out.println("[Processing class " + args[i] + ".class]");
processClass(args[i].replace(File.separatorChar, '.'));
}
catch (IOException e)
{
errors.add(e);
}
catch (RMICException e)
{
errors.add(e);
}
}
if (errors.size() > 0)
{
for (Iterator it = errors.iterator(); it.hasNext(); )
{
Exception ex = (Exception) it.next();
logError(ex);
}
}
return errorCount == 0;
}
private void processClass(String cls) throws IOException, RMICException
{
// reset class specific vars
clazz = null;
classname = null;
classInternalName = null;
fullclassname = null;
remotemethods = null;
stubname = null;
skelname = null;
mRemoteInterfaces = new ArrayList();
analyzeClass(cls);
generateStub();
if (need11Stubs)
generateSkel();
}
private void analyzeClass(String cname)
throws RMICException
{
if (verbose)
System.out.println("[analyze class " + cname + "]");
int p = cname.lastIndexOf('.');
if (p != -1)
classname = cname.substring(p + 1);
else
classname = cname;
fullclassname = cname;
findClass();
findRemoteMethods();
}
/**
* @deprecated
*/
public Exception getException()
{
return errors.size() == 0 ? null : (Exception) errors.get(0);
}
private void findClass()
throws RMICException
{
ClassLoader cl = (loader == null
? ClassLoader.getSystemClassLoader()
: loader);
try
{
clazz = Class.forName(fullclassname, false, cl);
}
catch (ClassNotFoundException cnfe)
{
throw new RMICException
("Class " + fullclassname + " not found in classpath", cnfe);
}
if (! Remote.class.isAssignableFrom(clazz))
{
throw new RMICException
("Class " + clazz.getName()
+ " does not implement a remote interface.");
}
}
private static Type[] typeArray(Class[] cls)
{
Type[] t = new Type[cls.length];
for (int i = 0; i < cls.length; i++)
{
t[i] = Type.getType(cls[i]);
}
return t;
}
private static String[] internalNameArray(Type[] t)
{
String[] s = new String[t.length];
for (int i = 0; i < t.length; i++)
{
s[i] = t[i].getInternalName();
}
return s;
}
private static String[] internalNameArray(Class[] c)
{
return internalNameArray(typeArray(c));
}
private static final String forName = "class$";
private static Object param(Method m, int argIndex)
{
List l = new ArrayList();
l.add(m);
l.add(new Integer(argIndex));
return l;
}
private static void generateClassForNamer(ClassVisitor cls)
{
MethodVisitor cv =
cls.visitMethod
(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_SYNTHETIC, forName,
Type.getMethodDescriptor
(Type.getType(Class.class), new Type[] { Type.getType(String.class) }),
null, null);
Label start = new Label();
cv.visitLabel(start);
cv.visitVarInsn(Opcodes.ALOAD, 0);
cv.visitMethodInsn
(Opcodes.INVOKESTATIC,
Type.getInternalName(Class.class),
"forName",
Type.getMethodDescriptor
(Type.getType(Class.class), new Type[] { Type.getType(String.class) }));
cv.visitInsn(Opcodes.ARETURN);
Label handler = new Label();
cv.visitLabel(handler);
cv.visitVarInsn(Opcodes.ASTORE, 1);
cv.visitTypeInsn(Opcodes.NEW, typeArg(NoClassDefFoundError.class));
cv.visitInsn(Opcodes.DUP);
cv.visitVarInsn(Opcodes.ALOAD, 1);
cv.visitMethodInsn
(Opcodes.INVOKEVIRTUAL,
Type.getInternalName(ClassNotFoundException.class),
"getMessage",
Type.getMethodDescriptor(Type.getType(String.class), new Type[] {}));
cv.visitMethodInsn
(Opcodes.INVOKESPECIAL,
Type.getInternalName(NoClassDefFoundError.class),
"<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE, new Type[] { Type.getType(String.class) }));
cv.visitInsn(Opcodes.ATHROW);
cv.visitTryCatchBlock
(start, handler, handler,
Type.getInternalName(ClassNotFoundException.class));
cv.visitMaxs(-1, -1);
}
private void generateClassConstant(MethodVisitor cv, Class cls) {
if (cls.isPrimitive())
{
Class boxCls;
if (cls.equals(Boolean.TYPE))
boxCls = Boolean.class;
else if (cls.equals(Character.TYPE))
boxCls = Character.class;
else if (cls.equals(Byte.TYPE))
boxCls = Byte.class;
else if (cls.equals(Short.TYPE))
boxCls = Short.class;
else if (cls.equals(Integer.TYPE))
boxCls = Integer.class;
else if (cls.equals(Long.TYPE))
boxCls = Long.class;
else if (cls.equals(Float.TYPE))
boxCls = Float.class;
else if (cls.equals(Double.TYPE))
boxCls = Double.class;
else if (cls.equals(Void.TYPE))
boxCls = Void.class;
else
throw new IllegalArgumentException("unknown primitive type " + cls);
cv.visitFieldInsn
(Opcodes.GETSTATIC, Type.getInternalName(boxCls), "TYPE",
Type.getDescriptor(Class.class));
return;
}
cv.visitLdcInsn(cls.getName());
cv.visitMethodInsn
(Opcodes.INVOKESTATIC, classInternalName, forName,
Type.getMethodDescriptor
(Type.getType(Class.class),
new Type[] { Type.getType(String.class) }));
}
private void generateClassArray(MethodVisitor code, Class[] classes)
{
code.visitLdcInsn(new Integer(classes.length));
code.visitTypeInsn(Opcodes.ANEWARRAY, typeArg(Class.class));
for (int i = 0; i < classes.length; i++)
{
code.visitInsn(Opcodes.DUP);
code.visitLdcInsn(new Integer(i));
generateClassConstant(code, classes[i]);
code.visitInsn(Opcodes.AASTORE);
}
}
private void fillOperationArray(MethodVisitor clinit)
{
// Operations array
clinit.visitLdcInsn(new Integer(remotemethods.length));
clinit.visitTypeInsn(Opcodes.ANEWARRAY, typeArg(Operation.class));
clinit.visitFieldInsn
(Opcodes.PUTSTATIC, classInternalName, "operations",
Type.getDescriptor(Operation[].class));
for (int i = 0; i < remotemethods.length; i++)
{
Method m = remotemethods[i].meth;
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
(Opcodes.GETSTATIC, classInternalName, "operations",
Type.getDescriptor(Operation[].class));
// push array index
clinit.visitLdcInsn(new Integer(i));
// instantiate operation and leave a copy on the stack
clinit.visitTypeInsn(Opcodes.NEW, typeArg(Operation.class));
clinit.visitInsn(Opcodes.DUP);
clinit.visitLdcInsn(desc.toString());
clinit.visitMethodInsn
(Opcodes.INVOKESPECIAL,
Type.getInternalName(Operation.class),
"<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE, new Type[] { Type.getType(String.class) }));
// store in operations array
clinit.visitInsn(Opcodes.AASTORE);
}
}
private void generateStaticMethodObjs(MethodVisitor clinit)
{
for (int i = 0; i < remotemethods.length; i++)
{
Method m = remotemethods[i].meth;
/*
* $method_<i>m.getName()</i>_<i>i</i> =
* <i>m.getDeclaringClass()</i>.class.getMethod
* (m.getName(), m.getParameterType())
*/
String methodVar = "$method_" + m.getName() + "_" + i;
generateClassConstant(clinit, m.getDeclaringClass());
clinit.visitLdcInsn(m.getName());
generateClassArray(clinit, m.getParameterTypes());
clinit.visitMethodInsn
(Opcodes.INVOKEVIRTUAL,
Type.getInternalName(Class.class),
"getMethod",
Type.getMethodDescriptor
(Type.getType(Method.class),
new Type[] { Type.getType(String.class),
Type.getType(Class[].class) }));
clinit.visitFieldInsn
(Opcodes.PUTSTATIC, classInternalName, methodVar,
Type.getDescriptor(Method.class));
}
}
private void generateStub()
throws IOException
{
stubname = fullclassname + "_Stub";
String stubclassname = classname + "_Stub";
File file = new File((destination == null ? "." : destination)
+ File.separator
+ stubname.replace('.', File.separatorChar)
+ ".class");
if (verbose)
System.out.println("[Generating class " + stubname + "]");
final ClassWriter stub = new ClassWriter(true);
classInternalName = stubname.replace('.', '/');
final String superInternalName =
Type.getType(RemoteStub.class).getInternalName();
String[] remoteInternalNames =
internalNameArray((Class[]) mRemoteInterfaces.toArray(new Class[] {}));
stub.visit
(Opcodes.V1_2, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, classInternalName,
null, superInternalName, remoteInternalNames);
if (need12Stubs)
{
stub.visitField
(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "serialVersionUID",
Type.LONG_TYPE.getDescriptor(), null, new Long(2L));
}
if (need11Stubs)
{
stub.visitField
(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL,
"interfaceHash", Type.LONG_TYPE.getDescriptor(), null,
new Long(RMIHashes.getInterfaceHash(clazz)));
if (need12Stubs)
{
stub.visitField
(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, "useNewInvoke",
Type.BOOLEAN_TYPE.getDescriptor(), null, null);
}
stub.visitField
(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL,
"operations", Type.getDescriptor(Operation[].class), null, null);
}
// Set of method references.
if (need12Stubs)
{
for (int i = 0; i < remotemethods.length; i++)
{
Method m = remotemethods[i].meth;
String slotName = "$method_" + m.getName() + "_" + i;
stub.visitField
(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC, slotName,
Type.getDescriptor(Method.class), null, null);
}
}
MethodVisitor clinit = stub.visitMethod
(Opcodes.ACC_STATIC, "<clinit>",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null, null);
if (need11Stubs)
{
fillOperationArray(clinit);
if (! need12Stubs)
clinit.visitInsn(Opcodes.RETURN);
}
if (need12Stubs)
{
// begin of try
Label begin = new Label();
// beginning of catch
Label handler = new Label();
clinit.visitLabel(begin);
// Initialize the methods references.
if (need11Stubs)
{
/*
* RemoteRef.class.getMethod("invoke", new Class[] {
* Remote.class, Method.class, Object[].class, long.class })
*/
generateClassConstant(clinit, RemoteRef.class);
clinit.visitLdcInsn("invoke");
generateClassArray
(clinit, new Class[] { Remote.class, Method.class,
Object[].class, long.class });
clinit.visitMethodInsn
(Opcodes.INVOKEVIRTUAL,
Type.getInternalName(Class.class),
"getMethod",
Type.getMethodDescriptor
(Type.getType(Method.class),
new Type[] { Type.getType(String.class),
Type.getType(Class[].class) }));
// useNewInvoke = true
clinit.visitInsn(Opcodes.ICONST_1);
clinit.visitFieldInsn
(Opcodes.PUTSTATIC, classInternalName, "useNewInvoke",
Type.BOOLEAN_TYPE.getDescriptor());
}
generateStaticMethodObjs(clinit);
// jump past handler
clinit.visitInsn(Opcodes.RETURN);
clinit.visitLabel(handler);
if (need11Stubs)
{
// useNewInvoke = false
clinit.visitInsn(Opcodes.ICONST_0);
clinit.visitFieldInsn
(Opcodes.PUTSTATIC, classInternalName, "useNewInvoke",
Type.BOOLEAN_TYPE.getDescriptor());
clinit.visitInsn(Opcodes.RETURN);
}
else
{
// throw NoSuchMethodError
clinit.visitTypeInsn(Opcodes.NEW, typeArg(NoSuchMethodError.class));
clinit.visitInsn(Opcodes.DUP);
clinit.visitLdcInsn("stub class initialization failed");
clinit.visitMethodInsn
(Opcodes.INVOKESPECIAL,
Type.getInternalName(NoSuchMethodError.class),
"<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE,
new Type[] { Type.getType(String.class) }));
clinit.visitInsn(Opcodes.ATHROW);
}
clinit.visitTryCatchBlock
(begin, handler, handler,
Type.getInternalName(NoSuchMethodException.class));
}
clinit.visitMaxs(-1, -1);
generateClassForNamer(stub);
// Constructors
if (need11Stubs)
{
// no arg public constructor
MethodVisitor code = stub.visitMethod
(Opcodes.ACC_PUBLIC, "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}),
null, null);
code.visitVarInsn(Opcodes.ALOAD, 0);
code.visitMethodInsn
(Opcodes.INVOKESPECIAL, superInternalName, "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
code.visitInsn(Opcodes.RETURN);
code.visitMaxs(-1, -1);
}
// public RemoteRef constructor
MethodVisitor constructor = stub.visitMethod
(Opcodes.ACC_PUBLIC, "<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE, new Type[] {Type.getType(RemoteRef.class)}),
null, null);
constructor.visitVarInsn(Opcodes.ALOAD, 0);
constructor.visitVarInsn(Opcodes.ALOAD, 1);
constructor.visitMethodInsn
(Opcodes.INVOKESPECIAL, superInternalName, "<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE, new Type[] {Type.getType(RemoteRef.class)}));
constructor.visitInsn(Opcodes.RETURN);
constructor.visitMaxs(-1, -1);
// Method implementations
for (int i = 0; i < remotemethods.length; i++)
{
Method m = remotemethods[i].meth;
Class[] sig = m.getParameterTypes();
Class returntype = m.getReturnType();
Class[] except = sortExceptions
((Class[]) remotemethods[i].exceptions.toArray(new Class[0]));
MethodVisitor code = stub.visitMethod
(Opcodes.ACC_PUBLIC,
m.getName(),
Type.getMethodDescriptor(Type.getType(returntype), typeArray(sig)),
null,
internalNameArray(typeArray(except)));
final Variables var = new Variables();
// this and parameters are the declared vars
var.declare("this");
for (int j = 0; j < sig.length; j++)
var.declare(param(m, j), size(sig[j]));
Label methodTryBegin = new Label();
code.visitLabel(methodTryBegin);
if (need12Stubs)
{
Label oldInvoke = new Label();
if (need11Stubs)
{
// if not useNewInvoke jump to old invoke
code.visitFieldInsn
(Opcodes.GETSTATIC, classInternalName, "useNewInvoke",
Type.getDescriptor(boolean.class));
code.visitJumpInsn(Opcodes.IFEQ, oldInvoke);
}
// this.ref
code.visitVarInsn(Opcodes.ALOAD, var.get("this"));
code.visitFieldInsn
(Opcodes.GETFIELD, Type.getInternalName(RemoteObject.class),
"ref", Type.getDescriptor(RemoteRef.class));
// "this" is first arg to invoke
code.visitVarInsn(Opcodes.ALOAD, var.get("this"));
// method object is second arg to invoke
String methName = "$method_" + m.getName() + "_" + i;
code.visitFieldInsn
(Opcodes.GETSTATIC, classInternalName, methName,
Type.getDescriptor(Method.class));
// args to remote method are third arg to invoke
if (sig.length == 0)
code.visitInsn(Opcodes.ACONST_NULL);
else
{
// create arg Object[] (with boxed primitives) and push it
code.visitLdcInsn(new Integer(sig.length));
code.visitTypeInsn(Opcodes.ANEWARRAY, typeArg(Object.class));
var.allocate("argArray");
code.visitVarInsn(Opcodes.ASTORE, var.get("argArray"));
for (int j = 0; j < sig.length; j++)
{
int size = size(sig[j]);
int insn = loadOpcode(sig[j]);
Class box = sig[j].isPrimitive() ? box(sig[j]) : null;
code.visitVarInsn(Opcodes.ALOAD, var.get("argArray"));
code.visitLdcInsn(new Integer(j));
// put argument on stack
if (box != null)
{
code.visitTypeInsn(Opcodes.NEW, typeArg(box));
code.visitInsn(Opcodes.DUP);
code.visitVarInsn(insn, var.get(param(m, j)));
code.visitMethodInsn
(Opcodes.INVOKESPECIAL,
Type.getInternalName(box),
"<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE,
new Type[] { Type.getType(sig[j]) }));
}
else
code.visitVarInsn(insn, var.get(param(m, j)));
code.visitInsn(Opcodes.AASTORE);
}
code.visitVarInsn(Opcodes.ALOAD, var.deallocate("argArray"));
}
// push remote operation opcode
code.visitLdcInsn(new Long(remotemethods[i].hash));
code.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(RemoteRef.class),
"invoke",
Type.getMethodDescriptor
(Type.getType(Object.class),
new Type[] { Type.getType(Remote.class),
Type.getType(Method.class),
Type.getType(Object[].class),
Type.LONG_TYPE }));
if (! returntype.equals(Void.TYPE))
{
int retcode = returnOpcode(returntype);
Class boxCls =
returntype.isPrimitive() ? box(returntype) : null;
code.visitTypeInsn
(Opcodes.CHECKCAST, typeArg(boxCls == null ? returntype : boxCls));
if (returntype.isPrimitive())
{
// unbox
code.visitMethodInsn
(Opcodes.INVOKEVIRTUAL,
Type.getType(boxCls).getInternalName(),
unboxMethod(returntype),
Type.getMethodDescriptor
(Type.getType(returntype), new Type[] {}));
}
code.visitInsn(retcode);
}
else
code.visitInsn(Opcodes.RETURN);
if (need11Stubs)
code.visitLabel(oldInvoke);
}
if (need11Stubs)
{
// this.ref.newCall(this, operations, index, interfaceHash)
code.visitVarInsn(Opcodes.ALOAD, var.get("this"));
code.visitFieldInsn
(Opcodes.GETFIELD,
Type.getInternalName(RemoteObject.class),
"ref",
Type.getDescriptor(RemoteRef.class));
// "this" is first arg to newCall
code.visitVarInsn(Opcodes.ALOAD, var.get("this"));
// operations is second arg to newCall
code.visitFieldInsn
(Opcodes.GETSTATIC, classInternalName, "operations",
Type.getDescriptor(Operation[].class));
// method index is third arg
code.visitLdcInsn(new Integer(i));
// interface hash is fourth arg
code.visitFieldInsn
(Opcodes.GETSTATIC, classInternalName, "interfaceHash",
Type.LONG_TYPE.getDescriptor());
code.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(RemoteRef.class),
"newCall",
Type.getMethodDescriptor
(Type.getType(RemoteCall.class),
new Type[] { Type.getType(RemoteObject.class),
Type.getType(Operation[].class),
Type.INT_TYPE,
Type.LONG_TYPE }));
// store call object on stack and leave copy on stack
var.allocate("call");
code.visitInsn(Opcodes.DUP);
code.visitVarInsn(Opcodes.ASTORE, var.get("call"));
Label beginArgumentTryBlock = new Label();
code.visitLabel(beginArgumentTryBlock);
// ObjectOutput out = call.getOutputStream();
code.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(RemoteCall.class),
"getOutputStream",
Type.getMethodDescriptor
(Type.getType(ObjectOutput.class), new Type[] {}));
for (int j = 0; j < sig.length; j++)
{
// dup the ObjectOutput
code.visitInsn(Opcodes.DUP);
// get j'th arg to remote method
code.visitVarInsn(loadOpcode(sig[j]), var.get(param(m, j)));
Class argCls =
sig[j].isPrimitive() ? sig[j] : Object.class;
// out.writeFoo
code.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(ObjectOutput.class),
writeMethod(sig[j]),
Type.getMethodDescriptor
(Type.VOID_TYPE,
new Type[] { Type.getType(argCls) }));
}
// pop ObjectOutput
code.visitInsn(Opcodes.POP);
Label iohandler = new Label();
Label endArgumentTryBlock = new Label();
code.visitJumpInsn(Opcodes.GOTO, endArgumentTryBlock);
code.visitLabel(iohandler);
// throw new MarshalException(msg, ioexception);
code.visitVarInsn(Opcodes.ASTORE, var.allocate("exception"));
code.visitTypeInsn(Opcodes.NEW, typeArg(MarshalException.class));
code.visitInsn(Opcodes.DUP);
code.visitLdcInsn("error marshalling arguments");
code.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception"));
code.visitMethodInsn
(Opcodes.INVOKESPECIAL,
Type.getInternalName(MarshalException.class),
"<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE,
new Type[] { Type.getType(String.class),
Type.getType(Exception.class) }));
code.visitInsn(Opcodes.ATHROW);
code.visitLabel(endArgumentTryBlock);
code.visitTryCatchBlock
(beginArgumentTryBlock, iohandler, iohandler,
Type.getInternalName(IOException.class));
// this.ref.invoke(call)
code.visitVarInsn(Opcodes.ALOAD, var.get("this"));
code.visitFieldInsn
(Opcodes.GETFIELD, Type.getInternalName(RemoteObject.class),
"ref", Type.getDescriptor(RemoteRef.class));
code.visitVarInsn(Opcodes.ALOAD, var.get("call"));
code.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(RemoteRef.class),
"invoke",
Type.getMethodDescriptor
(Type.VOID_TYPE,
new Type[] { Type.getType(RemoteCall.class) }));
// handle return value
boolean needcastcheck = false;
Label beginReturnTryCatch = new Label();
code.visitLabel(beginReturnTryCatch);
int returncode = returnOpcode(returntype);
if (! returntype.equals(Void.TYPE))
{
// call.getInputStream()
code.visitVarInsn(Opcodes.ALOAD, var.get("call"));
code.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(RemoteCall.class),
"getInputStream",
Type.getMethodDescriptor
(Type.getType(ObjectInput.class), new Type[] {}));
Class readCls =
returntype.isPrimitive() ? returntype : Object.class;
code.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(ObjectInput.class),
readMethod(returntype),
Type.getMethodDescriptor
(Type.getType(readCls), new Type[] {}));
boolean castresult = false;
if (! returntype.isPrimitive())
{
if (! returntype.equals(Object.class))
castresult = true;
else
needcastcheck = true;
}
if (castresult)
code.visitTypeInsn(Opcodes.CHECKCAST, typeArg(returntype));
// leave result on stack for return
}
// this.ref.done(call)
code.visitVarInsn(Opcodes.ALOAD, var.get("this"));
code.visitFieldInsn
(Opcodes.GETFIELD,
Type.getInternalName(RemoteObject.class),
"ref",
Type.getDescriptor(RemoteRef.class));
code.visitVarInsn(Opcodes.ALOAD, var.deallocate("call"));
code.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(RemoteRef.class),
"done",
Type.getMethodDescriptor
(Type.VOID_TYPE,
new Type[] { Type.getType(RemoteCall.class) }));
// return; or return result;
code.visitInsn(returncode);
// exception handler
Label handler = new Label();
code.visitLabel(handler);
code.visitVarInsn(Opcodes.ASTORE, var.allocate("exception"));
// throw new UnmarshalException(msg, e)
code.visitTypeInsn(Opcodes.NEW, typeArg(UnmarshalException.class));
code.visitInsn(Opcodes.DUP);
code.visitLdcInsn("error unmarshalling return");
code.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception"));
code.visitMethodInsn
(Opcodes.INVOKESPECIAL,
Type.getInternalName(UnmarshalException.class),
"<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE,
new Type[] { Type.getType(String.class),
Type.getType(Exception.class) }));
code.visitInsn(Opcodes.ATHROW);
Label endReturnTryCatch = new Label();
// catch IOException
code.visitTryCatchBlock
(beginReturnTryCatch, handler, handler,
Type.getInternalName(IOException.class));
if (needcastcheck)
{
// catch ClassNotFoundException
code.visitTryCatchBlock
(beginReturnTryCatch, handler, handler,
Type.getInternalName(ClassNotFoundException.class));
}
}
Label rethrowHandler = new Label();
code.visitLabel(rethrowHandler);
// rethrow declared exceptions
code.visitInsn(Opcodes.ATHROW);
boolean needgeneral = true;
for (int j = 0; j < except.length; j++)
{
if (except[j] == Exception.class)
needgeneral = false;
}
for (int j = 0; j < except.length; j++)
{
code.visitTryCatchBlock
(methodTryBegin, rethrowHandler, rethrowHandler,
Type.getInternalName(except[j]));
}
if (needgeneral)
{
// rethrow unchecked exceptions
code.visitTryCatchBlock
(methodTryBegin, rethrowHandler, rethrowHandler,
Type.getInternalName(RuntimeException.class));
Label generalHandler = new Label();
code.visitLabel(generalHandler);
String msg = "undeclared checked exception";
// throw new java.rmi.UnexpectedException(msg, e)
code.visitVarInsn(Opcodes.ASTORE, var.allocate("exception"));
code.visitTypeInsn(Opcodes.NEW, typeArg(UnexpectedException.class));
code.visitInsn(Opcodes.DUP);
code.visitLdcInsn(msg);
code.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception"));
code.visitMethodInsn
(Opcodes.INVOKESPECIAL,
Type.getInternalName(UnexpectedException.class),
"<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE,
new Type [] { Type.getType(String.class),
Type.getType(Exception.class) }));
code.visitInsn(Opcodes.ATHROW);
code.visitTryCatchBlock
(methodTryBegin, rethrowHandler, generalHandler,
Type.getInternalName(Exception.class));
}
code.visitMaxs(-1, -1);
}
stub.visitEnd();
byte[] classData = stub.toByteArray();
if (!noWrite)
{
if (file.exists())
file.delete();
if (file.getParentFile() != null)
file.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(file);
fos.write(classData);
fos.flush();
fos.close();
}
}
private void generateSkel() throws IOException
{
skelname = fullclassname + "_Skel";
String skelclassname = classname + "_Skel";
File file = new File(destination == null ? "" : destination
+ File.separator
+ skelname.replace('.', File.separatorChar)
+ ".class");
if (verbose)
System.out.println("[Generating class " + skelname + "]");
final ClassWriter skel = new ClassWriter(true);
classInternalName = skelname.replace('.', '/');
skel.visit
(Opcodes.V1_1, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL,
classInternalName, Type.getInternalName(Object.class), null,
new String[] { Type.getType(Skeleton.class).getInternalName() });
skel.visitField
(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "interfaceHash",
Type.LONG_TYPE.getDescriptor(), null,
new Long(RMIHashes.getInterfaceHash(clazz)));
skel.visitField
(Opcodes.ACC_PRIVATE + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL, "operations",
Type.getDescriptor(Operation[].class), null, null);
MethodVisitor clinit = skel.visitMethod
(Opcodes.ACC_STATIC, "<clinit>",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null, null);
fillOperationArray(clinit);
clinit.visitInsn(Opcodes.RETURN);
clinit.visitMaxs(-1, -1);
// no arg public constructor
MethodVisitor init = skel.visitMethod
(Opcodes.ACC_PUBLIC, "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}), null, null);
init.visitVarInsn(Opcodes.ALOAD, 0);
init.visitMethodInsn
(Opcodes.INVOKESPECIAL, Type.getInternalName(Object.class), "<init>",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
init.visitInsn(Opcodes.RETURN);
init.visitMaxs(-1, -1);
/*
* public Operation[] getOperations()
* returns a clone of the operations array
*/
MethodVisitor getOp = skel.visitMethod
(Opcodes.ACC_PUBLIC, "getOperations",
Type.getMethodDescriptor
(Type.getType(Operation[].class), new Type[] {}),
null, null);
getOp.visitFieldInsn
(Opcodes.GETSTATIC, classInternalName, "operations",
Type.getDescriptor(Operation[].class));
getOp.visitMethodInsn
(Opcodes.INVOKEVIRTUAL, Type.getInternalName(Object.class),
"clone", Type.getMethodDescriptor(Type.getType(Object.class),
new Type[] {}));
getOp.visitTypeInsn(Opcodes.CHECKCAST, typeArg(Operation[].class));
getOp.visitInsn(Opcodes.ARETURN);
getOp.visitMaxs(-1, -1);
// public void dispatch(Remote, RemoteCall, int opnum, long hash)
MethodVisitor dispatch = skel.visitMethod
(Opcodes.ACC_PUBLIC,
"dispatch",
Type.getMethodDescriptor
(Type.VOID_TYPE,
new Type[] { Type.getType(Remote.class),
Type.getType(RemoteCall.class),
Type.INT_TYPE, Type.LONG_TYPE }), null,
new String[] { Type.getInternalName(Exception.class) });
Variables var = new Variables();
var.declare("this");
var.declare("remoteobj");
var.declare("remotecall");
var.declare("opnum");
var.declareWide("hash");
/*
* if opnum >= 0
* XXX it is unclear why there is handling of negative opnums
*/
dispatch.visitVarInsn(Opcodes.ILOAD, var.get("opnum"));
Label nonNegativeOpnum = new Label();
Label opnumSet = new Label();
dispatch.visitJumpInsn(Opcodes.IFGE, nonNegativeOpnum);
for (int i = 0; i < remotemethods.length; i++)
{
// assign opnum if hash matches supplied hash
dispatch.visitVarInsn(Opcodes.LLOAD, var.get("hash"));
dispatch.visitLdcInsn(new Long(remotemethods[i].hash));
Label notIt = new Label();
dispatch.visitInsn(Opcodes.LCMP);
dispatch.visitJumpInsn(Opcodes.IFNE, notIt);
// opnum = <opnum>
dispatch.visitLdcInsn(new Integer(i));
dispatch.visitVarInsn(Opcodes.ISTORE, var.get("opnum"));
dispatch.visitJumpInsn(Opcodes.GOTO, opnumSet);
dispatch.visitLabel(notIt);
}
// throw new SkeletonMismatchException
Label mismatch = new Label();
dispatch.visitJumpInsn(Opcodes.GOTO, mismatch);
dispatch.visitLabel(nonNegativeOpnum);
// if opnum is already set, check that the hash matches the interface
dispatch.visitVarInsn(Opcodes.LLOAD, var.get("hash"));
dispatch.visitFieldInsn
(Opcodes.GETSTATIC, classInternalName,
"interfaceHash", Type.LONG_TYPE.getDescriptor());
dispatch.visitInsn(Opcodes.LCMP);
dispatch.visitJumpInsn(Opcodes.IFEQ, opnumSet);
dispatch.visitLabel(mismatch);
dispatch.visitTypeInsn
(Opcodes.NEW, typeArg(SkeletonMismatchException.class));
dispatch.visitInsn(Opcodes.DUP);
dispatch.visitLdcInsn("interface hash mismatch");
dispatch.visitMethodInsn
(Opcodes.INVOKESPECIAL,
Type.getInternalName(SkeletonMismatchException.class),
"<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE, new Type[] { Type.getType(String.class) }));
dispatch.visitInsn(Opcodes.ATHROW);
// opnum has been set
dispatch.visitLabel(opnumSet);
dispatch.visitVarInsn(Opcodes.ALOAD, var.get("remoteobj"));
dispatch.visitTypeInsn(Opcodes.CHECKCAST, typeArg(clazz));
dispatch.visitVarInsn(Opcodes.ASTORE, var.get("remoteobj"));
Label deflt = new Label();
Label[] methLabels = new Label[remotemethods.length];
for (int i = 0; i < methLabels.length; i++)
methLabels[i] = new Label();
// switch on opnum
dispatch.visitVarInsn(Opcodes.ILOAD, var.get("opnum"));
dispatch.visitTableSwitchInsn
(0, remotemethods.length - 1, deflt, methLabels);
// Method dispatch
for (int i = 0; i < remotemethods.length; i++)
{
dispatch.visitLabel(methLabels[i]);
Method m = remotemethods[i].meth;
generateMethodSkel(dispatch, m, var);
}
dispatch.visitLabel(deflt);
dispatch.visitTypeInsn(Opcodes.NEW, typeArg(UnmarshalException.class));
dispatch.visitInsn(Opcodes.DUP);
dispatch.visitLdcInsn("invalid method number");
dispatch.visitMethodInsn
(Opcodes.INVOKESPECIAL,
Type.getInternalName(UnmarshalException.class),
"<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE, new Type[] { Type.getType(String.class) }));
dispatch.visitInsn(Opcodes.ATHROW);
dispatch.visitMaxs(-1, -1);
skel.visitEnd();
byte[] classData = skel.toByteArray();
if (!noWrite)
{
if (file.exists())
file.delete();
if (file.getParentFile() != null)
file.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(file);
fos.write(classData);
fos.flush();
fos.close();
}
}
private void generateMethodSkel(MethodVisitor cv, Method m, Variables var)
{
Class[] sig = m.getParameterTypes();
Label readArgs = new Label();
cv.visitLabel(readArgs);
boolean needcastcheck = false;
// ObjectInput in = call.getInputStream();
cv.visitVarInsn(Opcodes.ALOAD, var.get("remotecall"));
cv.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(RemoteCall.class), "getInputStream",
Type.getMethodDescriptor
(Type.getType(ObjectInput.class), new Type[] {}));
cv.visitVarInsn(Opcodes.ASTORE, var.allocate("objectinput"));
for (int i = 0; i < sig.length; i++)
{
// dup input stream
cv.visitVarInsn(Opcodes.ALOAD, var.get("objectinput"));
Class readCls = sig[i].isPrimitive() ? sig[i] : Object.class;
// in.readFoo()
cv.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(ObjectInput.class),
readMethod(sig[i]),
Type.getMethodDescriptor
(Type.getType(readCls), new Type [] {}));
if (! sig[i].isPrimitive() && ! sig[i].equals(Object.class))
{
needcastcheck = true;
cv.visitTypeInsn(Opcodes.CHECKCAST, typeArg(sig[i]));
}
// store arg in variable
cv.visitVarInsn
(storeOpcode(sig[i]), var.allocate(param(m, i), size(sig[i])));
}
var.deallocate("objectinput");
Label doCall = new Label();
Label closeInput = new Label();
cv.visitJumpInsn(Opcodes.JSR, closeInput);
cv.visitJumpInsn(Opcodes.GOTO, doCall);
// throw new UnmarshalException
Label handler = new Label();
cv.visitLabel(handler);
cv.visitVarInsn(Opcodes.ASTORE, var.allocate("exception"));
cv.visitTypeInsn(Opcodes.NEW, typeArg(UnmarshalException.class));
cv.visitInsn(Opcodes.DUP);
cv.visitLdcInsn("error unmarshalling arguments");
cv.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception"));
cv.visitMethodInsn
(Opcodes.INVOKESPECIAL,
Type.getInternalName(UnmarshalException.class),
"<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE, new Type[] { Type.getType(String.class),
Type.getType(Exception.class) }));
cv.visitVarInsn(Opcodes.ASTORE, var.allocate("toThrow"));
cv.visitJumpInsn(Opcodes.JSR, closeInput);
cv.visitVarInsn(Opcodes.ALOAD, var.get("toThrow"));
cv.visitInsn(Opcodes.ATHROW);
cv.visitTryCatchBlock
(readArgs, handler, handler, Type.getInternalName(IOException.class));
if (needcastcheck)
{
cv.visitTryCatchBlock
(readArgs, handler, handler,
Type.getInternalName(ClassCastException.class));
}
// finally block
cv.visitLabel(closeInput);
cv.visitVarInsn(Opcodes.ASTORE, var.allocate("retAddress"));
cv.visitVarInsn(Opcodes.ALOAD, var.get("remotecall"));
cv.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(RemoteCall.class),
"releaseInputStream",
Type.getMethodDescriptor(Type.VOID_TYPE, new Type[] {}));
cv.visitVarInsn(Opcodes.RET, var.deallocate("retAddress"));
var.deallocate("toThrow");
// do the call using args stored as variables
cv.visitLabel(doCall);
cv.visitVarInsn(Opcodes.ALOAD, var.get("remoteobj"));
for (int i = 0; i < sig.length; i++)
cv.visitVarInsn(loadOpcode(sig[i]), var.deallocate(param(m, i)));
cv.visitMethodInsn
(Opcodes.INVOKEVIRTUAL, Type.getInternalName(clazz), m.getName(),
Type.getMethodDescriptor(m));
Class returntype = m.getReturnType();
if (! returntype.equals(Void.TYPE))
{
cv.visitVarInsn
(storeOpcode(returntype), var.allocate("result", size(returntype)));
}
// write result to result stream
Label writeResult = new Label();
cv.visitLabel(writeResult);
cv.visitVarInsn(Opcodes.ALOAD, var.get("remotecall"));
cv.visitInsn(Opcodes.ICONST_1);
cv.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(RemoteCall.class),
"getResultStream",
Type.getMethodDescriptor
(Type.getType(ObjectOutput.class),
new Type[] { Type.BOOLEAN_TYPE }));
if (! returntype.equals(Void.TYPE))
{
// out.writeFoo(result)
cv.visitVarInsn(loadOpcode(returntype), var.deallocate("result"));
Class writeCls = returntype.isPrimitive() ? returntype : Object.class;
cv.visitMethodInsn
(Opcodes.INVOKEINTERFACE,
Type.getInternalName(ObjectOutput.class),
writeMethod(returntype),
Type.getMethodDescriptor
(Type.VOID_TYPE, new Type[] { Type.getType(writeCls) }));
}
cv.visitInsn(Opcodes.RETURN);
// throw new MarshalException
Label marshalHandler = new Label();
cv.visitLabel(marshalHandler);
cv.visitVarInsn(Opcodes.ASTORE, var.allocate("exception"));
cv.visitTypeInsn(Opcodes.NEW, typeArg(MarshalException.class));
cv.visitInsn(Opcodes.DUP);
cv.visitLdcInsn("error marshalling return");
cv.visitVarInsn(Opcodes.ALOAD, var.deallocate("exception"));
cv.visitMethodInsn
(Opcodes.INVOKESPECIAL,
Type.getInternalName(MarshalException.class),
"<init>",
Type.getMethodDescriptor
(Type.VOID_TYPE, new Type[] { Type.getType(String.class),
Type.getType(Exception.class) }));
cv.visitInsn(Opcodes.ATHROW);
cv.visitTryCatchBlock
(writeResult, marshalHandler, marshalHandler,
Type.getInternalName(IOException.class));
}
private static String typeArg(Class cls)
{
if (cls.isArray())
return Type.getDescriptor(cls);
return Type.getInternalName(cls);
}
private static String readMethod(Class cls)
{
if (cls.equals(Void.TYPE))
throw new IllegalArgumentException("can not read void");
String method;
if (cls.equals(Boolean.TYPE))
method = "readBoolean";
else if (cls.equals(Byte.TYPE))
method = "readByte";
else if (cls.equals(Character.TYPE))
method = "readChar";
else if (cls.equals(Short.TYPE))
method = "readShort";
else if (cls.equals(Integer.TYPE))
method = "readInt";
else if (cls.equals(Long.TYPE))
method = "readLong";
else if (cls.equals(Float.TYPE))
method = "readFloat";
else if (cls.equals(Double.TYPE))
method = "readDouble";
else
method = "readObject";
return method;
}
private static String writeMethod(Class cls)
{
if (cls.equals(Void.TYPE))
throw new IllegalArgumentException("can not read void");
String method;
if (cls.equals(Boolean.TYPE))
method = "writeBoolean";
else if (cls.equals(Byte.TYPE))
method = "writeByte";
else if (cls.equals(Character.TYPE))
method = "writeChar";
else if (cls.equals(Short.TYPE))
method = "writeShort";
else if (cls.equals(Integer.TYPE))
method = "writeInt";
else if (cls.equals(Long.TYPE))
method = "writeLong";
else if (cls.equals(Float.TYPE))
method = "writeFloat";
else if (cls.equals(Double.TYPE))
method = "writeDouble";
else
method = "writeObject";
return method;
}
private static int returnOpcode(Class cls)
{
int returncode;
if (cls.equals(Boolean.TYPE))
returncode = Opcodes.IRETURN;
else if (cls.equals(Byte.TYPE))
returncode = Opcodes.IRETURN;
else if (cls.equals(Character.TYPE))
returncode = Opcodes.IRETURN;
else if (cls.equals(Short.TYPE))
returncode = Opcodes.IRETURN;
else if (cls.equals(Integer.TYPE))
returncode = Opcodes.IRETURN;
else if (cls.equals(Long.TYPE))
returncode = Opcodes.LRETURN;
else if (cls.equals(Float.TYPE))
returncode = Opcodes.FRETURN;
else if (cls.equals(Double.TYPE))
returncode = Opcodes.DRETURN;
else if (cls.equals(Void.TYPE))
returncode = Opcodes.RETURN;
else
returncode = Opcodes.ARETURN;
return returncode;
}
private static int loadOpcode(Class cls)
{
if (cls.equals(Void.TYPE))
throw new IllegalArgumentException("can not load void");
int loadcode;
if (cls.equals(Boolean.TYPE))
loadcode = Opcodes.ILOAD;
else if (cls.equals(Byte.TYPE))
loadcode = Opcodes.ILOAD;
else if (cls.equals(Character.TYPE))
loadcode = Opcodes.ILOAD;
else if (cls.equals(Short.TYPE))
loadcode = Opcodes.ILOAD;
else if (cls.equals(Integer.TYPE))
loadcode = Opcodes.ILOAD;
else if (cls.equals(Long.TYPE))
loadcode = Opcodes.LLOAD;
else if (cls.equals(Float.TYPE))
loadcode = Opcodes.FLOAD;
else if (cls.equals(Double.TYPE))
loadcode = Opcodes.DLOAD;
else
loadcode = Opcodes.ALOAD;
return loadcode;
}
private static int storeOpcode(Class cls)
{
if (cls.equals(Void.TYPE))
throw new IllegalArgumentException("can not load void");
int storecode;
if (cls.equals(Boolean.TYPE))
storecode = Opcodes.ISTORE;
else if (cls.equals(Byte.TYPE))
storecode = Opcodes.ISTORE;
else if (cls.equals(Character.TYPE))
storecode = Opcodes.ISTORE;
else if (cls.equals(Short.TYPE))
storecode = Opcodes.ISTORE;
else if (cls.equals(Integer.TYPE))
storecode = Opcodes.ISTORE;
else if (cls.equals(Long.TYPE))
storecode = Opcodes.LSTORE;
else if (cls.equals(Float.TYPE))
storecode = Opcodes.FSTORE;
else if (cls.equals(Double.TYPE))
storecode = Opcodes.DSTORE;
else
storecode = Opcodes.ASTORE;
return storecode;
}
private static String unboxMethod(Class primitive)
{
if (! primitive.isPrimitive())
throw new IllegalArgumentException("can not unbox nonprimitive");
String method;
if (primitive.equals(Boolean.TYPE))
method = "booleanValue";
else if (primitive.equals(Byte.TYPE))
method = "byteValue";
else if (primitive.equals(Character.TYPE))
method = "charValue";
else if (primitive.equals(Short.TYPE))
method = "shortValue";
else if (primitive.equals(Integer.TYPE))
method = "intValue";
else if (primitive.equals(Long.TYPE))
method = "longValue";
else if (primitive.equals(Float.TYPE))
method = "floatValue";
else if (primitive.equals(Double.TYPE))
method = "doubleValue";
else
throw new IllegalStateException("unknown primitive class " + primitive);
return method;
}
public static Class box(Class cls)
{
if (! cls.isPrimitive())
throw new IllegalArgumentException("can only box primitive");
Class box;
if (cls.equals(Boolean.TYPE))
box = Boolean.class;
else if (cls.equals(Byte.TYPE))
box = Byte.class;
else if (cls.equals(Character.TYPE))
box = Character.class;
else if (cls.equals(Short.TYPE))
box = Short.class;
else if (cls.equals(Integer.TYPE))
box = Integer.class;
else if (cls.equals(Long.TYPE))
box = Long.class;
else if (cls.equals(Float.TYPE))
box = Float.class;
else if (cls.equals(Double.TYPE))
box = Double.class;
else
throw new IllegalStateException("unknown primitive type " + cls);
return box;
}
private static int size(Class cls) {
if (cls.equals(Long.TYPE) || cls.equals(Double.TYPE))
return 2;
else
return 1;
}
/**
* Sort exceptions so the most general go last.
*/
private Class[] sortExceptions(Class[] except)
{
for (int i = 0; i < except.length; i++)
{
for (int j = i + 1; j < except.length; j++)
{
if (except[i].isAssignableFrom(except[j]))
{
Class tmp = except[i];
except[i] = except[j];
except[j] = tmp;
}
}
}
return (except);
}
public void setup(boolean keep, boolean need11Stubs, boolean need12Stubs,
boolean iiop, boolean poa, boolean debug, boolean warnings,
boolean noWrite, boolean verbose, boolean force, String classpath,
String bootclasspath, String extdirs, String outputDirectory)
{
this.keep = keep;
this.need11Stubs = need11Stubs;
this.need12Stubs = need12Stubs;
this.verbose = verbose;
this.noWrite = noWrite;
// Set up classpath.
this.classpath = classpath;
StringTokenizer st =
new StringTokenizer(classpath, File.pathSeparator);
URL[] u = new URL[st.countTokens()];
for (int i = 0; i < u.length; i++)
{
String path = st.nextToken();
File f = new File(path);
try
{
u[i] = f.toURL();
}
catch (java.net.MalformedURLException mue)
{
logError("malformed classpath component " + path);
return;
}
}
loader = new URLClassLoader(u);
destination = outputDirectory;
}
private void findRemoteMethods()
throws RMICException
{
List rmeths = new ArrayList();
for (Class cur = clazz; cur != null; cur = cur.getSuperclass())
{
Class[] interfaces = cur.getInterfaces();
for (int i = 0; i < interfaces.length; i++)
{
if (java.rmi.Remote.class.isAssignableFrom(interfaces[i]))
{
Class remoteInterface = interfaces[i];
if (verbose)
System.out.println
("[implements " + remoteInterface.getName() + "]");
// check if the methods declare RemoteExceptions
Method[] meths = remoteInterface.getMethods();
for (int j = 0; j < meths.length; j++)
{
Method m = meths[j];
Class[] exs = m.getExceptionTypes();
boolean throwsRemote = false;
for (int k = 0; k < exs.length; k++)
{
if (exs[k].isAssignableFrom(RemoteException.class))
throwsRemote = true;
}
if (! throwsRemote)
{
throw new RMICException
("Method " + m + " in interface " + remoteInterface
+ " does not throw a RemoteException");
}
rmeths.add(m);
}
mRemoteInterfaces.add(remoteInterface);
}
}
}
// intersect exceptions for doubly inherited methods
boolean[] skip = new boolean[rmeths.size()];
for (int i = 0; i < skip.length; i++)
skip[i] = false;
List methrefs = new ArrayList();
for (int i = 0; i < rmeths.size(); i++)
{
if (skip[i]) continue;
Method current = (Method) rmeths.get(i);
MethodRef ref = new MethodRef(current);
for (int j = i+1; j < rmeths.size(); j++)
{
Method other = (Method) rmeths.get(j);
if (ref.isMatch(other))
{
ref.intersectExceptions(other);
skip[j] = true;
}
}
methrefs.add(ref);
}
// Convert into a MethodRef array and sort them
remotemethods = (MethodRef[])
methrefs.toArray(new MethodRef[methrefs.size()]);
Arrays.sort(remotemethods);
}
/**
* Prints an error to System.err and increases the error count.
*/
private void logError(Exception theError)
{
logError(theError.getMessage());
if (verbose)
theError.printStackTrace(System.err);
}
/**
* Prints an error to System.err and increases the error count.
*/
private void logError(String theError)
{
errorCount++;
System.err.println("error: " + theError);
}
private static String getPrettyName(Class cls)
{
StringBuffer str = new StringBuffer();
for (int count = 0;; count++)
{
if (! cls.isArray())
{
str.append(cls.getName());
for (; count > 0; count--)
str.append("[]");
return (str.toString());
}
cls = cls.getComponentType();
}
}
private static class MethodRef
implements Comparable
{
Method meth;
long hash;
List exceptions;
private String sig;
MethodRef(Method m) {
meth = m;
sig = Type.getMethodDescriptor(meth);
hash = RMIHashes.getMethodHash(m);
// add exceptions removing subclasses
exceptions = removeSubclasses(m.getExceptionTypes());
}
public int compareTo(Object obj) {
MethodRef that = (MethodRef) obj;
int name = this.meth.getName().compareTo(that.meth.getName());
if (name == 0) {
return this.sig.compareTo(that.sig);
}
return name;
}
public boolean isMatch(Method m)
{
if (!meth.getName().equals(m.getName()))
return false;
Class[] params1 = meth.getParameterTypes();
Class[] params2 = m.getParameterTypes();
if (params1.length != params2.length)
return false;
for (int i = 0; i < params1.length; i++)
if (!params1[i].equals(params2[i])) return false;
return true;
}
private static List removeSubclasses(Class[] classes)
{
List list = new ArrayList();
for (int i = 0; i < classes.length; i++)
{
Class candidate = classes[i];
boolean add = true;
for (int j = 0; j < classes.length; j++)
{
if (classes[j].equals(candidate))
continue;
else if (classes[j].isAssignableFrom(candidate))
add = false;
}
if (add) list.add(candidate);
}
return list;
}
public void intersectExceptions(Method m)
{
List incoming = removeSubclasses(m.getExceptionTypes());
List updated = new ArrayList();
for (int i = 0; i < exceptions.size(); i++)
{
Class outer = (Class) exceptions.get(i);
boolean addOuter = false;
for (int j = 0; j < incoming.size(); j++)
{
Class inner = (Class) incoming.get(j);
if (inner.equals(outer) || inner.isAssignableFrom(outer))
addOuter = true;
else if (outer.isAssignableFrom(inner))
updated.add(inner);
}
if (addOuter)
updated.add(outer);
}
exceptions = updated;
}
}
}