< 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 

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

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










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

 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     private boolean requiresDeserializer;
 128     /** true if represented class implements Serializable */
 129     private boolean serializable;
 130     /** true if represented class implements Externalizable */
 131     private boolean externalizable;
 132     /** true if desc has data written by class-defined writeObject method */
 133     private boolean hasWriteObjectData;
 134     /**
 135      * true if desc has externalizable data written in block data format; this
 136      * must be true by default to accommodate ObjectInputStream subclasses which
 137      * override readClassDescriptor() to return class descriptors obtained from
 138      * ObjectStreamClass.lookup() (see 4461737)
 139      */
 140     private boolean hasBlockExternalData = true;
 141 
 142     /**
 143      * Contains information about InvalidClassException instances to be thrown
 144      * when attempting operations on an invalid class. Note that instances of
 145      * this class are immutable and are potentially shared among
 146      * ObjectStreamClass instances.
 147      */

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

 333      * @param   cl class to look up
 334      * @param   all if true, return descriptors for all classes; if false, only
 335      *          return descriptors for serializable classes
 336      */
 337     static ObjectStreamClass lookup(Class<?> cl, boolean all) {
 338         if (!(all || Serializable.class.isAssignableFrom(cl))) {
 339             return null;
 340         }
 341         return Caches.localDescs.get(cl);
 342     }
 343 
 344     /**
 345      * Creates local class descriptor representing given class.
 346      */
 347     private ObjectStreamClass(final Class<?> cl) {
 348         this.cl = cl;
 349         name = cl.getName();
 350         isProxy = Proxy.isProxyClass(cl);
 351         isEnum = Enum.class.isAssignableFrom(cl);
 352         isRecord = cl.isRecord();
 353         requiresDeserializer = cl.isValue() || ValueClass.hasStrictInstanceField(cl);
 354         serializable = Serializable.class.isAssignableFrom(cl);
 355         externalizable = Externalizable.class.isAssignableFrom(cl);
 356 
 357         Class<?> superCl = cl.getSuperclass();
 358         superDesc = (superCl != null) ? lookup(superCl, false) : null;
 359         localDesc = this;
 360 
 361         if (serializable) {
 362             if (isEnum) {
 363                 suid = 0L;
 364                 fields = NO_FIELDS;
 365             } else if (cl.isArray()) {
 366                 fields = NO_FIELDS;
 367             } else {
 368                 suid = getDeclaredSUID(cl);
 369                 try {
 370                     fields = getSerialFields(cl);
 371                     computeFieldOffsets();
 372                 } catch (InvalidClassException e) {
 373                     serializeEx = deserializeEx =
 374                             new ExceptionInfo(e.classname, e.getMessage());
 375                     fields = NO_FIELDS;
 376                 }
 377 
 378                 if (isRecord) {
 379                     canonicalCtr = canonicalRecordCtr(cl);
 380                     cachedRecordConstructors = new RecordConstructorsCache();
 381                 } else if (requiresDeserializer) {
 382                     if (!Modifier.isAbstract(cl.getModifiers())) {
 383                         // Serializable value classes that appear in streams should
 384                         // have a factory annotated with @Deserializer
 385                         deserializer = findDeserializer(cl, fields);
 386                     }
 387                     if (deserializer == null) {
 388                         serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
 389                                                                         "cannot serialize value class");
 390                     }
 391                 } else if (externalizable) {
 392                     cons = getExternalizableConstructor(cl);
 393                 } else {
 394                     cons = getSerializableConstructor(cl);
 395                     writeObjectMethod = getPrivateMethod(cl, "writeObject",
 396                             new Class<?>[]{ObjectOutputStream.class},
 397                             Void.TYPE);
 398                     readObjectMethod = getPrivateMethod(cl, "readObject",
 399                             new Class<?>[]{ObjectInputStream.class},
 400                             Void.TYPE);
 401                     readObjectNoDataMethod = getPrivateMethod(
 402                             cl, "readObjectNoData", null, Void.TYPE);
 403                     hasWriteObjectData = (writeObjectMethod != null);
 404                 }
 405                 writeReplaceMethod = getInheritableMethod(
 406                         cl, "writeReplace", null, Object.class);
 407                 readResolveMethod = getInheritableMethod(
 408                         cl, "readResolve", null, Object.class);
 409             }
 410         } else {
 411             suid = 0L;
 412             fields = NO_FIELDS;
 413         }
 414 
 415         try {
 416             fieldRefl = getReflector(fields, this);
 417         } catch (InvalidClassException ex) {
 418             // field mismatches impossible when matching local fields vs. self
 419             throw new InternalError(ex);
 420         }
 421 
 422         if (deserializeEx == null) {
 423             if (isEnum) {
 424                 deserializeEx = new ExceptionInfo(name, "enum type");
 425             } else if (cons == null && !(isRecord || requiresDeserializer)) {
 426                 deserializeEx = new ExceptionInfo(name, "no valid constructor");
 427             }
 428         }
 429         if (isRecord && canonicalCtr == null) {
 430             deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
 431         } else {
 432             for (int i = 0; i < fields.length; i++) {
 433                 if (fields[i].getField() == null) {
 434                     defaultSerializeEx = new ExceptionInfo(
 435                         name, "unmatched serializable field(s) declared");
 436                 }
 437             }
 438         }
 439         initialized = true;
 440 
 441         if (SerializationMisdeclarationEvent.enabled() && serializable) {
 442             SerializationMisdeclarationChecker.checkMisdeclarations(cl);
 443         }
 444     }
 445 

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

 824     }
 825 
 826     /**
 827      * Returns true if represented class implements Externalizable, false
 828      * otherwise.
 829      */
 830     boolean isExternalizable() {
 831         requireInitialized();
 832         return externalizable;
 833     }
 834 
 835     /**
 836      * Returns true if represented class implements Serializable, false
 837      * otherwise.
 838      */
 839     boolean isSerializable() {
 840         requireInitialized();
 841         return serializable;
 842     }
 843 
 844     /**
 845      * {@return whether this class must use a deserialize factory}
 846      * Concrete value classes and classes declaring strict fields cannot use the
 847      * standard allocate-and-fill deserialization process.
 848      */
 849     boolean requiresDeserializer() {
 850         requireInitialized();
 851         return requiresDeserializer;
 852     }
 853 
 854     /**
 855      * {@return whether this class declares a deserialize factory}
 856      */
 857     boolean hasDeserializer() {
 858         requireInitialized();
 859         return deserializer != null;
 860     }
 861 
 862     /**
 863      * Returns true if class descriptor represents externalizable class that
 864      * has written its data in 1.2 (block data) format, false otherwise.
 865      */
 866     boolean hasBlockExternalData() {
 867         requireInitialized();
 868         return hasBlockExternalData;
 869     }
 870 
 871     /**
 872      * Returns true if class descriptor represents serializable (but not
 873      * externalizable) class which has written its data via a custom
 874      * writeObject() method, false otherwise.
 875      */
 876     boolean hasWriteObjectData() {
 877         requireInitialized();
 878         return hasWriteObjectData;
 879     }
 880 
 881     /**

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

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

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

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

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

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


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



2421         }
2422 
2423         /** Returns the number of primitive fields for the given descriptor. */
2424         private static int numberPrimValues(ObjectStreamClass desc) {
2425             ObjectStreamField[] fields = desc.getFields();
2426             int primValueCount = 0;
2427             for (int i = 0; i < fields.length; i++) {
2428                 if (fields[i].isPrimitive())
2429                     primValueCount++;
2430                 else
2431                     break;  // can be no more
2432             }
2433             return primValueCount;
2434         }
2435 
2436         /**
2437          * Returns extractor MethodHandle taking the primValues and objValues arrays
2438          * and extracting the argument of canonical constructor with given name and type
2439          * or producing  default value for the given type if the field is absent.
2440          */
< prev index next >