< prev index next >

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

Print this page

   1 /*
   2  * Copyright (c) 1996, 2024, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package java.io;
  27 
  28 import java.lang.invoke.MethodHandle;
  29 import java.lang.invoke.MethodHandles;
  30 import java.lang.invoke.MethodType;
  31 import java.lang.reflect.Constructor;
  32 import java.lang.reflect.Field;

  33 import java.lang.reflect.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) {

1772 
1773     /**
1774      * Class for setting and retrieving serializable field values in batch.
1775      */
1776     // REMIND: dynamically generate these?
1777     private static final class FieldReflector {
1778 
1779         /** handle for performing unsafe operations */
1780         private static final Unsafe UNSAFE = Unsafe.getUnsafe();
1781 
1782         /** fields to operate on */
1783         private final ObjectStreamField[] fields;
1784         /** number of primitive fields */
1785         private final int numPrimFields;
1786         /** unsafe field keys for reading fields - may contain dupes */
1787         private final long[] readKeys;
1788         /** unsafe fields keys for writing fields - no dupes */
1789         private final long[] writeKeys;
1790         /** field data offsets */
1791         private final int[] offsets;


1792         /** field type codes */
1793         private final char[] typeCodes;
1794         /** field types */
1795         private final Class<?>[] types;
1796 
1797         /**
1798          * Constructs FieldReflector capable of setting/getting values from the
1799          * subset of fields whose ObjectStreamFields contain non-null
1800          * reflective Field objects.  ObjectStreamFields with null Fields are
1801          * treated as filler, for which get operations return default values
1802          * and set operations discard given values.
1803          */
1804         FieldReflector(ObjectStreamField[] fields) {
1805             this.fields = fields;
1806             int nfields = fields.length;
1807             readKeys = new long[nfields];
1808             writeKeys = new long[nfields];
1809             offsets = new int[nfields];

1810             typeCodes = new char[nfields];
1811             ArrayList<Class<?>> typeList = new ArrayList<>();
1812             Set<Long> usedKeys = new HashSet<>();
1813 
1814 
1815             for (int i = 0; i < nfields; i++) {
1816                 ObjectStreamField f = fields[i];
1817                 Field rf = f.getField();
1818                 long key = (rf != null) ?
1819                     UNSAFE.objectFieldOffset(rf) : Unsafe.INVALID_FIELD_OFFSET;
1820                 readKeys[i] = key;
1821                 writeKeys[i] = usedKeys.add(key) ?
1822                     key : Unsafe.INVALID_FIELD_OFFSET;
1823                 offsets[i] = f.getOffset();

1824                 typeCodes[i] = f.getTypeCode();
1825                 if (!f.isPrimitive()) {
1826                     typeList.add((rf != null) ? rf.getType() : null);
1827                 }
1828             }
1829 
1830             types = typeList.toArray(new Class<?>[typeList.size()]);
1831             numPrimFields = nfields - types.length;
1832         }
1833 
1834         /**
1835          * Returns list of ObjectStreamFields representing fields operated on
1836          * by this reflector.  The shared/unshared values and Field objects
1837          * contained by ObjectStreamFields in the list reflect their bindings
1838          * to locally defined serializable fields.
1839          */
1840         ObjectStreamField[] getFields() {
1841             return fields;
1842         }
1843 

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,

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

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

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

 544             if (!osc.isProxy) {
 545                 throw new InvalidClassException(
 546                     "cannot bind proxy descriptor to a non-proxy class");
 547             }
 548         }
 549         this.cl = cl;
 550         this.resolveEx = resolveEx;
 551         this.superDesc = superDesc;
 552         isProxy = true;
 553         serializable = true;
 554         suid = 0L;
 555         fields = NO_FIELDS;
 556         if (osc != null) {
 557             localDesc = osc;
 558             name = localDesc.name;
 559             externalizable = localDesc.externalizable;
 560             writeReplaceMethod = localDesc.writeReplaceMethod;
 561             readResolveMethod = localDesc.readResolveMethod;
 562             deserializeEx = localDesc.deserializeEx;
 563             cons = localDesc.cons;
 564             factoryMode = localDesc.factoryMode;
 565         } else {
 566             factoryMode = DeserializationMode.READ_OBJECT_DEFAULT;
 567         }
 568         fieldRefl = getReflector(fields, localDesc);
 569         initialized = true;
 570     }
 571 
 572     /**
 573      * Initializes class descriptor representing a non-proxy class.
 574      */
 575     void initNonProxy(ObjectStreamClass model,
 576                       Class<?> cl,
 577                       ClassNotFoundException resolveEx,
 578                       ObjectStreamClass superDesc)
 579         throws InvalidClassException
 580     {
 581         long suid = model.getSerialVersionUID();
 582         ObjectStreamClass osc = null;
 583         if (cl != null) {
 584             osc = lookup(cl, true);
 585             if (osc.isProxy) {
 586                 throw new InvalidClassException(

 625         }
 626 
 627         this.cl = cl;
 628         this.resolveEx = resolveEx;
 629         this.superDesc = superDesc;
 630         name = model.name;
 631         this.suid = suid;
 632         isProxy = false;
 633         isEnum = model.isEnum;
 634         serializable = model.serializable;
 635         externalizable = model.externalizable;
 636         hasBlockExternalData = model.hasBlockExternalData;
 637         hasWriteObjectData = model.hasWriteObjectData;
 638         fields = model.fields;
 639         primDataSize = model.primDataSize;
 640         numObjFields = model.numObjFields;
 641 
 642         if (osc != null) {
 643             localDesc = osc;
 644             isRecord = localDesc.isRecord;
 645             isValue = localDesc.isValue;
 646             // canonical record constructor is shared
 647             canonicalCtr = localDesc.canonicalCtr;
 648             // cache of deserialization constructors is shared
 649             deserializationCtrs = localDesc.deserializationCtrs;
 650             writeObjectMethod = localDesc.writeObjectMethod;
 651             readObjectMethod = localDesc.readObjectMethod;
 652             readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
 653             writeReplaceMethod = localDesc.writeReplaceMethod;
 654             readResolveMethod = localDesc.readResolveMethod;
 655             if (deserializeEx == null) {
 656                 deserializeEx = localDesc.deserializeEx;
 657             }
 658             assert cl.isRecord() ? localDesc.cons == null : true;
 659             cons = localDesc.cons;
 660             factoryMode = localDesc.factoryMode;
 661         } else {
 662             // No local class, read data using only the schema from the stream
 663             factoryMode = (externalizable)
 664                     ? DeserializationMode.READ_EXTERNALIZABLE
 665                     : DeserializationMode.READ_NO_LOCAL_CLASS;
 666         }
 667 
 668         fieldRefl = getReflector(fields, localDesc);
 669         // reassign to matched fields so as to reflect local unshared settings
 670         fields = fieldRefl.getFields();
 671 
 672         initialized = true;
 673     }
 674 
 675     /**
 676      * Reads non-proxy class descriptor information from given input stream.
 677      * The resulting class descriptor is not fully functional; it can only be
 678      * used as input to the ObjectInputStream.resolveClass() and
 679      * ObjectStreamClass.initNonProxy() methods.
 680      */
 681     void readNonProxy(ObjectInputStream in)
 682         throws IOException, ClassNotFoundException
 683     {
 684         name = in.readUTF();
 685         suid = in.readLong();

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

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

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


1246 



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

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

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

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

1721                 }
1722             } catch (NoSuchFieldException ex) {
1723             }
1724             if (boundFields[i] == null) {
1725                 boundFields[i] = new ObjectStreamField(fname, spf.getType(), spf.isUnshared(), i);

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

1951 
1952     /**
1953      * Class for setting and retrieving serializable field values in batch.
1954      */
1955     // REMIND: dynamically generate these?
1956     private static final class FieldReflector {
1957 
1958         /** handle for performing unsafe operations */
1959         private static final Unsafe UNSAFE = Unsafe.getUnsafe();
1960 
1961         /** fields to operate on */
1962         private final ObjectStreamField[] fields;
1963         /** number of primitive fields */
1964         private final int numPrimFields;
1965         /** unsafe field keys for reading fields - may contain dupes */
1966         private final long[] readKeys;
1967         /** unsafe fields keys for writing fields - no dupes */
1968         private final long[] writeKeys;
1969         /** field data offsets */
1970         private final int[] offsets;
1971         /** field layouts */
1972         private final int[] layouts;
1973         /** field type codes */
1974         private final char[] typeCodes;
1975         /** field types */
1976         private final Class<?>[] types;
1977 
1978         /**
1979          * Constructs FieldReflector capable of setting/getting values from the
1980          * subset of fields whose ObjectStreamFields contain non-null
1981          * reflective Field objects.  ObjectStreamFields with null Fields are
1982          * treated as filler, for which get operations return default values
1983          * and set operations discard given values.
1984          */
1985         FieldReflector(ObjectStreamField[] fields) {
1986             this.fields = fields;
1987             int nfields = fields.length;
1988             readKeys = new long[nfields];
1989             writeKeys = new long[nfields];
1990             offsets = new int[nfields];
1991             layouts = new int[nfields];
1992             typeCodes = new char[nfields];
1993             ArrayList<Class<?>> typeList = new ArrayList<>();
1994             Set<Long> usedKeys = new HashSet<>();
1995 
1996 
1997             for (int i = 0; i < nfields; i++) {
1998                 ObjectStreamField f = fields[i];
1999                 Field rf = f.getField();
2000                 long key = (rf != null) ?
2001                     UNSAFE.objectFieldOffset(rf) : Unsafe.INVALID_FIELD_OFFSET;
2002                 readKeys[i] = key;
2003                 writeKeys[i] = usedKeys.add(key) ?
2004                     key : Unsafe.INVALID_FIELD_OFFSET;
2005                 offsets[i] = f.getOffset();
2006                 layouts[i] = rf != null ? UNSAFE.fieldLayout(rf) : 0;
2007                 typeCodes[i] = f.getTypeCode();
2008                 if (!f.isPrimitive()) {
2009                     typeList.add((rf != null) ? rf.getType() : null);
2010                 }
2011             }
2012 
2013             types = typeList.toArray(new Class<?>[typeList.size()]);
2014             numPrimFields = nfields - types.length;
2015         }
2016 
2017         /**
2018          * Returns list of ObjectStreamFields representing fields operated on
2019          * by this reflector.  The shared/unshared values and Field objects
2020          * contained by ObjectStreamFields in the list reflect their bindings
2021          * to locally defined serializable fields.
2022          */
2023         ObjectStreamField[] getFields() {
2024             return fields;
2025         }
2026 

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

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

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

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

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