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