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