History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/rmi/rmic/RemoteClass.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/rmi/rmic/RemoteClass.java+876−0
@@ -0,0 +1,876 @@
1+/*
2+ * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved.
3+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4+ *
5+ * This code is free software; you can redistribute it and/or modify it
6+ * under the terms of the GNU General Public License version 2 only, as
7+ * published by the Free Software Foundation. Oracle designates this
8+ * particular file as subject to the "Classpath" exception as provided
9+ * by Oracle in the LICENSE file that accompanied this code.
10+ *
11+ * This code is distributed in the hope that it will be useful, but WITHOUT
12+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14+ * version 2 for more details (a copy is included in the LICENSE file that
15+ * accompanied this code).
16+ *
17+ * You should have received a copy of the GNU General Public License version
18+ * 2 along with this work; if not, write to the Free Software Foundation,
19+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20+ *
21+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22+ * or visit www.oracle.com if you need additional information or have any
23+ * questions.
24+ */
25+
26+package sun.rmi.rmic;
27+
28+import java.util.Vector;
29+import java.util.Hashtable;
30+import java.util.Enumeration;
31+import java.io.IOException;
32+import java.io.ByteArrayOutputStream;
33+import java.io.DataOutputStream;
34+import java.security.MessageDigest;
35+import java.security.DigestOutputStream;
36+import java.security.NoSuchAlgorithmException;
37+import sun.tools.java.Type;
38+import sun.tools.java.ClassDefinition;
39+import sun.tools.java.ClassDeclaration;
40+import sun.tools.java.MemberDefinition;
41+import sun.tools.java.Identifier;
42+import sun.tools.java.ClassNotFound;
43+
44+/**
45+ * A RemoteClass object encapsulates RMI-specific information about
46+ * a remote implementation class, i.e. a class that implements
47+ * one or more remote interfaces.
48+ *
49+ * WARNING: The contents of this source file are not part of any
50+ * supported API. Code that depends on them does so at its own risk:
51+ * they are subject to change or removal without notice.
52+ *
53+ * @author Peter Jones
54+ */
55+public class RemoteClass implements sun.rmi.rmic.RMIConstants {
56+
57+ /**
58+ * Create a RemoteClass object representing the remote meta-information
59+ * of the given class.
60+ *
61+ * Returns true if successful. If the class is not a properly formed
62+ * remote implementation class or if some other error occurs, the
63+ * return value will be null, and errors will have been reported to
64+ * the supplied BatchEnvironment.
65+ */
66+ public static RemoteClass forClass(BatchEnvironment env,
67+ ClassDefinition implClassDef)
68+ {
69+ RemoteClass rc = new RemoteClass(env, implClassDef);
70+ if (rc.initialize()) {
71+ return rc;
72+ } else {
73+ return null;
74+ }
75+ }
76+
77+ /**
78+ * Return the ClassDefinition for this class.
79+ */
80+ public ClassDefinition getClassDefinition() {
81+ return implClassDef;
82+ }
83+
84+ /**
85+ * Return the name of the class represented by this object.
86+ */
87+ public Identifier getName() {
88+ return implClassDef.getName();
89+ }
90+
91+ /**
92+ * Return an array of ClassDefinitions representing all of the remote
93+ * interfaces implemented by this class.
94+ *
95+ * A remote interface is any interface that extends Remote,
96+ * directly or indirectly. The remote interfaces of a class
97+ * are the interfaces directly listed in either the class's
98+ * "implements" clause, or the "implements" clause of any
99+ * of its superclasses, that are remote interfaces.
100+ *
101+ * The order of the array returned is arbitrary, and some elements
102+ * may be superfluous (i.e., superinterfaces of other interfaces
103+ * in the array).
104+ */
105+ public ClassDefinition[] getRemoteInterfaces() {
106+ return remoteInterfaces.clone();
107+ }
108+
109+ /**
110+ * Return an array of RemoteClass.Method objects representing all of
111+ * the remote methods implemented by this class, i.e. all of the
112+ * methods in the class's remote interfaces.
113+ *
114+ * The methods in the array are ordered according to the comparision
115+ * of the strings consisting of their method name followed by their
116+ * type signature, so each method's index in the array corresponds
117+ * to its "operation number" in the JDK 1.1 version of the
118+ * stub/skeleton protocol.
119+ */
120+ public Method[] getRemoteMethods() {
121+ return remoteMethods.clone();
122+ }
123+
124+ /**
125+ * Return the "interface hash" used to match a stub/skeleton pair for
126+ * this class in the JDK 1.1 version of the stub/skeleton protocol.
127+ */
128+ public long getInterfaceHash() {
129+ return interfaceHash;
130+ }
131+
132+ /**
133+ * Return string representation of this object, consisting of
134+ * the string "remote class " followed by the class name.
135+ */
136+ public String toString() {
137+ return "remote class " + implClassDef.getName().toString();
138+ }
139+
140+ /** rmic environment for this object */
141+ private BatchEnvironment env;
142+
143+ /** the remote implementation class this object corresponds to */
144+ private ClassDefinition implClassDef;
145+
146+ /** remote interfaces implemented by this class */
147+ private ClassDefinition[] remoteInterfaces;
148+
149+ /** all the remote methods of this class */
150+ private Method[] remoteMethods;
151+
152+ /** stub/skeleton "interface hash" for this class */
153+ private long interfaceHash;
154+
155+ /** cached definition for certain classes used in this environment */
156+ private ClassDefinition defRemote;
157+ private ClassDefinition defException;
158+ private ClassDefinition defRemoteException;
159+
160+ /**
161+ * Create a RemoteClass instance for the given class. The resulting
162+ * object is not yet initialized.
163+ */
164+ private RemoteClass(BatchEnvironment env, ClassDefinition implClassDef) {
165+ this.env = env;
166+ this.implClassDef = implClassDef;
167+ }
168+
169+ /**
170+ * Validate that the remote implementation class is properly formed
171+ * and fill in the data structures required by the public interface.
172+ */
173+ private boolean initialize() {
174+ /*
175+ * Verify that the "impl" is really a class, not an interface.
176+ */
177+ if (implClassDef.isInterface()) {
178+ env.error(0, "rmic.cant.make.stubs.for.interface",
179+ implClassDef.getName());
180+ return false;
181+ }
182+
183+ /*
184+ * Initialize cached definitions for the Remote interface and
185+ * the RemoteException class.
186+ */
187+ try {
188+ defRemote =
189+ env.getClassDeclaration(idRemote).getClassDefinition(env);
190+ defException =
191+ env.getClassDeclaration(idJavaLangException).
192+ getClassDefinition(env);
193+ defRemoteException =
194+ env.getClassDeclaration(idRemoteException).
195+ getClassDefinition(env);
196+ } catch (ClassNotFound e) {
197+ env.error(0, "rmic.class.not.found", e.name);
198+ return false;
199+ }
200+
201+ /*
202+ * Here we find all of the remote interfaces of our remote
203+ * implementation class. For each class up the superclass
204+ * chain, add each directly-implemented interface that
205+ * somehow extends Remote to a list.
206+ */
207+ Vector<ClassDefinition> remotesImplemented = // list of remote interfaces found
208+ new Vector<ClassDefinition>();
209+ for (ClassDefinition classDef = implClassDef;
210+ classDef != null;)
211+ {
212+ try {
213+ ClassDeclaration[] interfaces = classDef.getInterfaces();
214+ for (int i = 0; i < interfaces.length; i++) {
215+ ClassDefinition interfaceDef =
216+ interfaces[i].getClassDefinition(env);
217+ /*
218+ * Add interface to the list if it extends Remote and
219+ * it is not already there.
220+ */
221+ if (!remotesImplemented.contains(interfaceDef) &&
222+ defRemote.implementedBy(env, interfaces[i]))
223+ {
224+ remotesImplemented.addElement(interfaceDef);
225+ /***** <DEBUG> */
226+ if (env.verbose()) {
227+ System.out.println("[found remote interface: " +
228+ interfaceDef.getName() + "]");
229+ /***** </DEBUG> */
230+ }
231+ }
232+ }
233+
234+ /*
235+ * Verify that the candidate remote implementation class
236+ * implements at least one remote interface directly.
237+ */
238+ if (classDef == implClassDef && remotesImplemented.isEmpty()) {
239+ if (defRemote.implementedBy(env,
240+ implClassDef.getClassDeclaration()))
241+ {
242+ /*
243+ * This error message is used if the class does
244+ * implement a remote interface through one of
245+ * its superclasses, but not directly.
246+ */
247+ env.error(0, "rmic.must.implement.remote.directly",
248+ implClassDef.getName());
249+ } else {
250+ /*
251+ * This error message is used if the class never
252+ * implements a remote interface.
253+ */
254+ env.error(0, "rmic.must.implement.remote",
255+ implClassDef.getName());
256+ }
257+ return false;
258+ }
259+
260+ /*
261+ * Get definition for next superclass.
262+ */
263+ classDef = (classDef.getSuperClass() != null ?
264+ classDef.getSuperClass().getClassDefinition(env) :
265+ null);
266+
267+ } catch (ClassNotFound e) {
268+ env.error(0, "class.not.found", e.name, classDef.getName());
269+ return false;
270+ }
271+ }
272+
273+ /*
274+ * The "remotesImplemented" vector now contains all of the remote
275+ * interfaces directly implemented by the remote class or by any
276+ * of its superclasses.
277+ *
278+ * At this point, we could optimize the list by removing superfluous
279+ * entries, i.e. any interfaces that are implemented by some other
280+ * interface in the list anyway.
281+ *
282+ * This should be correct; would it be worthwhile?
283+ *
284+ * for (int i = 0; i < remotesImplemented.size();) {
285+ * ClassDefinition interfaceDef =
286+ * (ClassDefinition) remotesImplemented.elementAt(i);
287+ * boolean isOtherwiseImplemented = false;
288+ * for (int j = 0; j < remotesImplemented.size; j++) {
289+ * if (j != i &&
290+ * interfaceDef.implementedBy(env, (ClassDefinition)
291+ * remotesImplemented.elementAt(j).
292+ * getClassDeclaration()))
293+ * {
294+ * isOtherwiseImplemented = true;
295+ * break;
296+ * }
297+ * }
298+ * if (isOtherwiseImplemented) {
299+ * remotesImplemented.removeElementAt(i);
300+ * } else {
301+ * ++i;
302+ * }
303+ * }
304+ */
305+
306+ /*
307+ * Now we collect the methods from all of the remote interfaces
308+ * into a hashtable.
309+ */
310+ Hashtable<String, Method> methods = new Hashtable<String, Method>();
311+ boolean errors = false;
312+ for (Enumeration<ClassDefinition> enumeration
313+ = remotesImplemented.elements();
314+ enumeration.hasMoreElements();)
315+ {
316+ ClassDefinition interfaceDef = enumeration.nextElement();
317+ if (!collectRemoteMethods(interfaceDef, methods))
318+ errors = true;
319+ }
320+ if (errors)
321+ return false;
322+
323+ /*
324+ * Convert vector of remote interfaces to an array
325+ * (order is not important for this array).
326+ */
327+ remoteInterfaces = new ClassDefinition[remotesImplemented.size()];
328+ remotesImplemented.copyInto(remoteInterfaces);
329+
330+ /*
331+ * Sort table of remote methods into an array. The elements are
332+ * sorted in ascending order of the string of the method's name
333+ * and type signature, so that each elements index is equal to
334+ * its operation number of the JDK 1.1 version of the stub/skeleton
335+ * protocol.
336+ */
337+ String[] orderedKeys = new String[methods.size()];
338+ int count = 0;
339+ for (Enumeration<Method> enumeration = methods.elements();
340+ enumeration.hasMoreElements();)
341+ {
342+ Method m = enumeration.nextElement();
343+ String key = m.getNameAndDescriptor();
344+ int i;
345+ for (i = count; i > 0; --i) {
346+ if (key.compareTo(orderedKeys[i - 1]) >= 0) {
347+ break;
348+ }
349+ orderedKeys[i] = orderedKeys[i - 1];
350+ }
351+ orderedKeys[i] = key;
352+ ++count;
353+ }
354+ remoteMethods = new Method[methods.size()];
355+ for (int i = 0; i < remoteMethods.length; i++) {
356+ remoteMethods[i] = methods.get(orderedKeys[i]);
357+ /***** <DEBUG> */
358+ if (env.verbose()) {
359+ System.out.print("[found remote method <" + i + ">: " +
360+ remoteMethods[i].getOperationString());
361+ ClassDeclaration[] exceptions =
362+ remoteMethods[i].getExceptions();
363+ if (exceptions.length > 0)
364+ System.out.print(" throws ");
365+ for (int j = 0; j < exceptions.length; j++) {
366+ if (j > 0)
367+ System.out.print(", ");
368+ System.out.print(exceptions[j].getName());
369+ }
370+ System.out.println("]");
371+ }
372+ /***** </DEBUG> */
373+ }
374+
375+ /**
376+ * Finally, pre-compute the interface hash to be used by
377+ * stubs/skeletons for this remote class.
378+ */
379+ interfaceHash = computeInterfaceHash();
380+
381+ return true;
382+ }
383+
384+ /**
385+ * Collect and validate all methods from given interface and all of
386+ * its superinterfaces as remote methods. Remote methods are added
387+ * to the supplied hashtable. Returns true if successful,
388+ * or false if an error occurred.
389+ */
390+ private boolean collectRemoteMethods(ClassDefinition interfaceDef,
391+ Hashtable<String, Method> table)
392+ {
393+ if (!interfaceDef.isInterface()) {
394+ throw new Error(
395+ "expected interface, not class: " + interfaceDef.getName());
396+ }
397+
398+ /*
399+ * rmic used to enforce that a remote interface could not extend
400+ * a non-remote interface, i.e. an interface that did not itself
401+ * extend from Remote. The current version of rmic does not have
402+ * this restriction, so the following code is now commented out.
403+ *
404+ * Verify that this interface extends Remote, since all interfaces
405+ * extended by a remote interface must implement Remote.
406+ *
407+ * try {
408+ * if (!defRemote.implementedBy(env,
409+ * interfaceDef.getClassDeclaration()))
410+ * {
411+ * env.error(0, "rmic.can.mix.remote.nonremote",
412+ * interfaceDef.getName());
413+ * return false;
414+ * }
415+ * } catch (ClassNotFound e) {
416+ * env.error(0, "class.not.found", e.name,
417+ * interfaceDef.getName());
418+ * return false;
419+ * }
420+ */
421+
422+ boolean errors = false;
423+
424+ /*
425+ * Search interface's members for methods.
426+ */
427+ nextMember:
428+ for (MemberDefinition member = interfaceDef.getFirstMember();
429+ member != null;
430+ member = member.getNextMember())
431+ {
432+ if (member.isMethod() &&
433+ !member.isConstructor() && !member.isInitializer())
434+ {
435+ /*
436+ * Verify that each method throws RemoteException.
437+ */
438+ ClassDeclaration[] exceptions = member.getExceptions(env);
439+ boolean hasRemoteException = false;
440+ for (int i = 0; i < exceptions.length; i++) {
441+ /*
442+ * rmic used to enforce that a remote method had to
443+ * explicitly list RemoteException in its "throws"
444+ * clause; i.e., just throwing Exception was not
445+ * acceptable. The current version of rmic does not
446+ * have this restriction, so the following code is
447+ * now commented out. Instead, the method is
448+ * considered valid if RemoteException is a subclass
449+ * of any of the methods declared exceptions.
450+ *
451+ * if (exceptions[i].getName().equals(
452+ * idRemoteException))
453+ * {
454+ * hasRemoteException = true;
455+ * break;
456+ * }
457+ */
458+ try {
459+ if (defRemoteException.subClassOf(
460+ env, exceptions[i]))
461+ {
462+ hasRemoteException = true;
463+ break;
464+ }
465+ } catch (ClassNotFound e) {
466+ env.error(0, "class.not.found", e.name,
467+ interfaceDef.getName());
468+ continue nextMember;
469+ }
470+ }
471+ /*
472+ * If this method did not throw RemoteException as required,
473+ * generate the error but continue, so that multiple such
474+ * errors can be reported.
475+ */
476+ if (!hasRemoteException) {
477+ env.error(0, "rmic.must.throw.remoteexception",
478+ interfaceDef.getName(), member.toString());
479+ errors = true;
480+ continue nextMember;
481+ }
482+
483+ /*
484+ * Verify that the implementation of this method throws only
485+ * java.lang.Exception or its subclasses (fix bugid 4092486).
486+ * JRMP does not support remote methods throwing
487+ * java.lang.Throwable or other subclasses.
488+ */
489+ try {
490+ MemberDefinition implMethod = implClassDef.findMethod(
491+ env, member.getName(), member.getType());
492+ if (implMethod != null) { // should not be null
493+ exceptions = implMethod.getExceptions(env);
494+ for (int i = 0; i < exceptions.length; i++) {
495+ if (!defException.superClassOf(
496+ env, exceptions[i]))
497+ {
498+ env.error(0, "rmic.must.only.throw.exception",
499+ implMethod.toString(),
500+ exceptions[i].getName());
501+ errors = true;
502+ continue nextMember;
503+ }
504+ }
505+ }
506+ } catch (ClassNotFound e) {
507+ env.error(0, "class.not.found", e.name,
508+ implClassDef.getName());
509+ continue nextMember;
510+ }
511+
512+ /*
513+ * Create RemoteClass.Method object to represent this method
514+ * found in a remote interface.
515+ */
516+ Method newMethod = new Method(member);
517+ /*
518+ * Store remote method's representation in the table of
519+ * remote methods found, keyed by its name and parameter
520+ * signature.
521+ *
522+ * If the table already contains an entry with the same
523+ * method name and parameter signature, then we must
524+ * replace the old entry with a Method object that
525+ * represents a legal combination of the old and the new
526+ * methods; specifically, the combined method must have
527+ * a throws list that contains (only) all of the checked
528+ * exceptions that can be thrown by both the old or
529+ * the new method (see bugid 4070653).
530+ */
531+ String key = newMethod.getNameAndDescriptor();
532+ Method oldMethod = table.get(key);
533+ if (oldMethod != null) {
534+ newMethod = newMethod.mergeWith(oldMethod);
535+ if (newMethod == null) {
536+ errors = true;
537+ continue nextMember;
538+ }
539+ }
540+ table.put(key, newMethod);
541+ }
542+ }
543+
544+ /*
545+ * Recursively collect methods for all superinterfaces.
546+ */
547+ try {
548+ ClassDeclaration[] superDefs = interfaceDef.getInterfaces();
549+ for (int i = 0; i < superDefs.length; i++) {
550+ ClassDefinition superDef =
551+ superDefs[i].getClassDefinition(env);
552+ if (!collectRemoteMethods(superDef, table))
553+ errors = true;
554+ }
555+ } catch (ClassNotFound e) {
556+ env.error(0, "class.not.found", e.name, interfaceDef.getName());
557+ return false;
558+ }
559+
560+ return !errors;
561+ }
562+
563+ /**
564+ * Compute the "interface hash" of the stub/skeleton pair for this
565+ * remote implementation class. This is the 64-bit value used to
566+ * enforce compatibility between a stub and a skeleton using the
567+ * JDK 1.1 version of the stub/skeleton protocol.
568+ *
569+ * It is calculated using the first 64 bits of a SHA digest. The
570+ * digest is from a stream consisting of the following data:
571+ * (int) stub version number, always 1
572+ * for each remote method, in order of operation number:
573+ * (UTF) method name
574+ * (UTF) method type signature
575+ * for each declared exception, in alphabetical name order:
576+ * (UTF) name of exception class
577+ *
578+ */
579+ private long computeInterfaceHash() {
580+ long hash = 0;
581+ ByteArrayOutputStream sink = new ByteArrayOutputStream(512);
582+ try {
583+ MessageDigest md = MessageDigest.getInstance("SHA");
584+ DataOutputStream out = new DataOutputStream(
585+ new DigestOutputStream(sink, md));
586+
587+ out.writeInt(INTERFACE_HASH_STUB_VERSION);
588+ for (int i = 0; i < remoteMethods.length; i++) {
589+ MemberDefinition m = remoteMethods[i].getMemberDefinition();
590+ Identifier name = m.getName();
591+ Type type = m.getType();
592+
593+ out.writeUTF(name.toString());
594+ // type signatures already use mangled class names
595+ out.writeUTF(type.getTypeSignature());
596+
597+ ClassDeclaration exceptions[] = m.getExceptions(env);
598+ sortClassDeclarations(exceptions);
599+ for (int j = 0; j < exceptions.length; j++) {
600+ out.writeUTF(Names.mangleClass(
601+ exceptions[j].getName()).toString());
602+ }
603+ }
604+ out.flush();
605+
606+ // use only the first 64 bits of the digest for the hash
607+ byte hashArray[] = md.digest();
608+ for (int i = 0; i < Math.min(8, hashArray.length); i++) {
609+ hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);
610+ }
611+ } catch (IOException e) {
612+ throw new Error(
613+ "unexpected exception computing intetrface hash: " + e);
614+ } catch (NoSuchAlgorithmException e) {
615+ throw new Error(
616+ "unexpected exception computing intetrface hash: " + e);
617+ }
618+
619+ return hash;
620+ }
621+
622+ /**
623+ * Sort array of class declarations alphabetically by their mangled
624+ * fully-qualfied class name. This is used to feed a method's exceptions
625+ * in a canonical order into the digest stream for the interface hash
626+ * computation.
627+ */
628+ private void sortClassDeclarations(ClassDeclaration[] decl) {
629+ for (int i = 1; i < decl.length; i++) {
630+ ClassDeclaration curr = decl[i];
631+ String name = Names.mangleClass(curr.getName()).toString();
632+ int j;
633+ for (j = i; j > 0; j--) {
634+ if (name.compareTo(
635+ Names.mangleClass(decl[j - 1].getName()).toString()) >= 0)
636+ {
637+ break;
638+ }
639+ decl[j] = decl[j - 1];
640+ }
641+ decl[j] = curr;
642+ }
643+ }
644+
645+
646+ /**
647+ * A RemoteClass.Method object encapsulates RMI-specific information
648+ * about a particular remote method in the remote implementation class
649+ * represented by the outer instance.
650+ */
651+ public class Method implements Cloneable {
652+
653+ /**
654+ * Return the definition of the actual class member corresponing
655+ * to this method of a remote interface.
656+ *
657+ * REMIND: Can this method be removed?
658+ */
659+ public MemberDefinition getMemberDefinition() {
660+ return memberDef;
661+ }
662+
663+ /**
664+ * Return the name of this method.
665+ */
666+ public Identifier getName() {
667+ return memberDef.getName();
668+ }
669+
670+ /**
671+ * Return the type of this method.
672+ */
673+ public Type getType() {
674+ return memberDef.getType();
675+ }
676+
677+ /**
678+ * Return an array of the exception classes declared to be
679+ * thrown by this remote method.
680+ *
681+ * For methods with the same name and type signature inherited
682+ * from multiple remote interfaces, the array will contain
683+ * the set of exceptions declared in all of the interfaces'
684+ * methods that can be legally thrown in each of them.
685+ */
686+ public ClassDeclaration[] getExceptions() {
687+ return exceptions.clone();
688+ }
689+
690+ /**
691+ * Return the "method hash" used to identify this remote method
692+ * in the JDK 1.2 version of the stub protocol.
693+ */
694+ public long getMethodHash() {
695+ return methodHash;
696+ }
697+
698+ /**
699+ * Return the string representation of this method.
700+ */
701+ public String toString() {
702+ return memberDef.toString();
703+ }
704+
705+ /**
706+ * Return the string representation of this method appropriate
707+ * for the construction of a java.rmi.server.Operation object.
708+ */
709+ public String getOperationString() {
710+ return memberDef.toString();
711+ }
712+
713+ /**
714+ * Return a string consisting of this method's name followed by
715+ * its method descriptor, using the Java VM's notation for
716+ * method descriptors (see section 4.3.3 of The Java Virtual
717+ * Machine Specification).
718+ */
719+ public String getNameAndDescriptor() {
720+ return memberDef.getName().toString() +
721+ memberDef.getType().getTypeSignature();
722+ }
723+
724+ /**
725+ * Member definition for this method, from one of the remote
726+ * interfaces that this method was found in.
727+ *
728+ * Note that this member definition may be only one of several
729+ * member defintions that correspond to this remote method object,
730+ * if several of this class's remote interfaces contain methods
731+ * with the same name and type signature. Therefore, this member
732+ * definition may declare more exceptions thrown that this remote
733+ * method does.
734+ */
735+ private MemberDefinition memberDef;
736+
737+ /** stub "method hash" to identify this method */
738+ private long methodHash;
739+
740+ /**
741+ * Exceptions declared to be thrown by this remote method.
742+ *
743+ * This list can include superfluous entries, such as
744+ * unchecked exceptions and subclasses of other entries.
745+ */
746+ private ClassDeclaration[] exceptions;
747+
748+ /**
749+ * Create a new Method object corresponding to the given
750+ * method definition.
751+ */
752+ /*
753+ * Temporarily comment out the private modifier until
754+ * the VM allows outer class to access inner class's
755+ * private constructor
756+ */
757+ /* private */ Method(MemberDefinition memberDef) {
758+ this.memberDef = memberDef;
759+ exceptions = memberDef.getExceptions(env);
760+ methodHash = computeMethodHash();
761+ }
762+
763+ /**
764+ * Cloning is supported by returning a shallow copy of this object.
765+ */
766+ protected Object clone() {
767+ try {
768+ return super.clone();
769+ } catch (CloneNotSupportedException e) {
770+ throw new Error("clone failed");
771+ }
772+ }
773+
774+ /**
775+ * Return a new Method object that is a legal combination of
776+ * this method object and another one.
777+ *
778+ * This requires determining the exceptions declared by the
779+ * combined method, which must be (only) all of the exceptions
780+ * declared in both old Methods that may thrown in either of
781+ * them.
782+ */
783+ private Method mergeWith(Method other) {
784+ if (!getName().equals(other.getName()) ||
785+ !getType().equals(other.getType()))
786+ {
787+ throw new Error("attempt to merge method \"" +
788+ other.getNameAndDescriptor() + "\" with \"" +
789+ getNameAndDescriptor());
790+ }
791+
792+ Vector<ClassDeclaration> legalExceptions
793+ = new Vector<ClassDeclaration>();
794+ try {
795+ collectCompatibleExceptions(
796+ other.exceptions, exceptions, legalExceptions);
797+ collectCompatibleExceptions(
798+ exceptions, other.exceptions, legalExceptions);
799+ } catch (ClassNotFound e) {
800+ env.error(0, "class.not.found", e.name,
801+ getClassDefinition().getName());
802+ return null;
803+ }
804+
805+ Method merged = (Method) clone();
806+ merged.exceptions = new ClassDeclaration[legalExceptions.size()];
807+ legalExceptions.copyInto(merged.exceptions);
808+
809+ return merged;
810+ }
811+
812+ /**
813+ * Add to the supplied list all exceptions in the "from" array
814+ * that are subclasses of an exception in the "with" array.
815+ */
816+ private void collectCompatibleExceptions(ClassDeclaration[] from,
817+ ClassDeclaration[] with,
818+ Vector<ClassDeclaration> list)
819+ throws ClassNotFound
820+ {
821+ for (int i = 0; i < from.length; i++) {
822+ ClassDefinition exceptionDef = from[i].getClassDefinition(env);
823+ if (!list.contains(from[i])) {
824+ for (int j = 0; j < with.length; j++) {
825+ if (exceptionDef.subClassOf(env, with[j])) {
826+ list.addElement(from[i]);
827+ break;
828+ }
829+ }
830+ }
831+ }
832+ }
833+
834+ /**
835+ * Compute the "method hash" of this remote method. The method
836+ * hash is a long containing the first 64 bits of the SHA digest
837+ * from the UTF encoded string of the method name and descriptor.
838+ *
839+ * REMIND: Should this method share implementation code with
840+ * the outer class's computeInterfaceHash() method?
841+ */
842+ private long computeMethodHash() {
843+ long hash = 0;
844+ ByteArrayOutputStream sink = new ByteArrayOutputStream(512);
845+ try {
846+ MessageDigest md = MessageDigest.getInstance("SHA");
847+ DataOutputStream out = new DataOutputStream(
848+ new DigestOutputStream(sink, md));
849+
850+ String methodString = getNameAndDescriptor();
851+ /***** <DEBUG> */
852+ if (env.verbose()) {
853+ System.out.println("[string used for method hash: \"" +
854+ methodString + "\"]");
855+ }
856+ /***** </DEBUG> */
857+ out.writeUTF(methodString);
858+
859+ // use only the first 64 bits of the digest for the hash
860+ out.flush();
861+ byte hashArray[] = md.digest();
862+ for (int i = 0; i < Math.min(8, hashArray.length); i++) {
863+ hash += ((long) (hashArray[i] & 0xFF)) << (i * 8);
864+ }
865+ } catch (IOException e) {
866+ throw new Error(
867+ "unexpected exception computing intetrface hash: " + e);
868+ } catch (NoSuchAlgorithmException e) {
869+ throw new Error(
870+ "unexpected exception computing intetrface hash: " + e);
871+ }
872+
873+ return hash;
874+ }
875+ }
876+}