< prev index next >

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

Print this page

   1 /*
   2  * Copyright (c) 1996, 2023, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.io;
  27 
  28 import java.lang.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      */
  97     @java.io.Serial
  98     private static final ObjectStreamField[] serialPersistentFields =
  99         NO_FIELDS;
 100 
 101     /** reflection factory for obtaining serialization constructors */
 102     @SuppressWarnings("removal")
 103     private static final ReflectionFactory reflFactory =
 104         AccessController.doPrivileged(
 105             new ReflectionFactory.GetReflectionFactoryAction());
 106 

































































 107     private static class Caches {
 108         /** cache mapping local classes -> descriptors */
 109         static final ClassCache<ObjectStreamClass> localDescs =
 110             new ClassCache<>() {
 111                 @Override
 112                 protected ObjectStreamClass computeValue(Class<?> type) {
 113                     return new ObjectStreamClass(type);
 114                 }
 115             };
 116 
 117         /** cache mapping field group/local desc pairs -> field reflectors */
 118         static final ClassCache<Map<FieldReflectorKey, FieldReflector>> reflectors =
 119             new ClassCache<>() {
 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      */

 177     }
 178 
 179     /** exception (if any) thrown while attempting to resolve class */
 180     private ClassNotFoundException resolveEx;
 181     /** exception (if any) to throw if non-enum deserialization attempted */
 182     private ExceptionInfo deserializeEx;
 183     /** exception (if any) to throw if non-enum serialization attempted */
 184     private ExceptionInfo serializeEx;
 185     /** exception (if any) to throw if default serialization attempted */
 186     private ExceptionInfo defaultSerializeEx;
 187 
 188     /** serializable fields */
 189     private ObjectStreamField[] fields;
 190     /** aggregate marshalled size of primitive fields */
 191     private int primDataSize;
 192     /** number of non-primitive fields */
 193     private int numObjFields;
 194     /** reflector for setting/getting serializable field values */
 195     private FieldReflector fieldRefl;
 196     /** data layout of serialized objects described by this class desc */
 197     private volatile ClassDataSlot[] dataLayout;
 198 
 199     /** serialization-appropriate constructor, or null if none */
 200     private Constructor<?> cons;
 201     /** record canonical constructor (shared among OSCs for same class), or null */
 202     private MethodHandle canonicalCtr;
 203     /** cache of record deserialization constructors per unique set of stream fields
 204      * (shared among OSCs for same class), or null */
 205     private DeserializationConstructorsCache deserializationCtrs;
 206     /** session-cache of record deserialization constructor
 207      * (in de-serialized OSC only), or null */
 208     private MethodHandle deserializationCtr;
 209     /** protection domains that need to be checked when calling the constructor */
 210     private ProtectionDomain[] domains;
 211 
 212     /** class-defined writeObject method, or null if none */
 213     private Method writeObjectMethod;
 214     /** class-defined readObject method, or null if none */
 215     private Method readObjectMethod;
 216     /** class-defined readObjectNoData method, or null if none */
 217     private Method readObjectNoDataMethod;

 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 

 549                 throw new InvalidClassException(
 550                     "cannot bind proxy descriptor to a non-proxy class");
 551             }
 552         }
 553         this.cl = cl;
 554         this.resolveEx = resolveEx;
 555         this.superDesc = superDesc;
 556         isProxy = true;
 557         serializable = true;
 558         suid = 0L;
 559         fields = NO_FIELDS;
 560         if (osc != null) {
 561             localDesc = osc;
 562             name = localDesc.name;
 563             externalizable = localDesc.externalizable;
 564             writeReplaceMethod = localDesc.writeReplaceMethod;
 565             readResolveMethod = localDesc.readResolveMethod;
 566             deserializeEx = localDesc.deserializeEx;
 567             domains = localDesc.domains;
 568             cons = localDesc.cons;



 569         }
 570         fieldRefl = getReflector(fields, localDesc);
 571         initialized = true;
 572     }
 573 
 574     /**
 575      * Initializes class descriptor representing a non-proxy class.
 576      */
 577     void initNonProxy(ObjectStreamClass model,
 578                       Class<?> cl,
 579                       ClassNotFoundException resolveEx,
 580                       ObjectStreamClass superDesc)
 581         throws InvalidClassException
 582     {
 583         long suid = model.getSerialVersionUID();
 584         ObjectStreamClass osc = null;
 585         if (cl != null) {
 586             osc = lookup(cl, true);
 587             if (osc.isProxy) {
 588                 throw new InvalidClassException(

 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();
 667 
 668         initialized = true;
 669     }
 670 
 671     /**
 672      * Reads non-proxy class descriptor information from given input stream.
 673      * The resulting class descriptor is not fully functional; it can only be
 674      * used as input to the ObjectInputStream.resolveClass() and
 675      * ObjectStreamClass.initNonProxy() methods.
 676      */
 677     void readNonProxy(ObjectInputStream in)
 678         throws IOException, ClassNotFoundException
 679     {
 680         name = in.readUTF();
 681         suid = in.readLong();

 697         serializable = externalizable || sflag;
 698         isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
 699         if (isEnum && suid.longValue() != 0L) {
 700             throw new InvalidClassException(name,
 701                 "enum descriptor has non-zero serialVersionUID: " + suid);
 702         }
 703 
 704         int numFields = in.readShort();
 705         if (isEnum && numFields != 0) {
 706             throw new InvalidClassException(name,
 707                 "enum descriptor has non-zero field count: " + numFields);
 708         }
 709         fields = (numFields > 0) ?
 710             new ObjectStreamField[numFields] : NO_FIELDS;
 711         for (int i = 0; i < numFields; i++) {
 712             char tcode = (char) in.readByte();
 713             String fname = in.readUTF();
 714             String signature = ((tcode == 'L') || (tcode == '[')) ?
 715                 in.readTypeString() : String.valueOf(tcode);
 716             try {
 717                 fields[i] = new ObjectStreamField(fname, signature, false);
 718             } catch (RuntimeException e) {
 719                 throw new InvalidClassException(name,
 720                                                 "invalid descriptor for field " +
 721                                                 fname, e);
 722             }
 723         }
 724         computeFieldOffsets();
 725     }
 726 
 727     /**
 728      * Writes non-proxy class descriptor information to given output stream.
 729      */
 730     void writeNonProxy(ObjectOutputStream out) throws IOException {
 731         out.writeUTF(name);
 732         out.writeLong(getSerialVersionUID());
 733 
 734         byte flags = 0;
 735         if (externalizable) {
 736             flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
 737             int protocol = out.getProtocolVersion();

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

1222     /**
1223      * Class representing the portion of an object's serialized form allotted
1224      * to data described by a given class descriptor.  If "hasData" is false,
1225      * the object's serialized form does not contain data associated with the
1226      * class descriptor.
1227      */
1228     static class ClassDataSlot {
1229 
1230         /** class descriptor "occupying" this slot */
1231         final ObjectStreamClass desc;
1232         /** true if serialized form includes data for this slot's descriptor */
1233         final boolean hasData;
1234 
1235         ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
1236             this.desc = desc;
1237             this.hasData = hasData;
1238         }
1239     }
1240 
1241     /**
1242      * Returns array of ClassDataSlot instances representing the data layout
1243      * (including superclass data) for serialized objects described by this
1244      * class descriptor.  ClassDataSlots are ordered by inheritance with those
1245      * containing "higher" superclasses appearing first.  The final
1246      * ClassDataSlot contains a reference to this descriptor.
1247      */
1248     ClassDataSlot[] getClassDataLayout() throws InvalidClassException {
1249         // REMIND: synchronize instead of relying on volatile?
1250         if (dataLayout == null) {
1251             dataLayout = getClassDataLayout0();
1252         }
1253         return dataLayout;
1254     }
1255 
1256     private ClassDataSlot[] getClassDataLayout0()
1257         throws InvalidClassException
1258     {
1259         ArrayList<ClassDataSlot> slots = new ArrayList<>();
1260         Class<?> start = cl, end = cl;
1261 
1262         // locate closest non-serializable superclass
1263         while (end != null && Serializable.class.isAssignableFrom(end)) {
1264             end = end.getSuperclass();
1265         }
1266 
1267         HashSet<String> oscNames = new HashSet<>(3);
1268 
1269         for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
1270             if (oscNames.contains(d.name)) {
1271                 throw new InvalidClassException("Circular reference.");
1272             } else {
1273                 oscNames.add(d.name);
1274             }
1275 
1276             // search up inheritance hierarchy for class with matching name
1277             String searchName = (d.cl != null) ? d.cl.getName() : d.name;
1278             Class<?> match = null;

1287             if (match != null) {
1288                 for (Class<?> c = start; c != match; c = c.getSuperclass()) {
1289                     slots.add(new ClassDataSlot(
1290                         ObjectStreamClass.lookup(c, true), false));
1291                 }
1292                 start = match.getSuperclass();
1293             }
1294 
1295             // record descriptor/class pairing
1296             slots.add(new ClassDataSlot(d.getVariantFor(match), true));
1297         }
1298 
1299         // add "no data" slot for any leftover unmatched classes
1300         for (Class<?> c = start; c != end; c = c.getSuperclass()) {
1301             slots.add(new ClassDataSlot(
1302                 ObjectStreamClass.lookup(c, true), false));
1303         }
1304 
1305         // order slots from superclass -> subclass
1306         Collections.reverse(slots);
1307         return slots.toArray(new ClassDataSlot[slots.size()]);

1308     }
1309 
1310     /**
1311      * Returns aggregate size (in bytes) of marshalled primitive field values
1312      * for represented class.
1313      */
1314     int getPrimDataSize() {
1315         return primDataSize;
1316     }
1317 
1318     /**
1319      * Returns number of non-primitive serializable fields of represented
1320      * class.
1321      */
1322     int getNumObjFields() {
1323         return numObjFields;
1324     }
1325 
1326     /**
1327      * Fetches the serializable primitive field values of object obj and

1415     /**
1416      * If given class is the same as the class associated with this class
1417      * descriptor, returns reference to this class descriptor.  Otherwise,
1418      * returns variant of this class descriptor bound to given class.
1419      */
1420     private ObjectStreamClass getVariantFor(Class<?> cl)
1421         throws InvalidClassException
1422     {
1423         if (this.cl == cl) {
1424             return this;
1425         }
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) {

1661 
1662         ObjectStreamField[] boundFields =
1663             new ObjectStreamField[serialPersistentFields.length];
1664         Set<String> fieldNames = HashSet.newHashSet(serialPersistentFields.length);
1665 
1666         for (int i = 0; i < serialPersistentFields.length; i++) {
1667             ObjectStreamField spf = serialPersistentFields[i];
1668 
1669             String fname = spf.getName();
1670             if (fieldNames.contains(fname)) {
1671                 throw new InvalidClassException(
1672                     "multiple serializable fields named " + fname);
1673             }
1674             fieldNames.add(fname);
1675 
1676             try {
1677                 Field f = cl.getDeclaredField(fname);
1678                 if ((f.getType() == spf.getType()) &&
1679                     ((f.getModifiers() & Modifier.STATIC) == 0))
1680                 {
1681                     boundFields[i] =
1682                         new ObjectStreamField(f, spf.isUnshared(), true);
1683                 }
1684             } catch (NoSuchFieldException ex) {
1685             }
1686             if (boundFields[i] == null) {
1687                 boundFields[i] = new ObjectStreamField(
1688                     fname, spf.getType(), spf.isUnshared());
1689             }
1690         }
1691         return boundFields;
1692     }
1693 
1694     /**
1695      * Returns array of ObjectStreamFields corresponding to all non-static
1696      * non-transient fields declared by given class.  Each ObjectStreamField
1697      * contains a Field object for the field it represents.  If no default
1698      * serializable fields exist, NO_FIELDS is returned.
1699      */
1700     private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
1701         Field[] clFields = cl.getDeclaredFields();
1702         ArrayList<ObjectStreamField> list = new ArrayList<>();
1703         int mask = Modifier.STATIC | Modifier.TRANSIENT;
1704 
1705         for (int i = 0; i < clFields.length; i++) {
1706             if ((clFields[i].getModifiers() & mask) == 0) {
1707                 list.add(new ObjectStreamField(clFields[i], false, true));
1708             }
1709         }
1710         int size = list.size();
1711         return (size == 0) ? NO_FIELDS :
1712             list.toArray(new ObjectStreamField[size]);
1713     }
1714 
1715     /**
1716      * Returns explicit serial version UID value declared by given class, or
1717      * null if none.
1718      */
1719     private static Long getDeclaredSUID(Class<?> cl) {
1720         try {
1721             Field f = cl.getDeclaredField("serialVersionUID");
1722             int mask = Modifier.STATIC | Modifier.FINAL;
1723             if ((f.getModifiers() & mask) == mask) {
1724                 f.setAccessible(true);
1725                 return f.getLong(null);
1726             }
1727         } catch (Exception ex) {

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     {

2203          * a non-local class descriptor.  To preserve this (questionable)
2204          * behavior, the ObjectStreamField instances returned by matchFields
2205          * cannot report non-primitive types other than Object.class; hence
2206          * localFields cannot be returned directly.
2207          */
2208 
2209         ObjectStreamField[] matches = new ObjectStreamField[fields.length];
2210         for (int i = 0; i < fields.length; i++) {
2211             ObjectStreamField f = fields[i], m = null;
2212             for (int j = 0; j < localFields.length; j++) {
2213                 ObjectStreamField lf = localFields[j];
2214                 if (f.getName().equals(lf.getName())) {
2215                     if ((f.isPrimitive() || lf.isPrimitive()) &&
2216                         f.getTypeCode() != lf.getTypeCode())
2217                     {
2218                         throw new InvalidClassException(localDesc.name,
2219                             "incompatible types for field " + f.getName());
2220                     }
2221                     if (lf.getField() != null) {
2222                         m = new ObjectStreamField(
2223                             lf.getField(), lf.isUnshared(), false);
2224                     } else {
2225                         m = new ObjectStreamField(
2226                             lf.getName(), lf.getSignature(), lf.isUnshared());
2227                     }
2228                 }
2229             }
2230             if (m == null) {
2231                 m = new ObjectStreamField(
2232                     f.getName(), f.getSignature(), false);
2233             }
2234             m.setOffset(f.getOffset());
2235             matches[i] = m;
2236         }
2237         return matches;
2238     }
2239 
2240     /**
2241      * A LRA cache of record deserialization constructors.
2242      */
2243     @SuppressWarnings("serial")
2244     private static final class DeserializationConstructorsCache
2245         extends ConcurrentHashMap<DeserializationConstructorsCache.Key, MethodHandle>  {
2246 
2247         // keep max. 10 cached entries - when the 11th element is inserted the oldest
2248         // is removed and 10 remains - 11 is the biggest map size where internal
2249         // table of 16 elements is sufficient (inserting 12th element would resize it to 32)
2250         private static final int MAX_SIZE = 10;
2251         private Key.Impl first, last; // first and last in FIFO queue
2252 

2334                     this.fieldTypes = new Class<?>[fields.length];
2335                     for (int i = 0; i < fields.length; i++) {
2336                         fieldNames[i] = fields[i].getName();
2337                         fieldTypes[i] = fields[i].getType();
2338                     }
2339                 }
2340 
2341                 @Override
2342                 int length() { return fieldNames.length; }
2343 
2344                 @Override
2345                 String fieldName(int i) { return fieldNames[i]; }
2346 
2347                 @Override
2348                 Class<?> fieldType(int i) { return fieldTypes[i]; }
2349             }
2350         }
2351     }
2352 
2353     /** Record specific support for retrieving and binding stream field values. */
2354     static final class RecordSupport {
2355         /**
2356          * Returns canonical record constructor adapted to take two arguments:
2357          * {@code (byte[] primValues, Object[] objValues)}
2358          * and return
2359          * {@code Object}
2360          */
2361         @SuppressWarnings("removal")
2362         static MethodHandle deserializationCtr(ObjectStreamClass desc) {
2363             // check the cached value 1st
2364             MethodHandle mh = desc.deserializationCtr;
2365             if (mh != null) return mh;
2366             mh = desc.deserializationCtrs.get(desc.getFields(false));
2367             if (mh != null) return desc.deserializationCtr = mh;
2368 
2369             // retrieve record components
2370             RecordComponent[] recordComponents;
2371             try {
2372                 Class<?> cls = desc.forClass();
2373                 PrivilegedExceptionAction<RecordComponent[]> pa = cls::getRecordComponents;
2374                 recordComponents = AccessController.doPrivileged(pa);

2391             for (int i = recordComponents.length-1; i >= 0; i--) {
2392                 String name = recordComponents[i].getName();
2393                 Class<?> type = recordComponents[i].getType();
2394                 // obtain stream field extractor that extracts argument at
2395                 // position i (Ti+1) from primValues and objValues arrays
2396                 // (byte[], Object[]):Ti+1
2397                 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2398                 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2399                 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2400                 mh = MethodHandles.foldArguments(mh, i, combiner);
2401             }
2402             // what we are left with is a MethodHandle taking just the primValues
2403             // and objValues arrays and returning the constructed record instance
2404             // (byte[], Object[]):Object
2405 
2406             // store it into cache and return the 1st value stored
2407             return desc.deserializationCtr =
2408                 desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
2409         }
2410 



























































2411         /** Returns the number of primitive fields for the given descriptor. */
2412         private static int numberPrimValues(ObjectStreamClass desc) {
2413             ObjectStreamField[] fields = desc.getFields();
2414             int primValueCount = 0;
2415             for (int i = 0; i < fields.length; i++) {
2416                 if (fields[i].isPrimitive())
2417                     primValueCount++;
2418                 else
2419                     break;  // can be no more
2420             }
2421             return primValueCount;
2422         }
2423 
2424         /**
2425          * Returns extractor MethodHandle taking the primValues and objValues arrays
2426          * and extracting the argument of canonical constructor with given name and type
2427          * or producing  default value for the given type if the field is absent.
2428          */
2429         private static MethodHandle streamFieldExtractor(String pName,
2430                                                          Class<?> pType,

   1 /*
   2  * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.io;
  27 
  28 import java.lang.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.List;
  57 import java.util.Map;
  58 import java.util.Set;
  59 import java.util.concurrent.ConcurrentHashMap;
  60 import java.util.stream.Stream;
  61 
  62 import jdk.internal.MigratedValueClass;
  63 import jdk.internal.event.SerializationMisdeclarationEvent;
  64 import jdk.internal.misc.Unsafe;
  65 import jdk.internal.reflect.CallerSensitive;
  66 import jdk.internal.reflect.Reflection;
  67 import jdk.internal.reflect.ReflectionFactory;
  68 import jdk.internal.access.SharedSecrets;
  69 import jdk.internal.access.JavaSecurityAccess;
  70 import jdk.internal.util.ByteArray;
  71 import jdk.internal.value.DeserializeConstructor;
  72 import sun.reflect.misc.ReflectUtil;
  73 
  74 import static java.io.ObjectInputStream.TRACE;
  75 
  76 /**
  77  * Serialization's descriptor for classes.  It contains the name and
  78  * serialVersionUID of the class.  The ObjectStreamClass for a specific class
  79  * loaded in this Java VM can be found/created using the lookup method.
  80  *
  81  * <p>The algorithm to compute the SerialVersionUID is described in
  82  * <a href="{@docRoot}/../specs/serialization/class.html#stream-unique-identifiers">
  83  *    <cite>Java Object Serialization Specification</cite>, Section 4.6, "Stream Unique Identifiers"</a>.
  84  *
  85  * @spec serialization/index.html Java Object Serialization Specification
  86  * @author      Mike Warres
  87  * @author      Roger Riggs
  88  * @see ObjectStreamField
  89  * @see <a href="{@docRoot}/../specs/serialization/class.html">
  90  *      <cite>Java Object Serialization Specification,</cite> Section 4, "Class Descriptors"</a>
  91  * @since   1.1
  92  */
  93 public final class ObjectStreamClass implements Serializable {
  94 
  95     /** serialPersistentFields value indicating no serializable fields */
  96     public static final ObjectStreamField[] NO_FIELDS =
  97         new ObjectStreamField[0];
  98 
  99     @java.io.Serial
 100     private static final long serialVersionUID = -6120832682080437368L;
 101     /**
 102      * {@code ObjectStreamClass} has no fields for default serialization.
 103      */
 104     @java.io.Serial
 105     private static final ObjectStreamField[] serialPersistentFields =
 106         NO_FIELDS;
 107 
 108     /** reflection factory for obtaining serialization constructors */
 109     @SuppressWarnings("removal")
 110     private static final ReflectionFactory reflFactory =
 111         AccessController.doPrivileged(
 112             new ReflectionFactory.GetReflectionFactoryAction());
 113 
 114     /**
 115      * The mode of deserialization for a class depending on its type and interfaces.
 116      * The markers used are {@linkplain java.io.Serializable}, {@linkplain java.io.Externalizable},
 117      * Class.isRecord(), Class.isValue(), constructors, and
 118      * the presence of methods `readObject`, `writeObject`, `readObjectNoData`, `writeObject`.
 119      * ObjectInputStream dispatches on the mode to construct objects from the stream.
 120      */
 121     enum DeserializationMode {
 122         /**
 123          * Construct an object from the stream for a class that has only default read object behaviors.
 124          * All classes and superclasses use defaultReadObject; no custom readObject or readObjectNoData.
 125          * The new instance is entered in the handle table if it is unshared,
 126          * allowing it to escape before it is initialized.
 127          * For each object, all the fields are read before any are assigned.
 128          * The `readObject` and `readObjectNoData` methods are not present and are not called.
 129          */
 130         READ_OBJECT_DEFAULT,
 131         /**
 132          * Creates a new object and invokes its readExternal method to read its contents.
 133          * If the class is instantiable, read externalizable data by invoking readExternal()
 134          * method of obj; otherwise, attempts to skip over externalizable data.
 135          * Expects that passHandle is set to obj's handle before this method is
 136          * called.  The new object is entered in the handle table immediately,
 137          * allowing it to leak before it is completely read.
 138          */
 139         READ_EXTERNALIZABLE,
 140         /**
 141          * Read all the record fields and invoke its canonical constructor.
 142          * Construct the record using its canonical constructor.
 143          * The new record is entered in the handle table only after the constructor returns.
 144          */
 145         READ_RECORD,
 146         /**
 147          * Fully custom read from the stream to create an instance.
 148          * If the class is not instantiatable or is tagged with ClassNotFoundException
 149          * the data in the stream for the class is read and discarded. {@link #READ_NO_LOCAL_CLASS}
 150          * The instance is created and set in the handle table, allowing it to leak before it is initialized.
 151          * For each serializable class in the stream, from superclass to subclass the
 152          * stream values are read by the `readObject` method, if present, or defaultReadObject.
 153          * Custom inline data is discarded if not consumed by the class `readObject` method.
 154          */
 155         READ_OBJECT_CUSTOM,
 156         /**
 157          * Construct an object by reading the values of all fields and
 158          * invoking a constructor or static factory method.
 159          * The constructor or static factory method is selected by matching its parameters with the
 160          * sequence of field types of the serializable fields of the local class and superclasses.
 161          * Invoke the constructor with all the values from the stream, inserting
 162          * defaults and dropping extra values as necessary.
 163          * This is very similar to the reading of records, except for the identification of
 164          * the constructor or static factory.
 165          */
 166         READ_OBJECT_VALUE,
 167         /**
 168          * Read and discard an entire object, leaving a null reference in the HandleTable.
 169          * The descriptor of the class in the stream is used to read the fields from the stream.
 170          * There is no instance in which to store the field values.
 171          * Custom data following the fields of any slot is read and discarded.
 172          * References to nested objects are read and retained in the
 173          * handle table using the regular mechanism.
 174          * Handles later in the stream may refer to the nested objects.
 175          */
 176         READ_NO_LOCAL_CLASS,
 177     }
 178 
 179     private static class Caches {
 180         /** cache mapping local classes -> descriptors */
 181         static final ClassCache<ObjectStreamClass> localDescs =
 182             new ClassCache<>() {
 183                 @Override
 184                 protected ObjectStreamClass computeValue(Class<?> type) {
 185                     return new ObjectStreamClass(type);
 186                 }
 187             };
 188 
 189         /** cache mapping field group/local desc pairs -> field reflectors */
 190         static final ClassCache<Map<FieldReflectorKey, FieldReflector>> reflectors =
 191             new ClassCache<>() {
 192                 @Override
 193                 protected Map<FieldReflectorKey, FieldReflector> computeValue(Class<?> type) {
 194                     return new ConcurrentHashMap<>();
 195                 }
 196             };
 197     }
 198 
 199     /** class associated with this descriptor (if any) */
 200     private Class<?> cl;
 201     /** name of class represented by this descriptor */
 202     private String name;
 203     /** serialVersionUID of represented class (null if not computed yet) */
 204     private volatile Long suid;
 205 
 206     /** true if represents dynamic proxy class */
 207     private boolean isProxy;
 208     /** true if represents enum type */
 209     private boolean isEnum;
 210     /** true if represents record type */
 211     private boolean isRecord;
 212     /** true if represents a value class */
 213     private boolean isValue;
 214     /** The DeserializationMode for this class. */
 215     private DeserializationMode factoryMode;
 216     /** true if represented class implements Serializable */
 217     private boolean serializable;
 218     /** true if represented class implements Externalizable */
 219     private boolean externalizable;
 220     /** true if desc has data written by class-defined writeObject method */
 221     private boolean hasWriteObjectData;
 222     /**
 223      * true if desc has externalizable data written in block data format; this
 224      * must be true by default to accommodate ObjectInputStream subclasses which
 225      * override readClassDescriptor() to return class descriptors obtained from
 226      * ObjectStreamClass.lookup() (see 4461737)
 227      */
 228     private boolean hasBlockExternalData = true;
 229 
 230     /**
 231      * Contains information about InvalidClassException instances to be thrown
 232      * when attempting operations on an invalid class. Note that instances of
 233      * this class are immutable and are potentially shared among
 234      * ObjectStreamClass instances.
 235      */

 253     }
 254 
 255     /** exception (if any) thrown while attempting to resolve class */
 256     private ClassNotFoundException resolveEx;
 257     /** exception (if any) to throw if non-enum deserialization attempted */
 258     private ExceptionInfo deserializeEx;
 259     /** exception (if any) to throw if non-enum serialization attempted */
 260     private ExceptionInfo serializeEx;
 261     /** exception (if any) to throw if default serialization attempted */
 262     private ExceptionInfo defaultSerializeEx;
 263 
 264     /** serializable fields */
 265     private ObjectStreamField[] fields;
 266     /** aggregate marshalled size of primitive fields */
 267     private int primDataSize;
 268     /** number of non-primitive fields */
 269     private int numObjFields;
 270     /** reflector for setting/getting serializable field values */
 271     private FieldReflector fieldRefl;
 272     /** data layout of serialized objects described by this class desc */
 273     private volatile List<ClassDataSlot> dataLayout;
 274 
 275     /** serialization-appropriate constructor, or null if none */
 276     private Constructor<?> cons;
 277     /** record canonical constructor (shared among OSCs for same class), or null */
 278     private MethodHandle canonicalCtr;
 279     /** cache of record deserialization constructors per unique set of stream fields
 280      * (shared among OSCs for same class), or null */
 281     private DeserializationConstructorsCache deserializationCtrs;
 282     /** session-cache of record deserialization constructor
 283      * (in de-serialized OSC only), or null */
 284     private MethodHandle deserializationCtr;
 285     /** protection domains that need to be checked when calling the constructor */
 286     private ProtectionDomain[] domains;
 287 
 288     /** class-defined writeObject method, or null if none */
 289     private Method writeObjectMethod;
 290     /** class-defined readObject method, or null if none */
 291     private Method readObjectMethod;
 292     /** class-defined readObjectNoData method, or null if none */
 293     private Method readObjectNoDataMethod;

 435      * @param   all if true, return descriptors for all classes; if false, only
 436      *          return descriptors for serializable classes
 437      */
 438     static ObjectStreamClass lookup(Class<?> cl, boolean all) {
 439         if (!(all || Serializable.class.isAssignableFrom(cl))) {
 440             return null;
 441         }
 442         return Caches.localDescs.get(cl);
 443     }
 444 
 445     /**
 446      * Creates local class descriptor representing given class.
 447      */
 448     @SuppressWarnings("removal")
 449     private ObjectStreamClass(final Class<?> cl) {
 450         this.cl = cl;
 451         name = cl.getName();
 452         isProxy = Proxy.isProxyClass(cl);
 453         isEnum = Enum.class.isAssignableFrom(cl);
 454         isRecord = cl.isRecord();
 455         isValue = cl.isValue();
 456         serializable = Serializable.class.isAssignableFrom(cl);
 457         externalizable = Externalizable.class.isAssignableFrom(cl);
 458 
 459         Class<?> superCl = cl.getSuperclass();
 460         superDesc = (superCl != null) ? lookup(superCl, false) : null;
 461         localDesc = this;
 462 
 463         if (serializable) {
 464             AccessController.doPrivileged(new PrivilegedAction<>() {
 465                 public Void run() {
 466                     if (isEnum) {
 467                         suid = 0L;
 468                         fields = NO_FIELDS;
 469                         return null;
 470                     }
 471                     if (cl.isArray()) {
 472                         fields = NO_FIELDS;
 473                         return null;
 474                     }
 475 
 476                     suid = getDeclaredSUID(cl);
 477                     try {
 478                         fields = getSerialFields(cl);
 479                         computeFieldOffsets();
 480                     } catch (InvalidClassException e) {
 481                         serializeEx = deserializeEx =
 482                             new ExceptionInfo(e.classname, e.getMessage());
 483                         fields = NO_FIELDS;
 484                     }
 485 
 486                     if (isRecord) {
 487                         factoryMode = DeserializationMode.READ_RECORD;
 488                         canonicalCtr = canonicalRecordCtr(cl);
 489                         deserializationCtrs = new DeserializationConstructorsCache();
 490                     } else if (externalizable) {
 491                         factoryMode = DeserializationMode.READ_EXTERNALIZABLE;
 492                         if (cl.isIdentity()) {
 493                             cons = getExternalizableConstructor(cl);
 494                         } else {
 495                             serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
 496                                     "Externalizable not valid for value class");
 497                         }
 498                     } else if (cl.isValue()) {
 499                         factoryMode = DeserializationMode.READ_OBJECT_VALUE;
 500                         if (!cl.isAnnotationPresent(MigratedValueClass.class)) {
 501                             serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
 502                                     "Value class serialization is only supported with `writeReplace`");
 503                         } else if (Modifier.isAbstract(cl.getModifiers())) {
 504                             serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
 505                                     "value class is abstract");
 506                         } else {
 507                             // Value classes should have constructor(s) annotated with {@link DeserializeConstructor}
 508                             canonicalCtr = getDeserializingValueCons(cl, fields);
 509                             deserializationCtrs = new DeserializationConstructorsCache();                            factoryMode = DeserializationMode.READ_OBJECT_VALUE;
 510                             if (canonicalCtr == null) {
 511                                 serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
 512                                         "no constructor or factory found for migrated value class");
 513                             }
 514                         }
 515                     } else {
 516                         cons = getSerializableConstructor(cl);
 517                         writeObjectMethod = getPrivateMethod(cl, "writeObject",
 518                             new Class<?>[] { ObjectOutputStream.class },
 519                             Void.TYPE);
 520                         readObjectMethod = getPrivateMethod(cl, "readObject",
 521                             new Class<?>[] { ObjectInputStream.class },
 522                             Void.TYPE);
 523                         readObjectNoDataMethod = getPrivateMethod(
 524                             cl, "readObjectNoData", null, Void.TYPE);
 525                         hasWriteObjectData = (writeObjectMethod != null);
 526                         factoryMode = ((superDesc == null || superDesc.factoryMode() == DeserializationMode.READ_OBJECT_DEFAULT)
 527                                 && readObjectMethod == null && readObjectNoDataMethod == null)
 528                                 ? DeserializationMode.READ_OBJECT_DEFAULT
 529                                 : DeserializationMode.READ_OBJECT_CUSTOM;
 530                     }
 531                     domains = getProtectionDomains(cons, cl);
 532                     writeReplaceMethod = getInheritableMethod(
 533                         cl, "writeReplace", null, Object.class);
 534                     readResolveMethod = getInheritableMethod(
 535                         cl, "readResolve", null, Object.class);
 536                     return null;
 537                 }
 538             });
 539         } else {
 540             suid = 0L;
 541             fields = NO_FIELDS;
 542         }
 543 
 544         try {
 545             fieldRefl = getReflector(fields, this);
 546         } catch (InvalidClassException ex) {
 547             // field mismatches impossible when matching local fields vs. self
 548             throw new InternalError(ex);
 549         }
 550 
 551         if (deserializeEx == null) {
 552             if (isEnum) {
 553                 deserializeEx = new ExceptionInfo(name, "enum type");
 554             } else if (cons == null && !(isRecord | isValue)) {
 555                 deserializeEx = new ExceptionInfo(name, "no valid constructor");
 556             }
 557         }
 558         if (isRecord && canonicalCtr == null) {
 559             deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
 560         } else {
 561             for (int i = 0; i < fields.length; i++) {
 562                 if (fields[i].getField() == null) {
 563                     defaultSerializeEx = new ExceptionInfo(
 564                         name, "unmatched serializable field(s) declared");
 565                 }
 566             }
 567         }
 568         initialized = true;
 569 
 570         if (SerializationMisdeclarationEvent.enabled() && serializable) {
 571             SerializationMisdeclarationChecker.checkMisdeclarations(cl);
 572         }
 573     }
 574 

 654                 throw new InvalidClassException(
 655                     "cannot bind proxy descriptor to a non-proxy class");
 656             }
 657         }
 658         this.cl = cl;
 659         this.resolveEx = resolveEx;
 660         this.superDesc = superDesc;
 661         isProxy = true;
 662         serializable = true;
 663         suid = 0L;
 664         fields = NO_FIELDS;
 665         if (osc != null) {
 666             localDesc = osc;
 667             name = localDesc.name;
 668             externalizable = localDesc.externalizable;
 669             writeReplaceMethod = localDesc.writeReplaceMethod;
 670             readResolveMethod = localDesc.readResolveMethod;
 671             deserializeEx = localDesc.deserializeEx;
 672             domains = localDesc.domains;
 673             cons = localDesc.cons;
 674             factoryMode = localDesc.factoryMode;
 675         } else {
 676             factoryMode = DeserializationMode.READ_OBJECT_DEFAULT;
 677         }
 678         fieldRefl = getReflector(fields, localDesc);
 679         initialized = true;
 680     }
 681 
 682     /**
 683      * Initializes class descriptor representing a non-proxy class.
 684      */
 685     void initNonProxy(ObjectStreamClass model,
 686                       Class<?> cl,
 687                       ClassNotFoundException resolveEx,
 688                       ObjectStreamClass superDesc)
 689         throws InvalidClassException
 690     {
 691         long suid = model.getSerialVersionUID();
 692         ObjectStreamClass osc = null;
 693         if (cl != null) {
 694             osc = lookup(cl, true);
 695             if (osc.isProxy) {
 696                 throw new InvalidClassException(

 735         }
 736 
 737         this.cl = cl;
 738         this.resolveEx = resolveEx;
 739         this.superDesc = superDesc;
 740         name = model.name;
 741         this.suid = suid;
 742         isProxy = false;
 743         isEnum = model.isEnum;
 744         serializable = model.serializable;
 745         externalizable = model.externalizable;
 746         hasBlockExternalData = model.hasBlockExternalData;
 747         hasWriteObjectData = model.hasWriteObjectData;
 748         fields = model.fields;
 749         primDataSize = model.primDataSize;
 750         numObjFields = model.numObjFields;
 751 
 752         if (osc != null) {
 753             localDesc = osc;
 754             isRecord = localDesc.isRecord;
 755             isValue = localDesc.isValue;
 756             // canonical record constructor is shared
 757             canonicalCtr = localDesc.canonicalCtr;
 758             // cache of deserialization constructors is shared
 759             deserializationCtrs = localDesc.deserializationCtrs;
 760             writeObjectMethod = localDesc.writeObjectMethod;
 761             readObjectMethod = localDesc.readObjectMethod;
 762             readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
 763             writeReplaceMethod = localDesc.writeReplaceMethod;
 764             readResolveMethod = localDesc.readResolveMethod;
 765             if (deserializeEx == null) {
 766                 deserializeEx = localDesc.deserializeEx;
 767             }
 768             domains = localDesc.domains;
 769             assert cl.isRecord() ? localDesc.cons == null : true;
 770             cons = localDesc.cons;
 771             factoryMode = localDesc.factoryMode;
 772         } else {
 773             // No local class, read data using only the schema from the stream
 774             factoryMode = (externalizable)
 775                     ? DeserializationMode.READ_EXTERNALIZABLE
 776                     : DeserializationMode.READ_NO_LOCAL_CLASS;
 777         }
 778 
 779         fieldRefl = getReflector(fields, localDesc);
 780         // reassign to matched fields so as to reflect local unshared settings
 781         fields = fieldRefl.getFields();
 782 
 783         initialized = true;
 784     }
 785 
 786     /**
 787      * Reads non-proxy class descriptor information from given input stream.
 788      * The resulting class descriptor is not fully functional; it can only be
 789      * used as input to the ObjectInputStream.resolveClass() and
 790      * ObjectStreamClass.initNonProxy() methods.
 791      */
 792     void readNonProxy(ObjectInputStream in)
 793         throws IOException, ClassNotFoundException
 794     {
 795         name = in.readUTF();
 796         suid = in.readLong();

 812         serializable = externalizable || sflag;
 813         isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
 814         if (isEnum && suid.longValue() != 0L) {
 815             throw new InvalidClassException(name,
 816                 "enum descriptor has non-zero serialVersionUID: " + suid);
 817         }
 818 
 819         int numFields = in.readShort();
 820         if (isEnum && numFields != 0) {
 821             throw new InvalidClassException(name,
 822                 "enum descriptor has non-zero field count: " + numFields);
 823         }
 824         fields = (numFields > 0) ?
 825             new ObjectStreamField[numFields] : NO_FIELDS;
 826         for (int i = 0; i < numFields; i++) {
 827             char tcode = (char) in.readByte();
 828             String fname = in.readUTF();
 829             String signature = ((tcode == 'L') || (tcode == '[')) ?
 830                 in.readTypeString() : String.valueOf(tcode);
 831             try {
 832                 fields[i] = new ObjectStreamField(fname, signature, false, -1);
 833             } catch (RuntimeException e) {
 834                 throw new InvalidClassException(name,
 835                                                 "invalid descriptor for field " +
 836                                                 fname, e);
 837             }
 838         }
 839         computeFieldOffsets();
 840     }
 841 
 842     /**
 843      * Writes non-proxy class descriptor information to given output stream.
 844      */
 845     void writeNonProxy(ObjectOutputStream out) throws IOException {
 846         out.writeUTF(name);
 847         out.writeLong(getSerialVersionUID());
 848 
 849         byte flags = 0;
 850         if (externalizable) {
 851             flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
 852             int protocol = out.getProtocolVersion();

1023     }
1024 
1025     /**
1026      * Returns true if represented class implements Externalizable, false
1027      * otherwise.
1028      */
1029     boolean isExternalizable() {
1030         requireInitialized();
1031         return externalizable;
1032     }
1033 
1034     /**
1035      * Returns true if represented class implements Serializable, false
1036      * otherwise.
1037      */
1038     boolean isSerializable() {
1039         requireInitialized();
1040         return serializable;
1041     }
1042 
1043     /**
1044      * {@return {code true} if the class is a value class, {@code false} otherwise}
1045      */
1046     boolean isValue() {
1047         requireInitialized();
1048         return isValue;
1049     }
1050 
1051     /**
1052      * {@return the factory mode for deserialization}
1053      */
1054     DeserializationMode factoryMode() {
1055         requireInitialized();
1056         return factoryMode;
1057     }
1058 
1059     /**
1060      * Returns true if class descriptor represents externalizable class that
1061      * has written its data in 1.2 (block data) format, false otherwise.
1062      */
1063     boolean hasBlockExternalData() {
1064         requireInitialized();
1065         return hasBlockExternalData;
1066     }
1067 
1068     /**
1069      * Returns true if class descriptor represents serializable (but not
1070      * externalizable) class which has written its data via a custom
1071      * writeObject() method, false otherwise.
1072      */
1073     boolean hasWriteObjectData() {
1074         requireInitialized();
1075         return hasWriteObjectData;
1076     }
1077 
1078     /**
1079      * Returns true if represented class is serializable/externalizable and can
1080      * be instantiated by the serialization runtime--i.e., if it is
1081      * externalizable and defines a public no-arg constructor, if it is
1082      * non-externalizable and its first non-serializable superclass defines an
1083      * accessible no-arg constructor, or if the class is a value class with a @DeserializeConstructor
1084      * constructor or static factory.
1085      * Otherwise, returns false.
1086      */
1087     boolean isInstantiable() {
1088         requireInitialized();
1089         return (cons != null || (isValue() && canonicalCtr != null));
1090     }
1091 
1092     /**
1093      * Returns true if represented class is serializable (but not
1094      * externalizable) and defines a conformant writeObject method.  Otherwise,
1095      * returns false.
1096      */
1097     boolean hasWriteObjectMethod() {
1098         requireInitialized();
1099         return (writeObjectMethod != null);
1100     }
1101 
1102     /**
1103      * Returns true if represented class is serializable (but not
1104      * externalizable) and defines a conformant readObject method.  Otherwise,
1105      * returns false.
1106      */
1107     boolean hasReadObjectMethod() {
1108         requireInitialized();
1109         return (readObjectMethod != null);

1355     /**
1356      * Class representing the portion of an object's serialized form allotted
1357      * to data described by a given class descriptor.  If "hasData" is false,
1358      * the object's serialized form does not contain data associated with the
1359      * class descriptor.
1360      */
1361     static class ClassDataSlot {
1362 
1363         /** class descriptor "occupying" this slot */
1364         final ObjectStreamClass desc;
1365         /** true if serialized form includes data for this slot's descriptor */
1366         final boolean hasData;
1367 
1368         ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
1369             this.desc = desc;
1370             this.hasData = hasData;
1371         }
1372     }
1373 
1374     /**
1375      * Returns a List of ClassDataSlot instances representing the data layout
1376      * (including superclass data) for serialized objects described by this
1377      * class descriptor.  ClassDataSlots are ordered by inheritance with those
1378      * containing "higher" superclasses appearing first.  The final
1379      * ClassDataSlot contains a reference to this descriptor.
1380      */
1381     List<ClassDataSlot> getClassDataLayout() throws InvalidClassException {
1382         // REMIND: synchronize instead of relying on volatile?
1383         List<ClassDataSlot> layout = dataLayout;
1384         if (layout != null)
1385             return layout;


1386 



1387         ArrayList<ClassDataSlot> slots = new ArrayList<>();
1388         Class<?> start = cl, end = cl;
1389 
1390         // locate closest non-serializable superclass
1391         while (end != null && Serializable.class.isAssignableFrom(end)) {
1392             end = end.getSuperclass();
1393         }
1394 
1395         HashSet<String> oscNames = new HashSet<>(3);
1396 
1397         for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
1398             if (oscNames.contains(d.name)) {
1399                 throw new InvalidClassException("Circular reference.");
1400             } else {
1401                 oscNames.add(d.name);
1402             }
1403 
1404             // search up inheritance hierarchy for class with matching name
1405             String searchName = (d.cl != null) ? d.cl.getName() : d.name;
1406             Class<?> match = null;

1415             if (match != null) {
1416                 for (Class<?> c = start; c != match; c = c.getSuperclass()) {
1417                     slots.add(new ClassDataSlot(
1418                         ObjectStreamClass.lookup(c, true), false));
1419                 }
1420                 start = match.getSuperclass();
1421             }
1422 
1423             // record descriptor/class pairing
1424             slots.add(new ClassDataSlot(d.getVariantFor(match), true));
1425         }
1426 
1427         // add "no data" slot for any leftover unmatched classes
1428         for (Class<?> c = start; c != end; c = c.getSuperclass()) {
1429             slots.add(new ClassDataSlot(
1430                 ObjectStreamClass.lookup(c, true), false));
1431         }
1432 
1433         // order slots from superclass -> subclass
1434         Collections.reverse(slots);
1435         dataLayout = slots;
1436         return slots;
1437     }
1438 
1439     /**
1440      * Returns aggregate size (in bytes) of marshalled primitive field values
1441      * for represented class.
1442      */
1443     int getPrimDataSize() {
1444         return primDataSize;
1445     }
1446 
1447     /**
1448      * Returns number of non-primitive serializable fields of represented
1449      * class.
1450      */
1451     int getNumObjFields() {
1452         return numObjFields;
1453     }
1454 
1455     /**
1456      * Fetches the serializable primitive field values of object obj and

1544     /**
1545      * If given class is the same as the class associated with this class
1546      * descriptor, returns reference to this class descriptor.  Otherwise,
1547      * returns variant of this class descriptor bound to given class.
1548      */
1549     private ObjectStreamClass getVariantFor(Class<?> cl)
1550         throws InvalidClassException
1551     {
1552         if (this.cl == cl) {
1553             return this;
1554         }
1555         ObjectStreamClass desc = new ObjectStreamClass();
1556         if (isProxy) {
1557             desc.initProxy(cl, null, superDesc);
1558         } else {
1559             desc.initNonProxy(this, cl, null, superDesc);
1560         }
1561         return desc;
1562     }
1563 
1564     /**
1565      * Return a method handle for the static method or constructor(s) that matches the
1566      * serializable fields and annotated with {@link DeserializeConstructor}.
1567      * The descriptor for the class is still being initialized, so is passed the fields needed.
1568      * @param clazz The class to query
1569      * @param fields the serializable fields of the class
1570      * @return a MethodHandle, null if none found
1571      */
1572     @SuppressWarnings("unchecked")
1573     private static MethodHandle getDeserializingValueCons(Class<?> clazz,
1574                                                           ObjectStreamField[] fields) {
1575         // Search for annotated static factory in methods or constructors
1576         MethodHandles.Lookup lookup = MethodHandles.lookup();
1577         MethodHandle mh = Stream.concat(
1578                 Arrays.stream(clazz.getDeclaredMethods()).filter(m -> Modifier.isStatic(m.getModifiers())),
1579                 Arrays.stream(clazz.getDeclaredConstructors()))
1580                 .filter(m -> m.isAnnotationPresent(DeserializeConstructor.class))
1581                 .map(m -> {
1582                     try {
1583                         m.setAccessible(true);
1584                         return (m instanceof Constructor<?> cons)
1585                                 ? lookup.unreflectConstructor(cons)
1586                                 : lookup.unreflect(((Method) m));
1587                     } catch (IllegalAccessException iae) {
1588                         throw new InternalError(iae);   // should not occur after setAccessible
1589                     }})
1590                 .filter(m -> matchFactoryParamTypes(clazz, m, fields))
1591                 .findFirst().orElse(null);
1592         TRACE("DeserializeConstructor for %s, mh: %s", clazz,  mh);
1593         return mh;
1594     }
1595 
1596     /**
1597      * Check that the parameters of the factory method match the fields of this class.
1598      *
1599      * @param mh a MethodHandle for a constructor or factory
1600      * @return true if all fields match the parameters, false if not
1601      */
1602     private static boolean matchFactoryParamTypes(Class<?> clazz,
1603                                                        MethodHandle mh,
1604                                                        ObjectStreamField[] fields) {
1605         TRACE("  matchFactoryParams checking class: %s, mh: %s", clazz, mh);
1606         var params = mh.type().parameterList();
1607         if (params.size() != fields.length) {
1608             TRACE("   matchFactoryParams %s, arg count mismatch %d params != %d fields",
1609                     clazz, params.size(), fields.length);
1610             return false;    // Mismatch in count of fields and parameters
1611         }
1612         for (ObjectStreamField field : fields) {
1613             int argIndex = field.getArgIndex();
1614             final Class<?> paramtype = params.get(argIndex);
1615             if (!field.getType().equals(paramtype)) {
1616                 TRACE("   matchFactoryParams %s: argIndex: %d type mismatch field: %s != param: %s",
1617                         clazz, argIndex, field.getType(), paramtype);
1618                 return false;
1619             }
1620         }
1621         return true;
1622     }
1623 
1624     /**
1625      * Returns public no-arg constructor of given class, or null if none found.
1626      * Access checks are disabled on the returned constructor (if any), since
1627      * the defining class may still be non-public.
1628      */
1629     private static Constructor<?> getExternalizableConstructor(Class<?> cl) {
1630         try {
1631             Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
1632             cons.setAccessible(true);
1633             return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
1634                 cons : null;
1635         } catch (NoSuchMethodException | InaccessibleObjectException ex) {
1636             return null;
1637         }
1638     }
1639 
1640     /**
1641      * Returns subclass-accessible no-arg constructor of first non-serializable
1642      * superclass, or null if none found.  Access checks are disabled on the
1643      * returned constructor (if any).
1644      */
1645     private static Constructor<?> getSerializableConstructor(Class<?> cl) {
1646         return reflFactory.newConstructorForSerialization(cl);
1647     }
1648 
1649     /**
1650      * Returns the canonical constructor for the given record class, or null if
1651      * the not found ( which should never happen for correctly generated record
1652      * classes ).
1653      */
1654     @SuppressWarnings("removal")
1655     private static MethodHandle canonicalRecordCtr(Class<?> cls) {

1850 
1851         ObjectStreamField[] boundFields =
1852             new ObjectStreamField[serialPersistentFields.length];
1853         Set<String> fieldNames = HashSet.newHashSet(serialPersistentFields.length);
1854 
1855         for (int i = 0; i < serialPersistentFields.length; i++) {
1856             ObjectStreamField spf = serialPersistentFields[i];
1857 
1858             String fname = spf.getName();
1859             if (fieldNames.contains(fname)) {
1860                 throw new InvalidClassException(
1861                     "multiple serializable fields named " + fname);
1862             }
1863             fieldNames.add(fname);
1864 
1865             try {
1866                 Field f = cl.getDeclaredField(fname);
1867                 if ((f.getType() == spf.getType()) &&
1868                     ((f.getModifiers() & Modifier.STATIC) == 0))
1869                 {
1870                     boundFields[i] = new ObjectStreamField(f, spf.isUnshared(), true, i);

1871                 }
1872             } catch (NoSuchFieldException ex) {
1873             }
1874             if (boundFields[i] == null) {
1875                 boundFields[i] = new ObjectStreamField(fname, spf.getType(), spf.isUnshared(), i);

1876             }
1877         }
1878         return boundFields;
1879     }
1880 
1881     /**
1882      * Returns array of ObjectStreamFields corresponding to all non-static
1883      * non-transient fields declared by given class.  Each ObjectStreamField
1884      * contains a Field object for the field it represents.  If no default
1885      * serializable fields exist, NO_FIELDS is returned.
1886      */
1887     private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
1888         Field[] clFields = cl.getDeclaredFields();
1889         ArrayList<ObjectStreamField> list = new ArrayList<>();
1890         int mask = Modifier.STATIC | Modifier.TRANSIENT;
1891 
1892         for (int i = 0, argIndex = 0; i < clFields.length; i++) {
1893             if ((clFields[i].getModifiers() & mask) == 0) {
1894                 list.add(new ObjectStreamField(clFields[i], false, true, argIndex++));
1895             }
1896         }
1897         int size = list.size();
1898         return (size == 0) ? NO_FIELDS :
1899             list.toArray(new ObjectStreamField[size]);
1900     }
1901 
1902     /**
1903      * Returns explicit serial version UID value declared by given class, or
1904      * null if none.
1905      */
1906     private static Long getDeclaredSUID(Class<?> cl) {
1907         try {
1908             Field f = cl.getDeclaredField("serialVersionUID");
1909             int mask = Modifier.STATIC | Modifier.FINAL;
1910             if ((f.getModifiers() & mask) == mask) {
1911                 f.setAccessible(true);
1912                 return f.getLong(null);
1913             }
1914         } catch (Exception ex) {

2226                     case 'D' -> UNSAFE.putDouble(obj, key, ByteArray.getDouble(buf, off));
2227                     default  -> throw new InternalError();
2228                 }
2229             }
2230         }
2231 
2232         /**
2233          * Fetches the serializable object field values of object obj and
2234          * stores them in array vals starting at offset 0.  The caller is
2235          * responsible for ensuring that obj is of the proper type.
2236          */
2237         void getObjFieldValues(Object obj, Object[] vals) {
2238             if (obj == null) {
2239                 throw new NullPointerException();
2240             }
2241             /* assuming checkDefaultSerialize() has been called on the class
2242              * descriptor this FieldReflector was obtained from, no field keys
2243              * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
2244              */
2245             for (int i = numPrimFields; i < fields.length; i++) {
2246                 Field f = fields[i].getField();
2247                 vals[offsets[i]] = switch (typeCodes[i]) {
2248                     case 'L', '[' ->
2249                             UNSAFE.isFlatField(f)
2250                                     ? UNSAFE.getValue(obj, readKeys[i], f.getType())
2251                                     : UNSAFE.getReference(obj, readKeys[i]);
2252                     default       -> throw new InternalError();
2253                 };
2254             }
2255         }
2256 
2257         /**
2258          * Checks that the given values, from array vals starting at offset 0,
2259          * are assignable to the given serializable object fields.
2260          * @throws ClassCastException if any value is not assignable
2261          */
2262         void checkObjectFieldValueTypes(Object obj, Object[] vals) {
2263             setObjFieldValues(obj, vals, true);
2264         }
2265 
2266         /**
2267          * Sets the serializable object fields of object obj using values from
2268          * array vals starting at offset 0.  The caller is responsible for
2269          * ensuring that obj is of the proper type; however, attempts to set a
2270          * field with a value of the wrong type will trigger an appropriate
2271          * ClassCastException.
2272          */
2273         void setObjFieldValues(Object obj, Object[] vals) {
2274             setObjFieldValues(obj, vals, false);
2275         }
2276 
2277         private void setObjFieldValues(Object obj, Object[] vals, boolean dryRun) {
2278             if (obj == null && !dryRun) {
2279                 throw new NullPointerException();
2280             }
2281             for (int i = numPrimFields; i < fields.length; i++) {
2282                 long key = writeKeys[i];
2283                 if (key == Unsafe.INVALID_FIELD_OFFSET) {
2284                     continue;           // discard value
2285                 }
2286                 switch (typeCodes[i]) {
2287                     case 'L', '[' -> {
2288                         Field f = fields[i].getField();
2289                         Object val = vals[offsets[i]];
2290                         if (val != null &&
2291                             !types[i - numPrimFields].isInstance(val))
2292                         {

2293                             throw new ClassCastException(
2294                                 "cannot assign instance of " +
2295                                 val.getClass().getName() + " to field " +
2296                                 f.getDeclaringClass().getName() + "." +
2297                                 f.getName() + " of type " +
2298                                 f.getType().getName() + " in instance of " +
2299                                 obj.getClass().getName());
2300                         }
2301                         if (!dryRun) {
2302                             if (UNSAFE.isFlatField(f)) {
2303                                 UNSAFE.putValue(obj, key, f.getType(), val);
2304                             } else {
2305                                 UNSAFE.putReference(obj, key, val);
2306                             }
2307                         }
2308                     }
2309                     default -> throw new InternalError();
2310                 }
2311             }
2312         }
2313     }
2314 
2315     /**
2316      * Matches given set of serializable fields with serializable fields
2317      * described by the given local class descriptor, and returns a
2318      * FieldReflector instance capable of setting/getting values from the
2319      * subset of fields that match (non-matching fields are treated as filler,
2320      * for which get operations return default values and set operations
2321      * discard given values).  Throws InvalidClassException if unresolvable
2322      * type conflicts exist between the two sets of fields.
2323      */
2324     private static FieldReflector getReflector(ObjectStreamField[] fields,
2325                                                ObjectStreamClass localDesc)
2326         throws InvalidClassException
2327     {

2399          * a non-local class descriptor.  To preserve this (questionable)
2400          * behavior, the ObjectStreamField instances returned by matchFields
2401          * cannot report non-primitive types other than Object.class; hence
2402          * localFields cannot be returned directly.
2403          */
2404 
2405         ObjectStreamField[] matches = new ObjectStreamField[fields.length];
2406         for (int i = 0; i < fields.length; i++) {
2407             ObjectStreamField f = fields[i], m = null;
2408             for (int j = 0; j < localFields.length; j++) {
2409                 ObjectStreamField lf = localFields[j];
2410                 if (f.getName().equals(lf.getName())) {
2411                     if ((f.isPrimitive() || lf.isPrimitive()) &&
2412                         f.getTypeCode() != lf.getTypeCode())
2413                     {
2414                         throw new InvalidClassException(localDesc.name,
2415                             "incompatible types for field " + f.getName());
2416                     }
2417                     if (lf.getField() != null) {
2418                         m = new ObjectStreamField(
2419                             lf.getField(), lf.isUnshared(), true, lf.getArgIndex()); // Don't hide type
2420                     } else {
2421                         m = new ObjectStreamField(
2422                             lf.getName(), lf.getSignature(), lf.isUnshared(), lf.getArgIndex());
2423                     }
2424                 }
2425             }
2426             if (m == null) {
2427                 m = new ObjectStreamField(
2428                     f.getName(), f.getSignature(), false, -1);
2429             }
2430             m.setOffset(f.getOffset());
2431             matches[i] = m;
2432         }
2433         return matches;
2434     }
2435 
2436     /**
2437      * A LRA cache of record deserialization constructors.
2438      */
2439     @SuppressWarnings("serial")
2440     private static final class DeserializationConstructorsCache
2441         extends ConcurrentHashMap<DeserializationConstructorsCache.Key, MethodHandle>  {
2442 
2443         // keep max. 10 cached entries - when the 11th element is inserted the oldest
2444         // is removed and 10 remains - 11 is the biggest map size where internal
2445         // table of 16 elements is sufficient (inserting 12th element would resize it to 32)
2446         private static final int MAX_SIZE = 10;
2447         private Key.Impl first, last; // first and last in FIFO queue
2448 

2530                     this.fieldTypes = new Class<?>[fields.length];
2531                     for (int i = 0; i < fields.length; i++) {
2532                         fieldNames[i] = fields[i].getName();
2533                         fieldTypes[i] = fields[i].getType();
2534                     }
2535                 }
2536 
2537                 @Override
2538                 int length() { return fieldNames.length; }
2539 
2540                 @Override
2541                 String fieldName(int i) { return fieldNames[i]; }
2542 
2543                 @Override
2544                 Class<?> fieldType(int i) { return fieldTypes[i]; }
2545             }
2546         }
2547     }
2548 
2549     /** Record specific support for retrieving and binding stream field values. */
2550     static final class ConstructorSupport {
2551         /**
2552          * Returns canonical record constructor adapted to take two arguments:
2553          * {@code (byte[] primValues, Object[] objValues)}
2554          * and return
2555          * {@code Object}
2556          */
2557         @SuppressWarnings("removal")
2558         static MethodHandle deserializationCtr(ObjectStreamClass desc) {
2559             // check the cached value 1st
2560             MethodHandle mh = desc.deserializationCtr;
2561             if (mh != null) return mh;
2562             mh = desc.deserializationCtrs.get(desc.getFields(false));
2563             if (mh != null) return desc.deserializationCtr = mh;
2564 
2565             // retrieve record components
2566             RecordComponent[] recordComponents;
2567             try {
2568                 Class<?> cls = desc.forClass();
2569                 PrivilegedExceptionAction<RecordComponent[]> pa = cls::getRecordComponents;
2570                 recordComponents = AccessController.doPrivileged(pa);

2587             for (int i = recordComponents.length-1; i >= 0; i--) {
2588                 String name = recordComponents[i].getName();
2589                 Class<?> type = recordComponents[i].getType();
2590                 // obtain stream field extractor that extracts argument at
2591                 // position i (Ti+1) from primValues and objValues arrays
2592                 // (byte[], Object[]):Ti+1
2593                 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2594                 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2595                 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2596                 mh = MethodHandles.foldArguments(mh, i, combiner);
2597             }
2598             // what we are left with is a MethodHandle taking just the primValues
2599             // and objValues arrays and returning the constructed record instance
2600             // (byte[], Object[]):Object
2601 
2602             // store it into cache and return the 1st value stored
2603             return desc.deserializationCtr =
2604                 desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
2605         }
2606 
2607         /**
2608          * Returns value object constructor adapted to take two arguments:
2609          * {@code (byte[] primValues, Object[] objValues)} and return {@code Object}
2610          */
2611         static MethodHandle deserializationValueCons(ObjectStreamClass desc) {
2612             // check the cached value 1st
2613             MethodHandle mh = desc.deserializationCtr;
2614             if (mh != null) return mh;
2615             mh = desc.deserializationCtrs.get(desc.getFields(false));
2616             if (mh != null) return desc.deserializationCtr = mh;
2617 
2618             // retrieve the selected constructor
2619             // (T1, T2, ..., Tn):TR
2620             ObjectStreamClass localDesc = desc.localDesc;
2621             mh = localDesc.canonicalCtr;
2622             MethodType mt = mh.type();
2623 
2624             // change return type to Object
2625             // (T1, T2, ..., Tn):TR -> (T1, T2, ..., Tn):Object
2626             mh = mh.asType(mh.type().changeReturnType(Object.class));
2627 
2628             // drop last 2 arguments representing primValues and objValues arrays
2629             // (T1, T2, ..., Tn):Object -> (T1, T2, ..., Tn, byte[], Object[]):Object
2630             mh = MethodHandles.dropArguments(mh, mh.type().parameterCount(), byte[].class, Object[].class);
2631 
2632             Class<?>[] params = mt.parameterArray();
2633             for (int i = params.length-1; i >= 0; i--) {
2634                 // Get the name from the local descriptor matching the argIndex
2635                 var field = getFieldForArgIndex(localDesc, i);
2636                 String name = (field == null) ? "" : field.getName();   // empty string to supply default
2637                 Class<?> type = params[i];
2638                 // obtain stream field extractor that extracts argument at
2639                 // position i (Ti+1) from primValues and objValues arrays
2640                 // (byte[], Object[]):Ti+1
2641                 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2642                 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2643                 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2644                 mh = MethodHandles.foldArguments(mh, i, combiner);
2645             }
2646             // what we are left with is a MethodHandle taking just the primValues
2647             // and objValues arrays and returning the constructed instance
2648             // (byte[], Object[]):Object
2649 
2650             // store it into cache and return the 1st value stored
2651             return desc.deserializationCtr =
2652                 desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
2653         }
2654 
2655         // Find the ObjectStreamField for the argument index, otherwise null
2656         private static ObjectStreamField getFieldForArgIndex(ObjectStreamClass desc, int argIndex) {
2657             for (var field : desc.fields) {
2658                 if (field.getArgIndex() == argIndex)
2659                     return field;
2660             }
2661             TRACE("field for ArgIndex is null: %s, index: %d, fields: %s",
2662                     desc, argIndex, Arrays.toString(desc.fields));
2663             return null;
2664         }
2665 
2666         /** Returns the number of primitive fields for the given descriptor. */
2667         private static int numberPrimValues(ObjectStreamClass desc) {
2668             ObjectStreamField[] fields = desc.getFields();
2669             int primValueCount = 0;
2670             for (int i = 0; i < fields.length; i++) {
2671                 if (fields[i].isPrimitive())
2672                     primValueCount++;
2673                 else
2674                     break;  // can be no more
2675             }
2676             return primValueCount;
2677         }
2678 
2679         /**
2680          * Returns extractor MethodHandle taking the primValues and objValues arrays
2681          * and extracting the argument of canonical constructor with given name and type
2682          * or producing  default value for the given type if the field is absent.
2683          */
2684         private static MethodHandle streamFieldExtractor(String pName,
2685                                                          Class<?> pType,
< prev index next >