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