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