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
26package sun.rmi.rmic;
27
28import java.util.Vector;
29import java.util.Hashtable;
30import java.util.Enumeration;
31import java.io.IOException;
32import java.io.ByteArrayOutputStream;
33import java.io.DataOutputStream;
34import java.security.MessageDigest;
35import java.security.DigestOutputStream;
36import java.security.NoSuchAlgorithmException;
37import sun.tools.java.Type;
38import sun.tools.java.ClassDefinition;
39import sun.tools.java.ClassDeclaration;
40import sun.tools.java.MemberDefinition;
41import sun.tools.java.Identifier;
42import 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 */
55public 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}