From 554fd8c5195424bdbcabf5de30fdc183aba391bd Mon Sep 17 00:00:00 2001 From: upstream source tree Date: Sun, 15 Mar 2015 20:14:05 -0400 Subject: obtained gcc-4.6.4.tar.bz2 from upstream website; verified gcc-4.6.4.tar.bz2.sig; imported gcc-4.6.4 source tree from verified upstream tarball. downloading a git-generated archive based on the 'upstream' tag should provide you with a source tree that is binary identical to the one extracted from the above tarball. if you have obtained the source via the command 'git clone', however, do note that line-endings of files in your working directory might differ from line-endings of the respective files in the upstream repository. --- libjava/classpath/java/io/ObjectInputStream.java | 2142 ++++++++++++++++++++++ 1 file changed, 2142 insertions(+) create mode 100644 libjava/classpath/java/io/ObjectInputStream.java (limited to 'libjava/classpath/java/io/ObjectInputStream.java') diff --git a/libjava/classpath/java/io/ObjectInputStream.java b/libjava/classpath/java/io/ObjectInputStream.java new file mode 100644 index 000000000..c37f7665f --- /dev/null +++ b/libjava/classpath/java/io/ObjectInputStream.java @@ -0,0 +1,2142 @@ +/* ObjectInputStream.java -- Class used to read serialized objects + Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2008 + Free Software Foundation, Inc. + +This file is part of GNU Classpath. + +GNU Classpath is free software; you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation; either version 2, or (at your option) +any later version. + +GNU Classpath 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 for more details. + +You should have received a copy of the GNU General Public License +along with GNU Classpath; see the file COPYING. If not, write to the +Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +02110-1301 USA. + +Linking this library statically or dynamically with other modules is +making a combined work based on this library. Thus, the terms and +conditions of the GNU General Public License cover the whole +combination. + +As a special exception, the copyright holders of this library give you +permission to link this library with independent modules to produce an +executable, regardless of the license terms of these independent +modules, and to copy and distribute the resulting executable under +terms of your choice, provided that you also meet, for each linked +independent module, the terms and conditions of the license of that +module. An independent module is a module which is not derived from +or based on this library. If you modify this library, you may extend +this exception to your version of the library, but you are not +obligated to do so. If you do not wish to do so, delete this +exception statement from your version. */ + + +package java.io; + +import gnu.classpath.Pair; +import gnu.classpath.VMStackWalker; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.lang.reflect.Modifier; +import java.lang.reflect.Proxy; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.Map; +import java.util.TreeSet; + +/** + * @author Tom Tromey (tromey@redhat.com) + * @author Jeroen Frijters (jeroen@frijters.net) + * @author Guilhem Lavaux (guilhem@kaffe.org) + * @author Michael Koch (konqueror@gmx.de) + * @author Andrew John Hughes (gnu_andrew@member.fsf.org) + */ +public class ObjectInputStream extends InputStream + implements ObjectInput, ObjectStreamConstants +{ + /** + * Creates a new ObjectInputStream that will do all of + * its reading from in. This method also checks + * the stream by reading the header information (stream magic number + * and stream version). + * + * @exception IOException Reading stream header from underlying + * stream cannot be completed. + * + * @exception StreamCorruptedException An invalid stream magic + * number or stream version was read from the stream. + * + * @see #readStreamHeader() + */ + public ObjectInputStream(InputStream in) + throws IOException, StreamCorruptedException + { + if (DEBUG) + { + String val = System.getProperty("gcj.dumpobjects"); + if (dump == false && val != null && !val.equals("")) + { + dump = true; + System.out.println ("Serialization debugging enabled"); + } + else if (dump == true && (val == null || val.equals(""))) + { + dump = false; + System.out.println ("Serialization debugging disabled"); + } + } + + this.resolveEnabled = false; + this.blockDataPosition = 0; + this.blockDataBytes = 0; + this.blockData = new byte[BUFFER_SIZE]; + this.blockDataInput = new DataInputStream(this); + this.realInputStream = new DataInputStream(in); + this.nextOID = baseWireHandle; + handles = new HashMap>(); + this.classLookupTable = new Hashtable(); + setBlockDataMode(true); + readStreamHeader(); + } + + + /** + * Returns the next deserialized object read from the underlying stream. + * + * This method can be overriden by a class by implementing + * private void readObject (ObjectInputStream). + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. This method can also throw Errors and + * RuntimeExceptions if caused by existing readResolve() user code. + * + * @return The object read from the underlying stream. + * + * @exception ClassNotFoundException The class that an object being + * read in belongs to cannot be found. + * + * @exception IOException Exception from underlying + * InputStream. + */ + public final Object readObject() + throws ClassNotFoundException, IOException + { + return readObject(true); + } + + /** + *

+ * Returns the next deserialized object read from the + * underlying stream in an unshared manner. Any object + * returned by this method will not be returned by + * subsequent calls to either this method or {@link #readObject()}. + *

+ *

+ * This behaviour is achieved by: + *

+ *
    + *
  • Marking the handles created by successful calls to this + * method, so that future calls to {@link #readObject()} or + * {@link #readUnshared()} will throw an {@link ObjectStreamException} + * rather than returning the same object reference.
  • + *
  • Throwing an {@link ObjectStreamException} if the next + * element in the stream is a reference to an earlier object.
  • + *
+ * + * @return a reference to the deserialized object. + * @throws ClassNotFoundException if the class of the object being + * deserialized can not be found. + * @throws StreamCorruptedException if information in the stream + * is inconsistent. + * @throws ObjectStreamException if the next object has already been + * returned by an earlier call to this + * method or {@link #readObject()}. + * @throws OptionalDataException if primitive data occurs next in the stream. + * @throws IOException if an I/O error occurs from the stream. + * @since 1.4 + * @see #readObject() + */ + public Object readUnshared() + throws IOException, ClassNotFoundException + { + return readObject(false); + } + + /** + * Returns the next deserialized object read from the underlying stream. + * + * This method can be overriden by a class by implementing + * private void readObject (ObjectInputStream). + * + * If an exception is thrown from this method, the stream is left in + * an undefined state. This method can also throw Errors and + * RuntimeExceptions if caused by existing readResolve() user code. + * + * @param shared true if handles created by this call should be shared + * with later calls. + * @return The object read from the underlying stream. + * + * @exception ClassNotFoundException The class that an object being + * read in belongs to cannot be found. + * + * @exception IOException Exception from underlying + * InputStream. + */ + private final Object readObject(boolean shared) + throws ClassNotFoundException, IOException + { + if (this.useSubclassMethod) + return readObjectOverride(); + + Object ret_val; + boolean old_mode = setBlockDataMode(false); + byte marker = this.realInputStream.readByte(); + + if (DEBUG) + depth += 2; + + if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " "); + + try + { + ret_val = parseContent(marker, shared); + } + finally + { + setBlockDataMode(old_mode); + if (DEBUG) + depth -= 2; + } + + return ret_val; + } + + /** + * Handles a content block within the stream, which begins with a marker + * byte indicating its type. + * + * @param marker the byte marker. + * @param shared true if handles created by this call should be shared + * with later calls. + * @return an object which represents the parsed content. + * @throws ClassNotFoundException if the class of an object being + * read in cannot be found. + * @throws IOException if invalid data occurs or one is thrown by the + * underlying InputStream. + */ + private Object parseContent(byte marker, boolean shared) + throws ClassNotFoundException, IOException + { + Object ret_val; + boolean is_consumed = false; + + switch (marker) + { + case TC_ENDBLOCKDATA: + { + ret_val = null; + is_consumed = true; + break; + } + + case TC_BLOCKDATA: + case TC_BLOCKDATALONG: + { + if (marker == TC_BLOCKDATALONG) + { if(dump) dumpElementln("BLOCKDATALONG"); } + else + { if(dump) dumpElementln("BLOCKDATA"); } + readNextBlock(marker); + } + + case TC_NULL: + { + if(dump) dumpElementln("NULL"); + ret_val = null; + break; + } + + case TC_REFERENCE: + { + if(dump) dumpElement("REFERENCE "); + int oid = realInputStream.readInt(); + if(dump) dumpElementln(Integer.toHexString(oid)); + ret_val = lookupHandle(oid); + if (!shared) + throw new + InvalidObjectException("References can not be read unshared."); + break; + } + + case TC_CLASS: + { + if(dump) dumpElementln("CLASS"); + ObjectStreamClass osc = (ObjectStreamClass)readObject(); + Class clazz = osc.forClass(); + assignNewHandle(clazz,shared); + ret_val = clazz; + break; + } + + case TC_PROXYCLASSDESC: + { + if(dump) dumpElementln("PROXYCLASS"); + +/* GCJ LOCAL */ + // The grammar at this point is + // TC_PROXYCLASSDESC newHandle proxyClassDescInfo + // i.e. we have to assign the handle immediately after + // reading the marker. + int handle = assignNewHandle("Dummy proxy",shared); +/* END GCJ LOCAL */ + + int n_intf = this.realInputStream.readInt(); + String[] intfs = new String[n_intf]; + for (int i = 0; i < n_intf; i++) + { + intfs[i] = this.realInputStream.readUTF(); + } + + boolean oldmode = setBlockDataMode(true); + Class cl = resolveProxyClass(intfs); + setBlockDataMode(oldmode); + + ObjectStreamClass osc = lookupClass(cl); + if (osc.firstNonSerializableParentConstructor == null) + { + osc.realClassIsSerializable = true; + osc.fields = osc.fieldMapping = new ObjectStreamField[0]; + try + { + osc.firstNonSerializableParentConstructor = + Object.class.getConstructor(new Class[0]); + } + catch (NoSuchMethodException x) + { + throw (InternalError) + new InternalError("Object ctor missing").initCause(x); + } + } +/* GCJ LOCAL */ + rememberHandle(osc,shared,handle); +/* END GCJ LOCAL */ + + if (!is_consumed) + { + byte b = this.realInputStream.readByte(); + if (b != TC_ENDBLOCKDATA) + throw new IOException("Data annotated to class was not consumed." + b); + } + else + is_consumed = false; + ObjectStreamClass superosc = (ObjectStreamClass)readObject(); + osc.setSuperclass(superosc); + ret_val = osc; + break; + } + + case TC_CLASSDESC: + { + ObjectStreamClass osc = readClassDescriptor(); + + if (!is_consumed) + { + byte b = this.realInputStream.readByte(); + if (b != TC_ENDBLOCKDATA) + throw new IOException("Data annotated to class was not consumed." + b); + } + else + is_consumed = false; + + osc.setSuperclass ((ObjectStreamClass)readObject()); + ret_val = osc; + break; + } + + case TC_STRING: + { + if(dump) dumpElement("STRING="); + String s = this.realInputStream.readUTF(); + if(dump) dumpElementln(s); + ret_val = processResolution(null, s, assignNewHandle(s,shared), + shared); + break; + } + + case TC_LONGSTRING: + { + if(dump) dumpElement("STRING="); + String s = this.realInputStream.readUTFLong(); + if(dump) dumpElementln(s); + ret_val = processResolution(null, s, assignNewHandle(s,shared), + shared); + break; + } + + case TC_ARRAY: + { + if(dump) dumpElementln("ARRAY"); + ObjectStreamClass osc = (ObjectStreamClass)readObject(); + Class componentType = osc.forClass().getComponentType(); + if(dump) dumpElement("ARRAY LENGTH="); + int length = this.realInputStream.readInt(); + if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType); + Object array = Array.newInstance(componentType, length); + int handle = assignNewHandle(array,shared); + readArrayElements(array, componentType); + if(dump) + for (int i = 0, len = Array.getLength(array); i < len; i++) + dumpElementln(" ELEMENT[" + i + "]=", Array.get(array, i)); + ret_val = processResolution(null, array, handle, shared); + break; + } + + case TC_OBJECT: + { + if(dump) dumpElementln("OBJECT"); + ObjectStreamClass osc = (ObjectStreamClass)readObject(); + Class clazz = osc.forClass(); + + if (!osc.realClassIsSerializable) + throw new NotSerializableException + (clazz + " is not Serializable, and thus cannot be deserialized."); + + if (osc.realClassIsExternalizable) + { + Externalizable obj = osc.newInstance(); + + int handle = assignNewHandle(obj,shared); + + boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0); + + boolean oldmode = this.readDataFromBlock; + if (read_from_blocks) + setBlockDataMode(true); + + obj.readExternal(this); + + if (read_from_blocks) + { + setBlockDataMode(oldmode); + if (!oldmode) + if (this.realInputStream.readByte() != TC_ENDBLOCKDATA) + throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method."); + } + + ret_val = processResolution(osc, obj, handle,shared); + break; + + } // end if (osc.realClassIsExternalizable) + + Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor); + + int handle = assignNewHandle(obj,shared); + Object prevObject = this.currentObject; + ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass; + TreeSet prevObjectValidators = + this.currentObjectValidators; + + this.currentObject = obj; + this.currentObjectValidators = null; + ObjectStreamClass[] hierarchy = hierarchy(clazz); + + for (int i = 0; i < hierarchy.length; i++) + { + this.currentObjectStreamClass = hierarchy[i]; + if(dump) dumpElementln("Reading fields of " + this.currentObjectStreamClass.getName ()); + + // XXX: should initialize fields in classes in the hierarchy + // that aren't in the stream + // should skip over classes in the stream that aren't in the + // real classes hierarchy + + Method readObjectMethod = this.currentObjectStreamClass.readObjectMethod; + if (readObjectMethod != null) + { + fieldsAlreadyRead = false; + boolean oldmode = setBlockDataMode(true); + callReadMethod(readObjectMethod, this.currentObjectStreamClass.forClass(), obj); + setBlockDataMode(oldmode); + } + else + { + readFields(obj, currentObjectStreamClass); + } + + if (this.currentObjectStreamClass.hasWriteMethod()) + { + if(dump) dumpElement("ENDBLOCKDATA? "); + try + { + /* Read blocks until an end marker */ + byte writeMarker = this.realInputStream.readByte(); + while (writeMarker != TC_ENDBLOCKDATA) + { + parseContent(writeMarker, shared); + writeMarker = this.realInputStream.readByte(); + } + if(dump) dumpElementln("yes"); + } + catch (EOFException e) + { + throw (IOException) new IOException + ("No end of block data seen for class with readObject (ObjectInputStream) method.").initCause(e); + } + } + } + + this.currentObject = prevObject; + this.currentObjectStreamClass = prevObjectStreamClass; + ret_val = processResolution(osc, obj, handle, shared); + if (currentObjectValidators != null) + invokeValidators(); + this.currentObjectValidators = prevObjectValidators; + + break; + } + + case TC_RESET: + if(dump) dumpElementln("RESET"); + clearHandles(); + ret_val = readObject(); + break; + + case TC_EXCEPTION: + { + if(dump) dumpElement("EXCEPTION="); + Exception e = (Exception)readObject(); + if(dump) dumpElementln(e.toString()); + clearHandles(); + throw new WriteAbortedException("Exception thrown during writing of stream", e); + } + + case TC_ENUM: + { + /* TC_ENUM classDesc newHandle enumConstantName */ + if (dump) + dumpElementln("ENUM="); + ObjectStreamClass osc = (ObjectStreamClass) readObject(); + String constantName = (String) readObject(); + if (dump) + dumpElementln("CONSTANT NAME = " + constantName); + Class clazz = osc.forClass(); + Enum instance = Enum.valueOf(clazz, constantName); + assignNewHandle(instance,shared); + ret_val = instance; + break; + } + + default: + throw new IOException("Unknown marker on stream: " + marker); + } + return ret_val; + } + + /** + * This method makes a partial check of types for the fields + * contained given in arguments. It checks primitive types of + * fields1 against non primitive types of fields2. This method + * assumes the two lists has already been sorted according to + * the Java specification. + * + * @param name Name of the class owning the given fields. + * @param fields1 First list to check. + * @param fields2 Second list to check. + * @throws InvalidClassException if a field in fields1, which has a primitive type, is a present + * in the non primitive part in fields2. + */ + private void checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2) + throws InvalidClassException + { + int nonPrimitive = 0; + + for (nonPrimitive = 0; + nonPrimitive < fields1.length + && fields1[nonPrimitive].isPrimitive(); nonPrimitive++) + { + } + + if (nonPrimitive == fields1.length) + return; + + int i = 0; + ObjectStreamField f1; + ObjectStreamField f2; + + while (i < fields2.length + && nonPrimitive < fields1.length) + { + f1 = fields1[nonPrimitive]; + f2 = fields2[i]; + + if (!f2.isPrimitive()) + break; + + int compVal = f1.getName().compareTo (f2.getName()); + + if (compVal < 0) + { + nonPrimitive++; + } + else if (compVal > 0) + { + i++; + } + else + { + throw new InvalidClassException + ("invalid field type for " + f2.getName() + + " in class " + name); + } + } + } + + /** + * This method reads a class descriptor from the real input stream + * and use these data to create a new instance of ObjectStreamClass. + * Fields are sorted and ordered for the real read which occurs for + * each instance of the described class. Be aware that if you call that + * method you must ensure that the stream is synchronized, in the other + * case it may be completely desynchronized. + * + * @return A new instance of ObjectStreamClass containing the freshly + * created descriptor. + * @throws ClassNotFoundException if the required class to build the + * descriptor has not been found in the system. + * @throws IOException An input/output error occured. + * @throws InvalidClassException If there was a compatibility problem + * between the class present in the system and the serialized class. + */ + protected ObjectStreamClass readClassDescriptor() + throws ClassNotFoundException, IOException + { + if(dump) dumpElement("CLASSDESC NAME="); + String name = this.realInputStream.readUTF(); + if(dump) dumpElement(name + "; UID="); + long uid = this.realInputStream.readLong (); + if(dump) dumpElement(Long.toHexString(uid) + "; FLAGS="); + byte flags = this.realInputStream.readByte (); + if(dump) dumpElement(Integer.toHexString(flags) + "; FIELD COUNT="); + short field_count = this.realInputStream.readShort(); + if(dump) dumpElementln(Short.toString(field_count)); + ObjectStreamField[] fields = new ObjectStreamField[field_count]; + ObjectStreamClass osc = new ObjectStreamClass(name, uid, + flags, fields); + assignNewHandle(osc,true); + + for (int i = 0; i < field_count; i++) + { + if(dump) dumpElement(" TYPE CODE="); + char type_code = (char)this.realInputStream.readByte(); + if(dump) dumpElement(type_code + "; FIELD NAME="); + String field_name = this.realInputStream.readUTF(); + if(dump) dumpElementln(field_name); + String class_name; + + // If the type code is an array or an object we must + // decode a String here. In the other case we convert + // the type code and pass it to ObjectStreamField. + // Type codes are decoded by gnu.java.lang.reflect.TypeSignature. + if (type_code == 'L' || type_code == '[') + class_name = (String)readObject(); + else + class_name = String.valueOf(type_code); + + fields[i] = + new ObjectStreamField(field_name, class_name); + } + + /* Now that fields have been read we may resolve the class + * (and read annotation if needed). */ + Class clazz = resolveClass(osc); + ClassLoader loader = clazz.getClassLoader(); + for (int i = 0; i < field_count; i++) + { + fields[i].resolveType(loader); + } + boolean oldmode = setBlockDataMode(true); + osc.setClass(clazz, lookupClass(clazz.getSuperclass())); + classLookupTable.put(clazz, osc); + setBlockDataMode(oldmode); + + // find the first non-serializable class in clazz's inheritance hierarchy + Class first_nonserial = clazz.getSuperclass(); + // Maybe it is a primitive class, those don't have a super class, + // or Object itself. Otherwise we can keep getting the superclass + // till we hit the Object class, or some other non-serializable class. + + if (first_nonserial == null) + first_nonserial = clazz; + else + while (Serializable.class.isAssignableFrom(first_nonserial)) + first_nonserial = first_nonserial.getSuperclass(); + + final Class local_constructor_class = first_nonserial; + + osc.firstNonSerializableParentConstructor = + (Constructor)AccessController.doPrivileged(new PrivilegedAction() + { + public Object run() + { + try + { + Constructor c = local_constructor_class. + getDeclaredConstructor(new Class[0]); + if (Modifier.isPrivate(c.getModifiers())) + return null; + return c; + } + catch (NoSuchMethodException e) + { + // error will be reported later, in newObject() + return null; + } + } + }); + + osc.realClassIsSerializable = Serializable.class.isAssignableFrom(clazz); + osc.realClassIsExternalizable = Externalizable.class.isAssignableFrom(clazz); + + ObjectStreamField[] stream_fields = osc.fields; + ObjectStreamField[] real_fields = ObjectStreamClass.lookupForClassObject(clazz).fields; + ObjectStreamField[] fieldmapping = new ObjectStreamField[2 * Math.max(stream_fields.length, real_fields.length)]; + + int stream_idx = 0; + int real_idx = 0; + int map_idx = 0; + + /* + * Check that there is no type inconsistencies between the lists. + * A special checking must be done for the two groups: primitive types and + * not primitive types. + */ + checkTypeConsistency(name, real_fields, stream_fields); + checkTypeConsistency(name, stream_fields, real_fields); + + + while (stream_idx < stream_fields.length + || real_idx < real_fields.length) + { + ObjectStreamField stream_field = null; + ObjectStreamField real_field = null; + + if (stream_idx == stream_fields.length) + { + real_field = real_fields[real_idx++]; + } + else if (real_idx == real_fields.length) + { + stream_field = stream_fields[stream_idx++]; + } + else + { + int comp_val = + real_fields[real_idx].compareTo (stream_fields[stream_idx]); + + if (comp_val < 0) + { + real_field = real_fields[real_idx++]; + } + else if (comp_val > 0) + { + stream_field = stream_fields[stream_idx++]; + } + else + { + stream_field = stream_fields[stream_idx++]; + real_field = real_fields[real_idx++]; + if (stream_field.getType() != real_field.getType()) + throw new InvalidClassException + ("invalid field type for " + real_field.getName() + + " in class " + name); + } + } + + /* If some of stream_fields does not correspond to any of real_fields, + * or the opposite, then fieldmapping will go short. + */ + if (map_idx == fieldmapping.length) + { + ObjectStreamField[] newfieldmapping = + new ObjectStreamField[fieldmapping.length + 2]; + System.arraycopy(fieldmapping, 0, + newfieldmapping, 0, fieldmapping.length); + fieldmapping = newfieldmapping; + } + fieldmapping[map_idx++] = stream_field; + fieldmapping[map_idx++] = real_field; + } + osc.fieldMapping = fieldmapping; + + return osc; + } + + /** + * Reads the current objects non-transient, non-static fields from + * the current class from the underlying output stream. + * + * This method is intended to be called from within a object's + * private void readObject (ObjectInputStream) + * method. + * + * @exception ClassNotFoundException The class that an object being + * read in belongs to cannot be found. + * + * @exception NotActiveException This method was called from a + * context other than from the current object's and current class's + * private void readObject (ObjectInputStream) + * method. + * + * @exception IOException Exception from underlying + * OutputStream. + */ + public void defaultReadObject() + throws ClassNotFoundException, IOException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException("defaultReadObject called by non-active" + + " class and/or object"); + + if (fieldsAlreadyRead) + throw new NotActiveException("defaultReadObject called but fields " + + "already read from stream (by " + + "defaultReadObject or readFields)"); + + boolean oldmode = setBlockDataMode(false); + readFields(this.currentObject, this.currentObjectStreamClass); + setBlockDataMode(oldmode); + + fieldsAlreadyRead = true; + } + + + /** + * Registers a ObjectInputValidation to be carried out + * on the object graph currently being deserialized before it is + * returned to the original caller of readObject (). + * The order of validation for multiple + * ObjectInputValidations can be controled using + * priority. Validators with higher priorities are + * called first. + * + * @see java.io.ObjectInputValidation + * + * @exception InvalidObjectException validator is + * null + * + * @exception NotActiveException an attempt was made to add a + * validator outside of the readObject method of the + * object currently being deserialized + */ + public void registerValidation(ObjectInputValidation validator, + int priority) + throws InvalidObjectException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException("registerValidation called by non-active " + + "class and/or object"); + + if (validator == null) + throw new InvalidObjectException("attempt to add a null " + + "ObjectInputValidation object"); + + if (currentObjectValidators == null) + currentObjectValidators = new TreeSet(); + + currentObjectValidators.add(new ValidatorAndPriority(validator, priority)); + } + + + /** + * Called when a class is being deserialized. This is a hook to + * allow subclasses to read in information written by the + * annotateClass (Class) method of an + * ObjectOutputStream. + * + * This implementation looks up the active call stack for a + * ClassLoader; if a ClassLoader is found, + * it is used to load the class associated with osc, + * otherwise, the default system ClassLoader is used. + * + * @exception IOException Exception from underlying + * OutputStream. + * + * @see java.io.ObjectOutputStream#annotateClass (java.lang.Class) + */ + protected Class resolveClass(ObjectStreamClass osc) + throws ClassNotFoundException, IOException + { + String name = osc.getName(); + try + { + return Class.forName(name, true, currentLoader()); + } + catch(ClassNotFoundException x) + { + if (name.equals("void")) + return Void.TYPE; + else if (name.equals("boolean")) + return Boolean.TYPE; + else if (name.equals("byte")) + return Byte.TYPE; + else if (name.equals("char")) + return Character.TYPE; + else if (name.equals("short")) + return Short.TYPE; + else if (name.equals("int")) + return Integer.TYPE; + else if (name.equals("long")) + return Long.TYPE; + else if (name.equals("float")) + return Float.TYPE; + else if (name.equals("double")) + return Double.TYPE; + else + throw x; + } + } + + /** + * Returns the most recent user defined ClassLoader on the execution stack + * or null if none is found. + */ + private ClassLoader currentLoader() + { + return VMStackWalker.firstNonNullClassLoader(); + } + + /** + * Lookup a class stored in the local hashtable. If it is not + * use the global lookup function in ObjectStreamClass to build + * the ObjectStreamClass. This method is requested according to + * the behaviour detected in the JDK by Kaffe's team. + * + * @param clazz Class to lookup in the hash table or for which + * we must build a descriptor. + * @return A valid instance of ObjectStreamClass corresponding + * to the specified class. + */ + private ObjectStreamClass lookupClass(Class clazz) + { + if (clazz == null) + return null; + + ObjectStreamClass oclazz; + oclazz = classLookupTable.get(clazz); + if (oclazz == null) + return ObjectStreamClass.lookup(clazz); + else + return oclazz; + } + + /** + * Reconstruct class hierarchy the same way {@link + * java.io.ObjectStreamClass#hierarchy} does but using lookupClass + * instead of ObjectStreamClass.lookup. + * + * @param clazz This is the class for which we want the hierarchy. + * + * @return An array of valid {@link java.io.ObjectStreamClass} instances which + * represent the class hierarchy for clazz. + */ + private ObjectStreamClass[] hierarchy(Class clazz) + { + ObjectStreamClass osc = lookupClass(clazz); + + return osc == null ? new ObjectStreamClass[0] : osc.hierarchy(); + } + + /** + * Allows subclasses to resolve objects that are read from the + * stream with other objects to be returned in their place. This + * method is called the first time each object is encountered. + * + * This method must be enabled before it will be called in the + * serialization process. + * + * @exception IOException Exception from underlying + * OutputStream. + * + * @see #enableResolveObject(boolean) + */ + protected Object resolveObject(Object obj) throws IOException + { + return obj; + } + + + protected Class resolveProxyClass(String[] intfs) + throws IOException, ClassNotFoundException + { + ClassLoader cl = currentLoader(); + + Class[] clss = new Class[intfs.length]; + if(cl == null) + { + for (int i = 0; i < intfs.length; i++) + clss[i] = Class.forName(intfs[i]); + cl = ClassLoader.getSystemClassLoader(); + } + else + for (int i = 0; i < intfs.length; i++) + clss[i] = Class.forName(intfs[i], false, cl); + try + { + return Proxy.getProxyClass(cl, clss); + } + catch (IllegalArgumentException e) + { + throw new ClassNotFoundException(null, e); + } + } + + /** + * If enable is true and this object is + * trusted, then resolveObject (Object) will be called + * in subsequent calls to readObject (Object). + * Otherwise, resolveObject (Object) will not be called. + * + * @exception SecurityException This class is not trusted. + */ + protected boolean enableResolveObject (boolean enable) + throws SecurityException + { + if (enable) + { + SecurityManager sm = System.getSecurityManager(); + if (sm != null) + sm.checkPermission(new SerializablePermission("enableSubstitution")); + } + + boolean old_val = this.resolveEnabled; + this.resolveEnabled = enable; + return old_val; + } + + /** + * Reads stream magic and stream version information from the + * underlying stream. + * + * @exception IOException Exception from underlying stream. + * + * @exception StreamCorruptedException An invalid stream magic + * number or stream version was read from the stream. + */ + protected void readStreamHeader() + throws IOException, StreamCorruptedException + { + if(dump) dumpElement("STREAM MAGIC "); + if (this.realInputStream.readShort() != STREAM_MAGIC) + throw new StreamCorruptedException("Invalid stream magic number"); + + if(dump) dumpElementln("STREAM VERSION "); + if (this.realInputStream.readShort() != STREAM_VERSION) + throw new StreamCorruptedException("Invalid stream version number"); + } + + public int read() throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition >= this.blockDataBytes) + readNextBlock(); + return (this.blockData[this.blockDataPosition++] & 0xff); + } + else + return this.realInputStream.read(); + } + + public int read(byte[] data, int offset, int length) throws IOException + { + if (this.readDataFromBlock) + { + int remain = this.blockDataBytes - this.blockDataPosition; + if (remain == 0) + { + readNextBlock(); + remain = this.blockDataBytes - this.blockDataPosition; + } + length = Math.min(length, remain); + System.arraycopy(this.blockData, this.blockDataPosition, + data, offset, length); + this.blockDataPosition += length; + + return length; + } + else + return this.realInputStream.read(data, offset, length); + } + + public int available() throws IOException + { + if (this.readDataFromBlock) + { + if (this.blockDataPosition >= this.blockDataBytes) + readNextBlock (); + + return this.blockDataBytes - this.blockDataPosition; + } + else + return this.realInputStream.available(); + } + + public void close() throws IOException + { + this.realInputStream.close(); + } + + public boolean readBoolean() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode (true); + boolean value = this.dataInputStream.readBoolean (); + if (switchmode) + setBlockDataMode (oldmode); + return value; + } + + public byte readByte() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + byte value = this.dataInputStream.readByte(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public int readUnsignedByte() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + int value = this.dataInputStream.readUnsignedByte(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public short readShort() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + short value = this.dataInputStream.readShort(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public int readUnsignedShort() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + int value = this.dataInputStream.readUnsignedShort(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public char readChar() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + char value = this.dataInputStream.readChar(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public int readInt() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + int value = this.dataInputStream.readInt(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public long readLong() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + long value = this.dataInputStream.readLong(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public float readFloat() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + float value = this.dataInputStream.readFloat(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public double readDouble() throws IOException + { + boolean switchmode = true; + boolean oldmode = this.readDataFromBlock; + if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8) + switchmode = false; + if (switchmode) + oldmode = setBlockDataMode(true); + double value = this.dataInputStream.readDouble(); + if (switchmode) + setBlockDataMode(oldmode); + return value; + } + + public void readFully(byte data[]) throws IOException + { + this.dataInputStream.readFully(data); + } + + public void readFully(byte data[], int offset, int size) + throws IOException + { + this.dataInputStream.readFully(data, offset, size); + } + + public int skipBytes(int len) throws IOException + { + return this.dataInputStream.skipBytes(len); + } + + /** + * @deprecated + * @see java.io.DataInputStream#readLine () + */ + public String readLine() throws IOException + { + return this.dataInputStream.readLine(); + } + + public String readUTF() throws IOException + { + return this.dataInputStream.readUTF(); + } + + /** + * This class allows a class to specify exactly which fields should + * be read, and what values should be read for these fields. + * + * XXX: finish up comments + */ + public abstract static class GetField + { + public abstract ObjectStreamClass getObjectStreamClass(); + + public abstract boolean defaulted(String name) + throws IOException, IllegalArgumentException; + + public abstract boolean get(String name, boolean defvalue) + throws IOException, IllegalArgumentException; + + public abstract char get(String name, char defvalue) + throws IOException, IllegalArgumentException; + + public abstract byte get(String name, byte defvalue) + throws IOException, IllegalArgumentException; + + public abstract short get(String name, short defvalue) + throws IOException, IllegalArgumentException; + + public abstract int get(String name, int defvalue) + throws IOException, IllegalArgumentException; + + public abstract long get(String name, long defvalue) + throws IOException, IllegalArgumentException; + + public abstract float get(String name, float defvalue) + throws IOException, IllegalArgumentException; + + public abstract double get(String name, double defvalue) + throws IOException, IllegalArgumentException; + + public abstract Object get(String name, Object defvalue) + throws IOException, IllegalArgumentException; + } + + /** + * This method should be called by a method called 'readObject' in the + * deserializing class (if present). It cannot (and should not)be called + * outside of it. Its goal is to read all fields in the real input stream + * and keep them accessible through the {@link GetField} class. Calling + * this method will not alter the deserializing object. + * + * @return A valid freshly created 'GetField' instance to get access to + * the deserialized stream. + * @throws IOException An input/output exception occured. + * @throws ClassNotFoundException + * @throws NotActiveException + */ + public GetField readFields() + throws IOException, ClassNotFoundException, NotActiveException + { + if (this.currentObject == null || this.currentObjectStreamClass == null) + throw new NotActiveException("readFields called by non-active class and/or object"); + + if (prereadFields != null) + return prereadFields; + + if (fieldsAlreadyRead) + throw new NotActiveException("readFields called but fields already read from" + + " stream (by defaultReadObject or readFields)"); + + final ObjectStreamClass clazz = this.currentObjectStreamClass; + final byte[] prim_field_data = new byte[clazz.primFieldSize]; + final Object[] objs = new Object[clazz.objectFieldCount]; + + // Apparently Block data is not used with GetField as per + // empirical evidence against JDK 1.2. Also see Mauve test + // java.io.ObjectInputOutput.Test.GetPutField. + boolean oldmode = setBlockDataMode(false); + readFully(prim_field_data); + for (int i = 0; i < objs.length; ++ i) + objs[i] = readObject(); + setBlockDataMode(oldmode); + + prereadFields = new GetField() + { + public ObjectStreamClass getObjectStreamClass() + { + return clazz; + } + + public boolean defaulted(String name) + throws IOException, IllegalArgumentException + { + ObjectStreamField f = clazz.getField(name); + + /* First if we have a serialized field use the descriptor */ + if (f != null) + { + /* It is in serialPersistentFields but setClass tells us + * it should not be set. This value is defaulted. + */ + if (f.isPersistent() && !f.isToSet()) + return true; + + return false; + } + + /* This is not a serialized field. There should be + * a default value only if the field really exists. + */ + try + { + return (clazz.forClass().getDeclaredField (name) != null); + } + catch (NoSuchFieldException e) + { + throw new IllegalArgumentException(e); + } + } + + public boolean get(String name, boolean defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Boolean.TYPE); + + if (field == null) + return defvalue; + + return prim_field_data[field.getOffset()] == 0 ? false : true; + } + + public char get(String name, char defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Character.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return (char)(((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public byte get(String name, byte defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Byte.TYPE); + + if (field == null) + return defvalue; + + return prim_field_data[field.getOffset()]; + } + + public short get(String name, short defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Short.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return (short)(((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public int get(String name, int defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Integer.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF); + } + + public long get(String name, long defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Long.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return (long)(((prim_field_data[off++] & 0xFFL) << 56) + | ((prim_field_data[off++] & 0xFFL) << 48) + | ((prim_field_data[off++] & 0xFFL) << 40) + | ((prim_field_data[off++] & 0xFFL) << 32) + | ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public float get(String name, float defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Float.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return Float.intBitsToFloat(((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF)); + } + + public double get(String name, double defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = getField(name, Double.TYPE); + + if (field == null) + return defvalue; + + int off = field.getOffset(); + + return Double.longBitsToDouble + ( (long) (((prim_field_data[off++] & 0xFFL) << 56) + | ((prim_field_data[off++] & 0xFFL) << 48) + | ((prim_field_data[off++] & 0xFFL) << 40) + | ((prim_field_data[off++] & 0xFFL) << 32) + | ((prim_field_data[off++] & 0xFF) << 24) + | ((prim_field_data[off++] & 0xFF) << 16) + | ((prim_field_data[off++] & 0xFF) << 8) + | (prim_field_data[off] & 0xFF))); + } + + public Object get(String name, Object defvalue) + throws IOException, IllegalArgumentException + { + ObjectStreamField field = + getField(name, defvalue == null ? null : defvalue.getClass ()); + + if (field == null) + return defvalue; + + return objs[field.getOffset()]; + } + + private ObjectStreamField getField(String name, Class type) + throws IllegalArgumentException + { + ObjectStreamField field = clazz.getField(name); + boolean illegal = false; + + // XXX This code is horrible and needs to be rewritten! + try + { + try + { + Class field_type = field.getType(); + + if (type == field_type || + (type == null && !field_type.isPrimitive())) + { + /* See defaulted */ + return field; + } + + illegal = true; + throw new IllegalArgumentException + ("Field requested is of type " + + field_type.getName() + + ", but requested type was " + + (type == null ? "Object" : type.getName())); + } + catch (NullPointerException _) + { + /* Here we catch NullPointerException, because it may + only come from the call 'field.getType()'. If field + is null, we have to return null and classpath ethic + say we must try to avoid 'if (xxx == null)'. + */ + } + catch (IllegalArgumentException e) + { + throw e; + } + + return null; + } + finally + { + /* If this is an unassigned field we should return + * the default value. + */ + if (!illegal && field != null && !field.isToSet() && field.isPersistent()) + return null; + + /* We do not want to modify transient fields. They should + * be left to 0. + */ + try + { + Field f = clazz.forClass().getDeclaredField(name); + if (Modifier.isTransient(f.getModifiers())) + throw new IllegalArgumentException + ("no such field (non transient) " + name); + if (field == null && f.getType() != type) + throw new IllegalArgumentException + ("Invalid requested type for field " + name); + } + catch (NoSuchFieldException e) + { + if (field == null) + throw new IllegalArgumentException(e); + } + + } + } + }; + + fieldsAlreadyRead = true; + return prereadFields; + } + + /** + * Protected constructor that allows subclasses to override + * deserialization. This constructor should be called by subclasses + * that wish to override readObject (Object). This + * method does a security check NOTE: currently not + * implemented, then sets a flag that informs + * readObject (Object) to call the subclasses + * readObjectOverride (Object) method. + * + * @see #readObjectOverride() + */ + protected ObjectInputStream() + throws IOException, SecurityException + { + SecurityManager sec_man = System.getSecurityManager(); + if (sec_man != null) + sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION); + this.useSubclassMethod = true; + } + + /** + * This method allows subclasses to override the default + * de serialization mechanism provided by + * ObjectInputStream. To make this method be used for + * writing objects, subclasses must invoke the 0-argument + * constructor on this class from their constructor. + * + * @see #ObjectInputStream() + */ + protected Object readObjectOverride() + throws ClassNotFoundException, IOException, OptionalDataException + { + throw new IOException("Subclass of ObjectInputStream must implement readObjectOverride"); + } + + /** + * Assigns the next available handle to obj. + * + * @param obj The object for which we want a new handle. + * @param shared True if the handle should be shared + * with later calls. + * @return A valid handle for the specified object. + */ + private int assignNewHandle(Object obj, boolean shared) + { + int handle = this.nextOID; + this.nextOID = handle + 1; + rememberHandle(obj,shared,handle); + return handle; + } + + /** + * Remember the object associated with the given handle. + * + * @param obj an object + * @param shared true if the reference should be shared + * with later calls. + * @param handle a handle, must be >= baseWireHandle + * + * @see #lookupHandle + */ + private void rememberHandle(Object obj, boolean shared, + int handle) + { + handles.put(handle, new Pair(shared, obj)); + } + + /** + * Look up the object associated with a given handle. + * + * @param handle a handle, must be >= baseWireHandle + * @return the object remembered for handle or null if none. + * @throws StreamCorruptedException if the handle is invalid. + * @throws InvalidObjectException if the reference is not shared. + * @see #rememberHandle + */ + private Object lookupHandle(int handle) + throws ObjectStreamException + { + Pair result = handles.get(handle); + if (result == null) + throw new StreamCorruptedException("The handle, " + + Integer.toHexString(handle) + + ", is invalid."); + if (!result.getLeft()) + throw new InvalidObjectException("The handle, " + + Integer.toHexString(handle) + + ", is not shared."); + return result.getRight(); + } + + private Object processResolution(ObjectStreamClass osc, Object obj, int handle, + boolean shared) + throws IOException + { + if (osc != null && obj instanceof Serializable) + { + try + { + Method m = osc.readResolveMethod; + if(m != null) + { + obj = m.invoke(obj, new Object[] {}); + } + } + catch (IllegalAccessException ignore) + { + } + catch (InvocationTargetException exception) + { + Throwable cause = exception.getCause(); + if (cause instanceof ObjectStreamException) + throw (ObjectStreamException) cause; + else if (cause instanceof RuntimeException) + throw (RuntimeException) cause; + else if (cause instanceof Error) + throw (Error) cause; + } + } + + if (this.resolveEnabled) + obj = resolveObject(obj); + + rememberHandle(obj, shared, handle); + if (!shared) + { + if (obj instanceof byte[]) + return ((byte[]) obj).clone(); + if (obj instanceof short[]) + return ((short[]) obj).clone(); + if (obj instanceof int[]) + return ((int[]) obj).clone(); + if (obj instanceof long[]) + return ((long[]) obj).clone(); + if (obj instanceof char[]) + return ((char[]) obj).clone(); + if (obj instanceof boolean[]) + return ((boolean[]) obj).clone(); + if (obj instanceof float[]) + return ((float[]) obj).clone(); + if (obj instanceof double[]) + return ((double[]) obj).clone(); + if (obj instanceof Object[]) + return ((Object[]) obj).clone(); + } + return obj; + } + + private void clearHandles() + { + handles.clear(); + this.nextOID = baseWireHandle; + } + + private void readNextBlock() throws IOException + { + byte marker = this.realInputStream.readByte(); + while (marker == TC_RESET) + { + if(dump) dumpElementln("RESET"); + clearHandles(); + marker = this.realInputStream.readByte(); + } + readNextBlock(marker); + } + + private void readNextBlock(byte marker) throws IOException + { + if (marker == TC_BLOCKDATA) + { + if(dump) dumpElement("BLOCK DATA SIZE="); + this.blockDataBytes = this.realInputStream.readUnsignedByte(); + if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); + } + else if (marker == TC_BLOCKDATALONG) + { + if(dump) dumpElement("BLOCK DATA LONG SIZE="); + this.blockDataBytes = this.realInputStream.readInt(); + if(dump) dumpElementln (Integer.toString(this.blockDataBytes)); + } + else + { + throw new EOFException("Attempt to read primitive data, but no data block is active."); + } + + if (this.blockData.length < this.blockDataBytes) + this.blockData = new byte[this.blockDataBytes]; + + this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes); + this.blockDataPosition = 0; + } + + private void readArrayElements (Object array, Class clazz) + throws ClassNotFoundException, IOException + { + if (clazz.isPrimitive()) + { + if (clazz == Boolean.TYPE) + { + boolean[] cast_array = (boolean[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readBoolean(); + return; + } + if (clazz == Byte.TYPE) + { + byte[] cast_array = (byte[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readByte(); + return; + } + if (clazz == Character.TYPE) + { + char[] cast_array = (char[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readChar(); + return; + } + if (clazz == Double.TYPE) + { + double[] cast_array = (double[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readDouble(); + return; + } + if (clazz == Float.TYPE) + { + float[] cast_array = (float[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readFloat(); + return; + } + if (clazz == Integer.TYPE) + { + int[] cast_array = (int[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readInt(); + return; + } + if (clazz == Long.TYPE) + { + long[] cast_array = (long[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readLong(); + return; + } + if (clazz == Short.TYPE) + { + short[] cast_array = (short[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = this.realInputStream.readShort(); + return; + } + } + else + { + Object[] cast_array = (Object[])array; + for (int i=0; i < cast_array.length; i++) + cast_array[i] = readObject(); + } + } + + private void readFields (Object obj, ObjectStreamClass stream_osc) + throws ClassNotFoundException, IOException + { + ObjectStreamField[] fields = stream_osc.fieldMapping; + + for (int i = 0; i < fields.length; i += 2) + { + ObjectStreamField stream_field = fields[i]; + ObjectStreamField real_field = fields[i + 1]; + boolean read_value = (stream_field != null && stream_field.getOffset() >= 0 && stream_field.isToSet()); + boolean set_value = (real_field != null && real_field.isToSet()); + String field_name; + char type; + + if (stream_field != null) + { + field_name = stream_field.getName(); + type = stream_field.getTypeCode(); + } + else + { + field_name = real_field.getName(); + type = real_field.getTypeCode(); + } + + switch(type) + { + case 'Z': + { + boolean value = + read_value ? this.realInputStream.readBoolean() : false; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setBooleanField(obj, value); + break; + } + case 'B': + { + byte value = + read_value ? this.realInputStream.readByte() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setByteField(obj, value); + break; + } + case 'C': + { + char value = + read_value ? this.realInputStream.readChar(): 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setCharField(obj, value); + break; + } + case 'D': + { + double value = + read_value ? this.realInputStream.readDouble() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setDoubleField(obj, value); + break; + } + case 'F': + { + float value = + read_value ? this.realInputStream.readFloat() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setFloatField(obj, value); + break; + } + case 'I': + { + int value = + read_value ? this.realInputStream.readInt() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setIntField(obj, value); + break; + } + case 'J': + { + long value = + read_value ? this.realInputStream.readLong() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setLongField(obj, value); + break; + } + case 'S': + { + short value = + read_value ? this.realInputStream.readShort() : 0; + if (dump && read_value && set_value) + dumpElementln(" " + field_name + ": " + value); + if (set_value) + real_field.setShortField(obj, value); + break; + } + case 'L': + case '[': + { + Object value = + read_value ? readObject() : null; + if (set_value) + real_field.setObjectField(obj, value); + break; + } + default: + throw new InternalError("Invalid type code: " + type); + } + } + } + + // Toggles writing primitive data to block-data buffer. + private boolean setBlockDataMode (boolean on) + { + boolean oldmode = this.readDataFromBlock; + this.readDataFromBlock = on; + + if (on) + this.dataInputStream = this.blockDataInput; + else + this.dataInputStream = this.realInputStream; + return oldmode; + } + + // returns a new instance of REAL_CLASS that has been constructed + // only to the level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS) + private Object newObject (Class real_class, Constructor constructor) + throws ClassNotFoundException, IOException + { + if (constructor == null) + throw new InvalidClassException("Missing accessible no-arg base class constructor for " + real_class.getName()); + try + { + return VMObjectInputStream.allocateObject(real_class, constructor.getDeclaringClass(), constructor); + } + catch (InstantiationException e) + { + throw (ClassNotFoundException) new ClassNotFoundException + ("Instance of " + real_class + " could not be created").initCause(e); + } + } + + // runs all registered ObjectInputValidations in prioritized order + // on OBJ + private void invokeValidators() throws InvalidObjectException + { + try + { + Iterator it = currentObjectValidators.iterator(); + while(it.hasNext()) + { + ValidatorAndPriority vap = it.next(); + ObjectInputValidation validator = vap.validator; + validator.validateObject(); + } + } + finally + { + currentObjectValidators = null; + } + } + + private void callReadMethod (Method readObject, Class klass, Object obj) + throws ClassNotFoundException, IOException + { + try + { + readObject.invoke(obj, new Object[] { this }); + } + catch (InvocationTargetException x) + { + /* Rethrow if possible. */ + Throwable exception = x.getTargetException(); + if (exception instanceof RuntimeException) + throw (RuntimeException) exception; + if (exception instanceof IOException) + throw (IOException) exception; + if (exception instanceof ClassNotFoundException) + throw (ClassNotFoundException) exception; + + throw (IOException) new IOException( + "Exception thrown from readObject() on " + klass).initCause(x); + } + catch (Exception x) + { + throw (IOException) new IOException( + "Failure invoking readObject() on " + klass).initCause(x); + } + + // Invalidate fields which has been read through readFields. + prereadFields = null; + } + + private static final int BUFFER_SIZE = 1024; + + private DataInputStream realInputStream; + private DataInputStream dataInputStream; + private DataInputStream blockDataInput; + private int blockDataPosition; + private int blockDataBytes; + private byte[] blockData; + private boolean useSubclassMethod; + private int nextOID; + private boolean resolveEnabled; + private Map> handles; + private Object currentObject; + private ObjectStreamClass currentObjectStreamClass; + private TreeSet currentObjectValidators; + private boolean readDataFromBlock; + private boolean fieldsAlreadyRead; + private Hashtable classLookupTable; + private GetField prereadFields; + + private static boolean dump; + + // The nesting depth for debugging output + private int depth = 0; + + private static final boolean DEBUG = false; + + private void dumpElement (String msg) + { + System.out.print(msg); + } + + private void dumpElementln (String msg) + { + System.out.println(msg); + for (int i = 0; i < depth; i++) + System.out.print (" "); + System.out.print (Thread.currentThread() + ": "); + } + + private void dumpElementln (String msg, Object obj) + { + try + { + System.out.print(msg); + if (java.lang.reflect.Proxy.isProxyClass(obj.getClass())) + System.out.println(obj.getClass()); + else + System.out.println(obj); + } + catch (Exception _) + { + } + for (int i = 0; i < depth; i++) + System.out.print (" "); + System.out.print (Thread.currentThread() + ": "); + } + + // used to keep a prioritized list of object validators + private static final class ValidatorAndPriority implements Comparable + { + int priority; + ObjectInputValidation validator; + + ValidatorAndPriority (ObjectInputValidation validator, int priority) + { + this.priority = priority; + this.validator = validator; + } + + public int compareTo (Object o) + { + ValidatorAndPriority vap = (ValidatorAndPriority)o; + return this.priority - vap.priority; + } + } +} -- cgit v1.2.3