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

Fri, 24 Sep 2010 22:42:14 -0700

author
skoppar
date
Fri, 24 Sep 2010 22:42:14 -0700
changeset 205
b2fff4b7e8cd
parent 158
91006f157c46
child 235
5d9708346d50
permissions
-rw-r--r--

6891766: Vulnerabilities in use of reflection in CORBA
Reviewed-by: hawtin

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

mercurial