View Javadoc
1   /*
2    * Copyright (c) 1997, 2013, 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-qualified 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 }