/* |
|
* Copyright (c) 1997, 2013, Oracle and/or its affiliates. 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. Oracle designates this |
|
* particular file as subject to the "Classpath" exception as provided |
|
* by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
* or visit www.oracle.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 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 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<ClassDefinition> remotesImplemented = // list of remote interfaces found |
|
new Vector<ClassDefinition>(); |
|
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<String, Method> methods = new Hashtable<String, Method>(); |
|
boolean errors = false; |
|
for (Enumeration<ClassDefinition> enumeration |
|
= remotesImplemented.elements(); |
|
enumeration.hasMoreElements();) |
|
{ |
|
ClassDefinition interfaceDef = 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<Method> enumeration = methods.elements(); |
|
enumeration.hasMoreElements();) |
|
{ |
|
Method m = 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] = 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<String, Method> 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 = 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-qualified 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 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<ClassDeclaration> legalExceptions |
|
= new Vector<ClassDeclaration>(); |
|
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<ClassDeclaration> 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; |
|
} |
|
} |
|
} |