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