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