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