< 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 

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

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


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

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

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

 627         }
 628 
 629         this.cl = cl;
 630         this.resolveEx = resolveEx;
 631         this.superDesc = superDesc;
 632         name = model.name;
 633         this.suid = suid;
 634         isProxy = false;
 635         isEnum = model.isEnum;
 636         serializable = model.serializable;
 637         externalizable = model.externalizable;
 638         hasBlockExternalData = model.hasBlockExternalData;
 639         hasWriteObjectData = model.hasWriteObjectData;
 640         fields = model.fields;
 641         primDataSize = model.primDataSize;
 642         numObjFields = model.numObjFields;
 643 
 644         if (osc != null) {
 645             localDesc = osc;
 646             isRecord = localDesc.isRecord;

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

 953      */
 954     boolean isInstantiable() {
 955         requireInitialized();
 956         return (cons != null);

 957     }
 958 
 959     /**
 960      * Returns true if represented class is serializable (but not
 961      * externalizable) and defines a conformant writeObject method.  Otherwise,
 962      * returns false.
 963      */
 964     boolean hasWriteObjectMethod() {
 965         requireInitialized();
 966         return (writeObjectMethod != null);
 967     }
 968 
 969     /**
 970      * Returns true if represented class is serializable (but not
 971      * externalizable) and defines a conformant readObject method.  Otherwise,
 972      * returns false.
 973      */
 974     boolean hasReadObjectMethod() {
 975         requireInitialized();
 976         return (readObjectMethod != null);

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



1068             throw new UnsupportedOperationException();
1069         }
1070     }
1071 









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

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

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























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

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

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



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

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





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

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

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

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

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

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

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

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

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

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

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