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

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


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

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

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



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

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

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

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








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

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

1033                     } catch (UndeclaredThrowableException x) {
1034                         Throwable cause = x.getCause();
1035                         if (cause instanceof InstantiationException)
1036                             throw (InstantiationException) cause;
1037                         if (cause instanceof InvocationTargetException)
1038                             throw (InvocationTargetException) cause;
1039                         if (cause instanceof IllegalAccessException)
1040                             throw (IllegalAccessException) cause;
1041                         // not supposed to happen
1042                         throw x;
1043                     }
1044                 }
1045             } catch (IllegalAccessException ex) {
1046                 // should not occur, as access checks have been suppressed
1047                 throw new InternalError(ex);
1048             } catch (InstantiationError err) {
1049                 var ex = new InstantiationException();
1050                 ex.initCause(err);
1051                 throw ex;
1052             }
1053         } else {



1054             throw new UnsupportedOperationException();
1055         }
1056     }
1057 









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

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

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






















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

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

2045                 vals[offsets[i]] = switch (typeCodes[i]) {
2046                     case 'L', '[' -> unsafe.getReference(obj, readKeys[i]);



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

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





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

  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 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  * @author      Mike Warres
  77  * @author      Roger Riggs
  78  * @see ObjectStreamField
  79  * @see <a href="{@docRoot}/../specs/serialization/class.html">
  80  *      <cite>Java Object Serialization Specification,</cite> Section 4, "Class Descriptors"</a>
  81  * @since   1.1
  82  */
  83 public final class ObjectStreamClass implements Serializable {
  84 
  85     /** serialPersistentFields value indicating no serializable fields */
  86     public static final ObjectStreamField[] NO_FIELDS =
  87         new ObjectStreamField[0];
  88 
  89     @java.io.Serial
  90     private static final long serialVersionUID = -6120832682080437368L;
  91     /**
  92      * {@code ObjectStreamClass} has no fields for default serialization.
  93      */
  94     @java.io.Serial

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

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

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

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

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

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

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

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

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