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