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