< prev index next >

src/java.base/share/classes/java/io/ObjectInputStream.java

Print this page

   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         }
< prev index next >