1 /*
2 * Copyright (c) 1996, 2023, 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.UndeclaredThrowableException;
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.AccessControlContext;
41 import java.security.AccessController;
42 import java.security.MessageDigest;
43 import java.security.NoSuchAlgorithmException;
44 import java.security.PermissionCollection;
45 import java.security.Permissions;
46 import java.security.PrivilegedAction;
47 import java.security.PrivilegedActionException;
48 import java.security.PrivilegedExceptionAction;
49 import java.security.ProtectionDomain;
50 import java.util.ArrayList;
51 import java.util.Arrays;
52 import java.util.Collections;
53 import java.util.Comparator;
54 import java.util.HashSet;
55 import java.util.Map;
56 import java.util.Set;
57 import java.util.concurrent.ConcurrentHashMap;
58
59 import jdk.internal.event.SerializationMisdeclarationEvent;
60 import jdk.internal.misc.Unsafe;
61 import jdk.internal.reflect.CallerSensitive;
62 import jdk.internal.reflect.Reflection;
63 import jdk.internal.reflect.ReflectionFactory;
64 import jdk.internal.access.SharedSecrets;
65 import jdk.internal.access.JavaSecurityAccess;
66 import jdk.internal.util.ByteArray;
67 import sun.reflect.misc.ReflectUtil;
68
69 /**
70 * Serialization's descriptor for classes. It contains the name and
71 * serialVersionUID of the class. The ObjectStreamClass for a specific class
72 * loaded in this Java VM can be found/created using the lookup method.
73 *
74 * <p>The algorithm to compute the SerialVersionUID is described in
75 * <a href="{@docRoot}/../specs/serialization/class.html#stream-unique-identifiers">
76 * <cite>Java Object Serialization Specification,</cite> Section 4.6, "Stream Unique Identifiers"</a>.
77 *
78 * @spec serialization/index.html Java Object Serialization Specification
79 * @author Mike Warres
80 * @author Roger Riggs
81 * @see ObjectStreamField
82 * @see <a href="{@docRoot}/../specs/serialization/class.html">
83 * <cite>Java Object Serialization Specification,</cite> Section 4, "Class Descriptors"</a>
84 * @since 1.1
85 */
86 public final class ObjectStreamClass implements Serializable {
87
88 /** serialPersistentFields value indicating no serializable fields */
89 public static final ObjectStreamField[] NO_FIELDS =
90 new ObjectStreamField[0];
91
92 @java.io.Serial
93 private static final long serialVersionUID = -6120832682080437368L;
94 /**
95 * {@code ObjectStreamClass} has no fields for default serialization.
96 */
97 @java.io.Serial
98 private static final ObjectStreamField[] serialPersistentFields =
99 NO_FIELDS;
100
101 /** reflection factory for obtaining serialization constructors */
102 @SuppressWarnings("removal")
103 private static final ReflectionFactory reflFactory =
104 AccessController.doPrivileged(
105 new ReflectionFactory.GetReflectionFactoryAction());
106
107 private static class Caches {
108 /** cache mapping local classes -> descriptors */
109 static final ClassCache<ObjectStreamClass> localDescs =
110 new ClassCache<>() {
111 @Override
112 protected ObjectStreamClass computeValue(Class<?> type) {
113 return new ObjectStreamClass(type);
114 }
115 };
116
117 /** cache mapping field group/local desc pairs -> field reflectors */
118 static final ClassCache<Map<FieldReflectorKey, FieldReflector>> reflectors =
119 new ClassCache<>() {
120 @Override
121 protected Map<FieldReflectorKey, FieldReflector> computeValue(Class<?> type) {
122 return new ConcurrentHashMap<>();
123 }
124 };
125 }
126
127 /** class associated with this descriptor (if any) */
128 private Class<?> cl;
129 /** name of class represented by this descriptor */
130 private String name;
131 /** serialVersionUID of represented class (null if not computed yet) */
132 private volatile Long suid;
133
134 /** true if represents dynamic proxy class */
135 private boolean isProxy;
136 /** true if represents enum type */
137 private boolean isEnum;
138 /** true if represents record type */
139 private boolean isRecord;
140 /** true if represented class implements Serializable */
141 private boolean serializable;
142 /** true if represented class implements Externalizable */
143 private boolean externalizable;
144 /** true if desc has data written by class-defined writeObject method */
145 private boolean hasWriteObjectData;
146 /**
147 * true if desc has externalizable data written in block data format; this
148 * must be true by default to accommodate ObjectInputStream subclasses which
149 * override readClassDescriptor() to return class descriptors obtained from
150 * ObjectStreamClass.lookup() (see 4461737)
151 */
152 private boolean hasBlockExternalData = true;
153
154 /**
155 * Contains information about InvalidClassException instances to be thrown
156 * when attempting operations on an invalid class. Note that instances of
157 * this class are immutable and are potentially shared among
158 * ObjectStreamClass instances.
159 */
177 }
178
179 /** exception (if any) thrown while attempting to resolve class */
180 private ClassNotFoundException resolveEx;
181 /** exception (if any) to throw if non-enum deserialization attempted */
182 private ExceptionInfo deserializeEx;
183 /** exception (if any) to throw if non-enum serialization attempted */
184 private ExceptionInfo serializeEx;
185 /** exception (if any) to throw if default serialization attempted */
186 private ExceptionInfo defaultSerializeEx;
187
188 /** serializable fields */
189 private ObjectStreamField[] fields;
190 /** aggregate marshalled size of primitive fields */
191 private int primDataSize;
192 /** number of non-primitive fields */
193 private int numObjFields;
194 /** reflector for setting/getting serializable field values */
195 private FieldReflector fieldRefl;
196 /** data layout of serialized objects described by this class desc */
197 private volatile ClassDataSlot[] dataLayout;
198
199 /** serialization-appropriate constructor, or null if none */
200 private Constructor<?> cons;
201 /** record canonical constructor (shared among OSCs for same class), or null */
202 private MethodHandle canonicalCtr;
203 /** cache of record deserialization constructors per unique set of stream fields
204 * (shared among OSCs for same class), or null */
205 private DeserializationConstructorsCache deserializationCtrs;
206 /** session-cache of record deserialization constructor
207 * (in de-serialized OSC only), or null */
208 private MethodHandle deserializationCtr;
209 /** protection domains that need to be checked when calling the constructor */
210 private ProtectionDomain[] domains;
211
212 /** class-defined writeObject method, or null if none */
213 private Method writeObjectMethod;
214 /** class-defined readObject method, or null if none */
215 private Method readObjectMethod;
216 /** class-defined readObjectNoData method, or null if none */
217 private Method readObjectNoDataMethod;
359 * @param all if true, return descriptors for all classes; if false, only
360 * return descriptors for serializable classes
361 */
362 static ObjectStreamClass lookup(Class<?> cl, boolean all) {
363 if (!(all || Serializable.class.isAssignableFrom(cl))) {
364 return null;
365 }
366 return Caches.localDescs.get(cl);
367 }
368
369 /**
370 * Creates local class descriptor representing given class.
371 */
372 @SuppressWarnings("removal")
373 private ObjectStreamClass(final Class<?> cl) {
374 this.cl = cl;
375 name = cl.getName();
376 isProxy = Proxy.isProxyClass(cl);
377 isEnum = Enum.class.isAssignableFrom(cl);
378 isRecord = cl.isRecord();
379 serializable = Serializable.class.isAssignableFrom(cl);
380 externalizable = Externalizable.class.isAssignableFrom(cl);
381
382 Class<?> superCl = cl.getSuperclass();
383 superDesc = (superCl != null) ? lookup(superCl, false) : null;
384 localDesc = this;
385
386 if (serializable) {
387 AccessController.doPrivileged(new PrivilegedAction<>() {
388 public Void run() {
389 if (isEnum) {
390 suid = 0L;
391 fields = NO_FIELDS;
392 return null;
393 }
394 if (cl.isArray()) {
395 fields = NO_FIELDS;
396 return null;
397 }
398
399 suid = getDeclaredSUID(cl);
400 try {
401 fields = getSerialFields(cl);
402 computeFieldOffsets();
403 } catch (InvalidClassException e) {
404 serializeEx = deserializeEx =
405 new ExceptionInfo(e.classname, e.getMessage());
406 fields = NO_FIELDS;
407 }
408
409 if (isRecord) {
410 canonicalCtr = canonicalRecordCtr(cl);
411 deserializationCtrs = new DeserializationConstructorsCache();
412 } else if (externalizable) {
413 cons = getExternalizableConstructor(cl);
414 } else {
415 cons = getSerializableConstructor(cl);
416 writeObjectMethod = getPrivateMethod(cl, "writeObject",
417 new Class<?>[] { ObjectOutputStream.class },
418 Void.TYPE);
419 readObjectMethod = getPrivateMethod(cl, "readObject",
420 new Class<?>[] { ObjectInputStream.class },
421 Void.TYPE);
422 readObjectNoDataMethod = getPrivateMethod(
423 cl, "readObjectNoData", null, Void.TYPE);
424 hasWriteObjectData = (writeObjectMethod != null);
425 }
426 domains = getProtectionDomains(cons, cl);
427 writeReplaceMethod = getInheritableMethod(
428 cl, "writeReplace", null, Object.class);
429 readResolveMethod = getInheritableMethod(
430 cl, "readResolve", null, Object.class);
431 return null;
432 }
433 });
434 } else {
435 suid = 0L;
436 fields = NO_FIELDS;
437 }
438
439 try {
440 fieldRefl = getReflector(fields, this);
441 } catch (InvalidClassException ex) {
442 // field mismatches impossible when matching local fields vs. self
443 throw new InternalError(ex);
444 }
445
446 if (deserializeEx == null) {
447 if (isEnum) {
448 deserializeEx = new ExceptionInfo(name, "enum type");
449 } else if (cons == null && !isRecord) {
450 deserializeEx = new ExceptionInfo(name, "no valid constructor");
451 }
452 }
453 if (isRecord && canonicalCtr == null) {
454 deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
455 } else {
456 for (int i = 0; i < fields.length; i++) {
457 if (fields[i].getField() == null) {
458 defaultSerializeEx = new ExceptionInfo(
459 name, "unmatched serializable field(s) declared");
460 }
461 }
462 }
463 initialized = true;
464
465 if (SerializationMisdeclarationEvent.enabled() && serializable) {
466 SerializationMisdeclarationChecker.checkMisdeclarations(cl);
467 }
468 }
469
549 throw new InvalidClassException(
550 "cannot bind proxy descriptor to a non-proxy class");
551 }
552 }
553 this.cl = cl;
554 this.resolveEx = resolveEx;
555 this.superDesc = superDesc;
556 isProxy = true;
557 serializable = true;
558 suid = 0L;
559 fields = NO_FIELDS;
560 if (osc != null) {
561 localDesc = osc;
562 name = localDesc.name;
563 externalizable = localDesc.externalizable;
564 writeReplaceMethod = localDesc.writeReplaceMethod;
565 readResolveMethod = localDesc.readResolveMethod;
566 deserializeEx = localDesc.deserializeEx;
567 domains = localDesc.domains;
568 cons = localDesc.cons;
569 }
570 fieldRefl = getReflector(fields, localDesc);
571 initialized = true;
572 }
573
574 /**
575 * Initializes class descriptor representing a non-proxy class.
576 */
577 void initNonProxy(ObjectStreamClass model,
578 Class<?> cl,
579 ClassNotFoundException resolveEx,
580 ObjectStreamClass superDesc)
581 throws InvalidClassException
582 {
583 long suid = model.getSerialVersionUID();
584 ObjectStreamClass osc = null;
585 if (cl != null) {
586 osc = lookup(cl, true);
587 if (osc.isProxy) {
588 throw new InvalidClassException(
627 }
628
629 this.cl = cl;
630 this.resolveEx = resolveEx;
631 this.superDesc = superDesc;
632 name = model.name;
633 this.suid = suid;
634 isProxy = false;
635 isEnum = model.isEnum;
636 serializable = model.serializable;
637 externalizable = model.externalizable;
638 hasBlockExternalData = model.hasBlockExternalData;
639 hasWriteObjectData = model.hasWriteObjectData;
640 fields = model.fields;
641 primDataSize = model.primDataSize;
642 numObjFields = model.numObjFields;
643
644 if (osc != null) {
645 localDesc = osc;
646 isRecord = localDesc.isRecord;
647 // canonical record constructor is shared
648 canonicalCtr = localDesc.canonicalCtr;
649 // cache of deserialization constructors is shared
650 deserializationCtrs = localDesc.deserializationCtrs;
651 writeObjectMethod = localDesc.writeObjectMethod;
652 readObjectMethod = localDesc.readObjectMethod;
653 readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
654 writeReplaceMethod = localDesc.writeReplaceMethod;
655 readResolveMethod = localDesc.readResolveMethod;
656 if (deserializeEx == null) {
657 deserializeEx = localDesc.deserializeEx;
658 }
659 domains = localDesc.domains;
660 assert cl.isRecord() ? localDesc.cons == null : true;
661 cons = localDesc.cons;
662 }
663
664 fieldRefl = getReflector(fields, localDesc);
665 // reassign to matched fields so as to reflect local unshared settings
666 fields = fieldRefl.getFields();
667
668 initialized = true;
669 }
670
671 /**
672 * Reads non-proxy class descriptor information from given input stream.
673 * The resulting class descriptor is not fully functional; it can only be
674 * used as input to the ObjectInputStream.resolveClass() and
675 * ObjectStreamClass.initNonProxy() methods.
676 */
677 void readNonProxy(ObjectInputStream in)
678 throws IOException, ClassNotFoundException
679 {
680 name = in.readUTF();
681 suid = in.readLong();
697 serializable = externalizable || sflag;
698 isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
699 if (isEnum && suid.longValue() != 0L) {
700 throw new InvalidClassException(name,
701 "enum descriptor has non-zero serialVersionUID: " + suid);
702 }
703
704 int numFields = in.readShort();
705 if (isEnum && numFields != 0) {
706 throw new InvalidClassException(name,
707 "enum descriptor has non-zero field count: " + numFields);
708 }
709 fields = (numFields > 0) ?
710 new ObjectStreamField[numFields] : NO_FIELDS;
711 for (int i = 0; i < numFields; i++) {
712 char tcode = (char) in.readByte();
713 String fname = in.readUTF();
714 String signature = ((tcode == 'L') || (tcode == '[')) ?
715 in.readTypeString() : String.valueOf(tcode);
716 try {
717 fields[i] = new ObjectStreamField(fname, signature, false);
718 } catch (RuntimeException e) {
719 throw new InvalidClassException(name,
720 "invalid descriptor for field " +
721 fname, e);
722 }
723 }
724 computeFieldOffsets();
725 }
726
727 /**
728 * Writes non-proxy class descriptor information to given output stream.
729 */
730 void writeNonProxy(ObjectOutputStream out) throws IOException {
731 out.writeUTF(name);
732 out.writeLong(getSerialVersionUID());
733
734 byte flags = 0;
735 if (externalizable) {
736 flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
737 int protocol = out.getProtocolVersion();
908 }
909
910 /**
911 * Returns true if represented class implements Externalizable, false
912 * otherwise.
913 */
914 boolean isExternalizable() {
915 requireInitialized();
916 return externalizable;
917 }
918
919 /**
920 * Returns true if represented class implements Serializable, false
921 * otherwise.
922 */
923 boolean isSerializable() {
924 requireInitialized();
925 return serializable;
926 }
927
928 /**
929 * Returns true if class descriptor represents externalizable class that
930 * has written its data in 1.2 (block data) format, false otherwise.
931 */
932 boolean hasBlockExternalData() {
933 requireInitialized();
934 return hasBlockExternalData;
935 }
936
937 /**
938 * Returns true if class descriptor represents serializable (but not
939 * externalizable) class which has written its data via a custom
940 * writeObject() method, false otherwise.
941 */
942 boolean hasWriteObjectData() {
943 requireInitialized();
944 return hasWriteObjectData;
945 }
946
947 /**
948 * Returns true if represented class is serializable/externalizable and can
949 * be instantiated by the serialization runtime--i.e., if it is
950 * externalizable and defines a public no-arg constructor, or if it is
951 * non-externalizable and its first non-serializable superclass defines an
952 * accessible no-arg constructor. Otherwise, returns false.
953 */
954 boolean isInstantiable() {
955 requireInitialized();
956 return (cons != null);
957 }
958
959 /**
960 * Returns true if represented class is serializable (but not
961 * externalizable) and defines a conformant writeObject method. Otherwise,
962 * returns false.
963 */
964 boolean hasWriteObjectMethod() {
965 requireInitialized();
966 return (writeObjectMethod != null);
967 }
968
969 /**
970 * Returns true if represented class is serializable (but not
971 * externalizable) and defines a conformant readObject method. Otherwise,
972 * returns false.
973 */
974 boolean hasReadObjectMethod() {
975 requireInitialized();
976 return (readObjectMethod != null);
1222 /**
1223 * Class representing the portion of an object's serialized form allotted
1224 * to data described by a given class descriptor. If "hasData" is false,
1225 * the object's serialized form does not contain data associated with the
1226 * class descriptor.
1227 */
1228 static class ClassDataSlot {
1229
1230 /** class descriptor "occupying" this slot */
1231 final ObjectStreamClass desc;
1232 /** true if serialized form includes data for this slot's descriptor */
1233 final boolean hasData;
1234
1235 ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
1236 this.desc = desc;
1237 this.hasData = hasData;
1238 }
1239 }
1240
1241 /**
1242 * Returns array of ClassDataSlot instances representing the data layout
1243 * (including superclass data) for serialized objects described by this
1244 * class descriptor. ClassDataSlots are ordered by inheritance with those
1245 * containing "higher" superclasses appearing first. The final
1246 * ClassDataSlot contains a reference to this descriptor.
1247 */
1248 ClassDataSlot[] getClassDataLayout() throws InvalidClassException {
1249 // REMIND: synchronize instead of relying on volatile?
1250 if (dataLayout == null) {
1251 dataLayout = getClassDataLayout0();
1252 }
1253 return dataLayout;
1254 }
1255
1256 private ClassDataSlot[] getClassDataLayout0()
1257 throws InvalidClassException
1258 {
1259 ArrayList<ClassDataSlot> slots = new ArrayList<>();
1260 Class<?> start = cl, end = cl;
1261
1262 // locate closest non-serializable superclass
1263 while (end != null && Serializable.class.isAssignableFrom(end)) {
1264 end = end.getSuperclass();
1265 }
1266
1267 HashSet<String> oscNames = new HashSet<>(3);
1268
1269 for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
1270 if (oscNames.contains(d.name)) {
1271 throw new InvalidClassException("Circular reference.");
1272 } else {
1273 oscNames.add(d.name);
1274 }
1275
1276 // search up inheritance hierarchy for class with matching name
1277 String searchName = (d.cl != null) ? d.cl.getName() : d.name;
1278 Class<?> match = null;
1287 if (match != null) {
1288 for (Class<?> c = start; c != match; c = c.getSuperclass()) {
1289 slots.add(new ClassDataSlot(
1290 ObjectStreamClass.lookup(c, true), false));
1291 }
1292 start = match.getSuperclass();
1293 }
1294
1295 // record descriptor/class pairing
1296 slots.add(new ClassDataSlot(d.getVariantFor(match), true));
1297 }
1298
1299 // add "no data" slot for any leftover unmatched classes
1300 for (Class<?> c = start; c != end; c = c.getSuperclass()) {
1301 slots.add(new ClassDataSlot(
1302 ObjectStreamClass.lookup(c, true), false));
1303 }
1304
1305 // order slots from superclass -> subclass
1306 Collections.reverse(slots);
1307 return slots.toArray(new ClassDataSlot[slots.size()]);
1308 }
1309
1310 /**
1311 * Returns aggregate size (in bytes) of marshalled primitive field values
1312 * for represented class.
1313 */
1314 int getPrimDataSize() {
1315 return primDataSize;
1316 }
1317
1318 /**
1319 * Returns number of non-primitive serializable fields of represented
1320 * class.
1321 */
1322 int getNumObjFields() {
1323 return numObjFields;
1324 }
1325
1326 /**
1327 * Fetches the serializable primitive field values of object obj and
1415 /**
1416 * If given class is the same as the class associated with this class
1417 * descriptor, returns reference to this class descriptor. Otherwise,
1418 * returns variant of this class descriptor bound to given class.
1419 */
1420 private ObjectStreamClass getVariantFor(Class<?> cl)
1421 throws InvalidClassException
1422 {
1423 if (this.cl == cl) {
1424 return this;
1425 }
1426 ObjectStreamClass desc = new ObjectStreamClass();
1427 if (isProxy) {
1428 desc.initProxy(cl, null, superDesc);
1429 } else {
1430 desc.initNonProxy(this, cl, null, superDesc);
1431 }
1432 return desc;
1433 }
1434
1435 /**
1436 * Returns public no-arg constructor of given class, or null if none found.
1437 * Access checks are disabled on the returned constructor (if any), since
1438 * the defining class may still be non-public.
1439 */
1440 private static Constructor<?> getExternalizableConstructor(Class<?> cl) {
1441 try {
1442 Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
1443 cons.setAccessible(true);
1444 return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
1445 cons : null;
1446 } catch (NoSuchMethodException ex) {
1447 return null;
1448 }
1449 }
1450
1451 /**
1452 * Returns subclass-accessible no-arg constructor of first non-serializable
1453 * superclass, or null if none found. Access checks are disabled on the
1454 * returned constructor (if any).
1455 */
1456 private static Constructor<?> getSerializableConstructor(Class<?> cl) {
1457 return reflFactory.newConstructorForSerialization(cl);
1458 }
1459
1460 /**
1461 * Returns the canonical constructor for the given record class, or null if
1462 * the not found ( which should never happen for correctly generated record
1463 * classes ).
1464 */
1465 @SuppressWarnings("removal")
1466 private static MethodHandle canonicalRecordCtr(Class<?> cls) {
1661
1662 ObjectStreamField[] boundFields =
1663 new ObjectStreamField[serialPersistentFields.length];
1664 Set<String> fieldNames = HashSet.newHashSet(serialPersistentFields.length);
1665
1666 for (int i = 0; i < serialPersistentFields.length; i++) {
1667 ObjectStreamField spf = serialPersistentFields[i];
1668
1669 String fname = spf.getName();
1670 if (fieldNames.contains(fname)) {
1671 throw new InvalidClassException(
1672 "multiple serializable fields named " + fname);
1673 }
1674 fieldNames.add(fname);
1675
1676 try {
1677 Field f = cl.getDeclaredField(fname);
1678 if ((f.getType() == spf.getType()) &&
1679 ((f.getModifiers() & Modifier.STATIC) == 0))
1680 {
1681 boundFields[i] =
1682 new ObjectStreamField(f, spf.isUnshared(), true);
1683 }
1684 } catch (NoSuchFieldException ex) {
1685 }
1686 if (boundFields[i] == null) {
1687 boundFields[i] = new ObjectStreamField(
1688 fname, spf.getType(), spf.isUnshared());
1689 }
1690 }
1691 return boundFields;
1692 }
1693
1694 /**
1695 * Returns array of ObjectStreamFields corresponding to all non-static
1696 * non-transient fields declared by given class. Each ObjectStreamField
1697 * contains a Field object for the field it represents. If no default
1698 * serializable fields exist, NO_FIELDS is returned.
1699 */
1700 private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
1701 Field[] clFields = cl.getDeclaredFields();
1702 ArrayList<ObjectStreamField> list = new ArrayList<>();
1703 int mask = Modifier.STATIC | Modifier.TRANSIENT;
1704
1705 for (int i = 0; i < clFields.length; i++) {
1706 if ((clFields[i].getModifiers() & mask) == 0) {
1707 list.add(new ObjectStreamField(clFields[i], false, true));
1708 }
1709 }
1710 int size = list.size();
1711 return (size == 0) ? NO_FIELDS :
1712 list.toArray(new ObjectStreamField[size]);
1713 }
1714
1715 /**
1716 * Returns explicit serial version UID value declared by given class, or
1717 * null if none.
1718 */
1719 private static Long getDeclaredSUID(Class<?> cl) {
1720 try {
1721 Field f = cl.getDeclaredField("serialVersionUID");
1722 int mask = Modifier.STATIC | Modifier.FINAL;
1723 if ((f.getModifiers() & mask) == mask) {
1724 f.setAccessible(true);
1725 return f.getLong(null);
1726 }
1727 } catch (Exception ex) {
2039 case 'D' -> UNSAFE.putDouble(obj, key, ByteArray.getDouble(buf, off));
2040 default -> throw new InternalError();
2041 }
2042 }
2043 }
2044
2045 /**
2046 * Fetches the serializable object field values of object obj and
2047 * stores them in array vals starting at offset 0. The caller is
2048 * responsible for ensuring that obj is of the proper type.
2049 */
2050 void getObjFieldValues(Object obj, Object[] vals) {
2051 if (obj == null) {
2052 throw new NullPointerException();
2053 }
2054 /* assuming checkDefaultSerialize() has been called on the class
2055 * descriptor this FieldReflector was obtained from, no field keys
2056 * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
2057 */
2058 for (int i = numPrimFields; i < fields.length; i++) {
2059 vals[offsets[i]] = switch (typeCodes[i]) {
2060 case 'L', '[' -> UNSAFE.getReference(obj, readKeys[i]);
2061 default -> throw new InternalError();
2062 };
2063 }
2064 }
2065
2066 /**
2067 * Checks that the given values, from array vals starting at offset 0,
2068 * are assignable to the given serializable object fields.
2069 * @throws ClassCastException if any value is not assignable
2070 */
2071 void checkObjectFieldValueTypes(Object obj, Object[] vals) {
2072 setObjFieldValues(obj, vals, true);
2073 }
2074
2075 /**
2076 * Sets the serializable object fields of object obj using values from
2077 * array vals starting at offset 0. The caller is responsible for
2078 * ensuring that obj is of the proper type; however, attempts to set a
2079 * field with a value of the wrong type will trigger an appropriate
2080 * ClassCastException.
2081 */
2082 void setObjFieldValues(Object obj, Object[] vals) {
2083 setObjFieldValues(obj, vals, false);
2084 }
2085
2086 private void setObjFieldValues(Object obj, Object[] vals, boolean dryRun) {
2087 if (obj == null) {
2088 throw new NullPointerException();
2089 }
2090 for (int i = numPrimFields; i < fields.length; i++) {
2091 long key = writeKeys[i];
2092 if (key == Unsafe.INVALID_FIELD_OFFSET) {
2093 continue; // discard value
2094 }
2095 switch (typeCodes[i]) {
2096 case 'L', '[' -> {
2097 Object val = vals[offsets[i]];
2098 if (val != null &&
2099 !types[i - numPrimFields].isInstance(val))
2100 {
2101 Field f = fields[i].getField();
2102 throw new ClassCastException(
2103 "cannot assign instance of " +
2104 val.getClass().getName() + " to field " +
2105 f.getDeclaringClass().getName() + "." +
2106 f.getName() + " of type " +
2107 f.getType().getName() + " in instance of " +
2108 obj.getClass().getName());
2109 }
2110 if (!dryRun)
2111 UNSAFE.putReference(obj, key, val);
2112 }
2113 default -> throw new InternalError();
2114 }
2115 }
2116 }
2117 }
2118
2119 /**
2120 * Matches given set of serializable fields with serializable fields
2121 * described by the given local class descriptor, and returns a
2122 * FieldReflector instance capable of setting/getting values from the
2123 * subset of fields that match (non-matching fields are treated as filler,
2124 * for which get operations return default values and set operations
2125 * discard given values). Throws InvalidClassException if unresolvable
2126 * type conflicts exist between the two sets of fields.
2127 */
2128 private static FieldReflector getReflector(ObjectStreamField[] fields,
2129 ObjectStreamClass localDesc)
2130 throws InvalidClassException
2131 {
2203 * a non-local class descriptor. To preserve this (questionable)
2204 * behavior, the ObjectStreamField instances returned by matchFields
2205 * cannot report non-primitive types other than Object.class; hence
2206 * localFields cannot be returned directly.
2207 */
2208
2209 ObjectStreamField[] matches = new ObjectStreamField[fields.length];
2210 for (int i = 0; i < fields.length; i++) {
2211 ObjectStreamField f = fields[i], m = null;
2212 for (int j = 0; j < localFields.length; j++) {
2213 ObjectStreamField lf = localFields[j];
2214 if (f.getName().equals(lf.getName())) {
2215 if ((f.isPrimitive() || lf.isPrimitive()) &&
2216 f.getTypeCode() != lf.getTypeCode())
2217 {
2218 throw new InvalidClassException(localDesc.name,
2219 "incompatible types for field " + f.getName());
2220 }
2221 if (lf.getField() != null) {
2222 m = new ObjectStreamField(
2223 lf.getField(), lf.isUnshared(), false);
2224 } else {
2225 m = new ObjectStreamField(
2226 lf.getName(), lf.getSignature(), lf.isUnshared());
2227 }
2228 }
2229 }
2230 if (m == null) {
2231 m = new ObjectStreamField(
2232 f.getName(), f.getSignature(), false);
2233 }
2234 m.setOffset(f.getOffset());
2235 matches[i] = m;
2236 }
2237 return matches;
2238 }
2239
2240 /**
2241 * A LRA cache of record deserialization constructors.
2242 */
2243 @SuppressWarnings("serial")
2244 private static final class DeserializationConstructorsCache
2245 extends ConcurrentHashMap<DeserializationConstructorsCache.Key, MethodHandle> {
2246
2247 // keep max. 10 cached entries - when the 11th element is inserted the oldest
2248 // is removed and 10 remains - 11 is the biggest map size where internal
2249 // table of 16 elements is sufficient (inserting 12th element would resize it to 32)
2250 private static final int MAX_SIZE = 10;
2251 private Key.Impl first, last; // first and last in FIFO queue
2252
2334 this.fieldTypes = new Class<?>[fields.length];
2335 for (int i = 0; i < fields.length; i++) {
2336 fieldNames[i] = fields[i].getName();
2337 fieldTypes[i] = fields[i].getType();
2338 }
2339 }
2340
2341 @Override
2342 int length() { return fieldNames.length; }
2343
2344 @Override
2345 String fieldName(int i) { return fieldNames[i]; }
2346
2347 @Override
2348 Class<?> fieldType(int i) { return fieldTypes[i]; }
2349 }
2350 }
2351 }
2352
2353 /** Record specific support for retrieving and binding stream field values. */
2354 static final class RecordSupport {
2355 /**
2356 * Returns canonical record constructor adapted to take two arguments:
2357 * {@code (byte[] primValues, Object[] objValues)}
2358 * and return
2359 * {@code Object}
2360 */
2361 @SuppressWarnings("removal")
2362 static MethodHandle deserializationCtr(ObjectStreamClass desc) {
2363 // check the cached value 1st
2364 MethodHandle mh = desc.deserializationCtr;
2365 if (mh != null) return mh;
2366 mh = desc.deserializationCtrs.get(desc.getFields(false));
2367 if (mh != null) return desc.deserializationCtr = mh;
2368
2369 // retrieve record components
2370 RecordComponent[] recordComponents;
2371 try {
2372 Class<?> cls = desc.forClass();
2373 PrivilegedExceptionAction<RecordComponent[]> pa = cls::getRecordComponents;
2374 recordComponents = AccessController.doPrivileged(pa);
2391 for (int i = recordComponents.length-1; i >= 0; i--) {
2392 String name = recordComponents[i].getName();
2393 Class<?> type = recordComponents[i].getType();
2394 // obtain stream field extractor that extracts argument at
2395 // position i (Ti+1) from primValues and objValues arrays
2396 // (byte[], Object[]):Ti+1
2397 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2398 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2399 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2400 mh = MethodHandles.foldArguments(mh, i, combiner);
2401 }
2402 // what we are left with is a MethodHandle taking just the primValues
2403 // and objValues arrays and returning the constructed record instance
2404 // (byte[], Object[]):Object
2405
2406 // store it into cache and return the 1st value stored
2407 return desc.deserializationCtr =
2408 desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
2409 }
2410
2411 /** Returns the number of primitive fields for the given descriptor. */
2412 private static int numberPrimValues(ObjectStreamClass desc) {
2413 ObjectStreamField[] fields = desc.getFields();
2414 int primValueCount = 0;
2415 for (int i = 0; i < fields.length; i++) {
2416 if (fields[i].isPrimitive())
2417 primValueCount++;
2418 else
2419 break; // can be no more
2420 }
2421 return primValueCount;
2422 }
2423
2424 /**
2425 * Returns extractor MethodHandle taking the primValues and objValues arrays
2426 * and extracting the argument of canonical constructor with given name and type
2427 * or producing default value for the given type if the field is absent.
2428 */
2429 private static MethodHandle streamFieldExtractor(String pName,
2430 Class<?> pType,
|
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.InaccessibleObjectException;
34 import java.lang.reflect.InvocationTargetException;
35 import java.lang.reflect.RecordComponent;
36 import java.lang.reflect.UndeclaredThrowableException;
37 import java.lang.reflect.Member;
38 import java.lang.reflect.Method;
39 import java.lang.reflect.Modifier;
40 import java.lang.reflect.Proxy;
41 import java.security.AccessControlContext;
42 import java.security.AccessController;
43 import java.security.MessageDigest;
44 import java.security.NoSuchAlgorithmException;
45 import java.security.PermissionCollection;
46 import java.security.Permissions;
47 import java.security.PrivilegedAction;
48 import java.security.PrivilegedActionException;
49 import java.security.PrivilegedExceptionAction;
50 import java.security.ProtectionDomain;
51 import java.util.ArrayList;
52 import java.util.Arrays;
53 import java.util.Collections;
54 import java.util.Comparator;
55 import java.util.HashSet;
56 import java.util.List;
57 import java.util.Map;
58 import java.util.Set;
59 import java.util.concurrent.ConcurrentHashMap;
60 import java.util.stream.Stream;
61
62 import jdk.internal.MigratedValueClass;
63 import jdk.internal.event.SerializationMisdeclarationEvent;
64 import jdk.internal.misc.Unsafe;
65 import jdk.internal.reflect.CallerSensitive;
66 import jdk.internal.reflect.Reflection;
67 import jdk.internal.reflect.ReflectionFactory;
68 import jdk.internal.access.SharedSecrets;
69 import jdk.internal.access.JavaSecurityAccess;
70 import jdk.internal.util.ByteArray;
71 import jdk.internal.value.DeserializeConstructor;
72 import sun.reflect.misc.ReflectUtil;
73
74 import static java.io.ObjectInputStream.TRACE;
75
76 /**
77 * Serialization's descriptor for classes. It contains the name and
78 * serialVersionUID of the class. The ObjectStreamClass for a specific class
79 * loaded in this Java VM can be found/created using the lookup method.
80 *
81 * <p>The algorithm to compute the SerialVersionUID is described in
82 * <a href="{@docRoot}/../specs/serialization/class.html#stream-unique-identifiers">
83 * <cite>Java Object Serialization Specification</cite>, Section 4.6, "Stream Unique Identifiers"</a>.
84 *
85 * @spec serialization/index.html Java Object Serialization Specification
86 * @author Mike Warres
87 * @author Roger Riggs
88 * @see ObjectStreamField
89 * @see <a href="{@docRoot}/../specs/serialization/class.html">
90 * <cite>Java Object Serialization Specification,</cite> Section 4, "Class Descriptors"</a>
91 * @since 1.1
92 */
93 public final class ObjectStreamClass implements Serializable {
94
95 /** serialPersistentFields value indicating no serializable fields */
96 public static final ObjectStreamField[] NO_FIELDS =
97 new ObjectStreamField[0];
98
99 @java.io.Serial
100 private static final long serialVersionUID = -6120832682080437368L;
101 /**
102 * {@code ObjectStreamClass} has no fields for default serialization.
103 */
104 @java.io.Serial
105 private static final ObjectStreamField[] serialPersistentFields =
106 NO_FIELDS;
107
108 /** reflection factory for obtaining serialization constructors */
109 @SuppressWarnings("removal")
110 private static final ReflectionFactory reflFactory =
111 AccessController.doPrivileged(
112 new ReflectionFactory.GetReflectionFactoryAction());
113
114 /**
115 * The mode of deserialization for a class depending on its type and interfaces.
116 * The markers used are {@linkplain java.io.Serializable}, {@linkplain java.io.Externalizable},
117 * Class.isRecord(), Class.isValue(), constructors, and
118 * the presence of methods `readObject`, `writeObject`, `readObjectNoData`, `writeObject`.
119 * ObjectInputStream dispatches on the mode to construct objects from the stream.
120 */
121 enum DeserializationMode {
122 /**
123 * Construct an object from the stream for a class that has only default read object behaviors.
124 * All classes and superclasses use defaultReadObject; no custom readObject or readObjectNoData.
125 * The new instance is entered in the handle table if it is unshared,
126 * allowing it to escape before it is initialized.
127 * For each object, all the fields are read before any are assigned.
128 * The `readObject` and `readObjectNoData` methods are not present and are not called.
129 */
130 READ_OBJECT_DEFAULT,
131 /**
132 * Creates a new object and invokes its readExternal method to read its contents.
133 * If the class is instantiable, read externalizable data by invoking readExternal()
134 * method of obj; otherwise, attempts to skip over externalizable data.
135 * Expects that passHandle is set to obj's handle before this method is
136 * called. The new object is entered in the handle table immediately,
137 * allowing it to leak before it is completely read.
138 */
139 READ_EXTERNALIZABLE,
140 /**
141 * Read all the record fields and invoke its canonical constructor.
142 * Construct the record using its canonical constructor.
143 * The new record is entered in the handle table only after the constructor returns.
144 */
145 READ_RECORD,
146 /**
147 * Fully custom read from the stream to create an instance.
148 * If the class is not instantiatable or is tagged with ClassNotFoundException
149 * the data in the stream for the class is read and discarded. {@link #READ_NO_LOCAL_CLASS}
150 * The instance is created and set in the handle table, allowing it to leak before it is initialized.
151 * For each serializable class in the stream, from superclass to subclass the
152 * stream values are read by the `readObject` method, if present, or defaultReadObject.
153 * Custom inline data is discarded if not consumed by the class `readObject` method.
154 */
155 READ_OBJECT_CUSTOM,
156 /**
157 * Construct an object by reading the values of all fields and
158 * invoking a constructor or static factory method.
159 * The constructor or static factory method is selected by matching its parameters with the
160 * sequence of field types of the serializable fields of the local class and superclasses.
161 * Invoke the constructor with all the values from the stream, inserting
162 * defaults and dropping extra values as necessary.
163 * This is very similar to the reading of records, except for the identification of
164 * the constructor or static factory.
165 */
166 READ_OBJECT_VALUE,
167 /**
168 * Read and discard an entire object, leaving a null reference in the HandleTable.
169 * The descriptor of the class in the stream is used to read the fields from the stream.
170 * There is no instance in which to store the field values.
171 * Custom data following the fields of any slot is read and discarded.
172 * References to nested objects are read and retained in the
173 * handle table using the regular mechanism.
174 * Handles later in the stream may refer to the nested objects.
175 */
176 READ_NO_LOCAL_CLASS,
177 }
178
179 private static class Caches {
180 /** cache mapping local classes -> descriptors */
181 static final ClassCache<ObjectStreamClass> localDescs =
182 new ClassCache<>() {
183 @Override
184 protected ObjectStreamClass computeValue(Class<?> type) {
185 return new ObjectStreamClass(type);
186 }
187 };
188
189 /** cache mapping field group/local desc pairs -> field reflectors */
190 static final ClassCache<Map<FieldReflectorKey, FieldReflector>> reflectors =
191 new ClassCache<>() {
192 @Override
193 protected Map<FieldReflectorKey, FieldReflector> computeValue(Class<?> type) {
194 return new ConcurrentHashMap<>();
195 }
196 };
197 }
198
199 /** class associated with this descriptor (if any) */
200 private Class<?> cl;
201 /** name of class represented by this descriptor */
202 private String name;
203 /** serialVersionUID of represented class (null if not computed yet) */
204 private volatile Long suid;
205
206 /** true if represents dynamic proxy class */
207 private boolean isProxy;
208 /** true if represents enum type */
209 private boolean isEnum;
210 /** true if represents record type */
211 private boolean isRecord;
212 /** true if represents a value class */
213 private boolean isValue;
214 /** The DeserializationMode for this class. */
215 private DeserializationMode factoryMode;
216 /** true if represented class implements Serializable */
217 private boolean serializable;
218 /** true if represented class implements Externalizable */
219 private boolean externalizable;
220 /** true if desc has data written by class-defined writeObject method */
221 private boolean hasWriteObjectData;
222 /**
223 * true if desc has externalizable data written in block data format; this
224 * must be true by default to accommodate ObjectInputStream subclasses which
225 * override readClassDescriptor() to return class descriptors obtained from
226 * ObjectStreamClass.lookup() (see 4461737)
227 */
228 private boolean hasBlockExternalData = true;
229
230 /**
231 * Contains information about InvalidClassException instances to be thrown
232 * when attempting operations on an invalid class. Note that instances of
233 * this class are immutable and are potentially shared among
234 * ObjectStreamClass instances.
235 */
253 }
254
255 /** exception (if any) thrown while attempting to resolve class */
256 private ClassNotFoundException resolveEx;
257 /** exception (if any) to throw if non-enum deserialization attempted */
258 private ExceptionInfo deserializeEx;
259 /** exception (if any) to throw if non-enum serialization attempted */
260 private ExceptionInfo serializeEx;
261 /** exception (if any) to throw if default serialization attempted */
262 private ExceptionInfo defaultSerializeEx;
263
264 /** serializable fields */
265 private ObjectStreamField[] fields;
266 /** aggregate marshalled size of primitive fields */
267 private int primDataSize;
268 /** number of non-primitive fields */
269 private int numObjFields;
270 /** reflector for setting/getting serializable field values */
271 private FieldReflector fieldRefl;
272 /** data layout of serialized objects described by this class desc */
273 private volatile List<ClassDataSlot> dataLayout;
274
275 /** serialization-appropriate constructor, or null if none */
276 private Constructor<?> cons;
277 /** record canonical constructor (shared among OSCs for same class), or null */
278 private MethodHandle canonicalCtr;
279 /** cache of record deserialization constructors per unique set of stream fields
280 * (shared among OSCs for same class), or null */
281 private DeserializationConstructorsCache deserializationCtrs;
282 /** session-cache of record deserialization constructor
283 * (in de-serialized OSC only), or null */
284 private MethodHandle deserializationCtr;
285 /** protection domains that need to be checked when calling the constructor */
286 private ProtectionDomain[] domains;
287
288 /** class-defined writeObject method, or null if none */
289 private Method writeObjectMethod;
290 /** class-defined readObject method, or null if none */
291 private Method readObjectMethod;
292 /** class-defined readObjectNoData method, or null if none */
293 private Method readObjectNoDataMethod;
435 * @param all if true, return descriptors for all classes; if false, only
436 * return descriptors for serializable classes
437 */
438 static ObjectStreamClass lookup(Class<?> cl, boolean all) {
439 if (!(all || Serializable.class.isAssignableFrom(cl))) {
440 return null;
441 }
442 return Caches.localDescs.get(cl);
443 }
444
445 /**
446 * Creates local class descriptor representing given class.
447 */
448 @SuppressWarnings("removal")
449 private ObjectStreamClass(final Class<?> cl) {
450 this.cl = cl;
451 name = cl.getName();
452 isProxy = Proxy.isProxyClass(cl);
453 isEnum = Enum.class.isAssignableFrom(cl);
454 isRecord = cl.isRecord();
455 isValue = cl.isValue();
456 serializable = Serializable.class.isAssignableFrom(cl);
457 externalizable = Externalizable.class.isAssignableFrom(cl);
458
459 Class<?> superCl = cl.getSuperclass();
460 superDesc = (superCl != null) ? lookup(superCl, false) : null;
461 localDesc = this;
462
463 if (serializable) {
464 AccessController.doPrivileged(new PrivilegedAction<>() {
465 public Void run() {
466 if (isEnum) {
467 suid = 0L;
468 fields = NO_FIELDS;
469 return null;
470 }
471 if (cl.isArray()) {
472 fields = NO_FIELDS;
473 return null;
474 }
475
476 suid = getDeclaredSUID(cl);
477 try {
478 fields = getSerialFields(cl);
479 computeFieldOffsets();
480 } catch (InvalidClassException e) {
481 serializeEx = deserializeEx =
482 new ExceptionInfo(e.classname, e.getMessage());
483 fields = NO_FIELDS;
484 }
485
486 if (isRecord) {
487 factoryMode = DeserializationMode.READ_RECORD;
488 canonicalCtr = canonicalRecordCtr(cl);
489 deserializationCtrs = new DeserializationConstructorsCache();
490 } else if (externalizable) {
491 factoryMode = DeserializationMode.READ_EXTERNALIZABLE;
492 if (cl.isIdentity()) {
493 cons = getExternalizableConstructor(cl);
494 } else {
495 serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
496 "Externalizable not valid for value class");
497 }
498 } else if (cl.isValue()) {
499 factoryMode = DeserializationMode.READ_OBJECT_VALUE;
500 if (!cl.isAnnotationPresent(MigratedValueClass.class)) {
501 serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
502 "Value class serialization is only supported with `writeReplace`");
503 } else if (Modifier.isAbstract(cl.getModifiers())) {
504 serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
505 "value class is abstract");
506 } else {
507 // Value classes should have constructor(s) annotated with {@link DeserializeConstructor}
508 canonicalCtr = getDeserializingValueCons(cl, fields);
509 deserializationCtrs = new DeserializationConstructorsCache(); factoryMode = DeserializationMode.READ_OBJECT_VALUE;
510 if (canonicalCtr == null) {
511 serializeEx = deserializeEx = new ExceptionInfo(cl.getName(),
512 "no constructor or factory found for migrated value class");
513 }
514 }
515 } else {
516 cons = getSerializableConstructor(cl);
517 writeObjectMethod = getPrivateMethod(cl, "writeObject",
518 new Class<?>[] { ObjectOutputStream.class },
519 Void.TYPE);
520 readObjectMethod = getPrivateMethod(cl, "readObject",
521 new Class<?>[] { ObjectInputStream.class },
522 Void.TYPE);
523 readObjectNoDataMethod = getPrivateMethod(
524 cl, "readObjectNoData", null, Void.TYPE);
525 hasWriteObjectData = (writeObjectMethod != null);
526 factoryMode = ((superDesc == null || superDesc.factoryMode() == DeserializationMode.READ_OBJECT_DEFAULT)
527 && readObjectMethod == null && readObjectNoDataMethod == null)
528 ? DeserializationMode.READ_OBJECT_DEFAULT
529 : DeserializationMode.READ_OBJECT_CUSTOM;
530 }
531 domains = getProtectionDomains(cons, cl);
532 writeReplaceMethod = getInheritableMethod(
533 cl, "writeReplace", null, Object.class);
534 readResolveMethod = getInheritableMethod(
535 cl, "readResolve", null, Object.class);
536 return null;
537 }
538 });
539 } else {
540 suid = 0L;
541 fields = NO_FIELDS;
542 }
543
544 try {
545 fieldRefl = getReflector(fields, this);
546 } catch (InvalidClassException ex) {
547 // field mismatches impossible when matching local fields vs. self
548 throw new InternalError(ex);
549 }
550
551 if (deserializeEx == null) {
552 if (isEnum) {
553 deserializeEx = new ExceptionInfo(name, "enum type");
554 } else if (cons == null && !(isRecord | isValue)) {
555 deserializeEx = new ExceptionInfo(name, "no valid constructor");
556 }
557 }
558 if (isRecord && canonicalCtr == null) {
559 deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
560 } else {
561 for (int i = 0; i < fields.length; i++) {
562 if (fields[i].getField() == null) {
563 defaultSerializeEx = new ExceptionInfo(
564 name, "unmatched serializable field(s) declared");
565 }
566 }
567 }
568 initialized = true;
569
570 if (SerializationMisdeclarationEvent.enabled() && serializable) {
571 SerializationMisdeclarationChecker.checkMisdeclarations(cl);
572 }
573 }
574
654 throw new InvalidClassException(
655 "cannot bind proxy descriptor to a non-proxy class");
656 }
657 }
658 this.cl = cl;
659 this.resolveEx = resolveEx;
660 this.superDesc = superDesc;
661 isProxy = true;
662 serializable = true;
663 suid = 0L;
664 fields = NO_FIELDS;
665 if (osc != null) {
666 localDesc = osc;
667 name = localDesc.name;
668 externalizable = localDesc.externalizable;
669 writeReplaceMethod = localDesc.writeReplaceMethod;
670 readResolveMethod = localDesc.readResolveMethod;
671 deserializeEx = localDesc.deserializeEx;
672 domains = localDesc.domains;
673 cons = localDesc.cons;
674 factoryMode = localDesc.factoryMode;
675 } else {
676 factoryMode = DeserializationMode.READ_OBJECT_DEFAULT;
677 }
678 fieldRefl = getReflector(fields, localDesc);
679 initialized = true;
680 }
681
682 /**
683 * Initializes class descriptor representing a non-proxy class.
684 */
685 void initNonProxy(ObjectStreamClass model,
686 Class<?> cl,
687 ClassNotFoundException resolveEx,
688 ObjectStreamClass superDesc)
689 throws InvalidClassException
690 {
691 long suid = model.getSerialVersionUID();
692 ObjectStreamClass osc = null;
693 if (cl != null) {
694 osc = lookup(cl, true);
695 if (osc.isProxy) {
696 throw new InvalidClassException(
735 }
736
737 this.cl = cl;
738 this.resolveEx = resolveEx;
739 this.superDesc = superDesc;
740 name = model.name;
741 this.suid = suid;
742 isProxy = false;
743 isEnum = model.isEnum;
744 serializable = model.serializable;
745 externalizable = model.externalizable;
746 hasBlockExternalData = model.hasBlockExternalData;
747 hasWriteObjectData = model.hasWriteObjectData;
748 fields = model.fields;
749 primDataSize = model.primDataSize;
750 numObjFields = model.numObjFields;
751
752 if (osc != null) {
753 localDesc = osc;
754 isRecord = localDesc.isRecord;
755 isValue = localDesc.isValue;
756 // canonical record constructor is shared
757 canonicalCtr = localDesc.canonicalCtr;
758 // cache of deserialization constructors is shared
759 deserializationCtrs = localDesc.deserializationCtrs;
760 writeObjectMethod = localDesc.writeObjectMethod;
761 readObjectMethod = localDesc.readObjectMethod;
762 readObjectNoDataMethod = localDesc.readObjectNoDataMethod;
763 writeReplaceMethod = localDesc.writeReplaceMethod;
764 readResolveMethod = localDesc.readResolveMethod;
765 if (deserializeEx == null) {
766 deserializeEx = localDesc.deserializeEx;
767 }
768 domains = localDesc.domains;
769 assert cl.isRecord() ? localDesc.cons == null : true;
770 cons = localDesc.cons;
771 factoryMode = localDesc.factoryMode;
772 } else {
773 // No local class, read data using only the schema from the stream
774 factoryMode = (externalizable)
775 ? DeserializationMode.READ_EXTERNALIZABLE
776 : DeserializationMode.READ_NO_LOCAL_CLASS;
777 }
778
779 fieldRefl = getReflector(fields, localDesc);
780 // reassign to matched fields so as to reflect local unshared settings
781 fields = fieldRefl.getFields();
782
783 initialized = true;
784 }
785
786 /**
787 * Reads non-proxy class descriptor information from given input stream.
788 * The resulting class descriptor is not fully functional; it can only be
789 * used as input to the ObjectInputStream.resolveClass() and
790 * ObjectStreamClass.initNonProxy() methods.
791 */
792 void readNonProxy(ObjectInputStream in)
793 throws IOException, ClassNotFoundException
794 {
795 name = in.readUTF();
796 suid = in.readLong();
812 serializable = externalizable || sflag;
813 isEnum = ((flags & ObjectStreamConstants.SC_ENUM) != 0);
814 if (isEnum && suid.longValue() != 0L) {
815 throw new InvalidClassException(name,
816 "enum descriptor has non-zero serialVersionUID: " + suid);
817 }
818
819 int numFields = in.readShort();
820 if (isEnum && numFields != 0) {
821 throw new InvalidClassException(name,
822 "enum descriptor has non-zero field count: " + numFields);
823 }
824 fields = (numFields > 0) ?
825 new ObjectStreamField[numFields] : NO_FIELDS;
826 for (int i = 0; i < numFields; i++) {
827 char tcode = (char) in.readByte();
828 String fname = in.readUTF();
829 String signature = ((tcode == 'L') || (tcode == '[')) ?
830 in.readTypeString() : String.valueOf(tcode);
831 try {
832 fields[i] = new ObjectStreamField(fname, signature, false, -1);
833 } catch (RuntimeException e) {
834 throw new InvalidClassException(name,
835 "invalid descriptor for field " +
836 fname, e);
837 }
838 }
839 computeFieldOffsets();
840 }
841
842 /**
843 * Writes non-proxy class descriptor information to given output stream.
844 */
845 void writeNonProxy(ObjectOutputStream out) throws IOException {
846 out.writeUTF(name);
847 out.writeLong(getSerialVersionUID());
848
849 byte flags = 0;
850 if (externalizable) {
851 flags |= ObjectStreamConstants.SC_EXTERNALIZABLE;
852 int protocol = out.getProtocolVersion();
1023 }
1024
1025 /**
1026 * Returns true if represented class implements Externalizable, false
1027 * otherwise.
1028 */
1029 boolean isExternalizable() {
1030 requireInitialized();
1031 return externalizable;
1032 }
1033
1034 /**
1035 * Returns true if represented class implements Serializable, false
1036 * otherwise.
1037 */
1038 boolean isSerializable() {
1039 requireInitialized();
1040 return serializable;
1041 }
1042
1043 /**
1044 * {@return {code true} if the class is a value class, {@code false} otherwise}
1045 */
1046 boolean isValue() {
1047 requireInitialized();
1048 return isValue;
1049 }
1050
1051 /**
1052 * {@return the factory mode for deserialization}
1053 */
1054 DeserializationMode factoryMode() {
1055 requireInitialized();
1056 return factoryMode;
1057 }
1058
1059 /**
1060 * Returns true if class descriptor represents externalizable class that
1061 * has written its data in 1.2 (block data) format, false otherwise.
1062 */
1063 boolean hasBlockExternalData() {
1064 requireInitialized();
1065 return hasBlockExternalData;
1066 }
1067
1068 /**
1069 * Returns true if class descriptor represents serializable (but not
1070 * externalizable) class which has written its data via a custom
1071 * writeObject() method, false otherwise.
1072 */
1073 boolean hasWriteObjectData() {
1074 requireInitialized();
1075 return hasWriteObjectData;
1076 }
1077
1078 /**
1079 * Returns true if represented class is serializable/externalizable and can
1080 * be instantiated by the serialization runtime--i.e., if it is
1081 * externalizable and defines a public no-arg constructor, if it is
1082 * non-externalizable and its first non-serializable superclass defines an
1083 * accessible no-arg constructor, or if the class is a value class with a @DeserializeConstructor
1084 * constructor or static factory.
1085 * Otherwise, returns false.
1086 */
1087 boolean isInstantiable() {
1088 requireInitialized();
1089 return (cons != null || (isValue() && canonicalCtr != null));
1090 }
1091
1092 /**
1093 * Returns true if represented class is serializable (but not
1094 * externalizable) and defines a conformant writeObject method. Otherwise,
1095 * returns false.
1096 */
1097 boolean hasWriteObjectMethod() {
1098 requireInitialized();
1099 return (writeObjectMethod != null);
1100 }
1101
1102 /**
1103 * Returns true if represented class is serializable (but not
1104 * externalizable) and defines a conformant readObject method. Otherwise,
1105 * returns false.
1106 */
1107 boolean hasReadObjectMethod() {
1108 requireInitialized();
1109 return (readObjectMethod != null);
1355 /**
1356 * Class representing the portion of an object's serialized form allotted
1357 * to data described by a given class descriptor. If "hasData" is false,
1358 * the object's serialized form does not contain data associated with the
1359 * class descriptor.
1360 */
1361 static class ClassDataSlot {
1362
1363 /** class descriptor "occupying" this slot */
1364 final ObjectStreamClass desc;
1365 /** true if serialized form includes data for this slot's descriptor */
1366 final boolean hasData;
1367
1368 ClassDataSlot(ObjectStreamClass desc, boolean hasData) {
1369 this.desc = desc;
1370 this.hasData = hasData;
1371 }
1372 }
1373
1374 /**
1375 * Returns a List of ClassDataSlot instances representing the data layout
1376 * (including superclass data) for serialized objects described by this
1377 * class descriptor. ClassDataSlots are ordered by inheritance with those
1378 * containing "higher" superclasses appearing first. The final
1379 * ClassDataSlot contains a reference to this descriptor.
1380 */
1381 List<ClassDataSlot> getClassDataLayout() throws InvalidClassException {
1382 // REMIND: synchronize instead of relying on volatile?
1383 List<ClassDataSlot> layout = dataLayout;
1384 if (layout != null)
1385 return layout;
1386
1387 ArrayList<ClassDataSlot> slots = new ArrayList<>();
1388 Class<?> start = cl, end = cl;
1389
1390 // locate closest non-serializable superclass
1391 while (end != null && Serializable.class.isAssignableFrom(end)) {
1392 end = end.getSuperclass();
1393 }
1394
1395 HashSet<String> oscNames = new HashSet<>(3);
1396
1397 for (ObjectStreamClass d = this; d != null; d = d.superDesc) {
1398 if (oscNames.contains(d.name)) {
1399 throw new InvalidClassException("Circular reference.");
1400 } else {
1401 oscNames.add(d.name);
1402 }
1403
1404 // search up inheritance hierarchy for class with matching name
1405 String searchName = (d.cl != null) ? d.cl.getName() : d.name;
1406 Class<?> match = null;
1415 if (match != null) {
1416 for (Class<?> c = start; c != match; c = c.getSuperclass()) {
1417 slots.add(new ClassDataSlot(
1418 ObjectStreamClass.lookup(c, true), false));
1419 }
1420 start = match.getSuperclass();
1421 }
1422
1423 // record descriptor/class pairing
1424 slots.add(new ClassDataSlot(d.getVariantFor(match), true));
1425 }
1426
1427 // add "no data" slot for any leftover unmatched classes
1428 for (Class<?> c = start; c != end; c = c.getSuperclass()) {
1429 slots.add(new ClassDataSlot(
1430 ObjectStreamClass.lookup(c, true), false));
1431 }
1432
1433 // order slots from superclass -> subclass
1434 Collections.reverse(slots);
1435 dataLayout = slots;
1436 return slots;
1437 }
1438
1439 /**
1440 * Returns aggregate size (in bytes) of marshalled primitive field values
1441 * for represented class.
1442 */
1443 int getPrimDataSize() {
1444 return primDataSize;
1445 }
1446
1447 /**
1448 * Returns number of non-primitive serializable fields of represented
1449 * class.
1450 */
1451 int getNumObjFields() {
1452 return numObjFields;
1453 }
1454
1455 /**
1456 * Fetches the serializable primitive field values of object obj and
1544 /**
1545 * If given class is the same as the class associated with this class
1546 * descriptor, returns reference to this class descriptor. Otherwise,
1547 * returns variant of this class descriptor bound to given class.
1548 */
1549 private ObjectStreamClass getVariantFor(Class<?> cl)
1550 throws InvalidClassException
1551 {
1552 if (this.cl == cl) {
1553 return this;
1554 }
1555 ObjectStreamClass desc = new ObjectStreamClass();
1556 if (isProxy) {
1557 desc.initProxy(cl, null, superDesc);
1558 } else {
1559 desc.initNonProxy(this, cl, null, superDesc);
1560 }
1561 return desc;
1562 }
1563
1564 /**
1565 * Return a method handle for the static method or constructor(s) that matches the
1566 * serializable fields and annotated with {@link DeserializeConstructor}.
1567 * The descriptor for the class is still being initialized, so is passed the fields needed.
1568 * @param clazz The class to query
1569 * @param fields the serializable fields of the class
1570 * @return a MethodHandle, null if none found
1571 */
1572 @SuppressWarnings("unchecked")
1573 private static MethodHandle getDeserializingValueCons(Class<?> clazz,
1574 ObjectStreamField[] fields) {
1575 // Search for annotated static factory in methods or constructors
1576 MethodHandles.Lookup lookup = MethodHandles.lookup();
1577 MethodHandle mh = Stream.concat(
1578 Arrays.stream(clazz.getDeclaredMethods()).filter(m -> Modifier.isStatic(m.getModifiers())),
1579 Arrays.stream(clazz.getDeclaredConstructors()))
1580 .filter(m -> m.isAnnotationPresent(DeserializeConstructor.class))
1581 .map(m -> {
1582 try {
1583 m.setAccessible(true);
1584 return (m instanceof Constructor<?> cons)
1585 ? lookup.unreflectConstructor(cons)
1586 : lookup.unreflect(((Method) m));
1587 } catch (IllegalAccessException iae) {
1588 throw new InternalError(iae); // should not occur after setAccessible
1589 }})
1590 .filter(m -> matchFactoryParamTypes(clazz, m, fields))
1591 .findFirst().orElse(null);
1592 TRACE("DeserializeConstructor for %s, mh: %s", clazz, mh);
1593 return mh;
1594 }
1595
1596 /**
1597 * Check that the parameters of the factory method match the fields of this class.
1598 *
1599 * @param mh a MethodHandle for a constructor or factory
1600 * @return true if all fields match the parameters, false if not
1601 */
1602 private static boolean matchFactoryParamTypes(Class<?> clazz,
1603 MethodHandle mh,
1604 ObjectStreamField[] fields) {
1605 TRACE(" matchFactoryParams checking class: %s, mh: %s", clazz, mh);
1606 var params = mh.type().parameterList();
1607 if (params.size() != fields.length) {
1608 TRACE(" matchFactoryParams %s, arg count mismatch %d params != %d fields",
1609 clazz, params.size(), fields.length);
1610 return false; // Mismatch in count of fields and parameters
1611 }
1612 for (ObjectStreamField field : fields) {
1613 int argIndex = field.getArgIndex();
1614 final Class<?> paramtype = params.get(argIndex);
1615 if (!field.getType().equals(paramtype)) {
1616 TRACE(" matchFactoryParams %s: argIndex: %d type mismatch field: %s != param: %s",
1617 clazz, argIndex, field.getType(), paramtype);
1618 return false;
1619 }
1620 }
1621 return true;
1622 }
1623
1624 /**
1625 * Returns public no-arg constructor of given class, or null if none found.
1626 * Access checks are disabled on the returned constructor (if any), since
1627 * the defining class may still be non-public.
1628 */
1629 private static Constructor<?> getExternalizableConstructor(Class<?> cl) {
1630 try {
1631 Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
1632 cons.setAccessible(true);
1633 return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
1634 cons : null;
1635 } catch (NoSuchMethodException | InaccessibleObjectException ex) {
1636 return null;
1637 }
1638 }
1639
1640 /**
1641 * Returns subclass-accessible no-arg constructor of first non-serializable
1642 * superclass, or null if none found. Access checks are disabled on the
1643 * returned constructor (if any).
1644 */
1645 private static Constructor<?> getSerializableConstructor(Class<?> cl) {
1646 return reflFactory.newConstructorForSerialization(cl);
1647 }
1648
1649 /**
1650 * Returns the canonical constructor for the given record class, or null if
1651 * the not found ( which should never happen for correctly generated record
1652 * classes ).
1653 */
1654 @SuppressWarnings("removal")
1655 private static MethodHandle canonicalRecordCtr(Class<?> cls) {
1850
1851 ObjectStreamField[] boundFields =
1852 new ObjectStreamField[serialPersistentFields.length];
1853 Set<String> fieldNames = HashSet.newHashSet(serialPersistentFields.length);
1854
1855 for (int i = 0; i < serialPersistentFields.length; i++) {
1856 ObjectStreamField spf = serialPersistentFields[i];
1857
1858 String fname = spf.getName();
1859 if (fieldNames.contains(fname)) {
1860 throw new InvalidClassException(
1861 "multiple serializable fields named " + fname);
1862 }
1863 fieldNames.add(fname);
1864
1865 try {
1866 Field f = cl.getDeclaredField(fname);
1867 if ((f.getType() == spf.getType()) &&
1868 ((f.getModifiers() & Modifier.STATIC) == 0))
1869 {
1870 boundFields[i] = new ObjectStreamField(f, spf.isUnshared(), true, i);
1871 }
1872 } catch (NoSuchFieldException ex) {
1873 }
1874 if (boundFields[i] == null) {
1875 boundFields[i] = new ObjectStreamField(fname, spf.getType(), spf.isUnshared(), i);
1876 }
1877 }
1878 return boundFields;
1879 }
1880
1881 /**
1882 * Returns array of ObjectStreamFields corresponding to all non-static
1883 * non-transient fields declared by given class. Each ObjectStreamField
1884 * contains a Field object for the field it represents. If no default
1885 * serializable fields exist, NO_FIELDS is returned.
1886 */
1887 private static ObjectStreamField[] getDefaultSerialFields(Class<?> cl) {
1888 Field[] clFields = cl.getDeclaredFields();
1889 ArrayList<ObjectStreamField> list = new ArrayList<>();
1890 int mask = Modifier.STATIC | Modifier.TRANSIENT;
1891
1892 for (int i = 0, argIndex = 0; i < clFields.length; i++) {
1893 if ((clFields[i].getModifiers() & mask) == 0) {
1894 list.add(new ObjectStreamField(clFields[i], false, true, argIndex++));
1895 }
1896 }
1897 int size = list.size();
1898 return (size == 0) ? NO_FIELDS :
1899 list.toArray(new ObjectStreamField[size]);
1900 }
1901
1902 /**
1903 * Returns explicit serial version UID value declared by given class, or
1904 * null if none.
1905 */
1906 private static Long getDeclaredSUID(Class<?> cl) {
1907 try {
1908 Field f = cl.getDeclaredField("serialVersionUID");
1909 int mask = Modifier.STATIC | Modifier.FINAL;
1910 if ((f.getModifiers() & mask) == mask) {
1911 f.setAccessible(true);
1912 return f.getLong(null);
1913 }
1914 } catch (Exception ex) {
2226 case 'D' -> UNSAFE.putDouble(obj, key, ByteArray.getDouble(buf, off));
2227 default -> throw new InternalError();
2228 }
2229 }
2230 }
2231
2232 /**
2233 * Fetches the serializable object field values of object obj and
2234 * stores them in array vals starting at offset 0. The caller is
2235 * responsible for ensuring that obj is of the proper type.
2236 */
2237 void getObjFieldValues(Object obj, Object[] vals) {
2238 if (obj == null) {
2239 throw new NullPointerException();
2240 }
2241 /* assuming checkDefaultSerialize() has been called on the class
2242 * descriptor this FieldReflector was obtained from, no field keys
2243 * in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
2244 */
2245 for (int i = numPrimFields; i < fields.length; i++) {
2246 Field f = fields[i].getField();
2247 vals[offsets[i]] = switch (typeCodes[i]) {
2248 case 'L', '[' ->
2249 UNSAFE.isFlatField(f)
2250 ? UNSAFE.getValue(obj, readKeys[i], f.getType())
2251 : UNSAFE.getReference(obj, readKeys[i]);
2252 default -> throw new InternalError();
2253 };
2254 }
2255 }
2256
2257 /**
2258 * Checks that the given values, from array vals starting at offset 0,
2259 * are assignable to the given serializable object fields.
2260 * @throws ClassCastException if any value is not assignable
2261 */
2262 void checkObjectFieldValueTypes(Object obj, Object[] vals) {
2263 setObjFieldValues(obj, vals, true);
2264 }
2265
2266 /**
2267 * Sets the serializable object fields of object obj using values from
2268 * array vals starting at offset 0. The caller is responsible for
2269 * ensuring that obj is of the proper type; however, attempts to set a
2270 * field with a value of the wrong type will trigger an appropriate
2271 * ClassCastException.
2272 */
2273 void setObjFieldValues(Object obj, Object[] vals) {
2274 setObjFieldValues(obj, vals, false);
2275 }
2276
2277 private void setObjFieldValues(Object obj, Object[] vals, boolean dryRun) {
2278 if (obj == null && !dryRun) {
2279 throw new NullPointerException();
2280 }
2281 for (int i = numPrimFields; i < fields.length; i++) {
2282 long key = writeKeys[i];
2283 if (key == Unsafe.INVALID_FIELD_OFFSET) {
2284 continue; // discard value
2285 }
2286 switch (typeCodes[i]) {
2287 case 'L', '[' -> {
2288 Field f = fields[i].getField();
2289 Object val = vals[offsets[i]];
2290 if (val != null &&
2291 !types[i - numPrimFields].isInstance(val))
2292 {
2293 throw new ClassCastException(
2294 "cannot assign instance of " +
2295 val.getClass().getName() + " to field " +
2296 f.getDeclaringClass().getName() + "." +
2297 f.getName() + " of type " +
2298 f.getType().getName() + " in instance of " +
2299 obj.getClass().getName());
2300 }
2301 if (!dryRun) {
2302 if (UNSAFE.isFlatField(f)) {
2303 UNSAFE.putValue(obj, key, f.getType(), val);
2304 } else {
2305 UNSAFE.putReference(obj, key, val);
2306 }
2307 }
2308 }
2309 default -> throw new InternalError();
2310 }
2311 }
2312 }
2313 }
2314
2315 /**
2316 * Matches given set of serializable fields with serializable fields
2317 * described by the given local class descriptor, and returns a
2318 * FieldReflector instance capable of setting/getting values from the
2319 * subset of fields that match (non-matching fields are treated as filler,
2320 * for which get operations return default values and set operations
2321 * discard given values). Throws InvalidClassException if unresolvable
2322 * type conflicts exist between the two sets of fields.
2323 */
2324 private static FieldReflector getReflector(ObjectStreamField[] fields,
2325 ObjectStreamClass localDesc)
2326 throws InvalidClassException
2327 {
2399 * a non-local class descriptor. To preserve this (questionable)
2400 * behavior, the ObjectStreamField instances returned by matchFields
2401 * cannot report non-primitive types other than Object.class; hence
2402 * localFields cannot be returned directly.
2403 */
2404
2405 ObjectStreamField[] matches = new ObjectStreamField[fields.length];
2406 for (int i = 0; i < fields.length; i++) {
2407 ObjectStreamField f = fields[i], m = null;
2408 for (int j = 0; j < localFields.length; j++) {
2409 ObjectStreamField lf = localFields[j];
2410 if (f.getName().equals(lf.getName())) {
2411 if ((f.isPrimitive() || lf.isPrimitive()) &&
2412 f.getTypeCode() != lf.getTypeCode())
2413 {
2414 throw new InvalidClassException(localDesc.name,
2415 "incompatible types for field " + f.getName());
2416 }
2417 if (lf.getField() != null) {
2418 m = new ObjectStreamField(
2419 lf.getField(), lf.isUnshared(), true, lf.getArgIndex()); // Don't hide type
2420 } else {
2421 m = new ObjectStreamField(
2422 lf.getName(), lf.getSignature(), lf.isUnshared(), lf.getArgIndex());
2423 }
2424 }
2425 }
2426 if (m == null) {
2427 m = new ObjectStreamField(
2428 f.getName(), f.getSignature(), false, -1);
2429 }
2430 m.setOffset(f.getOffset());
2431 matches[i] = m;
2432 }
2433 return matches;
2434 }
2435
2436 /**
2437 * A LRA cache of record deserialization constructors.
2438 */
2439 @SuppressWarnings("serial")
2440 private static final class DeserializationConstructorsCache
2441 extends ConcurrentHashMap<DeserializationConstructorsCache.Key, MethodHandle> {
2442
2443 // keep max. 10 cached entries - when the 11th element is inserted the oldest
2444 // is removed and 10 remains - 11 is the biggest map size where internal
2445 // table of 16 elements is sufficient (inserting 12th element would resize it to 32)
2446 private static final int MAX_SIZE = 10;
2447 private Key.Impl first, last; // first and last in FIFO queue
2448
2530 this.fieldTypes = new Class<?>[fields.length];
2531 for (int i = 0; i < fields.length; i++) {
2532 fieldNames[i] = fields[i].getName();
2533 fieldTypes[i] = fields[i].getType();
2534 }
2535 }
2536
2537 @Override
2538 int length() { return fieldNames.length; }
2539
2540 @Override
2541 String fieldName(int i) { return fieldNames[i]; }
2542
2543 @Override
2544 Class<?> fieldType(int i) { return fieldTypes[i]; }
2545 }
2546 }
2547 }
2548
2549 /** Record specific support for retrieving and binding stream field values. */
2550 static final class ConstructorSupport {
2551 /**
2552 * Returns canonical record constructor adapted to take two arguments:
2553 * {@code (byte[] primValues, Object[] objValues)}
2554 * and return
2555 * {@code Object}
2556 */
2557 @SuppressWarnings("removal")
2558 static MethodHandle deserializationCtr(ObjectStreamClass desc) {
2559 // check the cached value 1st
2560 MethodHandle mh = desc.deserializationCtr;
2561 if (mh != null) return mh;
2562 mh = desc.deserializationCtrs.get(desc.getFields(false));
2563 if (mh != null) return desc.deserializationCtr = mh;
2564
2565 // retrieve record components
2566 RecordComponent[] recordComponents;
2567 try {
2568 Class<?> cls = desc.forClass();
2569 PrivilegedExceptionAction<RecordComponent[]> pa = cls::getRecordComponents;
2570 recordComponents = AccessController.doPrivileged(pa);
2587 for (int i = recordComponents.length-1; i >= 0; i--) {
2588 String name = recordComponents[i].getName();
2589 Class<?> type = recordComponents[i].getType();
2590 // obtain stream field extractor that extracts argument at
2591 // position i (Ti+1) from primValues and objValues arrays
2592 // (byte[], Object[]):Ti+1
2593 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2594 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2595 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2596 mh = MethodHandles.foldArguments(mh, i, combiner);
2597 }
2598 // what we are left with is a MethodHandle taking just the primValues
2599 // and objValues arrays and returning the constructed record instance
2600 // (byte[], Object[]):Object
2601
2602 // store it into cache and return the 1st value stored
2603 return desc.deserializationCtr =
2604 desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
2605 }
2606
2607 /**
2608 * Returns value object constructor adapted to take two arguments:
2609 * {@code (byte[] primValues, Object[] objValues)} and return {@code Object}
2610 */
2611 static MethodHandle deserializationValueCons(ObjectStreamClass desc) {
2612 // check the cached value 1st
2613 MethodHandle mh = desc.deserializationCtr;
2614 if (mh != null) return mh;
2615 mh = desc.deserializationCtrs.get(desc.getFields(false));
2616 if (mh != null) return desc.deserializationCtr = mh;
2617
2618 // retrieve the selected constructor
2619 // (T1, T2, ..., Tn):TR
2620 ObjectStreamClass localDesc = desc.localDesc;
2621 mh = localDesc.canonicalCtr;
2622 MethodType mt = mh.type();
2623
2624 // change return type to Object
2625 // (T1, T2, ..., Tn):TR -> (T1, T2, ..., Tn):Object
2626 mh = mh.asType(mh.type().changeReturnType(Object.class));
2627
2628 // drop last 2 arguments representing primValues and objValues arrays
2629 // (T1, T2, ..., Tn):Object -> (T1, T2, ..., Tn, byte[], Object[]):Object
2630 mh = MethodHandles.dropArguments(mh, mh.type().parameterCount(), byte[].class, Object[].class);
2631
2632 Class<?>[] params = mt.parameterArray();
2633 for (int i = params.length-1; i >= 0; i--) {
2634 // Get the name from the local descriptor matching the argIndex
2635 var field = getFieldForArgIndex(localDesc, i);
2636 String name = (field == null) ? "" : field.getName(); // empty string to supply default
2637 Class<?> type = params[i];
2638 // obtain stream field extractor that extracts argument at
2639 // position i (Ti+1) from primValues and objValues arrays
2640 // (byte[], Object[]):Ti+1
2641 MethodHandle combiner = streamFieldExtractor(name, type, desc);
2642 // fold byte[] privValues and Object[] objValues into argument at position i (Ti+1)
2643 // (..., Ti, Ti+1, byte[], Object[]):Object -> (..., Ti, byte[], Object[]):Object
2644 mh = MethodHandles.foldArguments(mh, i, combiner);
2645 }
2646 // what we are left with is a MethodHandle taking just the primValues
2647 // and objValues arrays and returning the constructed instance
2648 // (byte[], Object[]):Object
2649
2650 // store it into cache and return the 1st value stored
2651 return desc.deserializationCtr =
2652 desc.deserializationCtrs.putIfAbsentAndGet(desc.getFields(false), mh);
2653 }
2654
2655 // Find the ObjectStreamField for the argument index, otherwise null
2656 private static ObjectStreamField getFieldForArgIndex(ObjectStreamClass desc, int argIndex) {
2657 for (var field : desc.fields) {
2658 if (field.getArgIndex() == argIndex)
2659 return field;
2660 }
2661 TRACE("field for ArgIndex is null: %s, index: %d, fields: %s",
2662 desc, argIndex, Arrays.toString(desc.fields));
2663 return null;
2664 }
2665
2666 /** Returns the number of primitive fields for the given descriptor. */
2667 private static int numberPrimValues(ObjectStreamClass desc) {
2668 ObjectStreamField[] fields = desc.getFields();
2669 int primValueCount = 0;
2670 for (int i = 0; i < fields.length; i++) {
2671 if (fields[i].isPrimitive())
2672 primValueCount++;
2673 else
2674 break; // can be no more
2675 }
2676 return primValueCount;
2677 }
2678
2679 /**
2680 * Returns extractor MethodHandle taking the primValues and objValues arrays
2681 * and extracting the argument of canonical constructor with given name and type
2682 * or producing default value for the given type if the field is absent.
2683 */
2684 private static MethodHandle streamFieldExtractor(String pName,
2685 Class<?> pType,
|