ObjectStreamClass.java revision ad41624e761bcf1af9c8008eb45187fc13983717
1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18package java.io;
19
20import java.lang.ref.SoftReference;
21import java.lang.reflect.Constructor;
22import java.lang.reflect.Field;
23import java.lang.reflect.Method;
24import java.lang.reflect.Modifier;
25import java.lang.reflect.Proxy;
26import java.nio.ByteOrder;
27import java.security.MessageDigest;
28import java.security.NoSuchAlgorithmException;
29import java.util.ArrayList;
30import java.util.Arrays;
31import java.util.Comparator;
32import java.util.HashMap;
33import java.util.List;
34import java.util.WeakHashMap;
35import libcore.base.EmptyArray;
36import org.apache.harmony.luni.platform.OSMemory;
37
38/**
39 * Represents a descriptor for identifying a class during serialization and
40 * deserialization. Information contained in the descriptor includes the name
41 * and SUID of the class as well as field names and types. Information inherited
42 * from the superclasses is also taken into account.
43 *
44 * @see ObjectOutputStream
45 * @see ObjectInputStream
46 * @see java.lang.Class
47 */
48public class ObjectStreamClass implements Serializable {
49
50    // No need to compute the SUID for ObjectStreamClass, just use the value
51    // below
52    private static final long serialVersionUID = -6120832682080437368L;
53
54    // Name of the field that contains the SUID value (if present)
55    private static final String UID_FIELD_NAME = "serialVersionUID";
56
57    static final long CONSTRUCTOR_IS_NOT_RESOLVED = -1;
58
59    private static final int CLASS_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.FINAL |
60            Modifier.INTERFACE | Modifier.ABSTRACT;
61
62    private static final int FIELD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE |
63            Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | Modifier.VOLATILE |
64            Modifier.TRANSIENT;
65
66    private static final int METHOD_MODIFIERS_MASK = Modifier.PUBLIC | Modifier.PRIVATE |
67            Modifier.PROTECTED | Modifier.STATIC | Modifier.FINAL | Modifier.SYNCHRONIZED |
68            Modifier.NATIVE | Modifier.ABSTRACT | Modifier.STRICT;
69
70    private static final Class<?>[] READ_PARAM_TYPES = new Class[] { ObjectInputStream.class };
71    private static final Class<?>[] WRITE_PARAM_TYPES = new Class[] { ObjectOutputStream.class };
72
73    /**
74     * Constant indicating that the class has no Serializable fields.
75     */
76    public static final ObjectStreamField[] NO_FIELDS = new ObjectStreamField[0];
77
78    /*
79     * used to fetch field serialPersistentFields and checking its type
80     */
81    static final Class<?> ARRAY_OF_FIELDS;
82
83    static {
84        try {
85            ARRAY_OF_FIELDS = Class.forName("[Ljava.io.ObjectStreamField;");
86        } catch (ClassNotFoundException e) {
87            // This should not happen
88            throw new AssertionError(e);
89        }
90    }
91
92    private static final String CLINIT_NAME = "<clinit>";
93
94    private static final int CLINIT_MODIFIERS = Modifier.STATIC;
95
96    private static final String CLINIT_SIGNATURE = "()V";
97
98    // Used to determine if an object is Serializable or Externalizable
99    private static final Class<Serializable> SERIALIZABLE = Serializable.class;
100
101    private static final Class<Externalizable> EXTERNALIZABLE = Externalizable.class;
102
103    // Used to test if the object is a String or a class.
104    static final Class<String> STRINGCLASS = String.class;
105
106    static final Class<?> CLASSCLASS = Class.class;
107
108    static final Class<ObjectStreamClass> OBJECTSTREAMCLASSCLASS = ObjectStreamClass.class;
109
110    private transient Method methodWriteReplace;
111
112    private transient Method methodReadResolve;
113
114    private transient Method methodWriteObject;
115
116    private transient Method methodReadObject;
117
118    private transient Method methodReadObjectNoData;
119
120    /**
121     * Indicates whether the class properties resolved
122     *
123     * @see #resolveProperties()
124     */
125    private transient boolean arePropertiesResolved;
126
127    /**
128     * Cached class properties
129     *
130     * @see #resolveProperties()
131     * @see #isSerializable()
132     * @see #isExternalizable()
133     * @see #isProxy()
134     * @see #isEnum()
135     */
136    private transient boolean isSerializable;
137    private transient boolean isExternalizable;
138    private transient boolean isProxy;
139    private transient boolean isEnum;
140
141    // ClassDesc //
142
143    // Name of the class this descriptor represents
144    private transient String className;
145
146    // Corresponding loaded class with the name above
147    private transient Class<?> resolvedClass;
148
149    private transient Class<?> resolvedConstructorClass;
150    private transient int resolvedConstructorMethodId;
151
152    // Serial version UID of the class the descriptor represents
153    private transient long svUID;
154
155    // ClassDescInfo //
156
157    // Any combination of SC_WRITE_METHOD, SC_SERIALIZABLE and SC_EXTERNALIZABLE
158    // (see ObjectStreamConstants)
159    private transient byte flags;
160
161    // Descriptor for the superclass of the class associated with this
162    // descriptor
163    private transient ObjectStreamClass superclass;
164
165    // Array of ObjectStreamField (see below) describing the fields of this
166    // class
167    private transient ObjectStreamField[] fields;
168
169    // Array of ObjectStreamField describing the serialized fields of this class
170    private transient ObjectStreamField[] loadFields;
171
172    // ObjectStreamField doesn't override hashCode or equals, so this is equivalent to an
173    // IdentityHashMap, which is fine for our purposes.
174    private transient HashMap<ObjectStreamField, Field> reflectionFields =
175            new HashMap<ObjectStreamField, Field>();
176
177    // MethodID for deserialization constructor
178    private transient long constructor = CONSTRUCTOR_IS_NOT_RESOLVED;
179
180    void setConstructor(long newConstructor) {
181        constructor = newConstructor;
182    }
183
184    long getConstructor() {
185        return constructor;
186    }
187
188    Field getReflectionField(ObjectStreamField osf) {
189        synchronized (reflectionFields) {
190            Field field = reflectionFields.get(osf);
191            if (field != null) {
192                return field;
193            }
194        }
195
196        try {
197            Class<?> declaringClass = forClass();
198            Field field = declaringClass.getDeclaredField(osf.getName());
199            field.setAccessible(true);
200            synchronized (reflectionFields) {
201                reflectionFields.put(osf, field);
202            }
203            return reflectionFields.get(osf);
204        } catch (NoSuchFieldException ex) {
205            // The caller messed up. We'll return null and won't try to resolve this again.
206            return null;
207        }
208    }
209
210    /*
211     * If an ObjectStreamClass describes an Externalizable class, it (the
212     * descriptor) should not have field descriptors (ObjectStreamField) at all.
213     * The ObjectStreamClass that gets saved should simply have no field info.
214     * This is a footnote in page 1511 (class Serializable) of "The Java Class
215     * Libraries, Second Edition, Vol. I".
216     */
217
218    /**
219     * Constructs a new instance of this class.
220     */
221    ObjectStreamClass() {
222        super();
223    }
224
225    /**
226     * Compute class descriptor for a given class <code>cl</code>.
227     *
228     * @param cl
229     *            a java.langClass for which to compute the corresponding
230     *            descriptor
231     * @return the computer class descriptor
232     */
233    private static ObjectStreamClass createClassDesc(Class<?> cl) {
234
235        ObjectStreamClass result = new ObjectStreamClass();
236
237        boolean isArray = cl.isArray();
238        boolean serializable = isSerializable(cl);
239        boolean externalizable = isExternalizable(cl);
240
241        result.isSerializable = serializable;
242        result.isExternalizable = externalizable;
243
244        // Now we fill in the values
245        result.setName(cl.getName());
246        result.setClass(cl);
247        Class<?> superclass = cl.getSuperclass();
248        if (superclass != null) {
249            result.setSuperclass(lookup(superclass));
250        }
251
252        Field[] declaredFields = null;
253
254        // Compute the SUID
255        if (serializable || externalizable) {
256            if (result.isEnum() || result.isProxy()) {
257                result.setSerialVersionUID(0L);
258            } else {
259                declaredFields = cl.getDeclaredFields();
260                result.setSerialVersionUID(computeSerialVersionUID(cl, declaredFields));
261            }
262        }
263
264        // Serializables need field descriptors
265        if (serializable && !isArray) {
266            if (declaredFields == null) {
267                declaredFields = cl.getDeclaredFields();
268            }
269            result.buildFieldDescriptors(declaredFields);
270        } else {
271            // Externalizables or arrays do not need FieldDesc info
272            result.setFields(NO_FIELDS);
273        }
274
275        // Copy all fields to loadFields - they should be read by default in
276        // ObjectInputStream.defaultReadObject() method
277        ObjectStreamField[] fields = result.getFields();
278
279        if (fields != null) {
280            ObjectStreamField[] loadFields = new ObjectStreamField[fields.length];
281
282            for (int i = 0; i < fields.length; ++i) {
283                loadFields[i] = new ObjectStreamField(fields[i].getName(),
284                        fields[i].getType(), fields[i].isUnshared());
285
286                // resolve type string to init typeString field in
287                // ObjectStreamField
288                loadFields[i].getTypeString();
289            }
290            result.setLoadFields(loadFields);
291        }
292
293        byte flags = 0;
294        if (externalizable) {
295            flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
296            flags |= ObjectStreamConstants.SC_BLOCK_DATA; // use protocol version 2 by default
297        } else if (serializable) {
298            flags |= ObjectStreamConstants.SC_SERIALIZABLE;
299        }
300        result.methodWriteReplace = findMethod(cl, "writeReplace");
301        result.methodReadResolve = findMethod(cl, "readResolve");
302        result.methodWriteObject = findPrivateMethod(cl, "writeObject", WRITE_PARAM_TYPES);
303        result.methodReadObject = findPrivateMethod(cl, "readObject", READ_PARAM_TYPES);
304        result.methodReadObjectNoData = findPrivateMethod(cl, "readObjectNoData", EmptyArray.CLASS);
305        if (result.hasMethodWriteObject()) {
306            flags |= ObjectStreamConstants.SC_WRITE_METHOD;
307        }
308        result.setFlags(flags);
309
310        return result;
311    }
312
313    /**
314     * Builds the collection of field descriptors for the receiver
315     *
316     * @param declaredFields
317     *            collection of java.lang.reflect.Field for which to compute
318     *            field descriptors
319     */
320    void buildFieldDescriptors(Field[] declaredFields) {
321        // We could find the field ourselves in the collection, but calling
322        // reflect is easier. Optimize if needed.
323        final Field f = ObjectStreamClass.fieldSerialPersistentFields(this.forClass());
324        // If we could not find the emulated fields, we'll have to compute
325        // dumpable fields from reflect fields
326        boolean useReflectFields = f == null; // Assume we will compute the
327        // fields to dump based on the
328        // reflect fields
329
330        ObjectStreamField[] _fields = null;
331        if (!useReflectFields) {
332            // The user declared a collection of emulated fields. Use them.
333            // We have to be able to fetch its value, even if it is private
334            f.setAccessible(true);
335            try {
336                // static field, pass null
337                _fields = (ObjectStreamField[]) f.get(null);
338            } catch (IllegalAccessException ex) {
339                throw new AssertionError(ex);
340            }
341        } else {
342            // Compute collection of dumpable fields based on reflect fields
343            List<ObjectStreamField> serializableFields =
344                    new ArrayList<ObjectStreamField>(declaredFields.length);
345            // Filter, we are only interested in fields that are serializable
346            for (Field declaredField : declaredFields) {
347                int modifiers = declaredField.getModifiers();
348                if (!Modifier.isStatic(modifiers) && !Modifier.isTransient(modifiers)) {
349                    ObjectStreamField field = new ObjectStreamField(declaredField.getName(),
350                            declaredField.getType());
351                    serializableFields.add(field);
352                }
353            }
354
355            if (serializableFields.size() == 0) {
356                _fields = NO_FIELDS; // If no serializable fields, share the
357                // special value so that users can test
358            } else {
359                _fields = serializableFields.toArray(new ObjectStreamField[serializableFields.size()]);
360            }
361        }
362        Arrays.sort(_fields);
363        // assign offsets
364        int primOffset = 0, objectOffset = 0;
365        for (int i = 0; i < _fields.length; i++) {
366            Class<?> type = _fields[i].getType();
367            if (type.isPrimitive()) {
368                _fields[i].offset = primOffset;
369                primOffset += primitiveSize(type);
370            } else {
371                _fields[i].offset = objectOffset++;
372            }
373        }
374        fields = _fields;
375    }
376
377    /**
378     * Compute and return the Serial Version UID of the class {@code cl}.
379     * The value is computed based on the class name, superclass chain, field
380     * names, method names, modifiers, etc.
381     *
382     * @param cl
383     *            a java.lang.Class for which to compute the SUID
384     * @param fields
385     *            cl.getDeclaredFields(), pre-computed by the caller
386     * @return the value of SUID of this class
387     */
388    private static long computeSerialVersionUID(Class<?> cl, Field[] fields) {
389        /*
390         * First we should try to fetch the static slot 'static final long
391         * serialVersionUID'. If it is defined, return it. If not defined, we
392         * really need to compute SUID using SHAOutputStream
393         */
394        for (int i = 0; i < fields.length; i++) {
395            final Field field = fields[i];
396            if (Long.TYPE == field.getType()) {
397                int modifiers = field.getModifiers();
398                if (Modifier.isStatic(modifiers) && Modifier.isFinal(modifiers)) {
399                    if (UID_FIELD_NAME.equals(field.getName())) {
400                        /*
401                         * We need to be able to see it even if we have no
402                         * visibility. That is why we set accessible first (new
403                         * API in reflect 1.2)
404                         */
405                        field.setAccessible(true);
406                        try {
407                            // Static field, parameter is ignored
408                            return field.getLong(null);
409                        } catch (IllegalAccessException iae) {
410                            throw new RuntimeException("Error fetching SUID: " + iae);
411                        }
412                    }
413                }
414            }
415        }
416
417        MessageDigest digest;
418        try {
419            digest = MessageDigest.getInstance("SHA");
420        } catch (NoSuchAlgorithmException e) {
421            throw new Error(e);
422        }
423        ByteArrayOutputStream sha = new ByteArrayOutputStream();
424        try {
425            DataOutputStream output = new DataOutputStream(sha);
426            output.writeUTF(cl.getName());
427            int classModifiers = CLASS_MODIFIERS_MASK & cl.getModifiers();
428            /*
429             * Workaround for 1F9LOQO. Arrays are ABSTRACT in JDK, but that is
430             * not in the specification. Since we want to be compatible for
431             * X-loading, we have to pretend we have the same shape
432             */
433            boolean isArray = cl.isArray();
434            if (isArray) {
435                classModifiers |= Modifier.ABSTRACT;
436            }
437            // Required for JDK UID compatibility
438            if (cl.isInterface() && !Modifier.isPublic(classModifiers)) {
439                classModifiers &= ~Modifier.ABSTRACT;
440            }
441            output.writeInt(classModifiers);
442
443            /*
444             * In JDK1.2 arrays implement Cloneable and Serializable but not in
445             * JDK 1.1.7. So, JDK 1.2 "pretends" arrays have no interfaces when
446             * computing SHA-1 to be compatible.
447             */
448            if (!isArray) {
449                // Interface information
450                Class<?>[] interfaces = cl.getInterfaces();
451                if (interfaces.length > 1) {
452                    // Only attempt to sort if really needed (saves object
453                    // creation, etc)
454                    Comparator<Class<?>> interfaceComparator = new Comparator<Class<?>>() {
455                        public int compare(Class<?> itf1, Class<?> itf2) {
456                            return itf1.getName().compareTo(itf2.getName());
457                        }
458                    };
459                    Arrays.sort(interfaces, interfaceComparator);
460                }
461
462                // Dump them
463                for (int i = 0; i < interfaces.length; i++) {
464                    output.writeUTF(interfaces[i].getName());
465                }
466            }
467
468            // Field information
469            if (fields.length > 1) {
470                // Only attempt to sort if really needed (saves object creation,
471                // etc)
472                Comparator<Field> fieldComparator = new Comparator<Field>() {
473                    public int compare(Field field1, Field field2) {
474                        return field1.getName().compareTo(field2.getName());
475                    }
476                };
477                Arrays.sort(fields, fieldComparator);
478            }
479
480            // Dump them
481            for (int i = 0; i < fields.length; i++) {
482                Field field = fields[i];
483                int modifiers = field.getModifiers() & FIELD_MODIFIERS_MASK;
484
485                boolean skip = Modifier.isPrivate(modifiers)
486                        && (Modifier.isTransient(modifiers) || Modifier
487                                .isStatic(modifiers));
488                if (!skip) {
489                    // write name, modifier & "descriptor" of all but private
490                    // static and private transient
491                    output.writeUTF(field.getName());
492                    output.writeInt(modifiers);
493                    output
494                            .writeUTF(descriptorForFieldSignature(getFieldSignature(field)));
495                }
496            }
497
498            /*
499             * Normally constructors come before methods (because <init> <
500             * anyMethodName). However, <clinit> is an exception. Besides,
501             * reflect will not let us get to it.
502             */
503            if (hasClinit(cl)) {
504                // write name, modifier & "descriptor"
505                output.writeUTF(CLINIT_NAME);
506                output.writeInt(CLINIT_MODIFIERS);
507                output.writeUTF(CLINIT_SIGNATURE);
508            }
509
510            // Constructor information
511            Constructor<?>[] constructors = cl.getDeclaredConstructors();
512            if (constructors.length > 1) {
513                // Only attempt to sort if really needed (saves object creation,
514                // etc)
515                Comparator<Constructor<?>> constructorComparator = new Comparator<Constructor<?>>() {
516                    public int compare(Constructor<?> ctr1, Constructor<?> ctr2) {
517                        // All constructors have same name, so we sort based on
518                        // signature
519                        return (getConstructorSignature(ctr1)
520                                .compareTo(getConstructorSignature(ctr2)));
521                    }
522                };
523                Arrays.sort(constructors, constructorComparator);
524            }
525
526            // Dump them
527            for (int i = 0; i < constructors.length; i++) {
528                Constructor<?> constructor = constructors[i];
529                int modifiers = constructor.getModifiers()
530                        & METHOD_MODIFIERS_MASK;
531                boolean isPrivate = Modifier.isPrivate(modifiers);
532                if (!isPrivate) {
533                    /*
534                     * write name, modifier & "descriptor" of all but private
535                     * ones
536                     *
537                     * constructor.getName() returns the constructor name as
538                     * typed, not the VM name
539                     */
540                    output.writeUTF("<init>");
541                    output.writeInt(modifiers);
542                    output.writeUTF(descriptorForSignature(
543                            getConstructorSignature(constructor)).replace('/',
544                            '.'));
545                }
546            }
547
548            // Method information
549            Method[] methods = cl.getDeclaredMethods();
550            if (methods.length > 1) {
551                Comparator<Method> methodComparator = new Comparator<Method>() {
552                    public int compare(Method m1, Method m2) {
553                        int result = m1.getName().compareTo(m2.getName());
554                        if (result == 0) {
555                            // same name, signature will tell which one comes
556                            // first
557                            return getMethodSignature(m1).compareTo(
558                                    getMethodSignature(m2));
559                        }
560                        return result;
561                    }
562                };
563                Arrays.sort(methods, methodComparator);
564            }
565
566            // Dump them
567            for (int i = 0; i < methods.length; i++) {
568                Method method = methods[i];
569                int modifiers = method.getModifiers() & METHOD_MODIFIERS_MASK;
570                boolean isPrivate = Modifier.isPrivate(modifiers);
571                if (!isPrivate) {
572                    // write name, modifier & "descriptor" of all but private
573                    // ones
574                    output.writeUTF(method.getName());
575                    output.writeInt(modifiers);
576                    output.writeUTF(descriptorForSignature(
577                            getMethodSignature(method)).replace('/', '.'));
578                }
579            }
580        } catch (IOException e) {
581            throw new RuntimeException(e + " computing SHA-1/SUID");
582        }
583
584        // now compute the UID based on the SHA
585        byte[] hash = digest.digest(sha.toByteArray());
586        return OSMemory.peekLong(hash, 0, ByteOrder.LITTLE_ENDIAN);
587    }
588
589    /**
590     * Returns what the serialization specification calls "descriptor" given a
591     * field signature.
592     *
593     * @param signature
594     *            a field signature
595     * @return containing the descriptor
596     */
597    private static String descriptorForFieldSignature(String signature) {
598        return signature.replace('.', '/');
599    }
600
601    /**
602     * Return what the serialization specification calls "descriptor" given a
603     * method/constructor signature.
604     *
605     * @param signature
606     *            a method or constructor signature
607     * @return containing the descriptor
608     */
609    private static String descriptorForSignature(String signature) {
610        return signature.substring(signature.indexOf("("));
611    }
612
613    /**
614     * Return the java.lang.reflect.Field {@code serialPersistentFields}
615     * if class {@code cl} implements it. Return null otherwise.
616     *
617     * @param cl
618     *            a java.lang.Class which to test
619     * @return {@code java.lang.reflect.Field} if the class has
620     *         serialPersistentFields {@code null} if the class does not
621     *         have serialPersistentFields
622     */
623    static Field fieldSerialPersistentFields(Class<?> cl) {
624        try {
625            Field f = cl.getDeclaredField("serialPersistentFields");
626            int modifiers = f.getModifiers();
627            if (Modifier.isStatic(modifiers) && Modifier.isPrivate(modifiers)
628                    && Modifier.isFinal(modifiers)) {
629                if (f.getType() == ARRAY_OF_FIELDS) {
630                    return f;
631                }
632            }
633        } catch (NoSuchFieldException nsm) {
634            // Ignored
635        }
636        return null;
637    }
638
639    /**
640     * Returns the class (java.lang.Class) for this descriptor.
641     *
642     * @return the class in the local VM that this descriptor represents;
643     *         {@code null} if there is no corresponding class.
644     */
645    public Class<?> forClass() {
646        return resolvedClass;
647    }
648
649    /**
650     * Create and return a new instance of class 'instantiationClass'
651     * using JNI to call the constructor chosen by resolveConstructorClass.
652     *
653     * The returned instance may have uninitialized fields, including final fields.
654     */
655    Object newInstance(Class<?> instantiationClass) throws InvalidClassException {
656        resolveConstructorClass(instantiationClass);
657        return newInstance(instantiationClass, resolvedConstructorMethodId);
658    }
659    private static native Object newInstance(Class<?> instantiationClass, int methodId);
660
661    private Class<?> resolveConstructorClass(Class<?> objectClass) throws InvalidClassException {
662        if (resolvedConstructorClass != null) {
663            return resolvedConstructorClass;
664        }
665
666        // The class of the instance may not be the same as the class of the
667        // constructor to run
668        // This is the constructor to run if Externalizable
669        Class<?> constructorClass = objectClass;
670
671        // WARNING - What if the object is serializable and externalizable ?
672        // Is that possible ?
673        boolean wasSerializable = (flags & ObjectStreamConstants.SC_SERIALIZABLE) != 0;
674        if (wasSerializable) {
675            // Now we must run the constructor of the class just above the
676            // one that implements Serializable so that slots that were not
677            // dumped can be initialized properly
678            while (constructorClass != null && ObjectStreamClass.isSerializable(constructorClass)) {
679                constructorClass = constructorClass.getSuperclass();
680            }
681        }
682
683        // Fetch the empty constructor, or null if none.
684        Constructor<?> constructor = null;
685        if (constructorClass != null) {
686            try {
687                constructor = constructorClass.getDeclaredConstructor(EmptyArray.CLASS);
688            } catch (NoSuchMethodException ignored) {
689            }
690        }
691
692        // Has to have an empty constructor
693        if (constructor == null) {
694            String className = constructorClass != null ? constructorClass.getName() : null;
695            throw new InvalidClassException(className, "IllegalAccessException");
696        }
697
698        int constructorModifiers = constructor.getModifiers();
699        boolean isPublic = Modifier.isPublic(constructorModifiers);
700        boolean isProtected = Modifier.isProtected(constructorModifiers);
701        boolean isPrivate = Modifier.isPrivate(constructorModifiers);
702
703        // Now we must check if the empty constructor is visible to the
704        // instantiation class
705        boolean wasExternalizable = (flags & ObjectStreamConstants.SC_EXTERNALIZABLE) != 0;
706        if (isPrivate || (wasExternalizable && !isPublic)) {
707            throw new InvalidClassException(constructorClass.getName(), "IllegalAccessException");
708        }
709
710        // We know we are testing from a subclass, so the only other case
711        // where the visibility is not allowed is when the constructor has
712        // default visibility and the instantiation class is in a different
713        // package than the constructor class
714        if (!isPublic && !isProtected) {
715            // Not public, not private and not protected...means default
716            // visibility. Check if same package
717            if (!inSamePackage(constructorClass, objectClass)) {
718                throw new InvalidClassException(constructorClass.getName(), "IllegalAccessException");
719            }
720        }
721
722        resolvedConstructorClass = constructorClass;
723        resolvedConstructorMethodId = getConstructorId(resolvedConstructorClass);
724        return constructorClass;
725    }
726    private static native int getConstructorId(Class<?> c);
727
728    /**
729     * Checks if two classes belong to the same package.
730     *
731     * @param c1
732     *            one of the classes to test.
733     * @param c2
734     *            the other class to test.
735     * @return {@code true} if the two classes belong to the same package,
736     *         {@code false} otherwise.
737     */
738    private boolean inSamePackage(Class<?> c1, Class<?> c2) {
739        String nameC1 = c1.getName();
740        String nameC2 = c2.getName();
741        int indexDotC1 = nameC1.lastIndexOf('.');
742        int indexDotC2 = nameC2.lastIndexOf('.');
743        if (indexDotC1 != indexDotC2) {
744            return false; // cannot be in the same package if indices are not the same
745        }
746        if (indexDotC1 == -1) {
747            return true; // both of them are in default package
748        }
749        return nameC1.regionMatches(0, nameC2, 0, indexDotC1);
750    }
751
752    /**
753     * Return a String representing the signature for a Constructor {@code c}.
754     *
755     * @param c
756     *            a java.lang.reflect.Constructor for which to compute the
757     *            signature
758     * @return the constructor's signature
759     */
760    static native String getConstructorSignature(Constructor<?> c);
761
762    /**
763     * Gets a field descriptor of the class represented by this class
764     * descriptor.
765     *
766     * @param name
767     *            the name of the desired field.
768     * @return the field identified by {@code name} or {@code null} if there is
769     *         no such field.
770     */
771    public ObjectStreamField getField(String name) {
772        ObjectStreamField[] allFields = getFields();
773        for (int i = 0; i < allFields.length; i++) {
774            ObjectStreamField f = allFields[i];
775            if (f.getName().equals(name)) {
776                return f;
777            }
778        }
779        return null;
780    }
781
782    /**
783     * Returns the collection of field descriptors for the fields of the
784     * corresponding class
785     *
786     * @return the receiver's collection of declared fields for the class it
787     *         represents
788     */
789    ObjectStreamField[] fields() {
790        if (fields == null) {
791            Class<?> forCl = forClass();
792            if (forCl != null && isSerializable() && !forCl.isArray()) {
793                buildFieldDescriptors(forCl.getDeclaredFields());
794            } else {
795                // Externalizables or arrays do not need FieldDesc info
796                setFields(NO_FIELDS);
797            }
798        }
799        return fields;
800    }
801
802    /**
803     * Returns a collection of field descriptors for the serialized fields of
804     * the class represented by this class descriptor.
805     *
806     * @return an array of field descriptors or an array of length zero if there
807     *         are no fields in this descriptor's class.
808     */
809    public ObjectStreamField[] getFields() {
810        copyFieldAttributes();
811        return loadFields == null ? fields().clone() : loadFields.clone();
812    }
813
814    private volatile List<ObjectStreamClass> cachedHierarchy;
815
816    List<ObjectStreamClass> getHierarchy() {
817        List<ObjectStreamClass> result = cachedHierarchy;
818        if (result == null) {
819            cachedHierarchy = result = makeHierarchy();
820        }
821        return result;
822    }
823
824    private List<ObjectStreamClass> makeHierarchy() {
825        ArrayList<ObjectStreamClass> result = new ArrayList<ObjectStreamClass>();
826        for (ObjectStreamClass osc = this; osc != null; osc = osc.getSuperclass()) {
827            result.add(0, osc);
828        }
829        return result;
830    }
831
832    /**
833     * If a Class uses "serialPersistentFields" to define the serialized fields,
834     * this.loadFields cannot get the "unshared" information when deserializing
835     * fields using current implementation of ObjectInputStream. This method
836     * provides a way to copy the "unshared" attribute from this.fields.
837     *
838     */
839    private void copyFieldAttributes() {
840        if ((loadFields == null) || fields == null) {
841            return;
842        }
843
844        for (int i = 0; i < loadFields.length; i++) {
845            ObjectStreamField loadField = loadFields[i];
846            String name = loadField.getName();
847            for (int j = 0; j < fields.length; j++) {
848                ObjectStreamField field = fields[j];
849                if (name.equals(field.getName())) {
850                    loadField.setUnshared(field.isUnshared());
851                    loadField.setOffset(field.getOffset());
852                    break;
853                }
854            }
855        }
856    }
857
858    /**
859     * Returns the collection of field descriptors for the input fields of the
860     * corresponding class
861     *
862     * @return the receiver's collection of input fields for the class it
863     *         represents
864     */
865    ObjectStreamField[] getLoadFields() {
866        return loadFields;
867    }
868
869    /**
870     * Return a String representing the signature for a field {@code f}.
871     *
872     * @param f
873     *            a java.lang.reflect.Field for which to compute the signature
874     * @return the field's signature
875     */
876    private static native String getFieldSignature(Field f);
877
878    /**
879     * Returns the flags for this descriptor, where possible combined values are
880     *
881     * ObjectStreamConstants.SC_WRITE_METHOD
882     * ObjectStreamConstants.SC_SERIALIZABLE
883     * ObjectStreamConstants.SC_EXTERNALIZABLE
884     *
885     * @return byte the receiver's flags for the class it represents
886     */
887    byte getFlags() {
888        return flags;
889    }
890
891    /**
892     * Return a String representing the signature for a method {@code m}.
893     *
894     * @param m
895     *            a java.lang.reflect.Method for which to compute the signature
896     * @return the method's signature
897     */
898    static native String getMethodSignature(Method m);
899
900    /**
901     * Returns the name of the class represented by this descriptor.
902     *
903     * @return the fully qualified name of the class this descriptor represents.
904     */
905    public String getName() {
906        return className;
907    }
908
909    /**
910     * Returns the Serial Version User ID of the class represented by this
911     * descriptor.
912     *
913     * @return the SUID for the class represented by this descriptor.
914     */
915    public long getSerialVersionUID() {
916        return svUID;
917    }
918
919    /**
920     * Returns the descriptor (ObjectStreamClass) of the superclass of the class
921     * represented by the receiver.
922     *
923     * @return an ObjectStreamClass representing the superclass of the class
924     *         represented by the receiver.
925     */
926    ObjectStreamClass getSuperclass() {
927        return superclass;
928    }
929
930    /**
931     * Return true if the given class {@code cl} has the
932     * compiler-generated method {@code clinit}. Even though it is
933     * compiler-generated, it is used by the serialization code to compute SUID.
934     * This is unfortunate, since it may depend on compiler optimizations in
935     * some cases.
936     *
937     * @param cl
938     *            a java.lang.Class which to test
939     * @return {@code true} if the class has <clinit> {@code false}
940     *         if the class does not have <clinit>
941     */
942    private static native boolean hasClinit(Class<?> cl);
943
944    /**
945     * Return true if instances of class {@code cl} are Externalizable,
946     * false otherwise.
947     *
948     * @param cl
949     *            a java.lang.Class which to test
950     * @return {@code true} if instances of the class are Externalizable
951     *         {@code false} if instances of the class are not
952     *         Externalizable
953     *
954     * @see Object#hashCode
955     */
956    static boolean isExternalizable(Class<?> cl) {
957        return EXTERNALIZABLE.isAssignableFrom(cl);
958    }
959
960    /**
961     * Return true if the type code
962     * <code>typecode<code> describes a primitive type
963     *
964     * @param typecode a char describing the typecode
965     * @return {@code true} if the typecode represents a primitive type
966     * {@code false} if the typecode represents an Object type (including arrays)
967     *
968     * @see Object#hashCode
969     */
970    static boolean isPrimitiveType(char typecode) {
971        return !(typecode == '[' || typecode == 'L');
972    }
973
974    /**
975     * Return true if instances of class {@code cl} are Serializable,
976     * false otherwise.
977     *
978     * @param cl
979     *            a java.lang.Class which to test
980     * @return {@code true} if instances of the class are Serializable
981     *         {@code false} if instances of the class are not
982     *         Serializable
983     *
984     * @see Object#hashCode
985     */
986    static boolean isSerializable(Class<?> cl) {
987        return SERIALIZABLE.isAssignableFrom(cl);
988    }
989
990    /**
991     * Resolves the class properties, if they weren't already
992     */
993    private void resolveProperties() {
994        if (arePropertiesResolved) {
995            return;
996        }
997
998        Class<?> cl = forClass();
999        isProxy = Proxy.isProxyClass(cl);
1000        isEnum = Enum.class.isAssignableFrom(cl);
1001        isSerializable = isSerializable(cl);
1002        isExternalizable = isExternalizable(cl);
1003
1004        arePropertiesResolved = true;
1005    }
1006
1007    boolean isSerializable() {
1008        resolveProperties();
1009        return isSerializable;
1010    }
1011
1012    boolean isExternalizable() {
1013        resolveProperties();
1014        return isExternalizable;
1015    }
1016
1017    boolean isProxy() {
1018        resolveProperties();
1019        return isProxy;
1020    }
1021
1022    boolean isEnum() {
1023        resolveProperties();
1024        return isEnum;
1025    }
1026
1027    /**
1028     * Returns the descriptor for a serializable class.
1029     * Returns null if the class doesn't implement {@code Serializable} or {@code Externalizable}.
1030     *
1031     * @param cl
1032     *            a java.lang.Class for which to obtain the corresponding
1033     *            descriptor
1034     * @return the corresponding descriptor if the class is serializable or
1035     *         externalizable; null otherwise.
1036     */
1037    public static ObjectStreamClass lookup(Class<?> cl) {
1038        ObjectStreamClass osc = lookupStreamClass(cl);
1039        return (osc.isSerializable() || osc.isExternalizable()) ? osc : null;
1040    }
1041
1042    /**
1043     * Returns the descriptor for any class, whether or not the class
1044     * implements Serializable or Externalizable.
1045     *
1046     * @param cl
1047     *            a java.lang.Class for which to obtain the corresponding
1048     *            descriptor
1049     * @return the descriptor
1050     * @since 1.6
1051     */
1052    public static ObjectStreamClass lookupAny(Class<?> cl) {
1053        return lookupStreamClass(cl);
1054    }
1055
1056    /**
1057     * Return the descriptor (ObjectStreamClass) corresponding to the class
1058     * {@code cl}. Returns an ObjectStreamClass even if instances of the
1059     * class cannot be serialized
1060     *
1061     * @param cl
1062     *            a java.langClass for which to obtain the corresponding
1063     *            descriptor
1064     * @return the corresponding descriptor
1065     */
1066    static ObjectStreamClass lookupStreamClass(Class<?> cl) {
1067        WeakHashMap<Class<?>, ObjectStreamClass> tlc = getCache();
1068        ObjectStreamClass cachedValue = tlc.get(cl);
1069        if (cachedValue == null) {
1070            cachedValue = createClassDesc(cl);
1071            tlc.put(cl, cachedValue);
1072        }
1073        return cachedValue;
1074
1075    }
1076
1077    /**
1078     * A ThreadLocal cache for lookupStreamClass, with the possibility of discarding the thread
1079     * local storage content when the heap is exhausted.
1080     */
1081    private static SoftReference<ThreadLocal<WeakHashMap<Class<?>, ObjectStreamClass>>> storage =
1082            new SoftReference<ThreadLocal<WeakHashMap<Class<?>, ObjectStreamClass>>>(null);
1083
1084    private static WeakHashMap<Class<?>, ObjectStreamClass> getCache() {
1085        ThreadLocal<WeakHashMap<Class<?>, ObjectStreamClass>> tls = storage.get();
1086        if (tls == null) {
1087            tls = new ThreadLocal<WeakHashMap<Class<?>, ObjectStreamClass>>() {
1088                public WeakHashMap<Class<?>, ObjectStreamClass> initialValue() {
1089                    return new WeakHashMap<Class<?>, ObjectStreamClass>();
1090                }
1091            };
1092            storage = new SoftReference<ThreadLocal<WeakHashMap<Class<?>, ObjectStreamClass>>>(tls);
1093        }
1094        return tls.get();
1095    }
1096
1097    /**
1098     * Return the java.lang.reflect.Method if class <code>cl</code> implements
1099     * <code>methodName</code> . Return null otherwise.
1100     *
1101     * @param cl
1102     *            a java.lang.Class which to test
1103     * @return <code>java.lang.reflect.Method</code> if the class implements
1104     *         writeReplace <code>null</code> if the class does not implement
1105     *         writeReplace
1106     */
1107    static Method findMethod(Class<?> cl, String methodName) {
1108        Class<?> search = cl;
1109        Method method = null;
1110        while (search != null) {
1111            try {
1112                method = search.getDeclaredMethod(methodName, (Class[]) null);
1113                if (search == cl
1114                        || (method.getModifiers() & Modifier.PRIVATE) == 0) {
1115                    method.setAccessible(true);
1116                    return method;
1117                }
1118            } catch (NoSuchMethodException nsm) {
1119            }
1120            search = search.getSuperclass();
1121        }
1122        return null;
1123    }
1124
1125    /**
1126     * Return the java.lang.reflect.Method if class <code>cl</code> implements
1127     * private <code>methodName</code> . Return null otherwise.
1128     *
1129     * @param cl
1130     *            a java.lang.Class which to test
1131     * @return {@code java.lang.reflect.Method} if the class implements
1132     *         writeReplace {@code null} if the class does not implement
1133     *         writeReplace
1134     */
1135    static Method findPrivateMethod(Class<?> cl, String methodName,
1136            Class<?>[] param) {
1137        try {
1138            Method method = cl.getDeclaredMethod(methodName, param);
1139            if (Modifier.isPrivate(method.getModifiers()) && method.getReturnType() == Void.TYPE) {
1140                method.setAccessible(true);
1141                return method;
1142            }
1143        } catch (NoSuchMethodException nsm) {
1144            // Ignored
1145        }
1146        return null;
1147    }
1148
1149    boolean hasMethodWriteReplace() {
1150        return (methodWriteReplace != null);
1151    }
1152
1153    Method getMethodWriteReplace() {
1154        return methodWriteReplace;
1155    }
1156
1157    boolean hasMethodReadResolve() {
1158        return (methodReadResolve != null);
1159    }
1160
1161    Method getMethodReadResolve() {
1162        return methodReadResolve;
1163    }
1164
1165    boolean hasMethodWriteObject() {
1166        return (methodWriteObject != null);
1167    }
1168
1169    Method getMethodWriteObject() {
1170        return methodWriteObject;
1171    }
1172
1173    boolean hasMethodReadObject() {
1174        return (methodReadObject != null);
1175    }
1176
1177    Method getMethodReadObject() {
1178        return methodReadObject;
1179    }
1180
1181    boolean hasMethodReadObjectNoData() {
1182        return (methodReadObjectNoData != null);
1183    }
1184
1185    Method getMethodReadObjectNoData() {
1186        return methodReadObjectNoData;
1187    }
1188
1189    void initPrivateFields(ObjectStreamClass desc) {
1190        methodWriteReplace = desc.methodWriteReplace;
1191        methodReadResolve = desc.methodReadResolve;
1192        methodWriteObject = desc.methodWriteObject;
1193        methodReadObject = desc.methodReadObject;
1194        methodReadObjectNoData = desc.methodReadObjectNoData;
1195    }
1196
1197    /**
1198     * Set the class (java.lang.Class) that the receiver represents
1199     *
1200     * @param c
1201     *            aClass, the new class that the receiver describes
1202     */
1203    void setClass(Class<?> c) {
1204        resolvedClass = c;
1205    }
1206
1207    /**
1208     * Set the collection of field descriptors for the fields of the
1209     * corresponding class
1210     *
1211     * @param f
1212     *            ObjectStreamField[], the receiver's new collection of declared
1213     *            fields for the class it represents
1214     */
1215    void setFields(ObjectStreamField[] f) {
1216        fields = f;
1217    }
1218
1219    /**
1220     * Set the collection of field descriptors for the input fields of the
1221     * corresponding class
1222     *
1223     * @param f
1224     *            ObjectStreamField[], the receiver's new collection of input
1225     *            fields for the class it represents
1226     */
1227    void setLoadFields(ObjectStreamField[] f) {
1228        loadFields = f;
1229    }
1230
1231    /**
1232     * Set the flags for this descriptor, where possible combined values are
1233     *
1234     * ObjectStreamConstants.SC_WRITE_METHOD
1235     * ObjectStreamConstants.SC_SERIALIZABLE
1236     * ObjectStreamConstants.SC_EXTERNALIZABLE
1237     *
1238     * @param b
1239     *            byte, the receiver's new flags for the class it represents
1240     */
1241    void setFlags(byte b) {
1242        flags = b;
1243    }
1244
1245    /**
1246     * Set the name of the class represented by the receiver
1247     *
1248     * @param newName
1249     *            a String, the new fully qualified name of the class the
1250     *            receiver represents
1251     */
1252    void setName(String newName) {
1253        className = newName;
1254    }
1255
1256    /**
1257     * Set the Serial Version User ID of the class represented by the receiver
1258     *
1259     * @param l
1260     *            a long, the new SUID for the class represented by the receiver
1261     */
1262    void setSerialVersionUID(long l) {
1263        svUID = l;
1264    }
1265
1266    /**
1267     * Set the descriptor for the superclass of the class described by the
1268     * receiver
1269     *
1270     * @param c
1271     *            an ObjectStreamClass, the new ObjectStreamClass for the
1272     *            superclass of the class represented by the receiver
1273     */
1274    void setSuperclass(ObjectStreamClass c) {
1275        superclass = c;
1276    }
1277
1278    private int primitiveSize(Class<?> type) {
1279        if (type == Byte.TYPE || type == Boolean.TYPE) {
1280            return 1;
1281        }
1282        if (type == Short.TYPE || type == Character.TYPE) {
1283            return 2;
1284        }
1285        if (type == Integer.TYPE || type == Float.TYPE) {
1286            return 4;
1287        }
1288        if (type == Long.TYPE || type == Double.TYPE) {
1289            return 8;
1290        }
1291        return 0;
1292    }
1293
1294    /**
1295     * Returns a string containing a concise, human-readable description of this
1296     * descriptor.
1297     *
1298     * @return a printable representation of this descriptor.
1299     */
1300    @Override
1301    public String toString() {
1302        return getName() + ": static final long serialVersionUID =" + getSerialVersionUID() + "L;";
1303    }
1304}
1305