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