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