ObjectStreamClass_1_3_1.java revision 689:c580f8cf1bc8
1/*
2 * Copyright (c) 2001, 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 1999  All Rights Reserved
29 *
30 */
31
32package com.sun.corba.se.impl.orbutil;
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.Serializable;
56import java.io.Externalizable;
57
58import java.util.Arrays;
59import java.util.Comparator;
60import java.util.Hashtable;
61
62import org.omg.CORBA.ValueMember;
63
64import com.sun.corba.se.impl.io.ValueUtility;
65import com.sun.corba.se.impl.io.ObjectStreamClass;
66
67/**
68 * This is duplicated here to preserve the JDK 1.3.1FCS behavior
69 * of calculating the OMG hash code incorrectly when serialPersistentFields
70 * is used, but some of the fields no longer exist in the class itself.
71 *
72 * We have to duplicate it since we aren't allowed to modify the
73 * com.sun.corba.se.impl.io version further, and can't make it
74 * public outside of its package for security reasons.
75 */
76/**
77 * A ObjectStreamClass_1_3_1 describes a class that can be serialized to a stream
78 * or a class that was serialized to a stream.  It contains the name
79 * and the serialVersionUID of the class.
80 * <br>
81 * The ObjectStreamClass_1_3_1 for a specific class loaded in this Java VM can
82 * be found using the lookup method.
83 *
84 * @author  Roger Riggs
85 * @since   JDK1.1
86 */
87public class ObjectStreamClass_1_3_1 implements java.io.Serializable {
88
89    public static final long kDefaultUID = -1;
90
91    private static Object noArgsList[] = {};
92    private static Class<?> noTypesList[] = {};
93
94    private static Hashtable translatedFields;
95
96    /** Find the descriptor for a class that can be serialized.  Null
97     * is returned if the specified class does not implement
98     * java.io.Serializable or java.io.Externalizable.
99     */
100    static final ObjectStreamClass_1_3_1 lookup(Class<?> cl)
101    {
102        ObjectStreamClass_1_3_1 desc = lookupInternal(cl);
103        if (desc.isSerializable() || desc.isExternalizable())
104            return desc;
105        return null;
106    }
107
108    /*
109     * Find the class descriptor for the specified class.
110     * Package access only so it can be called from ObjectIn/OutStream.
111     */
112    static ObjectStreamClass_1_3_1 lookupInternal(Class<?> cl)
113    {
114        /* Synchronize on the hashtable so no two threads will do
115         * this at the same time.
116         */
117        ObjectStreamClass_1_3_1 desc = null;
118        synchronized (descriptorFor) {
119            /* Find the matching descriptor if it already known */
120            desc = findDescriptorFor(cl);
121            if (desc != null) {
122                return desc;
123            }
124
125                /* Check if it's serializable */
126                boolean serializable = Serializable.class.isAssignableFrom(cl);
127                /* If the class is only Serializable,
128                 * lookup the descriptor for the superclass.
129                 */
130                ObjectStreamClass_1_3_1 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_1_3_1(cl, superdesc,
155                                         serializable, externalizable);
156        }
157        desc.init();
158        return desc;
159    }
160
161    /**
162     * The name of the class described by this descriptor.
163     */
164    public final String getName() {
165        return name;
166    }
167
168    /**
169     * Return the serialVersionUID for this class.
170     * The serialVersionUID defines a set of classes all with the same name
171     * that have evolved from a common root class and agree to be serialized
172     * and deserialized using a common format.
173     */
174    public static final long getSerialVersionUID( java.lang.Class<?> clazz) {
175        ObjectStreamClass_1_3_1 theosc = ObjectStreamClass_1_3_1.lookup( clazz );
176        if( theosc != null )
177        {
178                return theosc.getSerialVersionUID( );
179        }
180        return 0;
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 final long getSerialVersionUID() {
190        return suid;
191    }
192
193    /**
194     * Return the serialVersionUID string for this class.
195     * The serialVersionUID defines a set of classes all with the same name
196     * that have evolved from a common root class and agree to be serialized
197     * and deserialized using a common format.
198     */
199    public final String getSerialVersionUIDStr() {
200        if (suidStr == null)
201            suidStr = Long.toHexString(suid).toUpperCase();
202        return suidStr;
203    }
204
205    /**
206     * Return the actual (computed) serialVersionUID for this class.
207     */
208    public static final long getActualSerialVersionUID( java.lang.Class<?> clazz )
209    {
210        ObjectStreamClass_1_3_1 theosc = ObjectStreamClass_1_3_1.lookup( clazz );
211        if( theosc != null )
212        {
213                return theosc.getActualSerialVersionUID( );
214        }
215        return 0;
216    }
217
218    /**
219     * Return the actual (computed) serialVersionUID for this class.
220     */
221    public final long getActualSerialVersionUID() {
222        return actualSuid;
223    }
224
225    /**
226     * Return the actual (computed) serialVersionUID for this class.
227     */
228    public final String getActualSerialVersionUIDStr() {
229        if (actualSuidStr == null)
230            actualSuidStr = Long.toHexString(actualSuid).toUpperCase();
231        return actualSuidStr;
232    }
233
234    /**
235     * Return the class in the local VM that this version is mapped to.
236     * Null is returned if there is no corresponding local class.
237     */
238    public final Class<?> forClass() {
239        return ofClass;
240    }
241
242    /**
243     * Return an array of the fields of this serializable class.
244     * @return an array containing an element for each persistent
245     * field of this class. Returns an array of length zero if
246     * there are no fields.
247     * @since JDK1.2
248     */
249    public ObjectStreamField[] getFields() {
250        // Return a copy so the caller can't change the fields.
251        if (fields.length > 0) {
252            ObjectStreamField[] dup = new ObjectStreamField[fields.length];
253            System.arraycopy(fields, 0, dup, 0, fields.length);
254            return dup;
255        } else {
256            return fields;
257        }
258    }
259
260    public boolean hasField(ValueMember field){
261
262        for (int i = 0; i < fields.length; i++){
263            try{
264                if (fields[i].getName().equals(field.name)) {
265
266                    if (fields[i].getSignature().equals(ValueUtility.getSignature(field)))
267                        return true;
268                }
269            }
270            catch(Throwable t){}
271        }
272        return false;
273    }
274
275    /* Avoid unnecessary allocations. */
276    final ObjectStreamField[] getFieldsNoCopy() {
277        return fields;
278    }
279
280    /**
281     * Get the field of this class by name.
282     * @return The ObjectStreamField object of the named field or null if there
283     * is no such named field.
284     */
285    public final ObjectStreamField getField(String name) {
286        /* Binary search of fields by name.
287         */
288        for (int i = fields.length-1; i >= 0; i--) {
289            if (name.equals(fields[i].getName())) {
290                return fields[i];
291            }
292        }
293        return null;
294    }
295
296    public Serializable writeReplace(Serializable value) {
297        if (writeReplaceObjectMethod != null) {
298            try {
299                return (Serializable) writeReplaceObjectMethod.invoke(value,noArgsList);
300            }
301            catch(Throwable t) {
302                throw new RuntimeException(t.getMessage());
303            }
304        }
305        else return value;
306    }
307
308    public Object readResolve(Object value) {
309        if (readResolveObjectMethod != null) {
310            try {
311                return readResolveObjectMethod.invoke(value,noArgsList);
312            }
313            catch(Throwable t) {
314                throw new RuntimeException(t.getMessage());
315            }
316        }
317        else return value;
318    }
319
320    /**
321     * Return a string describing this ObjectStreamClass_1_3_1.
322     */
323    public final String toString() {
324        StringBuffer sb = new StringBuffer();
325
326        sb.append(name);
327        sb.append(": static final long serialVersionUID = ");
328        sb.append(Long.toString(suid));
329        sb.append("L;");
330        return sb.toString();
331    }
332
333    /*
334     * Create a new ObjectStreamClass_1_3_1 from a loaded class.
335     * Don't call this directly, call lookup instead.
336     */
337    private ObjectStreamClass_1_3_1(java.lang.Class<?> cl, ObjectStreamClass_1_3_1 superdesc,
338                              boolean serial, boolean extern)
339    {
340        ofClass = cl;           /* created from this class */
341
342        if (Proxy.isProxyClass(cl)) {
343            forProxyClass = true;
344        }
345
346        name = cl.getName();
347        superclass = superdesc;
348        serializable = serial;
349        if (!forProxyClass) {
350            // proxy classes are never externalizable
351            externalizable = extern;
352        }
353
354        /*
355         * Enter this class in the table of known descriptors.
356         * Otherwise, when the fields are read it may recurse
357         * trying to find the descriptor for itself.
358         */
359        insertDescriptorFor(this);
360
361        /*
362         * The remainder of initialization occurs in init(), which is called
363         * after the lock on the global class descriptor table has been
364         * released.
365         */
366    }
367
368    /*
369     * Initialize class descriptor.  This method is only invoked on class
370     * descriptors created via calls to lookupInternal().  This method is kept
371     * separate from the ObjectStreamClass_1_3_1 constructor so that lookupInternal
372     * does not have to hold onto a global class descriptor table lock while the
373     * class descriptor is being initialized (see bug 4165204).
374     */
375
376
377    private void init() {
378      synchronized (lock) {
379
380        final Class<?> cl = ofClass;
381
382        if (fields != null) // already initialized
383                return;
384
385
386        if (!serializable ||
387            externalizable ||
388            forProxyClass ||
389            name.equals("java.lang.String")) {
390            fields = NO_FIELDS;
391        } else if (serializable) {
392
393            /* Ask for permission to override field access checks.
394             */
395            AccessController.doPrivileged(new PrivilegedAction() {
396                public Object run() {
397                /* Fill in the list of persistent fields.
398                 * If it is declared, use the declared serialPersistentFields.
399                 * Otherwise, extract the fields from the class itself.
400                 */
401                try {
402                    Field pf = cl.getDeclaredField("serialPersistentFields");
403                    // serial bug 7; the serialPersistentFields were not
404                    // being read and stored as Accessible bit was not set
405                    pf.setAccessible(true);
406                    // serial bug 7; need to find if the field is of type
407                    // java.io.ObjectStreamField
408                    java.io.ObjectStreamField[] f =
409                           (java.io.ObjectStreamField[])pf.get(cl);
410                    int mods = pf.getModifiers();
411                    if ((Modifier.isPrivate(mods)) &&
412                        (Modifier.isStatic(mods)) &&
413                        (Modifier.isFinal(mods)))
414                    {
415                        fields = (ObjectStreamField[])translateFields((Object[])pf.get(cl));
416                    }
417                } catch (NoSuchFieldException e) {
418                    fields = null;
419                } catch (IllegalAccessException e) {
420                    fields = null;
421                } catch (IllegalArgumentException e) {
422                    fields = null;
423                } catch (ClassCastException e) {
424                    /* Thrown if a field serialPersistentField exists
425                     * but it is not of type ObjectStreamField.
426                     */
427                    fields = null;
428                }
429
430
431                if (fields == null) {
432                    /* Get all of the declared fields for this
433                     * Class. setAccessible on all fields so they
434                     * can be accessed later.  Create a temporary
435                     * ObjectStreamField array to hold each
436                     * non-static, non-transient field. Then copy the
437                     * temporary array into an array of the correct
438                     * size once the number of fields is known.
439                     */
440                    Field[] actualfields = cl.getDeclaredFields();
441
442                    int numFields = 0;
443                    ObjectStreamField[] tempFields =
444                        new ObjectStreamField[actualfields.length];
445                    for (int i = 0; i < actualfields.length; i++) {
446                        int modifiers = actualfields[i].getModifiers();
447                        if (!Modifier.isStatic(modifiers) &&
448                            !Modifier.isTransient(modifiers)) {
449                            tempFields[numFields++] =
450                                new ObjectStreamField(actualfields[i]);
451                        }
452                    }
453                    fields = new ObjectStreamField[numFields];
454                    System.arraycopy(tempFields, 0, fields, 0, numFields);
455
456                } else {
457                    // For each declared persistent field, look for an actual
458                    // reflected Field. If there is one, make sure it's the correct
459                    // type and cache it in the ObjectStreamClass_1_3_1 for that field.
460                    for (int j = fields.length-1; j >= 0; j--) {
461                        try {
462                            Field reflField = cl.getDeclaredField(fields[j].getName());
463                            if (fields[j].getType() == reflField.getType()) {
464                                // reflField.setAccessible(true);
465                                fields[j].setField(reflField);
466                            }
467                        } catch (NoSuchFieldException e) {
468                            // Nothing to do
469                        }
470                    }
471                }
472                return null;
473            }
474            });
475
476            if (fields.length > 1)
477                Arrays.sort(fields);
478
479            /* Set up field data for use while writing using the API api. */
480            computeFieldInfo();
481        }
482
483        /* Get the serialVersionUID from the class.
484         * It uses the access override mechanism so make sure
485         * the field objects is only used here.
486         *
487         * NonSerializable classes have a serialVerisonUID of 0L.
488         */
489         if (isNonSerializable()) {
490             suid = 0L;
491         } else {
492             // Lookup special Serializable members using reflection.
493             AccessController.doPrivileged(new PrivilegedAction() {
494                public Object run() {
495                if (forProxyClass) {
496                    // proxy classes always have serialVersionUID of 0L
497                    suid = 0L;
498                } else {
499                    try {
500                        final Field f = cl.getDeclaredField("serialVersionUID");
501                        int mods = f.getModifiers();
502                    // SerialBug 5:  static final SUID should be read
503                        if (Modifier.isStatic(mods) &&
504                            Modifier.isFinal(mods) ) {
505                            f.setAccessible(true);
506                            suid = f.getLong(cl);
507                            // get rid of native code
508                            // suid = getSerialVersionUIDField(cl);
509                    // SerialBug 2: should be computed after writeObject
510                    // actualSuid = computeStructuralUID(cl);
511                        } else {
512                            suid = ObjectStreamClass.getSerialVersionUID(cl);
513                            // SerialBug 2: should be computed after writeObject
514                            // actualSuid = computeStructuralUID(cl);
515                        }
516                    } catch (NoSuchFieldException ex) {
517                        suid = ObjectStreamClass.getSerialVersionUID(cl);
518                        // SerialBug 2: should be computed after writeObject
519                        // actualSuid = computeStructuralUID(cl);
520                    } catch (IllegalAccessException ex) {
521                        suid = ObjectStreamClass.getSerialVersionUID(cl);
522                    }
523                }
524
525
526                try {
527                    writeReplaceObjectMethod = cl.getDeclaredMethod("writeReplace", noTypesList);
528                    if (Modifier.isStatic(writeReplaceObjectMethod.getModifiers())) {
529                        writeReplaceObjectMethod = null;
530                    } else {
531                        writeReplaceObjectMethod.setAccessible(true);
532                    }
533
534                } catch (NoSuchMethodException e2) {
535
536                }
537
538                try {
539                    readResolveObjectMethod = cl.getDeclaredMethod("readResolve", noTypesList);
540                    if (Modifier.isStatic(readResolveObjectMethod.getModifiers())) {
541                       readResolveObjectMethod = null;
542                    } else {
543                       readResolveObjectMethod.setAccessible(true);
544                    }
545
546                } catch (NoSuchMethodException e2) {
547
548                }
549
550                /* Cache lookup of writeObject and readObject for
551                 * Serializable classes. (Do not lookup for
552                 * Externalizable)
553                 */
554
555                if (serializable && !forProxyClass) {
556
557                    /* Look for the writeObject method
558                     * Set the accessible flag on it here. ObjectOutputStream
559                     * will call it as necessary.
560                     */
561                    try {
562                      Class<?>[] args = {java.io.ObjectOutputStream.class};
563                      writeObjectMethod = cl.getDeclaredMethod("writeObject", args);
564                      hasWriteObjectMethod = true;
565                      int mods = writeObjectMethod.getModifiers();
566
567                      // Method must be private and non-static
568                      if (!Modifier.isPrivate(mods) ||
569                        Modifier.isStatic(mods)) {
570                        writeObjectMethod = null;
571                        hasWriteObjectMethod = false;
572                      }
573
574                    } catch (NoSuchMethodException e) {
575                    }
576
577                    /* Look for the readObject method
578                     * set the access override and save the reference for
579                     * ObjectInputStream so it can all the method directly.
580                     */
581                    try {
582                      Class<?>[] args = {java.io.ObjectInputStream.class};
583                      readObjectMethod = cl.getDeclaredMethod("readObject", args);
584                      int mods = readObjectMethod.getModifiers();
585
586                      // Method must be private and non-static
587                      if (!Modifier.isPrivate(mods) ||
588                        Modifier.isStatic(mods)) {
589                        readObjectMethod = null;
590                      }
591                    } catch (NoSuchMethodException e) {
592                    }
593                    // Compute the structural UID.  This must be done after the
594                    // calculation for writeObject.  Fixed 4/20/2000, eea1
595                    // SerialBug 2: to have correct value in RepId
596                }
597                return null;
598            }
599          });
600        }
601
602        actualSuid = computeStructuralUID(this, cl);
603      }
604
605    }
606
607    /*
608     * Create an empty ObjectStreamClass_1_3_1 for a class about to be read.
609     * This is separate from read so ObjectInputStream can assign the
610     * wire handle early, before any nested ObjectStreamClass_1_3_1 might
611     * be read.
612     */
613    ObjectStreamClass_1_3_1(String n, long s) {
614        name = n;
615        suid = s;
616        superclass = null;
617    }
618
619    private static Object[] translateFields(Object objs[])
620        throws NoSuchFieldException {
621        try{
622            java.io.ObjectStreamField fields[] = (java.io.ObjectStreamField[])objs;
623            Object translation[] = null;
624
625            if (translatedFields == null)
626                translatedFields = new Hashtable();
627
628            translation = (Object[])translatedFields.get(fields);
629
630            if (translation != null)
631                return translation;
632            else {
633                Class<?> osfClass = com.sun.corba.se.impl.orbutil.ObjectStreamField.class;
634
635                translation = (Object[])java.lang.reflect.Array.newInstance(osfClass, objs.length);
636                Object arg[] = new Object[2];
637                Class<?> types[] = {String.class, Class.class};
638                Constructor constructor = osfClass.getDeclaredConstructor(types);
639                for (int i = fields.length -1; i >= 0; i--){
640                    arg[0] = fields[i].getName();
641                    arg[1] = fields[i].getType();
642
643                    translation[i] = constructor.newInstance(arg);
644                }
645                translatedFields.put(fields, translation);
646
647            }
648
649            return (Object[])translation;
650        }
651        catch(Throwable t){
652            throw new NoSuchFieldException();
653        }
654    }
655
656    /* Compare the base class names of streamName and localName.
657     *
658     * @return  Return true iff the base class name compare.
659     * @param streamName    Fully qualified class name.
660     * @param localName     Fully qualified class name.
661     * @param pkgSeparator  class names use either '.' or '/'.
662     *
663     * Only compare base class name to allow package renaming.
664     */
665    static boolean compareClassNames(String streamName,
666                                     String localName,
667                                     char pkgSeparator) {
668        /* compare the class names, stripping off package names. */
669        int streamNameIndex = streamName.lastIndexOf(pkgSeparator);
670        if (streamNameIndex < 0)
671            streamNameIndex = 0;
672
673        int localNameIndex = localName.lastIndexOf(pkgSeparator);
674        if (localNameIndex < 0)
675            localNameIndex = 0;
676
677        return streamName.regionMatches(false, streamNameIndex,
678                                        localName, localNameIndex,
679                                        streamName.length() - streamNameIndex);
680    }
681
682    /*
683     * Compare the types of two class descriptors.
684     * They match if they have the same class name and suid
685     */
686    final boolean typeEquals(ObjectStreamClass_1_3_1 other) {
687        return (suid == other.suid) &&
688            compareClassNames(name, other.name, '.');
689    }
690
691    /*
692     * Return the superclass descriptor of this descriptor.
693     */
694    final void setSuperclass(ObjectStreamClass_1_3_1 s) {
695        superclass = s;
696    }
697
698    /*
699     * Return the superclass descriptor of this descriptor.
700     */
701    final ObjectStreamClass_1_3_1 getSuperclass() {
702        return superclass;
703    }
704
705    /*
706     * Return whether the class has a writeObject method
707     */
708    final boolean hasWriteObject() {
709        return hasWriteObjectMethod;
710    }
711
712    final boolean isCustomMarshaled() {
713        return (hasWriteObject() || isExternalizable());
714    }
715
716    /*
717     * Return true if all instances of 'this' Externalizable class
718     * are written in block-data mode from the stream that 'this' was read
719     * from. <p>
720     *
721     * In JDK 1.1, all Externalizable instances are not written
722     * in block-data mode.
723     * In JDK 1.2, all Externalizable instances, by default, are written
724     * in block-data mode and the Externalizable instance is terminated with
725     * tag TC_ENDBLOCKDATA. Change enabled the ability to skip Externalizable
726     * instances.
727     *
728     * IMPLEMENTATION NOTE:
729     *   This should have been a mode maintained per stream; however,
730     *   for compatibility reasons, it was only possible to record
731     *   this change per class. All Externalizable classes within
732     *   a given stream should either have this mode enabled or
733     *   disabled. This is enforced by not allowing the PROTOCOL_VERSION
734     *   of a stream to he changed after any objects have been written.
735     *
736     * @see ObjectOutputStream#useProtocolVersion
737     * @see ObjectStreamConstants#PROTOCOL_VERSION_1
738     * @see ObjectStreamConstants#PROTOCOL_VERSION_2
739     *
740     * @since JDK 1.2
741     */
742    boolean hasExternalizableBlockDataMode() {
743        return hasExternalizableBlockData;
744    }
745
746    /*
747     * Return the ObjectStreamClass_1_3_1 of the local class this one is based on.
748     */
749    final ObjectStreamClass_1_3_1 localClassDescriptor() {
750        return localClassDesc;
751    }
752
753    /*
754     * Get the Serializability of the class.
755     */
756    boolean isSerializable() {
757        return serializable;
758    }
759
760    /*
761     * Get the externalizability of the class.
762     */
763    boolean isExternalizable() {
764        return externalizable;
765    }
766
767    boolean isNonSerializable() {
768        return ! (externalizable || serializable);
769    }
770
771    /*
772     * Calculate the size of the array needed to store primitive data and the
773     * number of object references to read when reading from the input
774     * stream.
775     */
776    private void computeFieldInfo() {
777        primBytes = 0;
778        objFields = 0;
779
780        for (int i = 0; i < fields.length; i++ ) {
781            switch (fields[i].getTypeCode()) {
782            case 'B':
783            case 'Z':
784                primBytes += 1;
785                break;
786            case 'C':
787            case 'S':
788                primBytes += 2;
789                break;
790
791            case 'I':
792            case 'F':
793                primBytes += 4;
794                break;
795            case 'J':
796            case 'D' :
797                primBytes += 8;
798                break;
799
800            case 'L':
801            case '[':
802                objFields += 1;
803                break;
804            }
805        }
806    }
807
808    private static long computeStructuralUID(ObjectStreamClass_1_3_1 osc, Class<?> cl) {
809        ByteArrayOutputStream devnull = new ByteArrayOutputStream(512);
810
811        long h = 0;
812        try {
813
814            if ((!java.io.Serializable.class.isAssignableFrom(cl)) ||
815                (cl.isInterface())){
816                return 0;
817            }
818
819            if (java.io.Externalizable.class.isAssignableFrom(cl)) {
820                return 1;
821            }
822
823            MessageDigest md = MessageDigest.getInstance("SHA");
824            DigestOutputStream mdo = new DigestOutputStream(devnull, md);
825            DataOutputStream data = new DataOutputStream(mdo);
826
827            // Get SUID of parent
828            Class<?> parent = cl.getSuperclass();
829            if ((parent != null))
830            // SerialBug 1; acc. to spec the one for
831            // java.lang.object
832            // should be computed and put
833            //     && (parent != java.lang.Object.class))
834            {
835                                //data.writeLong(computeSerialVersionUID(null,parent));
836                data.writeLong(computeStructuralUID(lookup(parent), parent));
837            }
838
839            if (osc.hasWriteObject())
840                data.writeInt(2);
841            else
842                data.writeInt(1);
843
844            /* Sort the field names to get a deterministic order */
845            // Field[] field = ObjectStreamClass_1_3_1.getDeclaredFields(cl);
846
847            ObjectStreamField[] fields = osc.getFields();
848
849            // Must make sure that the Field array we allocate
850            // below is exactly the right size.  Bug fix for
851            // 4397133.
852            int numNonNullFields = 0;
853            for (int i = 0; i < fields.length; i++)
854                if (fields[i].getField() != null)
855                    numNonNullFields++;
856
857            Field [] field = new java.lang.reflect.Field[numNonNullFields];
858            for (int i = 0, fieldNum = 0; i < fields.length; i++) {
859                if (fields[i].getField() != null) {
860                    field[fieldNum++] = fields[i].getField();
861                }
862            }
863
864            if (field.length > 1)
865                Arrays.sort(field, compareMemberByName);
866
867            for (int i = 0; i < field.length; i++) {
868                Field f = field[i];
869
870                                /* Include in the hash all fields except those that are
871                                 * transient
872                                 */
873                int m = f.getModifiers();
874                //Serial 6
875                //if (Modifier.isTransient(m) || Modifier.isStatic(m))
876                // spec reference 00-01-06.pdf, 1.3.5.6, states non-static
877                // non-transient, public fields are mapped to Java IDL.
878                //
879                // Here's the quote from the first paragraph:
880                // Java non-static non-transient public fields are mapped to
881                // OMG IDL public data members, and other Java fields are
882                // not mapped.
883
884                // if (Modifier.isTransient(m) || Modifier.isStatic(m))
885                //     continue;
886
887                data.writeUTF(f.getName());
888                data.writeUTF(getSignature(f.getType()));
889            }
890
891            /* Compute the hash value for this class.
892             * Use only the first 64 bits of the hash.
893             */
894            data.flush();
895            byte hasharray[] = md.digest();
896            // int minimum = Math.min(8, hasharray.length);
897            // SerialBug 3: SHA computation is wrong; for loop reversed
898            //for (int i = minimum; i > 0; i--)
899            for (int i = 0; i < Math.min(8, hasharray.length); i++) {
900                h += (long)(hasharray[i] & 255) << (i * 8);
901            }
902        } catch (IOException ignore) {
903            /* can't happen, but be deterministic anyway. */
904            h = -1;
905        } catch (NoSuchAlgorithmException complain) {
906            throw new SecurityException(complain.getMessage());
907        }
908        return h;
909    }
910
911    /**
912     * Compute the JVM signature for the class.
913     */
914    static String getSignature(Class<?> clazz) {
915        String type = null;
916        if (clazz.isArray()) {
917            Class<?> cl = clazz;
918            int dimensions = 0;
919            while (cl.isArray()) {
920                dimensions++;
921                cl = cl.getComponentType();
922            }
923            StringBuffer sb = new StringBuffer();
924            for (int i = 0; i < dimensions; i++) {
925                sb.append("[");
926            }
927            sb.append(getSignature(cl));
928            type = sb.toString();
929        } else if (clazz.isPrimitive()) {
930            if (clazz == Integer.TYPE) {
931                type = "I";
932            } else if (clazz == Byte.TYPE) {
933                type = "B";
934            } else if (clazz == Long.TYPE) {
935                type = "J";
936            } else if (clazz == Float.TYPE) {
937                type = "F";
938            } else if (clazz == Double.TYPE) {
939                type = "D";
940            } else if (clazz == Short.TYPE) {
941                type = "S";
942            } else if (clazz == Character.TYPE) {
943                type = "C";
944            } else if (clazz == Boolean.TYPE) {
945                type = "Z";
946            } else if (clazz == Void.TYPE) {
947                type = "V";
948            }
949        } else {
950            type = "L" + clazz.getName().replace('.', '/') + ";";
951        }
952        return type;
953    }
954
955    /*
956     * Compute the JVM method descriptor for the method.
957     */
958    static String getSignature(Method meth) {
959        StringBuffer sb = new StringBuffer();
960
961        sb.append("(");
962
963        Class<?>[] params = meth.getParameterTypes(); // avoid clone
964        for (int j = 0; j < params.length; j++) {
965            sb.append(getSignature(params[j]));
966        }
967        sb.append(")");
968        sb.append(getSignature(meth.getReturnType()));
969        return sb.toString();
970    }
971
972    /*
973     * Compute the JVM constructor descriptor for the constructor.
974     */
975    static String getSignature(Constructor cons) {
976        StringBuffer sb = new StringBuffer();
977
978        sb.append("(");
979
980        Class<?>[] params = cons.getParameterTypes(); // avoid clone
981        for (int j = 0; j < params.length; j++) {
982            sb.append(getSignature(params[j]));
983        }
984        sb.append(")V");
985        return sb.toString();
986    }
987
988    /*
989     * Cache of Class -> ClassDescriptor Mappings.
990     */
991    static private ObjectStreamClassEntry[] descriptorFor = new ObjectStreamClassEntry[61];
992
993    /*
994     * findDescriptorFor a Class.  This looks in the cache for a
995     * mapping from Class -> ObjectStreamClass mappings.  The hashCode
996     * of the Class is used for the lookup since the Class is the key.
997     * The entries are extended from java.lang.ref.SoftReference so the
998     * gc will be able to free them if needed.
999     */
1000    private static ObjectStreamClass_1_3_1 findDescriptorFor(Class<?> cl) {
1001
1002        int hash = cl.hashCode();
1003        int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
1004        ObjectStreamClassEntry e;
1005        ObjectStreamClassEntry prev;
1006
1007        /* Free any initial entries whose refs have been cleared */
1008        while ((e = descriptorFor[index]) != null && e.get() == null) {
1009            descriptorFor[index] = e.next;
1010        }
1011
1012        /* Traverse the chain looking for a descriptor with ofClass == cl.
1013         * unlink entries that are unresolved.
1014         */
1015        prev = e;
1016        while (e != null ) {
1017            ObjectStreamClass_1_3_1 desc = (ObjectStreamClass_1_3_1)(e.get());
1018            if (desc == null) {
1019                // This entry has been cleared,  unlink it
1020                prev.next = e.next;
1021            } else {
1022                if (desc.ofClass == cl)
1023                    return desc;
1024                prev = e;
1025            }
1026            e = e.next;
1027        }
1028        return null;
1029    }
1030
1031    /*
1032     * insertDescriptorFor a Class -> ObjectStreamClass_1_3_1 mapping.
1033     */
1034    private static void insertDescriptorFor(ObjectStreamClass_1_3_1 desc) {
1035        // Make sure not already present
1036        if (findDescriptorFor(desc.ofClass) != null) {
1037            return;
1038        }
1039
1040        int hash = desc.ofClass.hashCode();
1041        int index = (hash & 0x7FFFFFFF) % descriptorFor.length;
1042        ObjectStreamClassEntry e = new ObjectStreamClassEntry(desc);
1043        e.next = descriptorFor[index];
1044        descriptorFor[index] = e;
1045    }
1046
1047    private static Field[] getDeclaredFields(final Class clz) {
1048        return (Field[]) AccessController.doPrivileged(new PrivilegedAction() {
1049            public Object run() {
1050                return clz.getDeclaredFields();
1051            }
1052        });
1053    }
1054
1055
1056    /*
1057     * The name of this descriptor
1058     */
1059    private String name;
1060
1061    /*
1062     * The descriptor of the supertype.
1063     */
1064    private ObjectStreamClass_1_3_1 superclass;
1065
1066    /*
1067     * Flags for Serializable and Externalizable.
1068     */
1069    private boolean serializable;
1070    private boolean externalizable;
1071
1072    /*
1073     * Array of persistent fields of this class, sorted by
1074     * type and name.
1075     */
1076    private ObjectStreamField[] fields;
1077
1078    /*
1079     * Class that is a descriptor for in this virtual machine.
1080     */
1081    private Class<?> ofClass;
1082
1083    /*
1084     * True if descriptor for a proxy class.
1085     */
1086    boolean forProxyClass;
1087
1088
1089    /*
1090     * SerialVersionUID for this class.
1091     */
1092    private long suid = kDefaultUID;
1093    private String suidStr = null;
1094
1095    /*
1096     * Actual (computed) SerialVersionUID for this class.
1097     */
1098    private long actualSuid = kDefaultUID;
1099    private String actualSuidStr = null;
1100
1101    /*
1102     * The total number of bytes of primitive fields.
1103     * The total number of object fields.
1104     */
1105    int primBytes;
1106    int objFields;
1107
1108    /* Internal lock object. */
1109    private Object lock = new Object();
1110
1111    /* True if this class has/had a writeObject method */
1112    private boolean hasWriteObjectMethod;
1113
1114    /* In JDK 1.1, external data was not written in block mode.
1115     * As of JDK 1.2, external data is written in block data mode. This
1116     * flag enables JDK 1.2 to be able to read JDK 1.1 written external data.
1117     *
1118     * @since JDK 1.2
1119     */
1120    private boolean hasExternalizableBlockData;
1121    Method writeObjectMethod;
1122    Method readObjectMethod;
1123    private transient Method writeReplaceObjectMethod;
1124    private transient Method readResolveObjectMethod;
1125
1126    /*
1127     * ObjectStreamClass_1_3_1 that this one was built from.
1128     */
1129    private ObjectStreamClass_1_3_1 localClassDesc;
1130
1131    /* Get the private static final field for serial version UID */
1132    // private static native long getSerialVersionUIDField(Class cl);
1133
1134    /** use serialVersionUID from JDK 1.1. for interoperability */
1135    private static final long serialVersionUID = -6120832682080437368L;
1136
1137    /**
1138     * Set serialPersistentFields of a Serializable class to this value to
1139     * denote that the class has no Serializable fields.
1140     */
1141    public static final ObjectStreamField[] NO_FIELDS =
1142        new ObjectStreamField[0];
1143
1144    /*
1145     * Entries held in the Cache of known ObjectStreamClass_1_3_1 objects.
1146     * Entries are chained together with the same hash value (modulo array size).
1147     */
1148    private static class ObjectStreamClassEntry // extends java.lang.ref.SoftReference
1149    {
1150        ObjectStreamClassEntry(ObjectStreamClass_1_3_1 c) {
1151            //super(c);
1152            this.c = c;
1153        }
1154        ObjectStreamClassEntry next;
1155
1156        public Object get()
1157        {
1158            return c;
1159        }
1160        private ObjectStreamClass_1_3_1 c;
1161    }
1162
1163    /*
1164     * Comparator object for Classes and Interfaces
1165     */
1166    private static Comparator compareClassByName =
1167        new CompareClassByName();
1168
1169    private static class CompareClassByName implements Comparator {
1170        public int compare(Object o1, Object o2) {
1171            Class<?> c1 = (Class)o1;
1172            Class<?> c2 = (Class)o2;
1173            return (c1.getName()).compareTo(c2.getName());
1174        }
1175    }
1176
1177    /*
1178     * Comparator object for Members, Fields, and Methods
1179     */
1180    private static Comparator compareMemberByName =
1181        new CompareMemberByName();
1182
1183    private static class CompareMemberByName implements Comparator {
1184        public int compare(Object o1, Object o2) {
1185            String s1 = ((Member)o1).getName();
1186            String s2 = ((Member)o2).getName();
1187
1188            if (o1 instanceof Method) {
1189                s1 += getSignature((Method)o1);
1190                s2 += getSignature((Method)o2);
1191            } else if (o1 instanceof Constructor) {
1192                s1 += getSignature((Constructor)o1);
1193                s2 += getSignature((Constructor)o2);
1194            }
1195            return s1.compareTo(s2);
1196        }
1197    }
1198
1199    /* It is expensive to recompute a method or constructor signature
1200       many times, so compute it only once using this data structure. */
1201    private static class MethodSignature implements Comparator {
1202        Member member;
1203        String signature;      // cached parameter signature
1204
1205        /* Given an array of Method or Constructor members,
1206           return a sorted array of the non-private members.*/
1207        /* A better implementation would be to implement the returned data
1208           structure as an insertion sorted link list.*/
1209        static MethodSignature[] removePrivateAndSort(Member[] m) {
1210            int numNonPrivate = 0;
1211            for (int i = 0; i < m.length; i++) {
1212                if (! Modifier.isPrivate(m[i].getModifiers())) {
1213                    numNonPrivate++;
1214                }
1215            }
1216            MethodSignature[] cm = new MethodSignature[numNonPrivate];
1217            int cmi = 0;
1218            for (int i = 0; i < m.length; i++) {
1219                if (! Modifier.isPrivate(m[i].getModifiers())) {
1220                    cm[cmi] = new MethodSignature(m[i]);
1221                    cmi++;
1222                }
1223            }
1224            if (cmi > 0)
1225                Arrays.sort(cm, cm[0]);
1226            return cm;
1227        }
1228
1229        /* Assumes that o1 and o2 are either both methods
1230           or both constructors.*/
1231        public int compare(Object o1, Object o2) {
1232            /* Arrays.sort calls compare when o1 and o2 are equal.*/
1233            if (o1 == o2)
1234                return 0;
1235
1236            MethodSignature c1 = (MethodSignature)o1;
1237            MethodSignature c2 = (MethodSignature)o2;
1238
1239            int result;
1240            if (isConstructor()) {
1241                result = c1.signature.compareTo(c2.signature);
1242            } else { // is a Method.
1243                result = c1.member.getName().compareTo(c2.member.getName());
1244                if (result == 0)
1245                    result = c1.signature.compareTo(c2.signature);
1246            }
1247            return result;
1248        }
1249
1250        final private boolean isConstructor() {
1251            return member instanceof Constructor;
1252        }
1253        private MethodSignature(Member m) {
1254            member = m;
1255            if (isConstructor()) {
1256                signature = ObjectStreamClass_1_3_1.getSignature((Constructor)m);
1257            } else {
1258                signature = ObjectStreamClass_1_3_1.getSignature((Method)m);
1259            }
1260        }
1261    }
1262}
1263