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