< prev index next >

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

Print this page

  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.io;
  27 
  28 import java.lang.invoke.MethodHandle;
  29 import java.lang.invoke.MethodHandles;
  30 import java.lang.invoke.MethodType;
  31 import java.lang.reflect.Constructor;
  32 import java.lang.reflect.Field;

  33 import java.lang.reflect.InvocationTargetException;
  34 import java.lang.reflect.RecordComponent;
  35 import java.lang.reflect.Member;
  36 import java.lang.reflect.Method;
  37 import java.lang.reflect.Modifier;
  38 import java.lang.reflect.Proxy;
  39 import java.security.MessageDigest;
  40 import java.security.NoSuchAlgorithmException;
  41 import java.util.ArrayList;
  42 import java.util.Arrays;
  43 import java.util.Collections;
  44 import java.util.Comparator;
  45 import java.util.HashSet;

  46 import java.util.Map;
  47 import java.util.Set;
  48 import java.util.concurrent.ConcurrentHashMap;

  49 

  50 import jdk.internal.event.SerializationMisdeclarationEvent;
  51 import jdk.internal.misc.Unsafe;
  52 import jdk.internal.reflect.ReflectionFactory;
  53 import jdk.internal.util.ByteArray;



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

































































  87     private static class Caches {
  88         /** cache mapping local classes -> descriptors */
  89         static final ClassCache<ObjectStreamClass> localDescs =
  90             new ClassCache<>() {
  91                 @Override
  92                 protected ObjectStreamClass computeValue(Class<?> type) {
  93                     return new ObjectStreamClass(type);
  94                 }
  95             };
  96 
  97         /** cache mapping field group/local desc pairs -> field reflectors */
  98         static final ClassCache<Map<FieldReflectorKey, FieldReflector>> reflectors =
  99             new ClassCache<>() {
 100                 @Override
 101                 protected Map<FieldReflectorKey, FieldReflector> computeValue(Class<?> type) {
 102                     return new ConcurrentHashMap<>();
 103                 }
 104             };
 105     }
 106 
 107     /** class associated with this descriptor (if any) */
 108     private Class<?> cl;
 109     /** name of class represented by this descriptor */
 110     private String name;
 111     /** serialVersionUID of represented class (null if not computed yet) */
 112     private volatile Long suid;
 113 
 114     /** true if represents dynamic proxy class */
 115     private boolean isProxy;
 116     /** true if represents enum type */
 117     private boolean isEnum;
 118     /** true if represents record type */
 119     private boolean isRecord;




 120     /** true if represented class implements Serializable */
 121     private boolean serializable;
 122     /** true if represented class implements Externalizable */
 123     private boolean externalizable;
 124     /** true if desc has data written by class-defined writeObject method */
 125     private boolean hasWriteObjectData;
 126     /**
 127      * true if desc has externalizable data written in block data format; this
 128      * must be true by default to accommodate ObjectInputStream subclasses which
 129      * override readClassDescriptor() to return class descriptors obtained from
 130      * ObjectStreamClass.lookup() (see 4461737)
 131      */
 132     private boolean hasBlockExternalData = true;
 133 
 134     /**
 135      * Contains information about InvalidClassException instances to be thrown
 136      * when attempting operations on an invalid class. Note that instances of
 137      * this class are immutable and are potentially shared among
 138      * ObjectStreamClass instances.
 139      */

 157     }
 158 
 159     /** exception (if any) thrown while attempting to resolve class */
 160     private ClassNotFoundException resolveEx;
 161     /** exception (if any) to throw if non-enum deserialization attempted */
 162     private ExceptionInfo deserializeEx;
 163     /** exception (if any) to throw if non-enum serialization attempted */
 164     private ExceptionInfo serializeEx;
 165     /** exception (if any) to throw if default serialization attempted */
 166     private ExceptionInfo defaultSerializeEx;
 167 
 168     /** serializable fields */
 169     private ObjectStreamField[] fields;
 170     /** aggregate marshalled size of primitive fields */
 171     private int primDataSize;
 172     /** number of non-primitive fields */
 173     private int numObjFields;
 174     /** reflector for setting/getting serializable field values */
 175     private FieldReflector fieldRefl;
 176     /** data layout of serialized objects described by this class desc */
 177     private volatile ClassDataSlot[] dataLayout;
 178 
 179     /** serialization-appropriate constructor, or null if none */
 180     private Constructor<?> cons;
 181     /** record canonical constructor (shared among OSCs for same class), or null */
 182     private MethodHandle canonicalCtr;
 183     /** cache of record deserialization constructors per unique set of stream fields
 184      * (shared among OSCs for same class), or null */
 185     private DeserializationConstructorsCache deserializationCtrs;
 186     /** session-cache of record deserialization constructor
 187      * (in de-serialized OSC only), or null */
 188     private MethodHandle deserializationCtr;
 189 
 190     /** class-defined writeObject method, or null if none */
 191     private Method writeObjectMethod;
 192     /** class-defined readObject method, or null if none */
 193     private Method readObjectMethod;
 194     /** class-defined readObjectNoData method, or null if none */
 195     private Method readObjectNoDataMethod;
 196     /** class-defined writeReplace method, or null if none */
 197     private Method writeReplaceMethod;

 321      * @param   cl class to look up
 322      * @param   all if true, return descriptors for all classes; if false, only
 323      *          return descriptors for serializable classes
 324      */
 325     static ObjectStreamClass lookup(Class<?> cl, boolean all) {
 326         if (!(all || Serializable.class.isAssignableFrom(cl))) {
 327             return null;
 328         }
 329         return Caches.localDescs.get(cl);
 330     }
 331 
 332     /**
 333      * Creates local class descriptor representing given class.
 334      */
 335     private ObjectStreamClass(final Class<?> cl) {
 336         this.cl = cl;
 337         name = cl.getName();
 338         isProxy = Proxy.isProxyClass(cl);
 339         isEnum = Enum.class.isAssignableFrom(cl);
 340         isRecord = cl.isRecord();

 341         serializable = Serializable.class.isAssignableFrom(cl);
 342         externalizable = Externalizable.class.isAssignableFrom(cl);
 343 
 344         Class<?> superCl = cl.getSuperclass();
 345         superDesc = (superCl != null) ? lookup(superCl, false) : null;
 346         localDesc = this;
 347 
 348         if (serializable) {
 349             if (isEnum) {
 350                 suid = 0L;
 351                 fields = NO_FIELDS;
 352             } else if (cl.isArray()) {
 353                 fields = NO_FIELDS;
 354             } else {
 355                 suid = getDeclaredSUID(cl);
 356                 try {
 357                     fields = getSerialFields(cl);
 358                     computeFieldOffsets();
 359                 } catch (InvalidClassException e) {
 360                     serializeEx = deserializeEx =
 361                             new ExceptionInfo(e.classname, e.getMessage());
 362                     fields = NO_FIELDS;
 363                 }
 364 
 365                 if (isRecord) {

 366                     canonicalCtr = canonicalRecordCtr(cl);
 367                     deserializationCtrs = new DeserializationConstructorsCache();
 368                 } else if (externalizable) {
 369                     cons = getExternalizableConstructor(cl);























 370                 } else {
 371                     cons = getSerializableConstructor(cl);
 372                     writeObjectMethod = getPrivateMethod(cl, "writeObject",
 373                             new Class<?>[]{ObjectOutputStream.class},
 374                             Void.TYPE);
 375                     readObjectMethod = getPrivateMethod(cl, "readObject",
 376                             new Class<?>[]{ObjectInputStream.class},
 377                             Void.TYPE);
 378                     readObjectNoDataMethod = getPrivateMethod(
 379                             cl, "readObjectNoData", null, Void.TYPE);
 380                     hasWriteObjectData = (writeObjectMethod != null);




 381                 }
 382                 writeReplaceMethod = getInheritableMethod(
 383                         cl, "writeReplace", null, Object.class);
 384                 readResolveMethod = getInheritableMethod(
 385                         cl, "readResolve", null, Object.class);
 386             }
 387         } else {
 388             suid = 0L;
 389             fields = NO_FIELDS;
 390         }
 391 
 392         try {
 393             fieldRefl = getReflector(fields, this);
 394         } catch (InvalidClassException ex) {
 395             // field mismatches impossible when matching local fields vs. self
 396             throw new InternalError(ex);
 397         }
 398 
 399         if (deserializeEx == null) {
 400             if (isEnum) {
 401                 deserializeEx = new ExceptionInfo(name, "enum type");
 402             } else if (cons == null && !isRecord) {
 403                 deserializeEx = new ExceptionInfo(name, "no valid constructor");
 404             }
 405         }
 406         if (isRecord && canonicalCtr == null) {
 407             deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
 408         } else {
 409             for (int i = 0; i < fields.length; i++) {
 410                 if (fields[i].getField() == null) {
 411                     defaultSerializeEx = new ExceptionInfo(
 412                         name, "unmatched serializable field(s) declared");
 413                 }
 414             }
 415         }
 416         initialized = true;
 417 
 418         if (SerializationMisdeclarationEvent.enabled() && serializable) {
 419             SerializationMisdeclarationChecker.checkMisdeclarations(cl);
 420         }
 421     }
 422 

 441             if (!osc.isProxy) {
 442                 throw new InvalidClassException(
 443                     "cannot bind proxy descriptor to a non-proxy class");
 444             }
 445         }
 446         this.cl = cl;
 447         this.resolveEx = resolveEx;
 448         this.superDesc = superDesc;
 449         isProxy = true;
 450         serializable = true;
 451         suid = 0L;
 452         fields = NO_FIELDS;
 453         if (osc != null) {
 454             localDesc = osc;
 455             name = localDesc.name;
 456             externalizable = localDesc.externalizable;
 457             writeReplaceMethod = localDesc.writeReplaceMethod;
 458             readResolveMethod = localDesc.readResolveMethod;
 459             deserializeEx = localDesc.deserializeEx;
 460             cons = localDesc.cons;



 461         }
 462         fieldRefl = getReflector(fields, localDesc);
 463         initialized = true;
 464     }
 465 
 466     /**
 467      * Initializes class descriptor representing a non-proxy class.
 468      */
 469     void initNonProxy(ObjectStreamClass model,
 470                       Class<?> cl,
 471                       ClassNotFoundException resolveEx,
 472                       ObjectStreamClass superDesc)
 473         throws InvalidClassException
 474     {
 475         long suid = model.getSerialVersionUID();
 476         ObjectStreamClass osc = null;
 477         if (cl != null) {
 478             osc = lookup(cl, true);
 479             if (osc.isProxy) {
 480                 throw new InvalidClassException(

 519         }
 520 
 521         this.cl = cl;
 522         this.resolveEx = resolveEx;
 523         this.superDesc = superDesc;
 524         name = model.name;
 525         this.suid = suid;
 526         isProxy = false;
 527         isEnum = model.isEnum;
 528         serializable = model.serializable;
 529         externalizable = model.externalizable;
 530         hasBlockExternalData = model.hasBlockExternalData;
 531         hasWriteObjectData = model.hasWriteObjectData;
 532         fields = model.fields;
 533         primDataSize = model.primDataSize;
 534         numObjFields = model.numObjFields;
 535 
 536         if (osc != null) {
 537             localDesc = osc;
 538             isRecord = localDesc.isRecord;

 539             // canonical record constructor is shared
 540             canonicalCtr = localDesc.canonicalCtr;
 541             // cache of deserialization constructors is shared
 542             deserializationCtrs = localDesc.deserializationCtrs;
 543             writeObjectMethod = localDesc.writeObjectMethod;
 544             readObjectMethod = localDesc.readObjectMethod;
 545             readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
 546             writeReplaceMethod = localDesc.writeReplaceMethod;
 547             readResolveMethod = localDesc.readResolveMethod;
 548             if (deserializeEx == null) {
 549                 deserializeEx = localDesc.deserializeEx;
 550             }
 551             assert cl.isRecord() ? localDesc.cons == null : true;
 552             cons = localDesc.cons;






 553         }
 554 
 555         fieldRefl = getReflector(fields, localDesc);
 556         // reassign to matched fields so as to reflect local unshared settings
 557         fields = fieldRefl.getFields();
 558 
 559         initialized = true;
 560     }
 561 
 562     /**
 563      * Reads non-proxy class descriptor information from given input stream.
 564      * The resulting class descriptor is not fully functional; it can only be
 565      * used as input to the ObjectInputStream.resolveClass() and
 566      * ObjectStreamClass.initNonProxy() methods.
 567      */
 568     void readNonProxy(ObjectInputStream in)
 569         throws IOException, ClassNotFoundException
 570     {
 571         name = in.readUTF();
 572         suid = in.readLong();

 588         serializable = externalizable || sflag;
 589         isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
 590         if (isEnum && suid.longValue() != 0L) {
 591             throw new InvalidClassException(name,
 592                 "enum descriptor has non-zero serialVersionUID: " + suid);
 593         }
 594 
 595         int numFields = in.readShort();
 596         if (isEnum && numFields != 0) {
 597             throw new InvalidClassException(name,
 598                 "enum descriptor has non-zero field count: " + numFields);
 599         }
 600         fields = (numFields > 0) ?
 601             new ObjectStreamField[numFields] : NO_FIELDS;
 602         for (int i = 0; i < numFields; i++) {
 603             char tcode = (char) in.readByte();
 604             String fname = in.readUTF();
 605             String signature = ((tcode == 'L') || (tcode == '[')) ?
 606                 in.readTypeString() : String.valueOf(tcode);
 607             try {
 608                 fields[i] = new ObjectStreamField(fname, signature, false);
 609             } catch (RuntimeException e) {
 610                 throw new InvalidClassException(name,
 611                                                 "invalid descriptor for field " +
 612                                                 fname, e);
 613             }
 614         }
 615         computeFieldOffsets();
 616     }
 617 
 618     /**
 619      * Writes non-proxy class descriptor information to given output stream.
 620      */
 621     void writeNonProxy(ObjectOutputStream out) throws IOException {
 622         out.writeUTF(name);
 623         out.writeLong(getSerialVersionUID());
 624 
 625         byte flags = 0;
 626         if (externalizable) {
 627             flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
 628             int protocol = out.getProtocolVersion();

 799     }
 800 
 801     /**
 802      * Returns true if represented class implements Externalizable, false
 803      * otherwise.
 804      */
 805     boolean isExternalizable() {
 806         requireInitialized();
 807         return externalizable;
 808     }
 809 
 810     /**
 811      * Returns true if represented class implements Serializable, false
 812      * otherwise.
 813      */
 814     boolean isSerializable() {
 815         requireInitialized();
 816         return serializable;
 817     }
 818 
















 819     /**
 820      * Returns true if class descriptor represents externalizable class that
 821      * has written its data in 1.2 (block data) format, false otherwise.
 822      */
 823     boolean hasBlockExternalData() {
 824         requireInitialized();
 825         return hasBlockExternalData;
 826     }
 827 
 828     /**
 829      * Returns true if class descriptor represents serializable (but not
 830      * externalizable) class which has written its data via a custom
 831      * writeObject() method, false otherwise.
 832      */
 833     boolean hasWriteObjectData() {
 834         requireInitialized();
 835         return hasWriteObjectData;
 836     }
 837 
 838     /**
 839      * Returns true if represented class is serializable/externalizable and can
 840      * be instantiated by the serialization runtime--i.e., if it is
 841      * externalizable and defines a public no-arg constructor, or if it is
 842      * non-externalizable and its first non-serializable superclass defines an
 843      * accessible no-arg constructor.  Otherwise, returns false.


 844      */
 845     boolean isInstantiable() {
 846         requireInitialized();
 847         return (cons != null);
 848     }
 849 
 850     /**
 851      * Returns true if represented class is serializable (but not
 852      * externalizable) and defines a conformant writeObject method.  Otherwise,
 853      * returns false.
 854      */
 855     boolean hasWriteObjectMethod() {
 856         requireInitialized();
 857         return (writeObjectMethod != null);
 858     }
 859 
 860     /**
 861      * Returns true if represented class is serializable (but not
 862      * externalizable) and defines a conformant readObject method.  Otherwise,
 863      * returns false.
 864      */
 865     boolean hasReadObjectMethod() {
 866         requireInitialized();
 867         return (readObjectMethod != null);

1084     /**
1085      * Class representing the portion of an object's serialized form allotted
1086      * to data described by a given class descriptor.  If "hasData" is false,
1087      * the object's serialized form does not contain data associated with the
1088      * class descriptor.
1089      */
1090     static class ClassDataSlot {
1091 
1092         /** class descriptor "occupying" this slot */
1093         final ObjectStreamClass desc;
1094         /** true if serialized form includes data for this slot's descriptor */
1095         final boolean hasData;
1096 
1097         ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
1098             this.desc = desc;
1099             this.hasData = hasData;
1100         }
1101     }
1102 
1103     /**
1104      * Returns array of ClassDataSlot instances representing the data layout
1105      * (including superclass data) for serialized objects described by this
1106      * class descriptor.  ClassDataSlots are ordered by inheritance with those
1107      * containing "higher" superclasses appearing first.  The final
1108      * ClassDataSlot contains a reference to this descriptor.
1109      */
1110     ClassDataSlot[] getClassDataLayout() throws InvalidClassException {
1111         // REMIND: synchronize instead of relying on volatile?
1112         if (dataLayout == null) {
1113             dataLayout = getClassDataLayout0();
1114         }
1115         return dataLayout;
1116     }
1117 
1118     private ClassDataSlot[] getClassDataLayout0()
1119         throws InvalidClassException
1120     {
1121         ArrayList<ClassDataSlot> slots = new ArrayList<>();
1122         Class<?> start = cl, end = cl;
1123 
1124         // locate closest non-serializable superclass
1125         while (end != null && Serializable.class.isAssignableFrom(end)) {
1126             end = end.getSuperclass();
1127         }
1128 
1129         HashSet<String> oscNames = new HashSet<>(3);
1130 
1131         for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
1132             if (oscNames.contains(d.name)) {
1133                 throw new InvalidClassException("Circular reference.");
1134             } else {
1135                 oscNames.add(d.name);
1136             }
1137 
1138             // search up inheritance hierarchy for class with matching name
1139             String searchName = (d.cl != null) ? d.cl.getName() : d.name;
1140             Class<?> match = null;

1149             if (match != null) {
1150                 for (Class<?> c = start; c != match; c = c.getSuperclass()) {
1151                     slots.add(new ClassDataSlot(
1152                         ObjectStreamClass.lookup(c, true), false));
1153                 }
1154                 start = match.getSuperclass();
1155             }
1156 
1157             // record descriptor/class pairing
1158             slots.add(new ClassDataSlot(d.getVariantFor(match), true));
1159         }
1160 
1161         // add "no data" slot for any leftover unmatched classes
1162         for (Class<?> c = start; c != end; c = c.getSuperclass()) {
1163             slots.add(new ClassDataSlot(
1164                 ObjectStreamClass.lookup(c, true), false));
1165         }
1166 
1167         // order slots from superclass -> subclass
1168         Collections.reverse(slots);
1169         return slots.toArray(new ClassDataSlot[slots.size()]);

1170     }
1171 
1172     /**
1173      * Returns aggregate size (in bytes) of marshalled primitive field values
1174      * for represented class.
1175      */
1176     int getPrimDataSize() {
1177         return primDataSize;
1178     }
1179 
1180     /**
1181      * Returns number of non-primitive serializable fields of represented
1182      * class.
1183      */
1184     int getNumObjFields() {
1185         return numObjFields;
1186     }
1187 
1188     /**
1189      * Fetches the serializable primitive field values of object obj and

1277     /**
1278      * If given class is the same as the class associated with this class
1279      * descriptor, returns reference to this class descriptor.  Otherwise,
1280      * returns variant of this class descriptor bound to given class.
1281      */
1282     private ObjectStreamClass getVariantFor(Class<?> cl)
1283         throws InvalidClassException
1284     {
1285         if (this.cl == cl) {
1286             return this;
1287         }
1288         ObjectStreamClass desc = new ObjectStreamClass();
1289         if (isProxy) {
1290             desc.initProxy(cl, null, superDesc);
1291         } else {
1292             desc.initNonProxy(this, cl, null, superDesc);
1293         }
1294         return desc;
1295     }
1296 




























































1297     /**
1298      * Returns public no-arg constructor of given class, or null if none found.
1299      * Access checks are disabled on the returned constructor (if any), since
1300      * the defining class may still be non-public.
1301      */
1302     private static Constructor<?> getExternalizableConstructor(Class<?> cl) {
1303         try {
1304             Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
1305             cons.setAccessible(true);
1306             return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
1307                 cons : null;
1308         } catch (NoSuchMethodException ex) {
1309             return null;
1310         }
1311     }
1312 
1313     /**
1314      * Returns subclass-accessible no-arg constructor of first non-serializable
1315      * superclass, or null if none found.  Access checks are disabled on the
1316      * returned constructor (if any).
1317      */
1318     private static Constructor<?> getSerializableConstructor(Class<?> cl) {
1319         return ReflectionFactory.getReflectionFactory().newConstructorForSerialization(cl);
1320     }
1321 
1322     /**
1323      * Returns the canonical constructor for the given record class, or null if
1324      * the not found ( which should never happen for correctly generated record
1325      * classes ).
1326      */
1327     private static MethodHandle canonicalRecordCtr(Class<?> cls) {
1328         assert cls.isRecord() : "Expected record, got: " + cls;

1519 
1520         ObjectStreamField[] boundFields =
1521             new ObjectStreamField[serialPersistentFields.length];
1522         Set<String> fieldNames = HashSet.newHashSet(serialPersistentFields.length);
1523 
1524         for (int i = 0; i < serialPersistentFields.length; i++) {
1525             ObjectStreamField spf = serialPersistentFields[i];
1526 
1527             String fname = spf.getName();
1528             if (fieldNames.contains(fname)) {
1529                 throw new InvalidClassException(
1530                     "multiple serializable fields named " + fname);
1531             }
1532             fieldNames.add(fname);
1533 
1534             try {
1535                 Field f = cl.getDeclaredField(fname);
1536                 if ((f.getType() == spf.getType()) &&
1537                     ((f.getModifiers() & Modifier.STATIC) == 0))
1538                 {
1539                     boundFields[i] =
1540                         new ObjectStreamField(f, spf.isUnshared(), true);
1541                 }
1542             } catch (NoSuchFieldException ex) {
1543             }
1544             if (boundFields[i] == null) {
1545                 boundFields[i] = new ObjectStreamField(
1546                     fname, spf.getType(), spf.isUnshared());
1547             }
1548         }
1549         return boundFields;
1550     }
1551 
1552     /**
1553      * Returns array of ObjectStreamFields corresponding to all non-static
1554      * non-transient fields declared by given class.  Each ObjectStreamField
1555      * contains a Field object for the field it represents.  If no default
1556      * serializable fields exist, NO_FIELDS is returned.
1557      */
1558     private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
1559         Field[] clFields = cl.getDeclaredFields();
1560         ArrayList<ObjectStreamField> list = new ArrayList<>();
1561         int mask = Modifier.STATIC | Modifier.TRANSIENT;
1562 
1563         for (int i = 0; i < clFields.length; i++) {
1564             if ((clFields[i].getModifiers() & mask) == 0) {
1565                 list.add(new ObjectStreamField(clFields[i], false, true));
1566             }
1567         }
1568         int size = list.size();
1569         return (size == 0) ? NO_FIELDS :
1570             list.toArray(new ObjectStreamField[size]);
1571     }
1572 
1573     /**
1574      * Returns explicit serial version UID value declared by given class, or
1575      * null if none.
1576      */
1577     private static Long getDeclaredSUID(Class<?> cl) {
1578         try {
1579             Field f = cl.getDeclaredField("serialVersionUID");
1580             int mask = Modifier.STATIC | Modifier.FINAL;
1581             if ((f.getModifiers() & mask) == mask) {
1582                 f.setAccessible(true);
1583                 return f.getLong(null);
1584             }
1585         } catch (Exception ex) {

1897                     case 'D' -> UNSAFE.putDouble(obj, key, ByteArray.getDouble(buf, off));
1898                     default  -> throw new InternalError();
1899                 }
1900             }
1901         }
1902 
1903         /**
1904          * Fetches the serializable object field values of object obj and
1905          * stores them in array vals starting at offset 0.  The caller is
1906          * responsible for ensuring that obj is of the proper type.
1907          */
1908         void getObjFieldValues(Object obj, Object[] vals) {
1909             if (obj == null) {
1910                 throw new NullPointerException();
1911             }
1912             /* assuming checkDefaultSerialize() has been called on the class
1913              * descriptor this FieldReflector was obtained from, no field keys
1914              * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
1915              */
1916             for (int i = numPrimFields; i < fields.length; i++) {

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



1919                     default       -> throw new InternalError();
1920                 };
1921             }
1922         }
1923 
1924         /**
1925          * Checks that the given values, from array vals starting at offset 0,
1926          * are assignable to the given serializable object fields.
1927          * @throws ClassCastException if any value is not assignable
1928          */
1929         void checkObjectFieldValueTypes(Object obj, Object[] vals) {
1930             setObjFieldValues(obj, vals, true);
1931         }
1932 
1933         /**
1934          * Sets the serializable object fields of object obj using values from
1935          * array vals starting at offset 0.  The caller is responsible for
1936          * ensuring that obj is of the proper type; however, attempts to set a
1937          * field with a value of the wrong type will trigger an appropriate
1938          * ClassCastException.
1939          */
1940         void setObjFieldValues(Object obj, Object[] vals) {
1941             setObjFieldValues(obj, vals, false);
1942         }
1943 
1944         private void setObjFieldValues(Object obj, Object[] vals, boolean dryRun) {
1945             if (obj == null) {
1946                 throw new NullPointerException();
1947             }
1948             for (int i = numPrimFields; i < fields.length; i++) {
1949                 long key = writeKeys[i];
1950                 if (key == Unsafe.INVALID_FIELD_OFFSET) {
1951                     continue;           // discard value
1952                 }
1953                 switch (typeCodes[i]) {
1954                     case 'L', '[' -> {

1955                         Object val = vals[offsets[i]];
1956                         if (val != null &&
1957                             !types[i - numPrimFields].isInstance(val))
1958                         {
1959                             Field f = fields[i].getField();
1960                             throw new ClassCastException(
1961                                 "cannot assign instance of " +
1962                                 val.getClass().getName() + " to field " +
1963                                 f.getDeclaringClass().getName() + "." +
1964                                 f.getName() + " of type " +
1965                                 f.getType().getName() + " in instance of " +
1966                                 obj.getClass().getName());
1967                         }
1968                         if (!dryRun)
1969                             UNSAFE.putReference(obj, key, val);





1970                     }
1971                     default -> throw new InternalError();
1972                 }
1973             }
1974         }
1975     }
1976 
1977     /**
1978      * Matches given set of serializable fields with serializable fields
1979      * described by the given local class descriptor, and returns a
1980      * FieldReflector instance capable of setting/getting values from the
1981      * subset of fields that match (non-matching fields are treated as filler,
1982      * for which get operations return default values and set operations
1983      * discard given values).  Throws InvalidClassException if unresolvable
1984      * type conflicts exist between the two sets of fields.
1985      */
1986     private static FieldReflector getReflector(ObjectStreamField[] fields,
1987                                                ObjectStreamClass localDesc)
1988         throws InvalidClassException
1989     {

2061          * a non-local class descriptor.  To preserve this (questionable)
2062          * behavior, the ObjectStreamField instances returned by matchFields
2063          * cannot report non-primitive types other than Object.class; hence
2064          * localFields cannot be returned directly.
2065          */
2066 
2067         ObjectStreamField[] matches = new ObjectStreamField[fields.length];
2068         for (int i = 0; i < fields.length; i++) {
2069             ObjectStreamField f = fields[i], m = null;
2070             for (int j = 0; j < localFields.length; j++) {
2071                 ObjectStreamField lf = localFields[j];
2072                 if (f.getName().equals(lf.getName())) {
2073                     if ((f.isPrimitive() || lf.isPrimitive()) &&
2074                         f.getTypeCode() != lf.getTypeCode())
2075                     {
2076                         throw new InvalidClassException(localDesc.name,
2077                             "incompatible types for field " + f.getName());
2078                     }
2079                     if (lf.getField() != null) {
2080                         m = new ObjectStreamField(
2081                             lf.getField(), lf.isUnshared(), false);
2082                     } else {
2083                         m = new ObjectStreamField(
2084                             lf.getName(), lf.getSignature(), lf.isUnshared());
2085                     }
2086                 }
2087             }
2088             if (m == null) {
2089                 m = new ObjectStreamField(
2090                     f.getName(), f.getSignature(), false);
2091             }
2092             m.setOffset(f.getOffset());
2093             matches[i] = m;
2094         }
2095         return matches;
2096     }
2097 
2098     /**
2099      * A LRA cache of record deserialization constructors.
2100      */
2101     @SuppressWarnings("serial")
2102     private static final class DeserializationConstructorsCache
2103         extends ConcurrentHashMap<DeserializationConstructorsCache.Key, MethodHandle>  {
2104 
2105         // keep max. 10 cached entries - when the 11th element is inserted the oldest
2106         // is removed and 10 remains - 11 is the biggest map size where internal
2107         // table of 16 elements is sufficient (inserting 12th element would resize it to 32)
2108         private static final int MAX_SIZE = 10;
2109         private Key.Impl first, last; // first and last in FIFO queue
2110 

2192                     this.fieldTypes = new Class<?>[fields.length];
2193                     for (int i = 0; i < fields.length; i++) {
2194                         fieldNames[i] = fields[i].getName();
2195                         fieldTypes[i] = fields[i].getType();
2196                     }
2197                 }
2198 
2199                 @Override
2200                 int length() { return fieldNames.length; }
2201 
2202                 @Override
2203                 String fieldName(int i) { return fieldNames[i]; }
2204 
2205                 @Override
2206                 Class<?> fieldType(int i) { return fieldTypes[i]; }
2207             }
2208         }
2209     }
2210 
2211     /** Record specific support for retrieving and binding stream field values. */
2212     static final class RecordSupport {
2213         /**
2214          * Returns canonical record constructor adapted to take two arguments:
2215          * {@code (byte[] primValues, Object[] objValues)}
2216          * and return
2217          * {@code Object}
2218          */
2219         static MethodHandle deserializationCtr(ObjectStreamClass desc) {
2220             // check the cached value 1st
2221             MethodHandle mh = desc.deserializationCtr;
2222             if (mh != null) return mh;
2223             mh = desc.deserializationCtrs.get(desc.getFields(false));
2224             if (mh != null) return desc.deserializationCtr = mh;
2225 
2226             // retrieve record components
2227             RecordComponent[] recordComponents = desc.forClass().getRecordComponents();
2228 
2229             // retrieve the canonical constructor
2230             // (T1, T2, ..., Tn):TR
2231             mh = desc.getRecordConstructor();
2232 

2241             for (int i = recordComponents.length-1; i >= 0; i--) {
2242                 String name = recordComponents[i].getName();
2243                 Class<?> type = recordComponents[i].getType();
2244                 // obtain stream field extractor that extracts argument at
2245                 // position i (Ti+1) from primValues and objValues arrays
2246                 // (byte[], Object[]):Ti+1
2247                 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2248                 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2249                 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2250                 mh = MethodHandles.foldArguments(mh, i, combiner);
2251             }
2252             // what we are left with is a MethodHandle taking just the primValues
2253             // and objValues arrays and returning the constructed record instance
2254             // (byte[], Object[]):Object
2255 
2256             // store it into cache and return the 1st value stored
2257             return desc.deserializationCtr =
2258                 desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
2259         }
2260 



























































2261         /** Returns the number of primitive fields for the given descriptor. */
2262         private static int numberPrimValues(ObjectStreamClass desc) {
2263             ObjectStreamField[] fields = desc.getFields();
2264             int primValueCount = 0;
2265             for (int i = 0; i < fields.length; i++) {
2266                 if (fields[i].isPrimitive())
2267                     primValueCount++;
2268                 else
2269                     break;  // can be no more
2270             }
2271             return primValueCount;
2272         }
2273 
2274         /**
2275          * Returns extractor MethodHandle taking the primValues and objValues arrays
2276          * and extracting the argument of canonical constructor with given name and type
2277          * or producing  default value for the given type if the field is absent.
2278          */
2279         private static MethodHandle streamFieldExtractor(String pName,
2280                                                          Class<?> pType,

  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.Member;
  37 import java.lang.reflect.Method;
  38 import java.lang.reflect.Modifier;
  39 import java.lang.reflect.Proxy;
  40 import java.security.MessageDigest;
  41 import java.security.NoSuchAlgorithmException;
  42 import java.util.ArrayList;
  43 import java.util.Arrays;
  44 import java.util.Collections;
  45 import java.util.Comparator;
  46 import java.util.HashSet;
  47 import java.util.List;
  48 import java.util.Map;
  49 import java.util.Set;
  50 import java.util.concurrent.ConcurrentHashMap;
  51 import java.util.stream.Stream;
  52 
  53 import jdk.internal.MigratedValueClass;
  54 import jdk.internal.event.SerializationMisdeclarationEvent;
  55 import jdk.internal.misc.Unsafe;
  56 import jdk.internal.reflect.ReflectionFactory;
  57 import jdk.internal.util.ByteArray;
  58 import jdk.internal.value.DeserializeConstructor;
  59 
  60 import static java.io.ObjectInputStream.TRACE;
  61 
  62 /**
  63  * Serialization's descriptor for classes.  It contains the name and
  64  * serialVersionUID of the class.  The ObjectStreamClass for a specific class
  65  * loaded in this Java VM can be found/created using the lookup method.
  66  *
  67  * <p>The algorithm to compute the SerialVersionUID is described in
  68  * <a href="{@docRoot}/../specs/serialization/class.html#stream-unique-identifiers">
  69  *    <cite>Java Object Serialization Specification</cite>, Section 4.6, "Stream Unique Identifiers"</a>.
  70  *
  71  * @spec serialization/index.html Java Object Serialization Specification
  72  * @author      Mike Warres
  73  * @author      Roger Riggs
  74  * @see ObjectStreamField
  75  * @see <a href="{@docRoot}/../specs/serialization/class.html">
  76  *      <cite>Java Object Serialization Specification,</cite> Section 4, "Class Descriptors"</a>
  77  * @since   1.1
  78  */
  79 public final class ObjectStreamClass implements Serializable {
  80 
  81     /** serialPersistentFields value indicating no serializable fields */
  82     public static final ObjectStreamField[] NO_FIELDS =
  83         new ObjectStreamField[0];
  84 
  85     @java.io.Serial
  86     private static final long serialVersionUID = -6120832682080437368L;
  87     /**
  88      * {@code ObjectStreamClass} has no fields for default serialization.
  89      */
  90     @java.io.Serial
  91     private static final ObjectStreamField[] serialPersistentFields =
  92         NO_FIELDS;
  93 
  94     /**
  95      * The mode of deserialization for a class depending on its type and interfaces.
  96      * The markers used are {@linkplain java.io.Serializable}, {@linkplain java.io.Externalizable},
  97      * Class.isRecord(), Class.isValue(), constructors, and
  98      * the presence of methods `readObject`, `writeObject`, `readObjectNoData`, `writeObject`.
  99      * ObjectInputStream dispatches on the mode to construct objects from the stream.
 100      */
 101     enum DeserializationMode {
 102         /**
 103          * Construct an object from the stream for a class that has only default read object behaviors.
 104          * All classes and superclasses use defaultReadObject; no custom readObject or readObjectNoData.
 105          * The new instance is entered in the handle table if it is unshared,
 106          * allowing it to escape before it is initialized.
 107          * For each object, all the fields are read before any are assigned.
 108          * The `readObject` and `readObjectNoData` methods are not present and are not called.
 109          */
 110         READ_OBJECT_DEFAULT,
 111         /**
 112          * Creates a new object and invokes its readExternal method to read its contents.
 113          * If the class is instantiable, read externalizable data by invoking readExternal()
 114          * method of obj; otherwise, attempts to skip over externalizable data.
 115          * Expects that passHandle is set to obj's handle before this method is
 116          * called.  The new object is entered in the handle table immediately,
 117          * allowing it to leak before it is completely read.
 118          */
 119         READ_EXTERNALIZABLE,
 120         /**
 121          * Read all the record fields and invoke its canonical constructor.
 122          * Construct the record using its canonical constructor.
 123          * The new record is entered in the handle table only after the constructor returns.
 124          */
 125         READ_RECORD,
 126         /**
 127          * Fully custom read from the stream to create an instance.
 128          * If the class is not instantiatable or is tagged with ClassNotFoundException
 129          * the data in the stream for the class is read and discarded. {@link #READ_NO_LOCAL_CLASS}
 130          * The instance is created and set in the handle table, allowing it to leak before it is initialized.
 131          * For each serializable class in the stream, from superclass to subclass the
 132          * stream values are read by the `readObject` method, if present, or defaultReadObject.
 133          * Custom inline data is discarded if not consumed by the class `readObject` method.
 134          */
 135         READ_OBJECT_CUSTOM,
 136         /**
 137          * Construct an object by reading the values of all fields and
 138          * invoking a constructor or static factory method.
 139          * The constructor or static factory method is selected by matching its parameters with the
 140          * sequence of field types of the serializable fields of the local class and superclasses.
 141          * Invoke the constructor with all the values from the stream, inserting
 142          * defaults and dropping extra values as necessary.
 143          * This is very similar to the reading of records, except for the identification of
 144          * the constructor or static factory.
 145          */
 146         READ_OBJECT_VALUE,
 147         /**
 148          * Read and discard an entire object, leaving a null reference in the HandleTable.
 149          * The descriptor of the class in the stream is used to read the fields from the stream.
 150          * There is no instance in which to store the field values.
 151          * Custom data following the fields of any slot is read and discarded.
 152          * References to nested objects are read and retained in the
 153          * handle table using the regular mechanism.
 154          * Handles later in the stream may refer to the nested objects.
 155          */
 156         READ_NO_LOCAL_CLASS,
 157     }
 158 
 159     private static class Caches {
 160         /** cache mapping local classes -> descriptors */
 161         static final ClassCache<ObjectStreamClass> localDescs =
 162             new ClassCache<>() {
 163                 @Override
 164                 protected ObjectStreamClass computeValue(Class<?> type) {
 165                     return new ObjectStreamClass(type);
 166                 }
 167             };
 168 
 169         /** cache mapping field group/local desc pairs -> field reflectors */
 170         static final ClassCache<Map<FieldReflectorKey, FieldReflector>> reflectors =
 171             new ClassCache<>() {
 172                 @Override
 173                 protected Map<FieldReflectorKey, FieldReflector> computeValue(Class<?> type) {
 174                     return new ConcurrentHashMap<>();
 175                 }
 176             };
 177     }
 178 
 179     /** class associated with this descriptor (if any) */
 180     private Class<?> cl;
 181     /** name of class represented by this descriptor */
 182     private String name;
 183     /** serialVersionUID of represented class (null if not computed yet) */
 184     private volatile Long suid;
 185 
 186     /** true if represents dynamic proxy class */
 187     private boolean isProxy;
 188     /** true if represents enum type */
 189     private boolean isEnum;
 190     /** true if represents record type */
 191     private boolean isRecord;
 192     /** true if represents a value class */
 193     private boolean isValue;
 194     /** The DeserializationMode for this class. */
 195     private DeserializationMode factoryMode;
 196     /** true if represented class implements Serializable */
 197     private boolean serializable;
 198     /** true if represented class implements Externalizable */
 199     private boolean externalizable;
 200     /** true if desc has data written by class-defined writeObject method */
 201     private boolean hasWriteObjectData;
 202     /**
 203      * true if desc has externalizable data written in block data format; this
 204      * must be true by default to accommodate ObjectInputStream subclasses which
 205      * override readClassDescriptor() to return class descriptors obtained from
 206      * ObjectStreamClass.lookup() (see 4461737)
 207      */
 208     private boolean hasBlockExternalData = true;
 209 
 210     /**
 211      * Contains information about InvalidClassException instances to be thrown
 212      * when attempting operations on an invalid class. Note that instances of
 213      * this class are immutable and are potentially shared among
 214      * ObjectStreamClass instances.
 215      */

 233     }
 234 
 235     /** exception (if any) thrown while attempting to resolve class */
 236     private ClassNotFoundException resolveEx;
 237     /** exception (if any) to throw if non-enum deserialization attempted */
 238     private ExceptionInfo deserializeEx;
 239     /** exception (if any) to throw if non-enum serialization attempted */
 240     private ExceptionInfo serializeEx;
 241     /** exception (if any) to throw if default serialization attempted */
 242     private ExceptionInfo defaultSerializeEx;
 243 
 244     /** serializable fields */
 245     private ObjectStreamField[] fields;
 246     /** aggregate marshalled size of primitive fields */
 247     private int primDataSize;
 248     /** number of non-primitive fields */
 249     private int numObjFields;
 250     /** reflector for setting/getting serializable field values */
 251     private FieldReflector fieldRefl;
 252     /** data layout of serialized objects described by this class desc */
 253     private volatile List<ClassDataSlot> dataLayout;
 254 
 255     /** serialization-appropriate constructor, or null if none */
 256     private Constructor<?> cons;
 257     /** record canonical constructor (shared among OSCs for same class), or null */
 258     private MethodHandle canonicalCtr;
 259     /** cache of record deserialization constructors per unique set of stream fields
 260      * (shared among OSCs for same class), or null */
 261     private DeserializationConstructorsCache deserializationCtrs;
 262     /** session-cache of record deserialization constructor
 263      * (in de-serialized OSC only), or null */
 264     private MethodHandle deserializationCtr;
 265 
 266     /** class-defined writeObject method, or null if none */
 267     private Method writeObjectMethod;
 268     /** class-defined readObject method, or null if none */
 269     private Method readObjectMethod;
 270     /** class-defined readObjectNoData method, or null if none */
 271     private Method readObjectNoDataMethod;
 272     /** class-defined writeReplace method, or null if none */
 273     private Method writeReplaceMethod;

 397      * @param   cl class to look up
 398      * @param   all if true, return descriptors for all classes; if false, only
 399      *          return descriptors for serializable classes
 400      */
 401     static ObjectStreamClass lookup(Class<?> cl, boolean all) {
 402         if (!(all || Serializable.class.isAssignableFrom(cl))) {
 403             return null;
 404         }
 405         return Caches.localDescs.get(cl);
 406     }
 407 
 408     /**
 409      * Creates local class descriptor representing given class.
 410      */
 411     private ObjectStreamClass(final Class<?> cl) {
 412         this.cl = cl;
 413         name = cl.getName();
 414         isProxy = Proxy.isProxyClass(cl);
 415         isEnum = Enum.class.isAssignableFrom(cl);
 416         isRecord = cl.isRecord();
 417         isValue = cl.isValue();
 418         serializable = Serializable.class.isAssignableFrom(cl);
 419         externalizable = Externalizable.class.isAssignableFrom(cl);
 420 
 421         Class<?> superCl = cl.getSuperclass();
 422         superDesc = (superCl != null) ? lookup(superCl, false) : null;
 423         localDesc = this;
 424 
 425         if (serializable) {
 426             if (isEnum) {
 427                 suid = 0L;
 428                 fields = NO_FIELDS;
 429             } else if (cl.isArray()) {
 430                 fields = NO_FIELDS;
 431             } else {
 432                 suid = getDeclaredSUID(cl);
 433                 try {
 434                     fields = getSerialFields(cl);
 435                     computeFieldOffsets();
 436                 } catch (InvalidClassException e) {
 437                     serializeEx = deserializeEx =
 438                             new ExceptionInfo(e.classname, e.getMessage());
 439                     fields = NO_FIELDS;
 440                 }
 441 
 442                 if (isRecord) {
 443                     factoryMode = DeserializationMode.READ_RECORD;
 444                     canonicalCtr = canonicalRecordCtr(cl);
 445                     deserializationCtrs = new DeserializationConstructorsCache();
 446                 } else if (externalizable) {
 447                     factoryMode = DeserializationMode.READ_EXTERNALIZABLE;
 448                     if (cl.isIdentity()) {
 449                         cons = getExternalizableConstructor(cl);
 450                     } else {
 451                         serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
 452                                 "Externalizable not valid for value class");
 453                     }
 454                 } else if (cl.isValue()) {
 455                     factoryMode = DeserializationMode.READ_OBJECT_VALUE;
 456                     if (!cl.isAnnotationPresent(MigratedValueClass.class)) {
 457                         serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
 458                                                                         "Value class serialization is only supported with `writeReplace`");
 459                     } else if (Modifier.isAbstract(cl.getModifiers())) {
 460                         serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
 461                                                                         "value class is abstract");
 462                     } else {
 463                         // Value classes should have constructor(s) annotated with {@link DeserializeConstructor}
 464                         canonicalCtr = getDeserializingValueCons(cl, fields);
 465                         deserializationCtrs = new DeserializationConstructorsCache();                            factoryMode = DeserializationMode.READ_OBJECT_VALUE;
 466                         if (canonicalCtr == null) {
 467                             serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
 468                                                                             "no constructor or factory found for migrated value class");
 469                         }
 470                     }
 471                 } else {
 472                     cons = getSerializableConstructor(cl);
 473                     writeObjectMethod = getPrivateMethod(cl, "writeObject",
 474                             new Class<?>[]{ObjectOutputStream.class},
 475                             Void.TYPE);
 476                     readObjectMethod = getPrivateMethod(cl, "readObject",
 477                             new Class<?>[]{ObjectInputStream.class},
 478                             Void.TYPE);
 479                     readObjectNoDataMethod = getPrivateMethod(
 480                             cl, "readObjectNoData", null, Void.TYPE);
 481                     hasWriteObjectData = (writeObjectMethod != null);
 482                     factoryMode = ((superDesc == null || superDesc.factoryMode() == DeserializationMode.READ_OBJECT_DEFAULT)
 483                                    && readObjectMethod == null && readObjectNoDataMethod == null)
 484                         ? DeserializationMode.READ_OBJECT_DEFAULT
 485                         : DeserializationMode.READ_OBJECT_CUSTOM;
 486                 }
 487                 writeReplaceMethod = getInheritableMethod(
 488                         cl, "writeReplace", null, Object.class);
 489                 readResolveMethod = getInheritableMethod(
 490                         cl, "readResolve", null, Object.class);
 491             }
 492         } else {
 493             suid = 0L;
 494             fields = NO_FIELDS;
 495         }
 496 
 497         try {
 498             fieldRefl = getReflector(fields, this);
 499         } catch (InvalidClassException ex) {
 500             // field mismatches impossible when matching local fields vs. self
 501             throw new InternalError(ex);
 502         }
 503 
 504         if (deserializeEx == null) {
 505             if (isEnum) {
 506                 deserializeEx = new ExceptionInfo(name, "enum type");
 507             } else if (cons == null && !(isRecord | isValue)) {
 508                 deserializeEx = new ExceptionInfo(name, "no valid constructor");
 509             }
 510         }
 511         if (isRecord && canonicalCtr == null) {
 512             deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
 513         } else {
 514             for (int i = 0; i < fields.length; i++) {
 515                 if (fields[i].getField() == null) {
 516                     defaultSerializeEx = new ExceptionInfo(
 517                         name, "unmatched serializable field(s) declared");
 518                 }
 519             }
 520         }
 521         initialized = true;
 522 
 523         if (SerializationMisdeclarationEvent.enabled() && serializable) {
 524             SerializationMisdeclarationChecker.checkMisdeclarations(cl);
 525         }
 526     }
 527 

 546             if (!osc.isProxy) {
 547                 throw new InvalidClassException(
 548                     "cannot bind proxy descriptor to a non-proxy class");
 549             }
 550         }
 551         this.cl = cl;
 552         this.resolveEx = resolveEx;
 553         this.superDesc = superDesc;
 554         isProxy = true;
 555         serializable = true;
 556         suid = 0L;
 557         fields = NO_FIELDS;
 558         if (osc != null) {
 559             localDesc = osc;
 560             name = localDesc.name;
 561             externalizable = localDesc.externalizable;
 562             writeReplaceMethod = localDesc.writeReplaceMethod;
 563             readResolveMethod = localDesc.readResolveMethod;
 564             deserializeEx = localDesc.deserializeEx;
 565             cons = localDesc.cons;
 566             factoryMode = localDesc.factoryMode;
 567         } else {
 568             factoryMode = DeserializationMode.READ_OBJECT_DEFAULT;
 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             isValue = localDesc.isValue;
 648             // canonical record constructor is shared
 649             canonicalCtr = localDesc.canonicalCtr;
 650             // cache of deserialization constructors is shared
 651             deserializationCtrs = localDesc.deserializationCtrs;
 652             writeObjectMethod = localDesc.writeObjectMethod;
 653             readObjectMethod = localDesc.readObjectMethod;
 654             readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
 655             writeReplaceMethod = localDesc.writeReplaceMethod;
 656             readResolveMethod = localDesc.readResolveMethod;
 657             if (deserializeEx == null) {
 658                 deserializeEx = localDesc.deserializeEx;
 659             }
 660             assert cl.isRecord() ? localDesc.cons == null : true;
 661             cons = localDesc.cons;
 662             factoryMode = localDesc.factoryMode;
 663         } else {
 664             // No local class, read data using only the schema from the stream
 665             factoryMode = (externalizable)
 666                     ? DeserializationMode.READ_EXTERNALIZABLE
 667                     : DeserializationMode.READ_NO_LOCAL_CLASS;
 668         }
 669 
 670         fieldRefl = getReflector(fields, localDesc);
 671         // reassign to matched fields so as to reflect local unshared settings
 672         fields = fieldRefl.getFields();
 673 
 674         initialized = true;
 675     }
 676 
 677     /**
 678      * Reads non-proxy class descriptor information from given input stream.
 679      * The resulting class descriptor is not fully functional; it can only be
 680      * used as input to the ObjectInputStream.resolveClass() and
 681      * ObjectStreamClass.initNonProxy() methods.
 682      */
 683     void readNonProxy(ObjectInputStream in)
 684         throws IOException, ClassNotFoundException
 685     {
 686         name = in.readUTF();
 687         suid = in.readLong();

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

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

1217     /**
1218      * Class representing the portion of an object's serialized form allotted
1219      * to data described by a given class descriptor.  If "hasData" is false,
1220      * the object's serialized form does not contain data associated with the
1221      * class descriptor.
1222      */
1223     static class ClassDataSlot {
1224 
1225         /** class descriptor "occupying" this slot */
1226         final ObjectStreamClass desc;
1227         /** true if serialized form includes data for this slot's descriptor */
1228         final boolean hasData;
1229 
1230         ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
1231             this.desc = desc;
1232             this.hasData = hasData;
1233         }
1234     }
1235 
1236     /**
1237      * Returns a List of ClassDataSlot instances representing the data layout
1238      * (including superclass data) for serialized objects described by this
1239      * class descriptor.  ClassDataSlots are ordered by inheritance with those
1240      * containing "higher" superclasses appearing first.  The final
1241      * ClassDataSlot contains a reference to this descriptor.
1242      */
1243     List<ClassDataSlot> getClassDataLayout() throws InvalidClassException {
1244         // REMIND: synchronize instead of relying on volatile?
1245         List<ClassDataSlot> layout = dataLayout;
1246         if (layout != null)
1247             return layout;


1248 



1249         ArrayList<ClassDataSlot> slots = new ArrayList<>();
1250         Class<?> start = cl, end = cl;
1251 
1252         // locate closest non-serializable superclass
1253         while (end != null && Serializable.class.isAssignableFrom(end)) {
1254             end = end.getSuperclass();
1255         }
1256 
1257         HashSet<String> oscNames = new HashSet<>(3);
1258 
1259         for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
1260             if (oscNames.contains(d.name)) {
1261                 throw new InvalidClassException("Circular reference.");
1262             } else {
1263                 oscNames.add(d.name);
1264             }
1265 
1266             // search up inheritance hierarchy for class with matching name
1267             String searchName = (d.cl != null) ? d.cl.getName() : d.name;
1268             Class<?> match = null;

1277             if (match != null) {
1278                 for (Class<?> c = start; c != match; c = c.getSuperclass()) {
1279                     slots.add(new ClassDataSlot(
1280                         ObjectStreamClass.lookup(c, true), false));
1281                 }
1282                 start = match.getSuperclass();
1283             }
1284 
1285             // record descriptor/class pairing
1286             slots.add(new ClassDataSlot(d.getVariantFor(match), true));
1287         }
1288 
1289         // add "no data" slot for any leftover unmatched classes
1290         for (Class<?> c = start; c != end; c = c.getSuperclass()) {
1291             slots.add(new ClassDataSlot(
1292                 ObjectStreamClass.lookup(c, true), false));
1293         }
1294 
1295         // order slots from superclass -> subclass
1296         Collections.reverse(slots);
1297         dataLayout = slots;
1298         return slots;
1299     }
1300 
1301     /**
1302      * Returns aggregate size (in bytes) of marshalled primitive field values
1303      * for represented class.
1304      */
1305     int getPrimDataSize() {
1306         return primDataSize;
1307     }
1308 
1309     /**
1310      * Returns number of non-primitive serializable fields of represented
1311      * class.
1312      */
1313     int getNumObjFields() {
1314         return numObjFields;
1315     }
1316 
1317     /**
1318      * Fetches the serializable primitive field values of object obj and

1406     /**
1407      * If given class is the same as the class associated with this class
1408      * descriptor, returns reference to this class descriptor.  Otherwise,
1409      * returns variant of this class descriptor bound to given class.
1410      */
1411     private ObjectStreamClass getVariantFor(Class<?> cl)
1412         throws InvalidClassException
1413     {
1414         if (this.cl == cl) {
1415             return this;
1416         }
1417         ObjectStreamClass desc = new ObjectStreamClass();
1418         if (isProxy) {
1419             desc.initProxy(cl, null, superDesc);
1420         } else {
1421             desc.initNonProxy(this, cl, null, superDesc);
1422         }
1423         return desc;
1424     }
1425 
1426     /**
1427      * Return a method handle for the static method or constructor(s) that matches the
1428      * serializable fields and annotated with {@link DeserializeConstructor}.
1429      * The descriptor for the class is still being initialized, so is passed the fields needed.
1430      * @param clazz The class to query
1431      * @param fields the serializable fields of the class
1432      * @return a MethodHandle, null if none found
1433      */
1434     @SuppressWarnings("unchecked")
1435     private static MethodHandle getDeserializingValueCons(Class<?> clazz,
1436                                                           ObjectStreamField[] fields) {
1437         // Search for annotated static factory in methods or constructors
1438         MethodHandles.Lookup lookup = MethodHandles.lookup();
1439         MethodHandle mh = Stream.concat(
1440                 Arrays.stream(clazz.getDeclaredMethods()).filter(m -> Modifier.isStatic(m.getModifiers())),
1441                 Arrays.stream(clazz.getDeclaredConstructors()))
1442                 .filter(m -> m.isAnnotationPresent(DeserializeConstructor.class))
1443                 .map(m -> {
1444                     try {
1445                         m.setAccessible(true);
1446                         return (m instanceof Constructor<?> cons)
1447                                 ? lookup.unreflectConstructor(cons)
1448                                 : lookup.unreflect(((Method) m));
1449                     } catch (IllegalAccessException iae) {
1450                         throw new InternalError(iae);   // should not occur after setAccessible
1451                     }})
1452                 .filter(m -> matchFactoryParamTypes(clazz, m, fields))
1453                 .findFirst().orElse(null);
1454         TRACE("DeserializeConstructor for %s, mh: %s", clazz,  mh);
1455         return mh;
1456     }
1457 
1458     /**
1459      * Check that the parameters of the factory method match the fields of this class.
1460      *
1461      * @param mh a MethodHandle for a constructor or factory
1462      * @return true if all fields match the parameters, false if not
1463      */
1464     private static boolean matchFactoryParamTypes(Class<?> clazz,
1465                                                        MethodHandle mh,
1466                                                        ObjectStreamField[] fields) {
1467         TRACE("  matchFactoryParams checking class: %s, mh: %s", clazz, mh);
1468         var params = mh.type().parameterList();
1469         if (params.size() != fields.length) {
1470             TRACE("   matchFactoryParams %s, arg count mismatch %d params != %d fields",
1471                     clazz, params.size(), fields.length);
1472             return false;    // Mismatch in count of fields and parameters
1473         }
1474         for (ObjectStreamField field : fields) {
1475             int argIndex = field.getArgIndex();
1476             final Class<?> paramtype = params.get(argIndex);
1477             if (!field.getType().equals(paramtype)) {
1478                 TRACE("   matchFactoryParams %s: argIndex: %d type mismatch field: %s != param: %s",
1479                         clazz, argIndex, field.getType(), paramtype);
1480                 return false;
1481             }
1482         }
1483         return true;
1484     }
1485 
1486     /**
1487      * Returns public no-arg constructor of given class, or null if none found.
1488      * Access checks are disabled on the returned constructor (if any), since
1489      * the defining class may still be non-public.
1490      */
1491     private static Constructor<?> getExternalizableConstructor(Class<?> cl) {
1492         try {
1493             Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
1494             cons.setAccessible(true);
1495             return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
1496                 cons : null;
1497         } catch (NoSuchMethodException | InaccessibleObjectException ex) {
1498             return null;
1499         }
1500     }
1501 
1502     /**
1503      * Returns subclass-accessible no-arg constructor of first non-serializable
1504      * superclass, or null if none found.  Access checks are disabled on the
1505      * returned constructor (if any).
1506      */
1507     private static Constructor<?> getSerializableConstructor(Class<?> cl) {
1508         return ReflectionFactory.getReflectionFactory().newConstructorForSerialization(cl);
1509     }
1510 
1511     /**
1512      * Returns the canonical constructor for the given record class, or null if
1513      * the not found ( which should never happen for correctly generated record
1514      * classes ).
1515      */
1516     private static MethodHandle canonicalRecordCtr(Class<?> cls) {
1517         assert cls.isRecord() : "Expected record, got: " + cls;

1708 
1709         ObjectStreamField[] boundFields =
1710             new ObjectStreamField[serialPersistentFields.length];
1711         Set<String> fieldNames = HashSet.newHashSet(serialPersistentFields.length);
1712 
1713         for (int i = 0; i < serialPersistentFields.length; i++) {
1714             ObjectStreamField spf = serialPersistentFields[i];
1715 
1716             String fname = spf.getName();
1717             if (fieldNames.contains(fname)) {
1718                 throw new InvalidClassException(
1719                     "multiple serializable fields named " + fname);
1720             }
1721             fieldNames.add(fname);
1722 
1723             try {
1724                 Field f = cl.getDeclaredField(fname);
1725                 if ((f.getType() == spf.getType()) &&
1726                     ((f.getModifiers() & Modifier.STATIC) == 0))
1727                 {
1728                     boundFields[i] = new ObjectStreamField(f, spf.isUnshared(), true, i);

1729                 }
1730             } catch (NoSuchFieldException ex) {
1731             }
1732             if (boundFields[i] == null) {
1733                 boundFields[i] = new ObjectStreamField(fname, spf.getType(), spf.isUnshared(), i);

1734             }
1735         }
1736         return boundFields;
1737     }
1738 
1739     /**
1740      * Returns array of ObjectStreamFields corresponding to all non-static
1741      * non-transient fields declared by given class.  Each ObjectStreamField
1742      * contains a Field object for the field it represents.  If no default
1743      * serializable fields exist, NO_FIELDS is returned.
1744      */
1745     private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
1746         Field[] clFields = cl.getDeclaredFields();
1747         ArrayList<ObjectStreamField> list = new ArrayList<>();
1748         int mask = Modifier.STATIC | Modifier.TRANSIENT;
1749 
1750         for (int i = 0, argIndex = 0; i < clFields.length; i++) {
1751             if ((clFields[i].getModifiers() & mask) == 0) {
1752                 list.add(new ObjectStreamField(clFields[i], false, true, argIndex++));
1753             }
1754         }
1755         int size = list.size();
1756         return (size == 0) ? NO_FIELDS :
1757             list.toArray(new ObjectStreamField[size]);
1758     }
1759 
1760     /**
1761      * Returns explicit serial version UID value declared by given class, or
1762      * null if none.
1763      */
1764     private static Long getDeclaredSUID(Class<?> cl) {
1765         try {
1766             Field f = cl.getDeclaredField("serialVersionUID");
1767             int mask = Modifier.STATIC | Modifier.FINAL;
1768             if ((f.getModifiers() & mask) == mask) {
1769                 f.setAccessible(true);
1770                 return f.getLong(null);
1771             }
1772         } catch (Exception ex) {

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

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

2257          * a non-local class descriptor.  To preserve this (questionable)
2258          * behavior, the ObjectStreamField instances returned by matchFields
2259          * cannot report non-primitive types other than Object.class; hence
2260          * localFields cannot be returned directly.
2261          */
2262 
2263         ObjectStreamField[] matches = new ObjectStreamField[fields.length];
2264         for (int i = 0; i < fields.length; i++) {
2265             ObjectStreamField f = fields[i], m = null;
2266             for (int j = 0; j < localFields.length; j++) {
2267                 ObjectStreamField lf = localFields[j];
2268                 if (f.getName().equals(lf.getName())) {
2269                     if ((f.isPrimitive() || lf.isPrimitive()) &&
2270                         f.getTypeCode() != lf.getTypeCode())
2271                     {
2272                         throw new InvalidClassException(localDesc.name,
2273                             "incompatible types for field " + f.getName());
2274                     }
2275                     if (lf.getField() != null) {
2276                         m = new ObjectStreamField(
2277                             lf.getField(), lf.isUnshared(), true, lf.getArgIndex()); // Don't hide type
2278                     } else {
2279                         m = new ObjectStreamField(
2280                             lf.getName(), lf.getSignature(), lf.isUnshared(), lf.getArgIndex());
2281                     }
2282                 }
2283             }
2284             if (m == null) {
2285                 m = new ObjectStreamField(
2286                     f.getName(), f.getSignature(), false, -1);
2287             }
2288             m.setOffset(f.getOffset());
2289             matches[i] = m;
2290         }
2291         return matches;
2292     }
2293 
2294     /**
2295      * A LRA cache of record deserialization constructors.
2296      */
2297     @SuppressWarnings("serial")
2298     private static final class DeserializationConstructorsCache
2299         extends ConcurrentHashMap<DeserializationConstructorsCache.Key, MethodHandle>  {
2300 
2301         // keep max. 10 cached entries - when the 11th element is inserted the oldest
2302         // is removed and 10 remains - 11 is the biggest map size where internal
2303         // table of 16 elements is sufficient (inserting 12th element would resize it to 32)
2304         private static final int MAX_SIZE = 10;
2305         private Key.Impl first, last; // first and last in FIFO queue
2306 

2388                     this.fieldTypes = new Class<?>[fields.length];
2389                     for (int i = 0; i < fields.length; i++) {
2390                         fieldNames[i] = fields[i].getName();
2391                         fieldTypes[i] = fields[i].getType();
2392                     }
2393                 }
2394 
2395                 @Override
2396                 int length() { return fieldNames.length; }
2397 
2398                 @Override
2399                 String fieldName(int i) { return fieldNames[i]; }
2400 
2401                 @Override
2402                 Class<?> fieldType(int i) { return fieldTypes[i]; }
2403             }
2404         }
2405     }
2406 
2407     /** Record specific support for retrieving and binding stream field values. */
2408     static final class ConstructorSupport {
2409         /**
2410          * Returns canonical record constructor adapted to take two arguments:
2411          * {@code (byte[] primValues, Object[] objValues)}
2412          * and return
2413          * {@code Object}
2414          */
2415         static MethodHandle deserializationCtr(ObjectStreamClass desc) {
2416             // check the cached value 1st
2417             MethodHandle mh = desc.deserializationCtr;
2418             if (mh != null) return mh;
2419             mh = desc.deserializationCtrs.get(desc.getFields(false));
2420             if (mh != null) return desc.deserializationCtr = mh;
2421 
2422             // retrieve record components
2423             RecordComponent[] recordComponents = desc.forClass().getRecordComponents();
2424 
2425             // retrieve the canonical constructor
2426             // (T1, T2, ..., Tn):TR
2427             mh = desc.getRecordConstructor();
2428 

2437             for (int i = recordComponents.length-1; i >= 0; i--) {
2438                 String name = recordComponents[i].getName();
2439                 Class<?> type = recordComponents[i].getType();
2440                 // obtain stream field extractor that extracts argument at
2441                 // position i (Ti+1) from primValues and objValues arrays
2442                 // (byte[], Object[]):Ti+1
2443                 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2444                 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2445                 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2446                 mh = MethodHandles.foldArguments(mh, i, combiner);
2447             }
2448             // what we are left with is a MethodHandle taking just the primValues
2449             // and objValues arrays and returning the constructed record instance
2450             // (byte[], Object[]):Object
2451 
2452             // store it into cache and return the 1st value stored
2453             return desc.deserializationCtr =
2454                 desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
2455         }
2456 
2457         /**
2458          * Returns value object constructor adapted to take two arguments:
2459          * {@code (byte[] primValues, Object[] objValues)} and return {@code Object}
2460          */
2461         static MethodHandle deserializationValueCons(ObjectStreamClass desc) {
2462             // check the cached value 1st
2463             MethodHandle mh = desc.deserializationCtr;
2464             if (mh != null) return mh;
2465             mh = desc.deserializationCtrs.get(desc.getFields(false));
2466             if (mh != null) return desc.deserializationCtr = mh;
2467 
2468             // retrieve the selected constructor
2469             // (T1, T2, ..., Tn):TR
2470             ObjectStreamClass localDesc = desc.localDesc;
2471             mh = localDesc.canonicalCtr;
2472             MethodType mt = mh.type();
2473 
2474             // change return type to Object
2475             // (T1, T2, ..., Tn):TR -> (T1, T2, ..., Tn):Object
2476             mh = mh.asType(mh.type().changeReturnType(Object.class));
2477 
2478             // drop last 2 arguments representing primValues and objValues arrays
2479             // (T1, T2, ..., Tn):Object -> (T1, T2, ..., Tn, byte[], Object[]):Object
2480             mh = MethodHandles.dropArguments(mh, mh.type().parameterCount(), byte[].class, Object[].class);
2481 
2482             Class<?>[] params = mt.parameterArray();
2483             for (int i = params.length-1; i >= 0; i--) {
2484                 // Get the name from the local descriptor matching the argIndex
2485                 var field = getFieldForArgIndex(localDesc, i);
2486                 String name = (field == null) ? "" : field.getName();   // empty string to supply default
2487                 Class<?> type = params[i];
2488                 // obtain stream field extractor that extracts argument at
2489                 // position i (Ti+1) from primValues and objValues arrays
2490                 // (byte[], Object[]):Ti+1
2491                 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2492                 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2493                 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2494                 mh = MethodHandles.foldArguments(mh, i, combiner);
2495             }
2496             // what we are left with is a MethodHandle taking just the primValues
2497             // and objValues arrays and returning the constructed instance
2498             // (byte[], Object[]):Object
2499 
2500             // store it into cache and return the 1st value stored
2501             return desc.deserializationCtr =
2502                 desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
2503         }
2504 
2505         // Find the ObjectStreamField for the argument index, otherwise null
2506         private static ObjectStreamField getFieldForArgIndex(ObjectStreamClass desc, int argIndex) {
2507             for (var field : desc.fields) {
2508                 if (field.getArgIndex() == argIndex)
2509                     return field;
2510             }
2511             TRACE("field for ArgIndex is null: %s, index: %d, fields: %s",
2512                     desc, argIndex, Arrays.toString(desc.fields));
2513             return null;
2514         }
2515 
2516         /** Returns the number of primitive fields for the given descriptor. */
2517         private static int numberPrimValues(ObjectStreamClass desc) {
2518             ObjectStreamField[] fields = desc.getFields();
2519             int primValueCount = 0;
2520             for (int i = 0; i < fields.length; i++) {
2521                 if (fields[i].isPrimitive())
2522                     primValueCount++;
2523                 else
2524                     break;  // can be no more
2525             }
2526             return primValueCount;
2527         }
2528 
2529         /**
2530          * Returns extractor MethodHandle taking the primValues and objValues arrays
2531          * and extracting the argument of canonical constructor with given name and type
2532          * or producing  default value for the given type if the field is absent.
2533          */
2534         private static MethodHandle streamFieldExtractor(String pName,
2535                                                          Class<?> pType,
< prev index next >