diff -r 4252bfc396fc -r d382dacfd73f emul/compact/src/main/java/java/io/ObjectInputStream.java --- a/emul/compact/src/main/java/java/io/ObjectInputStream.java Tue Feb 26 14:55:55 2013 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,3357 +0,0 @@ -/* - * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package java.io; - -import java.lang.reflect.Array; -import java.lang.reflect.Modifier; -import java.lang.reflect.Proxy; -import java.util.Arrays; -import java.util.HashMap; -import org.apidesign.bck2brwsr.emul.lang.System; - -/** - * An ObjectInputStream deserializes primitive data and objects previously - * written using an ObjectOutputStream. - * - *

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

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

Only objects that support the java.io.Serializable or - * java.io.Externalizable interface can be read from streams. - * - *

The method readObject 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. - * - *

Primitive data types can be read from the stream using the appropriate - * method on DataInput. - * - *

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

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

For example to read from a stream as written by the example in - * ObjectOutputStream: - *
- *

- *      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();
- * 
- * - *

Classes control how they are serialized by implementing either the - * java.io.Serializable or java.io.Externalizable interfaces. - * - *

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

Serializable classes that require special handling during the - * serialization and deserialization process should implement the following - * methods:

- * - *

- * private void writeObject(java.io.ObjectOutputStream stream)
- *     throws IOException;
- * private void readObject(java.io.ObjectInputStream stream)
- *     throws IOException, ClassNotFoundException;
- * private void readObjectNoData()
- *     throws ObjectStreamException;
- * 
- * - *

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

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

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 - * ObjectStreamConstants.PROTOCOL_VERSION_1 protocol, in which the - * end of data written by writeExternal methods is not demarcated, and hence - * cannot be detected. - * - *

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

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

Any exception that occurs while deserializing an object will be caught by - * the ObjectInputStream and abort the reading process. - * - *

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

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 Enum.valueOf(Class, String) 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 Object Serialization Specification, Section 3, Object Input Classes - * @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> 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); - } - - /** filter stream for handling block data conversion */ - private final BlockDataInputStream bin; - /** validation callback list */ - private final ValidationList vlist; - /** recursion depth */ - private int depth; - /** 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 Object curContext; - - /** - * 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. - * - *

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 in is null - * @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(); - 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. - * - *

If there is a security manager installed, this method first calls the - * security manager's checkPermission method with the - * SerializablePermission("enableSubclassImplementation") - * permission to ensure it's ok to enable subclassing. - * - * @throws SecurityException if a security manager exists and its - * checkPermission method denies enabling - * subclassing. - * @see SecurityManager#checkPermission - * @see java.io.SerializablePermission - */ - protected ObjectInputStream() throws IOException, SecurityException { - throw new SecurityException(); - } - - /** - * 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 overriden 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. - * - *

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

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 - { - throw new IOException(); - } - - /** - * 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: - *

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

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 - { - if (curContext == null) { - throw new NotActiveException("not in call to readObject"); - } - Object curObj = null; // curContext.getObj(); - ObjectStreamClass curDesc = null; // curContext.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 GetField 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 - { - if (curContext == null) { - throw new NotActiveException("not in call to readObject"); - } - Object curObj = null; // curContext.getObj(); - ObjectStreamClass curDesc = null; // curContext.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. - * - *

The corresponding method in ObjectOutputStream is - * annotateClass. 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 - * Class 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. - * - *

The default implementation of this method in - * ObjectInputStream returns the result of calling - *

-     *     Class.forName(desc.getName(), false, loader)
-     * 
- * where loader 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 loader is class - * loader corresponding to the closest such method to the currently - * executing frame; otherwise, loader is - * null. If this call results in a - * ClassNotFoundException and the name of the passed - * ObjectStreamClass instance is the Java language keyword - * for a primitive type or void, then the Class object - * representing that primitive type or void will be returned - * (e.g., an ObjectStreamClass with the name - * "int" will be resolved to Integer.TYPE). - * Otherwise, the ClassNotFoundException will be thrown to - * the caller of this method. - * - * @param desc an instance of class ObjectStreamClass - * @return a Class object corresponding to desc - * @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. - * - *

This method is called exactly once for each unique proxy class - * descriptor in the stream. - * - *

The corresponding method in ObjectOutputStream is - * annotateProxyClass. For a given subclass of - * ObjectInputStream that overrides this method, the - * annotateProxyClass method in the corresponding subclass of - * ObjectOutputStream must write any data or objects read by - * this method. - * - *

The default implementation of this method in - * ObjectInputStream returns the result of calling - * Proxy.getProxyClass with the list of Class - * objects for the interfaces that are named in the interfaces - * parameter. The Class object for each interface name - * i is the value returned by calling - *

-     *     Class.forName(i, false, loader)
-     * 
- * where loader is that of the first non-null - * class loader up the execution stack, or null if no - * non-null class loaders are on the stack (the same class - * loader choice used by the resolveClass method). Unless any - * of the resolved interfaces are non-public, this same value of - * loader is also the class loader passed to - * Proxy.getProxyClass; if non-public interfaces are present, - * their class loader is passed instead (if more than one non-public - * interface class loader is encountered, an - * IllegalAccessError is thrown). - * If Proxy.getProxyClass throws an - * IllegalArgumentException, resolveProxyClass - * will throw a ClassNotFoundException containing the - * IllegalArgumentException. - * - * @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 - * InputStream - * @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. - * - *

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

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

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

If enable is true, and there is a security manager installed, - * this method first calls the security manager's - * checkPermission method with the - * SerializablePermission("enableSubstitution") 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 resolveObject for - * every object being deserialized - * @return the previous setting before this method was invoked - * @throws SecurityException if a security manager exists and its - * checkPermission 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 - { - throw new SecurityException(); - } - - /** - * 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 InputStream - * @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 writeClassDescriptor 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 InputStream - */ - 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 InputStream - * @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 - * modified UTF-8 - * format. - * - * @return the String. - * @throws IOException if there are I/O errors while reading from the - * underlying InputStream - * @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(); - } - - /** - * 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 InputStream - * @throws IllegalArgumentException if name 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 name does not - * have a value - * @return the value of the named boolean field - * @throws IOException if there are I/O errors while reading from the - * underlying InputStream - * @throws IllegalArgumentException if type of name 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 name does not - * have a value - * @return the value of the named byte field - * @throws IOException if there are I/O errors while reading from the - * underlying InputStream - * @throws IllegalArgumentException if type of name 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 name does not - * have a value - * @return the value of the named char field - * @throws IOException if there are I/O errors while reading from the - * underlying InputStream - * @throws IllegalArgumentException if type of name 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 name does not - * have a value - * @return the value of the named short field - * @throws IOException if there are I/O errors while reading from the - * underlying InputStream - * @throws IllegalArgumentException if type of name 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 name does not - * have a value - * @return the value of the named int field - * @throws IOException if there are I/O errors while reading from the - * underlying InputStream - * @throws IllegalArgumentException if type of name 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 name does not - * have a value - * @return the value of the named long field - * @throws IOException if there are I/O errors while reading from the - * underlying InputStream - * @throws IllegalArgumentException if type of name 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 name does not - * have a value - * @return the value of the named float field - * @throws IOException if there are I/O errors while reading from the - * underlying InputStream - * @throws IllegalArgumentException if type of name 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 name does not - * have a value - * @return the value of the named double field - * @throws IOException if there are I/O errors while reading from the - * underlying InputStream - * @throws IllegalArgumentException if type of name 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 name does not - * have a value - * @return the value of the named Object field - * @throws IOException if there are I/O errors while reading from the - * underlying InputStream - * @throws IllegalArgumentException if type of name 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; - } - throw new SecurityException(); - } - - /** - * 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++; - 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) { - 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"); - } - 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(); - switch (tc) { - case TC_NULL: - return (ObjectStreamClass) readNull(); - - case TC_REFERENCE: - return (ObjectStreamClass) readHandle(unshared); - - case TC_PROXYCLASSDESC: - return readProxyDesc(unshared); - - case TC_CLASSDESC: - return readNonProxyDesc(unshared); - - default: - throw new StreamCorruptedException( - String.format("invalid type code: %02X", tc)); - } - } - - /** - * 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"); - } - } catch (ClassNotFoundException ex) { - resolveEx = ex; - } - skipCustomData(); - - desc.initProxy(cl, resolveEx, readClassDesc(false)); - - 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); - try { - if ((cl = resolveClass(readDesc)) == null) { - resolveEx = new ClassNotFoundException("null class"); - } - } catch (ClassNotFoundException ex) { - resolveEx = ex; - } - skipCustomData(); - - desc.initNonProxy(readDesc, cl, resolveEx, readClassDesc(false)); - - 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(); - - 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 en = null; - Class cl = desc.forClass(); - if (cl != null) { - try { - en = Enum.valueOf(cl, name); - } catch (IllegalArgumentException ex) { - throw (IOException) new InvalidObjectException( - "enum constant " + name + " does not exist in " + - cl).initCause(ex); - } - if (!unshared) { - handles.setObject(enumHandle, en); - } - } - - handles.finish(enumHandle); - passHandle = enumHandle; - return en; - } - - /** - * 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(); - - 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) { - 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 - { - Object oldContext = curContext; - 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 { - 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 && - slotDesc.hasReadObjectMethod() && - handles.lookupException(passHandle) == null) - { - Object oldContext = curContext; - - try { - curContext = null; //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 { - //curContext.setUsed(); - curContext = oldContext; - } - - /* - * 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 - { - // REMIND: is isInstance check necessary? - 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. - */ - // REMIND: change name to something more accurate? - private static native ClassLoader 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 { - - - /** - * 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"); - } - throw new InvalidObjectException("Does not work."); - } - - /** - * 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 { - } - - /** - * Resets the callback list to its initial (empty) state. - */ - public void clear() { - } - } - - /** - * 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; - - /** - * 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 { - return (peekb >= 0) ? peekb : (peekb = in.read()); - } - - public int read() throws IOException { - if (peekb >= 0) { - int v = peekb; - peekb = -1; - return v; - } else { - return in.read(); - } - } - - public int read(byte[] b, int off, int len) throws IOException { - if (len == 0) { - return 0; - } else if (peekb < 0) { - return in.read(b, off, len); - } else { - b[off++] = (byte) peekb; - len--; - peekb = -1; - int n = in.read(b, off, len); - return (n >= 0) ? (n + 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--; - } - return skipped + skip(n); - } - - public int available() throws IOException { - return in.available() + ((peekb >= 0) ? 1 : 0); - } - - public void close() throws IOException { - in.close(); - } - } - - /** - * 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()); - } - - 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(); - } - } - } - - /** - * 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. - * - *

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

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

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(); - } - } - -}