001    /* ObjectInputStream.java -- Class used to read serialized objects
002       Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2008
003       Free Software Foundation, Inc.
004    
005    This file is part of GNU Classpath.
006    
007    GNU Classpath is free software; you can redistribute it and/or modify
008    it under the terms of the GNU General Public License as published by
009    the Free Software Foundation; either version 2, or (at your option)
010    any later version.
011    
012    GNU Classpath is distributed in the hope that it will be useful, but
013    WITHOUT ANY WARRANTY; without even the implied warranty of
014    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
015    General Public License for more details.
016    
017    You should have received a copy of the GNU General Public License
018    along with GNU Classpath; see the file COPYING.  If not, write to the
019    Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
020    02110-1301 USA.
021    
022    Linking this library statically or dynamically with other modules is
023    making a combined work based on this library.  Thus, the terms and
024    conditions of the GNU General Public License cover the whole
025    combination.
026    
027    As a special exception, the copyright holders of this library give you
028    permission to link this library with independent modules to produce an
029    executable, regardless of the license terms of these independent
030    modules, and to copy and distribute the resulting executable under
031    terms of your choice, provided that you also meet, for each linked
032    independent module, the terms and conditions of the license of that
033    module.  An independent module is a module which is not derived from
034    or based on this library.  If you modify this library, you may extend
035    this exception to your version of the library, but you are not
036    obligated to do so.  If you do not wish to do so, delete this
037    exception statement from your version. */
038    
039    
040    package java.io;
041    
042    import gnu.classpath.Pair;
043    import gnu.classpath.VMStackWalker;
044    
045    import java.lang.reflect.Array;
046    import java.lang.reflect.Constructor;
047    import java.lang.reflect.Field;
048    import java.lang.reflect.InvocationTargetException;
049    import java.lang.reflect.Method;
050    import java.lang.reflect.Modifier;
051    import java.lang.reflect.Proxy;
052    import java.security.AccessController;
053    import java.security.PrivilegedAction;
054    import java.util.HashMap;
055    import java.util.Hashtable;
056    import java.util.Iterator;
057    import java.util.Map;
058    import java.util.TreeSet;
059    
060    /**
061     * @author Tom Tromey (tromey@redhat.com)
062     * @author Jeroen Frijters (jeroen@frijters.net)
063     * @author Guilhem Lavaux (guilhem@kaffe.org)
064     * @author Michael Koch (konqueror@gmx.de)
065     * @author Andrew John Hughes (gnu_andrew@member.fsf.org)
066     */
067    public class ObjectInputStream extends InputStream
068      implements ObjectInput, ObjectStreamConstants
069    {
070      /**
071       * Creates a new <code>ObjectInputStream</code> that will do all of
072       * its reading from <code>in</code>.  This method also checks
073       * the stream by reading the header information (stream magic number
074       * and stream version).
075       *
076       * @exception IOException Reading stream header from underlying
077       * stream cannot be completed.
078       *
079       * @exception StreamCorruptedException An invalid stream magic
080       * number or stream version was read from the stream.
081       *
082       * @see #readStreamHeader()
083       */
084      public ObjectInputStream(InputStream in)
085        throws IOException, StreamCorruptedException
086      {
087        if (DEBUG)
088          {
089            String val = System.getProperty("gcj.dumpobjects");
090            if (dump == false && val != null && !val.equals(""))
091              {
092                dump = true;
093                System.out.println ("Serialization debugging enabled");
094              }
095            else if (dump == true && (val == null || val.equals("")))
096              {
097                dump = false;
098                System.out.println ("Serialization debugging disabled");
099              }
100          }
101    
102        this.resolveEnabled = false;
103        this.blockDataPosition = 0;
104        this.blockDataBytes = 0;
105        this.blockData = new byte[BUFFER_SIZE];
106        this.blockDataInput = new DataInputStream(this);
107        this.realInputStream = new DataInputStream(in);
108        this.nextOID = baseWireHandle;
109        handles = new HashMap<Integer,Pair<Boolean,Object>>();
110        this.classLookupTable = new Hashtable<Class,ObjectStreamClass>();
111        setBlockDataMode(true);
112        readStreamHeader();
113      }
114    
115    
116      /**
117       * Returns the next deserialized object read from the underlying stream.
118       *
119       * This method can be overriden by a class by implementing
120       * <code>private void readObject (ObjectInputStream)</code>.
121       *
122       * If an exception is thrown from this method, the stream is left in
123       * an undefined state. This method can also throw Errors and
124       * RuntimeExceptions if caused by existing readResolve() user code.
125       *
126       * @return The object read from the underlying stream.
127       *
128       * @exception ClassNotFoundException The class that an object being
129       * read in belongs to cannot be found.
130       *
131       * @exception IOException Exception from underlying
132       * <code>InputStream</code>.
133       */
134      public final Object readObject()
135        throws ClassNotFoundException, IOException
136      {
137        return readObject(true);
138      }
139    
140      /**
141       * <p>
142       * Returns the next deserialized object read from the
143       * underlying stream in an unshared manner.  Any object
144       * returned by this method will not be returned by
145       * subsequent calls to either this method or {@link #readObject()}.
146       * </p>
147       * <p>
148       * This behaviour is achieved by:
149       * </p>
150       * <ul>
151       * <li>Marking the handles created by successful calls to this
152       * method, so that future calls to {@link #readObject()} or
153       * {@link #readUnshared()} will throw an {@link ObjectStreamException}
154       * rather than returning the same object reference.</li>
155       * <li>Throwing an {@link ObjectStreamException} if the next
156       * element in the stream is a reference to an earlier object.</li>
157       * </ul>
158       *
159       * @return a reference to the deserialized object.
160       * @throws ClassNotFoundException if the class of the object being
161       *                                deserialized can not be found.
162       * @throws StreamCorruptedException if information in the stream
163       *                                  is inconsistent.
164       * @throws ObjectStreamException if the next object has already been
165       *                               returned by an earlier call to this
166       *                               method or {@link #readObject()}.
167       * @throws OptionalDataException if primitive data occurs next in the stream.
168       * @throws IOException if an I/O error occurs from the stream.
169       * @since 1.4
170       * @see #readObject()
171       */
172      public Object readUnshared()
173        throws IOException, ClassNotFoundException
174      {
175        return readObject(false);
176      }
177    
178      /**
179       * Returns the next deserialized object read from the underlying stream.
180       *
181       * This method can be overriden by a class by implementing
182       * <code>private void readObject (ObjectInputStream)</code>.
183       *
184       * If an exception is thrown from this method, the stream is left in
185       * an undefined state. This method can also throw Errors and
186       * RuntimeExceptions if caused by existing readResolve() user code.
187       *
188       * @param shared true if handles created by this call should be shared
189       *               with later calls.
190       * @return The object read from the underlying stream.
191       *
192       * @exception ClassNotFoundException The class that an object being
193       * read in belongs to cannot be found.
194       *
195       * @exception IOException Exception from underlying
196       * <code>InputStream</code>.
197       */
198      private final Object readObject(boolean shared)
199        throws ClassNotFoundException, IOException
200      {
201        if (this.useSubclassMethod)
202          return readObjectOverride();
203    
204        Object ret_val;
205        boolean old_mode = setBlockDataMode(false);
206        byte marker = this.realInputStream.readByte();
207    
208        if (DEBUG)
209          depth += 2;
210    
211        if(dump) dumpElement("MARKER: 0x" + Integer.toHexString(marker) + " ");
212    
213        try
214          {
215            ret_val = parseContent(marker, shared);
216          }
217        finally
218          {
219            setBlockDataMode(old_mode);
220            if (DEBUG)
221              depth -= 2;
222          }
223    
224        return ret_val;
225      }
226    
227       /**
228        * Handles a content block within the stream, which begins with a marker
229        * byte indicating its type.
230        *
231        * @param marker the byte marker.
232        * @param shared true if handles created by this call should be shared
233        *               with later calls.
234        * @return an object which represents the parsed content.
235        * @throws ClassNotFoundException if the class of an object being
236        *                                read in cannot be found.
237        * @throws IOException if invalid data occurs or one is thrown by the
238        *                     underlying <code>InputStream</code>.
239        */
240       private Object parseContent(byte marker, boolean shared)
241         throws ClassNotFoundException, IOException
242       {
243         Object ret_val;
244         boolean is_consumed = false;
245    
246         switch (marker)
247           {
248           case TC_ENDBLOCKDATA:
249            {
250              ret_val = null;
251              is_consumed = true;
252              break;
253            }
254    
255           case TC_BLOCKDATA:
256           case TC_BLOCKDATALONG:
257            {
258              if (marker == TC_BLOCKDATALONG)
259                { if(dump) dumpElementln("BLOCKDATALONG"); }
260              else
261                { if(dump) dumpElementln("BLOCKDATA"); }
262              readNextBlock(marker);
263            }
264    
265           case TC_NULL:
266            {
267              if(dump) dumpElementln("NULL");
268              ret_val = null;
269              break;
270            }
271    
272           case TC_REFERENCE:
273            {
274              if(dump) dumpElement("REFERENCE ");
275              int oid = realInputStream.readInt();
276              if(dump) dumpElementln(Integer.toHexString(oid));
277              ret_val = lookupHandle(oid);
278              if (!shared)
279                throw new
280                  InvalidObjectException("References can not be read unshared.");
281              break;
282            }
283    
284           case TC_CLASS:
285            {
286              if(dump) dumpElementln("CLASS");
287              ObjectStreamClass osc = (ObjectStreamClass)readObject();
288              Class clazz = osc.forClass();
289              assignNewHandle(clazz,shared);
290              ret_val = clazz;
291              break;
292            }
293    
294           case TC_PROXYCLASSDESC:
295            {
296              if(dump) dumpElementln("PROXYCLASS");
297    
298    /* GCJ LOCAL */
299              // The grammar at this point is
300              //   TC_PROXYCLASSDESC newHandle proxyClassDescInfo
301              // i.e. we have to assign the handle immediately after
302              // reading the marker.
303              int handle = assignNewHandle("Dummy proxy",shared);
304    /* END GCJ LOCAL */
305    
306              int n_intf = this.realInputStream.readInt();
307              String[] intfs = new String[n_intf];
308              for (int i = 0; i < n_intf; i++)
309                {
310                  intfs[i] = this.realInputStream.readUTF();
311                }
312    
313              boolean oldmode = setBlockDataMode(true);
314              Class cl = resolveProxyClass(intfs);
315              setBlockDataMode(oldmode);
316    
317              ObjectStreamClass osc = lookupClass(cl);
318              if (osc.firstNonSerializableParentConstructor == null)
319                {
320                  osc.realClassIsSerializable = true;
321                  osc.fields = osc.fieldMapping = new ObjectStreamField[0];
322                  try
323                    {
324                      osc.firstNonSerializableParentConstructor =
325                        Object.class.getConstructor(new Class[0]);
326                    }
327                  catch (NoSuchMethodException x)
328                    {
329                      throw (InternalError)
330                        new InternalError("Object ctor missing").initCause(x);
331                    }
332                }
333    /* GCJ LOCAL */
334              rememberHandle(osc,shared,handle);
335    /* END GCJ LOCAL */
336    
337              if (!is_consumed)
338                {
339                  byte b = this.realInputStream.readByte();
340                  if (b != TC_ENDBLOCKDATA)
341                    throw new IOException("Data annotated to class was not consumed." + b);
342                }
343              else
344                is_consumed = false;
345              ObjectStreamClass superosc = (ObjectStreamClass)readObject();
346              osc.setSuperclass(superosc);
347              ret_val = osc;
348              break;
349            }
350    
351           case TC_CLASSDESC:
352            {
353              ObjectStreamClass osc = readClassDescriptor();
354    
355              if (!is_consumed)
356                {
357                  byte b = this.realInputStream.readByte();
358                  if (b != TC_ENDBLOCKDATA)
359                    throw new IOException("Data annotated to class was not consumed." + b);
360                }
361              else
362                is_consumed = false;
363    
364              osc.setSuperclass ((ObjectStreamClass)readObject());
365              ret_val = osc;
366              break;
367            }
368    
369           case TC_STRING:
370            {
371              if(dump) dumpElement("STRING=");
372              String s = this.realInputStream.readUTF();
373              if(dump) dumpElementln(s);
374              ret_val = processResolution(null, s, assignNewHandle(s,shared),
375                                          shared);
376              break;
377            }
378    
379           case TC_LONGSTRING:
380            {
381              if(dump) dumpElement("STRING=");
382              String s = this.realInputStream.readUTFLong();
383              if(dump) dumpElementln(s);
384              ret_val = processResolution(null, s, assignNewHandle(s,shared),
385                                          shared);
386              break;
387            }
388    
389           case TC_ARRAY:
390            {
391              if(dump) dumpElementln("ARRAY");
392              ObjectStreamClass osc = (ObjectStreamClass)readObject();
393              Class componentType = osc.forClass().getComponentType();
394              if(dump) dumpElement("ARRAY LENGTH=");
395              int length = this.realInputStream.readInt();
396              if(dump) dumpElementln (length + "; COMPONENT TYPE=" + componentType);
397              Object array = Array.newInstance(componentType, length);
398              int handle = assignNewHandle(array,shared);
399              readArrayElements(array, componentType);
400              if(dump)
401                for (int i = 0, len = Array.getLength(array); i < len; i++)
402                  dumpElementln("  ELEMENT[" + i + "]=", Array.get(array, i));
403              ret_val = processResolution(null, array, handle, shared);
404              break;
405            }
406    
407           case TC_OBJECT:
408            {
409              if(dump) dumpElementln("OBJECT");
410              ObjectStreamClass osc = (ObjectStreamClass)readObject();
411              Class clazz = osc.forClass();
412    
413              if (!osc.realClassIsSerializable)
414                throw new NotSerializableException
415                  (clazz + " is not Serializable, and thus cannot be deserialized.");
416    
417              if (osc.realClassIsExternalizable)
418                {
419                  Externalizable obj = osc.newInstance();
420    
421                  int handle = assignNewHandle(obj,shared);
422    
423                  boolean read_from_blocks = ((osc.getFlags() & SC_BLOCK_DATA) != 0);
424    
425                  boolean oldmode = this.readDataFromBlock;
426                  if (read_from_blocks)
427                    setBlockDataMode(true);
428    
429                  obj.readExternal(this);
430    
431                  if (read_from_blocks)
432                    {
433                      setBlockDataMode(oldmode);
434                      if (!oldmode)
435                        if (this.realInputStream.readByte() != TC_ENDBLOCKDATA)
436                          throw new IOException("No end of block data seen for class with readExternal (ObjectInputStream) method.");
437                    }
438    
439                  ret_val = processResolution(osc, obj, handle,shared);
440                  break;
441    
442                } // end if (osc.realClassIsExternalizable)
443    
444              Object obj = newObject(clazz, osc.firstNonSerializableParentConstructor);
445    
446              int handle = assignNewHandle(obj,shared);
447              Object prevObject = this.currentObject;
448              ObjectStreamClass prevObjectStreamClass = this.currentObjectStreamClass;
449              TreeSet<ValidatorAndPriority> prevObjectValidators =
450                this.currentObjectValidators;
451    
452              this.currentObject = obj;
453              this.currentObjectValidators = null;
454              ObjectStreamClass[] hierarchy = hierarchy(clazz);
455    
456              for (int i = 0; i < hierarchy.length; i++)
457              {
458                  this.currentObjectStreamClass = hierarchy[i];
459                  if(dump) dumpElementln("Reading fields of " + this.currentObjectStreamClass.getName ());
460    
461                  // XXX: should initialize fields in classes in the hierarchy
462                  // that aren't in the stream
463                  // should skip over classes in the stream that aren't in the
464                  // real classes hierarchy
465    
466                  Method readObjectMethod = this.currentObjectStreamClass.readObjectMethod;
467                  if (readObjectMethod != null)
468                    {
469                      fieldsAlreadyRead = false;
470                      boolean oldmode = setBlockDataMode(true);
471                      callReadMethod(readObjectMethod, this.currentObjectStreamClass.forClass(), obj);
472                      setBlockDataMode(oldmode);
473                    }
474                  else
475                    {
476                      readFields(obj, currentObjectStreamClass);
477                    }
478    
479                  if (this.currentObjectStreamClass.hasWriteMethod())
480                    {
481                      if(dump) dumpElement("ENDBLOCKDATA? ");
482                      try
483                        {
484                          /* Read blocks until an end marker */
485                          byte writeMarker = this.realInputStream.readByte();
486                          while (writeMarker != TC_ENDBLOCKDATA)
487                            {
488                              parseContent(writeMarker, shared);
489                              writeMarker = this.realInputStream.readByte();
490                            }
491                          if(dump) dumpElementln("yes");
492                        }
493                      catch (EOFException e)
494                        {
495                          throw (IOException) new IOException
496                            ("No end of block data seen for class with readObject (ObjectInputStream) method.").initCause(e);
497                        }
498                    }
499                }
500    
501              this.currentObject = prevObject;
502              this.currentObjectStreamClass = prevObjectStreamClass;
503              ret_val = processResolution(osc, obj, handle, shared);
504              if (currentObjectValidators != null)
505                invokeValidators();
506              this.currentObjectValidators = prevObjectValidators;
507    
508              break;
509            }
510    
511           case TC_RESET:
512            if(dump) dumpElementln("RESET");
513            clearHandles();
514            ret_val = readObject();
515            break;
516    
517           case TC_EXCEPTION:
518            {
519              if(dump) dumpElement("EXCEPTION=");
520              Exception e = (Exception)readObject();
521              if(dump) dumpElementln(e.toString());
522              clearHandles();
523              throw new WriteAbortedException("Exception thrown during writing of stream", e);
524            }
525    
526           case TC_ENUM:
527             {
528               /* TC_ENUM classDesc newHandle enumConstantName */
529               if (dump)
530                 dumpElementln("ENUM=");
531               ObjectStreamClass osc = (ObjectStreamClass) readObject();
532               String constantName = (String) readObject();
533               if (dump)
534                 dumpElementln("CONSTANT NAME = " + constantName);
535               Class clazz = osc.forClass();
536               Enum instance = Enum.valueOf(clazz, constantName);
537               assignNewHandle(instance,shared);
538               ret_val = instance;
539               break;
540             }
541    
542           default:
543            throw new IOException("Unknown marker on stream: " + marker);
544          }
545        return ret_val;
546      }
547    
548      /**
549       * This method makes a partial check of types for the fields
550       * contained given in arguments. It checks primitive types of
551       * fields1 against non primitive types of fields2. This method
552       * assumes the two lists has already been sorted according to
553       * the Java specification.
554       *
555       * @param name Name of the class owning the given fields.
556       * @param fields1 First list to check.
557       * @param fields2 Second list to check.
558       * @throws InvalidClassException if a field in fields1, which has a primitive type, is a present
559       * in the non primitive part in fields2.
560       */
561      private void checkTypeConsistency(String name, ObjectStreamField[] fields1, ObjectStreamField[] fields2)
562        throws InvalidClassException
563      {
564        int nonPrimitive = 0;
565    
566        for (nonPrimitive = 0;
567             nonPrimitive < fields1.length
568               && fields1[nonPrimitive].isPrimitive(); nonPrimitive++)
569          {
570          }
571    
572        if (nonPrimitive == fields1.length)
573          return;
574    
575        int i = 0;
576        ObjectStreamField f1;
577        ObjectStreamField f2;
578    
579        while (i < fields2.length
580               && nonPrimitive < fields1.length)
581          {
582            f1 = fields1[nonPrimitive];
583            f2 = fields2[i];
584    
585            if (!f2.isPrimitive())
586              break;
587    
588            int compVal = f1.getName().compareTo (f2.getName());
589    
590            if (compVal < 0)
591              {
592                nonPrimitive++;
593              }
594            else if (compVal > 0)
595              {
596                i++;
597              }
598            else
599              {
600                throw new InvalidClassException
601                  ("invalid field type for " + f2.getName() +
602                   " in class " + name);
603              }
604          }
605      }
606    
607      /**
608       * This method reads a class descriptor from the real input stream
609       * and use these data to create a new instance of ObjectStreamClass.
610       * Fields are sorted and ordered for the real read which occurs for
611       * each instance of the described class. Be aware that if you call that
612       * method you must ensure that the stream is synchronized, in the other
613       * case it may be completely desynchronized.
614       *
615       * @return A new instance of ObjectStreamClass containing the freshly
616       * created descriptor.
617       * @throws ClassNotFoundException if the required class to build the
618       * descriptor has not been found in the system.
619       * @throws IOException An input/output error occured.
620       * @throws InvalidClassException If there was a compatibility problem
621       * between the class present in the system and the serialized class.
622       */
623      protected ObjectStreamClass readClassDescriptor()
624        throws ClassNotFoundException, IOException
625      {
626        if(dump) dumpElement("CLASSDESC NAME=");
627        String name = this.realInputStream.readUTF();
628        if(dump) dumpElement(name + "; UID=");
629        long uid = this.realInputStream.readLong ();
630        if(dump) dumpElement(Long.toHexString(uid) + "; FLAGS=");
631        byte flags = this.realInputStream.readByte ();
632        if(dump) dumpElement(Integer.toHexString(flags) + "; FIELD COUNT=");
633        short field_count = this.realInputStream.readShort();
634        if(dump) dumpElementln(Short.toString(field_count));
635        ObjectStreamField[] fields = new ObjectStreamField[field_count];
636        ObjectStreamClass osc = new ObjectStreamClass(name, uid,
637                                                      flags, fields);
638        assignNewHandle(osc,true);
639    
640        for (int i = 0; i < field_count; i++)
641          {
642            if(dump) dumpElement("  TYPE CODE=");
643            char type_code = (char)this.realInputStream.readByte();
644            if(dump) dumpElement(type_code + "; FIELD NAME=");
645            String field_name = this.realInputStream.readUTF();
646            if(dump) dumpElementln(field_name);
647            String class_name;
648    
649            // If the type code is an array or an object we must
650            // decode a String here. In the other case we convert
651            // the type code and pass it to ObjectStreamField.
652            // Type codes are decoded by gnu.java.lang.reflect.TypeSignature.
653            if (type_code == 'L' || type_code == '[')
654              class_name = (String)readObject();
655            else
656              class_name = String.valueOf(type_code);
657    
658            fields[i] =
659              new ObjectStreamField(field_name, class_name);
660          }
661    
662        /* Now that fields have been read we may resolve the class
663         * (and read annotation if needed). */
664        Class clazz = resolveClass(osc);
665        ClassLoader loader = clazz.getClassLoader();
666        for (int i = 0; i < field_count; i++)
667          {
668            fields[i].resolveType(loader);
669          }
670        boolean oldmode = setBlockDataMode(true);
671        osc.setClass(clazz, lookupClass(clazz.getSuperclass()));
672        classLookupTable.put(clazz, osc);
673        setBlockDataMode(oldmode);
674    
675        // find the first non-serializable class in clazz's inheritance hierarchy
676        Class first_nonserial = clazz.getSuperclass();
677        // Maybe it is a primitive class, those don't have a super class,
678        // or Object itself.  Otherwise we can keep getting the superclass
679        // till we hit the Object class, or some other non-serializable class.
680    
681        if (first_nonserial == null)
682          first_nonserial = clazz;
683        else
684          while (Serializable.class.isAssignableFrom(first_nonserial))
685            first_nonserial = first_nonserial.getSuperclass();
686    
687        final Class local_constructor_class = first_nonserial;
688    
689        osc.firstNonSerializableParentConstructor =
690            (Constructor)AccessController.doPrivileged(new PrivilegedAction()
691              {
692                public Object run()
693                {
694                  try
695                    {
696                      Constructor c = local_constructor_class.
697                                        getDeclaredConstructor(new Class[0]);
698                      if (Modifier.isPrivate(c.getModifiers()))
699                        return null;
700                      return c;
701                    }
702                  catch (NoSuchMethodException e)
703                    {
704                      // error will be reported later, in newObject()
705                      return null;
706                    }
707                }
708              });
709    
710        osc.realClassIsSerializable = Serializable.class.isAssignableFrom(clazz);
711        osc.realClassIsExternalizable = Externalizable.class.isAssignableFrom(clazz);
712    
713        ObjectStreamField[] stream_fields = osc.fields;
714        ObjectStreamField[] real_fields = ObjectStreamClass.lookupForClassObject(clazz).fields;
715        ObjectStreamField[] fieldmapping = new ObjectStreamField[2 * Math.max(stream_fields.length, real_fields.length)];
716    
717        int stream_idx = 0;
718        int real_idx = 0;
719        int map_idx = 0;
720    
721        /*
722         * Check that there is no type inconsistencies between the lists.
723         * A special checking must be done for the two groups: primitive types and
724         * not primitive types.
725         */
726        checkTypeConsistency(name, real_fields, stream_fields);
727        checkTypeConsistency(name, stream_fields, real_fields);
728    
729    
730        while (stream_idx < stream_fields.length
731               || real_idx < real_fields.length)
732          {
733            ObjectStreamField stream_field = null;
734            ObjectStreamField real_field = null;
735    
736            if (stream_idx == stream_fields.length)
737              {
738                real_field = real_fields[real_idx++];
739              }
740            else if (real_idx == real_fields.length)
741              {
742                stream_field = stream_fields[stream_idx++];
743              }
744            else
745              {
746                int comp_val =
747                  real_fields[real_idx].compareTo (stream_fields[stream_idx]);
748    
749                if (comp_val < 0)
750                  {
751                    real_field = real_fields[real_idx++];
752                  }
753                else if (comp_val > 0)
754                  {
755                    stream_field = stream_fields[stream_idx++];
756                  }
757                else
758                  {
759                    stream_field = stream_fields[stream_idx++];
760                    real_field = real_fields[real_idx++];
761                    if (stream_field.getType() != real_field.getType())
762                      throw new InvalidClassException
763                        ("invalid field type for " + real_field.getName() +
764                         " in class " + name);
765                  }
766              }
767    
768            /* If some of stream_fields does not correspond to any of real_fields,
769             * or the opposite, then fieldmapping will go short.
770             */
771            if (map_idx == fieldmapping.length)
772              {
773                ObjectStreamField[] newfieldmapping =
774                  new ObjectStreamField[fieldmapping.length + 2];
775                System.arraycopy(fieldmapping, 0,
776                                 newfieldmapping, 0, fieldmapping.length);
777                fieldmapping = newfieldmapping;
778              }
779            fieldmapping[map_idx++] = stream_field;
780            fieldmapping[map_idx++] = real_field;
781          }
782        osc.fieldMapping = fieldmapping;
783    
784        return osc;
785      }
786    
787      /**
788       * Reads the current objects non-transient, non-static fields from
789       * the current class from the underlying output stream.
790       *
791       * This method is intended to be called from within a object's
792       * <code>private void readObject (ObjectInputStream)</code>
793       * method.
794       *
795       * @exception ClassNotFoundException The class that an object being
796       * read in belongs to cannot be found.
797       *
798       * @exception NotActiveException This method was called from a
799       * context other than from the current object's and current class's
800       * <code>private void readObject (ObjectInputStream)</code>
801       * method.
802       *
803       * @exception IOException Exception from underlying
804       * <code>OutputStream</code>.
805       */
806      public void defaultReadObject()
807        throws ClassNotFoundException, IOException, NotActiveException
808      {
809        if (this.currentObject == null || this.currentObjectStreamClass == null)
810          throw new NotActiveException("defaultReadObject called by non-active"
811                                       + " class and/or object");
812    
813        if (fieldsAlreadyRead)
814          throw new NotActiveException("defaultReadObject called but fields "
815                                       + "already read from stream (by "
816                                       + "defaultReadObject or readFields)");
817    
818        boolean oldmode = setBlockDataMode(false);
819        readFields(this.currentObject, this.currentObjectStreamClass);
820        setBlockDataMode(oldmode);
821    
822        fieldsAlreadyRead = true;
823      }
824    
825    
826      /**
827       * Registers a <code>ObjectInputValidation</code> to be carried out
828       * on the object graph currently being deserialized before it is
829       * returned to the original caller of <code>readObject ()</code>.
830       * The order of validation for multiple
831       * <code>ObjectInputValidation</code>s can be controled using
832       * <code>priority</code>.  Validators with higher priorities are
833       * called first.
834       *
835       * @see java.io.ObjectInputValidation
836       *
837       * @exception InvalidObjectException <code>validator</code> is
838       * <code>null</code>
839       *
840       * @exception NotActiveException an attempt was made to add a
841       * validator outside of the <code>readObject</code> method of the
842       * object currently being deserialized
843       */
844      public void registerValidation(ObjectInputValidation validator,
845                                     int priority)
846        throws InvalidObjectException, NotActiveException
847      {
848        if (this.currentObject == null || this.currentObjectStreamClass == null)
849          throw new NotActiveException("registerValidation called by non-active "
850                                       + "class and/or object");
851    
852        if (validator == null)
853          throw new InvalidObjectException("attempt to add a null "
854                                           + "ObjectInputValidation object");
855    
856        if (currentObjectValidators == null)
857          currentObjectValidators = new TreeSet<ValidatorAndPriority>();
858    
859        currentObjectValidators.add(new ValidatorAndPriority(validator, priority));
860      }
861    
862    
863      /**
864       * Called when a class is being deserialized.  This is a hook to
865       * allow subclasses to read in information written by the
866       * <code>annotateClass (Class)</code> method of an
867       * <code>ObjectOutputStream</code>.
868       *
869       * This implementation looks up the active call stack for a
870       * <code>ClassLoader</code>; if a <code>ClassLoader</code> is found,
871       * it is used to load the class associated with <code>osc</code>,
872       * otherwise, the default system <code>ClassLoader</code> is used.
873       *
874       * @exception IOException Exception from underlying
875       * <code>OutputStream</code>.
876       *
877       * @see java.io.ObjectOutputStream#annotateClass (java.lang.Class)
878       */
879      protected Class<?> resolveClass(ObjectStreamClass osc)
880        throws ClassNotFoundException, IOException
881      {
882        String name = osc.getName();
883        try
884          {
885            return Class.forName(name, true, currentLoader());
886          }
887        catch(ClassNotFoundException x)
888          {
889            if (name.equals("void"))
890              return Void.TYPE;
891            else if (name.equals("boolean"))
892              return Boolean.TYPE;
893            else if (name.equals("byte"))
894              return Byte.TYPE;
895            else if (name.equals("char"))
896              return Character.TYPE;
897            else if (name.equals("short"))
898              return Short.TYPE;
899            else if (name.equals("int"))
900              return Integer.TYPE;
901            else if (name.equals("long"))
902              return Long.TYPE;
903            else if (name.equals("float"))
904              return Float.TYPE;
905            else if (name.equals("double"))
906              return Double.TYPE;
907            else
908              throw x;
909          }
910      }
911    
912      /**
913       * Returns the most recent user defined ClassLoader on the execution stack
914       * or null if none is found.
915       */
916      private ClassLoader currentLoader()
917      {
918        return VMStackWalker.firstNonNullClassLoader();
919      }
920    
921      /**
922       * Lookup a class stored in the local hashtable. If it is not
923       * use the global lookup function in ObjectStreamClass to build
924       * the ObjectStreamClass. This method is requested according to
925       * the behaviour detected in the JDK by Kaffe's team.
926       *
927       * @param clazz Class to lookup in the hash table or for which
928       * we must build a descriptor.
929       * @return A valid instance of ObjectStreamClass corresponding
930       * to the specified class.
931       */
932      private ObjectStreamClass lookupClass(Class clazz)
933      {
934        if (clazz == null)
935          return null;
936    
937        ObjectStreamClass oclazz;
938        oclazz = classLookupTable.get(clazz);
939        if (oclazz == null)
940          return ObjectStreamClass.lookup(clazz);
941        else
942          return oclazz;
943      }
944    
945      /**
946       * Reconstruct class hierarchy the same way {@link
947       * java.io.ObjectStreamClass#hierarchy} does but using lookupClass
948       * instead of ObjectStreamClass.lookup.
949       *
950       * @param clazz This is the class for which we want the hierarchy.
951       *
952       * @return An array of valid {@link java.io.ObjectStreamClass} instances which
953       * represent the class hierarchy for clazz.
954       */
955      private ObjectStreamClass[] hierarchy(Class clazz)
956      {
957        ObjectStreamClass osc = lookupClass(clazz);
958    
959        return osc == null ? new ObjectStreamClass[0] : osc.hierarchy();
960      }
961    
962      /**
963       * Allows subclasses to resolve objects that are read from the
964       * stream with other objects to be returned in their place.  This
965       * method is called the first time each object is encountered.
966       *
967       * This method must be enabled before it will be called in the
968       * serialization process.
969       *
970       * @exception IOException Exception from underlying
971       * <code>OutputStream</code>.
972       *
973       * @see #enableResolveObject(boolean)
974       */
975      protected Object resolveObject(Object obj) throws IOException
976      {
977        return obj;
978      }
979    
980    
981      protected Class<?> resolveProxyClass(String[] intfs)
982        throws IOException, ClassNotFoundException
983      {
984        ClassLoader cl = currentLoader();
985    
986        Class<?>[] clss = new Class<?>[intfs.length];
987        if(cl == null)
988          {
989            for (int i = 0; i < intfs.length; i++)
990              clss[i] = Class.forName(intfs[i]);
991            cl = ClassLoader.getSystemClassLoader();
992          }
993        else
994          for (int i = 0; i < intfs.length; i++)
995            clss[i] = Class.forName(intfs[i], false, cl);
996        try
997          {
998            return Proxy.getProxyClass(cl, clss);
999          }
1000        catch (IllegalArgumentException e)
1001          {
1002            throw new ClassNotFoundException(null, e);
1003          }
1004      }
1005    
1006      /**
1007       * If <code>enable</code> is <code>true</code> and this object is
1008       * trusted, then <code>resolveObject (Object)</code> will be called
1009       * in subsequent calls to <code>readObject (Object)</code>.
1010       * Otherwise, <code>resolveObject (Object)</code> will not be called.
1011       *
1012       * @exception SecurityException This class is not trusted.
1013       */
1014      protected boolean enableResolveObject (boolean enable)
1015        throws SecurityException
1016      {
1017        if (enable)
1018          {
1019            SecurityManager sm = System.getSecurityManager();
1020            if (sm != null)
1021              sm.checkPermission(new SerializablePermission("enableSubstitution"));
1022          }
1023    
1024        boolean old_val = this.resolveEnabled;
1025        this.resolveEnabled = enable;
1026        return old_val;
1027      }
1028    
1029      /**
1030       * Reads stream magic and stream version information from the
1031       * underlying stream.
1032       *
1033       * @exception IOException Exception from underlying stream.
1034       *
1035       * @exception StreamCorruptedException An invalid stream magic
1036       * number or stream version was read from the stream.
1037       */
1038      protected void readStreamHeader()
1039        throws IOException, StreamCorruptedException
1040      {
1041        if(dump) dumpElement("STREAM MAGIC ");
1042        if (this.realInputStream.readShort() != STREAM_MAGIC)
1043          throw new StreamCorruptedException("Invalid stream magic number");
1044    
1045        if(dump) dumpElementln("STREAM VERSION ");
1046        if (this.realInputStream.readShort() != STREAM_VERSION)
1047          throw new StreamCorruptedException("Invalid stream version number");
1048      }
1049    
1050      public int read() throws IOException
1051      {
1052        if (this.readDataFromBlock)
1053          {
1054            if (this.blockDataPosition >= this.blockDataBytes)
1055              readNextBlock();
1056            return (this.blockData[this.blockDataPosition++] & 0xff);
1057          }
1058        else
1059          return this.realInputStream.read();
1060      }
1061    
1062      public int read(byte[] data, int offset, int length) throws IOException
1063      {
1064        if (this.readDataFromBlock)
1065          {
1066            int remain = this.blockDataBytes - this.blockDataPosition;
1067            if (remain == 0)
1068              {
1069                readNextBlock();
1070                remain = this.blockDataBytes - this.blockDataPosition;
1071              }
1072            length = Math.min(length, remain);
1073            System.arraycopy(this.blockData, this.blockDataPosition,
1074                             data, offset, length);
1075            this.blockDataPosition += length;
1076    
1077            return length;
1078          }
1079        else
1080          return this.realInputStream.read(data, offset, length);
1081      }
1082    
1083      public int available() throws IOException
1084      {
1085        if (this.readDataFromBlock)
1086          {
1087            if (this.blockDataPosition >= this.blockDataBytes)
1088              readNextBlock ();
1089    
1090            return this.blockDataBytes - this.blockDataPosition;
1091          }
1092        else
1093          return this.realInputStream.available();
1094      }
1095    
1096      public void close() throws IOException
1097      {
1098        this.realInputStream.close();
1099      }
1100    
1101      public boolean readBoolean() throws IOException
1102      {
1103        boolean switchmode = true;
1104        boolean oldmode = this.readDataFromBlock;
1105        if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
1106          switchmode = false;
1107        if (switchmode)
1108          oldmode = setBlockDataMode (true);
1109        boolean value = this.dataInputStream.readBoolean ();
1110        if (switchmode)
1111          setBlockDataMode (oldmode);
1112        return value;
1113      }
1114    
1115      public byte readByte() throws IOException
1116      {
1117        boolean switchmode = true;
1118        boolean oldmode = this.readDataFromBlock;
1119        if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
1120          switchmode = false;
1121        if (switchmode)
1122          oldmode = setBlockDataMode(true);
1123        byte value = this.dataInputStream.readByte();
1124        if (switchmode)
1125          setBlockDataMode(oldmode);
1126        return value;
1127      }
1128    
1129      public int readUnsignedByte() throws IOException
1130      {
1131        boolean switchmode = true;
1132        boolean oldmode = this.readDataFromBlock;
1133        if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 1)
1134          switchmode = false;
1135        if (switchmode)
1136          oldmode = setBlockDataMode(true);
1137        int value = this.dataInputStream.readUnsignedByte();
1138        if (switchmode)
1139          setBlockDataMode(oldmode);
1140        return value;
1141      }
1142    
1143      public short readShort() throws IOException
1144      {
1145        boolean switchmode = true;
1146        boolean oldmode = this.readDataFromBlock;
1147        if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
1148          switchmode = false;
1149        if (switchmode)
1150          oldmode = setBlockDataMode(true);
1151        short value = this.dataInputStream.readShort();
1152        if (switchmode)
1153          setBlockDataMode(oldmode);
1154        return value;
1155      }
1156    
1157      public int readUnsignedShort() throws IOException
1158      {
1159        boolean switchmode = true;
1160        boolean oldmode = this.readDataFromBlock;
1161        if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
1162          switchmode = false;
1163        if (switchmode)
1164          oldmode = setBlockDataMode(true);
1165        int value = this.dataInputStream.readUnsignedShort();
1166        if (switchmode)
1167          setBlockDataMode(oldmode);
1168        return value;
1169      }
1170    
1171      public char readChar() throws IOException
1172      {
1173        boolean switchmode = true;
1174        boolean oldmode = this.readDataFromBlock;
1175        if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 2)
1176          switchmode = false;
1177        if (switchmode)
1178          oldmode = setBlockDataMode(true);
1179        char value = this.dataInputStream.readChar();
1180        if (switchmode)
1181          setBlockDataMode(oldmode);
1182        return value;
1183      }
1184    
1185      public int readInt() throws IOException
1186      {
1187        boolean switchmode = true;
1188        boolean oldmode = this.readDataFromBlock;
1189        if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4)
1190          switchmode = false;
1191        if (switchmode)
1192          oldmode = setBlockDataMode(true);
1193        int value = this.dataInputStream.readInt();
1194        if (switchmode)
1195          setBlockDataMode(oldmode);
1196        return value;
1197      }
1198    
1199      public long readLong() throws IOException
1200      {
1201        boolean switchmode = true;
1202        boolean oldmode = this.readDataFromBlock;
1203        if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8)
1204          switchmode = false;
1205        if (switchmode)
1206          oldmode = setBlockDataMode(true);
1207        long value = this.dataInputStream.readLong();
1208        if (switchmode)
1209          setBlockDataMode(oldmode);
1210        return value;
1211      }
1212    
1213      public float readFloat() throws IOException
1214      {
1215        boolean switchmode = true;
1216        boolean oldmode = this.readDataFromBlock;
1217        if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 4)
1218          switchmode = false;
1219        if (switchmode)
1220          oldmode = setBlockDataMode(true);
1221        float value = this.dataInputStream.readFloat();
1222        if (switchmode)
1223          setBlockDataMode(oldmode);
1224        return value;
1225      }
1226    
1227      public double readDouble() throws IOException
1228      {
1229        boolean switchmode = true;
1230        boolean oldmode = this.readDataFromBlock;
1231        if (!oldmode || this.blockDataBytes - this.blockDataPosition >= 8)
1232          switchmode = false;
1233        if (switchmode)
1234          oldmode = setBlockDataMode(true);
1235        double value = this.dataInputStream.readDouble();
1236        if (switchmode)
1237          setBlockDataMode(oldmode);
1238        return value;
1239      }
1240    
1241      public void readFully(byte data[]) throws IOException
1242      {
1243        this.dataInputStream.readFully(data);
1244      }
1245    
1246      public void readFully(byte data[], int offset, int size)
1247        throws IOException
1248      {
1249        this.dataInputStream.readFully(data, offset, size);
1250      }
1251    
1252      public int skipBytes(int len) throws IOException
1253      {
1254        return this.dataInputStream.skipBytes(len);
1255      }
1256    
1257      /**
1258       * @deprecated
1259       * @see java.io.DataInputStream#readLine ()
1260       */
1261      public String readLine() throws IOException
1262      {
1263        return this.dataInputStream.readLine();
1264      }
1265    
1266      public String readUTF() throws IOException
1267      {
1268        return this.dataInputStream.readUTF();
1269      }
1270    
1271      /**
1272       * This class allows a class to specify exactly which fields should
1273       * be read, and what values should be read for these fields.
1274       *
1275       * XXX: finish up comments
1276       */
1277      public abstract static class GetField
1278      {
1279        public abstract ObjectStreamClass getObjectStreamClass();
1280    
1281        public abstract boolean defaulted(String name)
1282          throws IOException, IllegalArgumentException;
1283    
1284        public abstract boolean get(String name, boolean defvalue)
1285          throws IOException, IllegalArgumentException;
1286    
1287        public abstract char get(String name, char defvalue)
1288          throws IOException, IllegalArgumentException;
1289    
1290        public abstract byte get(String name, byte defvalue)
1291          throws IOException, IllegalArgumentException;
1292    
1293        public abstract short get(String name, short defvalue)
1294          throws IOException, IllegalArgumentException;
1295    
1296        public abstract int get(String name, int defvalue)
1297          throws IOException, IllegalArgumentException;
1298    
1299        public abstract long get(String name, long defvalue)
1300          throws IOException, IllegalArgumentException;
1301    
1302        public abstract float get(String name, float defvalue)
1303          throws IOException, IllegalArgumentException;
1304    
1305        public abstract double get(String name, double defvalue)
1306          throws IOException, IllegalArgumentException;
1307    
1308        public abstract Object get(String name, Object defvalue)
1309          throws IOException, IllegalArgumentException;
1310      }
1311    
1312      /**
1313       * This method should be called by a method called 'readObject' in the
1314       * deserializing class (if present). It cannot (and should not)be called
1315       * outside of it. Its goal is to read all fields in the real input stream
1316       * and keep them accessible through the {@link GetField} class. Calling
1317       * this method will not alter the deserializing object.
1318       *
1319       * @return A valid freshly created 'GetField' instance to get access to
1320       * the deserialized stream.
1321       * @throws IOException An input/output exception occured.
1322       * @throws ClassNotFoundException
1323       * @throws NotActiveException
1324       */
1325      public GetField readFields()
1326        throws IOException, ClassNotFoundException, NotActiveException
1327      {
1328        if (this.currentObject == null || this.currentObjectStreamClass == null)
1329          throw new NotActiveException("readFields called by non-active class and/or object");
1330    
1331        if (prereadFields != null)
1332          return prereadFields;
1333    
1334        if (fieldsAlreadyRead)
1335          throw new NotActiveException("readFields called but fields already read from"
1336                                       + " stream (by defaultReadObject or readFields)");
1337    
1338        final ObjectStreamClass clazz = this.currentObjectStreamClass;
1339        final byte[] prim_field_data = new byte[clazz.primFieldSize];
1340        final Object[] objs = new Object[clazz.objectFieldCount];
1341    
1342        // Apparently Block data is not used with GetField as per
1343        // empirical evidence against JDK 1.2.  Also see Mauve test
1344        // java.io.ObjectInputOutput.Test.GetPutField.
1345        boolean oldmode = setBlockDataMode(false);
1346        readFully(prim_field_data);
1347        for (int i = 0; i < objs.length; ++ i)
1348          objs[i] = readObject();
1349        setBlockDataMode(oldmode);
1350    
1351        prereadFields = new GetField()
1352          {
1353            public ObjectStreamClass getObjectStreamClass()
1354            {
1355              return clazz;
1356            }
1357    
1358            public boolean defaulted(String name)
1359              throws IOException, IllegalArgumentException
1360            {
1361              ObjectStreamField f = clazz.getField(name);
1362    
1363              /* First if we have a serialized field use the descriptor */
1364              if (f != null)
1365                {
1366                  /* It is in serialPersistentFields but setClass tells us
1367                   * it should not be set. This value is defaulted.
1368                   */
1369                  if (f.isPersistent() && !f.isToSet())
1370                    return true;
1371    
1372                  return false;
1373                }
1374    
1375              /* This is not a serialized field. There should be
1376               * a default value only if the field really exists.
1377               */
1378              try
1379                {
1380                  return (clazz.forClass().getDeclaredField (name) != null);
1381                }
1382              catch (NoSuchFieldException e)
1383                {
1384                  throw new IllegalArgumentException(e);
1385                }
1386            }
1387    
1388            public boolean get(String name, boolean defvalue)
1389              throws IOException, IllegalArgumentException
1390            {
1391              ObjectStreamField field = getField(name, Boolean.TYPE);
1392    
1393              if (field == null)
1394                return defvalue;
1395    
1396              return prim_field_data[field.getOffset()] == 0 ? false : true;
1397            }
1398    
1399            public char get(String name, char defvalue)
1400              throws IOException, IllegalArgumentException
1401            {
1402              ObjectStreamField field = getField(name, Character.TYPE);
1403    
1404              if (field == null)
1405                return defvalue;
1406    
1407              int off = field.getOffset();
1408    
1409              return (char)(((prim_field_data[off++] & 0xFF) << 8)
1410                            | (prim_field_data[off] & 0xFF));
1411            }
1412    
1413            public byte get(String name, byte defvalue)
1414              throws IOException, IllegalArgumentException
1415            {
1416              ObjectStreamField field = getField(name, Byte.TYPE);
1417    
1418              if (field == null)
1419                return defvalue;
1420    
1421              return prim_field_data[field.getOffset()];
1422            }
1423    
1424            public short get(String name, short defvalue)
1425              throws IOException, IllegalArgumentException
1426            {
1427              ObjectStreamField field = getField(name, Short.TYPE);
1428    
1429              if (field == null)
1430                return defvalue;
1431    
1432              int off = field.getOffset();
1433    
1434              return (short)(((prim_field_data[off++] & 0xFF) << 8)
1435                             | (prim_field_data[off] & 0xFF));
1436            }
1437    
1438            public int get(String name, int defvalue)
1439              throws IOException, IllegalArgumentException
1440            {
1441              ObjectStreamField field = getField(name, Integer.TYPE);
1442    
1443              if (field == null)
1444                return defvalue;
1445    
1446              int off = field.getOffset();
1447    
1448              return ((prim_field_data[off++] & 0xFF) << 24)
1449                | ((prim_field_data[off++] & 0xFF) << 16)
1450                | ((prim_field_data[off++] & 0xFF) << 8)
1451                | (prim_field_data[off] & 0xFF);
1452            }
1453    
1454            public long get(String name, long defvalue)
1455              throws IOException, IllegalArgumentException
1456            {
1457              ObjectStreamField field = getField(name, Long.TYPE);
1458    
1459              if (field == null)
1460                return defvalue;
1461    
1462              int off = field.getOffset();
1463    
1464              return (long)(((prim_field_data[off++] & 0xFFL) << 56)
1465                            | ((prim_field_data[off++] & 0xFFL) << 48)
1466                            | ((prim_field_data[off++] & 0xFFL) << 40)
1467                            | ((prim_field_data[off++] & 0xFFL) << 32)
1468                            | ((prim_field_data[off++] & 0xFF) << 24)
1469                            | ((prim_field_data[off++] & 0xFF) << 16)
1470                            | ((prim_field_data[off++] & 0xFF) << 8)
1471                            | (prim_field_data[off] & 0xFF));
1472            }
1473    
1474            public float get(String name, float defvalue)
1475              throws IOException, IllegalArgumentException
1476            {
1477              ObjectStreamField field = getField(name, Float.TYPE);
1478    
1479              if (field == null)
1480                return defvalue;
1481    
1482              int off = field.getOffset();
1483    
1484              return Float.intBitsToFloat(((prim_field_data[off++] & 0xFF) << 24)
1485                                          | ((prim_field_data[off++] & 0xFF) << 16)
1486                                          | ((prim_field_data[off++] & 0xFF) << 8)
1487                                          | (prim_field_data[off] & 0xFF));
1488            }
1489    
1490            public double get(String name, double defvalue)
1491              throws IOException, IllegalArgumentException
1492            {
1493              ObjectStreamField field = getField(name, Double.TYPE);
1494    
1495              if (field == null)
1496                return defvalue;
1497    
1498              int off = field.getOffset();
1499    
1500              return Double.longBitsToDouble
1501                ( (long) (((prim_field_data[off++] & 0xFFL) << 56)
1502                          | ((prim_field_data[off++] & 0xFFL) << 48)
1503                          | ((prim_field_data[off++] & 0xFFL) << 40)
1504                          | ((prim_field_data[off++] & 0xFFL) << 32)
1505                          | ((prim_field_data[off++] & 0xFF) << 24)
1506                          | ((prim_field_data[off++] & 0xFF) << 16)
1507                          | ((prim_field_data[off++] & 0xFF) << 8)
1508                          | (prim_field_data[off] & 0xFF)));
1509            }
1510    
1511            public Object get(String name, Object defvalue)
1512              throws IOException, IllegalArgumentException
1513            {
1514              ObjectStreamField field =
1515                getField(name, defvalue == null ? null : defvalue.getClass ());
1516    
1517              if (field == null)
1518                return defvalue;
1519    
1520              return objs[field.getOffset()];
1521            }
1522    
1523            private ObjectStreamField getField(String name, Class type)
1524              throws IllegalArgumentException
1525            {
1526              ObjectStreamField field = clazz.getField(name);
1527              boolean illegal = false;
1528    
1529              // XXX This code is horrible and needs to be rewritten!
1530              try
1531                {
1532                  try
1533                    {
1534                      Class field_type = field.getType();
1535    
1536                      if (type == field_type ||
1537                          (type == null && !field_type.isPrimitive()))
1538                        {
1539                          /* See defaulted */
1540                          return field;
1541                        }
1542    
1543                      illegal = true;
1544                      throw new IllegalArgumentException
1545                        ("Field requested is of type "
1546                         + field_type.getName()
1547                         + ", but requested type was "
1548                         + (type == null ?  "Object" : type.getName()));
1549                    }
1550                  catch (NullPointerException _)
1551                    {
1552                      /* Here we catch NullPointerException, because it may
1553                         only come from the call 'field.getType()'. If field
1554                         is null, we have to return null and classpath ethic
1555                         say we must try to avoid 'if (xxx == null)'.
1556                      */
1557                    }
1558                  catch (IllegalArgumentException e)
1559                    {
1560                      throw e;
1561                    }
1562    
1563                  return null;
1564                }
1565              finally
1566                {
1567                  /* If this is an unassigned field we should return
1568                   * the default value.
1569                   */
1570                  if (!illegal && field != null && !field.isToSet() && field.isPersistent())
1571                    return null;
1572    
1573                  /* We do not want to modify transient fields. They should
1574                   * be left to 0.
1575                   */
1576                  try
1577                    {
1578                      Field f = clazz.forClass().getDeclaredField(name);
1579                      if (Modifier.isTransient(f.getModifiers()))
1580                        throw new IllegalArgumentException
1581                          ("no such field (non transient) " + name);
1582                      if (field == null && f.getType() != type)
1583                        throw new IllegalArgumentException
1584                          ("Invalid requested type for field " + name);
1585                    }
1586                  catch (NoSuchFieldException e)
1587                    {
1588                      if (field == null)
1589                        throw new IllegalArgumentException(e);
1590                    }
1591    
1592                }
1593            }
1594          };
1595    
1596        fieldsAlreadyRead = true;
1597        return prereadFields;
1598      }
1599    
1600      /**
1601       * Protected constructor that allows subclasses to override
1602       * deserialization.  This constructor should be called by subclasses
1603       * that wish to override <code>readObject (Object)</code>.  This
1604       * method does a security check <i>NOTE: currently not
1605       * implemented</i>, then sets a flag that informs
1606       * <code>readObject (Object)</code> to call the subclasses
1607       * <code>readObjectOverride (Object)</code> method.
1608       *
1609       * @see #readObjectOverride()
1610       */
1611      protected ObjectInputStream()
1612        throws IOException, SecurityException
1613      {
1614        SecurityManager sec_man = System.getSecurityManager();
1615        if (sec_man != null)
1616          sec_man.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
1617        this.useSubclassMethod = true;
1618      }
1619    
1620      /**
1621       * This method allows subclasses to override the default
1622       * de serialization mechanism provided by
1623       * <code>ObjectInputStream</code>.  To make this method be used for
1624       * writing objects, subclasses must invoke the 0-argument
1625       * constructor on this class from their constructor.
1626       *
1627       * @see #ObjectInputStream()
1628       */
1629      protected Object readObjectOverride()
1630        throws ClassNotFoundException, IOException, OptionalDataException
1631      {
1632        throw new IOException("Subclass of ObjectInputStream must implement readObjectOverride");
1633      }
1634    
1635      /**
1636       * Assigns the next available handle to <code>obj</code>.
1637       *
1638       * @param obj The object for which we want a new handle.
1639       * @param shared True if the handle should be shared
1640       *               with later calls.
1641       * @return A valid handle for the specified object.
1642       */
1643      private int assignNewHandle(Object obj, boolean shared)
1644      {
1645        int handle = this.nextOID;
1646        this.nextOID = handle + 1;
1647        rememberHandle(obj,shared,handle);
1648        return handle;
1649      }
1650    
1651      /**
1652       * Remember the object associated with the given handle.
1653       *
1654       * @param obj an object
1655       * @param shared true if the reference should be shared
1656       *               with later calls.
1657       * @param handle a handle, must be >= baseWireHandle
1658       *
1659       * @see #lookupHandle
1660       */
1661      private void rememberHandle(Object obj, boolean shared,
1662                                  int handle)
1663      {
1664        handles.put(handle, new Pair<Boolean,Object>(shared, obj));
1665      }
1666    
1667      /**
1668       * Look up the object associated with a given handle.
1669       *
1670       * @param handle a handle, must be >= baseWireHandle
1671       * @return the object remembered for handle or null if none.
1672       * @throws StreamCorruptedException if the handle is invalid.
1673       * @throws InvalidObjectException if the reference is not shared.
1674       * @see #rememberHandle
1675       */
1676      private Object lookupHandle(int handle)
1677        throws ObjectStreamException
1678      {
1679        Pair<Boolean,Object> result = handles.get(handle);
1680        if (result == null)
1681          throw new StreamCorruptedException("The handle, " +
1682                                             Integer.toHexString(handle) +
1683                                             ", is invalid.");
1684        if (!result.getLeft())
1685          throw new InvalidObjectException("The handle, " +
1686                                           Integer.toHexString(handle) +
1687                                           ", is not shared.");
1688        return result.getRight();
1689      }
1690    
1691      private Object processResolution(ObjectStreamClass osc, Object obj, int handle,
1692                                       boolean shared)
1693        throws IOException
1694      {
1695        if (osc != null && obj instanceof Serializable)
1696          {
1697            try
1698              {
1699                Method m = osc.readResolveMethod;
1700                if(m != null)
1701                {
1702                    obj = m.invoke(obj, new Object[] {});
1703                }
1704              }
1705            catch (IllegalAccessException ignore)
1706              {
1707              }
1708            catch (InvocationTargetException exception)
1709              {
1710                Throwable cause = exception.getCause();
1711                if (cause instanceof ObjectStreamException)
1712                  throw (ObjectStreamException) cause;
1713                else if (cause instanceof RuntimeException)
1714                  throw (RuntimeException) cause;
1715                else if (cause instanceof Error)
1716                  throw (Error) cause;
1717              }
1718          }
1719    
1720        if (this.resolveEnabled)
1721          obj = resolveObject(obj);
1722    
1723        rememberHandle(obj, shared, handle);
1724        if (!shared)
1725          {
1726            if (obj instanceof byte[])
1727              return ((byte[]) obj).clone();
1728            if (obj instanceof short[])
1729              return ((short[]) obj).clone();
1730            if (obj instanceof int[])
1731              return ((int[]) obj).clone();
1732            if (obj instanceof long[])
1733              return ((long[]) obj).clone();
1734            if (obj instanceof char[])
1735              return ((char[]) obj).clone();
1736            if (obj instanceof boolean[])
1737              return ((boolean[]) obj).clone();
1738            if (obj instanceof float[])
1739              return ((float[]) obj).clone();
1740            if (obj instanceof double[])
1741              return ((double[]) obj).clone();
1742            if (obj instanceof Object[])
1743              return ((Object[]) obj).clone();
1744          }
1745        return obj;
1746      }
1747    
1748      private void clearHandles()
1749      {
1750        handles.clear();
1751        this.nextOID = baseWireHandle;
1752      }
1753    
1754      private void readNextBlock() throws IOException
1755      {
1756        byte marker = this.realInputStream.readByte();
1757        while (marker == TC_RESET)
1758          {
1759            if(dump) dumpElementln("RESET");
1760            clearHandles();
1761            marker = this.realInputStream.readByte();
1762          }
1763        readNextBlock(marker);
1764      }
1765    
1766      private void readNextBlock(byte marker) throws IOException
1767      {
1768        if (marker == TC_BLOCKDATA)
1769          {
1770            if(dump) dumpElement("BLOCK DATA SIZE=");
1771            this.blockDataBytes = this.realInputStream.readUnsignedByte();
1772            if(dump) dumpElementln (Integer.toString(this.blockDataBytes));
1773          }
1774        else if (marker == TC_BLOCKDATALONG)
1775          {
1776            if(dump) dumpElement("BLOCK DATA LONG SIZE=");
1777            this.blockDataBytes = this.realInputStream.readInt();
1778            if(dump) dumpElementln (Integer.toString(this.blockDataBytes));
1779          }
1780        else
1781          {
1782            throw new EOFException("Attempt to read primitive data, but no data block is active.");
1783          }
1784    
1785        if (this.blockData.length < this.blockDataBytes)
1786          this.blockData = new byte[this.blockDataBytes];
1787    
1788        this.realInputStream.readFully (this.blockData, 0, this.blockDataBytes);
1789        this.blockDataPosition = 0;
1790      }
1791    
1792      private void readArrayElements (Object array, Class clazz)
1793        throws ClassNotFoundException, IOException
1794      {
1795        if (clazz.isPrimitive())
1796          {
1797            if (clazz == Boolean.TYPE)
1798              {
1799                boolean[] cast_array = (boolean[])array;
1800                for (int i=0; i < cast_array.length; i++)
1801                  cast_array[i] = this.realInputStream.readBoolean();
1802                return;
1803              }
1804            if (clazz == Byte.TYPE)
1805              {
1806                byte[] cast_array = (byte[])array;
1807                for (int i=0; i < cast_array.length; i++)
1808                  cast_array[i] = this.realInputStream.readByte();
1809                return;
1810              }
1811            if (clazz == Character.TYPE)
1812              {
1813                char[] cast_array = (char[])array;
1814                for (int i=0; i < cast_array.length; i++)
1815                  cast_array[i] = this.realInputStream.readChar();
1816                return;
1817              }
1818            if (clazz == Double.TYPE)
1819              {
1820                double[] cast_array = (double[])array;
1821                for (int i=0; i < cast_array.length; i++)
1822                  cast_array[i] = this.realInputStream.readDouble();
1823                return;
1824              }
1825            if (clazz == Float.TYPE)
1826              {
1827                float[] cast_array = (float[])array;
1828                for (int i=0; i < cast_array.length; i++)
1829                  cast_array[i] = this.realInputStream.readFloat();
1830                return;
1831              }
1832            if (clazz == Integer.TYPE)
1833              {
1834                int[] cast_array = (int[])array;
1835                for (int i=0; i < cast_array.length; i++)
1836                  cast_array[i] = this.realInputStream.readInt();
1837                return;
1838              }
1839            if (clazz == Long.TYPE)
1840              {
1841                long[] cast_array = (long[])array;
1842                for (int i=0; i < cast_array.length; i++)
1843                  cast_array[i] = this.realInputStream.readLong();
1844                return;
1845              }
1846            if (clazz == Short.TYPE)
1847              {
1848                short[] cast_array = (short[])array;
1849                for (int i=0; i < cast_array.length; i++)
1850                  cast_array[i] = this.realInputStream.readShort();
1851                return;
1852              }
1853          }
1854        else
1855          {
1856            Object[] cast_array = (Object[])array;
1857            for (int i=0; i < cast_array.length; i++)
1858              cast_array[i] = readObject();
1859          }
1860      }
1861    
1862      private void readFields (Object obj, ObjectStreamClass stream_osc)
1863        throws ClassNotFoundException, IOException
1864      {
1865        ObjectStreamField[] fields = stream_osc.fieldMapping;
1866    
1867        for (int i = 0; i < fields.length; i += 2)
1868          {
1869            ObjectStreamField stream_field = fields[i];
1870            ObjectStreamField real_field = fields[i + 1];
1871            boolean read_value = (stream_field != null && stream_field.getOffset() >= 0 && stream_field.isToSet());
1872            boolean set_value = (real_field != null && real_field.isToSet());
1873            String field_name;
1874            char type;
1875    
1876            if (stream_field != null)
1877              {
1878                field_name = stream_field.getName();
1879                type = stream_field.getTypeCode();
1880              }
1881            else
1882              {
1883                field_name = real_field.getName();
1884                type = real_field.getTypeCode();
1885              }
1886    
1887            switch(type)
1888              {
1889              case 'Z':
1890                {
1891                  boolean value =
1892                    read_value ? this.realInputStream.readBoolean() : false;
1893                  if (dump && read_value && set_value)
1894                    dumpElementln("  " + field_name + ": " + value);
1895                  if (set_value)
1896                    real_field.setBooleanField(obj, value);
1897                  break;
1898                }
1899              case 'B':
1900                {
1901                  byte value =
1902                    read_value ? this.realInputStream.readByte() : 0;
1903                  if (dump && read_value && set_value)
1904                    dumpElementln("  " + field_name + ": " + value);
1905                  if (set_value)
1906                    real_field.setByteField(obj, value);
1907                  break;
1908                }
1909              case 'C':
1910                {
1911                  char value =
1912                    read_value ? this.realInputStream.readChar(): 0;
1913                  if (dump && read_value && set_value)
1914                    dumpElementln("  " + field_name + ": " + value);
1915                  if (set_value)
1916                    real_field.setCharField(obj, value);
1917                  break;
1918                }
1919              case 'D':
1920                {
1921                  double value =
1922                    read_value ? this.realInputStream.readDouble() : 0;
1923                  if (dump && read_value && set_value)
1924                    dumpElementln("  " + field_name + ": " + value);
1925                  if (set_value)
1926                    real_field.setDoubleField(obj, value);
1927                  break;
1928                }
1929              case 'F':
1930                {
1931                  float value =
1932                    read_value ? this.realInputStream.readFloat() : 0;
1933                  if (dump && read_value && set_value)
1934                    dumpElementln("  " + field_name + ": " + value);
1935                  if (set_value)
1936                    real_field.setFloatField(obj, value);
1937                  break;
1938                }
1939              case 'I':
1940                {
1941                  int value =
1942                    read_value ? this.realInputStream.readInt() : 0;
1943                  if (dump && read_value && set_value)
1944                    dumpElementln("  " + field_name + ": " + value);
1945                  if (set_value)
1946                    real_field.setIntField(obj, value);
1947                  break;
1948                }
1949              case 'J':
1950                {
1951                  long value =
1952                    read_value ? this.realInputStream.readLong() : 0;
1953                  if (dump && read_value && set_value)
1954                    dumpElementln("  " + field_name + ": " + value);
1955                  if (set_value)
1956                    real_field.setLongField(obj, value);
1957                  break;
1958                }
1959              case 'S':
1960                {
1961                  short value =
1962                    read_value ? this.realInputStream.readShort() : 0;
1963                  if (dump && read_value && set_value)
1964                    dumpElementln("  " + field_name + ": " + value);
1965                  if (set_value)
1966                    real_field.setShortField(obj, value);
1967                  break;
1968                }
1969              case 'L':
1970              case '[':
1971                {
1972                  Object value =
1973                    read_value ? readObject() : null;
1974                  if (set_value)
1975                    real_field.setObjectField(obj, value);
1976                  break;
1977                }
1978              default:
1979                throw new InternalError("Invalid type code: " + type);
1980              }
1981          }
1982      }
1983    
1984      // Toggles writing primitive data to block-data buffer.
1985      private boolean setBlockDataMode (boolean on)
1986      {
1987        boolean oldmode = this.readDataFromBlock;
1988        this.readDataFromBlock = on;
1989    
1990        if (on)
1991          this.dataInputStream = this.blockDataInput;
1992        else
1993          this.dataInputStream = this.realInputStream;
1994        return oldmode;
1995      }
1996    
1997      // returns a new instance of REAL_CLASS that has been constructed
1998      // only to the level of CONSTRUCTOR_CLASS (a super class of REAL_CLASS)
1999      private Object newObject (Class real_class, Constructor constructor)
2000        throws ClassNotFoundException, IOException
2001      {
2002        if (constructor == null)
2003            throw new InvalidClassException("Missing accessible no-arg base class constructor for " + real_class.getName());
2004        try
2005          {
2006            return VMObjectInputStream.allocateObject(real_class, constructor.getDeclaringClass(), constructor);
2007          }
2008        catch (InstantiationException e)
2009          {
2010            throw (ClassNotFoundException) new ClassNotFoundException
2011              ("Instance of " + real_class + " could not be created").initCause(e);
2012          }
2013      }
2014    
2015      // runs all registered ObjectInputValidations in prioritized order
2016      // on OBJ
2017      private void invokeValidators() throws InvalidObjectException
2018      {
2019        try
2020          {
2021            Iterator<ValidatorAndPriority> it = currentObjectValidators.iterator();
2022            while(it.hasNext())
2023              {
2024                ValidatorAndPriority vap = it.next();
2025                ObjectInputValidation validator = vap.validator;
2026                validator.validateObject();
2027              }
2028          }
2029        finally
2030          {
2031            currentObjectValidators = null;
2032          }
2033      }
2034    
2035      private void callReadMethod (Method readObject, Class klass, Object obj)
2036        throws ClassNotFoundException, IOException
2037      {
2038        try
2039          {
2040            readObject.invoke(obj, new Object[] { this });
2041          }
2042        catch (InvocationTargetException x)
2043          {
2044            /* Rethrow if possible. */
2045            Throwable exception = x.getTargetException();
2046            if (exception instanceof RuntimeException)
2047              throw (RuntimeException) exception;
2048            if (exception instanceof IOException)
2049              throw (IOException) exception;
2050            if (exception instanceof ClassNotFoundException)
2051              throw (ClassNotFoundException) exception;
2052    
2053            throw (IOException) new IOException(
2054              "Exception thrown from readObject() on " + klass).initCause(x);
2055          }
2056        catch (Exception x)
2057          {
2058            throw (IOException) new IOException(
2059              "Failure invoking readObject() on " + klass).initCause(x);
2060          }
2061    
2062        // Invalidate fields which has been read through readFields.
2063        prereadFields = null;
2064      }
2065    
2066      private static final int BUFFER_SIZE = 1024;
2067    
2068      private DataInputStream realInputStream;
2069      private DataInputStream dataInputStream;
2070      private DataInputStream blockDataInput;
2071      private int blockDataPosition;
2072      private int blockDataBytes;
2073      private byte[] blockData;
2074      private boolean useSubclassMethod;
2075      private int nextOID;
2076      private boolean resolveEnabled;
2077      private Map<Integer,Pair<Boolean,Object>> handles;
2078      private Object currentObject;
2079      private ObjectStreamClass currentObjectStreamClass;
2080      private TreeSet<ValidatorAndPriority> currentObjectValidators;
2081      private boolean readDataFromBlock;
2082      private boolean fieldsAlreadyRead;
2083      private Hashtable<Class,ObjectStreamClass> classLookupTable;
2084      private GetField prereadFields;
2085    
2086      private static boolean dump;
2087    
2088      // The nesting depth for debugging output
2089      private int depth = 0;
2090    
2091      private static final boolean DEBUG = false;
2092    
2093      private void dumpElement (String msg)
2094      {
2095        System.out.print(msg);
2096      }
2097    
2098      private void dumpElementln (String msg)
2099      {
2100        System.out.println(msg);
2101        for (int i = 0; i < depth; i++)
2102          System.out.print (" ");
2103        System.out.print (Thread.currentThread() + ": ");
2104      }
2105    
2106      private void dumpElementln (String msg, Object obj)
2107      {
2108        try
2109          {
2110            System.out.print(msg);
2111            if (java.lang.reflect.Proxy.isProxyClass(obj.getClass()))
2112              System.out.println(obj.getClass());
2113            else
2114            System.out.println(obj);
2115          }
2116        catch (Exception _)
2117          {
2118          }
2119        for (int i = 0; i < depth; i++)
2120          System.out.print (" ");
2121        System.out.print (Thread.currentThread() + ": ");
2122      }
2123    
2124      // used to keep a prioritized list of object validators
2125      private static final class ValidatorAndPriority implements Comparable
2126      {
2127        int priority;
2128        ObjectInputValidation validator;
2129    
2130        ValidatorAndPriority (ObjectInputValidation validator, int priority)
2131        {
2132          this.priority = priority;
2133          this.validator = validator;
2134        }
2135    
2136        public int compareTo (Object o)
2137        {
2138          ValidatorAndPriority vap = (ValidatorAndPriority)o;
2139          return this.priority - vap.priority;
2140        }
2141      }
2142    }