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