• ObjectInputStream


    ObjectInputStream

    /*
     * Copyright (c) 1996, 2017, Oracle and/or its affiliates. All rights reserved.
     * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
     */
    
    package java.io;
    
    import java.io.ObjectStreamClass.WeakClassKey;
    import java.lang.ref.ReferenceQueue;
    import java.lang.reflect.Array;
    import java.lang.reflect.Modifier;
    import java.lang.reflect.Proxy;
    import java.security.AccessControlContext;
    import java.security.AccessController;
    import java.security.PrivilegedAction;
    import java.security.PrivilegedActionException;
    import java.security.PrivilegedExceptionAction;
    import java.util.Arrays;
    import java.util.HashMap;
    import java.util.Objects;
    import java.util.concurrent.ConcurrentHashMap;
    import java.util.concurrent.ConcurrentMap;
    
    import static java.io.ObjectStreamClass.processQueue;
    
    import sun.misc.ObjectInputFilter;
    import sun.misc.ObjectStreamClassValidator;
    import sun.misc.SharedSecrets;
    import sun.reflect.misc.ReflectUtil;
    import sun.misc.JavaOISAccess;
    import sun.util.logging.PlatformLogger;
    
    /**
     * An ObjectInputStream deserializes primitive data and objects previously
     * written using an ObjectOutputStream.
     *
     * <p>ObjectOutputStream and ObjectInputStream can provide an application with
     * persistent storage for graphs of objects when used with a FileOutputStream
     * and FileInputStream respectively.  ObjectInputStream is used to recover
     * those objects previously serialized. Other uses include passing objects
     * between hosts using a socket stream or for marshaling and unmarshaling
     * arguments and parameters in a remote communication system.
     *
     * <p>ObjectInputStream ensures that the types of all objects in the graph
     * created from the stream match the classes present in the Java Virtual
     * Machine.  Classes are loaded as required using the standard mechanisms.
     *
     * <p>Only objects that support the java.io.Serializable or
     * java.io.Externalizable interface can be read from streams.
     *
     * <p>The method <code>readObject</code> is used to read an object from the
     * stream.  Java's safe casting should be used to get the desired type.  In
     * Java, strings and arrays are objects and are treated as objects during
     * serialization. When read they need to be cast to the expected type.
     *
     * <p>Primitive data types can be read from the stream using the appropriate
     * method on DataInput.
     *
     * <p>The default deserialization mechanism for objects restores the contents
     * of each field to the value and type it had when it was written.  Fields
     * declared as transient or static are ignored by the deserialization process.
     * References to other objects cause those objects to be read from the stream
     * as necessary.  Graphs of objects are restored correctly using a reference
     * sharing mechanism.  New objects are always allocated when deserializing,
     * which prevents existing objects from being overwritten.
     *
     * <p>Reading an object is analogous to running the constructors of a new
     * object.  Memory is allocated for the object and initialized to zero (NULL).
     * No-arg constructors are invoked for the non-serializable classes and then
     * the fields of the serializable classes are restored from the stream starting
     * with the serializable class closest to java.lang.object and finishing with
     * the object's most specific class.
     *
     * <p>For example to read from a stream as written by the example in
     * ObjectOutputStream:
     * <br>
     * <pre>
     *      FileInputStream fis = new FileInputStream("t.tmp");
     *      ObjectInputStream ois = new ObjectInputStream(fis);
     *
     *      int i = ois.readInt();
     *      String today = (String) ois.readObject();
     *      Date date = (Date) ois.readObject();
     *
     *      ois.close();
     * </pre>
     *
     * <p>Classes control how they are serialized by implementing either the
     * java.io.Serializable or java.io.Externalizable interfaces.
     *
     * <p>Implementing the Serializable interface allows object serialization to
     * save and restore the entire state of the object and it allows classes to
     * evolve between the time the stream is written and the time it is read.  It
     * automatically traverses references between objects, saving and restoring
     * entire graphs.
     *
     * <p>Serializable classes that require special handling during the
     * serialization and deserialization process should implement the following
     * methods:
     *
     * <pre>
     * private void writeObject(java.io.ObjectOutputStream stream)
     *     throws IOException;
     * private void readObject(java.io.ObjectInputStream stream)
     *     throws IOException, ClassNotFoundException;
     * private void readObjectNoData()
     *     throws ObjectStreamException;
     * </pre>
     *
     * <p>The readObject method is responsible for reading and restoring the state
     * of the object for its particular class using data written to the stream by
     * the corresponding writeObject method.  The method does not need to concern
     * itself with the state belonging to its superclasses or subclasses.  State is
     * restored by reading data from the ObjectInputStream for the individual
     * fields and making assignments to the appropriate fields of the object.
     * Reading primitive data types is supported by DataInput.
     *
     * <p>Any attempt to read object data which exceeds the boundaries of the
     * custom data written by the corresponding writeObject method will cause an
     * OptionalDataException to be thrown with an eof field value of true.
     * Non-object reads which exceed the end of the allotted data will reflect the
     * end of data in the same way that they would indicate the end of the stream:
     * bytewise reads will return -1 as the byte read or number of bytes read, and
     * primitive reads will throw EOFExceptions.  If there is no corresponding
     * writeObject method, then the end of default serialized data marks the end of
     * the allotted data.
     *
     * <p>Primitive and object read calls issued from within a readExternal method
     * behave in the same manner--if the stream is already positioned at the end of
     * data written by the corresponding writeExternal method, object reads will
     * throw OptionalDataExceptions with eof set to true, bytewise reads will
     * return -1, and primitive reads will throw EOFExceptions.  Note that this
     * behavior does not hold for streams written with the old
     * <code>ObjectStreamConstants.PROTOCOL_VERSION_1</code> protocol, in which the
     * end of data written by writeExternal methods is not demarcated, and hence
     * cannot be detected.
     *
     * <p>The readObjectNoData method is responsible for initializing the state of
     * the object for its particular class in the event that the serialization
     * stream does not list the given class as a superclass of the object being
     * deserialized.  This may occur in cases where the receiving party uses a
     * different version of the deserialized instance's class than the sending
     * party, and the receiver's version extends classes that are not extended by
     * the sender's version.  This may also occur if the serialization stream has
     * been tampered; hence, readObjectNoData is useful for initializing
     * deserialized objects properly despite a "hostile" or incomplete source
     * stream.
     *
     * <p>Serialization does not read or assign values to the fields of any object
     * that does not implement the java.io.Serializable interface.  Subclasses of
     * Objects that are not serializable can be serializable. In this case the
     * non-serializable class must have a no-arg constructor to allow its fields to
     * be initialized.  In this case it is the responsibility of the subclass to
     * save and restore the state of the non-serializable class. It is frequently
     * the case that the fields of that class are accessible (public, package, or
     * protected) or that there are get and set methods that can be used to restore
     * the state.
     *
     * <p>Any exception that occurs while deserializing an object will be caught by
     * the ObjectInputStream and abort the reading process.
     *
     * <p>Implementing the Externalizable interface allows the object to assume
     * complete control over the contents and format of the object's serialized
     * form.  The methods of the Externalizable interface, writeExternal and
     * readExternal, are called to save and restore the objects state.  When
     * implemented by a class they can write and read their own state using all of
     * the methods of ObjectOutput and ObjectInput.  It is the responsibility of
     * the objects to handle any versioning that occurs.
     *
     * <p>Enum constants are deserialized differently than ordinary serializable or
     * externalizable objects.  The serialized form of an enum constant consists
     * solely of its name; field values of the constant are not transmitted.  To
     * deserialize an enum constant, ObjectInputStream reads the constant name from
     * the stream; the deserialized constant is then obtained by calling the static
     * method <code>Enum.valueOf(Class, String)</code> with the enum constant's
     * base type and the received constant name as arguments.  Like other
     * serializable or externalizable objects, enum constants can function as the
     * targets of back references appearing subsequently in the serialization
     * stream.  The process by which enum constants are deserialized cannot be
     * customized: any class-specific readObject, readObjectNoData, and readResolve
     * methods defined by enum types are ignored during deserialization.
     * Similarly, any serialPersistentFields or serialVersionUID field declarations
     * are also ignored--all enum types have a fixed serialVersionUID of 0L.
     *
     * @author      Mike Warres
     * @author      Roger Riggs
     * @see java.io.DataInput
     * @see java.io.ObjectOutputStream
     * @see java.io.Serializable
     * @see <a href="../../../platform/serialization/spec/input.html"> Object Serialization Specification, Section 3, Object Input Classes</a>
     * @since   JDK1.1
     */
    public class ObjectInputStream
        extends InputStream implements ObjectInput, ObjectStreamConstants
    {
        /** handle value representing null */
        private static final int NULL_HANDLE = -1;
    
        /** marker for unshared objects in internal handle table */
        private static final Object unsharedMarker = new Object();
    
        /** table mapping primitive type names to corresponding class objects */
        private static final HashMap<String, Class<?>> primClasses
            = new HashMap<>(8, 1.0F);
        static {
            primClasses.put("boolean", boolean.class);
            primClasses.put("byte", byte.class);
            primClasses.put("char", char.class);
            primClasses.put("short", short.class);
            primClasses.put("int", int.class);
            primClasses.put("long", long.class);
            primClasses.put("float", float.class);
            primClasses.put("double", double.class);
            primClasses.put("void", void.class);
        }
    
        private static class Caches {
            /** cache of subclass security audit results */
            static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits =
                new ConcurrentHashMap<>();
    
            /** queue for WeakReferences to audited subclasses */
            static final ReferenceQueue<Class<?>> subclassAuditsQueue =
                new ReferenceQueue<>();
        }
    
        static {
            /* Setup access so sun.misc can invoke package private functions. */
            sun.misc.SharedSecrets.setJavaOISAccess(new JavaOISAccess() {
                public void setObjectInputFilter(ObjectInputStream stream, ObjectInputFilter filter) {
                    stream.setInternalObjectInputFilter(filter);
                }
    
                public ObjectInputFilter getObjectInputFilter(ObjectInputStream stream) {
                    return stream.getInternalObjectInputFilter();
                }
            });
        }
    
        /*
         * Separate class to defer initialization of logging until needed.
         */
        private static class Logging {
    
            /*
             * Logger for ObjectInputFilter results.
             * Setup the filter logger if it is set to INFO or WARNING.
             * (Assuming it will not change).
             */
            private static final PlatformLogger traceLogger;
            private static final PlatformLogger infoLogger;
            static {
                PlatformLogger filterLog = PlatformLogger.getLogger("java.io.serialization");
                infoLogger = (filterLog != null &&
                    filterLog.isLoggable(PlatformLogger.Level.INFO)) ? filterLog : null;
                traceLogger = (filterLog != null &&
                    filterLog.isLoggable(PlatformLogger.Level.FINER)) ? filterLog : null;
            }
        }
    
        /** filter stream for handling block data conversion */
        private final BlockDataInputStream bin;
        /** validation callback list */
        private final ValidationList vlist;
        /** recursion depth */
        private long depth;
        /** Total number of references to any type of object, class, enum, proxy, etc. */
        private long totalObjectRefs;
        /** whether stream is closed */
        private boolean closed;
    
        /** wire handle -> obj/exception map */
        private final HandleTable handles;
        /** scratch field for passing handle values up/down call stack */
        private int passHandle = NULL_HANDLE;
        /** flag set when at end of field value block with no TC_ENDBLOCKDATA */
        private boolean defaultDataEnd = false;
    
        /** buffer for reading primitive field values */
        private byte[] primVals;
    
        /** if true, invoke readObjectOverride() instead of readObject() */
        private final boolean enableOverride;
        /** if true, invoke resolveObject() */
        private boolean enableResolve;
    
        /**
         * Context during upcalls to class-defined readObject methods; holds
         * object currently being deserialized and descriptor for current class.
         * Null when not during readObject upcall.
         */
        private SerialCallbackContext curContext;
    
        /**
         * Filter of class descriptors and classes read from the stream;
         * may be null.
         */
        private ObjectInputFilter serialFilter;
    
        /**
         * Creates an ObjectInputStream that reads from the specified InputStream.
         * A serialization stream header is read from the stream and verified.
         * This constructor will block until the corresponding ObjectOutputStream
         * has written and flushed the header.
         *
         * <p>If a security manager is installed, this constructor will check for
         * the "enableSubclassImplementation" SerializablePermission when invoked
         * directly or indirectly by the constructor of a subclass which overrides
         * the ObjectInputStream.readFields or ObjectInputStream.readUnshared
         * methods.
         *
         * @param   in input stream to read from
         * @throws  StreamCorruptedException if the stream header is incorrect
         * @throws  IOException if an I/O error occurs while reading stream header
         * @throws  SecurityException if untrusted subclass illegally overrides
         *          security-sensitive methods
         * @throws  NullPointerException if <code>in</code> is <code>null</code>
         * @see     ObjectInputStream#ObjectInputStream()
         * @see     ObjectInputStream#readFields()
         * @see     ObjectOutputStream#ObjectOutputStream(OutputStream)
         */
        public ObjectInputStream(InputStream in) throws IOException {
            verifySubclass();
            bin = new BlockDataInputStream(in);
            handles = new HandleTable(10);
            vlist = new ValidationList();
            serialFilter = ObjectInputFilter.Config.getSerialFilter();
            enableOverride = false;
            readStreamHeader();
            bin.setBlockDataMode(true);
        }
    
        /**
         * Provide a way for subclasses that are completely reimplementing
         * ObjectInputStream to not have to allocate private data just used by this
         * implementation of ObjectInputStream.
         *
         * <p>If there is a security manager installed, this method first calls the
         * security manager's <code>checkPermission</code> method with the
         * <code>SerializablePermission("enableSubclassImplementation")</code>
         * permission to ensure it's ok to enable subclassing.
         *
         * @throws  SecurityException if a security manager exists and its
         *          <code>checkPermission</code> method denies enabling
         *          subclassing.
         * @throws  IOException if an I/O error occurs while creating this stream
         * @see SecurityManager#checkPermission
         * @see java.io.SerializablePermission
         */
        protected ObjectInputStream() throws IOException, SecurityException {
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
            }
            bin = null;
            handles = null;
            vlist = null;
            serialFilter = ObjectInputFilter.Config.getSerialFilter();
            enableOverride = true;
        }
    
        /**
         * Read an object from the ObjectInputStream.  The class of the object, the
         * signature of the class, and the values of the non-transient and
         * non-static fields of the class and all of its supertypes are read.
         * Default deserializing for a class can be overridden using the writeObject
         * and readObject methods.  Objects referenced by this object are read
         * transitively so that a complete equivalent graph of objects is
         * reconstructed by readObject.
         *
         * <p>The root object is completely restored when all of its fields and the
         * objects it references are completely restored.  At this point the object
         * validation callbacks are executed in order based on their registered
         * priorities. The callbacks are registered by objects (in the readObject
         * special methods) as they are individually restored.
         *
         * <p>Exceptions are thrown for problems with the InputStream and for
         * classes that should not be deserialized.  All exceptions are fatal to
         * the InputStream and leave it in an indeterminate state; it is up to the
         * caller to ignore or recover the stream state.
         *
         * @throws  ClassNotFoundException Class of a serialized object cannot be
         *          found.
         * @throws  InvalidClassException Something is wrong with a class used by
         *          serialization.
         * @throws  StreamCorruptedException Control information in the
         *          stream is inconsistent.
         * @throws  OptionalDataException Primitive data was found in the
         *          stream instead of objects.
         * @throws  IOException Any of the usual Input/Output related exceptions.
         */
        public final Object readObject()
            throws IOException, ClassNotFoundException
        {
            if (enableOverride) {
                return readObjectOverride();
            }
    
            // if nested read, passHandle contains handle of enclosing object
            int outerHandle = passHandle;
            try {
                Object obj = readObject0(false);
                handles.markDependency(outerHandle, passHandle);
                ClassNotFoundException ex = handles.lookupException(passHandle);
                if (ex != null) {
                    throw ex;
                }
                if (depth == 0) {
                    vlist.doCallbacks();
                }
                return obj;
            } finally {
                passHandle = outerHandle;
                if (closed && depth == 0) {
                    clear();
                }
            }
        }
    
        /**
         * This method is called by trusted subclasses of ObjectOutputStream that
         * constructed ObjectOutputStream using the protected no-arg constructor.
         * The subclass is expected to provide an override method with the modifier
         * "final".
         *
         * @return  the Object read from the stream.
         * @throws  ClassNotFoundException Class definition of a serialized object
         *          cannot be found.
         * @throws  OptionalDataException Primitive data was found in the stream
         *          instead of objects.
         * @throws  IOException if I/O errors occurred while reading from the
         *          underlying stream
         * @see #ObjectInputStream()
         * @see #readObject()
         * @since 1.2
         */
        protected Object readObjectOverride()
            throws IOException, ClassNotFoundException
        {
            return null;
        }
    
        /**
         * Reads an "unshared" object from the ObjectInputStream.  This method is
         * identical to readObject, except that it prevents subsequent calls to
         * readObject and readUnshared from returning additional references to the
         * deserialized instance obtained via this call.  Specifically:
         * <ul>
         *   <li>If readUnshared is called to deserialize a back-reference (the
         *       stream representation of an object which has been written
         *       previously to the stream), an ObjectStreamException will be
         *       thrown.
         *
         *   <li>If readUnshared returns successfully, then any subsequent attempts
         *       to deserialize back-references to the stream handle deserialized
         *       by readUnshared will cause an ObjectStreamException to be thrown.
         * </ul>
         * Deserializing an object via readUnshared invalidates the stream handle
         * associated with the returned object.  Note that this in itself does not
         * always guarantee that the reference returned by readUnshared is unique;
         * the deserialized object may define a readResolve method which returns an
         * object visible to other parties, or readUnshared may return a Class
         * object or enum constant obtainable elsewhere in the stream or through
         * external means. If the deserialized object defines a readResolve method
         * and the invocation of that method returns an array, then readUnshared
         * returns a shallow clone of that array; this guarantees that the returned
         * array object is unique and cannot be obtained a second time from an
         * invocation of readObject or readUnshared on the ObjectInputStream,
         * even if the underlying data stream has been manipulated.
         *
         * <p>ObjectInputStream subclasses which override this method can only be
         * constructed in security contexts possessing the
         * "enableSubclassImplementation" SerializablePermission; any attempt to
         * instantiate such a subclass without this permission will cause a
         * SecurityException to be thrown.
         *
         * @return  reference to deserialized object
         * @throws  ClassNotFoundException if class of an object to deserialize
         *          cannot be found
         * @throws  StreamCorruptedException if control information in the stream
         *          is inconsistent
         * @throws  ObjectStreamException if object to deserialize has already
         *          appeared in stream
         * @throws  OptionalDataException if primitive data is next in stream
         * @throws  IOException if an I/O error occurs during deserialization
         * @since   1.4
         */
        public Object readUnshared() throws IOException, ClassNotFoundException {
            // if nested read, passHandle contains handle of enclosing object
            int outerHandle = passHandle;
            try {
                Object obj = readObject0(true);
                handles.markDependency(outerHandle, passHandle);
                ClassNotFoundException ex = handles.lookupException(passHandle);
                if (ex != null) {
                    throw ex;
                }
                if (depth == 0) {
                    vlist.doCallbacks();
                }
                return obj;
            } finally {
                passHandle = outerHandle;
                if (closed && depth == 0) {
                    clear();
                }
            }
        }
    
        /**
         * Read the non-static and non-transient fields of the current class from
         * this stream.  This may only be called from the readObject method of the
         * class being deserialized. It will throw the NotActiveException if it is
         * called otherwise.
         *
         * @throws  ClassNotFoundException if the class of a serialized object
         *          could not be found.
         * @throws  IOException if an I/O error occurs.
         * @throws  NotActiveException if the stream is not currently reading
         *          objects.
         */
        public void defaultReadObject()
            throws IOException, ClassNotFoundException
        {
            SerialCallbackContext ctx = curContext;
            if (ctx == null) {
                throw new NotActiveException("not in call to readObject");
            }
            Object curObj = ctx.getObj();
            ObjectStreamClass curDesc = ctx.getDesc();
            bin.setBlockDataMode(false);
            defaultReadFields(curObj, curDesc);
            bin.setBlockDataMode(true);
            if (!curDesc.hasWriteObjectData()) {
                /*
                 * Fix for 4360508: since stream does not contain terminating
                 * TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere
                 * knows to simulate end-of-custom-data behavior.
                 */
                defaultDataEnd = true;
            }
            ClassNotFoundException ex = handles.lookupException(passHandle);
            if (ex != null) {
                throw ex;
            }
        }
    
        /**
         * Reads the persistent fields from the stream and makes them available by
         * name.
         *
         * @return  the <code>GetField</code> object representing the persistent
         *          fields of the object being deserialized
         * @throws  ClassNotFoundException if the class of a serialized object
         *          could not be found.
         * @throws  IOException if an I/O error occurs.
         * @throws  NotActiveException if the stream is not currently reading
         *          objects.
         * @since 1.2
         */
        public ObjectInputStream.GetField readFields()
            throws IOException, ClassNotFoundException
        {
            SerialCallbackContext ctx = curContext;
            if (ctx == null) {
                throw new NotActiveException("not in call to readObject");
            }
            Object curObj = ctx.getObj();
            ObjectStreamClass curDesc = ctx.getDesc();
            bin.setBlockDataMode(false);
            GetFieldImpl getField = new GetFieldImpl(curDesc);
            getField.readFields();
            bin.setBlockDataMode(true);
            if (!curDesc.hasWriteObjectData()) {
                /*
                 * Fix for 4360508: since stream does not contain terminating
                 * TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere
                 * knows to simulate end-of-custom-data behavior.
                 */
                defaultDataEnd = true;
            }
    
            return getField;
        }
    
        /**
         * Register an object to be validated before the graph is returned.  While
         * similar to resolveObject these validations are called after the entire
         * graph has been reconstituted.  Typically, a readObject method will
         * register the object with the stream so that when all of the objects are
         * restored a final set of validations can be performed.
         *
         * @param   obj the object to receive the validation callback.
         * @param   prio controls the order of callbacks;zero is a good default.
         *          Use higher numbers to be called back earlier, lower numbers for
         *          later callbacks. Within a priority, callbacks are processed in
         *          no particular order.
         * @throws  NotActiveException The stream is not currently reading objects
         *          so it is invalid to register a callback.
         * @throws  InvalidObjectException The validation object is null.
         */
        public void registerValidation(ObjectInputValidation obj, int prio)
            throws NotActiveException, InvalidObjectException
        {
            if (depth == 0) {
                throw new NotActiveException("stream inactive");
            }
            vlist.register(obj, prio);
        }
    
        /**
         * Load the local class equivalent of the specified stream class
         * description.  Subclasses may implement this method to allow classes to
         * be fetched from an alternate source.
         *
         * <p>The corresponding method in <code>ObjectOutputStream</code> is
         * <code>annotateClass</code>.  This method will be invoked only once for
         * each unique class in the stream.  This method can be implemented by
         * subclasses to use an alternate loading mechanism but must return a
         * <code>Class</code> object. Once returned, if the class is not an array
         * class, its serialVersionUID is compared to the serialVersionUID of the
         * serialized class, and if there is a mismatch, the deserialization fails
         * and an {@link InvalidClassException} is thrown.
         *
         * <p>The default implementation of this method in
         * <code>ObjectInputStream</code> returns the result of calling
         * <pre>
         *     Class.forName(desc.getName(), false, loader)
         * </pre>
         * where <code>loader</code> is determined as follows: if there is a
         * method on the current thread's stack whose declaring class was
         * defined by a user-defined class loader (and was not a generated to
         * implement reflective invocations), then <code>loader</code> is class
         * loader corresponding to the closest such method to the currently
         * executing frame; otherwise, <code>loader</code> is
         * <code>null</code>. If this call results in a
         * <code>ClassNotFoundException</code> and the name of the passed
         * <code>ObjectStreamClass</code> instance is the Java language keyword
         * for a primitive type or void, then the <code>Class</code> object
         * representing that primitive type or void will be returned
         * (e.g., an <code>ObjectStreamClass</code> with the name
         * <code>"int"</code> will be resolved to <code>Integer.TYPE</code>).
         * Otherwise, the <code>ClassNotFoundException</code> will be thrown to
         * the caller of this method.
         *
         * @param   desc an instance of class <code>ObjectStreamClass</code>
         * @return  a <code>Class</code> object corresponding to <code>desc</code>
         * @throws  IOException any of the usual Input/Output exceptions.
         * @throws  ClassNotFoundException if class of a serialized object cannot
         *          be found.
         */
        protected Class<?> resolveClass(ObjectStreamClass desc)
            throws IOException, ClassNotFoundException
        {
            String name = desc.getName();
            try {
                return Class.forName(name, false, latestUserDefinedLoader());
            } catch (ClassNotFoundException ex) {
                Class<?> cl = primClasses.get(name);
                if (cl != null) {
                    return cl;
                } else {
                    throw ex;
                }
            }
        }
    
        /**
         * Returns a proxy class that implements the interfaces named in a proxy
         * class descriptor; subclasses may implement this method to read custom
         * data from the stream along with the descriptors for dynamic proxy
         * classes, allowing them to use an alternate loading mechanism for the
         * interfaces and the proxy class.
         *
         * <p>This method is called exactly once for each unique proxy class
         * descriptor in the stream.
         *
         * <p>The corresponding method in <code>ObjectOutputStream</code> is
         * <code>annotateProxyClass</code>.  For a given subclass of
         * <code>ObjectInputStream</code> that overrides this method, the
         * <code>annotateProxyClass</code> method in the corresponding subclass of
         * <code>ObjectOutputStream</code> must write any data or objects read by
         * this method.
         *
         * <p>The default implementation of this method in
         * <code>ObjectInputStream</code> returns the result of calling
         * <code>Proxy.getProxyClass</code> with the list of <code>Class</code>
         * objects for the interfaces that are named in the <code>interfaces</code>
         * parameter.  The <code>Class</code> object for each interface name
         * <code>i</code> is the value returned by calling
         * <pre>
         *     Class.forName(i, false, loader)
         * </pre>
         * where <code>loader</code> is that of the first non-<code>null</code>
         * class loader up the execution stack, or <code>null</code> if no
         * non-<code>null</code> class loaders are on the stack (the same class
         * loader choice used by the <code>resolveClass</code> method).  Unless any
         * of the resolved interfaces are non-public, this same value of
         * <code>loader</code> is also the class loader passed to
         * <code>Proxy.getProxyClass</code>; if non-public interfaces are present,
         * their class loader is passed instead (if more than one non-public
         * interface class loader is encountered, an
         * <code>IllegalAccessError</code> is thrown).
         * If <code>Proxy.getProxyClass</code> throws an
         * <code>IllegalArgumentException</code>, <code>resolveProxyClass</code>
         * will throw a <code>ClassNotFoundException</code> containing the
         * <code>IllegalArgumentException</code>.
         *
         * @param interfaces the list of interface names that were
         *                deserialized in the proxy class descriptor
         * @return  a proxy class for the specified interfaces
         * @throws        IOException any exception thrown by the underlying
         *                <code>InputStream</code>
         * @throws        ClassNotFoundException if the proxy class or any of the
         *                named interfaces could not be found
         * @see ObjectOutputStream#annotateProxyClass(Class)
         * @since 1.3
         */
        protected Class<?> resolveProxyClass(String[] interfaces)
            throws IOException, ClassNotFoundException
        {
            ClassLoader latestLoader = latestUserDefinedLoader();
            ClassLoader nonPublicLoader = null;
            boolean hasNonPublicInterface = false;
    
            // define proxy in class loader of non-public interface(s), if any
            Class<?>[] classObjs = new Class<?>[interfaces.length];
            for (int i = 0; i < interfaces.length; i++) {
                Class<?> cl = Class.forName(interfaces[i], false, latestLoader);
                if ((cl.getModifiers() & Modifier.PUBLIC) == 0) {
                    if (hasNonPublicInterface) {
                        if (nonPublicLoader != cl.getClassLoader()) {
                            throw new IllegalAccessError(
                                "conflicting non-public interface class loaders");
                        }
                    } else {
                        nonPublicLoader = cl.getClassLoader();
                        hasNonPublicInterface = true;
                    }
                }
                classObjs[i] = cl;
            }
            try {
                return Proxy.getProxyClass(
                    hasNonPublicInterface ? nonPublicLoader : latestLoader,
                    classObjs);
            } catch (IllegalArgumentException e) {
                throw new ClassNotFoundException(null, e);
            }
        }
    
        /**
         * This method will allow trusted subclasses of ObjectInputStream to
         * substitute one object for another during deserialization. Replacing
         * objects is disabled until enableResolveObject is called. The
         * enableResolveObject method checks that the stream requesting to resolve
         * object can be trusted. Every reference to serializable objects is passed
         * to resolveObject.  To insure that the private state of objects is not
         * unintentionally exposed only trusted streams may use resolveObject.
         *
         * <p>This method is called after an object has been read but before it is
         * returned from readObject.  The default resolveObject method just returns
         * the same object.
         *
         * <p>When a subclass is replacing objects it must insure that the
         * substituted object is compatible with every field where the reference
         * will be stored.  Objects whose type is not a subclass of the type of the
         * field or array element abort the serialization by raising an exception
         * and the object is not be stored.
         *
         * <p>This method is called only once when each object is first
         * encountered.  All subsequent references to the object will be redirected
         * to the new object.
         *
         * @param   obj object to be substituted
         * @return  the substituted object
         * @throws  IOException Any of the usual Input/Output exceptions.
         */
        protected Object resolveObject(Object obj) throws IOException {
            return obj;
        }
    
        /**
         * Enable the stream to allow objects read from the stream to be replaced.
         * When enabled, the resolveObject method is called for every object being
         * deserialized.
         *
         * <p>If <i>enable</i> is true, and there is a security manager installed,
         * this method first calls the security manager's
         * <code>checkPermission</code> method with the
         * <code>SerializablePermission("enableSubstitution")</code> permission to
         * ensure it's ok to enable the stream to allow objects read from the
         * stream to be replaced.
         *
         * @param   enable true for enabling use of <code>resolveObject</code> for
         *          every object being deserialized
         * @return  the previous setting before this method was invoked
         * @throws  SecurityException if a security manager exists and its
         *          <code>checkPermission</code> method denies enabling the stream
         *          to allow objects read from the stream to be replaced.
         * @see SecurityManager#checkPermission
         * @see java.io.SerializablePermission
         */
        protected boolean enableResolveObject(boolean enable)
            throws SecurityException
        {
            if (enable == enableResolve) {
                return enable;
            }
            if (enable) {
                SecurityManager sm = System.getSecurityManager();
                if (sm != null) {
                    sm.checkPermission(SUBSTITUTION_PERMISSION);
                }
            }
            enableResolve = enable;
            return !enableResolve;
        }
    
        /**
         * The readStreamHeader method is provided to allow subclasses to read and
         * verify their own stream headers. It reads and verifies the magic number
         * and version number.
         *
         * @throws  IOException if there are I/O errors while reading from the
         *          underlying <code>InputStream</code>
         * @throws  StreamCorruptedException if control information in the stream
         *          is inconsistent
         */
        protected void readStreamHeader()
            throws IOException, StreamCorruptedException
        {
            short s0 = bin.readShort();
            short s1 = bin.readShort();
            if (s0 != STREAM_MAGIC || s1 != STREAM_VERSION) {
                throw new StreamCorruptedException(
                    String.format("invalid stream header: %04X%04X", s0, s1));
            }
        }
    
        /**
         * Read a class descriptor from the serialization stream.  This method is
         * called when the ObjectInputStream expects a class descriptor as the next
         * item in the serialization stream.  Subclasses of ObjectInputStream may
         * override this method to read in class descriptors that have been written
         * in non-standard formats (by subclasses of ObjectOutputStream which have
         * overridden the <code>writeClassDescriptor</code> method).  By default,
         * this method reads class descriptors according to the format defined in
         * the Object Serialization specification.
         *
         * @return  the class descriptor read
         * @throws  IOException If an I/O error has occurred.
         * @throws  ClassNotFoundException If the Class of a serialized object used
         *          in the class descriptor representation cannot be found
         * @see java.io.ObjectOutputStream#writeClassDescriptor(java.io.ObjectStreamClass)
         * @since 1.3
         */
        protected ObjectStreamClass readClassDescriptor()
            throws IOException, ClassNotFoundException
        {
            ObjectStreamClass desc = new ObjectStreamClass();
            desc.readNonProxy(this);
            return desc;
        }
    
        /**
         * Reads a byte of data. This method will block if no input is available.
         *
         * @return  the byte read, or -1 if the end of the stream is reached.
         * @throws  IOException If an I/O error has occurred.
         */
        public int read() throws IOException {
            return bin.read();
        }
    
        /**
         * Reads into an array of bytes.  This method will block until some input
         * is available. Consider using java.io.DataInputStream.readFully to read
         * exactly 'length' bytes.
         *
         * @param   buf the buffer into which the data is read
         * @param   off the start offset of the data
         * @param   len the maximum number of bytes read
         * @return  the actual number of bytes read, -1 is returned when the end of
         *          the stream is reached.
         * @throws  IOException If an I/O error has occurred.
         * @see java.io.DataInputStream#readFully(byte[],int,int)
         */
        public int read(byte[] buf, int off, int len) throws IOException {
            if (buf == null) {
                throw new NullPointerException();
            }
            int endoff = off + len;
            if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
                throw new IndexOutOfBoundsException();
            }
            return bin.read(buf, off, len, false);
        }
    
        /**
         * Returns the number of bytes that can be read without blocking.
         *
         * @return  the number of available bytes.
         * @throws  IOException if there are I/O errors while reading from the
         *          underlying <code>InputStream</code>
         */
        public int available() throws IOException {
            return bin.available();
        }
    
        /**
         * Closes the input stream. Must be called to release any resources
         * associated with the stream.
         *
         * @throws  IOException If an I/O error has occurred.
         */
        public void close() throws IOException {
            /*
             * Even if stream already closed, propagate redundant close to
             * underlying stream to stay consistent with previous implementations.
             */
            closed = true;
            if (depth == 0) {
                clear();
            }
            bin.close();
        }
    
        /**
         * Reads in a boolean.
         *
         * @return  the boolean read.
         * @throws  EOFException If end of file is reached.
         * @throws  IOException If other I/O error has occurred.
         */
        public boolean readBoolean() throws IOException {
            return bin.readBoolean();
        }
    
        /**
         * Reads an 8 bit byte.
         *
         * @return  the 8 bit byte read.
         * @throws  EOFException If end of file is reached.
         * @throws  IOException If other I/O error has occurred.
         */
        public byte readByte() throws IOException  {
            return bin.readByte();
        }
    
        /**
         * Reads an unsigned 8 bit byte.
         *
         * @return  the 8 bit byte read.
         * @throws  EOFException If end of file is reached.
         * @throws  IOException If other I/O error has occurred.
         */
        public int readUnsignedByte()  throws IOException {
            return bin.readUnsignedByte();
        }
    
        /**
         * Reads a 16 bit char.
         *
         * @return  the 16 bit char read.
         * @throws  EOFException If end of file is reached.
         * @throws  IOException If other I/O error has occurred.
         */
        public char readChar()  throws IOException {
            return bin.readChar();
        }
    
        /**
         * Reads a 16 bit short.
         *
         * @return  the 16 bit short read.
         * @throws  EOFException If end of file is reached.
         * @throws  IOException If other I/O error has occurred.
         */
        public short readShort()  throws IOException {
            return bin.readShort();
        }
    
        /**
         * Reads an unsigned 16 bit short.
         *
         * @return  the 16 bit short read.
         * @throws  EOFException If end of file is reached.
         * @throws  IOException If other I/O error has occurred.
         */
        public int readUnsignedShort() throws IOException {
            return bin.readUnsignedShort();
        }
    
        /**
         * Reads a 32 bit int.
         *
         * @return  the 32 bit integer read.
         * @throws  EOFException If end of file is reached.
         * @throws  IOException If other I/O error has occurred.
         */
        public int readInt()  throws IOException {
            return bin.readInt();
        }
    
        /**
         * Reads a 64 bit long.
         *
         * @return  the read 64 bit long.
         * @throws  EOFException If end of file is reached.
         * @throws  IOException If other I/O error has occurred.
         */
        public long readLong()  throws IOException {
            return bin.readLong();
        }
    
        /**
         * Reads a 32 bit float.
         *
         * @return  the 32 bit float read.
         * @throws  EOFException If end of file is reached.
         * @throws  IOException If other I/O error has occurred.
         */
        public float readFloat() throws IOException {
            return bin.readFloat();
        }
    
        /**
         * Reads a 64 bit double.
         *
         * @return  the 64 bit double read.
         * @throws  EOFException If end of file is reached.
         * @throws  IOException If other I/O error has occurred.
         */
        public double readDouble() throws IOException {
            return bin.readDouble();
        }
    
        /**
         * Reads bytes, blocking until all bytes are read.
         *
         * @param   buf the buffer into which the data is read
         * @throws  EOFException If end of file is reached.
         * @throws  IOException If other I/O error has occurred.
         */
        public void readFully(byte[] buf) throws IOException {
            bin.readFully(buf, 0, buf.length, false);
        }
    
        /**
         * Reads bytes, blocking until all bytes are read.
         *
         * @param   buf the buffer into which the data is read
         * @param   off the start offset of the data
         * @param   len the maximum number of bytes to read
         * @throws  EOFException If end of file is reached.
         * @throws  IOException If other I/O error has occurred.
         */
        public void readFully(byte[] buf, int off, int len) throws IOException {
            int endoff = off + len;
            if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
                throw new IndexOutOfBoundsException();
            }
            bin.readFully(buf, off, len, false);
        }
    
        /**
         * Skips bytes.
         *
         * @param   len the number of bytes to be skipped
         * @return  the actual number of bytes skipped.
         * @throws  IOException If an I/O error has occurred.
         */
        public int skipBytes(int len) throws IOException {
            return bin.skipBytes(len);
        }
    
        /**
         * Reads in a line that has been terminated by a \n, \r, \r\n or EOF.
         *
         * @return  a String copy of the line.
         * @throws  IOException if there are I/O errors while reading from the
         *          underlying <code>InputStream</code>
         * @deprecated This method does not properly convert bytes to characters.
         *          see DataInputStream for the details and alternatives.
         */
        @Deprecated
        public String readLine() throws IOException {
            return bin.readLine();
        }
    
        /**
         * Reads a String in
         * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
         * format.
         *
         * @return  the String.
         * @throws  IOException if there are I/O errors while reading from the
         *          underlying <code>InputStream</code>
         * @throws  UTFDataFormatException if read bytes do not represent a valid
         *          modified UTF-8 encoding of a string
         */
        public String readUTF() throws IOException {
            return bin.readUTF();
        }
    
        /**
         * Returns the serialization filter for this stream.
         * The serialization filter is the most recent filter set in
         * {@link #setInternalObjectInputFilter setInternalObjectInputFilter} or
         * the initial process-wide filter from
         * {@link ObjectInputFilter.Config#getSerialFilter() ObjectInputFilter.Config.getSerialFilter}.
         *
         * @return the serialization filter for the stream; may be null
         */
        private final ObjectInputFilter getInternalObjectInputFilter() {
            return serialFilter;
        }
    
        /**
         * Set the serialization filter for the stream.
         * The filter's {@link ObjectInputFilter#checkInput checkInput} method is called
         * for each class and reference in the stream.
         * The filter can check any or all of the class, the array length, the number
         * of references, the depth of the graph, and the size of the input stream.
         * <p>
         * If the filter returns {@link ObjectInputFilter.Status#REJECTED Status.REJECTED},
         * {@code null} or throws a {@link RuntimeException},
         * the active {@code readObject} or {@code readUnshared}
         * throws {@link InvalidClassException}, otherwise deserialization
         * continues uninterrupted.
         * <p>
         * The serialization filter is initialized to the value of
         * {@link ObjectInputFilter.Config#getSerialFilter() ObjectInputFilter.Config.getSerialFilter}
         * when the {@code  ObjectInputStream} is constructed and can be set
         * to a custom filter only once.
         *
         * @implSpec
         * The filter, when not {@code null}, is invoked during {@link #readObject readObject}
         * and {@link #readUnshared readUnshared} for each object
         * (regular or class) in the stream including the following:
         * <ul>
         *     <li>each object reference previously deserialized from the stream
         *     (class is {@code null}, arrayLength is -1),
         *     <li>each regular class (class is not {@code null}, arrayLength is -1),
         *     <li>each interface of a dynamic proxy and the dynamic proxy class itself
         *     (class is not {@code null}, arrayLength is -1),
         *     <li>each array is filtered using the array type and length of the array
         *     (class is the array type, arrayLength is the requested length),
         *     <li>each object replaced by its class' {@code readResolve} method
         *         is filtered using the replacement object's class, if not {@code null},
         *         and if it is an array, the arrayLength, otherwise -1,
         *     <li>and each object replaced by {@link #resolveObject resolveObject}
         *         is filtered using the replacement object's class, if not {@code null},
         *         and if it is an array, the arrayLength, otherwise -1.
         * </ul>
         *
         * When the {@link ObjectInputFilter#checkInput checkInput} method is invoked
         * it is given access to the current class, the array length,
         * the current number of references already read from the stream,
         * the depth of nested calls to {@link #readObject readObject} or
         * {@link #readUnshared readUnshared},
         * and the implementation dependent number of bytes consumed from the input stream.
         * <p>
         * Each call to {@link #readObject readObject} or
         * {@link #readUnshared readUnshared} increases the depth by 1
         * before reading an object and decreases by 1 before returning
         * normally or exceptionally.
         * The depth starts at {@code 1} and increases for each nested object and
         * decrements when each nested call returns.
         * The count of references in the stream starts at {@code 1} and
         * is increased before reading an object.
         *
         * @param filter the filter, may be null
         * @throws SecurityException if there is security manager and the
         *       {@code SerializablePermission("serialFilter")} is not granted
         * @throws IllegalStateException if the {@linkplain #getInternalObjectInputFilter() current filter}
         *       is not {@code null} and is not the process-wide filter
         */
        private final void setInternalObjectInputFilter(ObjectInputFilter filter) {
            SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                sm.checkPermission(new SerializablePermission("serialFilter"));
            }
            // Allow replacement of the process-wide filter if not already set
            if (serialFilter != null &&
                    serialFilter != ObjectInputFilter.Config.getSerialFilter()) {
                throw new IllegalStateException("filter can not be set more than once");
            }
            this.serialFilter = filter;
        }
    
        /**
         * Invoke the serialization filter if non-null.
         * If the filter rejects or an exception is thrown, throws InvalidClassException.
         *
         * @param clazz the class; may be null
         * @param arrayLength the array length requested; use {@code -1} if not creating an array
         * @throws InvalidClassException if it rejected by the filter or
         *        a {@link RuntimeException} is thrown
         */
        private void filterCheck(Class<?> clazz, int arrayLength)
                throws InvalidClassException {
            if (serialFilter != null) {
                RuntimeException ex = null;
                ObjectInputFilter.Status status;
                try {
                    status = serialFilter.checkInput(new FilterValues(clazz, arrayLength,
                            totalObjectRefs, depth, bin.getBytesRead()));
                } catch (RuntimeException e) {
                    // Preventive interception of an exception to log
                    status = ObjectInputFilter.Status.REJECTED;
                    ex = e;
                }
                if (status == null  ||
                        status == ObjectInputFilter.Status.REJECTED) {
                    // Debug logging of filter checks that fail
                    if (Logging.infoLogger != null) {
                        Logging.infoLogger.info(
                                "ObjectInputFilter {0}: {1}, array length: {2}, nRefs: {3}, depth: {4}, bytes: {5}, ex: {6}",
                                status, clazz, arrayLength, totalObjectRefs, depth, bin.getBytesRead(),
                                Objects.toString(ex, "n/a"));
                    }
                    InvalidClassException ice = new InvalidClassException("filter status: " + status);
                    ice.initCause(ex);
                    throw ice;
                } else {
                    // Trace logging for those that succeed
                    if (Logging.traceLogger != null) {
                        Logging.traceLogger.finer(
                                "ObjectInputFilter {0}: {1}, array length: {2}, nRefs: {3}, depth: {4}, bytes: {5}, ex: {6}",
                                status, clazz, arrayLength, totalObjectRefs, depth, bin.getBytesRead(),
                                Objects.toString(ex, "n/a"));
                    }
                }
            }
        }
    
        /**
         * Provide access to the persistent fields read from the input stream.
         */
        public static abstract class GetField {
    
            /**
             * Get the ObjectStreamClass that describes the fields in the stream.
             *
             * @return  the descriptor class that describes the serializable fields
             */
            public abstract ObjectStreamClass getObjectStreamClass();
    
            /**
             * Return true if the named field is defaulted and has no value in this
             * stream.
             *
             * @param  name the name of the field
             * @return true, if and only if the named field is defaulted
             * @throws IOException if there are I/O errors while reading from
             *         the underlying <code>InputStream</code>
             * @throws IllegalArgumentException if <code>name</code> does not
             *         correspond to a serializable field
             */
            public abstract boolean defaulted(String name) throws IOException;
    
            /**
             * Get the value of the named boolean field from the persistent field.
             *
             * @param  name the name of the field
             * @param  val the default value to use if <code>name</code> does not
             *         have a value
             * @return the value of the named <code>boolean</code> field
             * @throws IOException if there are I/O errors while reading from the
             *         underlying <code>InputStream</code>
             * @throws IllegalArgumentException if type of <code>name</code> is
             *         not serializable or if the field type is incorrect
             */
            public abstract boolean get(String name, boolean val)
                throws IOException;
    
            /**
             * Get the value of the named byte field from the persistent field.
             *
             * @param  name the name of the field
             * @param  val the default value to use if <code>name</code> does not
             *         have a value
             * @return the value of the named <code>byte</code> field
             * @throws IOException if there are I/O errors while reading from the
             *         underlying <code>InputStream</code>
             * @throws IllegalArgumentException if type of <code>name</code> is
             *         not serializable or if the field type is incorrect
             */
            public abstract byte get(String name, byte val) throws IOException;
    
            /**
             * Get the value of the named char field from the persistent field.
             *
             * @param  name the name of the field
             * @param  val the default value to use if <code>name</code> does not
             *         have a value
             * @return the value of the named <code>char</code> field
             * @throws IOException if there are I/O errors while reading from the
             *         underlying <code>InputStream</code>
             * @throws IllegalArgumentException if type of <code>name</code> is
             *         not serializable or if the field type is incorrect
             */
            public abstract char get(String name, char val) throws IOException;
    
            /**
             * Get the value of the named short field from the persistent field.
             *
             * @param  name the name of the field
             * @param  val the default value to use if <code>name</code> does not
             *         have a value
             * @return the value of the named <code>short</code> field
             * @throws IOException if there are I/O errors while reading from the
             *         underlying <code>InputStream</code>
             * @throws IllegalArgumentException if type of <code>name</code> is
             *         not serializable or if the field type is incorrect
             */
            public abstract short get(String name, short val) throws IOException;
    
            /**
             * Get the value of the named int field from the persistent field.
             *
             * @param  name the name of the field
             * @param  val the default value to use if <code>name</code> does not
             *         have a value
             * @return the value of the named <code>int</code> field
             * @throws IOException if there are I/O errors while reading from the
             *         underlying <code>InputStream</code>
             * @throws IllegalArgumentException if type of <code>name</code> is
             *         not serializable or if the field type is incorrect
             */
            public abstract int get(String name, int val) throws IOException;
    
            /**
             * Get the value of the named long field from the persistent field.
             *
             * @param  name the name of the field
             * @param  val the default value to use if <code>name</code> does not
             *         have a value
             * @return the value of the named <code>long</code> field
             * @throws IOException if there are I/O errors while reading from the
             *         underlying <code>InputStream</code>
             * @throws IllegalArgumentException if type of <code>name</code> is
             *         not serializable or if the field type is incorrect
             */
            public abstract long get(String name, long val) throws IOException;
    
            /**
             * Get the value of the named float field from the persistent field.
             *
             * @param  name the name of the field
             * @param  val the default value to use if <code>name</code> does not
             *         have a value
             * @return the value of the named <code>float</code> field
             * @throws IOException if there are I/O errors while reading from the
             *         underlying <code>InputStream</code>
             * @throws IllegalArgumentException if type of <code>name</code> is
             *         not serializable or if the field type is incorrect
             */
            public abstract float get(String name, float val) throws IOException;
    
            /**
             * Get the value of the named double field from the persistent field.
             *
             * @param  name the name of the field
             * @param  val the default value to use if <code>name</code> does not
             *         have a value
             * @return the value of the named <code>double</code> field
             * @throws IOException if there are I/O errors while reading from the
             *         underlying <code>InputStream</code>
             * @throws IllegalArgumentException if type of <code>name</code> is
             *         not serializable or if the field type is incorrect
             */
            public abstract double get(String name, double val) throws IOException;
    
            /**
             * Get the value of the named Object field from the persistent field.
             *
             * @param  name the name of the field
             * @param  val the default value to use if <code>name</code> does not
             *         have a value
             * @return the value of the named <code>Object</code> field
             * @throws IOException if there are I/O errors while reading from the
             *         underlying <code>InputStream</code>
             * @throws IllegalArgumentException if type of <code>name</code> is
             *         not serializable or if the field type is incorrect
             */
            public abstract Object get(String name, Object val) throws IOException;
        }
    
        /**
         * Verifies that this (possibly subclass) instance can be constructed
         * without violating security constraints: the subclass must not override
         * security-sensitive non-final methods, or else the
         * "enableSubclassImplementation" SerializablePermission is checked.
         */
        private void verifySubclass() {
            Class<?> cl = getClass();
            if (cl == ObjectInputStream.class) {
                return;
            }
            SecurityManager sm = System.getSecurityManager();
            if (sm == null) {
                return;
            }
            processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
            WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
            Boolean result = Caches.subclassAudits.get(key);
            if (result == null) {
                result = Boolean.valueOf(auditSubclass(cl));
                Caches.subclassAudits.putIfAbsent(key, result);
            }
            if (result.booleanValue()) {
                return;
            }
            sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
        }
    
        /**
         * Performs reflective checks on given subclass to verify that it doesn't
         * override security-sensitive non-final methods.  Returns true if subclass
         * is "safe", false otherwise.
         */
        private static boolean auditSubclass(final Class<?> subcl) {
            Boolean result = AccessController.doPrivileged(
                new PrivilegedAction<Boolean>() {
                    public Boolean run() {
                        for (Class<?> cl = subcl;
                             cl != ObjectInputStream.class;
                             cl = cl.getSuperclass())
                        {
                            try {
                                cl.getDeclaredMethod(
                                    "readUnshared", (Class[]) null);
                                return Boolean.FALSE;
                            } catch (NoSuchMethodException ex) {
                            }
                            try {
                                cl.getDeclaredMethod("readFields", (Class[]) null);
                                return Boolean.FALSE;
                            } catch (NoSuchMethodException ex) {
                            }
                        }
                        return Boolean.TRUE;
                    }
                }
            );
            return result.booleanValue();
        }
    
        /**
         * Clears internal data structures.
         */
        private void clear() {
            handles.clear();
            vlist.clear();
        }
    
        /**
         * Underlying readObject implementation.
         */
        private Object readObject0(boolean unshared) throws IOException {
            boolean oldMode = bin.getBlockDataMode();
            if (oldMode) {
                int remain = bin.currentBlockRemaining();
                if (remain > 0) {
                    throw new OptionalDataException(remain);
                } else if (defaultDataEnd) {
                    /*
                     * Fix for 4360508: stream is currently at the end of a field
                     * value block written via default serialization; since there
                     * is no terminating TC_ENDBLOCKDATA tag, simulate
                     * end-of-custom-data behavior explicitly.
                     */
                    throw new OptionalDataException(true);
                }
                bin.setBlockDataMode(false);
            }
    
            byte tc;
            while ((tc = bin.peekByte()) == TC_RESET) {
                bin.readByte();
                handleReset();
            }
    
            depth++;
            totalObjectRefs++;
            try {
                switch (tc) {
                    case TC_NULL:
                        return readNull();
    
                    case TC_REFERENCE:
                        return readHandle(unshared);
    
                    case TC_CLASS:
                        return readClass(unshared);
    
                    case TC_CLASSDESC:
                    case TC_PROXYCLASSDESC:
                        return readClassDesc(unshared);
    
                    case TC_STRING:
                    case TC_LONGSTRING:
                        return checkResolve(readString(unshared));
    
                    case TC_ARRAY:
                        return checkResolve(readArray(unshared));
    
                    case TC_ENUM:
                        return checkResolve(readEnum(unshared));
    
                    case TC_OBJECT:
                        return checkResolve(readOrdinaryObject(unshared));
    
                    case TC_EXCEPTION:
                        IOException ex = readFatalException();
                        throw new WriteAbortedException("writing aborted", ex);
    
                    case TC_BLOCKDATA:
                    case TC_BLOCKDATALONG:
                        if (oldMode) {
                            bin.setBlockDataMode(true);
                            bin.peek();             // force header read
                            throw new OptionalDataException(
                                bin.currentBlockRemaining());
                        } else {
                            throw new StreamCorruptedException(
                                "unexpected block data");
                        }
    
                    case TC_ENDBLOCKDATA:
                        if (oldMode) {
                            throw new OptionalDataException(true);
                        } else {
                            throw new StreamCorruptedException(
                                "unexpected end of block data");
                        }
    
                    default:
                        throw new StreamCorruptedException(
                            String.format("invalid type code: %02X", tc));
                }
            } finally {
                depth--;
                bin.setBlockDataMode(oldMode);
            }
        }
    
        /**
         * If resolveObject has been enabled and given object does not have an
         * exception associated with it, calls resolveObject to determine
         * replacement for object, and updates handle table accordingly.  Returns
         * replacement object, or echoes provided object if no replacement
         * occurred.  Expects that passHandle is set to given object's handle prior
         * to calling this method.
         */
        private Object checkResolve(Object obj) throws IOException {
            if (!enableResolve || handles.lookupException(passHandle) != null) {
                return obj;
            }
            Object rep = resolveObject(obj);
            if (rep != obj) {
                // The type of the original object has been filtered but resolveObject
                // may have replaced it;  filter the replacement's type
                if (rep != null) {
                    if (rep.getClass().isArray()) {
                        filterCheck(rep.getClass(), Array.getLength(rep));
                    } else {
                        filterCheck(rep.getClass(), -1);
                    }
                }
                handles.setObject(passHandle, rep);
            }
            return rep;
        }
    
        /**
         * Reads string without allowing it to be replaced in stream.  Called from
         * within ObjectStreamClass.read().
         */
        String readTypeString() throws IOException {
            int oldHandle = passHandle;
            try {
                byte tc = bin.peekByte();
                switch (tc) {
                    case TC_NULL:
                        return (String) readNull();
    
                    case TC_REFERENCE:
                        return (String) readHandle(false);
    
                    case TC_STRING:
                    case TC_LONGSTRING:
                        return readString(false);
    
                    default:
                        throw new StreamCorruptedException(
                            String.format("invalid type code: %02X", tc));
                }
            } finally {
                passHandle = oldHandle;
            }
        }
    
        /**
         * Reads in null code, sets passHandle to NULL_HANDLE and returns null.
         */
        private Object readNull() throws IOException {
            if (bin.readByte() != TC_NULL) {
                throw new InternalError();
            }
            passHandle = NULL_HANDLE;
            return null;
        }
    
        /**
         * Reads in object handle, sets passHandle to the read handle, and returns
         * object associated with the handle.
         */
        private Object readHandle(boolean unshared) throws IOException {
            if (bin.readByte() != TC_REFERENCE) {
                throw new InternalError();
            }
            passHandle = bin.readInt() - baseWireHandle;
            if (passHandle < 0 || passHandle >= handles.size()) {
                throw new StreamCorruptedException(
                    String.format("invalid handle value: %08X", passHandle +
                    baseWireHandle));
            }
            if (unshared) {
                // REMIND: what type of exception to throw here?
                throw new InvalidObjectException(
                    "cannot read back reference as unshared");
            }
    
            Object obj = handles.lookupObject(passHandle);
            if (obj == unsharedMarker) {
                // REMIND: what type of exception to throw here?
                throw new InvalidObjectException(
                    "cannot read back reference to unshared object");
            }
            filterCheck(null, -1);       // just a check for number of references, depth, no class
            return obj;
        }
    
        /**
         * Reads in and returns class object.  Sets passHandle to class object's
         * assigned handle.  Returns null if class is unresolvable (in which case a
         * ClassNotFoundException will be associated with the class' handle in the
         * handle table).
         */
        private Class<?> readClass(boolean unshared) throws IOException {
            if (bin.readByte() != TC_CLASS) {
                throw new InternalError();
            }
            ObjectStreamClass desc = readClassDesc(false);
            Class<?> cl = desc.forClass();
            passHandle = handles.assign(unshared ? unsharedMarker : cl);
    
            ClassNotFoundException resolveEx = desc.getResolveException();
            if (resolveEx != null) {
                handles.markException(passHandle, resolveEx);
            }
    
            handles.finish(passHandle);
            return cl;
        }
    
        /**
         * Reads in and returns (possibly null) class descriptor.  Sets passHandle
         * to class descriptor's assigned handle.  If class descriptor cannot be
         * resolved to a class in the local VM, a ClassNotFoundException is
         * associated with the class descriptor's handle.
         */
        private ObjectStreamClass readClassDesc(boolean unshared)
            throws IOException
        {
            byte tc = bin.peekByte();
            ObjectStreamClass descriptor;
            switch (tc) {
                case TC_NULL:
                    descriptor = (ObjectStreamClass) readNull();
                    break;
                case TC_REFERENCE:
                    descriptor = (ObjectStreamClass) readHandle(unshared);
                    break;
                case TC_PROXYCLASSDESC:
                    descriptor = readProxyDesc(unshared);
                    break;
                case TC_CLASSDESC:
                    descriptor = readNonProxyDesc(unshared);
                    break;
                default:
                    throw new StreamCorruptedException(
                        String.format("invalid type code: %02X", tc));
            }
            if (descriptor != null) {
                validateDescriptor(descriptor);
            }
            return descriptor;
        }
    
        private boolean isCustomSubclass() {
            // Return true if this class is a custom subclass of ObjectInputStream
            return getClass().getClassLoader()
                        != ObjectInputStream.class.getClassLoader();
        }
    
        /**
         * Reads in and returns class descriptor for a dynamic proxy class.  Sets
         * passHandle to proxy class descriptor's assigned handle.  If proxy class
         * descriptor cannot be resolved to a class in the local VM, a
         * ClassNotFoundException is associated with the descriptor's handle.
         */
        private ObjectStreamClass readProxyDesc(boolean unshared)
            throws IOException
        {
            if (bin.readByte() != TC_PROXYCLASSDESC) {
                throw new InternalError();
            }
    
            ObjectStreamClass desc = new ObjectStreamClass();
            int descHandle = handles.assign(unshared ? unsharedMarker : desc);
            passHandle = NULL_HANDLE;
    
            int numIfaces = bin.readInt();
            String[] ifaces = new String[numIfaces];
            for (int i = 0; i < numIfaces; i++) {
                ifaces[i] = bin.readUTF();
            }
    
            Class<?> cl = null;
            ClassNotFoundException resolveEx = null;
            bin.setBlockDataMode(true);
            try {
                if ((cl = resolveProxyClass(ifaces)) == null) {
                    resolveEx = new ClassNotFoundException("null class");
                } else if (!Proxy.isProxyClass(cl)) {
                    throw new InvalidClassException("Not a proxy");
                } else {
                    // ReflectUtil.checkProxyPackageAccess makes a test
                    // equivalent to isCustomSubclass so there's no need
                    // to condition this call to isCustomSubclass == true here.
                    ReflectUtil.checkProxyPackageAccess(
                            getClass().getClassLoader(),
                            cl.getInterfaces());
                    // Filter the interfaces
                    for (Class<?> clazz : cl.getInterfaces()) {
                        filterCheck(clazz, -1);
                    }
                }
            } catch (ClassNotFoundException ex) {
                resolveEx = ex;
            }
    
            // Call filterCheck on the class before reading anything else
            filterCheck(cl, -1);
    
            skipCustomData();
    
            try {
                totalObjectRefs++;
                depth++;
                desc.initProxy(cl, resolveEx, readClassDesc(false));
            } finally {
                depth--;
            }
    
            handles.finish(descHandle);
            passHandle = descHandle;
            return desc;
        }
    
        /**
         * Reads in and returns class descriptor for a class that is not a dynamic
         * proxy class.  Sets passHandle to class descriptor's assigned handle.  If
         * class descriptor cannot be resolved to a class in the local VM, a
         * ClassNotFoundException is associated with the descriptor's handle.
         */
        private ObjectStreamClass readNonProxyDesc(boolean unshared)
            throws IOException
        {
            if (bin.readByte() != TC_CLASSDESC) {
                throw new InternalError();
            }
    
            ObjectStreamClass desc = new ObjectStreamClass();
            int descHandle = handles.assign(unshared ? unsharedMarker : desc);
            passHandle = NULL_HANDLE;
    
            ObjectStreamClass readDesc = null;
            try {
                readDesc = readClassDescriptor();
            } catch (ClassNotFoundException ex) {
                throw (IOException) new InvalidClassException(
                    "failed to read class descriptor").initCause(ex);
            }
    
            Class<?> cl = null;
            ClassNotFoundException resolveEx = null;
            bin.setBlockDataMode(true);
            final boolean checksRequired = isCustomSubclass();
            try {
                if ((cl = resolveClass(readDesc)) == null) {
                    resolveEx = new ClassNotFoundException("null class");
                } else if (checksRequired) {
                    ReflectUtil.checkPackageAccess(cl);
                }
            } catch (ClassNotFoundException ex) {
                resolveEx = ex;
            }
    
            // Call filterCheck on the class before reading anything else
            filterCheck(cl, -1);
    
            skipCustomData();
    
            try {
                totalObjectRefs++;
                depth++;
                desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false));
            } finally {
                depth--;
            }
    
            handles.finish(descHandle);
            passHandle = descHandle;
    
            return desc;
        }
    
        /**
         * Reads in and returns new string.  Sets passHandle to new string's
         * assigned handle.
         */
        private String readString(boolean unshared) throws IOException {
            String str;
            byte tc = bin.readByte();
            switch (tc) {
                case TC_STRING:
                    str = bin.readUTF();
                    break;
    
                case TC_LONGSTRING:
                    str = bin.readLongUTF();
                    break;
    
                default:
                    throw new StreamCorruptedException(
                        String.format("invalid type code: %02X", tc));
            }
            passHandle = handles.assign(unshared ? unsharedMarker : str);
            handles.finish(passHandle);
            return str;
        }
    
        /**
         * Reads in and returns array object, or null if array class is
         * unresolvable.  Sets passHandle to array's assigned handle.
         */
        private Object readArray(boolean unshared) throws IOException {
            if (bin.readByte() != TC_ARRAY) {
                throw new InternalError();
            }
    
            ObjectStreamClass desc = readClassDesc(false);
            int len = bin.readInt();
    
            filterCheck(desc.forClass(), len);
    
            Object array = null;
            Class<?> cl, ccl = null;
            if ((cl = desc.forClass()) != null) {
                ccl = cl.getComponentType();
                array = Array.newInstance(ccl, len);
            }
    
            int arrayHandle = handles.assign(unshared ? unsharedMarker : array);
            ClassNotFoundException resolveEx = desc.getResolveException();
            if (resolveEx != null) {
                handles.markException(arrayHandle, resolveEx);
            }
    
            if (ccl == null) {
                for (int i = 0; i < len; i++) {
                    readObject0(false);
                }
            } else if (ccl.isPrimitive()) {
                if (ccl == Integer.TYPE) {
                    bin.readInts((int[]) array, 0, len);
                } else if (ccl == Byte.TYPE) {
                    bin.readFully((byte[]) array, 0, len, true);
                } else if (ccl == Long.TYPE) {
                    bin.readLongs((long[]) array, 0, len);
                } else if (ccl == Float.TYPE) {
                    bin.readFloats((float[]) array, 0, len);
                } else if (ccl == Double.TYPE) {
                    bin.readDoubles((double[]) array, 0, len);
                } else if (ccl == Short.TYPE) {
                    bin.readShorts((short[]) array, 0, len);
                } else if (ccl == Character.TYPE) {
                    bin.readChars((char[]) array, 0, len);
                } else if (ccl == Boolean.TYPE) {
                    bin.readBooleans((boolean[]) array, 0, len);
                } else {
                    throw new InternalError();
                }
            } else {
                Object[] oa = (Object[]) array;
                for (int i = 0; i < len; i++) {
                    oa[i] = readObject0(false);
                    handles.markDependency(arrayHandle, passHandle);
                }
            }
    
            handles.finish(arrayHandle);
            passHandle = arrayHandle;
            return array;
        }
    
        /**
         * Reads in and returns enum constant, or null if enum type is
         * unresolvable.  Sets passHandle to enum constant's assigned handle.
         */
        private Enum<?> readEnum(boolean unshared) throws IOException {
            if (bin.readByte() != TC_ENUM) {
                throw new InternalError();
            }
    
            ObjectStreamClass desc = readClassDesc(false);
            if (!desc.isEnum()) {
                throw new InvalidClassException("non-enum class: " + desc);
            }
    
            int enumHandle = handles.assign(unshared ? unsharedMarker : null);
            ClassNotFoundException resolveEx = desc.getResolveException();
            if (resolveEx != null) {
                handles.markException(enumHandle, resolveEx);
            }
    
            String name = readString(false);
            Enum<?> result = null;
            Class<?> cl = desc.forClass();
            if (cl != null) {
                try {
                    @SuppressWarnings("unchecked")
                    Enum<?> en = Enum.valueOf((Class)cl, name);
                    result = en;
                } catch (IllegalArgumentException ex) {
                    throw (IOException) new InvalidObjectException(
                        "enum constant " + name + " does not exist in " +
                        cl).initCause(ex);
                }
                if (!unshared) {
                    handles.setObject(enumHandle, result);
                }
            }
    
            handles.finish(enumHandle);
            passHandle = enumHandle;
            return result;
        }
    
        /**
         * Reads and returns "ordinary" (i.e., not a String, Class,
         * ObjectStreamClass, array, or enum constant) object, or null if object's
         * class is unresolvable (in which case a ClassNotFoundException will be
         * associated with object's handle).  Sets passHandle to object's assigned
         * handle.
         */
        private Object readOrdinaryObject(boolean unshared)
            throws IOException
        {
            if (bin.readByte() != TC_OBJECT) {
                throw new InternalError();
            }
    
            ObjectStreamClass desc = readClassDesc(false);
            desc.checkDeserialize();
    
            Class<?> cl = desc.forClass();
            if (cl == String.class || cl == Class.class
                    || cl == ObjectStreamClass.class) {
                throw new InvalidClassException("invalid class descriptor");
            }
    
            Object obj;
            try {
                obj = desc.isInstantiable() ? desc.newInstance() : null;
            } catch (Exception ex) {
                throw (IOException) new InvalidClassException(
                    desc.forClass().getName(),
                    "unable to create instance").initCause(ex);
            }
    
            passHandle = handles.assign(unshared ? unsharedMarker : obj);
            ClassNotFoundException resolveEx = desc.getResolveException();
            if (resolveEx != null) {
                handles.markException(passHandle, resolveEx);
            }
    
            if (desc.isExternalizable()) {
                readExternalData((Externalizable) obj, desc);
            } else {
                readSerialData(obj, desc);
            }
    
            handles.finish(passHandle);
    
            if (obj != null &&
                handles.lookupException(passHandle) == null &&
                desc.hasReadResolveMethod())
            {
                Object rep = desc.invokeReadResolve(obj);
                if (unshared && rep.getClass().isArray()) {
                    rep = cloneArray(rep);
                }
                if (rep != obj) {
                    // Filter the replacement object
                    if (rep != null) {
                        if (rep.getClass().isArray()) {
                            filterCheck(rep.getClass(), Array.getLength(rep));
                        } else {
                            filterCheck(rep.getClass(), -1);
                        }
                    }
                    handles.setObject(passHandle, obj = rep);
                }
            }
    
            return obj;
        }
    
        /**
         * If obj is non-null, reads externalizable data by invoking readExternal()
         * method of obj; otherwise, attempts to skip over externalizable data.
         * Expects that passHandle is set to obj's handle before this method is
         * called.
         */
        private void readExternalData(Externalizable obj, ObjectStreamClass desc)
            throws IOException
        {
            SerialCallbackContext oldContext = curContext;
            if (oldContext != null)
                oldContext.check();
            curContext = null;
            try {
                boolean blocked = desc.hasBlockExternalData();
                if (blocked) {
                    bin.setBlockDataMode(true);
                }
                if (obj != null) {
                    try {
                        obj.readExternal(this);
                    } catch (ClassNotFoundException ex) {
                        /*
                         * In most cases, the handle table has already propagated
                         * a CNFException to passHandle at this point; this mark
                         * call is included to address cases where the readExternal
                         * method has cons'ed and thrown a new CNFException of its
                         * own.
                         */
                         handles.markException(passHandle, ex);
                    }
                }
                if (blocked) {
                    skipCustomData();
                }
            } finally {
                if (oldContext != null)
                    oldContext.check();
                curContext = oldContext;
            }
            /*
             * At this point, if the externalizable data was not written in
             * block-data form and either the externalizable class doesn't exist
             * locally (i.e., obj == null) or readExternal() just threw a
             * CNFException, then the stream is probably in an inconsistent state,
             * since some (or all) of the externalizable data may not have been
             * consumed.  Since there's no "correct" action to take in this case,
             * we mimic the behavior of past serialization implementations and
             * blindly hope that the stream is in sync; if it isn't and additional
             * externalizable data remains in the stream, a subsequent read will
             * most likely throw a StreamCorruptedException.
             */
        }
    
        /**
         * Reads (or attempts to skip, if obj is null or is tagged with a
         * ClassNotFoundException) instance data for each serializable class of
         * object in stream, from superclass to subclass.  Expects that passHandle
         * is set to obj's handle before this method is called.
         */
        private void readSerialData(Object obj, ObjectStreamClass desc)
            throws IOException
        {
            ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
            for (int i = 0; i < slots.length; i++) {
                ObjectStreamClass slotDesc = slots[i].desc;
    
                if (slots[i].hasData) {
                    if (obj == null || handles.lookupException(passHandle) != null) {
                        defaultReadFields(null, slotDesc); // skip field values
                    } else if (slotDesc.hasReadObjectMethod()) {
                        ThreadDeath t = null;
                        boolean reset = false;
                        SerialCallbackContext oldContext = curContext;
                        if (oldContext != null)
                            oldContext.check();
                        try {
                            curContext = new SerialCallbackContext(obj, slotDesc);
    
                            bin.setBlockDataMode(true);
                            slotDesc.invokeReadObject(obj, this);
                        } catch (ClassNotFoundException ex) {
                            /*
                             * In most cases, the handle table has already
                             * propagated a CNFException to passHandle at this
                             * point; this mark call is included to address cases
                             * where the custom readObject method has cons'ed and
                             * thrown a new CNFException of its own.
                             */
                            handles.markException(passHandle, ex);
                        } finally {
                            do {
                                try {
                                    curContext.setUsed();
                                    if (oldContext!= null)
                                        oldContext.check();
                                    curContext = oldContext;
                                    reset = true;
                                } catch (ThreadDeath x) {
                                    t = x;  // defer until reset is true
                                }
                            } while (!reset);
                            if (t != null)
                                throw t;
                        }
    
                        /*
                         * defaultDataEnd may have been set indirectly by custom
                         * readObject() method when calling defaultReadObject() or
                         * readFields(); clear it to restore normal read behavior.
                         */
                        defaultDataEnd = false;
                    } else {
                        defaultReadFields(obj, slotDesc);
                        }
    
                    if (slotDesc.hasWriteObjectData()) {
                        skipCustomData();
                    } else {
                        bin.setBlockDataMode(false);
                    }
                } else {
                    if (obj != null &&
                        slotDesc.hasReadObjectNoDataMethod() &&
                        handles.lookupException(passHandle) == null)
                    {
                        slotDesc.invokeReadObjectNoData(obj);
                    }
                }
            }
                }
    
        /**
         * Skips over all block data and objects until TC_ENDBLOCKDATA is
         * encountered.
         */
        private void skipCustomData() throws IOException {
            int oldHandle = passHandle;
            for (;;) {
                if (bin.getBlockDataMode()) {
                    bin.skipBlockData();
                    bin.setBlockDataMode(false);
                }
                switch (bin.peekByte()) {
                    case TC_BLOCKDATA:
                    case TC_BLOCKDATALONG:
                        bin.setBlockDataMode(true);
                        break;
    
                    case TC_ENDBLOCKDATA:
                        bin.readByte();
                        passHandle = oldHandle;
                        return;
    
                    default:
                        readObject0(false);
                        break;
                }
            }
        }
    
        /**
         * Reads in values of serializable fields declared by given class
         * descriptor.  If obj is non-null, sets field values in obj.  Expects that
         * passHandle is set to obj's handle before this method is called.
         */
        private void defaultReadFields(Object obj, ObjectStreamClass desc)
            throws IOException
        {
            Class<?> cl = desc.forClass();
            if (cl != null && obj != null && !cl.isInstance(obj)) {
                throw new ClassCastException();
            }
    
            int primDataSize = desc.getPrimDataSize();
            if (primVals == null || primVals.length < primDataSize) {
                primVals = new byte[primDataSize];
            }
                bin.readFully(primVals, 0, primDataSize, false);
            if (obj != null) {
                desc.setPrimFieldValues(obj, primVals);
            }
    
            int objHandle = passHandle;
            ObjectStreamField[] fields = desc.getFields(false);
            Object[] objVals = new Object[desc.getNumObjFields()];
            int numPrimFields = fields.length - objVals.length;
            for (int i = 0; i < objVals.length; i++) {
                ObjectStreamField f = fields[numPrimFields + i];
                objVals[i] = readObject0(f.isUnshared());
                if (f.getField() != null) {
                    handles.markDependency(objHandle, passHandle);
                }
            }
            if (obj != null) {
                desc.setObjFieldValues(obj, objVals);
            }
            passHandle = objHandle;
        }
    
        /**
         * Reads in and returns IOException that caused serialization to abort.
         * All stream state is discarded prior to reading in fatal exception.  Sets
         * passHandle to fatal exception's handle.
         */
        private IOException readFatalException() throws IOException {
            if (bin.readByte() != TC_EXCEPTION) {
                throw new InternalError();
            }
            clear();
            return (IOException) readObject0(false);
        }
    
        /**
         * If recursion depth is 0, clears internal data structures; otherwise,
         * throws a StreamCorruptedException.  This method is called when a
         * TC_RESET typecode is encountered.
         */
        private void handleReset() throws StreamCorruptedException {
            if (depth > 0) {
                throw new StreamCorruptedException(
                    "unexpected reset; recursion depth: " + depth);
            }
            clear();
        }
    
        /**
         * Converts specified span of bytes into float values.
         */
        // REMIND: remove once hotspot inlines Float.intBitsToFloat
        private static native void bytesToFloats(byte[] src, int srcpos,
                                                 float[] dst, int dstpos,
                                                 int nfloats);
    
        /**
         * Converts specified span of bytes into double values.
         */
        // REMIND: remove once hotspot inlines Double.longBitsToDouble
        private static native void bytesToDoubles(byte[] src, int srcpos,
                                                  double[] dst, int dstpos,
                                                  int ndoubles);
    
        /**
         * Returns the first non-null class loader (not counting class loaders of
         * generated reflection implementation classes) up the execution stack, or
         * null if only code from the null class loader is on the stack.  This
         * method is also called via reflection by the following RMI-IIOP class:
         *
         *     com.sun.corba.se.internal.util.JDKClassLoader
         *
         * This method should not be removed or its signature changed without
         * corresponding modifications to the above class.
         */
        private static ClassLoader latestUserDefinedLoader() {
            return sun.misc.VM.latestUserDefinedLoader();
        }
    
        /**
         * Default GetField implementation.
         */
        private class GetFieldImpl extends GetField {
    
            /** class descriptor describing serializable fields */
            private final ObjectStreamClass desc;
            /** primitive field values */
            private final byte[] primVals;
            /** object field values */
            private final Object[] objVals;
            /** object field value handles */
            private final int[] objHandles;
    
            /**
             * Creates GetFieldImpl object for reading fields defined in given
             * class descriptor.
             */
            GetFieldImpl(ObjectStreamClass desc) {
                this.desc = desc;
                primVals = new byte[desc.getPrimDataSize()];
                objVals = new Object[desc.getNumObjFields()];
                objHandles = new int[objVals.length];
            }
    
            public ObjectStreamClass getObjectStreamClass() {
                return desc;
            }
    
            public boolean defaulted(String name) throws IOException {
                return (getFieldOffset(name, null) < 0);
            }
    
            public boolean get(String name, boolean val) throws IOException {
                int off = getFieldOffset(name, Boolean.TYPE);
                return (off >= 0) ? Bits.getBoolean(primVals, off) : val;
            }
    
            public byte get(String name, byte val) throws IOException {
                int off = getFieldOffset(name, Byte.TYPE);
                return (off >= 0) ? primVals[off] : val;
            }
    
            public char get(String name, char val) throws IOException {
                int off = getFieldOffset(name, Character.TYPE);
                return (off >= 0) ? Bits.getChar(primVals, off) : val;
            }
    
            public short get(String name, short val) throws IOException {
                int off = getFieldOffset(name, Short.TYPE);
                return (off >= 0) ? Bits.getShort(primVals, off) : val;
            }
    
            public int get(String name, int val) throws IOException {
                int off = getFieldOffset(name, Integer.TYPE);
                return (off >= 0) ? Bits.getInt(primVals, off) : val;
            }
    
            public float get(String name, float val) throws IOException {
                int off = getFieldOffset(name, Float.TYPE);
                return (off >= 0) ? Bits.getFloat(primVals, off) : val;
            }
    
            public long get(String name, long val) throws IOException {
                int off = getFieldOffset(name, Long.TYPE);
                return (off >= 0) ? Bits.getLong(primVals, off) : val;
            }
    
            public double get(String name, double val) throws IOException {
                int off = getFieldOffset(name, Double.TYPE);
                return (off >= 0) ? Bits.getDouble(primVals, off) : val;
            }
    
            public Object get(String name, Object val) throws IOException {
                int off = getFieldOffset(name, Object.class);
                if (off >= 0) {
                    int objHandle = objHandles[off];
                    handles.markDependency(passHandle, objHandle);
                    return (handles.lookupException(objHandle) == null) ?
                        objVals[off] : null;
                } else {
                    return val;
                }
            }
    
            /**
             * Reads primitive and object field values from stream.
             */
            void readFields() throws IOException {
                bin.readFully(primVals, 0, primVals.length, false);
    
                int oldHandle = passHandle;
                ObjectStreamField[] fields = desc.getFields(false);
                int numPrimFields = fields.length - objVals.length;
                for (int i = 0; i < objVals.length; i++) {
                    objVals[i] =
                        readObject0(fields[numPrimFields + i].isUnshared());
                    objHandles[i] = passHandle;
                }
                passHandle = oldHandle;
            }
    
            /**
             * Returns offset of field with given name and type.  A specified type
             * of null matches all types, Object.class matches all non-primitive
             * types, and any other non-null type matches assignable types only.
             * If no matching field is found in the (incoming) class
             * descriptor but a matching field is present in the associated local
             * class descriptor, returns -1.  Throws IllegalArgumentException if
             * neither incoming nor local class descriptor contains a match.
             */
            private int getFieldOffset(String name, Class<?> type) {
                ObjectStreamField field = desc.getField(name, type);
                if (field != null) {
                    return field.getOffset();
                } else if (desc.getLocalDesc().getField(name, type) != null) {
                    return -1;
                } else {
                    throw new IllegalArgumentException("no such field " + name +
                                                       " with type " + type);
                }
            }
        }
    
        /**
         * Prioritized list of callbacks to be performed once object graph has been
         * completely deserialized.
         */
        private static class ValidationList {
    
            private static class Callback {
                final ObjectInputValidation obj;
                final int priority;
                Callback next;
                final AccessControlContext acc;
    
                Callback(ObjectInputValidation obj, int priority, Callback next,
                    AccessControlContext acc)
                {
                    this.obj = obj;
                    this.priority = priority;
                    this.next = next;
                    this.acc = acc;
                }
            }
    
            /** linked list of callbacks */
            private Callback list;
    
            /**
             * Creates new (empty) ValidationList.
             */
            ValidationList() {
            }
    
            /**
             * Registers callback.  Throws InvalidObjectException if callback
             * object is null.
             */
            void register(ObjectInputValidation obj, int priority)
                throws InvalidObjectException
            {
                if (obj == null) {
                    throw new InvalidObjectException("null callback");
                }
    
                Callback prev = null, cur = list;
                while (cur != null && priority < cur.priority) {
                    prev = cur;
                    cur = cur.next;
                }
                AccessControlContext acc = AccessController.getContext();
                if (prev != null) {
                    prev.next = new Callback(obj, priority, cur, acc);
                } else {
                    list = new Callback(obj, priority, list, acc);
                }
            }
    
            /**
             * Invokes all registered callbacks and clears the callback list.
             * Callbacks with higher priorities are called first; those with equal
             * priorities may be called in any order.  If any of the callbacks
             * throws an InvalidObjectException, the callback process is terminated
             * and the exception propagated upwards.
             */
            void doCallbacks() throws InvalidObjectException {
                try {
                    while (list != null) {
                        AccessController.doPrivileged(
                            new PrivilegedExceptionAction<Void>()
                        {
                            public Void run() throws InvalidObjectException {
                                list.obj.validateObject();
                                return null;
                            }
                        }, list.acc);
                        list = list.next;
                    }
                } catch (PrivilegedActionException ex) {
                    list = null;
                    throw (InvalidObjectException) ex.getException();
                }
            }
    
            /**
             * Resets the callback list to its initial (empty) state.
             */
            public void clear() {
                list = null;
            }
        }
    
        /**
         * Hold a snapshot of values to be passed to an ObjectInputFilter.
         */
        static class FilterValues implements ObjectInputFilter.FilterInfo {
            final Class<?> clazz;
            final long arrayLength;
            final long totalObjectRefs;
            final long depth;
            final long streamBytes;
    
            public FilterValues(Class<?> clazz, long arrayLength, long totalObjectRefs,
                                long depth, long streamBytes) {
                this.clazz = clazz;
                this.arrayLength = arrayLength;
                this.totalObjectRefs = totalObjectRefs;
                this.depth = depth;
                this.streamBytes = streamBytes;
            }
    
            @Override
            public Class<?> serialClass() {
                return clazz;
            }
    
            @Override
            public long arrayLength() {
                return arrayLength;
            }
    
            @Override
            public long references() {
                return totalObjectRefs;
            }
    
            @Override
            public long depth() {
                return depth;
            }
    
            @Override
            public long streamBytes() {
                return streamBytes;
            }
        }
    
        /**
         * Input stream supporting single-byte peek operations.
         */
        private static class PeekInputStream extends InputStream {
    
            /** underlying stream */
            private final InputStream in;
            /** peeked byte */
            private int peekb = -1;
            /** total bytes read from the stream */
            private long totalBytesRead = 0;
    
            /**
             * Creates new PeekInputStream on top of given underlying stream.
             */
            PeekInputStream(InputStream in) {
                this.in = in;
            }
    
            /**
             * Peeks at next byte value in stream.  Similar to read(), except
             * that it does not consume the read value.
             */
            int peek() throws IOException {
                if (peekb >= 0) {
                    return peekb;
                }
                peekb = in.read();
                totalBytesRead += peekb >= 0 ? 1 : 0;
                return peekb;
            }
    
            public int read() throws IOException {
                if (peekb >= 0) {
                    int v = peekb;
                    peekb = -1;
                    return v;
                } else {
                    int nbytes = in.read();
                    totalBytesRead += nbytes >= 0 ? 1 : 0;
                    return nbytes;
                }
            }
    
            public int read(byte[] b, int off, int len) throws IOException {
                int nbytes;
                if (len == 0) {
                    return 0;
                } else if (peekb < 0) {
                    nbytes = in.read(b, off, len);
                    totalBytesRead += nbytes >= 0 ? nbytes : 0;
                    return nbytes;
                } else {
                    b[off++] = (byte) peekb;
                    len--;
                    peekb = -1;
                    nbytes = in.read(b, off, len);
                    totalBytesRead += nbytes >= 0 ? nbytes : 0;
                    return (nbytes >= 0) ? (nbytes + 1) : 1;
                }
            }
    
            void readFully(byte[] b, int off, int len) throws IOException {
                int n = 0;
                while (n < len) {
                    int count = read(b, off + n, len - n);
                    if (count < 0) {
                        throw new EOFException();
                    }
                    n += count;
                }
            }
    
            public long skip(long n) throws IOException {
                if (n <= 0) {
                    return 0;
                }
                int skipped = 0;
                if (peekb >= 0) {
                    peekb = -1;
                    skipped++;
                    n--;
                }
                n = skipped + in.skip(n);
                totalBytesRead += n;
                return n;
            }
    
            public int available() throws IOException {
                return in.available() + ((peekb >= 0) ? 1 : 0);
            }
    
            public void close() throws IOException {
                in.close();
            }
    
            public long getBytesRead() {
                return totalBytesRead;
            }
        }
    
        /**
         * Input stream with two modes: in default mode, inputs data written in the
         * same format as DataOutputStream; in "block data" mode, inputs data
         * bracketed by block data markers (see object serialization specification
         * for details).  Buffering depends on block data mode: when in default
         * mode, no data is buffered in advance; when in block data mode, all data
         * for the current data block is read in at once (and buffered).
         */
        private class BlockDataInputStream
            extends InputStream implements DataInput
        {
            /** maximum data block length */
            private static final int MAX_BLOCK_SIZE = 1024;
            /** maximum data block header length */
            private static final int MAX_HEADER_SIZE = 5;
            /** (tunable) length of char buffer (for reading strings) */
            private static final int CHAR_BUF_SIZE = 256;
            /** readBlockHeader() return value indicating header read may block */
            private static final int HEADER_BLOCKED = -2;
    
            /** buffer for reading general/block data */
            private final byte[] buf = new byte[MAX_BLOCK_SIZE];
            /** buffer for reading block data headers */
            private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
            /** char buffer for fast string reads */
            private final char[] cbuf = new char[CHAR_BUF_SIZE];
    
            /** block data mode */
            private boolean blkmode = false;
    
            // block data state fields; values meaningful only when blkmode true
            /** current offset into buf */
            private int pos = 0;
            /** end offset of valid data in buf, or -1 if no more block data */
            private int end = -1;
            /** number of bytes in current block yet to be read from stream */
            private int unread = 0;
    
            /** underlying stream (wrapped in peekable filter stream) */
            private final PeekInputStream in;
            /** loopback stream (for data reads that span data blocks) */
            private final DataInputStream din;
    
            /**
             * Creates new BlockDataInputStream on top of given underlying stream.
             * Block data mode is turned off by default.
             */
            BlockDataInputStream(InputStream in) {
                this.in = new PeekInputStream(in);
                din = new DataInputStream(this);
            }
    
            /**
             * Sets block data mode to the given mode (true == on, false == off)
             * and returns the previous mode value.  If the new mode is the same as
             * the old mode, no action is taken.  Throws IllegalStateException if
             * block data mode is being switched from on to off while unconsumed
             * block data is still present in the stream.
             */
            boolean setBlockDataMode(boolean newmode) throws IOException {
                if (blkmode == newmode) {
                    return blkmode;
                }
                if (newmode) {
                    pos = 0;
                    end = 0;
                    unread = 0;
                } else if (pos < end) {
                    throw new IllegalStateException("unread block data");
                }
                blkmode = newmode;
                return !blkmode;
            }
    
            /**
             * Returns true if the stream is currently in block data mode, false
             * otherwise.
             */
            boolean getBlockDataMode() {
                return blkmode;
            }
    
            /**
             * If in block data mode, skips to the end of the current group of data
             * blocks (but does not unset block data mode).  If not in block data
             * mode, throws an IllegalStateException.
             */
            void skipBlockData() throws IOException {
                if (!blkmode) {
                    throw new IllegalStateException("not in block data mode");
                }
                while (end >= 0) {
                    refill();
                }
            }
    
            /**
             * Attempts to read in the next block data header (if any).  If
             * canBlock is false and a full header cannot be read without possibly
             * blocking, returns HEADER_BLOCKED, else if the next element in the
             * stream is a block data header, returns the block data length
             * specified by the header, else returns -1.
             */
            private int readBlockHeader(boolean canBlock) throws IOException {
                if (defaultDataEnd) {
                    /*
                     * Fix for 4360508: stream is currently at the end of a field
                     * value block written via default serialization; since there
                     * is no terminating TC_ENDBLOCKDATA tag, simulate
                     * end-of-custom-data behavior explicitly.
                     */
                    return -1;
                }
                try {
                    for (;;) {
                        int avail = canBlock ? Integer.MAX_VALUE : in.available();
                        if (avail == 0) {
                            return HEADER_BLOCKED;
                        }
    
                        int tc = in.peek();
                        switch (tc) {
                            case TC_BLOCKDATA:
                                if (avail < 2) {
                                    return HEADER_BLOCKED;
                                }
                                in.readFully(hbuf, 0, 2);
                                return hbuf[1] & 0xFF;
    
                            case TC_BLOCKDATALONG:
                                if (avail < 5) {
                                    return HEADER_BLOCKED;
                                }
                                in.readFully(hbuf, 0, 5);
                                int len = Bits.getInt(hbuf, 1);
                                if (len < 0) {
                                    throw new StreamCorruptedException(
                                        "illegal block data header length: " +
                                        len);
                                }
                                return len;
    
                            /*
                             * TC_RESETs may occur in between data blocks.
                             * Unfortunately, this case must be parsed at a lower
                             * level than other typecodes, since primitive data
                             * reads may span data blocks separated by a TC_RESET.
                             */
                            case TC_RESET:
                                in.read();
                                handleReset();
                                break;
    
                            default:
                                if (tc >= 0 && (tc < TC_BASE || tc > TC_MAX)) {
                                    throw new StreamCorruptedException(
                                        String.format("invalid type code: %02X",
                                        tc));
                                }
                                return -1;
                        }
                    }
                } catch (EOFException ex) {
                    throw new StreamCorruptedException(
                        "unexpected EOF while reading block data header");
                }
            }
    
            /**
             * Refills internal buffer buf with block data.  Any data in buf at the
             * time of the call is considered consumed.  Sets the pos, end, and
             * unread fields to reflect the new amount of available block data; if
             * the next element in the stream is not a data block, sets pos and
             * unread to 0 and end to -1.
             */
            private void refill() throws IOException {
                try {
                    do {
                        pos = 0;
                        if (unread > 0) {
                            int n =
                                in.read(buf, 0, Math.min(unread, MAX_BLOCK_SIZE));
                            if (n >= 0) {
                                end = n;
                                unread -= n;
                            } else {
                                throw new StreamCorruptedException(
                                    "unexpected EOF in middle of data block");
                            }
                        } else {
                            int n = readBlockHeader(true);
                            if (n >= 0) {
                                end = 0;
                                unread = n;
                            } else {
                                end = -1;
                                unread = 0;
                            }
                        }
                    } while (pos == end);
                } catch (IOException ex) {
                    pos = 0;
                    end = -1;
                    unread = 0;
                    throw ex;
                }
            }
    
            /**
             * If in block data mode, returns the number of unconsumed bytes
             * remaining in the current data block.  If not in block data mode,
             * throws an IllegalStateException.
             */
            int currentBlockRemaining() {
                if (blkmode) {
                    return (end >= 0) ? (end - pos) + unread : 0;
                } else {
                    throw new IllegalStateException();
                }
            }
    
            /**
             * Peeks at (but does not consume) and returns the next byte value in
             * the stream, or -1 if the end of the stream/block data (if in block
             * data mode) has been reached.
             */
            int peek() throws IOException {
                if (blkmode) {
                    if (pos == end) {
                        refill();
                    }
                    return (end >= 0) ? (buf[pos] & 0xFF) : -1;
                } else {
                    return in.peek();
                }
            }
    
            /**
             * Peeks at (but does not consume) and returns the next byte value in
             * the stream, or throws EOFException if end of stream/block data has
             * been reached.
             */
            byte peekByte() throws IOException {
                int val = peek();
                if (val < 0) {
                    throw new EOFException();
                }
                return (byte) val;
            }
    
    
            /* ----------------- generic input stream methods ------------------ */
            /*
             * The following methods are equivalent to their counterparts in
             * InputStream, except that they interpret data block boundaries and
             * read the requested data from within data blocks when in block data
             * mode.
             */
    
            public int read() throws IOException {
                if (blkmode) {
                    if (pos == end) {
                        refill();
                    }
                    return (end >= 0) ? (buf[pos++] & 0xFF) : -1;
                } else {
                    return in.read();
                }
            }
    
            public int read(byte[] b, int off, int len) throws IOException {
                return read(b, off, len, false);
            }
    
            public long skip(long len) throws IOException {
                long remain = len;
                while (remain > 0) {
                    if (blkmode) {
                        if (pos == end) {
                            refill();
                        }
                        if (end < 0) {
                            break;
                        }
                        int nread = (int) Math.min(remain, end - pos);
                        remain -= nread;
                        pos += nread;
                    } else {
                        int nread = (int) Math.min(remain, MAX_BLOCK_SIZE);
                        if ((nread = in.read(buf, 0, nread)) < 0) {
                            break;
                        }
                        remain -= nread;
                    }
                }
                return len - remain;
            }
    
            public int available() throws IOException {
                if (blkmode) {
                    if ((pos == end) && (unread == 0)) {
                        int n;
                        while ((n = readBlockHeader(false)) == 0) ;
                        switch (n) {
                            case HEADER_BLOCKED:
                                break;
    
                            case -1:
                                pos = 0;
                                end = -1;
                                break;
    
                            default:
                                pos = 0;
                                end = 0;
                                unread = n;
                                break;
                        }
                    }
                    // avoid unnecessary call to in.available() if possible
                    int unreadAvail = (unread > 0) ?
                        Math.min(in.available(), unread) : 0;
                    return (end >= 0) ? (end - pos) + unreadAvail : 0;
                } else {
                    return in.available();
                }
            }
    
            public void close() throws IOException {
                if (blkmode) {
                    pos = 0;
                    end = -1;
                    unread = 0;
                }
                in.close();
            }
    
            /**
             * Attempts to read len bytes into byte array b at offset off.  Returns
             * the number of bytes read, or -1 if the end of stream/block data has
             * been reached.  If copy is true, reads values into an intermediate
             * buffer before copying them to b (to avoid exposing a reference to
             * b).
             */
            int read(byte[] b, int off, int len, boolean copy) throws IOException {
                if (len == 0) {
                    return 0;
                } else if (blkmode) {
                    if (pos == end) {
                        refill();
                    }
                    if (end < 0) {
                        return -1;
                    }
                    int nread = Math.min(len, end - pos);
                    System.arraycopy(buf, pos, b, off, nread);
                    pos += nread;
                    return nread;
                } else if (copy) {
                    int nread = in.read(buf, 0, Math.min(len, MAX_BLOCK_SIZE));
                    if (nread > 0) {
                        System.arraycopy(buf, 0, b, off, nread);
                    }
                    return nread;
                } else {
                    return in.read(b, off, len);
                }
            }
    
            /* ----------------- primitive data input methods ------------------ */
            /*
             * The following methods are equivalent to their counterparts in
             * DataInputStream, except that they interpret data block boundaries
             * and read the requested data from within data blocks when in block
             * data mode.
             */
    
            public void readFully(byte[] b) throws IOException {
                readFully(b, 0, b.length, false);
            }
    
            public void readFully(byte[] b, int off, int len) throws IOException {
                readFully(b, off, len, false);
            }
    
            public void readFully(byte[] b, int off, int len, boolean copy)
                throws IOException
            {
                while (len > 0) {
                    int n = read(b, off, len, copy);
                    if (n < 0) {
                        throw new EOFException();
                    }
                    off += n;
                    len -= n;
                }
            }
    
            public int skipBytes(int n) throws IOException {
                return din.skipBytes(n);
            }
    
            public boolean readBoolean() throws IOException {
                int v = read();
                if (v < 0) {
                    throw new EOFException();
                }
                return (v != 0);
            }
    
            public byte readByte() throws IOException {
                int v = read();
                if (v < 0) {
                    throw new EOFException();
                }
                return (byte) v;
            }
    
            public int readUnsignedByte() throws IOException {
                int v = read();
                if (v < 0) {
                    throw new EOFException();
                }
                return v;
            }
    
            public char readChar() throws IOException {
                if (!blkmode) {
                    pos = 0;
                    in.readFully(buf, 0, 2);
                } else if (end - pos < 2) {
                    return din.readChar();
                }
                char v = Bits.getChar(buf, pos);
                pos += 2;
                return v;
            }
    
            public short readShort() throws IOException {
                if (!blkmode) {
                    pos = 0;
                    in.readFully(buf, 0, 2);
                } else if (end - pos < 2) {
                    return din.readShort();
                }
                short v = Bits.getShort(buf, pos);
                pos += 2;
                return v;
            }
    
            public int readUnsignedShort() throws IOException {
                if (!blkmode) {
                    pos = 0;
                    in.readFully(buf, 0, 2);
                } else if (end - pos < 2) {
                    return din.readUnsignedShort();
                }
                int v = Bits.getShort(buf, pos) & 0xFFFF;
                pos += 2;
                return v;
            }
    
            public int readInt() throws IOException {
                if (!blkmode) {
                    pos = 0;
                    in.readFully(buf, 0, 4);
                } else if (end - pos < 4) {
                    return din.readInt();
                }
                int v = Bits.getInt(buf, pos);
                pos += 4;
                return v;
            }
    
            public float readFloat() throws IOException {
                if (!blkmode) {
                    pos = 0;
                    in.readFully(buf, 0, 4);
                } else if (end - pos < 4) {
                    return din.readFloat();
                }
                float v = Bits.getFloat(buf, pos);
                pos += 4;
                return v;
            }
    
            public long readLong() throws IOException {
                if (!blkmode) {
                    pos = 0;
                    in.readFully(buf, 0, 8);
                } else if (end - pos < 8) {
                    return din.readLong();
                }
                long v = Bits.getLong(buf, pos);
                pos += 8;
                return v;
            }
    
            public double readDouble() throws IOException {
                if (!blkmode) {
                    pos = 0;
                    in.readFully(buf, 0, 8);
                } else if (end - pos < 8) {
                    return din.readDouble();
                }
                double v = Bits.getDouble(buf, pos);
                pos += 8;
                return v;
            }
    
            public String readUTF() throws IOException {
                return readUTFBody(readUnsignedShort());
            }
    
            @SuppressWarnings("deprecation")
            public String readLine() throws IOException {
                return din.readLine();      // deprecated, not worth optimizing
            }
    
            /* -------------- primitive data array input methods --------------- */
            /*
             * The following methods read in spans of primitive data values.
             * Though equivalent to calling the corresponding primitive read
             * methods repeatedly, these methods are optimized for reading groups
             * of primitive data values more efficiently.
             */
    
            void readBooleans(boolean[] v, int off, int len) throws IOException {
                int stop, endoff = off + len;
                while (off < endoff) {
                    if (!blkmode) {
                        int span = Math.min(endoff - off, MAX_BLOCK_SIZE);
                        in.readFully(buf, 0, span);
                        stop = off + span;
                        pos = 0;
                    } else if (end - pos < 1) {
                        v[off++] = din.readBoolean();
                        continue;
                    } else {
                        stop = Math.min(endoff, off + end - pos);
                    }
    
                    while (off < stop) {
                        v[off++] = Bits.getBoolean(buf, pos++);
                    }
                }
            }
    
            void readChars(char[] v, int off, int len) throws IOException {
                int stop, endoff = off + len;
                while (off < endoff) {
                    if (!blkmode) {
                        int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 1);
                        in.readFully(buf, 0, span << 1);
                        stop = off + span;
                        pos = 0;
                    } else if (end - pos < 2) {
                        v[off++] = din.readChar();
                        continue;
                    } else {
                        stop = Math.min(endoff, off + ((end - pos) >> 1));
                    }
    
                    while (off < stop) {
                        v[off++] = Bits.getChar(buf, pos);
                        pos += 2;
                    }
                }
            }
    
            void readShorts(short[] v, int off, int len) throws IOException {
                int stop, endoff = off + len;
                while (off < endoff) {
                    if (!blkmode) {
                        int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 1);
                        in.readFully(buf, 0, span << 1);
                        stop = off + span;
                        pos = 0;
                    } else if (end - pos < 2) {
                        v[off++] = din.readShort();
                        continue;
                    } else {
                        stop = Math.min(endoff, off + ((end - pos) >> 1));
                    }
    
                    while (off < stop) {
                        v[off++] = Bits.getShort(buf, pos);
                        pos += 2;
                    }
                }
            }
    
            void readInts(int[] v, int off, int len) throws IOException {
                int stop, endoff = off + len;
                while (off < endoff) {
                    if (!blkmode) {
                        int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 2);
                        in.readFully(buf, 0, span << 2);
                        stop = off + span;
                        pos = 0;
                    } else if (end - pos < 4) {
                        v[off++] = din.readInt();
                        continue;
                    } else {
                        stop = Math.min(endoff, off + ((end - pos) >> 2));
                    }
    
                    while (off < stop) {
                        v[off++] = Bits.getInt(buf, pos);
                        pos += 4;
                    }
                }
            }
    
            void readFloats(float[] v, int off, int len) throws IOException {
                int span, endoff = off + len;
                while (off < endoff) {
                    if (!blkmode) {
                        span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 2);
                        in.readFully(buf, 0, span << 2);
                        pos = 0;
                    } else if (end - pos < 4) {
                        v[off++] = din.readFloat();
                        continue;
                    } else {
                        span = Math.min(endoff - off, ((end - pos) >> 2));
                    }
    
                    bytesToFloats(buf, pos, v, off, span);
                    off += span;
                    pos += span << 2;
                }
            }
    
            void readLongs(long[] v, int off, int len) throws IOException {
                int stop, endoff = off + len;
                while (off < endoff) {
                    if (!blkmode) {
                        int span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 3);
                        in.readFully(buf, 0, span << 3);
                        stop = off + span;
                        pos = 0;
                    } else if (end - pos < 8) {
                        v[off++] = din.readLong();
                        continue;
                    } else {
                        stop = Math.min(endoff, off + ((end - pos) >> 3));
                    }
    
                    while (off < stop) {
                        v[off++] = Bits.getLong(buf, pos);
                        pos += 8;
                    }
                }
            }
    
            void readDoubles(double[] v, int off, int len) throws IOException {
                int span, endoff = off + len;
                while (off < endoff) {
                    if (!blkmode) {
                        span = Math.min(endoff - off, MAX_BLOCK_SIZE >> 3);
                        in.readFully(buf, 0, span << 3);
                        pos = 0;
                    } else if (end - pos < 8) {
                        v[off++] = din.readDouble();
                        continue;
                    } else {
                        span = Math.min(endoff - off, ((end - pos) >> 3));
                    }
    
                    bytesToDoubles(buf, pos, v, off, span);
                    off += span;
                    pos += span << 3;
                }
            }
    
            /**
             * Reads in string written in "long" UTF format.  "Long" UTF format is
             * identical to standard UTF, except that it uses an 8 byte header
             * (instead of the standard 2 bytes) to convey the UTF encoding length.
             */
            String readLongUTF() throws IOException {
                return readUTFBody(readLong());
            }
    
            /**
             * Reads in the "body" (i.e., the UTF representation minus the 2-byte
             * or 8-byte length header) of a UTF encoding, which occupies the next
             * utflen bytes.
             */
            private String readUTFBody(long utflen) throws IOException {
                StringBuilder sbuf = new StringBuilder();
                if (!blkmode) {
                    end = pos = 0;
                }
    
                while (utflen > 0) {
                    int avail = end - pos;
                    if (avail >= 3 || (long) avail == utflen) {
                        utflen -= readUTFSpan(sbuf, utflen);
                    } else {
                        if (blkmode) {
                            // near block boundary, read one byte at a time
                            utflen -= readUTFChar(sbuf, utflen);
                        } else {
                            // shift and refill buffer manually
                            if (avail > 0) {
                                System.arraycopy(buf, pos, buf, 0, avail);
                            }
                            pos = 0;
                            end = (int) Math.min(MAX_BLOCK_SIZE, utflen);
                            in.readFully(buf, avail, end - avail);
                        }
                    }
                }
    
                return sbuf.toString();
            }
    
            /**
             * Reads span of UTF-encoded characters out of internal buffer
             * (starting at offset pos and ending at or before offset end),
             * consuming no more than utflen bytes.  Appends read characters to
             * sbuf.  Returns the number of bytes consumed.
             */
            private long readUTFSpan(StringBuilder sbuf, long utflen)
                throws IOException
            {
                int cpos = 0;
                int start = pos;
                int avail = Math.min(end - pos, CHAR_BUF_SIZE);
                // stop short of last char unless all of utf bytes in buffer
                int stop = pos + ((utflen > avail) ? avail - 2 : (int) utflen);
                boolean outOfBounds = false;
    
                try {
                    while (pos < stop) {
                        int b1, b2, b3;
                        b1 = buf[pos++] & 0xFF;
                        switch (b1 >> 4) {
                            case 0:
                            case 1:
                            case 2:
                            case 3:
                            case 4:
                            case 5:
                            case 6:
                            case 7:   // 1 byte format: 0xxxxxxx
                                cbuf[cpos++] = (char) b1;
                                break;
    
                            case 12:
                            case 13:  // 2 byte format: 110xxxxx 10xxxxxx
                                b2 = buf[pos++];
                                if ((b2 & 0xC0) != 0x80) {
                                    throw new UTFDataFormatException();
                                }
                                cbuf[cpos++] = (char) (((b1 & 0x1F) << 6) |
                                                       ((b2 & 0x3F) << 0));
                                break;
    
                            case 14:  // 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx
                                b3 = buf[pos + 1];
                                b2 = buf[pos + 0];
                                pos += 2;
                                if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) {
                                    throw new UTFDataFormatException();
                                }
                                cbuf[cpos++] = (char) (((b1 & 0x0F) << 12) |
                                                       ((b2 & 0x3F) << 6) |
                                                       ((b3 & 0x3F) << 0));
                                break;
    
                            default:  // 10xx xxxx, 1111 xxxx
                                throw new UTFDataFormatException();
                        }
                    }
                } catch (ArrayIndexOutOfBoundsException ex) {
                    outOfBounds = true;
                } finally {
                    if (outOfBounds || (pos - start) > utflen) {
                        /*
                         * Fix for 4450867: if a malformed utf char causes the
                         * conversion loop to scan past the expected end of the utf
                         * string, only consume the expected number of utf bytes.
                         */
                        pos = start + (int) utflen;
                        throw new UTFDataFormatException();
                    }
                }
    
                sbuf.append(cbuf, 0, cpos);
                return pos - start;
            }
    
            /**
             * Reads in single UTF-encoded character one byte at a time, appends
             * the character to sbuf, and returns the number of bytes consumed.
             * This method is used when reading in UTF strings written in block
             * data mode to handle UTF-encoded characters which (potentially)
             * straddle block-data boundaries.
             */
            private int readUTFChar(StringBuilder sbuf, long utflen)
                throws IOException
            {
                int b1, b2, b3;
                b1 = readByte() & 0xFF;
                switch (b1 >> 4) {
                    case 0:
                    case 1:
                    case 2:
                    case 3:
                    case 4:
                    case 5:
                    case 6:
                    case 7:     // 1 byte format: 0xxxxxxx
                        sbuf.append((char) b1);
                        return 1;
    
                    case 12:
                    case 13:    // 2 byte format: 110xxxxx 10xxxxxx
                        if (utflen < 2) {
                            throw new UTFDataFormatException();
                        }
                        b2 = readByte();
                        if ((b2 & 0xC0) != 0x80) {
                            throw new UTFDataFormatException();
                        }
                        sbuf.append((char) (((b1 & 0x1F) << 6) |
                                            ((b2 & 0x3F) << 0)));
                        return 2;
    
                    case 14:    // 3 byte format: 1110xxxx 10xxxxxx 10xxxxxx
                        if (utflen < 3) {
                            if (utflen == 2) {
                                readByte();         // consume remaining byte
                            }
                            throw new UTFDataFormatException();
                        }
                        b2 = readByte();
                        b3 = readByte();
                        if ((b2 & 0xC0) != 0x80 || (b3 & 0xC0) != 0x80) {
                            throw new UTFDataFormatException();
                        }
                        sbuf.append((char) (((b1 & 0x0F) << 12) |
                                            ((b2 & 0x3F) << 6) |
                                            ((b3 & 0x3F) << 0)));
                        return 3;
    
                    default:   // 10xx xxxx, 1111 xxxx
                        throw new UTFDataFormatException();
                }
            }
    
            /**
             * Returns the number of bytes read from the input stream.
             * @return the number of bytes read from the input stream
             */
            long getBytesRead() {
                return in.getBytesRead();
            }
        }
    
        /**
         * Unsynchronized table which tracks wire handle to object mappings, as
         * well as ClassNotFoundExceptions associated with deserialized objects.
         * This class implements an exception-propagation algorithm for
         * determining which objects should have ClassNotFoundExceptions associated
         * with them, taking into account cycles and discontinuities (e.g., skipped
         * fields) in the object graph.
         *
         * <p>General use of the table is as follows: during deserialization, a
         * given object is first assigned a handle by calling the assign method.
         * This method leaves the assigned handle in an "open" state, wherein
         * dependencies on the exception status of other handles can be registered
         * by calling the markDependency method, or an exception can be directly
         * associated with the handle by calling markException.  When a handle is
         * tagged with an exception, the HandleTable assumes responsibility for
         * propagating the exception to any other objects which depend
         * (transitively) on the exception-tagged object.
         *
         * <p>Once all exception information/dependencies for the handle have been
         * registered, the handle should be "closed" by calling the finish method
         * on it.  The act of finishing a handle allows the exception propagation
         * algorithm to aggressively prune dependency links, lessening the
         * performance/memory impact of exception tracking.
         *
         * <p>Note that the exception propagation algorithm used depends on handles
         * being assigned/finished in LIFO order; however, for simplicity as well
         * as memory conservation, it does not enforce this constraint.
         */
        // REMIND: add full description of exception propagation algorithm?
        private static class HandleTable {
    
            /* status codes indicating whether object has associated exception */
            private static final byte STATUS_OK = 1;
            private static final byte STATUS_UNKNOWN = 2;
            private static final byte STATUS_EXCEPTION = 3;
    
            /** array mapping handle -> object status */
            byte[] status;
            /** array mapping handle -> object/exception (depending on status) */
            Object[] entries;
            /** array mapping handle -> list of dependent handles (if any) */
            HandleList[] deps;
            /** lowest unresolved dependency */
            int lowDep = -1;
            /** number of handles in table */
            int size = 0;
    
            /**
             * Creates handle table with the given initial capacity.
             */
            HandleTable(int initialCapacity) {
                status = new byte[initialCapacity];
                entries = new Object[initialCapacity];
                deps = new HandleList[initialCapacity];
            }
    
            /**
             * Assigns next available handle to given object, and returns assigned
             * handle.  Once object has been completely deserialized (and all
             * dependencies on other objects identified), the handle should be
             * "closed" by passing it to finish().
             */
            int assign(Object obj) {
                if (size >= entries.length) {
                    grow();
                }
                status[size] = STATUS_UNKNOWN;
                entries[size] = obj;
                return size++;
            }
    
            /**
             * Registers a dependency (in exception status) of one handle on
             * another.  The dependent handle must be "open" (i.e., assigned, but
             * not finished yet).  No action is taken if either dependent or target
             * handle is NULL_HANDLE.
             */
            void markDependency(int dependent, int target) {
                if (dependent == NULL_HANDLE || target == NULL_HANDLE) {
                    return;
                }
                switch (status[dependent]) {
    
                    case STATUS_UNKNOWN:
                        switch (status[target]) {
                            case STATUS_OK:
                                // ignore dependencies on objs with no exception
                                break;
    
                            case STATUS_EXCEPTION:
                                // eagerly propagate exception
                                markException(dependent,
                                    (ClassNotFoundException) entries[target]);
                                break;
    
                            case STATUS_UNKNOWN:
                                // add to dependency list of target
                                if (deps[target] == null) {
                                    deps[target] = new HandleList();
                                }
                                deps[target].add(dependent);
    
                                // remember lowest unresolved target seen
                                if (lowDep < 0 || lowDep > target) {
                                    lowDep = target;
                                }
                                break;
    
                            default:
                                throw new InternalError();
                        }
                        break;
    
                    case STATUS_EXCEPTION:
                        break;
    
                    default:
                        throw new InternalError();
                }
            }
    
            /**
             * Associates a ClassNotFoundException (if one not already associated)
             * with the currently active handle and propagates it to other
             * referencing objects as appropriate.  The specified handle must be
             * "open" (i.e., assigned, but not finished yet).
             */
            void markException(int handle, ClassNotFoundException ex) {
                switch (status[handle]) {
                    case STATUS_UNKNOWN:
                        status[handle] = STATUS_EXCEPTION;
                        entries[handle] = ex;
    
                        // propagate exception to dependents
                        HandleList dlist = deps[handle];
                        if (dlist != null) {
                            int ndeps = dlist.size();
                            for (int i = 0; i < ndeps; i++) {
                                markException(dlist.get(i), ex);
                            }
                            deps[handle] = null;
                        }
                        break;
    
                    case STATUS_EXCEPTION:
                        break;
    
                    default:
                        throw new InternalError();
                }
            }
    
            /**
             * Marks given handle as finished, meaning that no new dependencies
             * will be marked for handle.  Calls to the assign and finish methods
             * must occur in LIFO order.
             */
            void finish(int handle) {
                int end;
                if (lowDep < 0) {
                    // no pending unknowns, only resolve current handle
                    end = handle + 1;
                } else if (lowDep >= handle) {
                    // pending unknowns now clearable, resolve all upward handles
                    end = size;
                    lowDep = -1;
                } else {
                    // unresolved backrefs present, can't resolve anything yet
                    return;
                }
    
                // change STATUS_UNKNOWN -> STATUS_OK in selected span of handles
                for (int i = handle; i < end; i++) {
                    switch (status[i]) {
                        case STATUS_UNKNOWN:
                            status[i] = STATUS_OK;
                            deps[i] = null;
                            break;
    
                        case STATUS_OK:
                        case STATUS_EXCEPTION:
                            break;
    
                        default:
                            throw new InternalError();
                    }
                }
            }
    
            /**
             * Assigns a new object to the given handle.  The object previously
             * associated with the handle is forgotten.  This method has no effect
             * if the given handle already has an exception associated with it.
             * This method may be called at any time after the handle is assigned.
             */
            void setObject(int handle, Object obj) {
                switch (status[handle]) {
                    case STATUS_UNKNOWN:
                    case STATUS_OK:
                        entries[handle] = obj;
                        break;
    
                    case STATUS_EXCEPTION:
                        break;
    
                    default:
                        throw new InternalError();
                }
            }
    
            /**
             * Looks up and returns object associated with the given handle.
             * Returns null if the given handle is NULL_HANDLE, or if it has an
             * associated ClassNotFoundException.
             */
            Object lookupObject(int handle) {
                return (handle != NULL_HANDLE &&
                        status[handle] != STATUS_EXCEPTION) ?
                    entries[handle] : null;
            }
    
            /**
             * Looks up and returns ClassNotFoundException associated with the
             * given handle.  Returns null if the given handle is NULL_HANDLE, or
             * if there is no ClassNotFoundException associated with the handle.
             */
            ClassNotFoundException lookupException(int handle) {
                return (handle != NULL_HANDLE &&
                        status[handle] == STATUS_EXCEPTION) ?
                    (ClassNotFoundException) entries[handle] : null;
            }
    
            /**
             * Resets table to its initial state.
             */
            void clear() {
                Arrays.fill(status, 0, size, (byte) 0);
                Arrays.fill(entries, 0, size, null);
                Arrays.fill(deps, 0, size, null);
                lowDep = -1;
                size = 0;
            }
    
            /**
             * Returns number of handles registered in table.
             */
            int size() {
                return size;
            }
    
            /**
             * Expands capacity of internal arrays.
             */
            private void grow() {
                int newCapacity = (entries.length << 1) + 1;
    
                byte[] newStatus = new byte[newCapacity];
                Object[] newEntries = new Object[newCapacity];
                HandleList[] newDeps = new HandleList[newCapacity];
    
                System.arraycopy(status, 0, newStatus, 0, size);
                System.arraycopy(entries, 0, newEntries, 0, size);
                System.arraycopy(deps, 0, newDeps, 0, size);
    
                status = newStatus;
                entries = newEntries;
                deps = newDeps;
            }
    
            /**
             * Simple growable list of (integer) handles.
             */
            private static class HandleList {
                private int[] list = new int[4];
                private int size = 0;
    
                public HandleList() {
                }
    
                public void add(int handle) {
                    if (size >= list.length) {
                        int[] newList = new int[list.length << 1];
                        System.arraycopy(list, 0, newList, 0, list.length);
                        list = newList;
                    }
                    list[size++] = handle;
                }
    
                public int get(int index) {
                    if (index >= size) {
                        throw new ArrayIndexOutOfBoundsException();
                    }
                    return list[index];
                }
    
                public int size() {
                    return size;
                }
            }
        }
    
        /**
         * Method for cloning arrays in case of using unsharing reading
         */
        private static Object cloneArray(Object array) {
            if (array instanceof Object[]) {
                return ((Object[]) array).clone();
            } else if (array instanceof boolean[]) {
                return ((boolean[]) array).clone();
            } else if (array instanceof byte[]) {
                return ((byte[]) array).clone();
            } else if (array instanceof char[]) {
                return ((char[]) array).clone();
            } else if (array instanceof double[]) {
                return ((double[]) array).clone();
            } else if (array instanceof float[]) {
                return ((float[]) array).clone();
            } else if (array instanceof int[]) {
                return ((int[]) array).clone();
            } else if (array instanceof long[]) {
                return ((long[]) array).clone();
            } else if (array instanceof short[]) {
                return ((short[]) array).clone();
            } else {
                throw new AssertionError();
            }
        }
    
        private void validateDescriptor(ObjectStreamClass descriptor) {
            ObjectStreamClassValidator validating = validator;
            if (validating != null) {
                validating.validateDescriptor(descriptor);
            }
        }
    
        // controlled access to ObjectStreamClassValidator
        private volatile ObjectStreamClassValidator validator;
    
        private static void setValidator(ObjectInputStream ois, ObjectStreamClassValidator validator) {
            ois.validator = validator;
        }
        static {
            SharedSecrets.setJavaObjectInputStreamAccess(ObjectInputStream::setValidator);
        }
    }
    

      

    #######################

  • 相关阅读:
    游LeetCode一月之闲谈
    新年计划与企盼
    F#周报2019年第51&52期
    F#周报2019年第50期
    F#周报2019年第49期
    F#周报2019年第48期
    F#周报2019年第47期
    F#周报2019年第46期
    F#周报2019年第45期
    关于我的随笔
  • 原文地址:https://www.cnblogs.com/herd/p/16268325.html
Copyright © 2020-2023  润新知