1 /*
2 * Copyright (c) 1996, 2025, 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.io.ObjectInputFilter.Config;
29 import java.io.ObjectStreamClass.RecordSupport;
30 import java.lang.System.Logger;
31 import java.lang.invoke.MethodHandle;
32 import java.lang.reflect.Array;
33 import java.lang.reflect.InvocationHandler;
34 import java.lang.reflect.Modifier;
35 import java.lang.reflect.Proxy;
36 import java.nio.charset.StandardCharsets;
37 import java.util.Arrays;
38 import java.util.Objects;
39
40 import jdk.internal.access.JavaLangAccess;
41 import jdk.internal.access.SharedSecrets;
42 import jdk.internal.event.DeserializationEvent;
43 import jdk.internal.misc.Unsafe;
44 import jdk.internal.util.ByteArray;
45
46 /**
47 * An ObjectInputStream deserializes primitive data and objects previously
48 * written using an ObjectOutputStream.
49 *
50 * <p><strong>Warning: Deserialization of untrusted data is inherently dangerous
51 * and should be avoided. Untrusted data should be carefully validated according to the
52 * "Serialization and Deserialization" section of the
53 * {@extLink secure_coding_guidelines_javase Secure Coding Guidelines for Java SE}.
54 * {@extLink serialization_filter_guide Serialization Filtering} describes best
55 * practices for defensive use of serial filters.
56 * </strong></p>
57 *
218 * the stream; the deserialized constant is then obtained by calling the static
219 * method {@code Enum.valueOf(Class, String)} with the enum constant's
220 * base type and the received constant name as arguments. Like other
221 * serializable or externalizable objects, enum constants can function as the
222 * targets of back references appearing subsequently in the serialization
223 * stream. The process by which enum constants are deserialized cannot be
224 * customized: any class-specific readObject, readObjectNoData, and readResolve
225 * methods defined by enum types are ignored during deserialization.
226 * Similarly, any serialPersistentFields or serialVersionUID field declarations
227 * are also ignored--all enum types have a fixed serialVersionUID of 0L.
228 *
229 * <a id="record-serialization"></a>
230 * <p>Records are serialized differently than ordinary serializable or externalizable
231 * objects. During deserialization the record's canonical constructor is invoked
232 * to construct the record object. Certain serialization-related methods, such
233 * as readObject and writeObject, are ignored for serializable records. See
234 * <a href="{@docRoot}/../specs/serialization/serial-arch.html#serialization-of-records">
235 * <cite>Java Object Serialization Specification,</cite> Section 1.13,
236 * "Serialization of Records"</a> for additional information.
237 *
238 * @spec serialization/index.html Java Object Serialization Specification
239 * @author Mike Warres
240 * @author Roger Riggs
241 * @see java.io.DataInput
242 * @see java.io.ObjectOutputStream
243 * @see java.io.Serializable
244 * @see <a href="{@docRoot}/../specs/serialization/input.html">
245 * <cite>Java Object Serialization Specification,</cite> Section 3, "Object Input Classes"</a>
246 * @since 1.1
247 */
248 public class ObjectInputStream
249 extends InputStream implements ObjectInput, ObjectStreamConstants
250 {
251 /** handle value representing null */
252 private static final int NULL_HANDLE = -1;
253
254 /** marker for unshared objects in internal handle table */
255 private static final Object unsharedMarker = new Object();
256
257 private static class Caches {
413 * Default deserializing for a class can be overridden using the writeObject
414 * and readObject methods. Objects referenced by this object are read
415 * transitively so that a complete equivalent graph of objects is
416 * reconstructed by readObject.
417 *
418 * <p>The root object is completely restored when all of its fields and the
419 * objects it references are completely restored. At this point the object
420 * validation callbacks are executed in order based on their registered
421 * priorities. The callbacks are registered by objects (in the readObject
422 * special methods) as they are individually restored.
423 *
424 * <p>The deserialization filter, when not {@code null}, is invoked for
425 * each object (regular or class) read to reconstruct the root object.
426 * See {@link #setObjectInputFilter(ObjectInputFilter) setObjectInputFilter} for details.
427 *
428 * <p>Exceptions are thrown for problems with the InputStream and for
429 * classes that should not be deserialized. All exceptions are fatal to
430 * the InputStream and leave it in an indeterminate state; it is up to the
431 * caller to ignore or recover the stream state.
432 *
433 * @throws ClassNotFoundException Class of a serialized object cannot be
434 * found.
435 * @throws InvalidClassException Something is wrong with a class used by
436 * deserialization.
437 * @throws StreamCorruptedException Control information in the
438 * stream is inconsistent.
439 * @throws OptionalDataException Primitive data was found in the
440 * stream instead of objects.
441 * @throws IOException Any of the usual Input/Output related exceptions.
442 */
443 public final Object readObject()
444 throws IOException, ClassNotFoundException {
445 return readObject(Object.class);
446 }
447
448 /**
449 * Reads a String and only a string.
450 *
451 * @return the String read
452 * @throws EOFException If end of file is reached.
2090 * class is unresolvable (in which case a ClassNotFoundException will be
2091 * associated with object's handle). Sets passHandle to object's assigned
2092 * handle.
2093 */
2094 private Object readOrdinaryObject(boolean unshared)
2095 throws IOException
2096 {
2097 if (bin.readByte() != TC_OBJECT) {
2098 throw new InternalError();
2099 }
2100
2101 ObjectStreamClass desc = readClassDesc(false);
2102 desc.checkDeserialize();
2103
2104 Class<?> cl = desc.forClass();
2105 if (cl == String.class || cl == Class.class
2106 || cl == ObjectStreamClass.class) {
2107 throw new InvalidClassException("invalid class descriptor");
2108 }
2109
2110 Object obj;
2111 try {
2112 obj = desc.isInstantiable() ? desc.newInstance() : null;
2113 } catch (Exception ex) {
2114 throw new InvalidClassException(desc.forClass().getName(),
2115 "unable to create instance", ex);
2116 }
2117
2118 passHandle = handles.assign(unshared ? unsharedMarker : obj);
2119 ClassNotFoundException resolveEx = desc.getResolveException();
2120 if (resolveEx != null) {
2121 handles.markException(passHandle, resolveEx);
2122 }
2123
2124 final boolean isRecord = desc.isRecord();
2125 if (isRecord) {
2126 assert obj == null;
2127 obj = readRecord(desc);
2128 if (!unshared)
2129 handles.setObject(passHandle, obj);
2130 } else if (desc.isExternalizable()) {
2131 readExternalData((Externalizable) obj, desc);
2132 } else {
2133 readSerialData(obj, desc);
2134 }
2135
2136 handles.finish(passHandle);
2137
2138 if (obj != null &&
2139 handles.lookupException(passHandle) == null &&
2140 desc.hasReadResolveMethod())
2141 {
2142 Object rep = desc.invokeReadResolve(obj);
2143 if (unshared && rep.getClass().isArray()) {
2144 rep = cloneArray(rep);
2145 }
2146 if (rep != obj) {
2147 // Filter the replacement object
2148 if (rep != null) {
2149 if (rep.getClass().isArray()) {
2150 filterCheck(rep.getClass(), Array.getLength(rep));
2151 } else {
2152 filterCheck(rep.getClass(), -1);
2153 }
2154 }
2155 handles.setObject(passHandle, obj = rep);
2156 }
2157 }
2158
2159 return obj;
2160 }
2161
2162 /**
2163 * If obj is non-null, reads externalizable data by invoking readExternal()
2164 * method of obj; otherwise, attempts to skip over externalizable data.
2165 * Expects that passHandle is set to obj's handle before this method is
2166 * called.
2167 */
2168 private void readExternalData(Externalizable obj, ObjectStreamClass desc)
2169 throws IOException
2170 {
2171 SerialCallbackContext oldContext = curContext;
2172 if (oldContext != null)
2173 oldContext.check();
2174 curContext = null;
2175 try {
2176 boolean blocked = desc.hasBlockExternalData();
2177 if (blocked) {
2178 bin.setBlockDataMode(true);
2179 }
2180 if (obj != null) {
2181 try {
2182 obj.readExternal(this);
2183 } catch (ClassNotFoundException ex) {
2184 /*
2185 * In most cases, the handle table has already propagated
2186 * a CNFException to passHandle at this point; this mark
2187 * call is included to address cases where the readExternal
2188 * method has cons'ed and thrown a new CNFException of its
2189 * own.
2190 */
2194 if (blocked) {
2195 skipCustomData();
2196 }
2197 } finally {
2198 if (oldContext != null)
2199 oldContext.check();
2200 curContext = oldContext;
2201 }
2202 /*
2203 * At this point, if the externalizable data was not written in
2204 * block-data form and either the externalizable class doesn't exist
2205 * locally (i.e., obj == null) or readExternal() just threw a
2206 * CNFException, then the stream is probably in an inconsistent state,
2207 * since some (or all) of the externalizable data may not have been
2208 * consumed. Since there's no "correct" action to take in this case,
2209 * we mimic the behavior of past serialization implementations and
2210 * blindly hope that the stream is in sync; if it isn't and additional
2211 * externalizable data remains in the stream, a subsequent read will
2212 * most likely throw a StreamCorruptedException.
2213 */
2214 }
2215
2216 /**
2217 * Reads and returns a record.
2218 * If an exception is marked for any of the fields, the dependency
2219 * mechanism marks the record as having an exception.
2220 * Null is returned from readRecord and later the exception is thrown at
2221 * the exit of {@link #readObject(Class)}.
2222 */
2223 private Object readRecord(ObjectStreamClass desc) throws IOException {
2224 ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
2225 if (slots.length != 1) {
2226 // skip any superclass stream field values
2227 for (int i = 0; i < slots.length-1; i++) {
2228 if (slots[i].hasData) {
2229 new FieldValues(slots[i].desc, true);
2230 }
2231 }
2232 }
2233
2234 FieldValues fieldValues = new FieldValues(desc, true);
2235 if (handles.lookupException(passHandle) != null) {
2236 return null; // slot marked with exception, don't create record
2237 }
2238
2239 // get canonical record constructor adapted to take two arguments:
2240 // - byte[] primValues
2241 // - Object[] objValues
2242 // and return Object
2243 MethodHandle ctrMH = RecordSupport.deserializationCtr(desc);
2244
2245 try {
2246 return (Object) ctrMH.invokeExact(fieldValues.primValues, fieldValues.objValues);
2247 } catch (Exception e) {
2248 throw new InvalidObjectException(e.getMessage(), e);
2249 } catch (Error e) {
2250 throw e;
2251 } catch (Throwable t) {
2252 throw new InvalidObjectException("ReflectiveOperationException " +
2253 "during deserialization", t);
2254 }
2255 }
2256
2257 /**
2258 * Reads (or attempts to skip, if obj is null or is tagged with a
2259 * ClassNotFoundException) instance data for each serializable class of
2260 * object in stream, from superclass to subclass. Expects that passHandle
2261 * is set to obj's handle before this method is called.
2262 */
2263 private void readSerialData(Object obj, ObjectStreamClass desc)
2264 throws IOException
2265 {
2266 ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
2267 // Best effort Failure Atomicity; slotValues will be non-null if field
2268 // values can be set after reading all field data in the hierarchy.
2269 // Field values can only be set after reading all data if there are no
2270 // user observable methods in the hierarchy, readObject(NoData). The
2271 // top most Serializable class in the hierarchy can be skipped.
2272 FieldValues[] slotValues = null;
2273
2274 boolean hasSpecialReadMethod = false;
2275 for (int i = 1; i < slots.length; i++) {
2276 ObjectStreamClass slotDesc = slots[i].desc;
2277 if (slotDesc.hasReadObjectMethod()
2278 || slotDesc.hasReadObjectNoDataMethod()) {
2279 hasSpecialReadMethod = true;
2280 break;
2281 }
2282 }
2283 // No special read methods, can store values and defer setting.
2284 if (!hasSpecialReadMethod)
2285 slotValues = new FieldValues[slots.length];
2286
2287 for (int i = 0; i < slots.length; i++) {
2288 ObjectStreamClass slotDesc = slots[i].desc;
2289
2290 if (slots[i].hasData) {
2291 if (obj == null || handles.lookupException(passHandle) != null) {
2292 // Read fields of the current descriptor into a new FieldValues and discard
2293 new FieldValues(slotDesc, true);
2294 } else if (slotDesc.hasReadObjectMethod()) {
2295 SerialCallbackContext oldContext = curContext;
2296 if (oldContext != null)
2297 oldContext.check();
2298 try {
2299 curContext = new SerialCallbackContext(obj, slotDesc);
2300
2301 bin.setBlockDataMode(true);
2302 slotDesc.invokeReadObject(obj, this);
2303 } catch (ClassNotFoundException ex) {
2304 /*
2305 * In most cases, the handle table has already
2306 * propagated a CNFException to passHandle at this
2307 * point; this mark call is included to address cases
2308 * where the custom readObject method has cons'ed and
2309 * thrown a new CNFException of its own.
2310 */
2311 handles.markException(passHandle, ex);
2312 } finally {
2313 curContext.setUsed();
2314 if (oldContext!= null)
2315 oldContext.check();
2316 curContext = oldContext;
2317 }
2318
2319 /*
2320 * defaultDataEnd may have been set indirectly by custom
2321 * readObject() method when calling defaultReadObject() or
2322 * readFields(); clear it to restore normal read behavior.
2323 */
2324 defaultDataEnd = false;
2325 } else {
2326 // Read fields of the current descriptor into a new FieldValues
2327 FieldValues values = new FieldValues(slotDesc, true);
2328 if (slotValues != null) {
2329 slotValues[i] = values;
2330 } else if (obj != null) {
2331 if (handles.lookupException(passHandle) == null) {
2332 // passHandle NOT marked with an exception; set field values
2333 values.defaultCheckFieldValues(obj);
2334 values.defaultSetFieldValues(obj);
2335 }
2336 }
2337 }
2338
2339 if (slotDesc.hasWriteObjectData()) {
2340 skipCustomData();
2341 } else {
2342 bin.setBlockDataMode(false);
2343 }
2344 } else {
2345 if (obj != null &&
2346 slotDesc.hasReadObjectNoDataMethod() &&
2347 handles.lookupException(passHandle) == null)
2348 {
2349 slotDesc.invokeReadObjectNoData(obj);
2350 }
2351 }
2352 }
2353
2354 if (obj != null && slotValues != null && handles.lookupException(passHandle) == null) {
2355 // passHandle NOT marked with an exception
2356 // Check that the non-primitive types are assignable for all slots
2357 // before assigning.
2358 for (int i = 0; i < slots.length; i++) {
2359 if (slotValues[i] != null)
2360 slotValues[i].defaultCheckFieldValues(obj);
2361 }
2362 for (int i = 0; i < slots.length; i++) {
2363 if (slotValues[i] != null)
2364 slotValues[i].defaultSetFieldValues(obj);
2365 }
2366 }
2367 }
2368
2369 /**
2370 * Skips over all block data and objects until TC_ENDBLOCKDATA is
2371 * encountered.
2372 */
2373 private void skipCustomData() throws IOException {
2374 int oldHandle = passHandle;
2375 for (;;) {
2376 if (bin.getBlockDataMode()) {
2377 bin.skipBlockData();
2378 bin.setBlockDataMode(false);
2379 }
2380 switch (bin.peekByte()) {
2381 case TC_BLOCKDATA:
2382 case TC_BLOCKDATALONG:
2383 bin.setBlockDataMode(true);
2384 break;
2385
2441 /**
2442 * Default GetField implementation.
2443 */
2444 private final class FieldValues extends GetField {
2445
2446 /** class descriptor describing serializable fields */
2447 private final ObjectStreamClass desc;
2448 /** primitive field values */
2449 final byte[] primValues;
2450 /** object field values */
2451 final Object[] objValues;
2452 /** object field value handles */
2453 private final int[] objHandles;
2454
2455 /**
2456 * Creates FieldValues object for reading fields defined in given
2457 * class descriptor.
2458 * @param desc the ObjectStreamClass to read
2459 * @param recordDependencies if true, record the dependencies
2460 * from current PassHandle and the object's read.
2461 */
2462 FieldValues(ObjectStreamClass desc, boolean recordDependencies) throws IOException {
2463 this.desc = desc;
2464
2465 int primDataSize = desc.getPrimDataSize();
2466 primValues = (primDataSize > 0) ? new byte[primDataSize] : null;
2467 if (primDataSize > 0) {
2468 bin.readFully(primValues, 0, primDataSize, false);
2469 }
2470
2471 int numObjFields = desc.getNumObjFields();
2472 objValues = (numObjFields > 0) ? new Object[numObjFields] : null;
2473 objHandles = (numObjFields > 0) ? new int[numObjFields] : null;
2474 if (numObjFields > 0) {
2475 int objHandle = passHandle;
2476 ObjectStreamField[] fields = desc.getFields(false);
2477 int numPrimFields = fields.length - objValues.length;
2478 for (int i = 0; i < objValues.length; i++) {
2479 ObjectStreamField f = fields[numPrimFields + i];
2480 objValues[i] = readObject0(Object.class, f.isUnshared());
2481 objHandles[i] = passHandle;
2482 if (recordDependencies && f.getField() != null) {
2483 handles.markDependency(objHandle, passHandle);
2484 }
2485 }
2486 passHandle = objHandle;
2487 }
2488 }
2489
2490 public ObjectStreamClass getObjectStreamClass() {
2491 return desc;
2492 }
2493
2494 public boolean defaulted(String name) {
2495 return (getFieldOffset(name, null) < 0);
2496 }
2497
2498 public boolean get(String name, boolean val) {
2499 int off = getFieldOffset(name, Boolean.TYPE);
2500 return (off >= 0) ? ByteArray.getBoolean(primValues, off) : val;
2501 }
2502
2503 public byte get(String name, byte val) {
2504 int off = getFieldOffset(name, Byte.TYPE);
2505 return (off >= 0) ? primValues[off] : val;
2506 }
|
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.io.ObjectInputFilter.Config;
29 import java.io.ObjectStreamClass.ConstructorSupport;
30 import java.io.ObjectStreamClass.ClassDataSlot;
31 import java.lang.System.Logger;
32 import java.lang.invoke.MethodHandle;
33 import java.lang.reflect.Array;
34 import java.lang.reflect.InvocationHandler;
35 import java.lang.reflect.InvocationTargetException;
36 import java.lang.reflect.Modifier;
37 import java.lang.reflect.Proxy;
38 import java.nio.charset.StandardCharsets;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.List;
42 import java.util.Objects;
43
44 import jdk.internal.access.JavaLangAccess;
45 import jdk.internal.access.SharedSecrets;
46 import jdk.internal.event.DeserializationEvent;
47 import jdk.internal.misc.Unsafe;
48 import jdk.internal.util.ByteArray;
49
50 /**
51 * An ObjectInputStream deserializes primitive data and objects previously
52 * written using an ObjectOutputStream.
53 *
54 * <p><strong>Warning: Deserialization of untrusted data is inherently dangerous
55 * and should be avoided. Untrusted data should be carefully validated according to the
56 * "Serialization and Deserialization" section of the
57 * {@extLink secure_coding_guidelines_javase Secure Coding Guidelines for Java SE}.
58 * {@extLink serialization_filter_guide Serialization Filtering} describes best
59 * practices for defensive use of serial filters.
60 * </strong></p>
61 *
222 * the stream; the deserialized constant is then obtained by calling the static
223 * method {@code Enum.valueOf(Class, String)} with the enum constant's
224 * base type and the received constant name as arguments. Like other
225 * serializable or externalizable objects, enum constants can function as the
226 * targets of back references appearing subsequently in the serialization
227 * stream. The process by which enum constants are deserialized cannot be
228 * customized: any class-specific readObject, readObjectNoData, and readResolve
229 * methods defined by enum types are ignored during deserialization.
230 * Similarly, any serialPersistentFields or serialVersionUID field declarations
231 * are also ignored--all enum types have a fixed serialVersionUID of 0L.
232 *
233 * <a id="record-serialization"></a>
234 * <p>Records are serialized differently than ordinary serializable or externalizable
235 * objects. During deserialization the record's canonical constructor is invoked
236 * to construct the record object. Certain serialization-related methods, such
237 * as readObject and writeObject, are ignored for serializable records. See
238 * <a href="{@docRoot}/../specs/serialization/serial-arch.html#serialization-of-records">
239 * <cite>Java Object Serialization Specification,</cite> Section 1.13,
240 * "Serialization of Records"</a> for additional information.
241 *
242 * <div class="preview-block">
243 * <div class="preview-comment">
244 * <p>{@linkplain Class#isValue Value classes} that are not records cannot be
245 * deserialized directly. To serialize an instance of a value class, a proxy
246 * object should be used instead. That object can then implement
247 * <a href="{@docRoot}/../specs/serialization/input.html#the-readresolve-method">
248 * {@code readResolve}</a> to construct and return the expected value class
249 * instance.
250 * </div>
251 * </div>
252 *
253 * @spec serialization/index.html Java Object Serialization Specification
254 * @author Mike Warres
255 * @author Roger Riggs
256 * @see java.io.DataInput
257 * @see java.io.ObjectOutputStream
258 * @see java.io.Serializable
259 * @see <a href="{@docRoot}/../specs/serialization/input.html">
260 * <cite>Java Object Serialization Specification,</cite> Section 3, "Object Input Classes"</a>
261 * @since 1.1
262 */
263 public class ObjectInputStream
264 extends InputStream implements ObjectInput, ObjectStreamConstants
265 {
266 /** handle value representing null */
267 private static final int NULL_HANDLE = -1;
268
269 /** marker for unshared objects in internal handle table */
270 private static final Object unsharedMarker = new Object();
271
272 private static class Caches {
428 * Default deserializing for a class can be overridden using the writeObject
429 * and readObject methods. Objects referenced by this object are read
430 * transitively so that a complete equivalent graph of objects is
431 * reconstructed by readObject.
432 *
433 * <p>The root object is completely restored when all of its fields and the
434 * objects it references are completely restored. At this point the object
435 * validation callbacks are executed in order based on their registered
436 * priorities. The callbacks are registered by objects (in the readObject
437 * special methods) as they are individually restored.
438 *
439 * <p>The deserialization filter, when not {@code null}, is invoked for
440 * each object (regular or class) read to reconstruct the root object.
441 * See {@link #setObjectInputFilter(ObjectInputFilter) setObjectInputFilter} for details.
442 *
443 * <p>Exceptions are thrown for problems with the InputStream and for
444 * classes that should not be deserialized. All exceptions are fatal to
445 * the InputStream and leave it in an indeterminate state; it is up to the
446 * caller to ignore or recover the stream state.
447 *
448 * <div class="preview-block">
449 * <div class="preview-comment">
450 * <p>An object in the stream that instantiates or extends a Serializable
451 * {@linkplain Class#isValue value class} can only be deserialized if
452 * it is a record or a boxed primitive value. Otherwise,
453 * {@code readObject} throws an {@code InvalidClassException}.
454 * </div>
455 * </div>
456 *
457 * @throws ClassNotFoundException Class of a serialized object cannot be
458 * found.
459 * @throws InvalidClassException Something is wrong with a class used by
460 * deserialization.
461 * @throws StreamCorruptedException Control information in the
462 * stream is inconsistent.
463 * @throws OptionalDataException Primitive data was found in the
464 * stream instead of objects.
465 * @throws IOException Any of the usual Input/Output related exceptions.
466 */
467 public final Object readObject()
468 throws IOException, ClassNotFoundException {
469 return readObject(Object.class);
470 }
471
472 /**
473 * Reads a String and only a string.
474 *
475 * @return the String read
476 * @throws EOFException If end of file is reached.
2114 * class is unresolvable (in which case a ClassNotFoundException will be
2115 * associated with object's handle). Sets passHandle to object's assigned
2116 * handle.
2117 */
2118 private Object readOrdinaryObject(boolean unshared)
2119 throws IOException
2120 {
2121 if (bin.readByte() != TC_OBJECT) {
2122 throw new InternalError();
2123 }
2124
2125 ObjectStreamClass desc = readClassDesc(false);
2126 desc.checkDeserialize();
2127
2128 Class<?> cl = desc.forClass();
2129 if (cl == String.class || cl == Class.class
2130 || cl == ObjectStreamClass.class) {
2131 throw new InvalidClassException("invalid class descriptor");
2132 }
2133
2134 // Assign the handle and initially set to null or the unsharedMarker
2135 passHandle = handles.assign(unshared ? unsharedMarker : null);
2136 ClassNotFoundException resolveEx = desc.getResolveException();
2137 if (resolveEx != null) {
2138 handles.markException(passHandle, resolveEx);
2139 }
2140
2141 try {
2142 // Dispatch on the factory mode to read an object from the stream.
2143 Object obj = switch (desc.factoryMode()) {
2144 case READ_OBJECT_DEFAULT -> readSerialDefaultObject(desc, unshared);
2145 case READ_OBJECT_CUSTOM -> readSerialCustomData(desc, unshared);
2146 case READ_RECORD -> readRecord(desc, unshared);
2147 case READ_EXTERNALIZABLE -> readExternalObject(desc, unshared);
2148 case READ_OBJECT_VALUE -> readObjectValue(desc, unshared);
2149 case READ_NO_LOCAL_CLASS -> readAbsentLocalClass(desc, unshared);
2150 case null -> throw new AssertionError("Unknown factoryMode for: " + desc.getName(),
2151 resolveEx);
2152 };
2153
2154 handles.finish(passHandle);
2155
2156 if (obj != null &&
2157 handles.lookupException(passHandle) == null &&
2158 desc.hasReadResolveMethod())
2159 {
2160 Object rep = desc.invokeReadResolve(obj);
2161 if (unshared && rep.getClass().isArray()) {
2162 rep = cloneArray(rep);
2163 }
2164 if (rep != obj) {
2165 // Filter the replacement object
2166 if (rep != null) {
2167 if (rep.getClass().isArray()) {
2168 filterCheck(rep.getClass(), Array.getLength(rep));
2169 } else {
2170 filterCheck(rep.getClass(), -1);
2171 }
2172 }
2173 handles.setObject(passHandle, obj = rep);
2174 }
2175 }
2176
2177 return obj;
2178 } catch (UncheckedIOException uioe) {
2179 // Consistent re-throw for nested UncheckedIOExceptions
2180 throw uioe.getCause();
2181 }
2182 }
2183
2184 /**
2185 * {@return a value class instance by invoking its constructor with field values read from the stream.
2186 * The fields of the class in the stream are matched to the local fields and applied to
2187 * the constructor.
2188 * If the stream contains superclasses with serializable fields,
2189 * an InvalidClassException is thrown with an incompatible class change message.
2190 *
2191 * @param desc the class descriptor read from the stream, the local class is a value class
2192 * @param unshared if the object is not to be shared
2193 * @throws InvalidClassException if the stream contains a superclass with serializable fields.
2194 * @throws IOException if there are I/O errors while reading from the
2195 * underlying {@code InputStream}
2196 */
2197 private Object readObjectValue(ObjectStreamClass desc, boolean unshared) throws IOException {
2198 final ObjectStreamClass localDesc = desc.getLocalDesc();
2199 // Check for un-expected fields in superclasses
2200 List<ClassDataSlot> slots = desc.getClassDataLayout();
2201 for (int i = 0; i < slots.size()-1; i++) {
2202 ClassDataSlot slot = slots.get(i);
2203 if (slot.hasData && slot.desc.getFields(false).length > 0) {
2204 throw new InvalidClassException("incompatible class change to value class: " +
2205 "stream class has non-empty super type: " + desc.getName());
2206 }
2207 }
2208 // Read values for the value class fields
2209 FieldValues fieldValues = new FieldValues(desc, true);
2210
2211 // Get value object constructor adapted to take primitive value buffer and object array.
2212 MethodHandle consMH = ConstructorSupport.deserializationValueCons(desc);
2213 try {
2214 Object obj = (Object) consMH.invokeExact(fieldValues.primValues, fieldValues.objValues);
2215 if (!unshared)
2216 handles.setObject(passHandle, obj);
2217 return obj;
2218 } catch (Exception e) {
2219 throw new InvalidObjectException(e.getMessage(), e);
2220 } catch (Error e) {
2221 throw e;
2222 } catch (Throwable t) {
2223 throw new InvalidObjectException("ReflectiveOperationException " +
2224 "during deserialization", t);
2225 }
2226 }
2227
2228 /**
2229 * Creates a new object and invokes its readExternal method to read its contents.
2230 *
2231 * If the class is instantiable, read externalizable data by invoking readExternal()
2232 * method of obj; otherwise, attempts to skip over externalizable data.
2233 * Expects that passHandle is set to obj's handle before this method is
2234 * called. The new object is entered in the handle table immediately,
2235 * allowing it to leak before it is completely read.
2236 */
2237 private Object readExternalObject(ObjectStreamClass desc, boolean unshared)
2238 throws IOException
2239 {
2240 // For Externalizable objects,
2241 // create the instance, publish the ref, and read the data
2242 Externalizable obj = null;
2243 try {
2244 if (desc.isInstantiable()) {
2245 obj = (Externalizable) desc.newInstance();
2246 }
2247 } catch (Exception ex) {
2248 throw new InvalidClassException(desc.getName(),
2249 "unable to create instance", ex);
2250 }
2251
2252 if (!unshared)
2253 handles.setObject(passHandle, obj);
2254
2255 SerialCallbackContext oldContext = curContext;
2256 if (oldContext != null)
2257 oldContext.check();
2258 curContext = null;
2259 try {
2260 boolean blocked = desc.hasBlockExternalData();
2261 if (blocked) {
2262 bin.setBlockDataMode(true);
2263 }
2264 if (obj != null) {
2265 try {
2266 obj.readExternal(this);
2267 } catch (ClassNotFoundException ex) {
2268 /*
2269 * In most cases, the handle table has already propagated
2270 * a CNFException to passHandle at this point; this mark
2271 * call is included to address cases where the readExternal
2272 * method has cons'ed and thrown a new CNFException of its
2273 * own.
2274 */
2278 if (blocked) {
2279 skipCustomData();
2280 }
2281 } finally {
2282 if (oldContext != null)
2283 oldContext.check();
2284 curContext = oldContext;
2285 }
2286 /*
2287 * At this point, if the externalizable data was not written in
2288 * block-data form and either the externalizable class doesn't exist
2289 * locally (i.e., obj == null) or readExternal() just threw a
2290 * CNFException, then the stream is probably in an inconsistent state,
2291 * since some (or all) of the externalizable data may not have been
2292 * consumed. Since there's no "correct" action to take in this case,
2293 * we mimic the behavior of past serialization implementations and
2294 * blindly hope that the stream is in sync; if it isn't and additional
2295 * externalizable data remains in the stream, a subsequent read will
2296 * most likely throw a StreamCorruptedException.
2297 */
2298 return obj;
2299 }
2300
2301 /**
2302 * Reads and returns a record.
2303 * If an exception is marked for any of the fields, the dependency
2304 * mechanism marks the record as having an exception.
2305 * Null is returned from readRecord and later the exception is thrown at
2306 * the exit of {@link #readObject(Class)}.
2307 */
2308 private Object readRecord(ObjectStreamClass desc, boolean unshared) throws IOException {
2309 List<ClassDataSlot> slots = desc.getClassDataLayout();
2310 if (slots.size() != 1) {
2311 // skip any superclass stream field values
2312 for (int i = 0; i < slots.size()-1; i++) {
2313 if (slots.get(i).hasData) {
2314 new FieldValues(slots.get(i).desc, true);
2315 }
2316 }
2317 }
2318
2319 FieldValues fieldValues = new FieldValues(desc, true);
2320 if (handles.lookupException(passHandle) != null) {
2321 return null; // slot marked with exception, don't create record
2322 }
2323
2324 // get canonical record constructor adapted to take two arguments:
2325 // - byte[] primValues
2326 // - Object[] objValues
2327 // and return Object
2328 MethodHandle ctrMH = ConstructorSupport.deserializationCtr(desc);
2329
2330 try {
2331 Object obj = (Object) ctrMH.invokeExact(fieldValues.primValues, fieldValues.objValues);
2332 if (!unshared)
2333 handles.setObject(passHandle, obj);
2334 return obj;
2335 } catch (Exception e) {
2336 throw new InvalidObjectException(e.getMessage(), e);
2337 } catch (Error e) {
2338 throw e;
2339 } catch (Throwable t) {
2340 throw new InvalidObjectException("ReflectiveOperationException " +
2341 "during deserialization", t);
2342 }
2343 }
2344
2345 /**
2346 * Construct an object from the stream for a class that has only default read object behaviors.
2347 * For each object, the fields are read before any are assigned.
2348 * The new instance is entered in the handle table if it is unshared,
2349 * allowing it to escape before it is initialized.
2350 * The `readObject` and `readObjectNoData` methods are not present and are not called.
2351 *
2352 * @param desc the class descriptor
2353 * @param unshared true if the object should be shared
2354 * @return the object constructed from the stream data
2355 * @throws IOException if there are I/O errors while reading from the
2356 * underlying {@code InputStream}
2357 * @throws InvalidClassException if the instance creation fails
2358 */
2359 private Object readSerialDefaultObject(ObjectStreamClass desc, boolean unshared)
2360 throws IOException, InvalidClassException {
2361 if (!desc.isInstantiable()) {
2362 // No local class to create, read and discard
2363 return readAbsentLocalClass(desc, unshared);
2364 }
2365 try {
2366 final Object obj = desc.newInstance();
2367 if (!unshared)
2368 handles.setObject(passHandle, obj);
2369
2370 // Best effort Failure Atomicity; slotValues will be non-null if field
2371 // values can be set after reading all field data in the hierarchy.
2372 List<ClassDataSlot> slots = desc.getClassDataLayout();
2373 List<FieldValues> slotValues = new ArrayList<>(slots.size());
2374 for (ClassDataSlot s : slots) {
2375 if (s.hasData) {
2376 var value = new FieldValues(s.desc, true);
2377 finishBlockData(s.desc);
2378 slotValues.add(value);
2379 }
2380 }
2381
2382 if (handles.lookupException(passHandle) != null) {
2383 return null; // some exception for a class, do not return the object
2384 }
2385
2386 // Check that the types are assignable for all slots before assigning.
2387 for (FieldValues slotValue : slotValues) {
2388 slotValue.defaultCheckFieldValues(obj);
2389 }
2390 for (FieldValues v : slotValues) {
2391 v.defaultSetFieldValues(obj);
2392 }
2393 return obj;
2394 } catch (InstantiationException | InvocationTargetException ex) {
2395 throw new InvalidClassException(desc.forClass().getName(),
2396 "unable to create instance", ex);
2397 }
2398 }
2399
2400
2401 /**
2402 * Reads (or attempts to skip, if not instantiatable or is tagged with a
2403 * ClassNotFoundException) instance data for each serializable class of
2404 * object in stream, from superclass to subclass.
2405 * Expects that passHandle is set to current handle before this method is called.
2406 */
2407 private Object readSerialCustomData(ObjectStreamClass desc, boolean unshared)
2408 throws IOException
2409 {
2410 if (!desc.isInstantiable()) {
2411 // No local class to create, read and discard
2412 return readAbsentLocalClass(desc, unshared);
2413 }
2414
2415 try {
2416 Object obj = desc.newInstance();
2417 if (!unshared)
2418 handles.setObject(passHandle, obj);
2419 // Read data into each of the slots for the class
2420 return readSerialCustomSlots(obj, desc.getClassDataLayout());
2421 } catch (InstantiationException | InvocationTargetException ex) {
2422 throw new InvalidClassException(desc.forClass().getName(),
2423 "unable to create instance", ex);
2424 }
2425 }
2426
2427 /**
2428 * Reads from the stream using custom or default readObject methods appropriate.
2429 * For each slot, either the custom readObject method or the default reader of fields
2430 * is invoked. Unused slot specific custom data is discarded.
2431 * This function is used by {@link #readSerialCustomData}.
2432 *
2433 * @param obj the object to assign the values to
2434 * @param slots a list of slots to read from the stream
2435 * @return the object being initialized
2436 * @throws IOException if there are I/O errors while reading from the
2437 * underlying {@code InputStream}
2438 */
2439 private Object readSerialCustomSlots(Object obj, List<ClassDataSlot> slots) throws IOException {
2440
2441 for (ClassDataSlot slot : slots) {
2442 ObjectStreamClass slotDesc = slot.desc;
2443 if (slot.hasData) {
2444 if (slotDesc.hasReadObjectMethod() &&
2445 handles.lookupException(passHandle) == null) {
2446 // Invoke slot custom readObject method
2447 readSlotViaReadObject(obj, slotDesc);
2448 } else {
2449 // Read fields of the current descriptor into a new FieldValues
2450 FieldValues values = new FieldValues(slotDesc, true);
2451 if (handles.lookupException(passHandle) == null) {
2452 // Set the instance fields if no previous exception
2453 values.defaultCheckFieldValues(obj);
2454 values.defaultSetFieldValues(obj);
2455 }
2456 finishBlockData(slotDesc);
2457 }
2458 } else {
2459 if (slotDesc.hasReadObjectNoDataMethod() &&
2460 handles.lookupException(passHandle) == null) {
2461 slotDesc.invokeReadObjectNoData(obj);
2462 }
2463 }
2464 }
2465 return obj;
2466 }
2467
2468 /**
2469 * Invoke the readObject method of the class to read and store the state from the stream.
2470 *
2471 * @param obj an instance of the class being created, only partially initialized.
2472 * @param slotDesc the ObjectStreamDescriptor for the current class
2473 * @throws IOException if there are I/O errors while reading from the
2474 * underlying {@code InputStream}
2475 */
2476 private void readSlotViaReadObject(Object obj, ObjectStreamClass slotDesc) throws IOException {
2477 assert obj != null : "readSlotViaReadObject called when obj == null";
2478
2479 SerialCallbackContext oldContext = curContext;
2480 if (oldContext != null)
2481 oldContext.check();
2482 try {
2483 curContext = new SerialCallbackContext(obj, slotDesc);
2484
2485 bin.setBlockDataMode(true);
2486 slotDesc.invokeReadObject(obj, this);
2487 } catch (ClassNotFoundException ex) {
2488 /*
2489 * In most cases, the handle table has already
2490 * propagated a CNFException to passHandle at this
2491 * point; this mark call is included to address cases
2492 * where the custom readObject method has cons'ed and
2493 * thrown a new CNFException of its own.
2494 */
2495 handles.markException(passHandle, ex);
2496 } finally {
2497 curContext.setUsed();
2498 if (oldContext!= null)
2499 oldContext.check();
2500 curContext = oldContext;
2501 }
2502
2503 /*
2504 * defaultDataEnd may have been set indirectly by custom
2505 * readObject() method when calling defaultReadObject() or
2506 * readFields(); clear it to restore normal read behavior.
2507 */
2508 defaultDataEnd = false;
2509
2510 finishBlockData(slotDesc);
2511 }
2512
2513
2514 /**
2515 * Read and discard an entire object, leaving a null reference in the HandleTable.
2516 * The descriptor of the class in the stream is used to read the fields from the stream.
2517 * There is no instance in which to store the field values.
2518 * Custom data following the fields of any slot is read and discarded.
2519 * References to nested objects are read and retained in the
2520 * handle table using the regular mechanism.
2521 * Handles later in the stream may refer to the nested objects.
2522 *
2523 * @param desc the stream class descriptor
2524 * @param unshared the unshared flag, ignored since no object is created
2525 * @return null, no object is created
2526 * @throws IOException if there are I/O errors while reading from the
2527 * underlying {@code InputStream}
2528 */
2529 private Object readAbsentLocalClass(ObjectStreamClass desc, boolean unshared)
2530 throws IOException {
2531 desc.getClassDataLayout().stream()
2532 .filter(s -> s.hasData)
2533 .forEach(s2 -> {new FieldValues(s2.desc, true); finishBlockData(s2.desc);});
2534 return null;
2535 }
2536
2537 // Finish handling of block data by skipping any remaining and setting BlockDataMode = false
2538 private void finishBlockData(ObjectStreamClass slotDesc) throws UncheckedIOException {
2539 try {
2540 if (slotDesc.hasWriteObjectData()) {
2541 skipCustomData();
2542 } else {
2543 bin.setBlockDataMode(false);
2544 }
2545 } catch (IOException ioe) {
2546 throw new UncheckedIOException(ioe);
2547 }
2548 }
2549
2550 /**
2551 * Skips over all block data and objects until TC_ENDBLOCKDATA is
2552 * encountered.
2553 */
2554 private void skipCustomData() throws IOException {
2555 int oldHandle = passHandle;
2556 for (;;) {
2557 if (bin.getBlockDataMode()) {
2558 bin.skipBlockData();
2559 bin.setBlockDataMode(false);
2560 }
2561 switch (bin.peekByte()) {
2562 case TC_BLOCKDATA:
2563 case TC_BLOCKDATALONG:
2564 bin.setBlockDataMode(true);
2565 break;
2566
2622 /**
2623 * Default GetField implementation.
2624 */
2625 private final class FieldValues extends GetField {
2626
2627 /** class descriptor describing serializable fields */
2628 private final ObjectStreamClass desc;
2629 /** primitive field values */
2630 final byte[] primValues;
2631 /** object field values */
2632 final Object[] objValues;
2633 /** object field value handles */
2634 private final int[] objHandles;
2635
2636 /**
2637 * Creates FieldValues object for reading fields defined in given
2638 * class descriptor.
2639 * @param desc the ObjectStreamClass to read
2640 * @param recordDependencies if true, record the dependencies
2641 * from current PassHandle and the object's read.
2642 * @throws UncheckedIOException if any IOException occurs
2643 */
2644 FieldValues(ObjectStreamClass desc, boolean recordDependencies) throws UncheckedIOException {
2645 try {
2646 this.desc = desc;
2647 int primDataSize = desc.getPrimDataSize();
2648 primValues = (primDataSize > 0) ? new byte[primDataSize] : null;
2649 if (primDataSize > 0) {
2650 bin.readFully(primValues, 0, primDataSize, false);
2651 }
2652
2653
2654 int numObjFields = desc.getNumObjFields();
2655 objValues = (numObjFields > 0) ? new Object[numObjFields] : null;
2656 objHandles = (numObjFields > 0) ? new int[numObjFields] : null;
2657 if (numObjFields > 0) {
2658 int objHandle = passHandle;
2659 ObjectStreamField[] fields = desc.getFields(false);
2660 int numPrimFields = fields.length - objValues.length;
2661 for (int i = 0; i < objValues.length; i++) {
2662 ObjectStreamField f = fields[numPrimFields + i];
2663 objValues[i] = readObject0(Object.class, f.isUnshared());
2664 objHandles[i] = passHandle;
2665 if (recordDependencies && f.getField() != null) {
2666 handles.markDependency(objHandle, passHandle);
2667 }
2668 }
2669 passHandle = objHandle;
2670 }
2671 } catch (IOException ioe) {
2672 throw new UncheckedIOException(ioe);
2673 }
2674 }
2675
2676 public ObjectStreamClass getObjectStreamClass() {
2677 return desc;
2678 }
2679
2680 public boolean defaulted(String name) {
2681 return (getFieldOffset(name, null) < 0);
2682 }
2683
2684 public boolean get(String name, boolean val) {
2685 int off = getFieldOffset(name, Boolean.TYPE);
2686 return (off >= 0) ? ByteArray.getBoolean(primValues, off) : val;
2687 }
2688
2689 public byte get(String name, byte val) {
2690 int off = getFieldOffset(name, Byte.TYPE);
2691 return (off >= 0) ? primValues[off] : val;
2692 }
|