< 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 

 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      */

 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;
 198     /** class-defined readResolve method, or null if none */
 199     private Method readResolveMethod;
 200 
 201     /** local class descriptor for represented class (may point to self) */
 202     private ObjectStreamClass localDesc;
 203     /** superclass descriptor appearing in stream */
 204     private ObjectStreamClass superDesc;
 205 
 206     /** true if, and only if, the object has been correctly initialized */
 207     private boolean initialized;
 208 

 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 

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

 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     /**

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

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 

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.

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     {

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 
2111         DeserializationConstructorsCache() {
2112             // start small - if there is more than one shape of ObjectStreamClass
2113             // deserialized, there will typically be two (current version and previous version)
2114             super(2);
2115         }
2116 
2117         MethodHandle get(ObjectStreamField[] fields) {
2118             return get(new Key.Lookup(fields));
2119         }
2120 
2121         synchronized MethodHandle putIfAbsentAndGet(ObjectStreamField[] fields, MethodHandle mh) {
2122             Key.Impl key = new Key.Impl(fields);
2123             var oldMh = putIfAbsent(key, mh);
2124             if (oldMh != null) return oldMh;
2125             // else we did insert new entry -> link the new key as last
2126             if (last == null) {
2127                 last = first = key;
2128             } else {
2129                 last = (last.next = key);
2130             }
2131             // may need to remove first

2191                     this.fieldNames = new String[fields.length];
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 































2233             // change return type to Object
2234             // (T1, T2, ..., Tn):TR -> (T1, T2, ..., Tn):Object
2235             mh = mh.asType(mh.type().changeReturnType(Object.class));
2236 
2237             // drop last 2 arguments representing primValues and objValues arrays
2238             // (T1, T2, ..., Tn):Object -> (T1, T2, ..., Tn, byte[], Object[]):Object
2239             mh = MethodHandles.dropArguments(mh, mh.type().parameterCount(), byte[].class, Object[].class);
2240 
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          */

   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.Executable;
  33 import java.lang.reflect.Field;
  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.HashMap;
  47 import java.util.HashSet;
  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.event.SerializationMisdeclarationEvent;
  54 import jdk.internal.misc.Unsafe;
  55 import jdk.internal.reflect.ReflectionFactory;
  56 import jdk.internal.util.ByteArray;
  57 import jdk.internal.value.Deserializer;
  58 import jdk.internal.value.ValueClass;
  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 

 105                 @Override
 106                 protected Map<FieldReflectorKey, FieldReflector> computeValue(Class<?> type) {
 107                     return new ConcurrentHashMap<>();
 108                 }
 109             };
 110     }
 111 
 112     /** class associated with this descriptor (if any) */
 113     private Class<?> cl;
 114     /** name of class represented by this descriptor */
 115     private String name;
 116     /** serialVersionUID of represented class (null if not computed yet) */
 117     private volatile Long suid;
 118 
 119     /** true if represents dynamic proxy class */
 120     private boolean isProxy;
 121     /** true if represents enum type */
 122     private boolean isEnum;
 123     /** true if represents record type */
 124     private boolean isRecord;
 125     /** true if represented class cannot use allocate-and-fill deserialization,
 126      * due to value class or strict field initialization restrictions.
 127      * Such a class either has a deserializer or has both serialize/deserialize
 128      * exceptions once initialized. */
 129     private boolean requiresDeserializer;
 130     /** true if represented class implements Serializable */
 131     private boolean serializable;
 132     /** true if represented class implements Externalizable */
 133     private boolean externalizable;
 134     /** true if desc has data written by class-defined writeObject method */
 135     private boolean hasWriteObjectData;
 136     /**
 137      * true if desc has externalizable data written in block data format; this
 138      * must be true by default to accommodate ObjectInputStream subclasses which
 139      * override readClassDescriptor() to return class descriptors obtained from
 140      * ObjectStreamClass.lookup() (see 4461737)
 141      */
 142     private boolean hasBlockExternalData = true;
 143 
 144     /**
 145      * Contains information about InvalidClassException instances to be thrown
 146      * when attempting operations on an invalid class. Note that instances of
 147      * this class are immutable and are potentially shared among
 148      * ObjectStreamClass instances.
 149      */

 175     /** exception (if any) to throw if default serialization attempted */
 176     private ExceptionInfo defaultSerializeEx;
 177 
 178     /** serializable fields */
 179     private ObjectStreamField[] fields;
 180     /** aggregate marshalled size of primitive fields */
 181     private int primDataSize;
 182     /** number of non-primitive fields */
 183     private int numObjFields;
 184     /** reflector for setting/getting serializable field values */
 185     private FieldReflector fieldRefl;
 186     /** data layout of serialized objects described by this class desc */
 187     private volatile ClassDataSlot[] dataLayout;
 188 
 189     /** serialization-appropriate constructor, or null if none */
 190     private Constructor<?> cons;
 191     /** record canonical constructor (shared among OSCs for same class), or null */
 192     private MethodHandle canonicalCtr;
 193     /** cache of record deserialization constructors per unique set of stream fields
 194      * (shared among OSCs for same class), or null */
 195     private RecordConstructorsCache cachedRecordConstructors;
 196     /** session-cache of deserialization factory
 197      * (in de-serialized OSC only), or null */
 198     private MethodHandle cachedAlternativeFactory;
 199     /** value deserialization factory method or constructor identified by
 200      * {@link Deserializer}, used when regular deserialization is
 201      * illegal but deserialization support is required. */
 202     private Executable deserializer;
 203 
 204     /** class-defined writeObject method, or null if none */
 205     private Method writeObjectMethod;
 206     /** class-defined readObject method, or null if none */
 207     private Method readObjectMethod;
 208     /** class-defined readObjectNoData method, or null if none */
 209     private Method readObjectNoDataMethod;
 210     /** class-defined writeReplace method, or null if none */
 211     private Method writeReplaceMethod;
 212     /** class-defined readResolve method, or null if none */
 213     private Method readResolveMethod;
 214 
 215     /** local class descriptor for represented class (may point to self) */
 216     private ObjectStreamClass localDesc;
 217     /** superclass descriptor appearing in stream */
 218     private ObjectStreamClass superDesc;
 219 
 220     /** true if, and only if, the object has been correctly initialized */
 221     private boolean initialized;
 222 

 338      */
 339     static ObjectStreamClass lookup(Class<?> cl, boolean all) {
 340         if (!(all || Serializable.class.isAssignableFrom(cl))) {
 341             return null;
 342         }
 343         return Caches.localDescs.get(cl);
 344     }
 345 
 346     /**
 347      * Creates local class descriptor representing given class.
 348      */
 349     private ObjectStreamClass(final Class<?> cl) {
 350         this.cl = cl;
 351         name = cl.getName();
 352         isProxy = Proxy.isProxyClass(cl);
 353         isEnum = Enum.class.isAssignableFrom(cl);
 354         isRecord = cl.isRecord();
 355         serializable = Serializable.class.isAssignableFrom(cl);
 356         externalizable = Externalizable.class.isAssignableFrom(cl);
 357 
 358         // Non-serializable superclasses may declare strictly-initialized instance
 359         // fields while their subclasses remain serializable through default
 360         // serialization, because the superclass constructors that initialize
 361         // those fields are called by default serialization.
 362         // Abstract value classes that do not declare any strictly-initialized
 363         // instance field, like java.lang.Number, are allowed because no field
 364         // initialization is skipped by default serialization.
 365         requiresDeserializer = serializable && (ValueClass.isConcreteValueClass(cl) || ValueClass.hasStrictInstanceField(cl));
 366 
 367         Class<?> superCl = cl.getSuperclass();
 368         superDesc = (superCl != null) ? lookup(superCl, false) : null;
 369         localDesc = this;
 370 
 371         if (superDesc != null) {
 372             requiresDeserializer |= superDesc.requiresDeserializer;
 373         }
 374 
 375         if (serializable) {
 376             if (isEnum) {
 377                 suid = 0L;
 378                 fields = NO_FIELDS;
 379             } else if (cl.isArray()) {
 380                 fields = NO_FIELDS;
 381             } else {
 382                 suid = getDeclaredSUID(cl);
 383                 try {
 384                     fields = getSerialFields(cl);
 385                     computeFieldOffsets();
 386                 } catch (InvalidClassException e) {
 387                     serializeEx = deserializeEx =
 388                             new ExceptionInfo(e.classname, e.getMessage());
 389                     fields = NO_FIELDS;
 390                 }
 391 
 392                 if (isRecord) {
 393                     canonicalCtr = canonicalRecordCtr(cl);
 394                     cachedRecordConstructors = new RecordConstructorsCache();
 395                 } else if (requiresDeserializer) {
 396                     // Concrete value classes and classes with strict instance
 397                     // fields must not breach their integrity with the serializable
 398                     // constructor. Make sure they fail also upon serialization
 399                     // in addition to deserialization if they don't have a
 400                     // correct internal @Deserializer
 401                     deserializer = findDeserializer(cl, fields);
 402                     if (deserializer == null) {
 403                         serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
 404                                 "cannot serialize due to final value class or strictly-initialized instance fields");
 405                     }
 406                 } else if (externalizable) {
 407                     cons = getExternalizableConstructor(cl);
 408                 } else {
 409                     cons = getSerializableConstructor(cl);
 410                     writeObjectMethod = getPrivateMethod(cl, "writeObject",
 411                             new Class<?>[]{ObjectOutputStream.class},
 412                             Void.TYPE);
 413                     readObjectMethod = getPrivateMethod(cl, "readObject",
 414                             new Class<?>[]{ObjectInputStream.class},
 415                             Void.TYPE);
 416                     readObjectNoDataMethod = getPrivateMethod(
 417                             cl, "readObjectNoData", null, Void.TYPE);
 418                     hasWriteObjectData = (writeObjectMethod != null);
 419                 }
 420                 writeReplaceMethod = getInheritableMethod(
 421                         cl, "writeReplace", null, Object.class);
 422                 readResolveMethod = getInheritableMethod(
 423                         cl, "readResolve", null, Object.class);
 424             }
 425         } else {
 426             suid = 0L;
 427             fields = NO_FIELDS;
 428         }
 429 
 430         try {
 431             fieldRefl = getReflector(fields, this);
 432         } catch (InvalidClassException ex) {
 433             // field mismatches impossible when matching local fields vs. self
 434             throw new InternalError(ex);
 435         }
 436 
 437         if (deserializeEx == null) {
 438             if (isEnum) {
 439                 deserializeEx = new ExceptionInfo(name, "enum type");
 440             } else if (cons == null && !isRecord && deserializer == null) {
 441                 deserializeEx = new ExceptionInfo(name, "no valid constructor");
 442             }
 443         }
 444         if (isRecord && canonicalCtr == null) {
 445             deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
 446         } else {
 447             for (int i = 0; i < fields.length; i++) {
 448                 if (fields[i].getField() == null) {
 449                     defaultSerializeEx = new ExceptionInfo(
 450                         name, "unmatched serializable field(s) declared");
 451                 }
 452             }
 453         }
 454         initialized = true;
 455 
 456         if (SerializationMisdeclarationEvent.enabled() && serializable) {
 457             SerializationMisdeclarationChecker.checkMisdeclarations(cl);
 458         }
 459     }
 460 

 557         }
 558 
 559         this.cl = cl;
 560         this.resolveEx = resolveEx;
 561         this.superDesc = superDesc;
 562         name = model.name;
 563         this.suid = suid;
 564         isProxy = false;
 565         isEnum = model.isEnum;
 566         serializable = model.serializable;
 567         externalizable = model.externalizable;
 568         hasBlockExternalData = model.hasBlockExternalData;
 569         hasWriteObjectData = model.hasWriteObjectData;
 570         fields = model.fields;
 571         primDataSize = model.primDataSize;
 572         numObjFields = model.numObjFields;
 573 
 574         if (osc != null) {
 575             localDesc = osc;
 576             isRecord = localDesc.isRecord;
 577             requiresDeserializer = localDesc.requiresDeserializer;
 578             // canonical record constructor is shared
 579             canonicalCtr = localDesc.canonicalCtr;
 580             // cache of deserialization constructors is shared
 581             cachedRecordConstructors = localDesc.cachedRecordConstructors;
 582             writeObjectMethod = localDesc.writeObjectMethod;
 583             readObjectMethod = localDesc.readObjectMethod;
 584             readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
 585             writeReplaceMethod = localDesc.writeReplaceMethod;
 586             readResolveMethod = localDesc.readResolveMethod;
 587             if (deserializeEx == null) {
 588                 deserializeEx = localDesc.deserializeEx;
 589             }
 590             assert cl.isRecord() ? localDesc.cons == null : true;
 591             cons = localDesc.cons;
 592             deserializer = localDesc.deserializer;
 593         }
 594 
 595         fieldRefl = getReflector(fields, localDesc);
 596         // reassign to matched fields so as to reflect local unshared settings
 597         fields = fieldRefl.getFields();
 598 
 599         initialized = true;
 600     }
 601 
 602     /**
 603      * Reads non-proxy class descriptor information from given input stream.
 604      * The resulting class descriptor is not fully functional; it can only be
 605      * used as input to the ObjectInputStream.resolveClass() and
 606      * ObjectStreamClass.initNonProxy() methods.
 607      */
 608     void readNonProxy(ObjectInputStream in)
 609         throws IOException, ClassNotFoundException
 610     {
 611         name = in.readUTF();
 612         suid = in.readLong();

 839     }
 840 
 841     /**
 842      * Returns true if represented class implements Externalizable, false
 843      * otherwise.
 844      */
 845     boolean isExternalizable() {
 846         requireInitialized();
 847         return externalizable;
 848     }
 849 
 850     /**
 851      * Returns true if represented class implements Serializable, false
 852      * otherwise.
 853      */
 854     boolean isSerializable() {
 855         requireInitialized();
 856         return serializable;
 857     }
 858 
 859     /**
 860      * {@return whether this class must use a deserialize factory}
 861      * Concrete value classes and classes declaring strict fields cannot use the
 862      * standard allocate-and-fill deserialization process.
 863      */
 864     boolean requiresDeserializer() {
 865         requireInitialized();
 866         return requiresDeserializer;
 867     }
 868 
 869     /**
 870      * {@return whether this class declares a deserialize factory}
 871      */
 872     boolean hasDeserializer() {
 873         requireInitialized();
 874         return deserializer != null;
 875     }
 876 
 877     /**
 878      * Returns true if class descriptor represents externalizable class that
 879      * has written its data in 1.2 (block data) format, false otherwise.
 880      */
 881     boolean hasBlockExternalData() {
 882         requireInitialized();
 883         return hasBlockExternalData;
 884     }
 885 
 886     /**
 887      * Returns true if class descriptor represents serializable (but not
 888      * externalizable) class which has written its data via a custom
 889      * writeObject() method, false otherwise.
 890      */
 891     boolean hasWriteObjectData() {
 892         requireInitialized();
 893         return hasWriteObjectData;
 894     }
 895 
 896     /**

1335     /**
1336      * If given class is the same as the class associated with this class
1337      * descriptor, returns reference to this class descriptor.  Otherwise,
1338      * returns variant of this class descriptor bound to given class.
1339      */
1340     private ObjectStreamClass getVariantFor(Class<?> cl)
1341         throws InvalidClassException
1342     {
1343         if (this.cl == cl) {
1344             return this;
1345         }
1346         ObjectStreamClass desc = new ObjectStreamClass();
1347         if (isProxy) {
1348             desc.initProxy(cl, null, superDesc);
1349         } else {
1350             desc.initNonProxy(this, cl, null, superDesc);
1351         }
1352         return desc;
1353     }
1354 
1355     /**
1356      * Return an Executable for the static method or constructor(s) that matches the
1357      * serializable fields and annotated with {@link Deserializer}.
1358      * The descriptor for the class is still being initialized, so is passed the fields needed.
1359      * @param clazz The class to query
1360      * @param fields the serializable fields of the class
1361      * @return an Executable, null if none found
1362      */
1363     private static Executable findDeserializer(Class<?> clazz,
1364                                                ObjectStreamField[] fields) {
1365         if (clazz.getClassLoader() != null) {
1366             // Only for boot loader classes
1367             return null;
1368         }
1369         return Stream.concat(
1370                 Arrays.stream(clazz.getDeclaredMethods()).filter(m -> Modifier.isStatic(m.getModifiers())),
1371                 Arrays.stream(clazz.getDeclaredConstructors()))
1372                 .<Executable>mapMulti((exec, sink) -> {
1373                     if (!isDeserializer(exec, fields))
1374                         return;
1375                     exec.setAccessible(true);
1376                     sink.accept(exec);
1377                 })
1378                 .findFirst().orElse(null);
1379     }
1380 
1381     /**
1382      * Check that an executable is a valid deserializer declaration for
1383      * this class. This checks parameters types of the executable and the
1384      * names identified by the deserializer annotation against the fields
1385      * of this class.
1386      *
1387      * @return true if exec is a valid deserializer
1388      */
1389     private static boolean isDeserializer(Executable exec,
1390                                           ObjectStreamField[] fields) {
1391         if (exec.getParameterCount() != fields.length) {
1392             return false;
1393         }
1394 
1395         var deserializer = exec.getDeclaredAnnotation(Deserializer.class);
1396         if (deserializer == null) {
1397             return false;
1398         }
1399 
1400         String[] names = deserializer.value();
1401         if (names.length != fields.length) {
1402             return false;
1403         }
1404 
1405         Map<String, Integer> map = HashMap.newHashMap(names.length);
1406         for (int i = 0; i < names.length; i++) {
1407             if (map.put(names[i], i) != null) {
1408                 return false; // Duplicate names in the factory
1409             }
1410         }
1411 
1412         var params = exec.getParameterTypes();
1413         for (ObjectStreamField field : fields) {
1414             Integer i = map.get(field.getName());
1415             if (i == null) {
1416                 return false; // Name not accounted by the factory
1417             }
1418             if (!field.getType().equals(params[i])) {
1419                 return false; // Name match, type mismatch
1420             }
1421         }
1422         return true;
1423     }
1424 
1425     /**
1426      * Returns public no-arg constructor of given class, or null if none found.
1427      * Access checks are disabled on the returned constructor (if any), since
1428      * the defining class may still be non-public.
1429      */
1430     private static Constructor<?> getExternalizableConstructor(Class<?> cl) {
1431         try {
1432             Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
1433             cons.setAccessible(true);
1434             return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
1435                 cons : null;
1436         } catch (NoSuchMethodException ex) {
1437             return null;
1438         }
1439     }
1440 
1441     /**
1442      * Returns subclass-accessible no-arg constructor of first non-serializable
1443      * superclass, or null if none found.  Access checks are disabled on the
1444      * returned constructor (if any).

1900 
1901     /**
1902      * Class for setting and retrieving serializable field values in batch.
1903      */
1904     // REMIND: dynamically generate these?
1905     private static final class FieldReflector {
1906 
1907         /** handle for performing unsafe operations */
1908         private static final Unsafe UNSAFE = Unsafe.getUnsafe();
1909 
1910         /** fields to operate on */
1911         private final ObjectStreamField[] fields;
1912         /** number of primitive fields */
1913         private final int numPrimFields;
1914         /** unsafe field keys for reading fields - may contain dupes */
1915         private final long[] readKeys;
1916         /** unsafe fields keys for writing fields - no dupes */
1917         private final long[] writeKeys;
1918         /** field data offsets */
1919         private final int[] offsets;
1920         /** field layouts, only used by reference fields */
1921         private final int[] layouts;
1922         /** field type codes */
1923         private final char[] typeCodes;
1924         /** reference field types, only fields.length - numPrimFields items */
1925         private final Class<?>[] types;
1926 
1927         /**
1928          * Constructs FieldReflector capable of setting/getting values from the
1929          * subset of fields whose ObjectStreamFields contain non-null
1930          * reflective Field objects.  ObjectStreamFields with null Fields are
1931          * treated as filler, for which get operations return default values
1932          * and set operations discard given values.
1933          */
1934         FieldReflector(ObjectStreamField[] fields) {
1935             this.fields = fields;
1936             int nfields = fields.length;
1937             readKeys = new long[nfields];
1938             writeKeys = new long[nfields];
1939             offsets = new int[nfields];
1940             layouts = new int[nfields];
1941             typeCodes = new char[nfields];
1942             ArrayList<Class<?>> typeList = new ArrayList<>();
1943             Set<Long> usedKeys = new HashSet<>();
1944 
1945 
1946             for (int i = 0; i < nfields; i++) {
1947                 ObjectStreamField f = fields[i];
1948                 Field rf = f.getField();
1949                 long key = (rf != null) ?
1950                     UNSAFE.objectFieldOffset(rf) : Unsafe.INVALID_FIELD_OFFSET;
1951                 readKeys[i] = key;
1952                 writeKeys[i] = usedKeys.add(key) ?
1953                     key : Unsafe.INVALID_FIELD_OFFSET;
1954                 offsets[i] = f.getOffset();
1955                 layouts[i] = rf != null && !f.isPrimitive() ? UNSAFE.fieldLayout(rf) : Unsafe.NON_FLAT_LAYOUT;
1956                 typeCodes[i] = f.getTypeCode();
1957                 if (!f.isPrimitive()) {
1958                     typeList.add((rf != null) ? rf.getType() : null);
1959                 }
1960             }
1961 
1962             types = typeList.toArray(new Class<?>[typeList.size()]);
1963             numPrimFields = nfields - types.length;
1964         }
1965 
1966         /**
1967          * Returns list of ObjectStreamFields representing fields operated on
1968          * by this reflector.  The shared/unshared values and Field objects
1969          * contained by ObjectStreamFields in the list reflect their bindings
1970          * to locally defined serializable fields.
1971          */
1972         ObjectStreamField[] getFields() {
1973             return fields;
1974         }
1975 

2030                     default  -> throw new InternalError();
2031                 }
2032             }
2033         }
2034 
2035         /**
2036          * Fetches the serializable object field values of object obj and
2037          * stores them in array vals starting at offset 0.  The caller is
2038          * responsible for ensuring that obj is of the proper type.
2039          */
2040         void getObjFieldValues(Object obj, Object[] vals) {
2041             if (obj == null) {
2042                 throw new NullPointerException();
2043             }
2044             /* assuming checkDefaultSerialize() has been called on the class
2045              * descriptor this FieldReflector was obtained from, no field keys
2046              * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
2047              */
2048             for (int i = numPrimFields; i < fields.length; i++) {
2049                 vals[offsets[i]] = switch (typeCodes[i]) {
2050                     case 'L', '[' ->
2051                             layouts[i] == Unsafe.NON_FLAT_LAYOUT
2052                                     ? UNSAFE.getReference(obj, readKeys[i])
2053                                     : UNSAFE.getFlatValue(obj, readKeys[i], layouts[i], types[i - numPrimFields]);
2054                     default       -> throw new InternalError();
2055                 };
2056             }
2057         }
2058 
2059         /**
2060          * Checks that the given values, from array vals starting at offset 0,
2061          * are assignable to the given serializable object fields.
2062          * @throws ClassCastException if any value is not assignable
2063          */
2064         void checkObjectFieldValueTypes(Object obj, Object[] vals) {
2065             setObjFieldValues(obj, vals, true);
2066         }
2067 
2068         /**
2069          * Sets the serializable object fields of object obj using values from
2070          * array vals starting at offset 0.  The caller is responsible for
2071          * ensuring that obj is of the proper type; however, attempts to set a
2072          * field with a value of the wrong type will trigger an appropriate
2073          * ClassCastException.

2083             for (int i = numPrimFields; i < fields.length; i++) {
2084                 long key = writeKeys[i];
2085                 if (key == Unsafe.INVALID_FIELD_OFFSET) {
2086                     continue;           // discard value
2087                 }
2088                 switch (typeCodes[i]) {
2089                     case 'L', '[' -> {
2090                         Object val = vals[offsets[i]];
2091                         if (val != null &&
2092                             !types[i - numPrimFields].isInstance(val))
2093                         {
2094                             Field f = fields[i].getField();
2095                             throw new ClassCastException(
2096                                 "cannot assign instance of " +
2097                                 val.getClass().getName() + " to field " +
2098                                 f.getDeclaringClass().getName() + "." +
2099                                 f.getName() + " of type " +
2100                                 f.getType().getName() + " in instance of " +
2101                                 obj.getClass().getName());
2102                         }
2103                         if (!dryRun) {
2104                             if (layouts[i] == Unsafe.NON_FLAT_LAYOUT) {
2105                                 UNSAFE.putReference(obj, key, val);
2106                             } else {
2107                                 UNSAFE.putFlatValue(obj, key, layouts[i], types[i - numPrimFields], val);
2108                             }
2109                         }
2110                     }
2111                     default -> throw new InternalError();
2112                 }
2113             }
2114         }
2115     }
2116 
2117     /**
2118      * Matches given set of serializable fields with serializable fields
2119      * described by the given local class descriptor, and returns a
2120      * FieldReflector instance capable of setting/getting values from the
2121      * subset of fields that match (non-matching fields are treated as filler,
2122      * for which get operations return default values and set operations
2123      * discard given values).  Throws InvalidClassException if unresolvable
2124      * type conflicts exist between the two sets of fields.
2125      */
2126     private static FieldReflector getReflector(ObjectStreamField[] fields,
2127                                                ObjectStreamClass localDesc)
2128         throws InvalidClassException
2129     {

2222                     } else {
2223                         m = new ObjectStreamField(
2224                             lf.getName(), lf.getSignature(), lf.isUnshared());
2225                     }
2226                 }
2227             }
2228             if (m == null) {
2229                 m = new ObjectStreamField(
2230                     f.getName(), f.getSignature(), false);
2231             }
2232             m.setOffset(f.getOffset());
2233             matches[i] = m;
2234         }
2235         return matches;
2236     }
2237 
2238     /**
2239      * A LRA cache of record deserialization constructors.
2240      */
2241     @SuppressWarnings("serial")
2242     private static final class RecordConstructorsCache
2243         extends ConcurrentHashMap<RecordConstructorsCache.Key, MethodHandle>  {
2244 
2245         // keep max. 10 cached entries - when the 11th element is inserted the oldest
2246         // is removed and 10 remains - 11 is the biggest map size where internal
2247         // table of 16 elements is sufficient (inserting 12th element would resize it to 32)
2248         private static final int MAX_SIZE = 10;
2249         private Key.Impl first, last; // first and last in FIFO queue
2250 
2251         RecordConstructorsCache() {
2252             // start small - if there is more than one shape of ObjectStreamClass
2253             // deserialized, there will typically be two (current version and previous version)
2254             super(2);
2255         }
2256 
2257         MethodHandle get(ObjectStreamField[] fields) {
2258             return get(new Key.Lookup(fields));
2259         }
2260 
2261         synchronized MethodHandle putIfAbsentAndGet(ObjectStreamField[] fields, MethodHandle mh) {
2262             Key.Impl key = new Key.Impl(fields);
2263             var oldMh = putIfAbsent(key, mh);
2264             if (oldMh != null) return oldMh;
2265             // else we did insert new entry -> link the new key as last
2266             if (last == null) {
2267                 last = first = key;
2268             } else {
2269                 last = (last.next = key);
2270             }
2271             // may need to remove first

2331                     this.fieldNames = new String[fields.length];
2332                     this.fieldTypes = new Class<?>[fields.length];
2333                     for (int i = 0; i < fields.length; i++) {
2334                         fieldNames[i] = fields[i].getName();
2335                         fieldTypes[i] = fields[i].getType();
2336                     }
2337                 }
2338 
2339                 @Override
2340                 int length() { return fieldNames.length; }
2341 
2342                 @Override
2343                 String fieldName(int i) { return fieldNames[i]; }
2344 
2345                 @Override
2346                 Class<?> fieldType(int i) { return fieldTypes[i]; }
2347             }
2348         }
2349     }
2350 
2351     /** Support for retrieving and binding stream field values for alternative
2352      * deserialization of record and factory-based value classes. */
2353     static final class AlternativeDeserialization {
2354         /**
2355          * Returns factory method handle adapted to take two arguments:
2356          * {@code (byte[] primValues, Object[] objValues)}
2357          * and return
2358          * {@code Object}
2359          */
2360         static MethodHandle getFactory(ObjectStreamClass desc) {
2361             // check the cached value 1st
2362             MethodHandle mh = desc.cachedAlternativeFactory;
2363             if (mh != null) return mh;
2364 
2365             mh = desc.isRecord() ? recordConstructor(desc) : deserializer(desc);
2366 
2367             // store into cache
2368             return desc.cachedAlternativeFactory = mh;
2369         }
2370 
2371         private static MethodHandle recordConstructor(ObjectStreamClass desc) {
2372             // check the cached value 1st
2373             MethodHandle mh = desc.cachedRecordConstructors.get(desc.getFields(false));
2374             if (mh != null) return mh;


2375 
2376             // retrieve record components
2377             RecordComponent[] recordComponents = desc.forClass().getRecordComponents();
2378 
2379             var types = Arrays.stream(recordComponents).map(RecordComponent::getType).toArray(Class<?>[]::new);
2380             var names = Arrays.stream(recordComponents).map(RecordComponent::getName).toArray(String[]::new);
2381             int count = recordComponents.length;
2382             // retrieve the canonical constructor
2383             // (T1, T2, ..., Tn):TR
2384             mh = desc.getRecordConstructor();
2385 
2386             mh = buildMethodHandle(desc, mh, types, names, count);
2387 
2388             // store it into cache and return the 1st value stored
2389             mh = desc.cachedRecordConstructors.putIfAbsentAndGet(desc.getFields(false), mh);
2390 
2391             return mh;
2392         }
2393 
2394         private static MethodHandle deserializer(ObjectStreamClass desc) {
2395             Executable deserializer = desc.deserializer;
2396             var types = deserializer.getParameterTypes();
2397             String[] names = deserializer.getDeclaredAnnotation(Deserializer.class).value();
2398             int count = types.length;
2399 
2400             MethodHandle mh;
2401             var lookup = MethodHandles.publicLookup();
2402             try {
2403                 mh = deserializer instanceof Method m ? lookup.unreflect(m)
2404                         : lookup.unreflectConstructor((Constructor<?>) deserializer);
2405             } catch (ReflectiveOperationException e) {
2406                 throw new InternalError(e);
2407             }
2408 
2409             return buildMethodHandle(desc, mh, types, names, count);
2410         }
2411 
2412         private static MethodHandle buildMethodHandle(ObjectStreamClass desc,
2413                                                       MethodHandle mh,
2414                                                       Class<?>[] types,
2415                                                       String[] names,
2416                                                       int count) {
2417             // change return type to Object
2418             // (T1, T2, ..., Tn):TR -> (T1, T2, ..., Tn):Object
2419             mh = mh.asType(mh.type().changeReturnType(Object.class));
2420 
2421             // drop last 2 arguments representing primValues and objValues arrays
2422             // (T1, T2, ..., Tn):Object -> (T1, T2, ..., Tn, byte[], Object[]):Object
2423             mh = MethodHandles.dropArguments(mh, mh.type().parameterCount(), byte[].class, Object[].class);
2424 
2425             for (int i = count-1; i >= 0; i--) {
2426                 String name = names[i];
2427                 Class<?> type = types[i];
2428                 // obtain stream field extractor that extracts argument at
2429                 // position i (Ti+1) from primValues and objValues arrays
2430                 // (byte[], Object[]):Ti+1
2431                 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2432                 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2433                 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2434                 mh = MethodHandles.foldArguments(mh, i, combiner);
2435             }
2436             // what we are left with is a MethodHandle taking just the primValues
2437             // and objValues arrays and returning the constructed record instance
2438             // (byte[], Object[]):Object
2439             return mh;



2440         }
2441 
2442         /** Returns the number of primitive fields for the given descriptor. */
2443         private static int numberPrimValues(ObjectStreamClass desc) {
2444             ObjectStreamField[] fields = desc.getFields();
2445             int primValueCount = 0;
2446             for (int i = 0; i < fields.length; i++) {
2447                 if (fields[i].isPrimitive())
2448                     primValueCount++;
2449                 else
2450                     break;  // can be no more
2451             }
2452             return primValueCount;
2453         }
2454 
2455         /**
2456          * Returns extractor MethodHandle taking the primValues and objValues arrays
2457          * and extracting the argument of canonical constructor with given name and type
2458          * or producing  default value for the given type if the field is absent.
2459          */
< prev index next >