< 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         return Stream.concat(
1366                 Arrays.stream(clazz.getDeclaredMethods()).filter(m -> Modifier.isStatic(m.getModifiers())),
1367                 Arrays.stream(clazz.getDeclaredConstructors()))
1368                 .<Executable>mapMulti((exec, sink) -> {
1369                     if (!isDeserializer(exec, fields))
1370                         return;
1371                     exec.setAccessible(true);
1372                     sink.accept(exec);
1373                 })
1374                 .findFirst().orElse(null);
1375     }
1376 
1377     /**
1378      * Check that an executable is a valid deserializer declaration for
1379      * this class. This checks parameters types of the executable and the
1380      * names identified by the deserializer annotation against the fields
1381      * of this class.
1382      *
1383      * @return true if exec is a valid deserializer
1384      */
1385     private static boolean isDeserializer(Executable exec,
1386                                           ObjectStreamField[] fields) {
1387         if (exec.getParameterCount() != fields.length) {
1388             return false;
1389         }
1390 
1391         var deserializer = exec.getDeclaredAnnotation(Deserializer.class);
1392         if (deserializer == null) {
1393             return false;
1394         }
1395 
1396         String[] names = deserializer.value();
1397         if (names.length != fields.length) {
1398             return false;
1399         }
1400 
1401         Map<String, Integer> map = HashMap.newHashMap(names.length);
1402         for (int i = 0; i < names.length; i++) {
1403             if (map.put(names[i], i) != null) {
1404                 return false; // Duplicate names in the factory
1405             }
1406         }
1407 
1408         var params = exec.getParameterTypes();
1409         for (ObjectStreamField field : fields) {
1410             Integer i = map.get(field.getName());
1411             if (i == null) {
1412                 return false; // Name not accounted by the factory
1413             }
1414             if (!field.getType().equals(params[i])) {
1415                 return false; // Name match, type mismatch
1416             }
1417         }
1418         return true;
1419     }
1420 
1421     /**
1422      * Returns public no-arg constructor of given class, or null if none found.
1423      * Access checks are disabled on the returned constructor (if any), since
1424      * the defining class may still be non-public.
1425      */
1426     private static Constructor<?> getExternalizableConstructor(Class<?> cl) {
1427         try {
1428             Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
1429             cons.setAccessible(true);
1430             return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
1431                 cons : null;
1432         } catch (NoSuchMethodException ex) {
1433             return null;
1434         }
1435     }
1436 
1437     /**
1438      * Returns subclass-accessible no-arg constructor of first non-serializable
1439      * superclass, or null if none found.  Access checks are disabled on the
1440      * returned constructor (if any).

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

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

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

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

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


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



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