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