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