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