< 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;

  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);

1035                     } catch (UndeclaredThrowableException x) {
1036                         Throwable cause = x.getCause();
1037                         if (cause instanceof InstantiationException)
1038                             throw (InstantiationException) cause;
1039                         if (cause instanceof InvocationTargetException)
1040                             throw (InvocationTargetException) cause;
1041                         if (cause instanceof IllegalAccessException)
1042                             throw (IllegalAccessException) cause;
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 (InstantiationError err) {
1051                 var ex = new InstantiationException();
1052                 ex.initCause(err);
1053                 throw ex;
1054             }
1055         } else {



1056             throw new UnsupportedOperationException();
1057         }
1058     }
1059 









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

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

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






















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

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

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



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

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





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

  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;

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

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

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

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

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

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

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

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

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

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