< prev index next >

src/java.base/share/classes/java/io/ObjectStreamClass.java

Print this page

  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.invoke.MethodHandle;
  29 import java.lang.invoke.MethodHandles;
  30 import java.lang.invoke.MethodType;
  31 import java.lang.reflect.Constructor;
  32 import java.lang.reflect.Field;

  33 import java.lang.reflect.InvocationTargetException;
  34 import java.lang.reflect.RecordComponent;
  35 import java.lang.reflect.UndeclaredThrowableException;
  36 import java.lang.reflect.Member;
  37 import java.lang.reflect.Method;
  38 import java.lang.reflect.Modifier;
  39 import java.lang.reflect.Proxy;
  40 import java.security.AccessControlContext;
  41 import java.security.AccessController;
  42 import java.security.MessageDigest;
  43 import java.security.NoSuchAlgorithmException;
  44 import java.security.PermissionCollection;
  45 import java.security.Permissions;
  46 import java.security.PrivilegedAction;
  47 import java.security.PrivilegedActionException;
  48 import java.security.PrivilegedExceptionAction;
  49 import java.security.ProtectionDomain;
  50 import java.util.ArrayList;
  51 import java.util.Arrays;
  52 import java.util.Collections;
  53 import java.util.Comparator;
  54 import java.util.HashSet;
  55 import java.util.Map;
  56 import java.util.Set;
  57 import java.util.concurrent.ConcurrentHashMap;
  58 import jdk.internal.misc.Unsafe;
  59 import jdk.internal.reflect.CallerSensitive;
  60 import jdk.internal.reflect.Reflection;
  61 import jdk.internal.reflect.ReflectionFactory;
  62 import jdk.internal.access.SharedSecrets;
  63 import jdk.internal.access.JavaSecurityAccess;
  64 import jdk.internal.util.ByteArray;

  65 import sun.reflect.misc.ReflectUtil;
  66 
  67 /**
  68  * Serialization's descriptor for classes.  It contains the name and
  69  * serialVersionUID of the class.  The ObjectStreamClass for a specific class
  70  * loaded in this Java VM can be found/created using the lookup method.
  71  *
  72  * <p>The algorithm to compute the SerialVersionUID is described in
  73  * <a href="{@docRoot}/../specs/serialization/class.html#stream-unique-identifiers">
  74  *    <cite>Java Object Serialization Specification,</cite> Section 4.6, "Stream Unique Identifiers"</a>.
  75  *
  76  * @spec serialization/index.html Java Object Serialization Specification
  77  * @author      Mike Warres
  78  * @author      Roger Riggs
  79  * @see ObjectStreamField
  80  * @see <a href="{@docRoot}/../specs/serialization/class.html">
  81  *      <cite>Java Object Serialization Specification,</cite> Section 4, "Class Descriptors"</a>
  82  * @since   1.1
  83  */
  84 public final class ObjectStreamClass implements Serializable {
  85 
  86     /** serialPersistentFields value indicating no serializable fields */
  87     public static final ObjectStreamField[] NO_FIELDS =
  88         new ObjectStreamField[0];
  89 
  90     @java.io.Serial
  91     private static final long serialVersionUID = -6120832682080437368L;
  92     /**
  93      * {@code ObjectStreamClass} has no fields for default serialization.
  94      */

 118                 @Override
 119                 protected Map<FieldReflectorKey, FieldReflector> computeValue(Class<?> type) {
 120                     return new ConcurrentHashMap<>();
 121                 }
 122             };
 123     }
 124 
 125     /** class associated with this descriptor (if any) */
 126     private Class<?> cl;
 127     /** name of class represented by this descriptor */
 128     private String name;
 129     /** serialVersionUID of represented class (null if not computed yet) */
 130     private volatile Long suid;
 131 
 132     /** true if represents dynamic proxy class */
 133     private boolean isProxy;
 134     /** true if represents enum type */
 135     private boolean isEnum;
 136     /** true if represents record type */
 137     private boolean isRecord;


 138     /** true if represented class implements Serializable */
 139     private boolean serializable;
 140     /** true if represented class implements Externalizable */
 141     private boolean externalizable;
 142     /** true if desc has data written by class-defined writeObject method */
 143     private boolean hasWriteObjectData;
 144     /**
 145      * true if desc has externalizable data written in block data format; this
 146      * must be true by default to accommodate ObjectInputStream subclasses which
 147      * override readClassDescriptor() to return class descriptors obtained from
 148      * ObjectStreamClass.lookup() (see 4461737)
 149      */
 150     private boolean hasBlockExternalData = true;
 151 
 152     /**
 153      * Contains information about InvalidClassException instances to be thrown
 154      * when attempting operations on an invalid class. Note that instances of
 155      * this class are immutable and are potentially shared among
 156      * ObjectStreamClass instances.
 157      */

 357      * @param   all if true, return descriptors for all classes; if false, only
 358      *          return descriptors for serializable classes
 359      */
 360     static ObjectStreamClass lookup(Class<?> cl, boolean all) {
 361         if (!(all || Serializable.class.isAssignableFrom(cl))) {
 362             return null;
 363         }
 364         return Caches.localDescs.get(cl);
 365     }
 366 
 367     /**
 368      * Creates local class descriptor representing given class.
 369      */
 370     @SuppressWarnings("removal")
 371     private ObjectStreamClass(final Class<?> cl) {
 372         this.cl = cl;
 373         name = cl.getName();
 374         isProxy = Proxy.isProxyClass(cl);
 375         isEnum = Enum.class.isAssignableFrom(cl);
 376         isRecord = cl.isRecord();

 377         serializable = Serializable.class.isAssignableFrom(cl);
 378         externalizable = Externalizable.class.isAssignableFrom(cl);
 379 
 380         Class<?> superCl = cl.getSuperclass();
 381         superDesc = (superCl != null) ? lookup(superCl, false) : null;
 382         localDesc = this;
 383 
 384         if (serializable) {
 385             AccessController.doPrivileged(new PrivilegedAction<>() {
 386                 public Void run() {
 387                     if (isEnum) {
 388                         suid = 0L;
 389                         fields = NO_FIELDS;
 390                         return null;
 391                     }
 392                     if (cl.isArray()) {
 393                         fields = NO_FIELDS;
 394                         return null;
 395                     }
 396 
 397                     suid = getDeclaredSUID(cl);
 398                     try {
 399                         fields = getSerialFields(cl);
 400                         computeFieldOffsets();
 401                     } catch (InvalidClassException e) {
 402                         serializeEx = deserializeEx =
 403                             new ExceptionInfo(e.classname, e.getMessage());
 404                         fields = NO_FIELDS;
 405                     }
 406 
 407                     if (isRecord) {
 408                         canonicalCtr = canonicalRecordCtr(cl);
 409                         deserializationCtrs = new DeserializationConstructorsCache();



 410                     } else if (externalizable) {
 411                         cons = getExternalizableConstructor(cl);
 412                     } else {
 413                         cons = getSerializableConstructor(cl);
 414                         writeObjectMethod = getPrivateMethod(cl, "writeObject",
 415                             new Class<?>[] { ObjectOutputStream.class },
 416                             Void.TYPE);
 417                         readObjectMethod = getPrivateMethod(cl, "readObject",
 418                             new Class<?>[] { ObjectInputStream.class },
 419                             Void.TYPE);
 420                         readObjectNoDataMethod = getPrivateMethod(
 421                             cl, "readObjectNoData", null, Void.TYPE);
 422                         hasWriteObjectData = (writeObjectMethod != null);
 423                     }
 424                     domains = getProtectionDomains(cons, cl);
 425                     writeReplaceMethod = getInheritableMethod(
 426                         cl, "writeReplace", null, Object.class);
 427                     readResolveMethod = getInheritableMethod(
 428                         cl, "readResolve", null, Object.class);
 429                     return null;
 430                 }
 431             });
 432         } else {
 433             suid = 0L;
 434             fields = NO_FIELDS;
 435         }
 436 
 437         try {
 438             fieldRefl = getReflector(fields, this);
 439         } catch (InvalidClassException ex) {
 440             // field mismatches impossible when matching local fields vs. self
 441             throw new InternalError(ex);
 442         }
 443 
 444         if (deserializeEx == null) {
 445             if (isEnum) {
 446                 deserializeEx = new ExceptionInfo(name, "enum type");
 447             } else if (cons == null && !isRecord) {
 448                 deserializeEx = new ExceptionInfo(name, "no valid constructor");
 449             }
 450         }
 451         if (isRecord && canonicalCtr == null) {
 452             deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
 453         } else {
 454             for (int i = 0; i < fields.length; i++) {
 455                 if (fields[i].getField() == null) {
 456                     defaultSerializeEx = new ExceptionInfo(
 457                         name, "unmatched serializable field(s) declared");
 458                 }
 459             }
 460         }
 461         initialized = true;
 462     }
 463 
 464     /**
 465      * Creates blank class descriptor which should be initialized via a
 466      * subsequent call to initProxy(), initNonProxy() or readNonProxy().
 467      */

 621         }
 622 
 623         this.cl = cl;
 624         this.resolveEx = resolveEx;
 625         this.superDesc = superDesc;
 626         name = model.name;
 627         this.suid = suid;
 628         isProxy = false;
 629         isEnum = model.isEnum;
 630         serializable = model.serializable;
 631         externalizable = model.externalizable;
 632         hasBlockExternalData = model.hasBlockExternalData;
 633         hasWriteObjectData = model.hasWriteObjectData;
 634         fields = model.fields;
 635         primDataSize = model.primDataSize;
 636         numObjFields = model.numObjFields;
 637 
 638         if (osc != null) {
 639             localDesc = osc;
 640             isRecord = localDesc.isRecord;

 641             // canonical record constructor is shared
 642             canonicalCtr = localDesc.canonicalCtr;
 643             // cache of deserialization constructors is shared
 644             deserializationCtrs = localDesc.deserializationCtrs;
 645             writeObjectMethod = localDesc.writeObjectMethod;
 646             readObjectMethod = localDesc.readObjectMethod;
 647             readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
 648             writeReplaceMethod = localDesc.writeReplaceMethod;
 649             readResolveMethod = localDesc.readResolveMethod;
 650             if (deserializeEx == null) {
 651                 deserializeEx = localDesc.deserializeEx;
 652             }
 653             domains = localDesc.domains;
 654             assert cl.isRecord() ? localDesc.cons == null : true;
 655             cons = localDesc.cons;
 656         }
 657 
 658         fieldRefl = getReflector(fields, localDesc);
 659         // reassign to matched fields so as to reflect local unshared settings
 660         fields = fieldRefl.getFields();

 902     }
 903 
 904     /**
 905      * Returns true if represented class implements Externalizable, false
 906      * otherwise.
 907      */
 908     boolean isExternalizable() {
 909         requireInitialized();
 910         return externalizable;
 911     }
 912 
 913     /**
 914      * Returns true if represented class implements Serializable, false
 915      * otherwise.
 916      */
 917     boolean isSerializable() {
 918         requireInitialized();
 919         return serializable;
 920     }
 921 








 922     /**
 923      * Returns true if class descriptor represents externalizable class that
 924      * has written its data in 1.2 (block data) format, false otherwise.
 925      */
 926     boolean hasBlockExternalData() {
 927         requireInitialized();
 928         return hasBlockExternalData;
 929     }
 930 
 931     /**
 932      * Returns true if class descriptor represents serializable (but not
 933      * externalizable) class which has written its data via a custom
 934      * writeObject() method, false otherwise.
 935      */
 936     boolean hasWriteObjectData() {
 937         requireInitialized();
 938         return hasWriteObjectData;
 939     }
 940 
 941     /**
 942      * Returns true if represented class is serializable/externalizable and can
 943      * be instantiated by the serialization runtime--i.e., if it is
 944      * externalizable and defines a public no-arg constructor, or if it is
 945      * non-externalizable and its first non-serializable superclass defines an
 946      * accessible no-arg constructor.  Otherwise, returns false.

 947      */
 948     boolean isInstantiable() {
 949         requireInitialized();
 950         return (cons != null);
 951     }
 952 
 953     /**
 954      * Returns true if represented class is serializable (but not
 955      * externalizable) and defines a conformant writeObject method.  Otherwise,
 956      * returns false.
 957      */
 958     boolean hasWriteObjectMethod() {
 959         requireInitialized();
 960         return (writeObjectMethod != null);
 961     }
 962 
 963     /**
 964      * Returns true if represented class is serializable (but not
 965      * externalizable) and defines a conformant readObject method.  Otherwise,
 966      * returns false.
 967      */
 968     boolean hasReadObjectMethod() {
 969         requireInitialized();
 970         return (readObjectMethod != null);

1041                         if (cause instanceof IllegalAccessException iae)
1042                             throw iae;
1043                         // not supposed to happen
1044                         throw x;
1045                     }
1046                 }
1047             } catch (IllegalAccessException ex) {
1048                 // should not occur, as access checks have been suppressed
1049                 throw new InternalError(ex);
1050             } catch (InvocationTargetException ex) {
1051                 Throwable cause = ex.getCause();
1052                 if (cause instanceof Error err)
1053                     throw err;
1054                 else
1055                     throw ex;
1056             } catch (InstantiationError err) {
1057                 var ex = new InstantiationException();
1058                 ex.initCause(err);
1059                 throw ex;
1060             }
1061         } else {



1062             throw new UnsupportedOperationException();
1063         }
1064     }
1065 









