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