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
74 /** serialPersistentFields value indicating no serializable fields */
75 public static final ObjectStreamField[] NO_FIELDS =
76 new ObjectStreamField[0];
77
78 @java.io.Serial
79 private static final long serialVersionUID = -6120832682080437368L;
80 /**
81 * {@code ObjectStreamClass} has no fields for default serialization.
82 */
83 @java.io.Serial
84 private static final ObjectStreamField[] serialPersistentFields =
85 NO_FIELDS;
86
87 private static class Caches {
88 /** cache mapping local classes -> descriptors */
89 static final ClassCache<ObjectStreamClass> localDescs =
90 new ClassCache<>() {
91 @Override
92 protected ObjectStreamClass computeValue(Class<?> type) {
93 return new ObjectStreamClass(type);
94 }
95 };
96
97 /** cache mapping field group/local desc pairs -> field reflectors */
98 static final ClassCache<Map<FieldReflectorKey, FieldReflector>> reflectors =
99 new ClassCache<>() {
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 */
157 }
158
159 /** exception (if any) thrown while attempting to resolve class */
160 private ClassNotFoundException resolveEx;
161 /** exception (if any) to throw if non-enum deserialization attempted */
162 private ExceptionInfo deserializeEx;
163 /** exception (if any) to throw if non-enum serialization attempted */
164 private ExceptionInfo serializeEx;
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;
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
441 if (!osc.isProxy) {
442 throw new InvalidClassException(
443 "cannot bind proxy descriptor to a non-proxy class");
444 }
445 }
446 this.cl = cl;
447 this.resolveEx = resolveEx;
448 this.superDesc = superDesc;
449 isProxy = true;
450 serializable = true;
451 suid = 0L;
452 fields = NO_FIELDS;
453 if (osc != null) {
454 localDesc = osc;
455 name = localDesc.name;
456 externalizable = localDesc.externalizable;
457 writeReplaceMethod = localDesc.writeReplaceMethod;
458 readResolveMethod = localDesc.readResolveMethod;
459 deserializeEx = localDesc.deserializeEx;
460 cons = localDesc.cons;
461 }
462 fieldRefl = getReflector(fields, localDesc);
463 initialized = true;
464 }
465
466 /**
467 * Initializes class descriptor representing a non-proxy class.
468 */
469 void initNonProxy(ObjectStreamClass model,
470 Class<?> cl,
471 ClassNotFoundException resolveEx,
472 ObjectStreamClass superDesc)
473 throws InvalidClassException
474 {
475 long suid = model.getSerialVersionUID();
476 ObjectStreamClass osc = null;
477 if (cl != null) {
478 osc = lookup(cl, true);
479 if (osc.isProxy) {
480 throw new InvalidClassException(
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();
588 serializable = externalizable || sflag;
589 isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
590 if (isEnum && suid.longValue() != 0L) {
591 throw new InvalidClassException(name,
592 "enum descriptor has non-zero serialVersionUID: " + suid);
593 }
594
595 int numFields = in.readShort();
596 if (isEnum && numFields != 0) {
597 throw new InvalidClassException(name,
598 "enum descriptor has non-zero field count: " + numFields);
599 }
600 fields = (numFields > 0) ?
601 new ObjectStreamField[numFields] : NO_FIELDS;
602 for (int i = 0; i < numFields; i++) {
603 char tcode = (char) in.readByte();
604 String fname = in.readUTF();
605 String signature = ((tcode == 'L') || (tcode == '[')) ?
606 in.readTypeString() : String.valueOf(tcode);
607 try {
608 fields[i] = new ObjectStreamField(fname, signature, false);
609 } catch (RuntimeException e) {
610 throw new InvalidClassException(name,
611 "invalid descriptor for field " +
612 fname, e);
613 }
614 }
615 computeFieldOffsets();
616 }
617
618 /**
619 * Writes non-proxy class descriptor information to given output stream.
620 */
621 void writeNonProxy(ObjectOutputStream out) throws IOException {
622 out.writeUTF(name);
623 out.writeLong(getSerialVersionUID());
624
625 byte flags = 0;
626 if (externalizable) {
627 flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
628 int protocol = out.getProtocolVersion();
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 /**
839 * Returns true if represented class is serializable/externalizable and can
840 * be instantiated by the serialization runtime--i.e., if it is
841 * externalizable and defines a public no-arg constructor, or if it is
842 * non-externalizable and its first non-serializable superclass defines an
843 * accessible no-arg constructor. Otherwise, returns false.
844 */
845 boolean isInstantiable() {
846 requireInitialized();
847 return (cons != null);
848 }
849
850 /**
851 * Returns true if represented class is serializable (but not
852 * externalizable) and defines a conformant writeObject method. Otherwise,
853 * returns false.
854 */
855 boolean hasWriteObjectMethod() {
856 requireInitialized();
857 return (writeObjectMethod != null);
858 }
859
860 /**
861 * Returns true if represented class is serializable (but not
862 * externalizable) and defines a conformant readObject method. Otherwise,
863 * returns false.
864 */
865 boolean hasReadObjectMethod() {
866 requireInitialized();
867 return (readObjectMethod != null);
1084 /**
1085 * Class representing the portion of an object's serialized form allotted
1086 * to data described by a given class descriptor. If "hasData" is false,
1087 * the object's serialized form does not contain data associated with the
1088 * class descriptor.
1089 */
1090 static class ClassDataSlot {
1091
1092 /** class descriptor "occupying" this slot */
1093 final ObjectStreamClass desc;
1094 /** true if serialized form includes data for this slot's descriptor */
1095 final boolean hasData;
1096
1097 ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
1098 this.desc = desc;
1099 this.hasData = hasData;
1100 }
1101 }
1102
1103 /**
1104 * Returns array of ClassDataSlot instances representing the data layout
1105 * (including superclass data) for serialized objects described by this
1106 * class descriptor. ClassDataSlots are ordered by inheritance with those
1107 * containing "higher" superclasses appearing first. The final
1108 * ClassDataSlot contains a reference to this descriptor.
1109 */
1110 ClassDataSlot[] getClassDataLayout() throws InvalidClassException {
1111 // REMIND: synchronize instead of relying on volatile?
1112 if (dataLayout == null) {
1113 dataLayout = getClassDataLayout0();
1114 }
1115 return dataLayout;
1116 }
1117
1118 private ClassDataSlot[] getClassDataLayout0()
1119 throws InvalidClassException
1120 {
1121 ArrayList<ClassDataSlot> slots = new ArrayList<>();
1122 Class<?> start = cl, end = cl;
1123
1124 // locate closest non-serializable superclass
1125 while (end != null && Serializable.class.isAssignableFrom(end)) {
1126 end = end.getSuperclass();
1127 }
1128
1129 HashSet<String> oscNames = new HashSet<>(3);
1130
1131 for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
1132 if (oscNames.contains(d.name)) {
1133 throw new InvalidClassException("Circular reference.");
1134 } else {
1135 oscNames.add(d.name);
1136 }
1137
1138 // search up inheritance hierarchy for class with matching name
1139 String searchName = (d.cl != null) ? d.cl.getName() : d.name;
1140 Class<?> match = null;
1149 if (match != null) {
1150 for (Class<?> c = start; c != match; c = c.getSuperclass()) {
1151 slots.add(new ClassDataSlot(
1152 ObjectStreamClass.lookup(c, true), false));
1153 }
1154 start = match.getSuperclass();
1155 }
1156
1157 // record descriptor/class pairing
1158 slots.add(new ClassDataSlot(d.getVariantFor(match), true));
1159 }
1160
1161 // add "no data" slot for any leftover unmatched classes
1162 for (Class<?> c = start; c != end; c = c.getSuperclass()) {
1163 slots.add(new ClassDataSlot(
1164 ObjectStreamClass.lookup(c, true), false));
1165 }
1166
1167 // order slots from superclass -> subclass
1168 Collections.reverse(slots);
1169 return slots.toArray(new ClassDataSlot[slots.size()]);
1170 }
1171
1172 /**
1173 * Returns aggregate size (in bytes) of marshalled primitive field values
1174 * for represented class.
1175 */
1176 int getPrimDataSize() {
1177 return primDataSize;
1178 }
1179
1180 /**
1181 * Returns number of non-primitive serializable fields of represented
1182 * class.
1183 */
1184 int getNumObjFields() {
1185 return numObjFields;
1186 }
1187
1188 /**
1189 * Fetches the serializable primitive field values of object obj and
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).
1317 */
1318 private static Constructor<?> getSerializableConstructor(Class<?> cl) {
1319 return ReflectionFactory.getReflectionFactory().newConstructorForSerialization(cl);
1320 }
1321
1322 /**
1323 * Returns the canonical constructor for the given record class, or null if
1324 * the not found ( which should never happen for correctly generated record
1325 * classes ).
1326 */
1327 private static MethodHandle canonicalRecordCtr(Class<?> cls) {
1328 assert cls.isRecord() : "Expected record, got: " + cls;
1519
1520 ObjectStreamField[] boundFields =
1521 new ObjectStreamField[serialPersistentFields.length];
1522 Set<String> fieldNames = HashSet.newHashSet(serialPersistentFields.length);
1523
1524 for (int i = 0; i < serialPersistentFields.length; i++) {
1525 ObjectStreamField spf = serialPersistentFields[i];
1526
1527 String fname = spf.getName();
1528 if (fieldNames.contains(fname)) {
1529 throw new InvalidClassException(
1530 "multiple serializable fields named " + fname);
1531 }
1532 fieldNames.add(fname);
1533
1534 try {
1535 Field f = cl.getDeclaredField(fname);
1536 if ((f.getType() == spf.getType()) &&
1537 ((f.getModifiers() & Modifier.STATIC) == 0))
1538 {
1539 boundFields[i] =
1540 new ObjectStreamField(f, spf.isUnshared(), true);
1541 }
1542 } catch (NoSuchFieldException ex) {
1543 }
1544 if (boundFields[i] == null) {
1545 boundFields[i] = new ObjectStreamField(
1546 fname, spf.getType(), spf.isUnshared());
1547 }
1548 }
1549 return boundFields;
1550 }
1551
1552 /**
1553 * Returns array of ObjectStreamFields corresponding to all non-static
1554 * non-transient fields declared by given class. Each ObjectStreamField
1555 * contains a Field object for the field it represents. If no default
1556 * serializable fields exist, NO_FIELDS is returned.
1557 */
1558 private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
1559 Field[] clFields = cl.getDeclaredFields();
1560 ArrayList<ObjectStreamField> list = new ArrayList<>();
1561 int mask = Modifier.STATIC | Modifier.TRANSIENT;
1562
1563 for (int i = 0; i < clFields.length; i++) {
1564 if ((clFields[i].getModifiers() & mask) == 0) {
1565 list.add(new ObjectStreamField(clFields[i], false, true));
1566 }
1567 }
1568 int size = list.size();
1569 return (size == 0) ? NO_FIELDS :
1570 list.toArray(new ObjectStreamField[size]);
1571 }
1572
1573 /**
1574 * Returns explicit serial version UID value declared by given class, or
1575 * null if none.
1576 */
1577 private static Long getDeclaredSUID(Class<?> cl) {
1578 try {
1579 Field f = cl.getDeclaredField("serialVersionUID");
1580 int mask = Modifier.STATIC | Modifier.FINAL;
1581 if ((f.getModifiers() & mask) == mask) {
1582 f.setAccessible(true);
1583 return f.getLong(null);
1584 }
1585 } catch (Exception ex) {
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
1897 case 'D' -> UNSAFE.putDouble(obj, key, ByteArray.getDouble(buf, off));
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.
1939 */
1940 void setObjFieldValues(Object obj, Object[] vals) {
1941 setObjFieldValues(obj, vals, false);
1942 }
1943
1944 private void setObjFieldValues(Object obj, Object[] vals, boolean dryRun) {
1945 if (obj == null) {
1946 throw new NullPointerException();
1947 }
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 {
2061 * a non-local class descriptor. To preserve this (questionable)
2062 * behavior, the ObjectStreamField instances returned by matchFields
2063 * cannot report non-primitive types other than Object.class; hence
2064 * localFields cannot be returned directly.
2065 */
2066
2067 ObjectStreamField[] matches = new ObjectStreamField[fields.length];
2068 for (int i = 0; i < fields.length; i++) {
2069 ObjectStreamField f = fields[i], m = null;
2070 for (int j = 0; j < localFields.length; j++) {
2071 ObjectStreamField lf = localFields[j];
2072 if (f.getName().equals(lf.getName())) {
2073 if ((f.isPrimitive() || lf.isPrimitive()) &&
2074 f.getTypeCode() != lf.getTypeCode())
2075 {
2076 throw new InvalidClassException(localDesc.name,
2077 "incompatible types for field " + f.getName());
2078 }
2079 if (lf.getField() != null) {
2080 m = new ObjectStreamField(
2081 lf.getField(), lf.isUnshared(), false);
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
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
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 */
2279 private static MethodHandle streamFieldExtractor(String pName,
2280 Class<?> pType,
|
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.Field;
33 import java.lang.reflect.InaccessibleObjectException;
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.HashSet;
47 import java.util.List;
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.MigratedValueClass;
54 import jdk.internal.event.SerializationMisdeclarationEvent;
55 import jdk.internal.misc.Unsafe;
56 import jdk.internal.reflect.ReflectionFactory;
57 import jdk.internal.util.ByteArray;
58 import jdk.internal.value.DeserializeConstructor;
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
79 /** serialPersistentFields value indicating no serializable fields */
80 public static final ObjectStreamField[] NO_FIELDS =
81 new ObjectStreamField[0];
82
83 @java.io.Serial
84 private static final long serialVersionUID = -6120832682080437368L;
85 /**
86 * {@code ObjectStreamClass} has no fields for default serialization.
87 */
88 @java.io.Serial
89 private static final ObjectStreamField[] serialPersistentFields =
90 NO_FIELDS;
91
92 /**
93 * The mode of deserialization for a class depending on its type and interfaces.
94 * The markers used are {@linkplain java.io.Serializable}, {@linkplain java.io.Externalizable},
95 * Class.isRecord(), Class.isValue(), constructors, and
96 * the presence of methods `readObject`, `writeObject`, `readObjectNoData`, `writeObject`.
97 * ObjectInputStream dispatches on the mode to construct objects from the stream.
98 */
99 enum DeserializationMode {
100 /**
101 * Construct an object from the stream for a class that has only default read object behaviors.
102 * All classes and superclasses use defaultReadObject; no custom readObject or readObjectNoData.
103 * The new instance is entered in the handle table if it is unshared,
104 * allowing it to escape before it is initialized.
105 * For each object, all the fields are read before any are assigned.
106 * The `readObject` and `readObjectNoData` methods are not present and are not called.
107 */
108 READ_OBJECT_DEFAULT,
109 /**
110 * Creates a new object and invokes its readExternal method to read its contents.
111 * If the class is instantiable, read externalizable data by invoking readExternal()
112 * method of obj; otherwise, attempts to skip over externalizable data.
113 * Expects that passHandle is set to obj's handle before this method is
114 * called. The new object is entered in the handle table immediately,
115 * allowing it to leak before it is completely read.
116 */
117 READ_EXTERNALIZABLE,
118 /**
119 * Read all the record fields and invoke its canonical constructor.
120 * Construct the record using its canonical constructor.
121 * The new record is entered in the handle table only after the constructor returns.
122 */
123 READ_RECORD,
124 /**
125 * Fully custom read from the stream to create an instance.
126 * If the class is not instantiatable or is tagged with ClassNotFoundException
127 * the data in the stream for the class is read and discarded. {@link #READ_NO_LOCAL_CLASS}
128 * The instance is created and set in the handle table, allowing it to leak before it is initialized.
129 * For each serializable class in the stream, from superclass to subclass the
130 * stream values are read by the `readObject` method, if present, or defaultReadObject.
131 * Custom inline data is discarded if not consumed by the class `readObject` method.
132 */
133 READ_OBJECT_CUSTOM,
134 /**
135 * Construct an object by reading the values of all fields and
136 * invoking a constructor or static factory method.
137 * The constructor or static factory method is selected by matching its parameters with the
138 * sequence of field types of the serializable fields of the local class and superclasses.
139 * Invoke the constructor with all the values from the stream, inserting
140 * defaults and dropping extra values as necessary.
141 * This is very similar to the reading of records, except for the identification of
142 * the constructor or static factory.
143 */
144 READ_OBJECT_VALUE,
145 /**
146 * Read and discard an entire object, leaving a null reference in the HandleTable.
147 * The descriptor of the class in the stream is used to read the fields from the stream.
148 * There is no instance in which to store the field values.
149 * Custom data following the fields of any slot is read and discarded.
150 * References to nested objects are read and retained in the
151 * handle table using the regular mechanism.
152 * Handles later in the stream may refer to the nested objects.
153 */
154 READ_NO_LOCAL_CLASS,
155 }
156
157 private static class Caches {
158 /** cache mapping local classes -> descriptors */
159 static final ClassCache<ObjectStreamClass> localDescs =
160 new ClassCache<>() {
161 @Override
162 protected ObjectStreamClass computeValue(Class<?> type) {
163 return new ObjectStreamClass(type);
164 }
165 };
166
167 /** cache mapping field group/local desc pairs -> field reflectors */
168 static final ClassCache<Map<FieldReflectorKey, FieldReflector>> reflectors =
169 new ClassCache<>() {
170 @Override
171 protected Map<FieldReflectorKey, FieldReflector> computeValue(Class<?> type) {
172 return new ConcurrentHashMap<>();
173 }
174 };
175 }
176
177 /** class associated with this descriptor (if any) */
178 private Class<?> cl;
179 /** name of class represented by this descriptor */
180 private String name;
181 /** serialVersionUID of represented class (null if not computed yet) */
182 private volatile Long suid;
183
184 /** true if represents dynamic proxy class */
185 private boolean isProxy;
186 /** true if represents enum type */
187 private boolean isEnum;
188 /** true if represents record type */
189 private boolean isRecord;
190 /** true if represents a value class */
191 private boolean isValue;
192 /** The DeserializationMode for this class. */
193 private DeserializationMode factoryMode;
194 /** true if represented class implements Serializable */
195 private boolean serializable;
196 /** true if represented class implements Externalizable */
197 private boolean externalizable;
198 /** true if desc has data written by class-defined writeObject method */
199 private boolean hasWriteObjectData;
200 /**
201 * true if desc has externalizable data written in block data format; this
202 * must be true by default to accommodate ObjectInputStream subclasses which
203 * override readClassDescriptor() to return class descriptors obtained from
204 * ObjectStreamClass.lookup() (see 4461737)
205 */
206 private boolean hasBlockExternalData = true;
207
208 /**
209 * Contains information about InvalidClassException instances to be thrown
210 * when attempting operations on an invalid class. Note that instances of
211 * this class are immutable and are potentially shared among
212 * ObjectStreamClass instances.
213 */
231 }
232
233 /** exception (if any) thrown while attempting to resolve class */
234 private ClassNotFoundException resolveEx;
235 /** exception (if any) to throw if non-enum deserialization attempted */
236 private ExceptionInfo deserializeEx;
237 /** exception (if any) to throw if non-enum serialization attempted */
238 private ExceptionInfo serializeEx;
239 /** exception (if any) to throw if default serialization attempted */
240 private ExceptionInfo defaultSerializeEx;
241
242 /** serializable fields */
243 private ObjectStreamField[] fields;
244 /** aggregate marshalled size of primitive fields */
245 private int primDataSize;
246 /** number of non-primitive fields */
247 private int numObjFields;
248 /** reflector for setting/getting serializable field values */
249 private FieldReflector fieldRefl;
250 /** data layout of serialized objects described by this class desc */
251 private volatile List<ClassDataSlot> dataLayout;
252
253 /** serialization-appropriate constructor, or null if none */
254 private Constructor<?> cons;
255 /** record canonical constructor (shared among OSCs for same class), or null */
256 private MethodHandle canonicalCtr;
257 /** cache of record deserialization constructors per unique set of stream fields
258 * (shared among OSCs for same class), or null */
259 private DeserializationConstructorsCache deserializationCtrs;
260 /** session-cache of record deserialization constructor
261 * (in de-serialized OSC only), or null */
262 private MethodHandle deserializationCtr;
263
264 /** class-defined writeObject method, or null if none */
265 private Method writeObjectMethod;
266 /** class-defined readObject method, or null if none */
267 private Method readObjectMethod;
268 /** class-defined readObjectNoData method, or null if none */
269 private Method readObjectNoDataMethod;
270 /** class-defined writeReplace method, or null if none */
271 private Method writeReplaceMethod;
395 * @param cl class to look up
396 * @param all if true, return descriptors for all classes; if false, only
397 * return descriptors for serializable classes
398 */
399 static ObjectStreamClass lookup(Class<?> cl, boolean all) {
400 if (!(all || Serializable.class.isAssignableFrom(cl))) {
401 return null;
402 }
403 return Caches.localDescs.get(cl);
404 }
405
406 /**
407 * Creates local class descriptor representing given class.
408 */
409 private ObjectStreamClass(final Class<?> cl) {
410 this.cl = cl;
411 name = cl.getName();
412 isProxy = Proxy.isProxyClass(cl);
413 isEnum = Enum.class.isAssignableFrom(cl);
414 isRecord = cl.isRecord();
415 isValue = cl.isValue();
416 serializable = Serializable.class.isAssignableFrom(cl);
417 externalizable = Externalizable.class.isAssignableFrom(cl);
418
419 Class<?> superCl = cl.getSuperclass();
420 superDesc = (superCl != null) ? lookup(superCl, false) : null;
421 localDesc = this;
422
423 if (serializable) {
424 if (isEnum) {
425 suid = 0L;
426 fields = NO_FIELDS;
427 } else if (cl.isArray()) {
428 fields = NO_FIELDS;
429 } else {
430 suid = getDeclaredSUID(cl);
431 try {
432 fields = getSerialFields(cl);
433 computeFieldOffsets();
434 } catch (InvalidClassException e) {
435 serializeEx = deserializeEx =
436 new ExceptionInfo(e.classname, e.getMessage());
437 fields = NO_FIELDS;
438 }
439
440 if (isRecord) {
441 factoryMode = DeserializationMode.READ_RECORD;
442 canonicalCtr = canonicalRecordCtr(cl);
443 deserializationCtrs = new DeserializationConstructorsCache();
444 } else if (externalizable) {
445 factoryMode = DeserializationMode.READ_EXTERNALIZABLE;
446 if (cl.isIdentity()) {
447 cons = getExternalizableConstructor(cl);
448 } else {
449 serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
450 "Externalizable not valid for value class");
451 }
452 } else if (cl.isValue()) {
453 factoryMode = DeserializationMode.READ_OBJECT_VALUE;
454 if (!cl.isAnnotationPresent(MigratedValueClass.class)) {
455 serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
456 "Value class serialization is only supported with `writeReplace`");
457 } else if (Modifier.isAbstract(cl.getModifiers())) {
458 serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
459 "value class is abstract");
460 } else {
461 // Value classes should have constructor(s) annotated with {@link DeserializeConstructor}
462 canonicalCtr = getDeserializingValueCons(cl, fields);
463 deserializationCtrs = new DeserializationConstructorsCache(); factoryMode = DeserializationMode.READ_OBJECT_VALUE;
464 if (canonicalCtr == null) {
465 serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
466 "no constructor or factory found for migrated value class");
467 }
468 }
469 } else {
470 cons = getSerializableConstructor(cl);
471 writeObjectMethod = getPrivateMethod(cl, "writeObject",
472 new Class<?>[]{ObjectOutputStream.class},
473 Void.TYPE);
474 readObjectMethod = getPrivateMethod(cl, "readObject",
475 new Class<?>[]{ObjectInputStream.class},
476 Void.TYPE);
477 readObjectNoDataMethod = getPrivateMethod(
478 cl, "readObjectNoData", null, Void.TYPE);
479 hasWriteObjectData = (writeObjectMethod != null);
480 factoryMode = ((superDesc == null || superDesc.factoryMode() == DeserializationMode.READ_OBJECT_DEFAULT)
481 && readObjectMethod == null && readObjectNoDataMethod == null)
482 ? DeserializationMode.READ_OBJECT_DEFAULT
483 : DeserializationMode.READ_OBJECT_CUSTOM;
484 }
485 writeReplaceMethod = getInheritableMethod(
486 cl, "writeReplace", null, Object.class);
487 readResolveMethod = getInheritableMethod(
488 cl, "readResolve", null, Object.class);
489 }
490 } else {
491 suid = 0L;
492 fields = NO_FIELDS;
493 }
494
495 try {
496 fieldRefl = getReflector(fields, this);
497 } catch (InvalidClassException ex) {
498 // field mismatches impossible when matching local fields vs. self
499 throw new InternalError(ex);
500 }
501
502 if (deserializeEx == null) {
503 if (isEnum) {
504 deserializeEx = new ExceptionInfo(name, "enum type");
505 } else if (cons == null && !(isRecord | isValue)) {
506 deserializeEx = new ExceptionInfo(name, "no valid constructor");
507 }
508 }
509 if (isRecord && canonicalCtr == null) {
510 deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
511 } else {
512 for (int i = 0; i < fields.length; i++) {
513 if (fields[i].getField() == null) {
514 defaultSerializeEx = new ExceptionInfo(
515 name, "unmatched serializable field(s) declared");
516 }
517 }
518 }
519 initialized = true;
520
521 if (SerializationMisdeclarationEvent.enabled() && serializable) {
522 SerializationMisdeclarationChecker.checkMisdeclarations(cl);
523 }
524 }
525
544 if (!osc.isProxy) {
545 throw new InvalidClassException(
546 "cannot bind proxy descriptor to a non-proxy class");
547 }
548 }
549 this.cl = cl;
550 this.resolveEx = resolveEx;
551 this.superDesc = superDesc;
552 isProxy = true;
553 serializable = true;
554 suid = 0L;
555 fields = NO_FIELDS;
556 if (osc != null) {
557 localDesc = osc;
558 name = localDesc.name;
559 externalizable = localDesc.externalizable;
560 writeReplaceMethod = localDesc.writeReplaceMethod;
561 readResolveMethod = localDesc.readResolveMethod;
562 deserializeEx = localDesc.deserializeEx;
563 cons = localDesc.cons;
564 factoryMode = localDesc.factoryMode;
565 } else {
566 factoryMode = DeserializationMode.READ_OBJECT_DEFAULT;
567 }
568 fieldRefl = getReflector(fields, localDesc);
569 initialized = true;
570 }
571
572 /**
573 * Initializes class descriptor representing a non-proxy class.
574 */
575 void initNonProxy(ObjectStreamClass model,
576 Class<?> cl,
577 ClassNotFoundException resolveEx,
578 ObjectStreamClass superDesc)
579 throws InvalidClassException
580 {
581 long suid = model.getSerialVersionUID();
582 ObjectStreamClass osc = null;
583 if (cl != null) {
584 osc = lookup(cl, true);
585 if (osc.isProxy) {
586 throw new InvalidClassException(
625 }
626
627 this.cl = cl;
628 this.resolveEx = resolveEx;
629 this.superDesc = superDesc;
630 name = model.name;
631 this.suid = suid;
632 isProxy = false;
633 isEnum = model.isEnum;
634 serializable = model.serializable;
635 externalizable = model.externalizable;
636 hasBlockExternalData = model.hasBlockExternalData;
637 hasWriteObjectData = model.hasWriteObjectData;
638 fields = model.fields;
639 primDataSize = model.primDataSize;
640 numObjFields = model.numObjFields;
641
642 if (osc != null) {
643 localDesc = osc;
644 isRecord = localDesc.isRecord;
645 isValue = localDesc.isValue;
646 // canonical record constructor is shared
647 canonicalCtr = localDesc.canonicalCtr;
648 // cache of deserialization constructors is shared
649 deserializationCtrs = localDesc.deserializationCtrs;
650 writeObjectMethod = localDesc.writeObjectMethod;
651 readObjectMethod = localDesc.readObjectMethod;
652 readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
653 writeReplaceMethod = localDesc.writeReplaceMethod;
654 readResolveMethod = localDesc.readResolveMethod;
655 if (deserializeEx == null) {
656 deserializeEx = localDesc.deserializeEx;
657 }
658 assert cl.isRecord() ? localDesc.cons == null : true;
659 cons = localDesc.cons;
660 factoryMode = localDesc.factoryMode;
661 } else {
662 // No local class, read data using only the schema from the stream
663 factoryMode = (externalizable)
664 ? DeserializationMode.READ_EXTERNALIZABLE
665 : DeserializationMode.READ_NO_LOCAL_CLASS;
666 }
667
668 fieldRefl = getReflector(fields, localDesc);
669 // reassign to matched fields so as to reflect local unshared settings
670 fields = fieldRefl.getFields();
671
672 initialized = true;
673 }
674
675 /**
676 * Reads non-proxy class descriptor information from given input stream.
677 * The resulting class descriptor is not fully functional; it can only be
678 * used as input to the ObjectInputStream.resolveClass() and
679 * ObjectStreamClass.initNonProxy() methods.
680 */
681 void readNonProxy(ObjectInputStream in)
682 throws IOException, ClassNotFoundException
683 {
684 name = in.readUTF();
685 suid = in.readLong();
701 serializable = externalizable || sflag;
702 isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
703 if (isEnum && suid.longValue() != 0L) {
704 throw new InvalidClassException(name,
705 "enum descriptor has non-zero serialVersionUID: " + suid);
706 }
707
708 int numFields = in.readShort();
709 if (isEnum && numFields != 0) {
710 throw new InvalidClassException(name,
711 "enum descriptor has non-zero field count: " + numFields);
712 }
713 fields = (numFields > 0) ?
714 new ObjectStreamField[numFields] : NO_FIELDS;
715 for (int i = 0; i < numFields; i++) {
716 char tcode = (char) in.readByte();
717 String fname = in.readUTF();
718 String signature = ((tcode == 'L') || (tcode == '[')) ?
719 in.readTypeString() : String.valueOf(tcode);
720 try {
721 fields[i] = new ObjectStreamField(fname, signature, false, -1);
722 } catch (RuntimeException e) {
723 throw new InvalidClassException(name,
724 "invalid descriptor for field " +
725 fname, e);
726 }
727 }
728 computeFieldOffsets();
729 }
730
731 /**
732 * Writes non-proxy class descriptor information to given output stream.
733 */
734 void writeNonProxy(ObjectOutputStream out) throws IOException {
735 out.writeUTF(name);
736 out.writeLong(getSerialVersionUID());
737
738 byte flags = 0;
739 if (externalizable) {
740 flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
741 int protocol = out.getProtocolVersion();
912 }
913
914 /**
915 * Returns true if represented class implements Externalizable, false
916 * otherwise.
917 */
918 boolean isExternalizable() {
919 requireInitialized();
920 return externalizable;
921 }
922
923 /**
924 * Returns true if represented class implements Serializable, false
925 * otherwise.
926 */
927 boolean isSerializable() {
928 requireInitialized();
929 return serializable;
930 }
931
932 /**
933 * {@return {code true} if the class is a value class, {@code false} otherwise}
934 */
935 boolean isValue() {
936 requireInitialized();
937 return isValue;
938 }
939
940 /**
941 * {@return the factory mode for deserialization}
942 */
943 DeserializationMode factoryMode() {
944 requireInitialized();
945 return factoryMode;
946 }
947
948 /**
949 * Returns true if class descriptor represents externalizable class that
950 * has written its data in 1.2 (block data) format, false otherwise.
951 */
952 boolean hasBlockExternalData() {
953 requireInitialized();
954 return hasBlockExternalData;
955 }
956
957 /**
958 * Returns true if class descriptor represents serializable (but not
959 * externalizable) class which has written its data via a custom
960 * writeObject() method, false otherwise.
961 */
962 boolean hasWriteObjectData() {
963 requireInitialized();
964 return hasWriteObjectData;
965 }
966
967 /**
968 * Returns true if represented class is serializable/externalizable and can
969 * be instantiated by the serialization runtime--i.e., if it is
970 * externalizable and defines a public no-arg constructor, if it is
971 * non-externalizable and its first non-serializable superclass defines an
972 * accessible no-arg constructor, or if the class is a value class with a @DeserializeConstructor
973 * constructor or static factory.
974 * Otherwise, returns false.
975 */
976 boolean isInstantiable() {
977 requireInitialized();
978 return (cons != null || (isValue() && canonicalCtr != null));
979 }
980
981 /**
982 * Returns true if represented class is serializable (but not
983 * externalizable) and defines a conformant writeObject method. Otherwise,
984 * returns false.
985 */
986 boolean hasWriteObjectMethod() {
987 requireInitialized();
988 return (writeObjectMethod != null);
989 }
990
991 /**
992 * Returns true if represented class is serializable (but not
993 * externalizable) and defines a conformant readObject method. Otherwise,
994 * returns false.
995 */
996 boolean hasReadObjectMethod() {
997 requireInitialized();
998 return (readObjectMethod != null);
1215 /**
1216 * Class representing the portion of an object's serialized form allotted
1217 * to data described by a given class descriptor. If "hasData" is false,
1218 * the object's serialized form does not contain data associated with the
1219 * class descriptor.
1220 */
1221 static class ClassDataSlot {
1222
1223 /** class descriptor "occupying" this slot */
1224 final ObjectStreamClass desc;
1225 /** true if serialized form includes data for this slot's descriptor */
1226 final boolean hasData;
1227
1228 ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
1229 this.desc = desc;
1230 this.hasData = hasData;
1231 }
1232 }
1233
1234 /**
1235 * Returns a List of ClassDataSlot instances representing the data layout
1236 * (including superclass data) for serialized objects described by this
1237 * class descriptor. ClassDataSlots are ordered by inheritance with those
1238 * containing "higher" superclasses appearing first. The final
1239 * ClassDataSlot contains a reference to this descriptor.
1240 */
1241 List<ClassDataSlot> getClassDataLayout() throws InvalidClassException {
1242 // REMIND: synchronize instead of relying on volatile?
1243 List<ClassDataSlot> layout = dataLayout;
1244 if (layout != null)
1245 return layout;
1246
1247 ArrayList<ClassDataSlot> slots = new ArrayList<>();
1248 Class<?> start = cl, end = cl;
1249
1250 // locate closest non-serializable superclass
1251 while (end != null && Serializable.class.isAssignableFrom(end)) {
1252 end = end.getSuperclass();
1253 }
1254
1255 HashSet<String> oscNames = new HashSet<>(3);
1256
1257 for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
1258 if (oscNames.contains(d.name)) {
1259 throw new InvalidClassException("Circular reference.");
1260 } else {
1261 oscNames.add(d.name);
1262 }
1263
1264 // search up inheritance hierarchy for class with matching name
1265 String searchName = (d.cl != null) ? d.cl.getName() : d.name;
1266 Class<?> match = null;
1275 if (match != null) {
1276 for (Class<?> c = start; c != match; c = c.getSuperclass()) {
1277 slots.add(new ClassDataSlot(
1278 ObjectStreamClass.lookup(c, true), false));
1279 }
1280 start = match.getSuperclass();
1281 }
1282
1283 // record descriptor/class pairing
1284 slots.add(new ClassDataSlot(d.getVariantFor(match), true));
1285 }
1286
1287 // add "no data" slot for any leftover unmatched classes
1288 for (Class<?> c = start; c != end; c = c.getSuperclass()) {
1289 slots.add(new ClassDataSlot(
1290 ObjectStreamClass.lookup(c, true), false));
1291 }
1292
1293 // order slots from superclass -> subclass
1294 Collections.reverse(slots);
1295 dataLayout = slots;
1296 return slots;
1297 }
1298
1299 /**
1300 * Returns aggregate size (in bytes) of marshalled primitive field values
1301 * for represented class.
1302 */
1303 int getPrimDataSize() {
1304 return primDataSize;
1305 }
1306
1307 /**
1308 * Returns number of non-primitive serializable fields of represented
1309 * class.
1310 */
1311 int getNumObjFields() {
1312 return numObjFields;
1313 }
1314
1315 /**
1316 * Fetches the serializable primitive field values of object obj and
1404 /**
1405 * If given class is the same as the class associated with this class
1406 * descriptor, returns reference to this class descriptor. Otherwise,
1407 * returns variant of this class descriptor bound to given class.
1408 */
1409 private ObjectStreamClass getVariantFor(Class<?> cl)
1410 throws InvalidClassException
1411 {
1412 if (this.cl == cl) {
1413 return this;
1414 }
1415 ObjectStreamClass desc = new ObjectStreamClass();
1416 if (isProxy) {
1417 desc.initProxy(cl, null, superDesc);
1418 } else {
1419 desc.initNonProxy(this, cl, null, superDesc);
1420 }
1421 return desc;
1422 }
1423
1424 /**
1425 * Return a method handle for the static method or constructor(s) that matches the
1426 * serializable fields and annotated with {@link DeserializeConstructor}.
1427 * The descriptor for the class is still being initialized, so is passed the fields needed.
1428 * @param clazz The class to query
1429 * @param fields the serializable fields of the class
1430 * @return a MethodHandle, null if none found
1431 */
1432 @SuppressWarnings("unchecked")
1433 private static MethodHandle getDeserializingValueCons(Class<?> clazz,
1434 ObjectStreamField[] fields) {
1435 // Search for annotated static factory in methods or constructors
1436 MethodHandles.Lookup lookup = MethodHandles.lookup();
1437 MethodHandle mh = Stream.concat(
1438 Arrays.stream(clazz.getDeclaredMethods()).filter(m -> Modifier.isStatic(m.getModifiers())),
1439 Arrays.stream(clazz.getDeclaredConstructors()))
1440 .filter(m -> m.isAnnotationPresent(DeserializeConstructor.class))
1441 .map(m -> {
1442 try {
1443 m.setAccessible(true);
1444 return (m instanceof Constructor<?> cons)
1445 ? lookup.unreflectConstructor(cons)
1446 : lookup.unreflect(((Method) m));
1447 } catch (IllegalAccessException iae) {
1448 throw new InternalError(iae); // should not occur after setAccessible
1449 }})
1450 .filter(m -> matchFactoryParamTypes(clazz, m, fields))
1451 .findFirst().orElse(null);
1452 return mh;
1453 }
1454
1455 /**
1456 * Check that the parameters of the factory method match the fields of this class.
1457 *
1458 * @param mh a MethodHandle for a constructor or factory
1459 * @return true if all fields match the parameters, false if not
1460 */
1461 private static boolean matchFactoryParamTypes(Class<?> clazz,
1462 MethodHandle mh,
1463 ObjectStreamField[] fields) {
1464 var params = mh.type().parameterList();
1465 if (params.size() != fields.length) {
1466 return false; // Mismatch in count of fields and parameters
1467 }
1468 for (ObjectStreamField field : fields) {
1469 int argIndex = field.getArgIndex();
1470 final Class<?> paramtype = params.get(argIndex);
1471 if (!field.getType().equals(paramtype)) {
1472 return false;
1473 }
1474 }
1475 return true;
1476 }
1477
1478 /**
1479 * Returns public no-arg constructor of given class, or null if none found.
1480 * Access checks are disabled on the returned constructor (if any), since
1481 * the defining class may still be non-public.
1482 */
1483 private static Constructor<?> getExternalizableConstructor(Class<?> cl) {
1484 try {
1485 Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
1486 cons.setAccessible(true);
1487 return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
1488 cons : null;
1489 } catch (NoSuchMethodException | InaccessibleObjectException ex) {
1490 return null;
1491 }
1492 }
1493
1494 /**
1495 * Returns subclass-accessible no-arg constructor of first non-serializable
1496 * superclass, or null if none found. Access checks are disabled on the
1497 * returned constructor (if any).
1498 */
1499 private static Constructor<?> getSerializableConstructor(Class<?> cl) {
1500 return ReflectionFactory.getReflectionFactory().newConstructorForSerialization(cl);
1501 }
1502
1503 /**
1504 * Returns the canonical constructor for the given record class, or null if
1505 * the not found ( which should never happen for correctly generated record
1506 * classes ).
1507 */
1508 private static MethodHandle canonicalRecordCtr(Class<?> cls) {
1509 assert cls.isRecord() : "Expected record, got: " + cls;
1700
1701 ObjectStreamField[] boundFields =
1702 new ObjectStreamField[serialPersistentFields.length];
1703 Set<String> fieldNames = HashSet.newHashSet(serialPersistentFields.length);
1704
1705 for (int i = 0; i < serialPersistentFields.length; i++) {
1706 ObjectStreamField spf = serialPersistentFields[i];
1707
1708 String fname = spf.getName();
1709 if (fieldNames.contains(fname)) {
1710 throw new InvalidClassException(
1711 "multiple serializable fields named " + fname);
1712 }
1713 fieldNames.add(fname);
1714
1715 try {
1716 Field f = cl.getDeclaredField(fname);
1717 if ((f.getType() == spf.getType()) &&
1718 ((f.getModifiers() & Modifier.STATIC) == 0))
1719 {
1720 boundFields[i] = new ObjectStreamField(f, spf.isUnshared(), true, i);
1721 }
1722 } catch (NoSuchFieldException ex) {
1723 }
1724 if (boundFields[i] == null) {
1725 boundFields[i] = new ObjectStreamField(fname, spf.getType(), spf.isUnshared(), i);
1726 }
1727 }
1728 return boundFields;
1729 }
1730
1731 /**
1732 * Returns array of ObjectStreamFields corresponding to all non-static
1733 * non-transient fields declared by given class. Each ObjectStreamField
1734 * contains a Field object for the field it represents. If no default
1735 * serializable fields exist, NO_FIELDS is returned.
1736 */
1737 private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
1738 Field[] clFields = cl.getDeclaredFields();
1739 ArrayList<ObjectStreamField> list = new ArrayList<>();
1740 int mask = Modifier.STATIC | Modifier.TRANSIENT;
1741
1742 for (int i = 0, argIndex = 0; i < clFields.length; i++) {
1743 if ((clFields[i].getModifiers() & mask) == 0) {
1744 list.add(new ObjectStreamField(clFields[i], false, true, argIndex++));
1745 }
1746 }
1747 int size = list.size();
1748 return (size == 0) ? NO_FIELDS :
1749 list.toArray(new ObjectStreamField[size]);
1750 }
1751
1752 /**
1753 * Returns explicit serial version UID value declared by given class, or
1754 * null if none.
1755 */
1756 private static Long getDeclaredSUID(Class<?> cl) {
1757 try {
1758 Field f = cl.getDeclaredField("serialVersionUID");
1759 int mask = Modifier.STATIC | Modifier.FINAL;
1760 if ((f.getModifiers() & mask) == mask) {
1761 f.setAccessible(true);
1762 return f.getLong(null);
1763 }
1764 } catch (Exception ex) {
1951
1952 /**
1953 * Class for setting and retrieving serializable field values in batch.
1954 */
1955 // REMIND: dynamically generate these?
1956 private static final class FieldReflector {
1957
1958 /** handle for performing unsafe operations */
1959 private static final Unsafe UNSAFE = Unsafe.getUnsafe();
1960
1961 /** fields to operate on */
1962 private final ObjectStreamField[] fields;
1963 /** number of primitive fields */
1964 private final int numPrimFields;
1965 /** unsafe field keys for reading fields - may contain dupes */
1966 private final long[] readKeys;
1967 /** unsafe fields keys for writing fields - no dupes */
1968 private final long[] writeKeys;
1969 /** field data offsets */
1970 private final int[] offsets;
1971 /** field layouts */
1972 private final int[] layouts;
1973 /** field type codes */
1974 private final char[] typeCodes;
1975 /** field types */
1976 private final Class<?>[] types;
1977
1978 /**
1979 * Constructs FieldReflector capable of setting/getting values from the
1980 * subset of fields whose ObjectStreamFields contain non-null
1981 * reflective Field objects. ObjectStreamFields with null Fields are
1982 * treated as filler, for which get operations return default values
1983 * and set operations discard given values.
1984 */
1985 FieldReflector(ObjectStreamField[] fields) {
1986 this.fields = fields;
1987 int nfields = fields.length;
1988 readKeys = new long[nfields];
1989 writeKeys = new long[nfields];
1990 offsets = new int[nfields];
1991 layouts = new int[nfields];
1992 typeCodes = new char[nfields];
1993 ArrayList<Class<?>> typeList = new ArrayList<>();
1994 Set<Long> usedKeys = new HashSet<>();
1995
1996
1997 for (int i = 0; i < nfields; i++) {
1998 ObjectStreamField f = fields[i];
1999 Field rf = f.getField();
2000 long key = (rf != null) ?
2001 UNSAFE.objectFieldOffset(rf) : Unsafe.INVALID_FIELD_OFFSET;
2002 readKeys[i] = key;
2003 writeKeys[i] = usedKeys.add(key) ?
2004 key : Unsafe.INVALID_FIELD_OFFSET;
2005 offsets[i] = f.getOffset();
2006 layouts[i] = rf != null ? UNSAFE.fieldLayout(rf) : 0;
2007 typeCodes[i] = f.getTypeCode();
2008 if (!f.isPrimitive()) {
2009 typeList.add((rf != null) ? rf.getType() : null);
2010 }
2011 }
2012
2013 types = typeList.toArray(new Class<?>[typeList.size()]);
2014 numPrimFields = nfields - types.length;
2015 }
2016
2017 /**
2018 * Returns list of ObjectStreamFields representing fields operated on
2019 * by this reflector. The shared/unshared values and Field objects
2020 * contained by ObjectStreamFields in the list reflect their bindings
2021 * to locally defined serializable fields.
2022 */
2023 ObjectStreamField[] getFields() {
2024 return fields;
2025 }
2026
2080 case 'D' -> UNSAFE.putDouble(obj, key, ByteArray.getDouble(buf, off));
2081 default -> throw new InternalError();
2082 }
2083 }
2084 }
2085
2086 /**
2087 * Fetches the serializable object field values of object obj and
2088 * stores them in array vals starting at offset 0. The caller is
2089 * responsible for ensuring that obj is of the proper type.
2090 */
2091 void getObjFieldValues(Object obj, Object[] vals) {
2092 if (obj == null) {
2093 throw new NullPointerException();
2094 }
2095 /* assuming checkDefaultSerialize() has been called on the class
2096 * descriptor this FieldReflector was obtained from, no field keys
2097 * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
2098 */
2099 for (int i = numPrimFields; i < fields.length; i++) {
2100 Field f = fields[i].getField();
2101 vals[offsets[i]] = switch (typeCodes[i]) {
2102 case 'L', '[' ->
2103 layouts[i] != 0
2104 ? UNSAFE.getFlatValue(obj, readKeys[i], layouts[i], f.getType())
2105 : UNSAFE.getReference(obj, readKeys[i]);
2106 default -> throw new InternalError();
2107 };
2108 }
2109 }
2110
2111 /**
2112 * Checks that the given values, from array vals starting at offset 0,
2113 * are assignable to the given serializable object fields.
2114 * @throws ClassCastException if any value is not assignable
2115 */
2116 void checkObjectFieldValueTypes(Object obj, Object[] vals) {
2117 setObjFieldValues(obj, vals, true);
2118 }
2119
2120 /**
2121 * Sets the serializable object fields of object obj using values from
2122 * array vals starting at offset 0. The caller is responsible for
2123 * ensuring that obj is of the proper type; however, attempts to set a
2124 * field with a value of the wrong type will trigger an appropriate
2125 * ClassCastException.
2126 */
2127 void setObjFieldValues(Object obj, Object[] vals) {
2128 setObjFieldValues(obj, vals, false);
2129 }
2130
2131 private void setObjFieldValues(Object obj, Object[] vals, boolean dryRun) {
2132 if (obj == null && !dryRun) {
2133 throw new NullPointerException();
2134 }
2135 for (int i = numPrimFields; i < fields.length; i++) {
2136 long key = writeKeys[i];
2137 if (key == Unsafe.INVALID_FIELD_OFFSET) {
2138 continue; // discard value
2139 }
2140 switch (typeCodes[i]) {
2141 case 'L', '[' -> {
2142 Field f = fields[i].getField();
2143 Object val = vals[offsets[i]];
2144 if (val != null &&
2145 !types[i - numPrimFields].isInstance(val))
2146 {
2147 throw new ClassCastException(
2148 "cannot assign instance of " +
2149 val.getClass().getName() + " to field " +
2150 f.getDeclaringClass().getName() + "." +
2151 f.getName() + " of type " +
2152 f.getType().getName() + " in instance of " +
2153 obj.getClass().getName());
2154 }
2155 if (!dryRun) {
2156 if (layouts[i] != 0) {
2157 UNSAFE.putFlatValue(obj, key, layouts[i], f.getType(), val);
2158 } else {
2159 UNSAFE.putReference(obj, key, val);
2160 }
2161 }
2162 }
2163 default -> throw new InternalError();
2164 }
2165 }
2166 }
2167 }
2168
2169 /**
2170 * Matches given set of serializable fields with serializable fields
2171 * described by the given local class descriptor, and returns a
2172 * FieldReflector instance capable of setting/getting values from the
2173 * subset of fields that match (non-matching fields are treated as filler,
2174 * for which get operations return default values and set operations
2175 * discard given values). Throws InvalidClassException if unresolvable
2176 * type conflicts exist between the two sets of fields.
2177 */
2178 private static FieldReflector getReflector(ObjectStreamField[] fields,
2179 ObjectStreamClass localDesc)
2180 throws InvalidClassException
2181 {
2253 * a non-local class descriptor. To preserve this (questionable)
2254 * behavior, the ObjectStreamField instances returned by matchFields
2255 * cannot report non-primitive types other than Object.class; hence
2256 * localFields cannot be returned directly.
2257 */
2258
2259 ObjectStreamField[] matches = new ObjectStreamField[fields.length];
2260 for (int i = 0; i < fields.length; i++) {
2261 ObjectStreamField f = fields[i], m = null;
2262 for (int j = 0; j < localFields.length; j++) {
2263 ObjectStreamField lf = localFields[j];
2264 if (f.getName().equals(lf.getName())) {
2265 if ((f.isPrimitive() || lf.isPrimitive()) &&
2266 f.getTypeCode() != lf.getTypeCode())
2267 {
2268 throw new InvalidClassException(localDesc.name,
2269 "incompatible types for field " + f.getName());
2270 }
2271 if (lf.getField() != null) {
2272 m = new ObjectStreamField(
2273 lf.getField(), lf.isUnshared(), true, lf.getArgIndex()); // Don't hide type
2274 } else {
2275 m = new ObjectStreamField(
2276 lf.getName(), lf.getSignature(), lf.isUnshared(), lf.getArgIndex());
2277 }
2278 }
2279 }
2280 if (m == null) {
2281 m = new ObjectStreamField(
2282 f.getName(), f.getSignature(), false, -1);
2283 }
2284 m.setOffset(f.getOffset());
2285 matches[i] = m;
2286 }
2287 return matches;
2288 }
2289
2290 /**
2291 * A LRA cache of record deserialization constructors.
2292 */
2293 @SuppressWarnings("serial")
2294 private static final class DeserializationConstructorsCache
2295 extends ConcurrentHashMap<DeserializationConstructorsCache.Key, MethodHandle> {
2296
2297 // keep max. 10 cached entries - when the 11th element is inserted the oldest
2298 // is removed and 10 remains - 11 is the biggest map size where internal
2299 // table of 16 elements is sufficient (inserting 12th element would resize it to 32)
2300 private static final int MAX_SIZE = 10;
2301 private Key.Impl first, last; // first and last in FIFO queue
2302
2384 this.fieldTypes = new Class<?>[fields.length];
2385 for (int i = 0; i < fields.length; i++) {
2386 fieldNames[i] = fields[i].getName();
2387 fieldTypes[i] = fields[i].getType();
2388 }
2389 }
2390
2391 @Override
2392 int length() { return fieldNames.length; }
2393
2394 @Override
2395 String fieldName(int i) { return fieldNames[i]; }
2396
2397 @Override
2398 Class<?> fieldType(int i) { return fieldTypes[i]; }
2399 }
2400 }
2401 }
2402
2403 /** Record specific support for retrieving and binding stream field values. */
2404 static final class ConstructorSupport {
2405 /**
2406 * Returns canonical record constructor adapted to take two arguments:
2407 * {@code (byte[] primValues, Object[] objValues)}
2408 * and return
2409 * {@code Object}
2410 */
2411 static MethodHandle deserializationCtr(ObjectStreamClass desc) {
2412 // check the cached value 1st
2413 MethodHandle mh = desc.deserializationCtr;
2414 if (mh != null) return mh;
2415 mh = desc.deserializationCtrs.get(desc.getFields(false));
2416 if (mh != null) return desc.deserializationCtr = mh;
2417
2418 // retrieve record components
2419 RecordComponent[] recordComponents = desc.forClass().getRecordComponents();
2420
2421 // retrieve the canonical constructor
2422 // (T1, T2, ..., Tn):TR
2423 mh = desc.getRecordConstructor();
2424
2433 for (int i = recordComponents.length-1; i >= 0; i--) {
2434 String name = recordComponents[i].getName();
2435 Class<?> type = recordComponents[i].getType();
2436 // obtain stream field extractor that extracts argument at
2437 // position i (Ti+1) from primValues and objValues arrays
2438 // (byte[], Object[]):Ti+1
2439 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2440 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2441 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2442 mh = MethodHandles.foldArguments(mh, i, combiner);
2443 }
2444 // what we are left with is a MethodHandle taking just the primValues
2445 // and objValues arrays and returning the constructed record instance
2446 // (byte[], Object[]):Object
2447
2448 // store it into cache and return the 1st value stored
2449 return desc.deserializationCtr =
2450 desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
2451 }
2452
2453 /**
2454 * Returns value object constructor adapted to take two arguments:
2455 * {@code (byte[] primValues, Object[] objValues)} and return {@code Object}
2456 */
2457 static MethodHandle deserializationValueCons(ObjectStreamClass desc) {
2458 // check the cached value 1st
2459 MethodHandle mh = desc.deserializationCtr;
2460 if (mh != null) return mh;
2461 mh = desc.deserializationCtrs.get(desc.getFields(false));
2462 if (mh != null) return desc.deserializationCtr = mh;
2463
2464 // retrieve the selected constructor
2465 // (T1, T2, ..., Tn):TR
2466 ObjectStreamClass localDesc = desc.localDesc;
2467 mh = localDesc.canonicalCtr;
2468 MethodType mt = mh.type();
2469
2470 // change return type to Object
2471 // (T1, T2, ..., Tn):TR -> (T1, T2, ..., Tn):Object
2472 mh = mh.asType(mh.type().changeReturnType(Object.class));
2473
2474 // drop last 2 arguments representing primValues and objValues arrays
2475 // (T1, T2, ..., Tn):Object -> (T1, T2, ..., Tn, byte[], Object[]):Object
2476 mh = MethodHandles.dropArguments(mh, mh.type().parameterCount(), byte[].class, Object[].class);
2477
2478 Class<?>[] params = mt.parameterArray();
2479 for (int i = params.length-1; i >= 0; i--) {
2480 // Get the name from the local descriptor matching the argIndex
2481 var field = getFieldForArgIndex(localDesc, i);
2482 String name = (field == null) ? "" : field.getName(); // empty string to supply default
2483 Class<?> type = params[i];
2484 // obtain stream field extractor that extracts argument at
2485 // position i (Ti+1) from primValues and objValues arrays
2486 // (byte[], Object[]):Ti+1
2487 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2488 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2489 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2490 mh = MethodHandles.foldArguments(mh, i, combiner);
2491 }
2492 // what we are left with is a MethodHandle taking just the primValues
2493 // and objValues arrays and returning the constructed instance
2494 // (byte[], Object[]):Object
2495
2496 // store it into cache and return the 1st value stored
2497 return desc.deserializationCtr =
2498 desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
2499 }
2500
2501 // Find the ObjectStreamField for the argument index, otherwise null
2502 private static ObjectStreamField getFieldForArgIndex(ObjectStreamClass desc, int argIndex) {
2503 for (var field : desc.fields) {
2504 if (field.getArgIndex() == argIndex)
2505 return field;
2506 }
2507 return null;
2508 }
2509
2510 /** Returns the number of primitive fields for the given descriptor. */
2511 private static int numberPrimValues(ObjectStreamClass desc) {
2512 ObjectStreamField[] fields = desc.getFields();
2513 int primValueCount = 0;
2514 for (int i = 0; i < fields.length; i++) {
2515 if (fields[i].isPrimitive())
2516 primValueCount++;
2517 else
2518 break; // can be no more
2519 }
2520 return primValueCount;
2521 }
2522
2523 /**
2524 * Returns extractor MethodHandle taking the primValues and objValues arrays
2525 * and extracting the argument of canonical constructor with given name and type
2526 * or producing default value for the given type if the field is absent.
2527 */
2528 private static MethodHandle streamFieldExtractor(String pName,
2529 Class<?> pType,
|