aboutsummaryrefslogtreecommitdiff
path: root/src/share/classes/sun/rmi/rmic/RemoteClass.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/share/classes/sun/rmi/rmic/RemoteClass.java')
-rw-r--r--src/share/classes/sun/rmi/rmic/RemoteClass.java875
1 files changed, 875 insertions, 0 deletions
diff --git a/src/share/classes/sun/rmi/rmic/RemoteClass.java b/src/share/classes/sun/rmi/rmic/RemoteClass.java
new file mode 100644
index 000000000..0caa07fe0
--- /dev/null
+++ b/src/share/classes/sun/rmi/rmic/RemoteClass.java
@@ -0,0 +1,875 @@
+/*
+ * Copyright 1997-2004 Sun Microsystems, Inc. All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+package sun.rmi.rmic;
+
+import java.util.Vector;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.io.IOException;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.security.MessageDigest;
+import java.security.DigestOutputStream;
+import java.security.NoSuchAlgorithmException;
+import sun.tools.java.Type;
+import sun.tools.java.ClassDefinition;
+import sun.tools.java.ClassDeclaration;
+import sun.tools.java.MemberDefinition;
+import sun.tools.java.Identifier;
+import sun.tools.java.ClassNotFound;
+
+/**
+ * A RemoteClass object encapsulates RMI-specific information about
+ * a remote implementation class, i.e. a class that implements
+ * one or more remote interfaces.
+ *
+ * WARNING: The contents of this source file are not part of any
+ * supported API. Code that depends on them does so at its own risk:
+ * they are subject to change or removal without notice.
+ *
+ * @author Peter Jones
+ */
+public class RemoteClass implements sun.rmi.rmic.RMIConstants {
+
+ /**
+ * Create a RemoteClass object representing the remote meta-information
+ * of the given class.
+ *
+ * Returns true if successful. If the class is not a properly formed
+ * remote implementation class or if some other error occurs, the
+ * return value will be null, and errors will have been reported to
+ * the supplied BatchEnvironment.
+ */
+ public static RemoteClass forClass(BatchEnvironment env,
+ ClassDefinition implClassDef)
+ {
+ RemoteClass rc = new RemoteClass(env, implClassDef);
+ if (rc.initialize()) {
+ return rc;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Return the ClassDefinition for this class.
+ */
+ public ClassDefinition getClassDefinition() {
+ return implClassDef;
+ }
+
+ /**
+ * Return the name of the class represented by this object.
+ */
+ public Identifier getName() {
+ return implClassDef.getName();
+ }
+
+ /**
+ * Return an array of ClassDefinitions representing all of the remote
+ * interfaces implemented by this class.
+ *
+ * A remote interface is any interface that extends Remote,
+ * directly or indirectly. The remote interfaces of a class
+ * are the interfaces directly listed in either the class's
+ * "implements" clause, or the "implements" clause of any
+ * of its superclasses, that are remote interfaces.
+ *
+ * The order of the array returned is arbitrary, and some elements
+ * may be superfluous (i.e., superinterfaces of other interfaces
+ * in the array).
+ */
+ public ClassDefinition[] getRemoteInterfaces() {
+ return (ClassDefinition[]) remoteInterfaces.clone();
+ }
+
+ /**
+ * Return an array of RemoteClass.Method objects representing all of
+ * the remote methods implemented by this class, i.e. all of the
+ * methods in the class's remote interfaces.
+ *
+ * The methods in the array are ordered according to the comparision
+ * of the strings consisting of their method name followed by their
+ * type signature, so each method's index in the array corresponds
+ * to its "operation number" in the JDK 1.1 version of the
+ * stub/skeleton protocol.
+ */
+ public Method[] getRemoteMethods() {
+ return (Method[]) remoteMethods.clone();
+ }
+
+ /**
+ * Return the "interface hash" used to match a stub/skeleton pair for
+ * this class in the JDK 1.1 version of the stub/skeleton protocol.
+ */
+ public long getInterfaceHash() {
+ return interfaceHash;
+ }
+
+ /**
+ * Return string representation of this object, consisting of
+ * the string "remote class " followed by the class name.
+ */
+ public String toString() {
+ return "remote class " + implClassDef.getName().toString();
+ }
+
+ /** rmic environment for this object */
+ private BatchEnvironment env;
+
+ /** the remote implementation class this object corresponds to */
+ private ClassDefinition implClassDef;
+
+ /** remote interfaces implemented by this class */
+ private ClassDefinition[] remoteInterfaces;
+
+ /** all the remote methods of this class */
+ private Method[] remoteMethods;
+
+ /** stub/skeleton "interface hash" for this class */
+ private long interfaceHash;
+
+ /** cached definition for certain classes used in this environment */
+ private ClassDefinition defRemote;
+ private ClassDefinition defException;
+ private ClassDefinition defRemoteException;
+
+ /**
+ * Create a RemoteClass instance for the given class. The resulting
+ * object is not yet initialized.
+ */
+ private RemoteClass(BatchEnvironment env, ClassDefinition implClassDef) {
+ this.env = env;
+ this.implClassDef = implClassDef;
+ }
+
+ /**
+ * Validate that the remote implementation class is properly formed
+ * and fill in the data structures required by the public interface.
+ */
+ private boolean initialize() {
+ /*
+ * Verify that the "impl" is really a class, not an interface.
+ */
+ if (implClassDef.isInterface()) {
+ env.error(0, "rmic.cant.make.stubs.for.interface",
+ implClassDef.getName());
+ return false;
+ }
+
+ /*
+ * Initialize cached definitions for the Remote interface and
+ * the RemoteException class.
+ */
+ try {
+ defRemote =
+ env.getClassDeclaration(idRemote).getClassDefinition(env);
+ defException =
+ env.getClassDeclaration(idJavaLangException).
+ getClassDefinition(env);
+ defRemoteException =
+ env.getClassDeclaration(idRemoteException).
+ getClassDefinition(env);
+ } catch (ClassNotFound e) {
+ env.error(0, "rmic.class.not.found", e.name);
+ return false;
+ }
+
+ /*
+ * Here we find all of the remote interfaces of our remote
+ * implementation class. For each class up the superclass
+ * chain, add each directly-implemented interface that
+ * somehow extends Remote to a list.
+ */
+ Vector remotesImplemented = // list of remote interfaces found
+ new Vector();
+ for (ClassDefinition classDef = implClassDef;
+ classDef != null;)
+ {
+ try {
+ ClassDeclaration[] interfaces = classDef.getInterfaces();
+ for (int i = 0; i < interfaces.length; i++) {
+ ClassDefinition interfaceDef =
+ interfaces[i].getClassDefinition(env);
+ /*
+ * Add interface to the list if it extends Remote and
+ * it is not already there.
+ */
+ if (!remotesImplemented.contains(interfaceDef) &&
+ defRemote.implementedBy(env, interfaces[i]))
+ {
+ remotesImplemented.addElement(interfaceDef);
+ /***** <DEBUG> */
+ if (env.verbose()) {
+ System.out.println("[found remote interface: " +
+ interfaceDef.getName() + "]");
+ /***** </DEBUG> */
+ }
+ }
+ }
+
+ /*
+ * Verify that the candidate remote implementation class
+ * implements at least one remote interface directly.
+ */
+ if (classDef == implClassDef && remotesImplemented.isEmpty()) {
+ if (defRemote.implementedBy(env,
+ implClassDef.getClassDeclaration()))
+ {
+ /*
+ * This error message is used if the class does
+ * implement a remote interface through one of
+ * its superclasses, but not directly.
+ */
+ env.error(0, "rmic.must.implement.remote.directly",
+ implClassDef.getName());
+ } else {
+ /*
+ * This error message is used if the class never
+ * implements a remote interface.
+ */
+ env.error(0, "rmic.must.implement.remote",
+ implClassDef.getName());
+ }
+ return false;
+ }
+
+ /*
+ * Get definition for next superclass.
+ */
+ classDef = (classDef.getSuperClass() != null ?
+ classDef.getSuperClass().getClassDefinition(env) :
+ null);
+
+ } catch (ClassNotFound e) {
+ env.error(0, "class.not.found", e.name, classDef.getName());
+ return false;
+ }
+ }
+
+ /*
+ * The "remotesImplemented" vector now contains all of the remote
+ * interfaces directly implemented by the remote class or by any
+ * of its superclasses.
+ *
+ * At this point, we could optimize the list by removing superfluous
+ * entries, i.e. any interfaces that are implemented by some other
+ * interface in the list anyway.
+ *
+ * This should be correct; would it be worthwhile?
+ *
+ * for (int i = 0; i < remotesImplemented.size();) {
+ * ClassDefinition interfaceDef =
+ * (ClassDefinition) remotesImplemented.elementAt(i);
+ * boolean isOtherwiseImplemented = false;
+ * for (int j = 0; j < remotesImplemented.size; j++) {
+ * if (j != i &&
+ * interfaceDef.implementedBy(env, (ClassDefinition)
+ * remotesImplemented.elementAt(j).
+ * getClassDeclaration()))
+ * {
+ * isOtherwiseImplemented = true;
+ * break;
+ * }
+ * }
+ * if (isOtherwiseImplemented) {
+ * remotesImplemented.removeElementAt(i);
+ * } else {
+ * ++i;
+ * }
+ * }
+ */
+
+ /*
+ * Now we collect the methods from all of the remote interfaces
+ * into a hashtable.
+ */
+ Hashtable methods = new Hashtable();
+ boolean errors = false;
+ for (Enumeration enumeration = remotesImplemented.elements();
+ enumeration.hasMoreElements();)
+ {
+ ClassDefinition interfaceDef =
+ (ClassDefinition) enumeration.nextElement();
+ if (!collectRemoteMethods(interfaceDef, methods))
+ errors = true;
+ }
+ if (errors)
+ return false;
+
+ /*
+ * Convert vector of remote interfaces to an array
+ * (order is not important for this array).
+ */
+ remoteInterfaces = new ClassDefinition[remotesImplemented.size()];
+ remotesImplemented.copyInto(remoteInterfaces);
+
+ /*
+ * Sort table of remote methods into an array. The elements are
+ * sorted in ascending order of the string of the method's name
+ * and type signature, so that each elements index is equal to
+ * its operation number of the JDK 1.1 version of the stub/skeleton
+ * protocol.
+ */
+ String[] orderedKeys = new String[methods.size()];
+ int count = 0;
+ for (Enumeration enumeration = methods.elements();
+ enumeration.hasMoreElements();)
+ {
+ Method m = (Method) enumeration.nextElement();
+ String key = m.getNameAndDescriptor();
+ int i;
+ for (i = count; i > 0; --i) {
+ if (key.compareTo(orderedKeys[i - 1]) >= 0) {
+ break;
+ }
+ orderedKeys[i] = orderedKeys[i - 1];
+ }
+ orderedKeys[i] = key;
+ ++count;
+ }
+ remoteMethods = new Method[methods.size()];
+ for (int i = 0; i < remoteMethods.length; i++) {
+ remoteMethods[i] = (Method) methods.get(orderedKeys[i]);
+ /***** <DEBUG> */
+ if (env.verbose()) {
+ System.out.print("[found remote method <" + i + ">: " +
+ remoteMethods[i].getOperationString());
+ ClassDeclaration[] exceptions =
+ remoteMethods[i].getExceptions();
+ if (exceptions.length > 0)
+ System.out.print(" throws ");
+ for (int j = 0; j < exceptions.length; j++) {
+ if (j > 0)
+ System.out.print(", ");
+ System.out.print(exceptions[j].getName());
+ }
+ System.out.println("]");
+ }
+ /***** </DEBUG> */
+ }
+
+ /**
+ * Finally, pre-compute the interface hash to be used by
+ * stubs/skeletons for this remote class.
+ */
+ interfaceHash = computeInterfaceHash();
+
+ return true;
+ }
+
+ /**
+ * Collect and validate all methods from given interface and all of
+ * its superinterfaces as remote methods. Remote methods are added
+ * to the supplied hashtable. Returns true if successful,
+ * or false if an error occurred.
+ */
+ private boolean collectRemoteMethods(ClassDefinition interfaceDef,
+ Hashtable table)
+ {
+ if (!interfaceDef.isInterface()) {
+ throw new Error(
+ "expected interface, not class: " + interfaceDef.getName());
+ }
+
+ /*
+ * rmic used to enforce that a remote interface could not extend
+ * a non-remote interface, i.e. an interface that did not itself
+ * extend from Remote. The current version of rmic does not have
+ * this restriction, so the following code is now commented out.
+ *
+ * Verify that this interface extends Remote, since all interfaces
+ * extended by a remote interface must implement Remote.
+ *
+ * try {
+ * if (!defRemote.implementedBy(env,
+ * interfaceDef.getClassDeclaration()))
+ * {
+ * env.error(0, "rmic.can.mix.remote.nonremote",
+ * interfaceDef.getName());
+ * return false;
+ * }
+ * } catch (ClassNotFound e) {
+ * env.error(0, "class.not.found", e.name,
+ * interfaceDef.getName());
+ * return false;
+ * }
+ */
+
+ boolean errors = false;
+
+ /*
+ * Search interface's members for methods.
+ */
+ nextMember:
+ for (MemberDefinition member = interfaceDef.getFirstMember();
+ member != null;
+ member = member.getNextMember())
+ {
+ if (member.isMethod() &&
+ !member.isConstructor() && !member.isInitializer())
+ {
+ /*
+ * Verify that each method throws RemoteException.
+ */
+ ClassDeclaration[] exceptions = member.getExceptions(env);
+ boolean hasRemoteException = false;
+ for (int i = 0; i < exceptions.length; i++) {
+ /*
+ * rmic used to enforce that a remote method had to
+ * explicitly list RemoteException in its "throws"
+ * clause; i.e., just throwing Exception was not
+ * acceptable. The current version of rmic does not
+ * have this restriction, so the following code is
+ * now commented out. Instead, the method is
+ * considered valid if RemoteException is a subclass
+ * of any of the methods declared exceptions.
+ *
+ * if (exceptions[i].getName().equals(
+ * idRemoteException))
+ * {
+ * hasRemoteException = true;
+ * break;
+ * }
+ */
+ try {
+ if (defRemoteException.subClassOf(
+ env, exceptions[i]))
+ {
+ hasRemoteException = true;
+ break;
+ }
+ } catch (ClassNotFound e) {
+ env.error(0, "class.not.found", e.name,
+ interfaceDef.getName());
+ continue nextMember;
+ }
+ }
+ /*
+ * If this method did not throw RemoteException as required,
+ * generate the error but continue, so that multiple such
+ * errors can be reported.
+ */
+ if (!hasRemoteException) {
+ env.error(0, "rmic.must.throw.remoteexception",
+ interfaceDef.getName(), member.toString());
+ errors = true;
+ continue nextMember;
+ }
+
+ /*
+ * Verify that the implementation of this method throws only
+ * java.lang.Exception or its subclasses (fix bugid 4092486).
+ * JRMP does not support remote methods throwing
+ * java.lang.Throwable or other subclasses.
+ */
+ try {
+ MemberDefinition implMethod = implClassDef.findMethod(
+ env, member.getName(), member.getType());
+ if (implMethod != null) { // should not be null
+ exceptions = implMethod.getExceptions(env);
+ for (int i = 0; i < exceptions.length; i++) {
+ if (!defException.superClassOf(
+ env, exceptions[i]))
+ {
+ env.error(0, "rmic.must.only.throw.exception",
+ implMethod.toString(),
+ exceptions[i].getName());
+ errors = true;
+ continue nextMember;
+ }
+ }
+ }
+ } catch (ClassNotFound e) {
+ env.error(0, "class.not.found", e.name,
+ implClassDef.getName());
+ continue nextMember;
+ }
+
+ /*
+ * Create RemoteClass.Method object to represent this method
+ * found in a remote interface.
+ */
+ Method newMethod = new Method(member);
+ /*
+ * Store remote method's representation in the table of
+ * remote methods found, keyed by its name and parameter
+ * signature.
+ *
+ * If the table already contains an entry with the same
+ * method name and parameter signature, then we must
+ * replace the old entry with a Method object that
+ * represents a legal combination of the old and the new
+ * methods; specifically, the combined method must have
+ * a throws list that contains (only) all of the checked
+ * exceptions that can be thrown by both the old or
+ * the new method (see bugid 4070653).
+ */
+ String key = newMethod.getNameAndDescriptor();
+ Method oldMethod = (Method) table.get(key);
+ if (oldMethod != null) {
+ newMethod = newMethod.mergeWith(oldMethod);
+ if (newMethod == null) {
+ errors = true;
+ continue nextMember;
+ }
+ }
+ table.put(key, newMethod);
+ }
+ }
+
+ /*
+ * Recursively collect methods for all superinterfaces.
+ */
+ try {
+ ClassDeclaration[] superDefs = interfaceDef.getInterfaces();
+ for (int i = 0; i < superDefs.length; i++) {
+ ClassDefinition superDef =
+ superDefs[i].getClassDefinition(env);
+ if (!collectRemoteMethods(superDef, table))
+ errors = true;
+ }
+ } catch (ClassNotFound e) {
+ env.error(0, "class.not.found", e.name, interfaceDef.getName());
+ return false;
+ }
+
+ return !errors;
+ }
+
+ /**
+ * Compute the "interface hash" of the stub/skeleton pair for this
+ * remote implementation class. This is the 64-bit value used to
+ * enforce compatibility between a stub and a skeleton using the
+ * JDK 1.1 version of the stub/skeleton protocol.
+ *
+ * It is calculated using the first 64 bits of a SHA digest. The
+ * digest is from a stream consisting of the following data:
+ * (int) stub version number, always 1
+ * for each remote method, in order of operation number:
+ * (UTF) method name
+ * (UTF) method type signature
+ * for each declared exception, in alphabetical name order:
+ * (UTF) name of exception class
+ *
+ */
+ private long computeInterfaceHash() {
+ long hash = 0;
+ ByteArrayOutputStream sink = new ByteArrayOutputStream(512);
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA");
+ DataOutputStream out = new DataOutputStream(
+ new DigestOutputStream(sink, md));
+
+ out.writeInt(INTERFACE_HASH_STUB_VERSION);
+ for (int i = 0; i < remoteMethods.length; i++) {
+ MemberDefinition m = remoteMethods[i].getMemberDefinition();
+ Identifier name = m.getName();
+ Type type = m.getType();
+
+ out.writeUTF(name.toString());
+ // type signatures already use mangled class names
+ out.writeUTF(type.getTypeSignature());
+
+ ClassDeclaration exceptions[] = m.getExceptions(env);
+ sortClassDeclarations(exceptions);
+ for (int j = 0; j < exceptions.length; j++) {
+ out.writeUTF(Names.mangleClass(
+ exceptions[j].getName()).toString());
+ }
+ }
+ out.flush();
+
+ // use only the first 64 bits of the digest for the hash
+ byte hashArray[] = md.digest();
+ for (int i = 0; i < Math.min(8, hashArray.length); i++) {
+ hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);
+ }
+ } catch (IOException e) {
+ throw new Error(
+ "unexpected exception computing intetrface hash: " + e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new Error(
+ "unexpected exception computing intetrface hash: " + e);
+ }
+
+ return hash;
+ }
+
+ /**
+ * Sort array of class declarations alphabetically by their mangled
+ * fully-qualfied class name. This is used to feed a method's exceptions
+ * in a canonical order into the digest stream for the interface hash
+ * computation.
+ */
+ private void sortClassDeclarations(ClassDeclaration[] decl) {
+ for (int i = 1; i < decl.length; i++) {
+ ClassDeclaration curr = decl[i];
+ String name = Names.mangleClass(curr.getName()).toString();
+ int j;
+ for (j = i; j > 0; j--) {
+ if (name.compareTo(
+ Names.mangleClass(decl[j - 1].getName()).toString()) >= 0)
+ {
+ break;
+ }
+ decl[j] = decl[j - 1];
+ }
+ decl[j] = curr;
+ }
+ }
+
+
+ /**
+ * A RemoteClass.Method object encapsulates RMI-specific information
+ * about a particular remote method in the remote implementation class
+ * represented by the outer instance.
+ */
+ public class Method implements Cloneable {
+
+ /**
+ * Return the definition of the actual class member corresponing
+ * to this method of a remote interface.
+ *
+ * REMIND: Can this method be removed?
+ */
+ public MemberDefinition getMemberDefinition() {
+ return memberDef;
+ }
+
+ /**
+ * Return the name of this method.
+ */
+ public Identifier getName() {
+ return memberDef.getName();
+ }
+
+ /**
+ * Return the type of this method.
+ */
+ public Type getType() {
+ return memberDef.getType();
+ }
+
+ /**
+ * Return an array of the exception classes declared to be
+ * thrown by this remote method.
+ *
+ * For methods with the same name and type signature inherited
+ * from multiple remote interfaces, the array will contain
+ * the set of exceptions declared in all of the interfaces'
+ * methods that can be legally thrown in each of them.
+ */
+ public ClassDeclaration[] getExceptions() {
+ return (ClassDeclaration[]) exceptions.clone();
+ }
+
+ /**
+ * Return the "method hash" used to identify this remote method
+ * in the JDK 1.2 version of the stub protocol.
+ */
+ public long getMethodHash() {
+ return methodHash;
+ }
+
+ /**
+ * Return the string representation of this method.
+ */
+ public String toString() {
+ return memberDef.toString();
+ }
+
+ /**
+ * Return the string representation of this method appropriate
+ * for the construction of a java.rmi.server.Operation object.
+ */
+ public String getOperationString() {
+ return memberDef.toString();
+ }
+
+ /**
+ * Return a string consisting of this method's name followed by
+ * its method descriptor, using the Java VM's notation for
+ * method descriptors (see section 4.3.3 of The Java Virtual
+ * Machine Specification).
+ */
+ public String getNameAndDescriptor() {
+ return memberDef.getName().toString() +
+ memberDef.getType().getTypeSignature();
+ }
+
+ /**
+ * Member definition for this method, from one of the remote
+ * interfaces that this method was found in.
+ *
+ * Note that this member definition may be only one of several
+ * member defintions that correspond to this remote method object,
+ * if several of this class's remote interfaces contain methods
+ * with the same name and type signature. Therefore, this member
+ * definition may declare more exceptions thrown that this remote
+ * method does.
+ */
+ private MemberDefinition memberDef;
+
+ /** stub "method hash" to identify this method */
+ private long methodHash;
+
+ /**
+ * Exceptions declared to be thrown by this remote method.
+ *
+ * This list can include superfluous entries, such as
+ * unchecked exceptions and subclasses of other entries.
+ */
+ private ClassDeclaration[] exceptions;
+
+ /**
+ * Create a new Method object corresponding to the given
+ * method definition.
+ */
+ /*
+ * Temporarily comment out the private modifier until
+ * the VM allows outer class to access inner class's
+ * private constructor
+ */
+ /* private */ Method(MemberDefinition memberDef) {
+ this.memberDef = memberDef;
+ exceptions = memberDef.getExceptions(env);
+ methodHash = computeMethodHash();
+ }
+
+ /**
+ * Cloning is supported by returning a shallow copy of this object.
+ */
+ protected Object clone() {
+ try {
+ return super.clone();
+ } catch (CloneNotSupportedException e) {
+ throw new Error("clone failed");
+ }
+ }
+
+ /**
+ * Return a new Method object that is a legal combination of
+ * this method object and another one.
+ *
+ * This requires determining the exceptions declared by the
+ * combined method, which must be (only) all of the exceptions
+ * declared in both old Methods that may thrown in either of
+ * them.
+ */
+ private Method mergeWith(Method other) {
+ if (!getName().equals(other.getName()) ||
+ !getType().equals(other.getType()))
+ {
+ throw new Error("attempt to merge method \"" +
+ other.getNameAndDescriptor() + "\" with \"" +
+ getNameAndDescriptor());
+ }
+
+ Vector legalExceptions = new Vector();
+ try {
+ collectCompatibleExceptions(
+ other.exceptions, exceptions, legalExceptions);
+ collectCompatibleExceptions(
+ exceptions, other.exceptions, legalExceptions);
+ } catch (ClassNotFound e) {
+ env.error(0, "class.not.found", e.name,
+ getClassDefinition().getName());
+ return null;
+ }
+
+ Method merged = (Method) clone();
+ merged.exceptions = new ClassDeclaration[legalExceptions.size()];
+ legalExceptions.copyInto(merged.exceptions);
+
+ return merged;
+ }
+
+ /**
+ * Add to the supplied list all exceptions in the "from" array
+ * that are subclasses of an exception in the "with" array.
+ */
+ private void collectCompatibleExceptions(ClassDeclaration[] from,
+ ClassDeclaration[] with,
+ Vector list)
+ throws ClassNotFound
+ {
+ for (int i = 0; i < from.length; i++) {
+ ClassDefinition exceptionDef = from[i].getClassDefinition(env);
+ if (!list.contains(from[i])) {
+ for (int j = 0; j < with.length; j++) {
+ if (exceptionDef.subClassOf(env, with[j])) {
+ list.addElement(from[i]);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Compute the "method hash" of this remote method. The method
+ * hash is a long containing the first 64 bits of the SHA digest
+ * from the UTF encoded string of the method name and descriptor.
+ *
+ * REMIND: Should this method share implementation code with
+ * the outer class's computeInterfaceHash() method?
+ */
+ private long computeMethodHash() {
+ long hash = 0;
+ ByteArrayOutputStream sink = new ByteArrayOutputStream(512);
+ try {
+ MessageDigest md = MessageDigest.getInstance("SHA");
+ DataOutputStream out = new DataOutputStream(
+ new DigestOutputStream(sink, md));
+
+ String methodString = getNameAndDescriptor();
+ /***** <DEBUG> */
+ if (env.verbose()) {
+ System.out.println("[string used for method hash: \"" +
+ methodString + "\"]");
+ }
+ /***** </DEBUG> */
+ out.writeUTF(methodString);
+
+ // use only the first 64 bits of the digest for the hash
+ out.flush();
+ byte hashArray[] = md.digest();
+ for (int i = 0; i < Math.min(8, hashArray.length); i++) {
+ hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);
+ }
+ } catch (IOException e) {
+ throw new Error(
+ "unexpected exception computing intetrface hash: " + e);
+ } catch (NoSuchAlgorithmException e) {
+ throw new Error(
+ "unexpected exception computing intetrface hash: " + e);
+ }
+
+ return hash;
+ }
+ }
+}