1 /* 2 * Copyright (c) 1998, 2011, 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 * Licensed Materials - Property of IBM 27 * RMI-IIOP v1.0 28 * Copyright IBM Corp. 1998 1999 All Rights Reserved 29 * 30 */ 31 32 package com.sun.corba.se.impl.io; 33 34 import java.security.MessageDigest; 35 import java.security.NoSuchAlgorithmException; 36 import java.security.DigestOutputStream; 37 import java.security.AccessController; 38 import java.security.PrivilegedExceptionAction; 39 import java.security.PrivilegedActionException; 40 import java.security.PrivilegedAction; 41 42 import java.lang.reflect.Modifier; 43 import java.lang.reflect.Array; 44 import java.lang.reflect.Field; 45 import java.lang.reflect.Member; 46 import java.lang.reflect.Method; 47 import java.lang.reflect.Constructor; 48 import java.lang.reflect.Proxy; 49 import java.lang.reflect.InvocationTargetException; 50 51 import java.io.IOException; 52 import java.io.DataOutputStream; 53 import java.io.ByteArrayOutputStream; 54 import java.io.InvalidClassException; 55 import java.io.Serializable; 56 57 import java.util.Arrays; 58 import java.util.Comparator; 59 import java.util.Hashtable; 60 61 import com.sun.corba.se.impl.util.RepositoryId; 62 63 import org.omg.CORBA.ValueMember; 64 65 import sun.corba.Bridge; 66 67 /** 68 * A ObjectStreamClass describes a class that can be serialized to a stream 69 * or a class that was serialized to a stream. It contains the name 70 * and the serialVersionUID of the class. 71 * <br> 72 * The ObjectStreamClass for a specific class loaded in this Java VM can 73 * be found using the lookup method. 74 * 75 * @author Roger Riggs 76 * @since JDK1.1 77 */ 78 public class ObjectStreamClass implements java.io.Serializable { 79 private static final boolean DEBUG_SVUID = false ; 80 81 public static final long kDefaultUID = -1; 82 83 private static Object noArgsList[] = {}; 84 private static Class noTypesList[] = {}; 85 86 private static Hashtable translatedFields; 87 88 /** true if represents enum type */ 89 private boolean isEnum; 90 91 private static final Bridge bridge = 92 (Bridge)AccessController.doPrivileged( 93 new PrivilegedAction() { 94 public Object run() { 95 return Bridge.get() ; 96 } 97 } 98 ) ; 99 100 /** Find the descriptor for a class that can be serialized. Null 101 * is returned if the specified class does not implement 102 * java.io.Serializable or java.io.Externalizable. 103 */ 104 static final ObjectStreamClass lookup(Class cl) 105 { 106 ObjectStreamClass desc = lookupInternal(cl); 107 if (desc.isSerializable() || desc.isExternalizable()) 108 return desc; 109 return null; 110 } 111 112 /* 113 * Find the class descriptor for the specified class. 114 * Package access only so it can be called from ObjectIn/OutStream. 115 */ 116 static ObjectStreamClass lookupInternal(Class cl) 117 { 118 /* Synchronize on the hashtable so no two threads will do 119 * this at the same time. 120 */ 121 ObjectStreamClass desc = null; 122 synchronized (descriptorFor) { 123 /* Find the matching descriptor if it already known */ 124 desc = findDescriptorFor(cl); 125 if (desc == null) { 126 /* Check if it's serializable */ 127 boolean serializable = classSerializable.isAssignableFrom(cl); 128 129 /* If the class is only Serializable, 130 * lookup the descriptor for the superclass. 131 */ 132 ObjectStreamClass superdesc = null; 133 if (serializable) { 134 Class superclass = cl.getSuperclass(); 135 if (superclass != null) 136 superdesc = lookup(superclass); 137 } 138 139 /* Check if its' externalizable. 140 * If it's Externalizable, clear the serializable flag. 141 * Only one or the other may be set in the protocol. 142 */ 143 boolean externalizable = false; 144 if (serializable) { 145 externalizable = 146 ((superdesc != null) && superdesc.isExternalizable()) || 147 classExternalizable.isAssignableFrom(cl); 148 if (externalizable) { 149 serializable = false; 150 } 151 } 152 153 /* Create a new version descriptor, 154 * it put itself in the known table. 155 */ 156 desc = new ObjectStreamClass(cl, superdesc, 157 serializable, externalizable); 158 } 159 // Must always call init. See bug 4488137. This code was 160 // incorrectly changed to return immediately on a non-null 161 // cache result. That allowed threads to gain access to 162 // unintialized instances. 163 // 164 // History: Note, the following init() call was originally within 165 // the synchronization block, as it currently is now. Later, the 166 // init() call was moved outside the synchronization block, and 167 // the init() method used a private member variable lock, to 168 // avoid performance problems. See bug 4165204. But that lead to 169 // a deadlock situation, see bug 5104239. Hence, the init() method 170 // has now been moved back into the synchronization block. The 171 // right approach to solving these problems would be to rewrite 172 // this class, based on the latest java.io.ObjectStreamClass. 173 desc.init(); 174 } 175 return desc; 176 } 177 178 /** 179 * The name of the class described by this descriptor. 180 */ 181 public final String getName() { 182 return name; 183 } 184 185 /** 186 * Return the serialVersionUID for this class. 187 * The serialVersionUID defines a set of classes all with the same name 188 * that have evolved from a common root class and agree to be serialized 189 * and deserialized using a common format. 190 */ 191 public static final long getSerialVersionUID( java.lang.Class clazz) { 192 ObjectStreamClass theosc = ObjectStreamClass.lookup( clazz ); 193 if( theosc != null ) 194 { 195 return theosc.getSerialVersionUID( ); 196 } 197 return 0; 198 } 199 200 /** 201 * Return the serialVersionUID for this class. 202 * The serialVersionUID defines a set of classes all with the same name 203 * that have evolved from a common root class and agree to be serialized 204 * and deserialized using a common format. 205 */ 206 public final long getSerialVersionUID() { 207 return suid; 208 } 209 210 /** 211 * Return the serialVersionUID string for this class. 212 * The serialVersionUID defines a set of classes all with the same name 213 * that have evolved from a common root class and agree to be serialized 214 * and deserialized using a common format. 215 */ 216 public final String getSerialVersionUIDStr() { 217 if (suidStr == null) 218 suidStr = Long.toHexString(suid).toUpperCase(); 219 return suidStr; 220 } 221 222 /** 223 * Return the actual (computed) serialVersionUID for this class. 224 */ 225 public static final long getActualSerialVersionUID( java.lang.Class clazz ) 226 { 227 ObjectStreamClass theosc = ObjectStreamClass.lookup( clazz ); 228 if( theosc != null ) 229 { 230 return theosc.getActualSerialVersionUID( ); 231 } 232 return 0; 233 } 234 235 /** 236 * Return the actual (computed) serialVersionUID for this class. 237 */ 238 public final long getActualSerialVersionUID() { 239 return actualSuid; 240 } 241 242 /** 243 * Return the actual (computed) serialVersionUID for this class. 244 */ 245 public final String getActualSerialVersionUIDStr() { 246 if (actualSuidStr == null) 247 actualSuidStr = Long.toHexString(actualSuid).toUpperCase(); 248 return actualSuidStr; 249 } 250 251 /** 252 * Return the class in the local VM that this version is mapped to. 253 * Null is returned if there is no corresponding local class. 254 */ 255 public final Class forClass() { 256 return ofClass; 257 } 258 259 /** 260 * Return an array of the fields of this serializable class. 261 * @return an array containing an element for each persistent 262 * field of this class. Returns an array of length zero if 263 * there are no fields. 264 * @since JDK1.2 265 */ 266 public ObjectStreamField[] getFields() { 267 // Return a copy so the caller can't change the fields. 268 if (fields.length > 0) { 269 ObjectStreamField[] dup = new ObjectStreamField[fields.length]; 270 System.arraycopy(fields, 0, dup, 0, fields.length); 271 return dup; 272 } else { 273 return fields; 274 } 275 } 276 277 public boolean hasField(ValueMember field) 278 { 279 try { 280 for (int i = 0; i < fields.length; i++) { 281 if (fields[i].getName().equals(field.name)) { 282 if (fields[i].getSignature().equals( 283 ValueUtility.getSignature(field))) 284 return true; 285 } 286 } 287 } catch (Exception exc) { 288 // Ignore this; all we want to do is return false 289 // Note that ValueUtility.getSignature can throw checked exceptions. 290 } 291 292 return false; 293 } 294 295 /* Avoid unnecessary allocations. */ 296 final ObjectStreamField[] getFieldsNoCopy() { 297 return fields; 298 } 299 300 /** 301 * Get the field of this class by name. 302 * @return The ObjectStreamField object of the named field or null if there 303 * is no such named field. 304 */ 305 public final ObjectStreamField getField(String name) { 306 /* Binary search of fields by name. 307 */ 308 for (int i = fields.length-1; i >= 0; i--) { 309 if (name.equals(fields[i].getName())) { 310 return fields[i]; 311 } 312 } 313 return null; 314 } 315 316 public Serializable writeReplace(Serializable value) { 317 if (writeReplaceObjectMethod != null) { 318 try { 319 return (Serializable) writeReplaceObjectMethod.invoke(value,noArgsList); 320 } catch(Throwable t) { 321 throw new RuntimeException(t); 322 } 323 } 324 else return value; 325 } 326 327 public Object readResolve(Object value) { 328 if (readResolveObjectMethod != null) { 329 try { 330 return readResolveObjectMethod.invoke(value,noArgsList); 331 } catch(Throwable t) { 332 throw new RuntimeException(t); 333 } 334 } 335 else return value; 336 } 337 338 /** 339 * Return a string describing this ObjectStreamClass. 340 */ 341 public final String toString() { 342 StringBuffer sb = new StringBuffer(); 343 344 sb.append(name); 345 sb.append(": static final long serialVersionUID = "); 346 sb.append(Long.toString(suid)); 347 sb.append("L;"); 348 return sb.toString(); 349 } 350 351 /* 352 * Create a new ObjectStreamClass from a loaded class. 353 * Don't call this directly, call lookup instead. 354 */ 355 private ObjectStreamClass(java.lang.Class cl, ObjectStreamClass superdesc, 356 boolean serial, boolean extern) 357 { 358 ofClass = cl; /* created from this class */ 359 360 if (Proxy.isProxyClass(cl)) { 361 forProxyClass = true; 362 } 363 364 name = cl.getName(); 365 isEnum = Enum.class.isAssignableFrom(cl); 366 superclass = superdesc; 367 serializable = serial; 368 if (!forProxyClass) { 369 // proxy classes are never externalizable 370 externalizable = extern; 371 } 372 373 /* 374 * Enter this class in the table of known descriptors. 375 * Otherwise, when the fields are read it may recurse 376 * trying to find the descriptor for itself. 377 */ 378 insertDescriptorFor(this); 379 380 /* 381 * The remainder of initialization occurs in init(), which is called 382 * after the lock on the global class descriptor table has been 383 * released. 384 */ 385 } 386 387 /* 388 * Initialize class descriptor. This method is only invoked on class 389 * descriptors created via calls to lookupInternal(). This method is kept 390 * separate from the ObjectStreamClass constructor so that lookupInternal 391 * does not have to hold onto a global class descriptor table lock while the 392 * class descriptor is being initialized (see bug 4165204). 393 */ 394 395 396 private void init() { 397 synchronized (lock) { 398 399 // See description at definition of initialized. 400 if (initialized) 401 return; 402 403 final Class cl = ofClass; 404 405 if (!serializable || 406 externalizable || 407 forProxyClass || 408 name.equals("java.lang.String")){ 409 fields = NO_FIELDS; 410 } else if (serializable) { 411 /* Ask for permission to override field access checks. 412 */ 413 AccessController.doPrivileged(new PrivilegedAction() { 414 public Object run() { 415 /* Fill in the list of persistent fields. 416 * If it is declared, use the declared serialPersistentFields. 417 * Otherwise, extract the fields from the class itself. 418 */ 419 try { 420 Field pf = cl.getDeclaredField("serialPersistentFields"); 421 // serial bug 7; the serialPersistentFields were not 422 // being read and stored as Accessible bit was not set 423 pf.setAccessible(true); 424 // serial bug 7; need to find if the field is of type 425 // java.io.ObjectStreamField 426 java.io.ObjectStreamField[] f = 427 (java.io.ObjectStreamField[])pf.get(cl); 428 int mods = pf.getModifiers(); 429 if ((Modifier.isPrivate(mods)) && 430 (Modifier.isStatic(mods)) && 431 (Modifier.isFinal(mods))) 432 { 433 fields = (ObjectStreamField[])translateFields((Object[])pf.get(cl)); 434 } 435 } catch (NoSuchFieldException e) { 436 fields = null; 437 } catch (IllegalAccessException e) { 438 fields = null; 439 } catch (IllegalArgumentException e) { 440 fields = null; 441 } catch (ClassCastException e) { 442 /* Thrown if a field serialPersistentField exists 443 * but it is not of type ObjectStreamField. 444 */ 445 fields = null; 446 } 447 448 449 if (fields == null) { 450 /* Get all of the declared fields for this 451 * Class. setAccessible on all fields so they 452 * can be accessed later. Create a temporary 453 * ObjectStreamField array to hold each 454 * non-static, non-transient field. Then copy the 455 * temporary array into an array of the correct 456 * size once the number of fields is known. 457 */ 458 Field[] actualfields = cl.getDeclaredFields(); 459 460 int numFields = 0; 461 ObjectStreamField[] tempFields = 462 new ObjectStreamField[actualfields.length]; 463 for (int i = 0; i < actualfields.length; i++) { 464 Field fld = actualfields[i] ; 465 int modifiers = fld.getModifiers(); 466 if (!Modifier.isStatic(modifiers) && 467 !Modifier.isTransient(modifiers)) { 468 fld.setAccessible(true) ; 469 tempFields[numFields++] = new ObjectStreamField(fld); 470 } 471 } 472 473 fields = new ObjectStreamField[numFields]; 474 System.arraycopy(tempFields, 0, fields, 0, numFields); 475 476 } else { 477 // For each declared persistent field, look for an actual 478 // reflected Field. If there is one, make sure it's the correct 479 // type and cache it in the ObjectStreamClass for that field. 480 for (int j = fields.length-1; j >= 0; j--) { 481 try { 482 Field reflField = cl.getDeclaredField(fields[j].getName()); 483 if (fields[j].getType() == reflField.getType()) { 484 reflField.setAccessible(true); 485 fields[j].setField(reflField); 486 } 487 } catch (NoSuchFieldException e) { 488 // Nothing to do 489 } 490 } 491 } 492 return null; 493 } 494 }); 495 496 if (fields.length > 1) 497 Arrays.sort(fields); 498 499 /* Set up field data for use while writing using the API api. */ 500 computeFieldInfo(); 501 } 502 503 /* Get the serialVersionUID from the class. 504 * It uses the access override mechanism so make sure 505 * the field objects is only used here. 506 * 507 * NonSerializable classes have a serialVerisonUID of 0L. 508 */ 509 if (isNonSerializable() || isEnum) { 510 suid = 0L; 511 } else { 512 // Lookup special Serializable members using reflection. 513 AccessController.doPrivileged(new PrivilegedAction() { 514 public Object run() { 515 if (forProxyClass) { 516 // proxy classes always have serialVersionUID of 0L 517 suid = 0L; 518 } else { 519 try { 520 final Field f = cl.getDeclaredField("serialVersionUID"); 521 int mods = f.getModifiers(); 522 // SerialBug 5: static final SUID should be read 523 if (Modifier.isStatic(mods) && Modifier.isFinal(mods) ) { 524 f.setAccessible(true); 525 suid = f.getLong(cl); 526 // SerialBug 2: should be computed after writeObject 527 // actualSuid = computeStructuralUID(cl); 528 } else { 529 suid = _computeSerialVersionUID(cl); 530 // SerialBug 2: should be computed after writeObject 531 // actualSuid = computeStructuralUID(cl); 532 } 533 } catch (NoSuchFieldException ex) { 534 suid = _computeSerialVersionUID(cl); 535 // SerialBug 2: should be computed after writeObject 536 // actualSuid = computeStructuralUID(cl); 537 } catch (IllegalAccessException ex) { 538 suid = _computeSerialVersionUID(cl); 539 } 540 } 541 542 writeReplaceObjectMethod = ObjectStreamClass.getInheritableMethod(cl, 543 "writeReplace", noTypesList, Object.class); 544 545 readResolveObjectMethod = ObjectStreamClass.getInheritableMethod(cl, 546 "readResolve", noTypesList, Object.class); 547 548 if (externalizable) 549 cons = getExternalizableConstructor(cl) ; 550 else 551 cons = getSerializableConstructor(cl) ; 552 553 if (serializable && !forProxyClass) { 554 /* Look for the writeObject method 555 * Set the accessible flag on it here. ObjectOutputStream 556 * will call it as necessary. 557 */ 558 writeObjectMethod = getPrivateMethod( cl, "writeObject", 559 new Class[] { java.io.ObjectOutputStream.class }, Void.TYPE ) ; 560 readObjectMethod = getPrivateMethod( cl, "readObject", 561 new Class[] { java.io.ObjectInputStream.class }, Void.TYPE ) ; 562 } 563 return null; 564 } 565 }); 566 } 567 568 // This call depends on a lot of information computed above! 569 actualSuid = ObjectStreamClass.computeStructuralUID(this, cl); 570 571 // If we have a write object method, precompute the 572 // RMI-IIOP stream format version 2 optional data 573 // repository ID. 574 if (hasWriteObject()) 575 rmiiiopOptionalDataRepId = computeRMIIIOPOptionalDataRepId(); 576 577 // This must be done last. 578 initialized = true; 579 } 580 } 581 582 /** 583 * Returns non-static private method with given signature defined by given 584 * class, or null if none found. Access checks are disabled on the 585 * returned method (if any). 586 */ 587 private static Method getPrivateMethod(Class cl, String name, 588 Class[] argTypes, 589 Class returnType) 590 { 591 try { 592 Method meth = cl.getDeclaredMethod(name, argTypes); 593 meth.setAccessible(true); 594 int mods = meth.getModifiers(); 595 return ((meth.getReturnType() == returnType) && 596 ((mods & Modifier.STATIC) == 0) && 597 ((mods & Modifier.PRIVATE) != 0)) ? meth : null; 598 } catch (NoSuchMethodException ex) { 599 return null; 600 } 601 } 602 603 // Specific to RMI-IIOP 604 /** 605 * Java to IDL ptc-02-01-12 1.5.1 606 * 607 * "The rep_id string passed to the start_value method must be 608 * 'RMI:org.omg.custom.class:hashcode:suid' where class is the 609 * fully-qualified name of the class whose writeObject method 610 * is being invoked and hashcode and suid are the class's hashcode 611 * and SUID." 612 */ 613 private String computeRMIIIOPOptionalDataRepId() { 614 615 StringBuffer sbuf = new StringBuffer("RMI:org.omg.custom."); 616 sbuf.append(RepositoryId.convertToISOLatin1(this.getName())); 617 sbuf.append(':'); 618 sbuf.append(this.getActualSerialVersionUIDStr()); 619 sbuf.append(':'); 620 sbuf.append(this.getSerialVersionUIDStr()); 621 622 return sbuf.toString(); 623 } 624 625 /** 626 * This will return null if there is no writeObject method. 627 */ 628 public final String getRMIIIOPOptionalDataRepId() { 629 return rmiiiopOptionalDataRepId; 630 } 631 632 /* 633 * Create an empty ObjectStreamClass for a class about to be read. 634 * This is separate from read so ObjectInputStream can assign the 635 * wire handle early, before any nested ObjectStreamClass might 636 * be read. 637 */ 638 ObjectStreamClass(String n, long s) { 639 name = n; 640 suid = s; 641 superclass = null; 642 } 643 644 private static Object[] translateFields(Object objs[]) 645 throws NoSuchFieldException { 646 try{ 647 java.io.ObjectStreamField fields[] = (java.io.ObjectStreamField[])objs; 648 Object translation[] = null; 649 650 if (translatedFields == null) 651 translatedFields = new Hashtable(); 652 653 translation = (Object[])translatedFields.get(fields); 654 655 if (translation != null) 656 return translation; 657 else { 658 Class osfClass = Class.forName("com.sun.corba.se.impl.io.ObjectStreamField"); 659 translation = (Object[])java.lang.reflect.Array.newInstance(osfClass, objs.length); 660 Object arg[] = new Object[2]; 661 Class types[] = {String.class, Class.class}; 662 Constructor constructor = osfClass.getDeclaredConstructor(types); 663 for (int i = fields.length -1; i >= 0; i--){ 664 arg[0] = fields[i].getName(); 665 arg[1] = fields[i].getType(); 666 667 translation[i] = constructor.newInstance(arg); 668 } 669 translatedFields.put(fields, translation); 670 671 } 672 673 return (Object[])translation; 674 } 675 catch(Throwable t){ 676 NoSuchFieldException nsfe = new NoSuchFieldException(); 677 nsfe.initCause( t ) ; 678 throw nsfe ; 679 } 680 } 681 682 /* 683 * Set the class this version descriptor matches. 684 * The base class name and serializable hash must match. 685 * Fill in the reflected Fields that will be used 686 * for reading. 687 */ 688 final void setClass(Class cl) throws InvalidClassException { 689 690 if (cl == null) { 691 localClassDesc = null; 692 ofClass = null; 693 computeFieldInfo(); 694 return; 695 } 696 697 localClassDesc = lookupInternal(cl); 698 if (localClassDesc == null) 699 // XXX I18N, logging needed 700 throw new InvalidClassException(cl.getName(), 701 "Local class not compatible"); 702 if (suid != localClassDesc.suid) { 703 704 /* Check for exceptional cases that allow mismatched suid. */ 705 706 /* Allow adding Serializable or Externalizable 707 * to a later release of the class. 708 */ 709 boolean addedSerialOrExtern = 710 isNonSerializable() || localClassDesc.isNonSerializable(); 711 712 /* Disregard the serialVersionUID of an array 713 * when name and cl.Name differ. If resolveClass() returns 714 * an array with a different package name, 715 * the serialVersionUIDs will not match since the fully 716 * qualified array class is used in the 717 * computation of the array's serialVersionUID. There is 718 * no way to set a permanent serialVersionUID for an array type. 719 */ 720 721 boolean arraySUID = (cl.isArray() && ! cl.getName().equals(name)); 722 723 if (! arraySUID && ! addedSerialOrExtern ) { 724 // XXX I18N, logging needed 725 throw new InvalidClassException(cl.getName(), 726 "Local class not compatible:" + 727 " stream classdesc serialVersionUID=" + suid + 728 " local class serialVersionUID=" + localClassDesc.suid); 729 } 730 } 731 732 /* compare the class names, stripping off package names. */ 733 if (! compareClassNames(name, cl.getName(), '.')) 734 // XXX I18N, logging needed 735 throw new InvalidClassException(cl.getName(), 736 "Incompatible local class name. " + 737 "Expected class name compatible with " + 738 name); 739 740 /* 741 * Test that both implement either serializable or externalizable. 742 */ 743 744 // The next check is more generic, since it covers the 745 // Proxy case, the JDK 1.3 serialization code has 746 // both checks 747 //if ((serializable && localClassDesc.externalizable) || 748 // (externalizable && localClassDesc.serializable)) 749 // throw new InvalidClassException(localCl.getName(), 750 // "Serializable is incompatible with Externalizable"); 751 752 if ((serializable != localClassDesc.serializable) || 753 (externalizable != localClassDesc.externalizable) || 754 (!serializable && !externalizable)) 755 756 // XXX I18N, logging needed 757 throw new InvalidClassException(cl.getName(), 758 "Serialization incompatible with Externalization"); 759 760 /* Set up the reflected Fields in the class where the value of each 761 * field in this descriptor should be stored. 762 * Each field in this ObjectStreamClass (the source) is located (by 763 * name) in the ObjectStreamClass of the class(the destination). 764 * In the usual (non-versioned case) the field is in both 765 * descriptors and the types match, so the reflected Field is copied. 766 * If the type does not match, a InvalidClass exception is thrown. 767 * If the field is not present in the class, the reflected Field 768 * remains null so the field will be read but discarded. 769 * If extra fields are present in the class they are ignored. Their 770 * values will be set to the default value by the object allocator. 771 * Both the src and dest field list are sorted by type and name. 772 */ 773 774 ObjectStreamField[] destfield = 775 (ObjectStreamField[])localClassDesc.fields; 776 ObjectStreamField[] srcfield = 777 (ObjectStreamField[])fields; 778 779 int j = 0; 780 nextsrc: 781 for (int i = 0; i < srcfield.length; i++ ) { 782 /* Find this field in the dest*/ 783 for (int k = j; k < destfield.length; k++) { 784 if (srcfield[i].getName().equals(destfield[k].getName())) { 785 /* found match */ 786 if (srcfield[i].isPrimitive() && 787 !srcfield[i].typeEquals(destfield[k])) { 788 // XXX I18N, logging needed 789 throw new InvalidClassException(cl.getName(), 790 "The type of field " + 791 srcfield[i].getName() + 792 " of class " + name + 793 " is incompatible."); 794 } 795 796 /* Skip over any fields in the dest that are not in the src */ 797 j = k; 798 799 srcfield[i].setField(destfield[j].getField()); 800 // go on to the next source field 801 continue nextsrc; 802 } 803 } 804 } 805 806 /* Set up field data for use while reading from the input stream. */ 807 computeFieldInfo(); 808 809 /* Remember the class this represents */ 810 ofClass = cl; 811 812 /* get the cache of these methods from the local class 813 * implementation. 814 */ 815 readObjectMethod = localClassDesc.readObjectMethod; 816 readResolveObjectMethod = localClassDesc.readResolveObjectMethod; 817 } 818 819 /* Compare the base class names of streamName and localName. 820 * 821 * @return Return true iff the base class name compare. 822 * @parameter streamName Fully qualified class name. 823 * @parameter localName Fully qualified class name. 824 * @parameter pkgSeparator class names use either '.' or '/'. 825 * 826 * Only compare base class name to allow package renaming. 827 */ 828 static boolean compareClassNames(String streamName, 829 String localName, 830 char pkgSeparator) { 831 /* compare the class names, stripping off package names. */ 832 int streamNameIndex = streamName.lastIndexOf(pkgSeparator); 833 if (streamNameIndex < 0) 834 streamNameIndex = 0; 835 836 int localNameIndex = localName.lastIndexOf(pkgSeparator); 837 if (localNameIndex < 0) 838 localNameIndex = 0; 839 840 return streamName.regionMatches(false, streamNameIndex, 841 localName, localNameIndex, 842 streamName.length() - streamNameIndex); 843 } 844 845 /* 846 * Compare the types of two class descriptors. 847 * They match if they have the same class name and suid 848 */ 849 final boolean typeEquals(ObjectStreamClass other) { 850 return (suid == other.suid) && 851 compareClassNames(name, other.name, '.'); 852 } 853 854 /* 855 * Return the superclass descriptor of this descriptor. 856 */ 857 final void setSuperclass(ObjectStreamClass s) { 858 superclass = s; 859 } 860 861 /* 862 * Return the superclass descriptor of this descriptor. 863 */ 864 final ObjectStreamClass getSuperclass() { 865 return superclass; 866 } 867 868 /** 869 * Return whether the class has a readObject method 870 */ 871 final boolean hasReadObject() { 872 return readObjectMethod != null; 873 } 874 875 /* 876 * Return whether the class has a writeObject method 877 */ 878 final boolean hasWriteObject() { 879 return writeObjectMethod != null ; 880 } 881 882 /** 883 * Returns when or not this class should be custom 884 * marshaled (use chunking). This should happen if 885 * it is Externalizable OR if it or 886 * any of its superclasses has a writeObject method, 887 */ 888 final boolean isCustomMarshaled() { 889 return (hasWriteObject() || isExternalizable()) 890 || (superclass != null && superclass.isCustomMarshaled()); 891 } 892 893 /* 894 * Return true if all instances of 'this' Externalizable class 895 * are written in block-data mode from the stream that 'this' was read 896 * from. <p> 897 * 898 * In JDK 1.1, all Externalizable instances are not written 899 * in block-data mode. 900 * In JDK 1.2, all Externalizable instances, by default, are written 901 * in block-data mode and the Externalizable instance is terminated with 902 * tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable 903 * instances. 904 * 905 * IMPLEMENTATION NOTE: 906 * This should have been a mode maintained per stream; however, 907 * for compatibility reasons, it was only possible to record 908 * this change per class. All Externalizable classes within 909 * a given stream should either have this mode enabled or 910 * disabled. This is enforced by not allowing the PROTOCOL_VERSION 911 * of a stream to he changed after any objects have been written. 912 * 913 * @see ObjectOutputStream#useProtocolVersion 914 * @see ObjectStreamConstants#PROTOCOL_VERSION_1 915 * @see ObjectStreamConstants#PROTOCOL_VERSION_2 916 * 917 * @since JDK 1.2 918 */ 919 boolean hasExternalizableBlockDataMode() { 920 return hasExternalizableBlockData; 921 } 922 923 /** 924 * Creates a new instance of the represented class. If the class is 925 * externalizable, invokes its public no-arg constructor; otherwise, if the 926 * class is serializable, invokes the no-arg constructor of the first 927 * non-serializable superclass. Throws UnsupportedOperationException if 928 * this class descriptor is not associated with a class, if the associated 929 * class is non-serializable or if the appropriate no-arg constructor is 930 * inaccessible/unavailable. 931 */ 932 Object newInstance() 933 throws InstantiationException, InvocationTargetException, 934 UnsupportedOperationException 935 { 936 if (cons != null) { 937 try { 938 return cons.newInstance(new Object[0]); 939 } catch (IllegalAccessException ex) { 940 // should not occur, as access checks have been suppressed 941 InternalError ie = new InternalError(); 942 ie.initCause( ex ) ; 943 throw ie ; 944 } 945 } else { 946 throw new UnsupportedOperationException(); 947 } 948 } 949 950 /** 951 * Returns public no-arg constructor of given class, or null if none found. 952 * Access checks are disabled on the returned constructor (if any), since 953 * the defining class may still be non-public. 954 */ 955 private static Constructor getExternalizableConstructor(Class cl) { 956 try { 957 Constructor cons = cl.getDeclaredConstructor(new Class[0]); 958 cons.setAccessible(true); 959 return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ? 960 cons : null; 961 } catch (NoSuchMethodException ex) { 962 return null; 963 } 964 } 965 966 /** 967 * Returns subclass-accessible no-arg constructor of first non-serializable 968 * superclass, or null if none found. Access checks are disabled on the 969 * returned constructor (if any). 970 */ 971 private static Constructor getSerializableConstructor(Class cl) { 972 Class initCl = cl; 973 while (Serializable.class.isAssignableFrom(initCl)) { 974 if ((initCl = initCl.getSuperclass()) == null) { 975 return null; 976 } 977 } 978 try { 979 Constructor cons = initCl.getDeclaredConstructor(new Class[0]); 980 int mods = cons.getModifiers(); 981 if ((mods & Modifier.PRIVATE) != 0 || 982 ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0 && 983 !packageEquals(cl, initCl))) 984 { 985 return null; 986 } 987 cons = bridge.newConstructorForSerialization(cl, cons); 988 cons.setAccessible(true); 989 return cons; 990 } catch (NoSuchMethodException ex) { 991 return null; 992 } 993 } 994 995 /* 996 * Return the ObjectStreamClass of the local class this one is based on. 997 */ 998 final ObjectStreamClass localClassDescriptor() { 999 return localClassDesc; 1000 } 1001 1002 /* 1003 * Get the Serializability of the class. 1004 */ 1005 boolean isSerializable() { 1006 return serializable; 1007 } 1008 1009 /* 1010 * Get the externalizability of the class. 1011 */ 1012 boolean isExternalizable() { 1013 return externalizable; 1014 } 1015 1016 boolean isNonSerializable() { 1017 return ! (externalizable || serializable); 1018 } 1019 1020 /* 1021 * Calculate the size of the array needed to store primitive data and the 1022 * number of object references to read when reading from the input 1023 * stream. 1024 */ 1025 private void computeFieldInfo() { 1026 primBytes = 0; 1027 objFields = 0; 1028 1029 for (int i = 0; i < fields.length; i++ ) { 1030 switch (fields[i].getTypeCode()) { 1031 case 'B': 1032 case 'Z': 1033 primBytes += 1; 1034 break; 1035 case 'C': 1036 case 'S': 1037 primBytes += 2; 1038 break; 1039 1040 case 'I': 1041 case 'F': 1042 primBytes += 4; 1043 break; 1044 case 'J': 1045 case 'D' : 1046 primBytes += 8; 1047 break; 1048 1049 case 'L': 1050 case '[': 1051 objFields += 1; 1052 break; 1053 } 1054 } 1055 } 1056 1057 private static void msg( String str ) 1058 { 1059 System.out.println( str ) ; 1060 } 1061 1062 /* JDK 1.5 has introduced some new modifier bits (such as SYNTHETIC) 1063 * that can affect the SVUID computation (see bug 4897937). These bits 1064 * must be ignored, as otherwise interoperability with ORBs in earlier 1065 * JDK versions can be compromised. I am adding these masks for this 1066 * purpose as discussed in the CCC for this bug (see http://ccc.sfbay/4897937). 1067 */ 1068 1069 public static final int CLASS_MASK = Modifier.PUBLIC | Modifier.FINAL | 1070 Modifier.INTERFACE | Modifier.ABSTRACT ; 1071 public static final int FIELD_MASK = Modifier.PUBLIC | Modifier.PRIVATE | 1072 Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | 1073 Modifier.TRANSIENT | Modifier.VOLATILE ; 1074 public static final int METHOD_MASK = Modifier.PUBLIC | Modifier.PRIVATE | 1075 Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | 1076 Modifier.SYNCHRONIZED | Modifier.NATIVE | Modifier.ABSTRACT | 1077 Modifier.STRICT ; 1078 1079 /* 1080 * Compute a hash for the specified class. Incrementally add 1081 * items to the hash accumulating in the digest stream. 1082 * Fold the hash into a long. Use the SHA secure hash function. 1083 */ 1084 private static long _computeSerialVersionUID(Class cl) { 1085 if (DEBUG_SVUID) 1086 msg( "Computing SerialVersionUID for " + cl ) ; 1087 ByteArrayOutputStream devnull = new ByteArrayOutputStream(512); 1088 1089 long h = 0; 1090 try { 1091 MessageDigest md = MessageDigest.getInstance("SHA"); 1092 DigestOutputStream mdo = new DigestOutputStream(devnull, md); 1093 DataOutputStream data = new DataOutputStream(mdo); 1094 1095 if (DEBUG_SVUID) 1096 msg( "\twriteUTF( \"" + cl.getName() + "\" )" ) ; 1097 data.writeUTF(cl.getName()); 1098 1099 int classaccess = cl.getModifiers(); 1100 classaccess &= (Modifier.PUBLIC | Modifier.FINAL | 1101 Modifier.INTERFACE | Modifier.ABSTRACT); 1102 1103 /* Workaround for javac bug that only set ABSTRACT for 1104 * interfaces if the interface had some methods. 1105 * The ABSTRACT bit reflects that the number of methods > 0. 1106 * This is required so correct hashes can be computed 1107 * for existing class files. 1108 * Previously this hack was previously present in the VM. 1109 */ 1110 Method[] method = cl.getDeclaredMethods(); 1111 if ((classaccess & Modifier.INTERFACE) != 0) { 1112 classaccess &= (~Modifier.ABSTRACT); 1113 if (method.length > 0) { 1114 classaccess |= Modifier.ABSTRACT; 1115 } 1116 } 1117 1118 // Mask out any post-1.4 attributes 1119 classaccess &= CLASS_MASK ; 1120 1121 if (DEBUG_SVUID) 1122 msg( "\twriteInt( " + classaccess + " ) " ) ; 1123 data.writeInt(classaccess); 1124 1125 /* 1126 * Get the list of interfaces supported, 1127 * Accumulate their names their names in Lexical order 1128 * and add them to the hash 1129 */ 1130 if (!cl.isArray()) { 1131 /* In 1.2fcs, getInterfaces() was modified to return 1132 * {java.lang.Cloneable, java.io.Serializable} when 1133 * called on array classes. These values would upset 1134 * the computation of the hash, so we explicitly omit 1135 * them from its computation. 1136 */ 1137 1138 Class interfaces[] = cl.getInterfaces(); 1139 Arrays.sort(interfaces, compareClassByName); 1140 1141 for (int i = 0; i < interfaces.length; i++) { 1142 if (DEBUG_SVUID) 1143 msg( "\twriteUTF( \"" + interfaces[i].getName() + "\" ) " ) ; 1144 data.writeUTF(interfaces[i].getName()); 1145 } 1146 } 1147 1148 /* Sort the field names to get a deterministic order */ 1149 Field[] field = cl.getDeclaredFields(); 1150 Arrays.sort(field, compareMemberByName); 1151 1152 for (int i = 0; i < field.length; i++) { 1153 Field f = field[i]; 1154 1155 /* Include in the hash all fields except those that are 1156 * private transient and private static. 1157 */ 1158 int m = f.getModifiers(); 1159 if (Modifier.isPrivate(m) && 1160 (Modifier.isTransient(m) || Modifier.isStatic(m))) 1161 continue; 1162 1163 if (DEBUG_SVUID) 1164 msg( "\twriteUTF( \"" + f.getName() + "\" ) " ) ; 1165 data.writeUTF(f.getName()); 1166 1167 // Mask out any post-1.4 bits 1168 m &= FIELD_MASK ; 1169 1170 if (DEBUG_SVUID) 1171 msg( "\twriteInt( " + m + " ) " ) ; 1172 data.writeInt(m); 1173 1174 if (DEBUG_SVUID) 1175 msg( "\twriteUTF( \"" + getSignature(f.getType()) + "\" ) " ) ; 1176 data.writeUTF(getSignature(f.getType())); 1177 } 1178 1179 if (hasStaticInitializer(cl)) { 1180 if (DEBUG_SVUID) 1181 msg( "\twriteUTF( \"<clinit>\" ) " ) ; 1182 data.writeUTF("<clinit>"); 1183 1184 if (DEBUG_SVUID) 1185 msg( "\twriteInt( " + Modifier.STATIC + " )" ) ; 1186 data.writeInt(Modifier.STATIC); // TBD: what modifiers does it have 1187 1188 if (DEBUG_SVUID) 1189 msg( "\twriteUTF( \"()V\" )" ) ; 1190 data.writeUTF("()V"); 1191 } 1192 1193 /* 1194 * Get the list of constructors including name and signature 1195 * Sort lexically, add all except the private constructors 1196 * to the hash with their access flags 1197 */ 1198 1199 MethodSignature[] constructors = 1200 MethodSignature.removePrivateAndSort(cl.getDeclaredConstructors()); 1201 for (int i = 0; i < constructors.length; i++) { 1202 MethodSignature c = constructors[i]; 1203 String mname = "<init>"; 1204 String desc = c.signature; 1205 desc = desc.replace('/', '.'); 1206 if (DEBUG_SVUID) 1207 msg( "\twriteUTF( \"" + mname + "\" )" ) ; 1208 data.writeUTF(mname); 1209 1210 // mask out post-1.4 modifiers 1211 int modifier = c.member.getModifiers() & METHOD_MASK ; 1212 1213 if (DEBUG_SVUID) 1214 msg( "\twriteInt( " + modifier + " ) " ) ; 1215 data.writeInt( modifier ) ; 1216 1217 if (DEBUG_SVUID) 1218 msg( "\twriteUTF( \"" + desc+ "\" )" ) ; 1219 data.writeUTF(desc); 1220 } 1221 1222 /* Include in the hash all methods except those that are 1223 * private transient and private static. 1224 */ 1225 MethodSignature[] methods = 1226 MethodSignature.removePrivateAndSort(method); 1227 for (int i = 0; i < methods.length; i++ ) { 1228 MethodSignature m = methods[i]; 1229 String desc = m.signature; 1230 desc = desc.replace('/', '.'); 1231 1232 if (DEBUG_SVUID) 1233 msg( "\twriteUTF( \"" + m.member.getName()+ "\" )" ) ; 1234 data.writeUTF(m.member.getName()); 1235 1236 // mask out post-1.4 modifiers 1237 int modifier = m.member.getModifiers() & METHOD_MASK ; 1238 1239 if (DEBUG_SVUID) 1240 msg( "\twriteInt( " + modifier + " ) " ) ; 1241 data.writeInt( modifier ) ; 1242 1243 if (DEBUG_SVUID) 1244 msg( "\twriteUTF( \"" + desc + "\" )" ) ; 1245 data.writeUTF(desc); 1246 } 1247 1248 /* Compute the hash value for this class. 1249 * Use only the first 64 bits of the hash. 1250 */ 1251 data.flush(); 1252 byte hasharray[] = md.digest(); 1253 for (int i = 0; i < Math.min(8, hasharray.length); i++) { 1254 h += (long)(hasharray[i] & 255) << (i * 8); 1255 } 1256 } catch (IOException ignore) { 1257 /* can't happen, but be deterministic anyway. */ 1258 h = -1; 1259 } catch (NoSuchAlgorithmException complain) { 1260 SecurityException se = new SecurityException() ; 1261 se.initCause( complain ) ; 1262 throw se ; 1263 } 1264 1265 return h; 1266 } 1267 1268 private static long computeStructuralUID(com.sun.corba.se.impl.io.ObjectStreamClass osc, Class cl) { 1269 ByteArrayOutputStream devnull = new ByteArrayOutputStream(512); 1270 1271 long h = 0; 1272 try { 1273 1274 if ((!java.io.Serializable.class.isAssignableFrom(cl)) || 1275 (cl.isInterface())){ 1276 return 0; 1277 } 1278 1279 if (java.io.Externalizable.class.isAssignableFrom(cl)) { 1280 return 1; 1281 } 1282 1283 MessageDigest md = MessageDigest.getInstance("SHA"); 1284 DigestOutputStream mdo = new DigestOutputStream(devnull, md); 1285 DataOutputStream data = new DataOutputStream(mdo); 1286 1287 // Get SUID of parent 1288 Class parent = cl.getSuperclass(); 1289 if ((parent != null)) 1290 // SerialBug 1; acc. to spec the one for 1291 // java.lang.object 1292 // should be computed and put 1293 // && (parent != java.lang.Object.class)) 1294 { 1295 //data.writeLong(computeSerialVersionUID(null,parent)); 1296 data.writeLong(computeStructuralUID(lookup(parent), parent)); 1297 } 1298 1299 if (osc.hasWriteObject()) 1300 data.writeInt(2); 1301 else 1302 data.writeInt(1); 1303 1304 // CORBA formal 00-11-03 10.6.2: For each field of the 1305 // class that is mapped to IDL, sorted lexicographically 1306 // by Java field name, in increasing order... 1307 ObjectStreamField[] field = osc.getFields(); 1308 if (field.length > 1) { 1309 Arrays.sort(field, compareObjStrFieldsByName); 1310 } 1311 1312 // ...Java field name in UTF encoding, field 1313 // descriptor, as defined by the JVM spec... 1314 for (int i = 0; i < field.length; i++) { 1315 data.writeUTF(field[i].getName()); 1316 data.writeUTF(field[i].getSignature()); 1317 } 1318 1319 /* Compute the hash value for this class. 1320 * Use only the first 64 bits of the hash. 1321 */ 1322 data.flush(); 1323 byte hasharray[] = md.digest(); 1324 // int minimum = Math.min(8, hasharray.length); 1325 // SerialBug 3: SHA computation is wrong; for loop reversed 1326 //for (int i = minimum; i > 0; i--) 1327 for (int i = 0; i < Math.min(8, hasharray.length); i++) { 1328 h += (long)(hasharray[i] & 255) << (i * 8); 1329 } 1330 } catch (IOException ignore) { 1331 /* can't happen, but be deterministic anyway. */ 1332 h = -1; 1333 } catch (NoSuchAlgorithmException complain) { 1334 SecurityException se = new SecurityException(); 1335 se.initCause( complain ) ; 1336 throw se ; 1337 } 1338 return h; 1339 } 1340 1341 /** 1342 * Compute the JVM signature for the class. 1343 */ 1344 static String getSignature(Class clazz) { 1345 String type = null; 1346 if (clazz.isArray()) { 1347 Class cl = clazz; 1348 int dimensions = 0; 1349 while (cl.isArray()) { 1350 dimensions++; 1351 cl = cl.getComponentType(); 1352 } 1353 StringBuffer sb = new StringBuffer(); 1354 for (int i = 0; i < dimensions; i++) { 1355 sb.append("["); 1356 } 1357 sb.append(getSignature(cl)); 1358 type = sb.toString(); 1359 } else if (clazz.isPrimitive()) { 1360 if (clazz == Integer.TYPE) { 1361 type = "I"; 1362 } else if (clazz == Byte.TYPE) { 1363 type = "B"; 1364 } else if (clazz == Long.TYPE) { 1365 type = "J"; 1366 } else if (clazz == Float.TYPE) { 1367 type = "F"; 1368 } else if (clazz == Double.TYPE) { 1369 type = "D"; 1370 } else if (clazz == Short.TYPE) { 1371 type = "S"; 1372 } else if (clazz == Character.TYPE) { 1373 type = "C"; 1374 } else if (clazz == Boolean.TYPE) { 1375 type = "Z"; 1376 } else if (clazz == Void.TYPE) { 1377 type = "V"; 1378 } 1379 } else { 1380 type = "L" + clazz.getName().replace('.', '/') + ";"; 1381 } 1382 return type; 1383 } 1384 1385 /* 1386 * Compute the JVM method descriptor for the method. 1387 */ 1388 static String getSignature(Method meth) { 1389 StringBuffer sb = new StringBuffer(); 1390 1391 sb.append("("); 1392 1393 Class[] params = meth.getParameterTypes(); // avoid clone 1394 for (int j = 0; j < params.length; j++) { 1395 sb.append(getSignature(params[j])); 1396 } 1397 sb.append(")"); 1398 sb.append(getSignature(meth.getReturnType())); 1399 return sb.toString(); 1400 } 1401 1402 /* 1403 * Compute the JVM constructor descriptor for the constructor. 1404 */ 1405 static String getSignature(Constructor cons) { 1406 StringBuffer sb = new StringBuffer(); 1407 1408 sb.append("("); 1409 1410 Class[] params = cons.getParameterTypes(); // avoid clone 1411 for (int j = 0; j < params.length; j++) { 1412 sb.append(getSignature(params[j])); 1413 } 1414 sb.append(")V"); 1415 return sb.toString(); 1416 } 1417 1418 /* 1419 * Cache of Class -> ClassDescriptor Mappings. 1420 */ 1421 static private ObjectStreamClassEntry[] descriptorFor = new ObjectStreamClassEntry[61]; 1422 1423 /* 1424 * findDescriptorFor a Class. This looks in the cache for a 1425 * mapping from Class -> ObjectStreamClass mappings. The hashCode 1426 * of the Class is used for the lookup since the Class is the key. 1427 * The entries are extended from java.lang.ref.SoftReference so the 1428 * gc will be able to free them if needed. 1429 */ 1430 private static ObjectStreamClass findDescriptorFor(Class cl) { 1431 1432 int hash = cl.hashCode(); 1433 int index = (hash & 0x7FFFFFFF) % descriptorFor.length; 1434 ObjectStreamClassEntry e; 1435 ObjectStreamClassEntry prev; 1436 1437 /* Free any initial entries whose refs have been cleared */ 1438 while ((e = descriptorFor[index]) != null && e.get() == null) { 1439 descriptorFor[index] = e.next; 1440 } 1441 1442 /* Traverse the chain looking for a descriptor with ofClass == cl. 1443 * unlink entries that are unresolved. 1444 */ 1445 prev = e; 1446 while (e != null ) { 1447 ObjectStreamClass desc = (ObjectStreamClass)(e.get()); 1448 if (desc == null) { 1449 // This entry has been cleared, unlink it 1450 prev.next = e.next; 1451 } else { 1452 if (desc.ofClass == cl) 1453 return desc; 1454 prev = e; 1455 } 1456 e = e.next; 1457 } 1458 return null; 1459 } 1460 1461 /* 1462 * insertDescriptorFor a Class -> ObjectStreamClass mapping. 1463 */ 1464 private static void insertDescriptorFor(ObjectStreamClass desc) { 1465 // Make sure not already present 1466 if (findDescriptorFor(desc.ofClass) != null) { 1467 return; 1468 } 1469 1470 int hash = desc.ofClass.hashCode(); 1471 int index = (hash & 0x7FFFFFFF) % descriptorFor.length; 1472 ObjectStreamClassEntry e = new ObjectStreamClassEntry(desc); 1473 e.next = descriptorFor[index]; 1474 descriptorFor[index] = e; 1475 } 1476 1477 private static Field[] getDeclaredFields(final Class clz) { 1478 return (Field[]) AccessController.doPrivileged(new PrivilegedAction() { 1479 public Object run() { 1480 return clz.getDeclaredFields(); 1481 } 1482 }); 1483 } 1484 1485 1486 /* 1487 * The name of this descriptor 1488 */ 1489 private String name; 1490 1491 /* 1492 * The descriptor of the supertype. 1493 */ 1494 private ObjectStreamClass superclass; 1495 1496 /* 1497 * Flags for Serializable and Externalizable. 1498 */ 1499 private boolean serializable; 1500 private boolean externalizable; 1501 1502 /* 1503 * Array of persistent fields of this class, sorted by 1504 * type and name. 1505 */ 1506 private ObjectStreamField[] fields; 1507 1508 /* 1509 * Class that is a descriptor for in this virtual machine. 1510 */ 1511 private Class ofClass; 1512 1513 /* 1514 * True if descriptor for a proxy class. 1515 */ 1516 boolean forProxyClass; 1517 1518 1519 /* 1520 * SerialVersionUID for this class. 1521 */ 1522 private long suid = kDefaultUID; 1523 private String suidStr = null; 1524 1525 /* 1526 * Actual (computed) SerialVersionUID for this class. 1527 */ 1528 private long actualSuid = kDefaultUID; 1529 private String actualSuidStr = null; 1530 1531 /* 1532 * The total number of bytes of primitive fields. 1533 * The total number of object fields. 1534 */ 1535 int primBytes; 1536 int objFields; 1537 1538 /** 1539 * Flag indicating whether or not this instance has 1540 * successfully completed initialization. This is to 1541 * try to fix bug 4373844. Working to move to 1542 * reusing java.io.ObjectStreamClass for JDK 1.5. 1543 */ 1544 private boolean initialized = false; 1545 1546 /* Internal lock object. */ 1547 private Object lock = new Object(); 1548 1549 /* In JDK 1.1, external data was not written in block mode. 1550 * As of JDK 1.2, external data is written in block data mode. This 1551 * flag enables JDK 1.2 to be able to read JDK 1.1 written external data. 1552 * 1553 * @since JDK 1.2 1554 */ 1555 private boolean hasExternalizableBlockData; 1556 Method writeObjectMethod; 1557 Method readObjectMethod; 1558 private Method writeReplaceObjectMethod; 1559 private Method readResolveObjectMethod; 1560 private Constructor cons ; 1561 1562 /** 1563 * Beginning in Java to IDL ptc/02-01-12, RMI-IIOP has a 1564 * stream format version 2 which puts a fake valuetype around 1565 * a Serializable's optional custom data. This valuetype has 1566 * a special repository ID made from the Serializable's 1567 * information which we are pre-computing and 1568 * storing here. 1569 */ 1570 private String rmiiiopOptionalDataRepId = null; 1571 1572 /* 1573 * ObjectStreamClass that this one was built from. 1574 */ 1575 private ObjectStreamClass localClassDesc; 1576 1577 /* Find out if the class has a static class initializer <clinit> */ 1578 private static Method hasStaticInitializerMethod = null; 1579 /** 1580 * Returns true if the given class defines a static initializer method, 1581 * false otherwise. 1582 */ 1583 private static boolean hasStaticInitializer(Class cl) { 1584 if (hasStaticInitializerMethod == null) { 1585 Class classWithThisMethod = null; 1586 1587 try { 1588 try { 1589 // When using rip-int with Merlin or when this is a Merlin 1590 // workspace, the method we want is in sun.misc.ClassReflector 1591 // and absent from java.io.ObjectStreamClass. 1592 // 1593 // When compiling rip-int with JDK 1.3.x, we have to get it 1594 // from java.io.ObjectStreamClass. 1595 classWithThisMethod = Class.forName("sun.misc.ClassReflector"); 1596 } catch (ClassNotFoundException cnfe) { 1597 // Do nothing. This is either not a Merlin workspace, 1598 // or rip-int is being compiled with something other than 1599 // Merlin, probably JDK 1.3. Fall back on java.io.ObjectStreaClass. 1600 } 1601 if (classWithThisMethod == null) 1602 classWithThisMethod = java.io.ObjectStreamClass.class; 1603 1604 hasStaticInitializerMethod = 1605 classWithThisMethod.getDeclaredMethod("hasStaticInitializer", 1606 new Class[] { Class.class }); 1607 } catch (NoSuchMethodException ex) { 1608 } 1609 1610 if (hasStaticInitializerMethod == null) { 1611 // XXX I18N, logging needed 1612 throw new InternalError("Can't find hasStaticInitializer method on " 1613 + classWithThisMethod.getName()); 1614 } 1615 hasStaticInitializerMethod.setAccessible(true); 1616 } 1617 1618 try { 1619 Boolean retval = (Boolean) 1620 hasStaticInitializerMethod.invoke(null, new Object[] { cl }); 1621 return retval.booleanValue(); 1622 } catch (Exception ex) { 1623 // XXX I18N, logging needed 1624 InternalError ie = new InternalError( "Error invoking hasStaticInitializer" ) ; 1625 ie.initCause( ex ) ; 1626 throw ie ; 1627 } 1628 } 1629 1630 1631 /* The Class Object for java.io.Serializable */ 1632 private static Class classSerializable = null; 1633 private static Class classExternalizable = null; 1634 1635 /* 1636 * Resolve java.io.Serializable at load time. 1637 */ 1638 static { 1639 try { 1640 classSerializable = Class.forName("java.io.Serializable"); 1641 classExternalizable = Class.forName("java.io.Externalizable"); 1642 } catch (Throwable e) { 1643 System.err.println("Could not load java.io.Serializable or java.io.Externalizable."); 1644 } 1645 } 1646 1647 /** use serialVersionUID from JDK 1.1. for interoperability */ 1648 private static final long serialVersionUID = -6120832682080437368L; 1649 1650 /** 1651 * Set serialPersistentFields of a Serializable class to this value to 1652 * denote that the class has no Serializable fields. 1653 */ 1654 public static final ObjectStreamField[] NO_FIELDS = 1655 new ObjectStreamField[0]; 1656 1657 /* 1658 * Entries held in the Cache of known ObjectStreamClass objects. 1659 * Entries are chained together with the same hash value (modulo array size). 1660 */ 1661 private static class ObjectStreamClassEntry // extends java.lang.ref.SoftReference 1662 { 1663 ObjectStreamClassEntry(ObjectStreamClass c) { 1664 //super(c); 1665 this.c = c; 1666 } 1667 ObjectStreamClassEntry next; 1668 1669 public Object get() 1670 { 1671 return c; 1672 } 1673 private ObjectStreamClass c; 1674 } 1675 1676 /* 1677 * Comparator object for Classes and Interfaces 1678 */ 1679 private static Comparator compareClassByName = 1680 new CompareClassByName(); 1681 1682 private static class CompareClassByName implements Comparator { 1683 public int compare(Object o1, Object o2) { 1684 Class c1 = (Class)o1; 1685 Class c2 = (Class)o2; 1686 return (c1.getName()).compareTo(c2.getName()); 1687 } 1688 } 1689 1690 /** 1691 * Comparator for ObjectStreamFields by name 1692 */ 1693 private final static Comparator compareObjStrFieldsByName 1694 = new CompareObjStrFieldsByName(); 1695 1696 private static class CompareObjStrFieldsByName implements Comparator { 1697 public int compare(Object o1, Object o2) { 1698 ObjectStreamField osf1 = (ObjectStreamField)o1; 1699 ObjectStreamField osf2 = (ObjectStreamField)o2; 1700 1701 return osf1.getName().compareTo(osf2.getName()); 1702 } 1703 } 1704 1705 /* 1706 * Comparator object for Members, Fields, and Methods 1707 */ 1708 private static Comparator compareMemberByName = 1709 new CompareMemberByName(); 1710 1711 private static class CompareMemberByName implements Comparator { 1712 public int compare(Object o1, Object o2) { 1713 String s1 = ((Member)o1).getName(); 1714 String s2 = ((Member)o2).getName(); 1715 1716 if (o1 instanceof Method) { 1717 s1 += getSignature((Method)o1); 1718 s2 += getSignature((Method)o2); 1719 } else if (o1 instanceof Constructor) { 1720 s1 += getSignature((Constructor)o1); 1721 s2 += getSignature((Constructor)o2); 1722 } 1723 return s1.compareTo(s2); 1724 } 1725 } 1726 1727 /* It is expensive to recompute a method or constructor signature 1728 many times, so compute it only once using this data structure. */ 1729 private static class MethodSignature implements Comparator { 1730 Member member; 1731 String signature; // cached parameter signature 1732 1733 /* Given an array of Method or Constructor members, 1734 return a sorted array of the non-private members.*/ 1735 /* A better implementation would be to implement the returned data 1736 structure as an insertion sorted link list.*/ 1737 static MethodSignature[] removePrivateAndSort(Member[] m) { 1738 int numNonPrivate = 0; 1739 for (int i = 0; i < m.length; i++) { 1740 if (! Modifier.isPrivate(m[i].getModifiers())) { 1741 numNonPrivate++; 1742 } 1743 } 1744 MethodSignature[] cm = new MethodSignature[numNonPrivate]; 1745 int cmi = 0; 1746 for (int i = 0; i < m.length; i++) { 1747 if (! Modifier.isPrivate(m[i].getModifiers())) { 1748 cm[cmi] = new MethodSignature(m[i]); 1749 cmi++; 1750 } 1751 } 1752 if (cmi > 0) 1753 Arrays.sort(cm, cm[0]); 1754 return cm; 1755 } 1756 1757 /* Assumes that o1 and o2 are either both methods 1758 or both constructors.*/ 1759 public int compare(Object o1, Object o2) { 1760 /* Arrays.sort calls compare when o1 and o2 are equal.*/ 1761 if (o1 == o2) 1762 return 0; 1763 1764 MethodSignature c1 = (MethodSignature)o1; 1765 MethodSignature c2 = (MethodSignature)o2; 1766 1767 int result; 1768 if (isConstructor()) { 1769 result = c1.signature.compareTo(c2.signature); 1770 } else { // is a Method. 1771 result = c1.member.getName().compareTo(c2.member.getName()); 1772 if (result == 0) 1773 result = c1.signature.compareTo(c2.signature); 1774 } 1775 return result; 1776 } 1777 1778 final private boolean isConstructor() { 1779 return member instanceof Constructor; 1780 } 1781 private MethodSignature(Member m) { 1782 member = m; 1783 if (isConstructor()) { 1784 signature = ObjectStreamClass.getSignature((Constructor)m); 1785 } else { 1786 signature = ObjectStreamClass.getSignature((Method)m); 1787 } 1788 } 1789 } 1790 1791 /** 1792 * Returns non-static, non-abstract method with given signature provided it 1793 * is defined by or accessible (via inheritance) by the given class, or 1794 * null if no match found. Access checks are disabled on the returned 1795 * method (if any). 1796 * 1797 * Copied from the Merlin java.io.ObjectStreamClass. 1798 */ 1799 private static Method getInheritableMethod(Class cl, String name, 1800 Class[] argTypes, 1801 Class returnType) 1802 { 1803 Method meth = null; 1804 Class defCl = cl; 1805 while (defCl != null) { 1806 try { 1807 meth = defCl.getDeclaredMethod(name, argTypes); 1808 break; 1809 } catch (NoSuchMethodException ex) { 1810 defCl = defCl.getSuperclass(); 1811 } 1812 } 1813 1814 if ((meth == null) || (meth.getReturnType() != returnType)) { 1815 return null; 1816 } 1817 meth.setAccessible(true); 1818 int mods = meth.getModifiers(); 1819 if ((mods & (Modifier.STATIC | Modifier.ABSTRACT)) != 0) { 1820 return null; 1821 } else if ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) != 0) { 1822 return meth; 1823 } else if ((mods & Modifier.PRIVATE) != 0) { 1824 return (cl == defCl) ? meth : null; 1825 } else { 1826 return packageEquals(cl, defCl) ? meth : null; 1827 } 1828 } 1829 1830 /** 1831 * Returns true if classes are defined in the same package, false 1832 * otherwise. 1833 * 1834 * Copied from the Merlin java.io.ObjectStreamClass. 1835 */ 1836 private static boolean packageEquals(Class cl1, Class cl2) { 1837 Package pkg1 = cl1.getPackage(), pkg2 = cl2.getPackage(); 1838 return ((pkg1 == pkg2) || ((pkg1 != null) && (pkg1.equals(pkg2)))); 1839 } 1840 }