OSDN Git Service

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