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 */ forClass(BatchEnvironment env, ClassDefinition implClassDef)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 */ getClassDefinition()80 public ClassDefinition getClassDefinition() { 81 return implClassDef; 82 } 83 84 /** 85 * Return the name of the class represented by this object. 86 */ getName()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 */ getRemoteInterfaces()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 */ getRemoteMethods()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 */ getInterfaceHash()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 */ toString()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 */ RemoteClass(BatchEnvironment env, ClassDefinition implClassDef)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 */ initialize()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 */ collectRemoteMethods(ClassDefinition interfaceDef, Hashtable<String, Method> table)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 */ computeInterfaceHash()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 */ sortClassDeclarations(ClassDeclaration[] decl)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 */ getMemberDefinition()659 public MemberDefinition getMemberDefinition() { 660 return memberDef; 661 } 662 663 /** 664 * Return the name of this method. 665 */ getName()666 public Identifier getName() { 667 return memberDef.getName(); 668 } 669 670 /** 671 * Return the type of this method. 672 */ getType()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 */ getExceptions()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 */ getMethodHash()694 public long getMethodHash() { 695 return methodHash; 696 } 697 698 /** 699 * Return the string representation of this method. 700 */ toString()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 */ getOperationString()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 */ getNameAndDescriptor()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 */ Method(MemberDefinition memberDef)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 */ clone()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 */ mergeWith(Method other)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 */ collectCompatibleExceptions(ClassDeclaration[] from, ClassDeclaration[] with, Vector<ClassDeclaration> list)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 */ computeMethodHash()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 } 877