1 /*
2  * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package java.io;
27 
28 import java.lang.reflect.Field;
29 import java.util.Objects;
30 
31 import jdk.internal.reflect.CallerSensitive;
32 import jdk.internal.reflect.Reflection;
33 import sun.reflect.misc.ReflectUtil;
34 
35 /**
36  * A description of a Serializable field from a Serializable class.  An array
37  * of ObjectStreamFields is used to declare the Serializable fields of a class.
38  *
39  * @author      Mike Warres
40  * @author      Roger Riggs
41  * @see ObjectStreamClass
42  * @since 1.2
43  */
44 public class ObjectStreamField
45     implements Comparable<Object>
46 {
47 
48     /** field name */
49     private final String name;
50     /** canonical JVM signature of field type, if given */
51     private final String signature;
52     /** field type (Object.class if unknown non-primitive type) */
53     private final Class<?> type;
54     /** lazily constructed signature for the type, if no explicit signature */
55     private String typeSignature;
56     /** whether or not to (de)serialize field values as unshared */
57     private final boolean unshared;
58     /** corresponding reflective field object, if any */
59     private final Field field;
60     /** offset of field value in enclosing field group */
61     private int offset;
62 
63     /**
64      * Create a Serializable field with the specified type.  This field should
65      * be documented with a {@code serialField} tag.
66      *
67      * @param   name the name of the serializable field
68      * @param   type the {@code Class} object of the serializable field
69      */
ObjectStreamField(String name, Class<?> type)70     public ObjectStreamField(String name, Class<?> type) {
71         this(name, type, false);
72     }
73 
74     /**
75      * Creates an ObjectStreamField representing a serializable field with the
76      * given name and type.  If unshared is false, values of the represented
77      * field are serialized and deserialized in the default manner--if the
78      * field is non-primitive, object values are serialized and deserialized as
79      * if they had been written and read by calls to writeObject and
80      * readObject.  If unshared is true, values of the represented field are
81      * serialized and deserialized as if they had been written and read by
82      * calls to writeUnshared and readUnshared.
83      *
84      * @param   name field name
85      * @param   type field type
86      * @param   unshared if false, write/read field values in the same manner
87      *          as writeObject/readObject; if true, write/read in the same
88      *          manner as writeUnshared/readUnshared
89      * @since   1.4
90      */
ObjectStreamField(String name, Class<?> type, boolean unshared)91     public ObjectStreamField(String name, Class<?> type, boolean unshared) {
92         if (name == null) {
93             throw new NullPointerException();
94         }
95         this.name = name;
96         // Android-changed: added null check. Javadoc tells nothing about it, but
97         // subsequent calls to methods like getSignature() will throw NPE. Prior to
98         // OpenJDK 17 NPE was thrown during signature field initialization, which
99         // is lazy now.
100         // this.type = type;
101         this.type = Objects.requireNonNull(type);
102         this.unshared = unshared;
103         this.field = null;
104         this.signature = null;
105     }
106 
107     /**
108      * Creates an ObjectStreamField representing a field with the given name,
109      * signature and unshared setting.
110      */
ObjectStreamField(String name, String signature, boolean unshared)111     ObjectStreamField(String name, String signature, boolean unshared) {
112         if (name == null) {
113             throw new NullPointerException();
114         }
115         this.name = name;
116         this.signature = signature.intern();
117         this.unshared = unshared;
118         this.field = null;
119 
120         type = switch (signature.charAt(0)) {
121             case 'Z'      -> Boolean.TYPE;
122             case 'B'      -> Byte.TYPE;
123             case 'C'      -> Character.TYPE;
124             case 'S'      -> Short.TYPE;
125             case 'I'      -> Integer.TYPE;
126             case 'J'      -> Long.TYPE;
127             case 'F'      -> Float.TYPE;
128             case 'D'      -> Double.TYPE;
129             case 'L', '[' -> Object.class;
130             default       -> throw new IllegalArgumentException("illegal signature");
131         };
132     }
133 
134     /**
135      * Returns JVM type signature for given primitive.
136      */
getPrimitiveSignature(Class<?> cl)137     private static String getPrimitiveSignature(Class<?> cl) {
138         if (cl == Integer.TYPE)
139             return "I";
140         else if (cl == Byte.TYPE)
141             return "B";
142         else if (cl == Long.TYPE)
143             return "J";
144         else if (cl == Float.TYPE)
145             return "F";
146         else if (cl == Double.TYPE)
147             return "D";
148         else if (cl == Short.TYPE)
149             return "S";
150         else if (cl == Character.TYPE)
151             return "C";
152         else if (cl == Boolean.TYPE)
153             return "Z";
154         else if (cl == Void.TYPE)
155             return "V";
156         else
157             throw new InternalError();
158     }
159 
160     /**
161      * Returns JVM type signature for given class.
162      */
getClassSignature(Class<?> cl)163     static String getClassSignature(Class<?> cl) {
164         if (cl.isPrimitive()) {
165             return getPrimitiveSignature(cl);
166         } else {
167             return appendClassSignature(new StringBuilder(), cl).toString();
168         }
169     }
170 
appendClassSignature(StringBuilder sbuf, Class<?> cl)171     static StringBuilder appendClassSignature(StringBuilder sbuf, Class<?> cl) {
172         while (cl.isArray()) {
173             sbuf.append('[');
174             cl = cl.getComponentType();
175         }
176 
177         if (cl.isPrimitive()) {
178             sbuf.append(getPrimitiveSignature(cl));
179         } else {
180             sbuf.append('L').append(cl.getName().replace('.', '/')).append(';');
181         }
182 
183         return sbuf;
184     }
185 
186     /**
187      * Creates an ObjectStreamField representing the given field with the
188      * specified unshared setting.  For compatibility with the behavior of
189      * earlier serialization implementations, a "showType" parameter is
190      * necessary to govern whether or not a getType() call on this
191      * ObjectStreamField (if non-primitive) will return Object.class (as
192      * opposed to a more specific reference type).
193      */
ObjectStreamField(Field field, boolean unshared, boolean showType)194     ObjectStreamField(Field field, boolean unshared, boolean showType) {
195         this.field = field;
196         this.unshared = unshared;
197         name = field.getName();
198         Class<?> ftype = field.getType();
199         type = (showType || ftype.isPrimitive()) ? ftype : Object.class;
200         signature = getClassSignature(ftype).intern();
201     }
202 
203     /**
204      * Get the name of this field.
205      *
206      * @return  a {@code String} representing the name of the serializable
207      *          field
208      */
getName()209     public String getName() {
210         return name;
211     }
212 
213     /**
214      * Get the type of the field.  If the type is non-primitive and this
215      * {@code ObjectStreamField} was obtained from a deserialized {@link
216      * ObjectStreamClass} instance, then {@code Object.class} is returned.
217      * Otherwise, the {@code Class} object for the type of the field is
218      * returned.
219      *
220      * @return  a {@code Class} object representing the type of the
221      *          serializable field
222      */
223     @SuppressWarnings("removal")
224     @CallerSensitive
getType()225     public Class<?> getType() {
226         // BEGIN Android-removed: Security manager is always null on Android.
227         /*
228         if (System.getSecurityManager() != null) {
229             Class<?> caller = Reflection.getCallerClass();
230             if (ReflectUtil.needsPackageAccessCheck(caller.getClassLoader(), type.getClassLoader())) {
231                 ReflectUtil.checkPackageAccess(type);
232             }
233         }
234         */
235         // END Android-removed: Security manager is always null on Android.
236         return type;
237     }
238 
239     /**
240      * Returns character encoding of field type.  The encoding is as follows:
241      * <blockquote><pre>
242      * B            byte
243      * C            char
244      * D            double
245      * F            float
246      * I            int
247      * J            long
248      * L            class or interface
249      * S            short
250      * Z            boolean
251      * [            array
252      * </pre></blockquote>
253      *
254      * @return  the typecode of the serializable field
255      */
256     // REMIND: deprecate?
getTypeCode()257     public char getTypeCode() {
258         return getSignature().charAt(0);
259     }
260 
261     /**
262      * Return the JVM type signature.
263      *
264      * @return  null if this field has a primitive type.
265      */
266     // REMIND: deprecate?
getTypeString()267     public String getTypeString() {
268         return isPrimitive() ? null : getSignature();
269     }
270 
271     /**
272      * Offset of field within instance data.
273      *
274      * @return  the offset of this field
275      * @see #setOffset
276      */
277     // REMIND: deprecate?
getOffset()278     public int getOffset() {
279         return offset;
280     }
281 
282     /**
283      * Offset within instance data.
284      *
285      * @param   offset the offset of the field
286      * @see #getOffset
287      */
288     // REMIND: deprecate?
setOffset(int offset)289     protected void setOffset(int offset) {
290         this.offset = offset;
291     }
292 
293     /**
294      * Return true if this field has a primitive type.
295      *
296      * @return  true if and only if this field corresponds to a primitive type
297      */
298     // REMIND: deprecate?
isPrimitive()299     public boolean isPrimitive() {
300         char tcode = getTypeCode();
301         return ((tcode != 'L') && (tcode != '['));
302     }
303 
304     /**
305      * Returns boolean value indicating whether or not the serializable field
306      * represented by this ObjectStreamField instance is unshared.
307      *
308      * @return {@code true} if this field is unshared
309      *
310      * @since 1.4
311      */
isUnshared()312     public boolean isUnshared() {
313         return unshared;
314     }
315 
316     /**
317      * Compare this field with another {@code ObjectStreamField}.  Return
318      * -1 if this is smaller, 0 if equal, 1 if greater.  Types that are
319      * primitives are "smaller" than object types.  If equal, the field names
320      * are compared.
321      */
322     // REMIND: deprecate?
compareTo(Object obj)323     public int compareTo(Object obj) {
324         ObjectStreamField other = (ObjectStreamField) obj;
325         boolean isPrim = isPrimitive();
326         if (isPrim != other.isPrimitive()) {
327             return isPrim ? -1 : 1;
328         }
329         return name.compareTo(other.name);
330     }
331 
332     /**
333      * Return a string that describes this field.
334      */
toString()335     public String toString() {
336         return getSignature() + ' ' + name;
337     }
338 
339     /**
340      * Returns field represented by this ObjectStreamField, or null if
341      * ObjectStreamField is not associated with an actual field.
342      */
getField()343     Field getField() {
344         return field;
345     }
346 
347     /**
348      * Returns JVM type signature of field (similar to getTypeString, except
349      * that signature strings are returned for primitive fields as well).
350      */
getSignature()351     String getSignature() {
352         if (signature != null) {
353             return signature;
354         }
355 
356         String sig = typeSignature;
357         // This lazy calculation is safe since signature can be null iff one
358         // of the public constructors are used, in which case type is always
359         // initialized to the exact type we want the signature to represent.
360         if (sig == null) {
361             typeSignature = sig = getClassSignature(type).intern();
362         }
363         return sig;
364     }
365 }
366