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