1066     /**
1067      * Invokes the writeObject method of the represented serializable class.
1068      * Throws UnsupportedOperationException if this class descriptor is not
1069      * associated with a class, or if the class is externalizable,
1070      * non-serializable or does not define writeObject.
1071      */
1072     void invokeWriteObject(Object obj, ObjectOutputStream out)
1073         throws IOException, UnsupportedOperationException
1074     {
1075         requireInitialized();
1076         if (writeObjectMethod != null) {
1077             try {
1078                 writeObjectMethod.invoke(obj, new Object[]{ out });
1079             } catch (InvocationTargetException ex) {
1080                 Throwable th = ex.getCause();
1081                 if (th instanceof IOException) {
1082                     throw (IOException) th;
1083                 } else {
1084                     throwMiscException(th);
1085                 }

1420         ObjectStreamClass desc = new ObjectStreamClass();
1421         if (isProxy) {
1422             desc.initProxy(cl, null, superDesc);
1423         } else {
1424             desc.initNonProxy(this, cl, null, superDesc);
1425         }
1426         return desc;
1427     }
1428 
1429     /**
1430      * Returns public no-arg constructor of given class, or null if none found.
1431      * Access checks are disabled on the returned constructor (if any), since
1432      * the defining class may still be non-public.
1433      */
1434     private static Constructor<?> getExternalizableConstructor(Class<?> cl) {
1435         try {
1436             Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
1437             cons.setAccessible(true);
1438             return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
1439                 cons : null;
1440         } catch (NoSuchMethodException ex) {
1441             return null;
1442         }
1443     }
1444 
1445     /**
1446      * Returns subclass-accessible no-arg constructor of first non-serializable
1447      * superclass, or null if none found.  Access checks are disabled on the
1448      * returned constructor (if any).
1449      */
1450     private static Constructor<?> getSerializableConstructor(Class<?> cl) {
1451         return reflFactory.newConstructorForSerialization(cl);
1452     }
1453 
1454     /**
1455      * Returns the canonical constructor for the given record class, or null if
1456      * the not found ( which should never happen for correctly generated record
1457      * classes ).
1458      */
1459     @SuppressWarnings("removal")
1460     private static MethodHandle canonicalRecordCtr(Class<?> cls) {

1913     private static final class FieldReflector {
1914 
1915         /** handle for performing unsafe operations */
1916         private static final Unsafe UNSAFE = Unsafe.getUnsafe();
1917 
1918         /** fields to operate on */
1919         private final ObjectStreamField[] fields;
1920         /** number of primitive fields */
1921         private final int numPrimFields;
1922         /** unsafe field keys for reading fields - may contain dupes */
1923         private final long[] readKeys;
1924         /** unsafe fields keys for writing fields - no dupes */
1925         private final long[] writeKeys;
1926         /** field data offsets */
1927         private final int[] offsets;
1928         /** field type codes */
1929         private final char[] typeCodes;
1930         /** field types */
1931         private final Class<?>[] types;
1932 























1933         /**
1934          * Constructs FieldReflector capable of setting/getting values from the
1935          * subset of fields whose ObjectStreamFields contain non-null
1936          * reflective Field objects.  ObjectStreamFields with null Fields are
1937          * treated as filler, for which get operations return default values
1938          * and set operations discard given values.
1939          */
1940         FieldReflector(ObjectStreamField[] fields) {
1941             this.fields = fields;
1942             int nfields = fields.length;
1943             readKeys = new long[nfields];
1944             writeKeys = new long[nfields];
1945             offsets = new int[nfields];
1946             typeCodes = new char[nfields];
1947             ArrayList<Class<?>> typeList = new ArrayList<>();
1948             Set<Long> usedKeys = new HashSet<>();
1949 
1950 
1951             for (int i = 0; i < nfields; i++) {
1952                 ObjectStreamField f = fields[i];

2033                     case 'D' -> UNSAFE.putDouble(obj, key, ByteArray.getDouble(buf, off));
2034                     default  -> throw new InternalError();
2035                 }
2036             }
2037         }
2038 
2039         /**
2040          * Fetches the serializable object field values of object obj and
2041          * stores them in array vals starting at offset 0.  The caller is
2042          * responsible for ensuring that obj is of the proper type.
2043          */
2044         void getObjFieldValues(Object obj, Object[] vals) {
2045             if (obj == null) {
2046                 throw new NullPointerException();
2047             }
2048             /* assuming checkDefaultSerialize() has been called on the class
2049              * descriptor this FieldReflector was obtained from, no field keys
2050              * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
2051              */
2052             for (int i = numPrimFields; i < fields.length; i++) {

2053                 vals[offsets[i]] = switch (typeCodes[i]) {
2054                     case 'L', '[' -> UNSAFE.getReference(obj, readKeys[i]);



2055                     default       -> throw new InternalError();
2056                 };
2057             }
2058         }
2059 
2060         /**
2061          * Checks that the given values, from array vals starting at offset 0,
2062          * are assignable to the given serializable object fields.
2063          * @throws ClassCastException if any value is not assignable
2064          */
2065         void checkObjectFieldValueTypes(Object obj, Object[] vals) {
2066             setObjFieldValues(obj, vals, true);
2067         }
2068 
2069         /**
2070          * Sets the serializable object fields of object obj using values from
2071          * array vals starting at offset 0.  The caller is responsible for
2072          * ensuring that obj is of the proper type; however, attempts to set a
2073          * field with a value of the wrong type will trigger an appropriate
2074          * ClassCastException.
2075          */
2076         void setObjFieldValues(Object obj, Object[] vals) {
2077             setObjFieldValues(obj, vals, false);
2078         }
2079 
2080         private void setObjFieldValues(Object obj, Object[] vals, boolean dryRun) {
2081             if (obj == null) {
2082                 throw new NullPointerException();
2083             }
2084             for (int i = numPrimFields; i < fields.length; i++) {
2085                 long key = writeKeys[i];
2086                 if (key == Unsafe.INVALID_FIELD_OFFSET) {
2087                     continue;           // discard value
2088                 }
2089                 switch (typeCodes[i]) {
2090                     case 'L', '[' -> {

2091                         Object val = vals[offsets[i]];
2092                         if (val != null &&
2093                             !types[i - numPrimFields].isInstance(val))
2094                         {
2095                             Field f = fields[i].getField();
2096                             throw new ClassCastException(
2097                                 "cannot assign instance of " +
2098                                 val.getClass().getName() + " to field " +
2099                                 f.getDeclaringClass().getName() + "." +
2100                                 f.getName() + " of type " +
2101                                 f.getType().getName() + " in instance of " +
2102                                 obj.getClass().getName());
2103                         }
2104                         if (!dryRun)
2105                             UNSAFE.putReference(obj, key, val);





2106                     }
2107                     default -> throw new InternalError();
2108                 }
2109             }
2110         }
2111     }
2112 
2113     /**
2114      * Matches given set of serializable fields with serializable fields
2115      * described by the given local class descriptor, and returns a
2116      * FieldReflector instance capable of setting/getting values from the
2117      * subset of fields that match (non-matching fields are treated as filler,
2118      * for which get operations return default values and set operations
2119      * discard given values).  Throws InvalidClassException if unresolvable
2120      * type conflicts exist between the two sets of fields.
2121      */
2122     private static FieldReflector getReflector(ObjectStreamField[] fields,
2123                                                ObjectStreamClass localDesc)
2124         throws InvalidClassException
2125     {

  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.invoke.MethodHandle;
  29 import java.lang.invoke.MethodHandles;
  30 import java.lang.invoke.MethodType;
  31 import java.lang.reflect.Constructor;
  32 import java.lang.reflect.Field;
  33 import java.lang.reflect.InaccessibleObjectException;
  34 import java.lang.reflect.InvocationTargetException;
  35 import java.lang.reflect.RecordComponent;
  36 import java.lang.reflect.UndeclaredThrowableException;
  37 import java.lang.reflect.Member;
  38 import java.lang.reflect.Method;
  39 import java.lang.reflect.Modifier;
  40 import java.lang.reflect.Proxy;
  41 import java.security.AccessControlContext;
  42 import java.security.AccessController;
  43 import java.security.MessageDigest;
  44 import java.security.NoSuchAlgorithmException;
  45 import java.security.PermissionCollection;
  46 import java.security.Permissions;
  47 import java.security.PrivilegedAction;
  48 import java.security.PrivilegedActionException;
  49 import java.security.PrivilegedExceptionAction;
  50 import java.security.ProtectionDomain;
  51 import java.util.ArrayList;
  52 import java.util.Arrays;
  53 import java.util.Collections;
  54 import java.util.Comparator;
  55 import java.util.HashSet;
  56 import java.util.Map;
  57 import java.util.Set;
  58 import java.util.concurrent.ConcurrentHashMap;
  59 import jdk.internal.misc.Unsafe;
  60 import jdk.internal.reflect.CallerSensitive;
  61 import jdk.internal.reflect.Reflection;
  62 import jdk.internal.reflect.ReflectionFactory;
  63 import jdk.internal.access.SharedSecrets;
  64 import jdk.internal.access.JavaSecurityAccess;
  65 import jdk.internal.util.ByteArray;
  66 import jdk.internal.value.ValueClass;
  67 import sun.reflect.misc.ReflectUtil;
  68 
  69 /**
  70  * Serialization's descriptor for classes.  It contains the name and
  71  * serialVersionUID of the class.  The ObjectStreamClass for a specific class
  72  * loaded in this Java VM can be found/created using the lookup method.
  73  *
  74  * <p>The algorithm to compute the SerialVersionUID is described in
  75  * <a href="{@docRoot}/../specs/serialization/class.html#stream-unique-identifiers">
  76  *    <cite>Java Object Serialization Specification</cite>, Section 4.6, "Stream Unique Identifiers"</a>.
  77  *
  78  * @spec serialization/index.html Java Object Serialization Specification
  79  * @author      Mike Warres
  80  * @author      Roger Riggs
  81  * @see ObjectStreamField
  82  * @see <a href="{@docRoot}/../specs/serialization/class.html">
  83  *      <cite>Java Object Serialization Specification,</cite> Section 4, "Class Descriptors"</a>
  84  * @since   1.1
  85  */
  86 public final class ObjectStreamClass implements Serializable {
  87 
  88     /** serialPersistentFields value indicating no serializable fields */
  89     public static final ObjectStreamField[] NO_FIELDS =
  90         new ObjectStreamField[0];
  91 
  92     @java.io.Serial
  93     private static final long serialVersionUID = -6120832682080437368L;
  94     /**
  95      * {@code ObjectStreamClass} has no fields for default serialization.
  96      */

 120                 @Override
 121                 protected Map<FieldReflectorKey, FieldReflector> computeValue(Class<?> type) {
 122                     return new ConcurrentHashMap<>();
 123                 }
 124             };
 125     }
 126 
 127     /** class associated with this descriptor (if any) */
 128     private Class<?> cl;
 129     /** name of class represented by this descriptor */
 130     private String name;
 131     /** serialVersionUID of represented class (null if not computed yet) */
 132     private volatile Long suid;
 133 
 134     /** true if represents dynamic proxy class */
 135     private boolean isProxy;
 136     /** true if represents enum type */
 137     private boolean isEnum;
 138     /** true if represents record type */
 139     private boolean isRecord;
 140     /** true if represents a value class */
 141     private boolean isValue;
 142     /** true if represented class implements Serializable */
 143     private boolean serializable;
 144     /** true if represented class implements Externalizable */
 145     private boolean externalizable;
 146     /** true if desc has data written by class-defined writeObject method */
 147     private boolean hasWriteObjectData;
 148     /**
 149      * true if desc has externalizable data written in block data format; this
 150      * must be true by default to accommodate ObjectInputStream subclasses which
 151      * override readClassDescriptor() to return class descriptors obtained from
 152      * ObjectStreamClass.lookup() (see 4461737)
 153      */
 154     private boolean hasBlockExternalData = true;
 155 
 156     /**
 157      * Contains information about InvalidClassException instances to be thrown
 158      * when attempting operations on an invalid class. Note that instances of
 159      * this class are immutable and are potentially shared among
 160      * ObjectStreamClass instances.
 161      */

 361      * @param   all if true, return descriptors for all classes; if false, only
 362      *          return descriptors for serializable classes
 363      */
 364     static ObjectStreamClass lookup(Class<?> cl, boolean all) {
 365         if (!(all || Serializable.class.isAssignableFrom(cl))) {
 366             return null;
 367         }
 368         return Caches.localDescs.get(cl);
 369     }
 370 
 371     /**
 372      * Creates local class descriptor representing given class.
 373      */
 374     @SuppressWarnings("removal")
 375     private ObjectStreamClass(final Class<?> cl) {
 376         this.cl = cl;
 377         name = cl.getName();
 378         isProxy = Proxy.isProxyClass(cl);
 379         isEnum = Enum.class.isAssignableFrom(cl);
 380         isRecord = cl.isRecord();
 381         isValue = cl.isValue();
 382         serializable = Serializable.class.isAssignableFrom(cl);
 383         externalizable = Externalizable.class.isAssignableFrom(cl);
 384 
 385         Class<?> superCl = cl.getSuperclass();
 386         superDesc = (superCl != null) ? lookup(superCl, false) : null;
 387         localDesc = this;
 388 
 389         if (serializable) {
 390             AccessController.doPrivileged(new PrivilegedAction<>() {
 391                 public Void run() {
 392                     if (isEnum) {
 393                         suid = 0L;
 394                         fields = NO_FIELDS;
 395                         return null;
 396                     }
 397                     if (cl.isArray()) {
 398                         fields = NO_FIELDS;
 399                         return null;
 400                     }
 401 
 402                     suid = getDeclaredSUID(cl);
 403                     try {
 404                         fields = getSerialFields(cl);
 405                         computeFieldOffsets();
 406                     } catch (InvalidClassException e) {
 407                         serializeEx = deserializeEx =
 408                             new ExceptionInfo(e.classname, e.getMessage());
 409                         fields = NO_FIELDS;
 410                     }
 411 
 412                     if (isRecord) {
 413                         canonicalCtr = canonicalRecordCtr(cl);
 414                         deserializationCtrs = new DeserializationConstructorsCache();
 415                     } else if (isValue) {
 416                         // Value objects are created using Unsafe.
 417                         cons = null;
 418                     } else if (externalizable) {
 419                         cons = getExternalizableConstructor(cl);
 420                     } else {
 421                         cons = getSerializableConstructor(cl);
 422                         writeObjectMethod = getPrivateMethod(cl, "writeObject",
 423                             new Class<?>[] { ObjectOutputStream.class },
 424                             Void.TYPE);
 425                         readObjectMethod = getPrivateMethod(cl, "readObject",
 426                             new Class<?>[] { ObjectInputStream.class },
 427                             Void.TYPE);
 428                         readObjectNoDataMethod = getPrivateMethod(
 429                             cl, "readObjectNoData", null, Void.TYPE);
 430                         hasWriteObjectData = (writeObjectMethod != null);
 431                     }
 432                     domains = getProtectionDomains(cons, cl);
 433                     writeReplaceMethod = getInheritableMethod(
 434                         cl, "writeReplace", null, Object.class);
 435                     readResolveMethod = getInheritableMethod(
 436                         cl, "readResolve", null, Object.class);
 437                     return null;
 438                 }
 439             });
 440         } else {
 441             suid = 0L;
 442             fields = NO_FIELDS;
 443         }
 444 
 445         try {
 446             fieldRefl = getReflector(fields, this);
 447         } catch (InvalidClassException ex) {
 448             // field mismatches impossible when matching local fields vs. self
 449             throw new InternalError(ex);
 450         }
 451 
 452         if (deserializeEx == null) {
 453             if (isEnum) {
 454                 deserializeEx = new ExceptionInfo(name, "enum type");
 455             } else if (cons == null && !(isRecord | isValue)) {
 456                 deserializeEx = new ExceptionInfo(name, "no valid constructor");
 457             }
 458         }
 459         if (isRecord && canonicalCtr == null) {
 460             deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
 461         } else {
 462             for (int i = 0; i < fields.length; i++) {
 463                 if (fields[i].getField() == null) {
 464                     defaultSerializeEx = new ExceptionInfo(
 465                         name, "unmatched serializable field(s) declared");
 466                 }
 467             }
 468         }
 469         initialized = true;
 470     }
 471 
 472     /**
 473      * Creates blank class descriptor which should be initialized via a
 474      * subsequent call to initProxy(), initNonProxy() or readNonProxy().
 475      */

 629         }
 630 
 631         this.cl = cl;
 632         this.resolveEx = resolveEx;
 633         this.superDesc = superDesc;
 634         name = model.name;
 635         this.suid = suid;
 636         isProxy = false;
 637         isEnum = model.isEnum;
 638         serializable = model.serializable;
 639         externalizable = model.externalizable;
 640         hasBlockExternalData = model.hasBlockExternalData;
 641         hasWriteObjectData = model.hasWriteObjectData;
 642         fields = model.fields;
 643         primDataSize = model.primDataSize;
 644         numObjFields = model.numObjFields;
 645 
 646         if (osc != null) {
 647             localDesc = osc;
 648             isRecord = localDesc.isRecord;
 649             isValue = localDesc.isValue;
 650             // canonical record constructor is shared
 651             canonicalCtr = localDesc.canonicalCtr;
 652             // cache of deserialization constructors is shared
 653             deserializationCtrs = localDesc.deserializationCtrs;
 654             writeObjectMethod = localDesc.writeObjectMethod;
 655             readObjectMethod = localDesc.readObjectMethod;
 656             readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
 657             writeReplaceMethod = localDesc.writeReplaceMethod;
 658             readResolveMethod = localDesc.readResolveMethod;
 659             if (deserializeEx == null) {
 660                 deserializeEx = localDesc.deserializeEx;
 661             }
 662             domains = localDesc.domains;
 663             assert cl.isRecord() ? localDesc.cons == null : true;
 664             cons = localDesc.cons;
 665         }
 666 
 667         fieldRefl = getReflector(fields, localDesc);
 668         // reassign to matched fields so as to reflect local unshared settings
 669         fields = fieldRefl.getFields();

 911     }
 912 
 913     /**
 914      * Returns true if represented class implements Externalizable, false
 915      * otherwise.
 916      */
 917     boolean isExternalizable() {
 918         requireInitialized();
 919         return externalizable;
 920     }
 921 
 922     /**
 923      * Returns true if represented class implements Serializable, false
 924      * otherwise.
 925      */
 926     boolean isSerializable() {
 927         requireInitialized();
 928         return serializable;
 929     }
 930 
 931     /**
 932      * {@return {code true} if the class is a value class, {@code false} otherwise}
 933      */
 934     boolean isValue() {
 935         requireInitialized();
 936         return isValue;
 937     }
 938 
 939     /**
 940      * Returns true if class descriptor represents externalizable class that
 941      * has written its data in 1.2 (block data) format, false otherwise.
 942      */
 943     boolean hasBlockExternalData() {
 944         requireInitialized();
 945         return hasBlockExternalData;
 946     }
 947 
 948     /**
 949      * Returns true if class descriptor represents serializable (but not
 950      * externalizable) class which has written its data via a custom
 951      * writeObject() method, false otherwise.
 952      */
 953     boolean hasWriteObjectData() {
 954         requireInitialized();
 955         return hasWriteObjectData;
 956     }
 957 
 958     /**
 959      * Returns true if represented class is serializable/externalizable and can
 960      * be instantiated by the serialization runtime--i.e., if it is
 961      * externalizable and defines a public no-arg constructor, if it is
 962      * non-externalizable and its first non-serializable superclass defines an
 963      * accessible no-arg constructor, or if the class is a value class.
 964      * Otherwise, returns false.
 965      */
 966     boolean isInstantiable() {
 967         requireInitialized();
 968         return (cons != null | isValue);
 969     }
 970 
 971     /**
 972      * Returns true if represented class is serializable (but not
 973      * externalizable) and defines a conformant writeObject method.  Otherwise,
 974      * returns false.
 975      */
 976     boolean hasWriteObjectMethod() {
 977         requireInitialized();
 978         return (writeObjectMethod != null);
 979     }
 980 
 981     /**
 982      * Returns true if represented class is serializable (but not
 983      * externalizable) and defines a conformant readObject method.  Otherwise,
 984      * returns false.
 985      */
 986     boolean hasReadObjectMethod() {
 987         requireInitialized();
 988         return (readObjectMethod != null);

1059                         if (cause instanceof IllegalAccessException iae)
1060                             throw iae;
1061                         // not supposed to happen
1062                         throw x;
1063                     }
1064                 }
1065             } catch (IllegalAccessException ex) {
1066                 // should not occur, as access checks have been suppressed
1067                 throw new InternalError(ex);
1068             } catch (InvocationTargetException ex) {
1069                 Throwable cause = ex.getCause();
1070                 if (cause instanceof Error err)
1071                     throw err;
1072                 else
1073                     throw ex;
1074             } catch (InstantiationError err) {
1075                 var ex = new InstantiationException();
1076                 ex.initCause(err);
1077                 throw ex;
1078             }
1079         } else if (isValue) {
1080             // Start with a buffered default value.
1081             return FieldReflector.newValueInstance(cl);
1082         }  else {
1083             throw new UnsupportedOperationException();
1084         }
1085     }
1086 
1087     /**
1088      * Finish the initialization of a value object.
1089      * @param obj an object (larval if a value object)
1090      * @return the finished object
1091      */
1092     Object finishValue(Object obj) {
1093         return (isValue) ? FieldReflector.finishValueInstance(obj) : obj;
1094     }
1095 
1096     /**
1097      * Invokes the writeObject method of the represented serializable class.
1098      * Throws UnsupportedOperationException if this class descriptor is not
1099      * associated with a class, or if the class is externalizable,
1100      * non-serializable or does not define writeObject.
1101      */
1102     void invokeWriteObject(Object obj, ObjectOutputStream out)
1103         throws IOException, UnsupportedOperationException
1104     {
1105         requireInitialized();
1106         if (writeObjectMethod != null) {
1107             try {
1108                 writeObjectMethod.invoke(obj, new Object[]{ out });
1109             } catch (InvocationTargetException ex) {
1110                 Throwable th = ex.getCause();
1111                 if (th instanceof IOException) {
1112                     throw (IOException) th;
1113                 } else {
1114                     throwMiscException(th);
1115                 }

1450         ObjectStreamClass desc = new ObjectStreamClass();
1451         if (isProxy) {
1452             desc.initProxy(cl, null, superDesc);
1453         } else {
1454             desc.initNonProxy(this, cl, null, superDesc);
1455         }
1456         return desc;
1457     }
1458 
1459     /**
1460      * Returns public no-arg constructor of given class, or null if none found.
1461      * Access checks are disabled on the returned constructor (if any), since
1462      * the defining class may still be non-public.
1463      */
1464     private static Constructor<?> getExternalizableConstructor(Class<?> cl) {
1465         try {
1466             Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
1467             cons.setAccessible(true);
1468             return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
1469                 cons : null;
1470         } catch (NoSuchMethodException | InaccessibleObjectException ex) {
1471             return null;
1472         }
1473     }
1474 
1475     /**
1476      * Returns subclass-accessible no-arg constructor of first non-serializable
1477      * superclass, or null if none found.  Access checks are disabled on the
1478      * returned constructor (if any).
1479      */
1480     private static Constructor<?> getSerializableConstructor(Class<?> cl) {
1481         return reflFactory.newConstructorForSerialization(cl);
1482     }
1483 
1484     /**
1485      * Returns the canonical constructor for the given record class, or null if
1486      * the not found ( which should never happen for correctly generated record
1487      * classes ).
1488      */
1489     @SuppressWarnings("removal")
1490     private static MethodHandle canonicalRecordCtr(Class<?> cls) {

1943     private static final class FieldReflector {
1944 
1945         /** handle for performing unsafe operations */
1946         private static final Unsafe UNSAFE = Unsafe.getUnsafe();
1947 
1948         /** fields to operate on */
1949         private final ObjectStreamField[] fields;
1950         /** number of primitive fields */
1951         private final int numPrimFields;
1952         /** unsafe field keys for reading fields - may contain dupes */
1953         private final long[] readKeys;
1954         /** unsafe fields keys for writing fields - no dupes */
1955         private final long[] writeKeys;
1956         /** field data offsets */
1957         private final int[] offsets;
1958         /** field type codes */
1959         private final char[] typeCodes;
1960         /** field types */
1961         private final Class<?>[] types;
1962 
1963         /**
1964          * Return a new instance of the class using Unsafe.uninitializedDefaultValue
1965          * and buffer it.
1966          * @param clazz The value class
1967          * @return a buffered default value
1968          */
1969         static Object newValueInstance(Class<?> clazz) throws InstantiationException{
1970             assert clazz.isValue() : "Should be a value class";
1971             // may not be implicitly constructible; so allocate with Unsafe
1972             Object obj = UNSAFE.uninitializedDefaultValue(clazz);
1973             return UNSAFE.makePrivateBuffer(obj);
1974         }
1975 
1976         /**
1977          * Finish a value object, clear the larval state and returning the value object.
1978          * @param obj a buffered value object in a larval state
1979          * @return the finished value object
1980          */
1981         static Object finishValueInstance(Object obj) {
1982             assert (obj.getClass().isValue()) : "Should be a value class";
1983             return UNSAFE.finishPrivateBuffer(obj);
1984         }
1985 
1986         /**
1987          * Constructs FieldReflector capable of setting/getting values from the
1988          * subset of fields whose ObjectStreamFields contain non-null
1989          * reflective Field objects.  ObjectStreamFields with null Fields are
1990          * treated as filler, for which get operations return default values
1991          * and set operations discard given values.
1992          */
1993         FieldReflector(ObjectStreamField[] fields) {
1994             this.fields = fields;
1995             int nfields = fields.length;
1996             readKeys = new long[nfields];
1997             writeKeys = new long[nfields];
1998             offsets = new int[nfields];
1999             typeCodes = new char[nfields];
2000             ArrayList<Class<?>> typeList = new ArrayList<>();
2001             Set<Long> usedKeys = new HashSet<>();
2002 
2003 
2004             for (int i = 0; i < nfields; i++) {
2005                 ObjectStreamField f = fields[i];

2086                     case 'D' -> UNSAFE.putDouble(obj, key, ByteArray.getDouble(buf, off));
2087                     default  -> throw new InternalError();
2088                 }
2089             }
2090         }
2091 
2092         /**
2093          * Fetches the serializable object field values of object obj and
2094          * stores them in array vals starting at offset 0.  The caller is
2095          * responsible for ensuring that obj is of the proper type.
2096          */
2097         void getObjFieldValues(Object obj, Object[] vals) {
2098             if (obj == null) {
2099                 throw new NullPointerException();
2100             }
2101             /* assuming checkDefaultSerialize() has been called on the class
2102              * descriptor this FieldReflector was obtained from, no field keys
2103              * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
2104              */
2105             for (int i = numPrimFields; i < fields.length; i++) {
2106                 Field f = fields[i].getField();
2107                 vals[offsets[i]] = switch (typeCodes[i]) {
2108                     case 'L', '[' ->
2109                             UNSAFE.isFlattened(f)
2110                                     ? UNSAFE.getValue(obj, readKeys[i], f.getType())
2111                                     : UNSAFE.getReference(obj, readKeys[i]);
2112                     default       -> throw new InternalError();
2113                 };
2114             }
2115         }
2116 
2117         /**
2118          * Checks that the given values, from array vals starting at offset 0,
2119          * are assignable to the given serializable object fields.
2120          * @throws ClassCastException if any value is not assignable
2121          */
2122         void checkObjectFieldValueTypes(Object obj, Object[] vals) {
2123             setObjFieldValues(obj, vals, true);
2124         }
2125 
2126         /**
2127          * Sets the serializable object fields of object obj using values from
2128          * array vals starting at offset 0.  The caller is responsible for
2129          * ensuring that obj is of the proper type; however, attempts to set a
2130          * field with a value of the wrong type will trigger an appropriate
2131          * ClassCastException.
2132          */
2133         void setObjFieldValues(Object obj, Object[] vals) {
2134             setObjFieldValues(obj, vals, false);
2135         }
2136 
2137         private void setObjFieldValues(Object obj, Object[] vals, boolean dryRun) {
2138             if (obj == null) {
2139                 throw new NullPointerException();
2140             }
2141             for (int i = numPrimFields; i < fields.length; i++) {
2142                 long key = writeKeys[i];
2143                 if (key == Unsafe.INVALID_FIELD_OFFSET) {
2144                     continue;           // discard value
2145                 }
2146                 switch (typeCodes[i]) {
2147                     case 'L', '[' -> {
2148                         Field f = fields[i].getField();
2149                         Object val = vals[offsets[i]];
2150                         if (val != null &&
2151                             !types[i - numPrimFields].isInstance(val))
2152                         {

2153                             throw new ClassCastException(
2154                                 "cannot assign instance of " +
2155                                 val.getClass().getName() + " to field " +
2156                                 f.getDeclaringClass().getName() + "." +
2157                                 f.getName() + " of type " +
2158                                 f.getType().getName() + " in instance of " +
2159                                 obj.getClass().getName());
2160                         }
2161                         if (!dryRun) {
2162                             if (UNSAFE.isFlattened(f)) {
2163                                 UNSAFE.putValue(obj, key, f.getType(), val);
2164                             } else {
2165                                 UNSAFE.putReference(obj, key, val);
2166                             }
2167                         }
2168                     }
2169                     default -> throw new InternalError();
2170                 }
2171             }
2172         }
2173     }
2174 
2175     /**
2176      * Matches given set of serializable fields with serializable fields
2177      * described by the given local class descriptor, and returns a
2178      * FieldReflector instance capable of setting/getting values from the
2179      * subset of fields that match (non-matching fields are treated as filler,
2180      * for which get operations return default values and set operations
2181      * discard given values).  Throws InvalidClassException if unresolvable
2182      * type conflicts exist between the two sets of fields.
2183      */
2184     private static FieldReflector getReflector(ObjectStreamField[] fields,
2185                                                ObjectStreamClass localDesc)
2186         throws InvalidClassException
2187     {
< prev index next >