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

 193  * <p>Serialization does not read or assign values to the fields of any object
 194  * that does not implement the java.io.Serializable interface.  Subclasses of
 195  * Objects that are not serializable can be serializable. In this case the
 196  * non-serializable class must have a no-arg constructor to allow its fields to
 197  * be initialized.  In this case it is the responsibility of the subclass to
 198  * save and restore the state of the non-serializable class. It is frequently
 199  * the case that the fields of that class are accessible (public, package, or
 200  * protected) or that there are get and set methods that can be used to restore
 201  * the state.
 202  *
 203  * <p>Any exception that occurs while deserializing an object will be caught by
 204  * the ObjectInputStream and abort the reading process.
 205  *
 206  * <p>Implementing the Externalizable interface allows the object to assume
 207  * complete control over the contents and format of the object's serialized
 208  * form.  The methods of the Externalizable interface, writeExternal and
 209  * readExternal, are called to save and restore the objects state.  When
 210  * implemented by a class they can write and read their own state using all of
 211  * the methods of ObjectOutput and ObjectInput.  It is the responsibility of
 212  * the objects to handle any versioning that occurs.


 213  *
 214  * <p>Enum constants are deserialized differently than ordinary serializable or
 215  * externalizable objects.  The serialized form of an enum constant consists
 216  * solely of its name; field values of the constant are not transmitted.  To
 217  * deserialize an enum constant, ObjectInputStream reads the constant name from
 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 {

 408 
 409     /**
 410      * Read an object from the ObjectInputStream.  The class of the object, the
 411      * signature of the class, and the values of the non-transient and
 412      * non-static fields of the class and all of its supertypes are read.
 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 

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  *

 197  * <p>Serialization does not read or assign values to the fields of any object
 198  * that does not implement the java.io.Serializable interface.  Subclasses of
 199  * Objects that are not serializable can be serializable. In this case the
 200  * non-serializable class must have a no-arg constructor to allow its fields to
 201  * be initialized.  In this case it is the responsibility of the subclass to
 202  * save and restore the state of the non-serializable class. It is frequently
 203  * the case that the fields of that class are accessible (public, package, or
 204  * protected) or that there are get and set methods that can be used to restore
 205  * the state.
 206  *
 207  * <p>Any exception that occurs while deserializing an object will be caught by
 208  * the ObjectInputStream and abort the reading process.
 209  *
 210  * <p>Implementing the Externalizable interface allows the object to assume
 211  * complete control over the contents and format of the object's serialized
 212  * form.  The methods of the Externalizable interface, writeExternal and
 213  * readExternal, are called to save and restore the objects state.  When
 214  * implemented by a class they can write and read their own state using all of
 215  * the methods of ObjectOutput and ObjectInput.  It is the responsibility of
 216  * the objects to handle any versioning that occurs.
 217  * Value objects cannot be `java.io.Externalizable` because value objects are
 218  * immutable and `Externalizable.readExternal` is unable to modify the fields of the value.
 219  *
 220  * <p>Enum constants are deserialized differently than ordinary serializable or
 221  * externalizable objects.  The serialized form of an enum constant consists
 222  * solely of its name; field values of the constant are not transmitted.  To
 223  * deserialize an enum constant, ObjectInputStream reads the constant name from
 224  * the stream; the deserialized constant is then obtained by calling the static
 225  * method {@code Enum.valueOf(Class, String)} with the enum constant's
 226  * base type and the received constant name as arguments.  Like other
 227  * serializable or externalizable objects, enum constants can function as the
 228  * targets of back references appearing subsequently in the serialization
 229  * stream.  The process by which enum constants are deserialized cannot be
 230  * customized: any class-specific readObject, readObjectNoData, and readResolve
 231  * methods defined by enum types are ignored during deserialization.
 232  * Similarly, any serialPersistentFields or serialVersionUID field declarations
 233  * are also ignored--all enum types have a fixed serialVersionUID of 0L.
 234  *
 235  * <a id="record-serialization"></a>
 236  * <p>Records are serialized differently than ordinary serializable or externalizable
 237  * objects. During deserialization the record's canonical constructor is invoked
 238  * to construct the record object. Certain serialization-related methods, such
 239  * as readObject and writeObject, are ignored for serializable records. See
 240  * <a href="{@docRoot}/../specs/serialization/serial-arch.html#serialization-of-records">
 241  * <cite>Java Object Serialization Specification,</cite> Section 1.13,
 242  * "Serialization of Records"</a> for additional information.
 243  *
 244  * <p>Value classes are {@linkplain Serializable} through the use of the serialization proxy pattern.
 245  * See {@linkplain ObjectOutputStream##valueclass-serialization value class serialization} for details.
 246  * When the proxy is deserialized it re-constructs and returns the value object.
 247  *
 248  * @spec serialization/index.html Java Object Serialization Specification
 249  * @author      Mike Warres
 250  * @author      Roger Riggs
 251  * @see java.io.DataInput
 252  * @see java.io.ObjectOutputStream
 253  * @see java.io.Serializable
 254  * @see <a href="{@docRoot}/../specs/serialization/input.html">
 255  *      <cite>Java Object Serialization Specification,</cite> Section 3, "Object Input Classes"</a>
 256  * @since   1.1
 257  */
 258 public class ObjectInputStream
 259     extends InputStream implements ObjectInput, ObjectStreamConstants
 260 {
 261     /** handle value representing null */
 262     private static final int NULL_HANDLE = -1;
 263 
 264     /** marker for unshared objects in internal handle table */
 265     private static final Object unsharedMarker = new Object();
 266 
 267     private static class Caches {

 418 
 419     /**
 420      * Read an object from the ObjectInputStream.  The class of the object, the
 421      * signature of the class, and the values of the non-transient and
 422      * non-static fields of the class and all of its supertypes are read.
 423      * Default deserializing for a class can be overridden using the writeObject
 424      * and readObject methods.  Objects referenced by this object are read
 425      * transitively so that a complete equivalent graph of objects is
 426      * reconstructed by readObject.
 427      *
 428      * <p>The root object is completely restored when all of its fields and the
 429      * objects it references are completely restored.  At this point the object
 430      * validation callbacks are executed in order based on their registered
 431      * priorities. The callbacks are registered by objects (in the readObject
 432      * special methods) as they are individually restored.
 433      *
 434      * <p>The deserialization filter, when not {@code null}, is invoked for
 435      * each object (regular or class) read to reconstruct the root object.
 436      * See {@link #setObjectInputFilter(ObjectInputFilter) setObjectInputFilter} for details.
 437      *
 438      * <p>Serialization and deserialization of value classes is described in
 439      * {@linkplain ObjectOutputStream##valueclass-serialization value class serialization}.
 440      *
 441      * @implSpec
 442      * When enabled with {@code --enable-preview}, serialization and deserialization of
 443      * Core Library value classes migrated from pre-JEP 401 identity classes is
 444      * implementation specific.
 445      *
 446      * <p>Exceptions are thrown for problems with the InputStream and for
 447      * classes that should not be deserialized.  All exceptions are fatal to
 448      * the InputStream and leave it in an indeterminate state; it is up to the
 449      * caller to ignore or recover the stream state.
 450      *
 451      * @throws  ClassNotFoundException Class of a serialized object cannot be
 452      *          found.
 453      * @throws  InvalidClassException Something is wrong with a class used by
 454      *          deserialization.
 455      * @throws  StreamCorruptedException Control information in the
 456      *          stream is inconsistent.
 457      * @throws  OptionalDataException Primitive data was found in the
 458      *          stream instead of objects.
 459      * @throws  IOException Any of the usual Input/Output related exceptions.
 460      */
 461     public final Object readObject()
 462         throws IOException, ClassNotFoundException {
 463         return readObject(Object.class);
 464     }
 465 

2108      * class is unresolvable (in which case a ClassNotFoundException will be
2109      * associated with object's handle).  Sets passHandle to object's assigned
2110      * handle.
2111      */
2112     private Object readOrdinaryObject(boolean unshared)
2113         throws IOException
2114     {
2115         if (bin.readByte() != TC_OBJECT) {
2116             throw new InternalError();
2117         }
2118 
2119         ObjectStreamClass desc = readClassDesc(false);
2120         desc.checkDeserialize();
2121 
2122         Class<?> cl = desc.forClass();
2123         if (cl == String.class || cl == Class.class
2124                 || cl == ObjectStreamClass.class) {
2125             throw new InvalidClassException("invalid class descriptor");
2126         }
2127 
2128         // Assign the handle and initially set to null or the unsharedMarker
2129         passHandle = handles.assign(unshared ? unsharedMarker : null);







2130         ClassNotFoundException resolveEx = desc.getResolveException();
2131         if (resolveEx != null) {
2132             handles.markException(passHandle, resolveEx);
2133         }
2134 
2135         try {
2136             // Dispatch on the factory mode to read an object from the stream.
2137             Object obj = switch (desc.factoryMode()) {
2138                 case READ_OBJECT_DEFAULT -> readSerialDefaultObject(desc, unshared);
2139                 case READ_OBJECT_CUSTOM -> readSerialCustomData(desc, unshared);
2140                 case READ_RECORD -> readRecord(desc, unshared);
2141                 case READ_EXTERNALIZABLE -> readExternalObject(desc, unshared);
2142                 case READ_OBJECT_VALUE -> readObjectValue(desc, unshared);
2143                 case READ_NO_LOCAL_CLASS -> readAbsentLocalClass(desc, unshared);
2144                 case null -> throw new AssertionError("Unknown factoryMode for: " + desc.getName(),
2145                         resolveEx);
2146             };
2147 
2148             handles.finish(passHandle);
2149 
2150             if (obj != null &&
2151                 handles.lookupException(passHandle) == null &&
2152                 desc.hasReadResolveMethod())
2153             {
2154                 Object rep = desc.invokeReadResolve(obj);
2155                 if (unshared && rep.getClass().isArray()) {
2156                     rep = cloneArray(rep);
2157                 }
2158                 if (rep != obj) {
2159                     // Filter the replacement object
2160                     if (rep != null) {
2161                         if (rep.getClass().isArray()) {
2162                             filterCheck(rep.getClass(), Array.getLength(rep));
2163                         } else {
2164                             filterCheck(rep.getClass(), -1);
2165                         }
2166                     }
2167                     handles.setObject(passHandle, obj = rep);
2168                 }

2169             }
2170 
2171             return obj;
2172         } catch (UncheckedIOException uioe) {
2173             // Consistent re-throw for nested UncheckedIOExceptions
2174             throw uioe.getCause();
2175         }
2176     }
2177 
2178     /**
2179      * {@return a value class instance by invoking its constructor with field values read from the stream.
2180      * The fields of the class in the stream are matched to the local fields and applied to
2181      * the constructor.
2182      * If the stream contains superclasses with serializable fields,
2183      * an InvalidClassException is thrown with an incompatible class change message.
2184      *
2185      * @param desc the class descriptor read from the stream, the local class is a value class
2186      * @param unshared if the object is not to be shared
2187      * @throws InvalidClassException if the stream contains a superclass with serializable fields.
2188      * @throws IOException if there are I/O errors while reading from the
2189      *         underlying {@code InputStream}
2190      */
2191     private Object readObjectValue(ObjectStreamClass desc, boolean unshared) throws IOException {
2192         final ObjectStreamClass localDesc = desc.getLocalDesc();
2193         // Check for un-expected fields in superclasses
2194         List<ClassDataSlot> slots = desc.getClassDataLayout();
2195         for (int i = 0; i < slots.size()-1; i++) {
2196             ClassDataSlot slot = slots.get(i);
2197             if (slot.hasData && slot.desc.getFields(false).length > 0) {
2198                 throw new InvalidClassException("incompatible class change to value class: " +
2199                         "stream class has non-empty super type: " + desc.getName());
2200             }
2201         }
2202         // Read values for the value class fields
2203         FieldValues fieldValues = new FieldValues(desc, true);
2204 
2205         // Get value object constructor adapted to take primitive value buffer and object array.
2206         MethodHandle consMH = ConstructorSupport.deserializationValueCons(desc);
2207         try {
2208             Object obj = (Object) consMH.invokeExact(fieldValues.primValues, fieldValues.objValues);
2209             if (!unshared)
2210                 handles.setObject(passHandle, obj);
2211             return obj;
2212         } catch (Exception e) {
2213             throw new InvalidObjectException(e.getMessage(), e);
2214         } catch (Error e) {
2215             throw e;
2216         } catch (Throwable t) {
2217             throw new InvalidObjectException("ReflectiveOperationException " +
2218                     "during deserialization", t);
2219         }
2220     }
2221 
2222     /**
2223      * Creates a new object and invokes its readExternal method to read its contents.
2224      *
2225      * If the class is instantiable, read externalizable data by invoking readExternal()
2226      * method of obj; otherwise, attempts to skip over externalizable data.
2227      * Expects that passHandle is set to obj's handle before this method is
2228      * called.  The new object is entered in the handle table immediately,
2229      * allowing it to leak before it is completely read.
2230      */
2231     private Object readExternalObject(ObjectStreamClass desc, boolean unshared)
2232         throws IOException
2233     {
2234         // For Externalizable objects,
2235         // create the instance, publish the ref, and read the data
2236         Externalizable obj = null;
2237         try {
2238             if (desc.isInstantiable()) {
2239                 obj = (Externalizable) desc.newInstance();
2240             }
2241         } catch (Exception ex) {
2242             throw new InvalidClassException(desc.getName(),
2243                     "unable to create instance", ex);
2244         }
2245 
2246         if (!unshared)
2247             handles.setObject(passHandle, obj);
2248 
2249         SerialCallbackContext oldContext = curContext;
2250         if (oldContext != null)
2251             oldContext.check();
2252         curContext = null;
2253         try {
2254             boolean blocked = desc.hasBlockExternalData();
2255             if (blocked) {
2256                 bin.setBlockDataMode(true);
2257             }
2258             if (obj != null) {
2259                 try {
2260                     obj.readExternal(this);
2261                 } catch (ClassNotFoundException ex) {
2262                     /*
2263                      * In most cases, the handle table has already propagated
2264                      * a CNFException to passHandle at this point; this mark
2265                      * call is included to address cases where the readExternal
2266                      * method has cons'ed and thrown a new CNFException of its
2267                      * own.
2268                      */

2272             if (blocked) {
2273                 skipCustomData();
2274             }
2275         } finally {
2276             if (oldContext != null)
2277                 oldContext.check();
2278             curContext = oldContext;
2279         }
2280         /*
2281          * At this point, if the externalizable data was not written in
2282          * block-data form and either the externalizable class doesn't exist
2283          * locally (i.e., obj == null) or readExternal() just threw a
2284          * CNFException, then the stream is probably in an inconsistent state,
2285          * since some (or all) of the externalizable data may not have been
2286          * consumed.  Since there's no "correct" action to take in this case,
2287          * we mimic the behavior of past serialization implementations and
2288          * blindly hope that the stream is in sync; if it isn't and additional
2289          * externalizable data remains in the stream, a subsequent read will
2290          * most likely throw a StreamCorruptedException.
2291          */
2292         return obj;
2293     }
2294 
2295     /**
2296      * Reads and returns a record.
2297      * If an exception is marked for any of the fields, the dependency
2298      * mechanism marks the record as having an exception.
2299      * Null is returned from readRecord and later the exception is thrown at
2300      * the exit of {@link #readObject(Class)}.
2301      */
2302     private Object readRecord(ObjectStreamClass desc, boolean unshared) throws IOException {
2303         List<ClassDataSlot> slots = desc.getClassDataLayout();
2304         if (slots.size() != 1) {
2305             // skip any superclass stream field values
2306             for (int i = 0; i < slots.size()-1; i++) {
2307                 if (slots.get(i).hasData) {
2308                     new FieldValues(slots.get(i).desc, true);
2309                 }
2310             }
2311         }
2312 
2313         FieldValues fieldValues = new FieldValues(desc, true);
2314         if (handles.lookupException(passHandle) != null) {
2315             return null;     // slot marked with exception, don't create record
2316         }
2317 
2318         // get canonical record constructor adapted to take two arguments:
2319         // - byte[] primValues
2320         // - Object[] objValues
2321         // and return Object
2322         MethodHandle ctrMH = ConstructorSupport.deserializationCtr(desc);
2323 
2324         try {
2325             Object obj = (Object) ctrMH.invokeExact(fieldValues.primValues, fieldValues.objValues);
2326             if (!unshared)
2327                 handles.setObject(passHandle, obj);
2328             return obj;
2329         } catch (Exception e) {
2330             throw new InvalidObjectException(e.getMessage(), e);
2331         } catch (Error e) {
2332             throw e;
2333         } catch (Throwable t) {
2334             throw new InvalidObjectException("ReflectiveOperationException " +
2335                                              "during deserialization", t);
2336         }
2337     }
2338 
2339     /**
2340      * Construct an object from the stream for a class that has only default read object behaviors.
2341      * For each object, the fields are read before any are assigned.
2342      * The new instance is entered in the handle table if it is unshared,
2343      * allowing it to escape before it is initialized.
2344      * The `readObject` and `readObjectNoData` methods are not present and are not called.
2345      *
2346      * @param desc the class descriptor
2347      * @param unshared true if the object should be shared
2348      * @return the object constructed from the stream data
2349      * @throws IOException if there are I/O errors while reading from the
2350      *         underlying {@code InputStream}
2351      * @throws InvalidClassException if the instance creation fails
2352      */
2353     private Object readSerialDefaultObject(ObjectStreamClass desc, boolean unshared)
2354             throws IOException, InvalidClassException {
2355         if (!desc.isInstantiable()) {
2356             // No local class to create, read and discard
2357             return readAbsentLocalClass(desc, unshared);
2358         }
2359         try {
2360             final Object obj = desc.newInstance();
2361             if (!unshared)
2362                 handles.setObject(passHandle, obj);
2363 
2364             // Best effort Failure Atomicity; slotValues will be non-null if field
2365             // values can be set after reading all field data in the hierarchy.
2366             List<ClassDataSlot> slots = desc.getClassDataLayout();
2367             List<FieldValues> slotValues = new ArrayList<>(slots.size());
2368             for (ClassDataSlot s : slots) {
2369                 if (s.hasData) {
2370                     var value = new FieldValues(s.desc, true);
2371                     finishBlockData(s.desc);
2372                     slotValues.add(value);
2373                 }
2374             }
2375 
2376             if (handles.lookupException(passHandle) != null) {
2377                 return null;    // some exception for a class, do not return the object
2378             }
2379 
2380             // Check that the types are assignable for all slots before assigning.
2381             for (FieldValues slotValue : slotValues) {
2382                 slotValue.defaultCheckFieldValues(obj);
2383             }
2384             for (FieldValues v : slotValues) {
2385                 v.defaultSetFieldValues(obj);
2386             }
2387             return obj;
2388         } catch (InstantiationException | InvocationTargetException ex) {
2389             throw new InvalidClassException(desc.forClass().getName(),
2390                     "unable to create instance", ex);
2391         }
2392     }
2393 
2394 
2395     /**
2396      * Reads (or attempts to skip, if not instantiatable or is tagged with a
2397      * ClassNotFoundException) instance data for each serializable class of
2398      * object in stream, from superclass to subclass.
2399      * Expects that passHandle is set to current handle before this method is called.
2400      */
2401     private Object readSerialCustomData(ObjectStreamClass desc, boolean unshared)
2402         throws IOException
2403     {
2404         if (!desc.isInstantiable()) {
2405             // No local class to create, read and discard
2406             return readAbsentLocalClass(desc, unshared);













2407         }






2408 
2409         try {
2410             Object obj = desc.newInstance();
2411             if (!unshared)
2412                 handles.setObject(passHandle, obj);
2413             // Read data into each of the slots for the class
2414             return readSerialCustomSlots(obj, desc.getClassDataLayout());
2415         } catch (InstantiationException | InvocationTargetException ex) {
2416             throw new InvalidClassException(desc.forClass().getName(),
2417                     "unable to create instance", ex);
2418         }
2419     }

















2420 
2421     /**
2422      * Reads from the stream using custom or default readObject methods appropriate.
2423      * For each slot, either the custom readObject method or the default reader of fields
2424      * is invoked. Unused slot specific custom data is discarded.
2425      * This function is used by {@link #readSerialCustomData}.
2426      *
2427      * @param obj the object to assign the values to
2428      * @param slots a list of slots to read from the stream
2429      * @return the object being initialized
2430      * @throws IOException if there are I/O errors while reading from the
2431      *         underlying {@code InputStream}
2432      */
2433     private Object readSerialCustomSlots(Object obj, List<ClassDataSlot> slots) throws IOException {
2434 
2435         for (ClassDataSlot slot : slots) {
2436             ObjectStreamClass slotDesc = slot.desc;
2437             if (slot.hasData) {
2438                 if (slotDesc.hasReadObjectMethod() &&
2439                         handles.lookupException(passHandle) == null) {
2440                     // Invoke slot custom readObject method
2441                     readSlotViaReadObject(obj, slotDesc);
2442                 } else {
2443                     // Read fields of the current descriptor into a new FieldValues
2444                     FieldValues values = new FieldValues(slotDesc, true);
2445                     if (handles.lookupException(passHandle) == null) {
2446                         // Set the instance fields if no previous exception
2447                         values.defaultCheckFieldValues(obj);
2448                         values.defaultSetFieldValues(obj);




2449                     }
2450                     finishBlockData(slotDesc);





2451                 }
2452             } else {
2453                 if (slotDesc.hasReadObjectNoDataMethod() &&
2454                         handles.lookupException(passHandle) == null) {


2455                     slotDesc.invokeReadObjectNoData(obj);
2456                 }
2457             }
2458         }
2459         return obj;
2460     }
2461 
2462     /**
2463      * Invoke the readObject method of the class to read and store the state from the stream.
2464      *
2465      * @param obj an instance of the class being created, only partially initialized.
2466      * @param slotDesc the ObjectStreamDescriptor for the current class
2467      * @throws IOException if there are I/O errors while reading from the
2468      *         underlying {@code InputStream}
2469      */
2470     private void readSlotViaReadObject(Object obj, ObjectStreamClass slotDesc) throws IOException {
2471         assert obj != null : "readSlotViaReadObject called when obj == null";
2472 
2473         SerialCallbackContext oldContext = curContext;
2474         if (oldContext != null)
2475             oldContext.check();
2476         try {
2477             curContext = new SerialCallbackContext(obj, slotDesc);
2478 
2479             bin.setBlockDataMode(true);
2480             slotDesc.invokeReadObject(obj, this);
2481         } catch (ClassNotFoundException ex) {
2482             /*
2483              * In most cases, the handle table has already
2484              * propagated a CNFException to passHandle at this
2485              * point; this mark call is included to address cases
2486              * where the custom readObject method has cons'ed and
2487              * thrown a new CNFException of its own.
2488              */
2489             handles.markException(passHandle, ex);
2490         } finally {
2491             curContext.setUsed();
2492             if (oldContext!= null)
2493                 oldContext.check();
2494             curContext = oldContext;
2495         }
2496 
2497         /*
2498          * defaultDataEnd may have been set indirectly by custom
2499          * readObject() method when calling defaultReadObject() or
2500          * readFields(); clear it to restore normal read behavior.
2501          */
2502         defaultDataEnd = false;
2503 
2504         finishBlockData(slotDesc);
2505     }
2506 
2507 
2508     /**
2509      * Read and discard an entire object, leaving a null reference in the HandleTable.
2510      * The descriptor of the class in the stream is used to read the fields from the stream.
2511      * There is no instance in which to store the field values.
2512      * Custom data following the fields of any slot is read and discarded.
2513      * References to nested objects are read and retained in the
2514      * handle table using the regular mechanism.
2515      * Handles later in the stream may refer to the nested objects.
2516      *
2517      * @param desc the stream class descriptor
2518      * @param unshared the unshared flag, ignored since no object is created
2519      * @return null, no object is created
2520      * @throws IOException if there are I/O errors while reading from the
2521      *         underlying {@code InputStream}
2522      */
2523     private Object readAbsentLocalClass(ObjectStreamClass desc, boolean unshared)
2524             throws IOException {
2525         desc.getClassDataLayout().stream()
2526                 .filter(s -> s.hasData)
2527                 .forEach(s2 -> {new FieldValues(s2.desc, true); finishBlockData(s2.desc);});
2528         return null;
2529     }
2530 
2531     // Finish handling of block data by skipping any remaining and setting BlockDataMode = false
2532     private void finishBlockData(ObjectStreamClass slotDesc) throws UncheckedIOException {
2533         try {
2534             if (slotDesc.hasWriteObjectData()) {
2535                 skipCustomData();
2536             } else {
2537                 bin.setBlockDataMode(false);
2538             }
2539         } catch (IOException ioe) {
2540             throw new UncheckedIOException(ioe);
2541         }
2542     }
2543 
2544     /**
2545      * Skips over all block data and objects until TC_ENDBLOCKDATA is
2546      * encountered.
2547      */
2548     private void skipCustomData() throws IOException {
2549         int oldHandle = passHandle;
2550         for (;;) {
2551             if (bin.getBlockDataMode()) {
2552                 bin.skipBlockData();
2553                 bin.setBlockDataMode(false);
2554             }
2555             switch (bin.peekByte()) {
2556                 case TC_BLOCKDATA:
2557                 case TC_BLOCKDATALONG:
2558                     bin.setBlockDataMode(true);
2559                     break;
2560 

2616     /**
2617      * Default GetField implementation.
2618      */
2619     private final class FieldValues extends GetField {
2620 
2621         /** class descriptor describing serializable fields */
2622         private final ObjectStreamClass desc;
2623         /** primitive field values */
2624         final byte[] primValues;
2625         /** object field values */
2626         final Object[] objValues;
2627         /** object field value handles */
2628         private final int[] objHandles;
2629 
2630         /**
2631          * Creates FieldValues object for reading fields defined in given
2632          * class descriptor.
2633          * @param desc the ObjectStreamClass to read
2634          * @param recordDependencies if true, record the dependencies
2635          *                           from current PassHandle and the object's read.
2636          * @throws UncheckedIOException if any IOException occurs
2637          */
2638         FieldValues(ObjectStreamClass desc, boolean recordDependencies) throws UncheckedIOException {
2639             try {
2640                 this.desc = desc;
2641                 int primDataSize = desc.getPrimDataSize();
2642                 primValues = (primDataSize > 0) ? new byte[primDataSize] : null;
2643                 if (primDataSize > 0) {
2644                     bin.readFully(primValues, 0, primDataSize, false);
2645                 }
2646 





2647 
2648                 int numObjFields = desc.getNumObjFields();
2649                 objValues = (numObjFields > 0) ? new Object[numObjFields] : null;
2650                 objHandles = (numObjFields > 0) ? new int[numObjFields] : null;
2651                 if (numObjFields > 0) {
2652                     int objHandle = passHandle;
2653                     ObjectStreamField[] fields = desc.getFields(false);
2654                     int numPrimFields = fields.length - objValues.length;
2655                     for (int i = 0; i < objValues.length; i++) {
2656                         ObjectStreamField f = fields[numPrimFields + i];
2657                         objValues[i] = readObject0(Object.class, f.isUnshared());
2658                         objHandles[i] = passHandle;
2659                         if (recordDependencies && f.getField() != null) {
2660                             handles.markDependency(objHandle, passHandle);
2661                         }
2662                     }
2663                     passHandle = objHandle;
2664                 }
2665             } catch (IOException ioe) {
2666                 throw new UncheckedIOException(ioe);
2667             }
2668         }
2669 
2670         public ObjectStreamClass getObjectStreamClass() {
2671             return desc;
2672         }
2673 
2674         public boolean defaulted(String name) {
2675             return (getFieldOffset(name, null) < 0);
2676         }
2677 
2678         public boolean get(String name, boolean val) {
2679             int off = getFieldOffset(name, Boolean.TYPE);
2680             return (off >= 0) ? ByteArray.getBoolean(primValues, off) : val;
2681         }
2682 
2683         public byte get(String name, byte val) {
2684             int off = getFieldOffset(name, Byte.TYPE);
2685             return (off >= 0) ? primValues[off] : val;
2686         }
< prev index next >