src/share/classes/com/sun/corba/se/impl/io/ObjectStreamClass.java

Tue, 28 Dec 2010 15:52:36 -0800

author
ohair
date
Tue, 28 Dec 2010 15:52:36 -0800
changeset 240
f90b3e014e83
parent 235
5d9708346d50
child 281
93e77c49b3bb
permissions
-rw-r--r--

6962318: Update copyright year
Reviewed-by: xdono

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

mercurial