IIOPInputStream.java revision 807:408c9c621938
1/*
2 * Copyright (c) 1998, 2015, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25/*
26 * Licensed Materials - Property of IBM
27 * RMI-IIOP v1.0
28 * Copyright IBM Corp. 1998 1999  All Rights Reserved
29 *
30 */
31
32package com.sun.corba.se.impl.io;
33
34import java.io.IOException;
35import java.io.StreamCorruptedException;
36import java.io.ObjectInputValidation;
37import java.io.NotActiveException;
38import java.io.InvalidObjectException;
39import java.io.InvalidClassException;
40import java.io.OptionalDataException;
41import java.io.Externalizable;
42import java.io.EOFException;
43import java.lang.reflect.*;
44import java.util.Vector;
45import java.util.Enumeration;
46
47import sun.corba.Bridge ;
48
49import java.security.AccessController ;
50import java.security.PrivilegedAction ;
51
52import com.sun.corba.se.impl.util.Utility;
53
54import org.omg.CORBA.portable.ValueInputStream;
55
56import org.omg.CORBA.ValueMember;
57import org.omg.CORBA.SystemException;
58import org.omg.CORBA.TCKind;
59import org.omg.CORBA.ORB;
60import org.omg.CORBA.CompletionStatus;
61import org.omg.CORBA.portable.IndirectionException;
62import org.omg.CORBA.MARSHAL;
63import org.omg.CORBA.TypeCode;
64
65import com.sun.org.omg.CORBA.ValueDefPackage.FullValueDescription;
66import com.sun.org.omg.SendingContext.CodeBase;
67
68import javax.rmi.CORBA.Util;
69import javax.rmi.CORBA.ValueHandler;
70
71import java.security.*;
72import java.util.*;
73
74import com.sun.corba.se.impl.logging.OMGSystemException ;
75import com.sun.corba.se.impl.logging.UtilSystemException ;
76
77import com.sun.corba.se.spi.logging.CORBALogDomains ;
78
79/**
80 * IIOPInputStream is used by the ValueHandlerImpl to handle Java serialization
81 * input semantics.
82 *
83 * @author  Stephen Lewallen
84 * @since   JDK1.1.6
85 */
86
87public class IIOPInputStream
88    extends com.sun.corba.se.impl.io.InputStreamHook
89{
90    private static Bridge bridge =
91        (Bridge)AccessController.doPrivileged(
92            new PrivilegedAction() {
93                public Object run() {
94                    return Bridge.get() ;
95                }
96            }
97        ) ;
98
99    private static OMGSystemException omgWrapper = OMGSystemException.get(
100        CORBALogDomains.RPC_ENCODING ) ;
101    private static UtilSystemException utilWrapper = UtilSystemException.get(
102        CORBALogDomains.RPC_ENCODING ) ;
103
104    // Necessary to pass the appropriate fields into the
105    // defaultReadObjectDelegate method (which takes no
106    // parameters since it's called from
107    // java.io.ObjectInpuStream defaultReadObject()
108    // which we can't change).
109    //
110    // This is only used in the case where the fields had
111    // to be obtained remotely because of a serializable
112    // version difference.  Set in inputObjectUsingFVD.
113    // Part of serialization evolution fixes for Ladybird,
114    // bug 4365188.
115    private ValueMember defaultReadObjectFVDMembers[] = null;
116
117    private org.omg.CORBA_2_3.portable.InputStream orbStream;
118
119    private CodeBase cbSender;
120
121    private ValueHandlerImpl vhandler;  //d4365188
122
123    private Object currentObject = null;
124
125    private ObjectStreamClass currentClassDesc = null;
126
127    private Class currentClass = null;
128
129    private int recursionDepth = 0;
130
131    private int simpleReadDepth = 0;
132
133    // The ActiveRecursionManager replaces the old RecursionManager which
134    // used to record how many recursions were made, and resolve them after
135    // an object was completely deserialized.
136    //
137    // That created problems (as in bug 4414154) because when custom
138    // unmarshaling in readObject, there can be recursive references
139    // to one of the objects currently being unmarshaled, and the
140    // passive recursion system failed.
141    ActiveRecursionManager activeRecursionMgr = new ActiveRecursionManager();
142
143    private IOException abortIOException = null;
144
145    /* Remember the first exception that stopped this stream. */
146    private ClassNotFoundException abortClassNotFoundException = null;
147
148    /* Vector of validation callback objects
149     * The vector is created as needed. The vector is maintained in
150     * order of highest (first) priority to lowest
151     */
152    private Vector callbacks;
153
154    // Serialization machinery fields
155    /* Arrays used to keep track of classes and ObjectStreamClasses
156     * as they are being merged; used in inputObject.
157     * spClass is the stack pointer for both.  */
158    ObjectStreamClass[] classdesc;
159    Class[] classes;
160    int spClass;
161
162    private static final String kEmptyStr = "";
163
164    // TCKind TypeCodes used in FVD inputClassFields
165    //public static final TypeCode kRemoteTypeCode = new TypeCodeImpl(TCKind._tk_objref);
166    //public static final TypeCode kValueTypeCode =  new TypeCodeImpl(TCKind._tk_value);
167    // removed TypeCodeImpl dependency
168    public static final TypeCode kRemoteTypeCode = ORB.init().get_primitive_tc(TCKind.tk_objref);
169    public static final TypeCode kValueTypeCode =  ORB.init().get_primitive_tc(TCKind.tk_value);
170
171    // TESTING CODE - useFVDOnly should be made final before FCS in order to
172    // optimize out the check.
173    private static final boolean useFVDOnly = false;
174
175    private byte streamFormatVersion;
176
177    // Return the stream format version currently being used
178    // to deserialize an object
179    protected byte getStreamFormatVersion() {
180        return streamFormatVersion;
181    }
182
183    // At the beginning of data sent by a writeObject or
184    // writeExternal method there is a byte telling the
185    // reader the stream format version.
186    private void readFormatVersion() throws IOException {
187
188        streamFormatVersion = orbStream.read_octet();
189
190        if (streamFormatVersion < 1 ||
191            streamFormatVersion > vhandler.getMaximumStreamFormatVersion()) {
192            SystemException sysex = omgWrapper.unsupportedFormatVersion(
193                    CompletionStatus.COMPLETED_MAYBE);
194            // XXX I18N?  Logging for IOException?
195            IOException result = new IOException("Unsupported format version: "
196                                                 + streamFormatVersion);
197            result.initCause( sysex ) ;
198            throw result ;
199        }
200
201        if (streamFormatVersion == 2) {
202            if (!(orbStream instanceof ValueInputStream)) {
203                SystemException sysex = omgWrapper.notAValueinputstream(
204                    CompletionStatus.COMPLETED_MAYBE);
205                // XXX I18N?  Logging for IOException?
206                IOException result = new IOException("Not a ValueInputStream");
207                result.initCause( sysex ) ;
208                throw result;
209            }
210        }
211    }
212
213    public static void setTestFVDFlag(boolean val){
214        //  useFVDOnly = val;
215    }
216
217    /**
218     * Dummy constructor; passes upper stream a dummy stream;
219     **/
220    public IIOPInputStream()
221        throws java.io.IOException {
222        super();
223        resetStream();
224    }
225
226    final void setOrbStream(org.omg.CORBA_2_3.portable.InputStream os) {
227        orbStream = os;
228    }
229
230    final org.omg.CORBA_2_3.portable.InputStream getOrbStream() {
231        return orbStream;
232    }
233
234    //added setSender and getSender
235    public final void setSender(CodeBase cb) {
236        cbSender = cb;
237    }
238
239    public final CodeBase getSender() {
240        return cbSender;
241    }
242
243    // 4365188 this is added to enable backward compatability w/ wrong
244    // rep-ids
245    public final void setValueHandler(ValueHandler vh) {
246        vhandler = (com.sun.corba.se.impl.io.ValueHandlerImpl) vh;
247    }
248
249    public final ValueHandler getValueHandler() {
250        return (javax.rmi.CORBA.ValueHandler) vhandler;
251    }
252
253    final void increaseRecursionDepth(){
254        recursionDepth++;
255    }
256
257    final int decreaseRecursionDepth(){
258        return --recursionDepth;
259    }
260
261    /**
262     * Override the actions of the final method "readObject()"
263     * in ObjectInputStream.
264     * @since     JDK1.1.6
265     *
266     * Read an object from the ObjectInputStream.
267     * The class of the object, the signature of the class, and the values
268     * of the non-transient and non-static fields of the class and all
269     * of its supertypes are read.  Default deserializing for a class can be
270     * overriden using the writeObject and readObject methods.
271     * Objects referenced by this object are read transitively so
272     * that a complete equivalent graph of objects is reconstructed by readObject. <p>
273     *
274     * The root object is completly restored when all of its fields
275     * and the objects it references are completely restored.  At this
276     * point the object validation callbacks are executed in order
277     * based on their registered priorities. The callbacks are
278     * registered by objects (in the readObject special methods)
279     * as they are individually restored.
280     *
281     * Exceptions are thrown for problems with the InputStream and for classes
282     * that should not be deserialized.  All exceptions are fatal to the
283     * InputStream and leave it in an indeterminate state; it is up to the caller
284     * to ignore or recover the stream state.
285     * @exception java.lang.ClassNotFoundException Class of a serialized object
286     *      cannot be found.
287     * @exception InvalidClassException Something is wrong with a class used by
288     *     serialization.
289     * @exception StreamCorruptedException Control information in the
290     *     stream is inconsistent.
291     * @exception OptionalDataException Primitive data was found in the
292     * stream instead of objects.
293     * @exception IOException Any of the usual Input/Output related exceptions.
294     * @since     JDK1.1
295     */
296    public final synchronized Object readObjectDelegate() throws IOException
297    {
298        try {
299
300            readObjectState.readData(this);
301
302            return orbStream.read_abstract_interface();
303        } catch (MARSHAL marshalException) {
304            handleOptionalDataMarshalException(marshalException, true);
305            throw marshalException;
306        } catch(IndirectionException cdrie)
307            {
308                // The CDR stream had never seen the given offset before,
309                // so check the recursion manager (it will throw an
310                // IOException if it doesn't have a reference, either).
311                return activeRecursionMgr.getObject(cdrie.offset);
312            }
313    }
314
315    final synchronized Object simpleReadObject(Class clz,
316                                  String repositoryID,
317                                  com.sun.org.omg.SendingContext.CodeBase sender,
318                                  int offset)
319                                         /* throws OptionalDataException, ClassNotFoundException, IOException */
320    {
321        /* Save the current state and get ready to read an object. */
322        Object prevObject = currentObject;
323        ObjectStreamClass prevClassDesc = currentClassDesc;
324        Class prevClass = currentClass;
325        byte oldStreamFormatVersion = streamFormatVersion;
326
327        simpleReadDepth++;      // Entering
328        Object obj = null;
329
330        /*
331         * Check for reset, handle it before reading an object.
332         */
333        try {
334            // d4365188: backward compatability
335            if (vhandler.useFullValueDescription(clz, repositoryID)) {
336                obj = inputObjectUsingFVD(clz, repositoryID, sender, offset);
337            } else {
338                obj = inputObject(clz, repositoryID, sender, offset);
339            }
340
341            obj = currentClassDesc.readResolve(obj);
342        }
343        catch(ClassNotFoundException cnfe)
344            {
345                bridge.throwException( cnfe ) ;
346                return null;
347            }
348        catch(IOException ioe)
349            {
350                // System.out.println("CLZ = " + clz + "; " + ioe.toString());
351                bridge.throwException(ioe) ;
352                return null;
353            }
354        finally {
355            simpleReadDepth --;
356            currentObject = prevObject;
357            currentClassDesc = prevClassDesc;
358            currentClass = prevClass;
359            streamFormatVersion = oldStreamFormatVersion;
360        }
361
362
363        /* Check for thrown exceptions and re-throw them, clearing them if
364         * this is the last recursive call .
365         */
366        IOException exIOE = abortIOException;
367        if (simpleReadDepth == 0)
368            abortIOException = null;
369        if (exIOE != null){
370            bridge.throwException( exIOE ) ;
371            return null;
372        }
373
374
375        ClassNotFoundException exCNF = abortClassNotFoundException;
376        if (simpleReadDepth == 0)
377            abortClassNotFoundException = null;
378        if (exCNF != null) {
379            bridge.throwException( exCNF ) ;
380            return null;
381        }
382
383        return obj;
384    }
385
386    public final synchronized  void simpleSkipObject(String repositoryID,
387                                       com.sun.org.omg.SendingContext.CodeBase sender)
388                                       /* throws OptionalDataException, ClassNotFoundException, IOException */
389    {
390
391        /* Save the current state and get ready to read an object. */
392        Object prevObject = currentObject;
393        ObjectStreamClass prevClassDesc = currentClassDesc;
394        Class prevClass = currentClass;
395        byte oldStreamFormatVersion = streamFormatVersion;
396
397        simpleReadDepth++;      // Entering
398        Object obj = null;
399
400        /*
401         * Check for reset, handle it before reading an object.
402         */
403        try {
404            skipObjectUsingFVD(repositoryID, sender);
405        }
406        catch(ClassNotFoundException cnfe)
407            {
408                bridge.throwException( cnfe ) ;
409                return;
410            }
411        catch(IOException ioe)
412            {
413                bridge.throwException( ioe ) ;
414                return;
415            }
416        finally {
417            simpleReadDepth --;
418            streamFormatVersion = oldStreamFormatVersion;
419            currentObject = prevObject;
420            currentClassDesc = prevClassDesc;
421            currentClass = prevClass;
422        }
423
424
425        /* Check for thrown exceptions and re-throw them, clearing them if
426         * this is the last recursive call .
427         */
428        IOException exIOE = abortIOException;
429        if (simpleReadDepth == 0)
430            abortIOException = null;
431        if (exIOE != null){
432            bridge.throwException( exIOE ) ;
433            return;
434        }
435
436
437        ClassNotFoundException exCNF = abortClassNotFoundException;
438        if (simpleReadDepth == 0)
439            abortClassNotFoundException = null;
440        if (exCNF != null) {
441            bridge.throwException( exCNF ) ;
442            return;
443        }
444
445        return;
446    }
447    /////////////////
448
449    /**
450     * This method is called by trusted subclasses of ObjectOutputStream
451     * that constructed ObjectOutputStream using the
452     * protected no-arg constructor. The subclass is expected to provide
453     * an override method with the modifier "final".
454     *
455     * @return the Object read from the stream.
456     *
457     * @see #ObjectInputStream()
458     * @see #readObject
459     * @since JDK 1.2
460     */
461    protected final Object readObjectOverride()
462        throws OptionalDataException, ClassNotFoundException, IOException
463    {
464        return readObjectDelegate();
465    }
466
467    /**
468     * Override the actions of the final method "defaultReadObject()"
469     * in ObjectInputStream.
470     * @since     JDK1.1.6
471     *
472     * Read the non-static and non-transient fields of the current class
473     * from this stream.  This may only be called from the readObject method
474     * of the class being deserialized. It will throw the NotActiveException
475     * if it is called otherwise.
476     *
477     * @exception java.lang.ClassNotFoundException if the class of a serialized
478     *              object could not be found.
479     * @exception IOException        if an I/O error occurs.
480     * @exception NotActiveException if the stream is not currently reading
481     *              objects.
482     * @since     JDK1.1
483     */
484    final synchronized void defaultReadObjectDelegate()
485    /* throws IOException, ClassNotFoundException, NotActiveException */
486    {
487        try {
488            if (currentObject == null || currentClassDesc == null)
489                // XXX I18N, logging needed.
490                throw new NotActiveException("defaultReadObjectDelegate");
491
492            if (!currentClassDesc.forClass().isAssignableFrom(
493                    currentObject.getClass())) {
494                throw new IOException("Object Type mismatch");
495            }
496
497            // The array will be null unless fields were retrieved
498            // remotely because of a serializable version difference.
499            // Bug fix for 4365188.  See the definition of
500            // defaultReadObjectFVDMembers for more information.
501            if (defaultReadObjectFVDMembers != null &&
502                defaultReadObjectFVDMembers.length > 0) {
503
504                // WARNING:  Be very careful!  What if some of
505                // these fields actually have to do this, too?
506                // This works because the defaultReadObjectFVDMembers
507                // reference is passed to inputClassFields, but
508                // there is no guarantee that
509                // defaultReadObjectFVDMembers will point to the
510                // same array after calling inputClassFields.
511
512                // Use the remote fields to unmarshal.
513                inputClassFields(currentObject,
514                                 currentClass,
515                                 currentClassDesc,
516                                 defaultReadObjectFVDMembers,
517                                 cbSender);
518
519            } else {
520
521                // Use the local fields to unmarshal.
522                ObjectStreamField[] fields =
523                    currentClassDesc.getFieldsNoCopy();
524                if (fields.length > 0) {
525                    inputClassFields(currentObject, currentClass, fields, cbSender);
526                }
527            }
528        }
529        catch(NotActiveException nae)
530            {
531                bridge.throwException( nae ) ;
532            }
533        catch(IOException ioe)
534            {
535                bridge.throwException( ioe ) ;
536            }
537        catch(ClassNotFoundException cnfe)
538            {
539                bridge.throwException( cnfe ) ;
540            }
541
542    }
543
544    /**
545     * Override the actions of the final method "enableResolveObject()"
546     * in ObjectInputStream.
547     * @since     JDK1.1.6
548     *
549     * Enable the stream to allow objects read from the stream to be replaced.
550     * If the stream is a trusted class it is allowed to enable replacment.
551     * Trusted classes are those classes with a classLoader equals null. <p>
552     *
553     * When enabled the resolveObject method is called for every object
554     * being deserialized.
555     *
556     * @exception SecurityException The classloader of this stream object is non-null.
557     * @since     JDK1.1
558     */
559    public final boolean enableResolveObjectDelegate(boolean enable)
560    /* throws SecurityException */
561    {
562        return false;
563    }
564
565    // The following three methods allow the implementing orbStream
566    // to provide mark/reset behavior as defined in java.io.InputStream.
567
568    public final void mark(int readAheadLimit) {
569        orbStream.mark(readAheadLimit);
570    }
571
572    public final boolean markSupported() {
573        return orbStream.markSupported();
574    }
575
576    public final void reset() throws IOException {
577        try {
578            orbStream.reset();
579        } catch (Error e) {
580            IOException err = new IOException(e.getMessage());
581            err.initCause(e) ;
582            throw err ;
583        }
584    }
585
586    public final int available() throws IOException{
587        return 0; // unreliable
588    }
589
590    public final void close() throws IOException{
591        // no op
592    }
593
594    public final int read() throws IOException{
595        try{
596            readObjectState.readData(this);
597
598            return (orbStream.read_octet() << 0) & 0x000000FF;
599        } catch (MARSHAL marshalException) {
600            if (marshalException.minor
601                == OMGSystemException.RMIIIOP_OPTIONAL_DATA_INCOMPATIBLE1) {
602                setState(IN_READ_OBJECT_NO_MORE_OPT_DATA);
603                return -1;
604            }
605
606            throw marshalException;
607        } catch(Error e) {
608            IOException exc = new IOException(e.getMessage());
609            exc.initCause(e) ;
610            throw exc ;
611        }
612    }
613
614    public final int read(byte data[], int offset, int length) throws IOException{
615        try{
616            readObjectState.readData(this);
617
618            orbStream.read_octet_array(data, offset, length);
619            return length;
620        } catch (MARSHAL marshalException) {
621            if (marshalException.minor
622                == OMGSystemException.RMIIIOP_OPTIONAL_DATA_INCOMPATIBLE1) {
623                setState(IN_READ_OBJECT_NO_MORE_OPT_DATA);
624                return -1;
625            }
626
627            throw marshalException;
628        } catch(Error e) {
629            IOException exc = new IOException(e.getMessage());
630            exc.initCause(e) ;
631            throw exc ;
632        }
633
634    }
635
636    public final boolean readBoolean() throws IOException{
637        try{
638            readObjectState.readData(this);
639
640            return orbStream.read_boolean();
641        } catch (MARSHAL marshalException) {
642            handleOptionalDataMarshalException(marshalException, false);
643            throw marshalException;
644
645        } catch(Error e) {
646            IOException exc = new IOException(e.getMessage());
647            exc.initCause(e);
648            throw exc ;
649        }
650    }
651
652    public final byte readByte() throws IOException{
653        try{
654            readObjectState.readData(this);
655
656            return orbStream.read_octet();
657        } catch (MARSHAL marshalException) {
658            handleOptionalDataMarshalException(marshalException, false);
659            throw marshalException;
660
661        } catch(Error e) {
662            IOException exc = new IOException(e.getMessage());
663            exc.initCause(e);
664            throw exc ;
665        }
666    }
667
668    public final char readChar() throws IOException{
669        try{
670            readObjectState.readData(this);
671
672            return orbStream.read_wchar();
673        } catch (MARSHAL marshalException) {
674            handleOptionalDataMarshalException(marshalException, false);
675            throw marshalException;
676
677        } catch(Error e) {
678            IOException exc = new IOException(e.getMessage());
679            exc.initCause(e);
680            throw exc ;
681        }
682    }
683
684    public final double readDouble() throws IOException{
685        try{
686            readObjectState.readData(this);
687
688            return orbStream.read_double();
689        } catch (MARSHAL marshalException) {
690            handleOptionalDataMarshalException(marshalException, false);
691            throw marshalException;
692        } catch(Error e) {
693            IOException exc = new IOException(e.getMessage());
694            exc.initCause(e);
695            throw exc ;
696        }
697    }
698
699    public final float readFloat() throws IOException{
700        try{
701            readObjectState.readData(this);
702
703            return orbStream.read_float();
704        } catch (MARSHAL marshalException) {
705            handleOptionalDataMarshalException(marshalException, false);
706            throw marshalException;
707        } catch(Error e) {
708            IOException exc = new IOException(e.getMessage());
709            exc.initCause(e);
710            throw exc ;
711        }
712    }
713
714    public final void readFully(byte data[]) throws IOException{
715// d11623 : implement readFully, required for serializing some core classes
716
717        readFully(data, 0, data.length);
718    }
719
720    public final void readFully(byte data[],  int offset,  int size) throws IOException{
721// d11623 : implement readFully, required for serializing some core classes
722        try{
723            readObjectState.readData(this);
724
725            orbStream.read_octet_array(data, offset, size);
726        } catch (MARSHAL marshalException) {
727            handleOptionalDataMarshalException(marshalException, false);
728
729            throw marshalException;
730        } catch(Error e) {
731            IOException exc = new IOException(e.getMessage());
732            exc.initCause(e);
733            throw exc ;
734        }
735    }
736
737    public final int readInt() throws IOException{
738        try{
739            readObjectState.readData(this);
740
741            return orbStream.read_long();
742        } catch (MARSHAL marshalException) {
743            handleOptionalDataMarshalException(marshalException, false);
744            throw marshalException;
745        } catch(Error e) {
746            IOException exc = new IOException(e.getMessage());
747            exc.initCause(e);
748            throw exc ;
749        }
750    }
751
752    public final String readLine() throws IOException{
753        // XXX I18N, logging needed.
754        throw new IOException("Method readLine not supported");
755    }
756
757    public final long readLong() throws IOException{
758        try{
759            readObjectState.readData(this);
760
761            return orbStream.read_longlong();
762        } catch (MARSHAL marshalException) {
763            handleOptionalDataMarshalException(marshalException, false);
764            throw marshalException;
765        } catch(Error e) {
766            IOException exc = new IOException(e.getMessage());
767            exc.initCause(e);
768            throw exc ;
769        }
770    }
771
772    public final short readShort() throws IOException{
773        try{
774            readObjectState.readData(this);
775
776            return orbStream.read_short();
777        } catch (MARSHAL marshalException) {
778            handleOptionalDataMarshalException(marshalException, false);
779            throw marshalException;
780        } catch(Error e) {
781            IOException exc = new IOException(e.getMessage());
782            exc.initCause(e);
783            throw exc ;
784        }
785    }
786
787    protected final void readStreamHeader() throws IOException, StreamCorruptedException{
788        // no op
789    }
790
791    public final int readUnsignedByte() throws IOException{
792        try{
793            readObjectState.readData(this);
794
795            return (orbStream.read_octet() << 0) & 0x000000FF;
796        } catch (MARSHAL marshalException) {
797            handleOptionalDataMarshalException(marshalException, false);
798            throw marshalException;
799        } catch(Error e) {
800            IOException exc = new IOException(e.getMessage());
801            exc.initCause(e);
802            throw exc ;
803        }
804    }
805
806    public final int readUnsignedShort() throws IOException{
807        try{
808            readObjectState.readData(this);
809
810            return (orbStream.read_ushort() << 0) & 0x0000FFFF;
811        } catch (MARSHAL marshalException) {
812            handleOptionalDataMarshalException(marshalException, false);
813            throw marshalException;
814        } catch(Error e) {
815            IOException exc = new IOException(e.getMessage());
816            exc.initCause(e);
817            throw exc ;
818        }
819    }
820
821    /**
822     * Helper method for correcting the Kestrel bug 4367783 (dealing
823     * with larger than 8-bit chars).  The old behavior is preserved
824     * in orbutil.IIOPInputStream_1_3 in order to interoperate with
825     * our legacy ORBs.
826     */
827    protected String internalReadUTF(org.omg.CORBA.portable.InputStream stream)
828    {
829        return stream.read_wstring();
830    }
831
832    public final String readUTF() throws IOException{
833        try{
834            readObjectState.readData(this);
835
836            return internalReadUTF(orbStream);
837        } catch (MARSHAL marshalException) {
838            handleOptionalDataMarshalException(marshalException, false);
839            throw marshalException;
840        } catch(Error e) {
841            IOException exc = new IOException(e.getMessage());
842            exc.initCause(e);
843            throw exc ;
844        }
845    }
846
847    // If the ORB stream detects an incompatibility between what's
848    // on the wire and what our Serializable's readObject wants,
849    // it throws a MARSHAL exception with a specific minor code.
850    // This is rethrown to the readObject as an OptionalDataException.
851    // So far in RMI-IIOP, this process isn't specific enough to
852    // tell the readObject how much data is available, so we always
853    // set the OptionalDataException's EOF marker to true.
854    private void handleOptionalDataMarshalException(MARSHAL marshalException,
855                                                    boolean objectRead)
856        throws IOException {
857
858        // Java Object Serialization spec 3.4: "If the readObject method
859        // of the class attempts to read more data than is present in the
860        // optional part of the stream for this class, the stream will
861        // return -1 for bytewise reads, throw an EOFException for
862        // primitive data reads, or throw an OptionalDataException
863        // with the eof field set to true for object reads."
864        if (marshalException.minor
865            == OMGSystemException.RMIIIOP_OPTIONAL_DATA_INCOMPATIBLE1) {
866
867            IOException result;
868
869            if (!objectRead)
870                result = new EOFException("No more optional data");
871            else
872                result = bridge.newOptionalDataExceptionForSerialization(true);
873
874            result.initCause(marshalException);
875
876            setState(IN_READ_OBJECT_NO_MORE_OPT_DATA);
877
878            throw result;
879        }
880    }
881
882    public final synchronized void registerValidation(ObjectInputValidation obj,
883                                                      int prio)
884        throws NotActiveException, InvalidObjectException{
885        // XXX I18N, logging needed.
886        throw new Error("Method registerValidation not supported");
887    }
888
889    protected final Class resolveClass(ObjectStreamClass v)
890        throws IOException, ClassNotFoundException{
891        // XXX I18N, logging needed.
892        throw new IOException("Method resolveClass not supported");
893    }
894
895    protected final Object resolveObject(Object obj) throws IOException{
896        // XXX I18N, logging needed.
897        throw new IOException("Method resolveObject not supported");
898    }
899
900    public final int skipBytes(int len) throws IOException{
901        try{
902            readObjectState.readData(this);
903
904            byte buf[] = new byte[len];
905            orbStream.read_octet_array(buf, 0, len);
906            return len;
907        } catch (MARSHAL marshalException) {
908            handleOptionalDataMarshalException(marshalException, false);
909
910            throw marshalException;
911        } catch(Error e) {
912            IOException exc = new IOException(e.getMessage());
913            exc.initCause(e) ;
914            throw exc ;
915        }
916    }
917
918    private synchronized Object inputObject(Class clz,
919                               String repositoryID,
920                               com.sun.org.omg.SendingContext.CodeBase sender,
921                               int offset)
922        throws IOException, ClassNotFoundException
923    {
924
925        /*
926         * Get the descriptor and then class of the incoming object.
927         */
928
929        currentClassDesc = ObjectStreamClass.lookup(clz);
930        currentClass = currentClassDesc.forClass();
931        //currentClassDesc.setClass(currentClass);
932        if (currentClass == null)
933            // XXX I18N, logging needed.
934            throw new ClassNotFoundException(currentClassDesc.getName());
935
936        try {
937            /* If Externalizable,
938             *  Create an instance and tell it to read its data.
939             * else,
940             *  Handle it as a serializable class.
941             */
942            if (Enum.class.isAssignableFrom( clz )) {
943                int ordinal = orbStream.read_long() ;
944                String value = (String)orbStream.read_value( String.class ) ;
945                return Enum.valueOf( clz, value ) ;
946            } else if (currentClassDesc.isExternalizable()) {
947                try {
948                    currentObject = (currentClass == null) ?
949                        null : currentClassDesc.newInstance();
950                    if (currentObject != null) {
951
952                        // Store this object and its beginning position
953                        // since there might be indirections to it while
954                        // it's been unmarshalled.
955                        activeRecursionMgr.addObject(offset, currentObject);
956
957                        // Read format version
958                        readFormatVersion();
959
960                        Externalizable ext = (Externalizable)currentObject;
961                        ext.readExternal(this);
962                }
963            } catch (InvocationTargetException e) {
964                InvalidClassException exc = new InvalidClassException(
965                    currentClass.getName(),
966                    "InvocationTargetException accessing no-arg constructor");
967                exc.initCause( e ) ;
968                throw exc ;
969            } catch (UnsupportedOperationException e) {
970                InvalidClassException exc = new InvalidClassException(
971                    currentClass.getName(),
972                    "UnsupportedOperationException accessing no-arg constructor");
973                exc.initCause( e ) ;
974                throw exc ;
975            } catch (InstantiationException e) {
976                InvalidClassException exc = new InvalidClassException(
977                    currentClass.getName(),
978                    "InstantiationException accessing no-arg constructor");
979                exc.initCause( e ) ;
980                throw exc ;
981            }
982        } // end : if (currentClassDesc.isExternalizable())
983        else {
984            /* Count number of classes and descriptors we might have
985             * to work on.
986             */
987
988            ObjectStreamClass currdesc = currentClassDesc;
989            Class currclass = currentClass;
990
991            int spBase = spClass;       // current top of stack
992
993            if (currentClass.getName().equals("java.lang.String")) {
994                return this.readUTF();
995            }
996            /* The object's classes should be processed from supertype to subtype
997             * Push all the clases of the current object onto a stack.
998             * Note that only the serializable classes are represented
999             * in the descriptor list.
1000             *
1001             * Handle versioning where one or more supertypes of
1002             * have been inserted or removed.  The stack will
1003             * contain pairs of descriptors and the corresponding
1004             * class.  If the object has a class that did not occur in
1005             * the original the descriptor will be null.  If the
1006             * original object had a descriptor for a class not
1007             * present in the local hierarchy of the object the class will be
1008             * null.
1009             *
1010             */
1011
1012            /*
1013             * This is your basic diff pattern, made simpler
1014             * because reordering is not allowed.
1015             */
1016            // sun.4296963 ibm.11861
1017            // d11861 we should stop when we find the highest serializable class
1018            // We need this so that when we allocate the new object below, we
1019            // can call the constructor of the non-serializable superclass.
1020            // Note that in the JRMP variant of this code the
1021            // ObjectStreamClass.lookup() method handles this, but we've put
1022            // this fix here rather than change lookup because the new behaviour
1023            // is needed in other cases.
1024
1025            for (currdesc = currentClassDesc, currclass = currentClass;
1026                 currdesc != null && currdesc.isSerializable();   /*sun.4296963 ibm.11861*/
1027                 currdesc = currdesc.getSuperclass()) {
1028
1029                /*
1030                 * Search the classes to see if the class of this
1031                 * descriptor appears further up the hierarchy. Until
1032                 * it's found assume its an inserted class.  If it's
1033                 * not found, its the descriptor's class that has been
1034                 * removed.
1035                 */
1036                Class cc = currdesc.forClass();
1037                Class cl;
1038                for (cl = currclass; cl != null; cl = cl.getSuperclass()) {
1039                    if (cc == cl) {
1040                        // found a superclass that matches this descriptor
1041                        break;
1042                    } else {
1043                        /* Ignore a class that doesn't match.  No
1044                         * action is needed since it is already
1045                         * initialized.
1046                         */
1047                    }
1048                } // end : for (cl = currclass; cl != null; cl = cl.getSuperclass())
1049                /* Test if there is room for this new entry.
1050                 * If not, double the size of the arrays and copy the contents.
1051                 */
1052                spClass++;
1053                if (spClass >= classes.length) {
1054                    int newlen = classes.length * 2;
1055                    Class[] newclasses = new Class[newlen];
1056                    ObjectStreamClass[] newclassdesc = new ObjectStreamClass[newlen];
1057
1058                    System.arraycopy(classes, 0,
1059                                     newclasses, 0,
1060                                     classes.length);
1061                    System.arraycopy(classdesc, 0,
1062                                     newclassdesc, 0,
1063                                     classes.length);
1064
1065                    classes = newclasses;
1066                    classdesc = newclassdesc;
1067                }
1068
1069                if (cl == null) {
1070                    /* Class not found corresponding to this descriptor.
1071                     * Pop off all the extra classes pushed.
1072                     * Push the descriptor and a null class.
1073                     */
1074                    classdesc[spClass] = currdesc;
1075                    classes[spClass] = null;
1076                } else {
1077                    /* Current class descriptor matches current class.
1078                     * Some classes may have been inserted.
1079                     * Record the match and advance the class, continue
1080                     * with the next descriptor.
1081                     */
1082                    classdesc[spClass] = currdesc;
1083                    classes[spClass] = cl;
1084                    currclass = cl.getSuperclass();
1085                }
1086            } // end : for (currdesc = currentClassDesc, currclass = currentClass;
1087
1088            /* Allocate a new object.  The object is only constructed
1089             * above the highest serializable class and is set to
1090             * default values for all more specialized classes.
1091             */
1092            try {
1093                currentObject = (currentClass == null) ?
1094                    null : currentClassDesc.newInstance() ;
1095
1096                // Store this object and its beginning position
1097                // since there might be indirections to it while
1098                // it's been unmarshalled.
1099                activeRecursionMgr.addObject(offset, currentObject);
1100            } catch (InvocationTargetException e) {
1101                InvalidClassException exc = new InvalidClassException(
1102                    currentClass.getName(),
1103                    "InvocationTargetException accessing no-arg constructor");
1104                exc.initCause( e ) ;
1105                throw exc ;
1106            } catch (UnsupportedOperationException e) {
1107                InvalidClassException exc = new InvalidClassException(
1108                    currentClass.getName(),
1109                    "UnsupportedOperationException accessing no-arg constructor");
1110                exc.initCause( e ) ;
1111                throw exc ;
1112            } catch (InstantiationException e) {
1113                InvalidClassException exc = new InvalidClassException(
1114                    currentClass.getName(),
1115                    "InstantiationException accessing no-arg constructor");
1116                exc.initCause( e ) ;
1117                throw exc ;
1118            }
1119
1120            /*
1121             * For all the pushed descriptors and classes.
1122             *  if the class has its own writeObject and readObject methods
1123             *      call the readObject method
1124             *  else
1125             *      invoke the defaultReadObject method
1126             */
1127            try {
1128                for (spClass = spClass; spClass > spBase; spClass--) {
1129                    /*
1130                     * Set current descriptor and corresponding class
1131                     */
1132                    currentClassDesc = classdesc[spClass];
1133                    currentClass = classes[spClass];
1134                    if (classes[spClass] != null) {
1135                        /* Read the data from the stream described by the
1136                         * descriptor and store into the matching class.
1137                         */
1138
1139                        ReadObjectState oldState = readObjectState;
1140                        setState(DEFAULT_STATE);
1141
1142                        try {
1143
1144                            // Changed since invokeObjectReader no longer does this.
1145                            if (currentClassDesc.hasWriteObject()) {
1146
1147                                // Read format version
1148                                readFormatVersion();
1149
1150                                // Read defaultWriteObject indicator
1151                                boolean calledDefaultWriteObject = readBoolean();
1152
1153                                readObjectState.beginUnmarshalCustomValue(this,
1154                                                                          calledDefaultWriteObject,
1155                                                                          currentClassDesc.hasReadObject());
1156                            } else {
1157                                if (currentClassDesc.hasReadObject())
1158                                    setState(IN_READ_OBJECT_REMOTE_NOT_CUSTOM_MARSHALED);
1159                            }
1160
1161                            if (!invokeObjectReader(currentClassDesc, currentObject, currentClass) ||
1162                                readObjectState == IN_READ_OBJECT_DEFAULTS_SENT) {
1163
1164                                // Error case of no readObject and didn't call
1165                                // defaultWriteObject handled in default state
1166
1167                                ObjectStreamField[] fields =
1168                                    currentClassDesc.getFieldsNoCopy();
1169                                if (fields.length > 0) {
1170                                    inputClassFields(currentObject, currentClass, fields, sender);
1171                                }
1172                            }
1173
1174                            if (currentClassDesc.hasWriteObject())
1175                                readObjectState.endUnmarshalCustomValue(this);
1176
1177                        } finally {
1178                            setState(oldState);
1179                        }
1180
1181                    } else {
1182
1183                        // _REVISIT_ : Can we ever get here?
1184                        /* No local class for this descriptor,
1185                         * Skip over the data for this class.
1186                         * like defaultReadObject with a null currentObject.
1187                         * The code will read the values but discard them.
1188                         */
1189                            ObjectStreamField[] fields =
1190                                currentClassDesc.getFieldsNoCopy();
1191                            if (fields.length > 0) {
1192                                inputClassFields(null, currentClass, fields, sender);
1193                            }
1194
1195                        }
1196
1197                }
1198            } finally {
1199                                // Make sure we exit at the same stack level as when we started.
1200                spClass = spBase;
1201            }
1202        }
1203        } finally {
1204            // We've completed deserializing this object.  Any
1205            // future indirections will be handled correctly at the
1206            // CDR level.  The ActiveRecursionManager only deals with
1207            // objects currently being deserialized.
1208            activeRecursionMgr.removeObject(offset);
1209        }
1210
1211        return currentObject;
1212    }
1213
1214    // This retrieves a vector of FVD's for the hierarchy of serializable classes stemming from
1215    // repositoryID.  It is assumed that the sender will not provide base_value id's for non-serializable
1216    // classes!
1217    private Vector getOrderedDescriptions(String repositoryID,
1218                                          com.sun.org.omg.SendingContext.CodeBase sender) {
1219        Vector descs = new Vector();
1220
1221        if (sender == null) {
1222            return descs;
1223        }
1224
1225        FullValueDescription aFVD = sender.meta(repositoryID);
1226        while (aFVD != null) {
1227            descs.insertElementAt(aFVD, 0);
1228            if ((aFVD.base_value != null) && !kEmptyStr.equals(aFVD.base_value)) {
1229                aFVD = sender.meta(aFVD.base_value);
1230            }
1231            else return descs;
1232        }
1233
1234        return descs;
1235    }
1236
1237    /**
1238     * This input method uses FullValueDescriptions retrieved from the sender's runtime to
1239     * read in the data.  This method is capable of throwing out data not applicable to client's fields.
1240     * This method handles instances where the reader has a class not sent by the sender, the sender sent
1241     * a class not present on the reader, and/or the reader's class does not match the sender's class.
1242     *
1243     * NOTE : If the local description indicates custom marshaling and the remote type's FVD also
1244     * indicates custom marsahling than the local type is used to read the data off the wire.  However,
1245     * if either says custom while the other does not, a MARSHAL error is thrown.  Externalizable is
1246     * a form of custom marshaling.
1247     *
1248     */
1249    private synchronized Object inputObjectUsingFVD(Class clz,
1250                                       String repositoryID,
1251                                       com.sun.org.omg.SendingContext.CodeBase sender,
1252                                       int offset)
1253        throws IOException, ClassNotFoundException
1254    {
1255        int spBase = spClass;   // current top of stack
1256        try{
1257
1258            /*
1259             * Get the descriptor and then class of the incoming object.
1260             */
1261
1262            ObjectStreamClass currdesc = currentClassDesc = ObjectStreamClass.lookup(clz);
1263            Class currclass = currentClass = clz;
1264
1265            /* If Externalizable,
1266             *  Create an instance and tell it to read its data.
1267             * else,
1268             *  Handle it as a serializable class.
1269             */
1270            if (currentClassDesc.isExternalizable()) {
1271                try {
1272                    currentObject = (currentClass == null) ?
1273                        null : currentClassDesc.newInstance();
1274                    if (currentObject != null) {
1275                        // Store this object and its beginning position
1276                        // since there might be indirections to it while
1277                        // it's been unmarshalled.
1278                        activeRecursionMgr.addObject(offset, currentObject);
1279
1280                        // Read format version
1281                        readFormatVersion();
1282
1283                        Externalizable ext = (Externalizable)currentObject;
1284                        ext.readExternal(this);
1285                    }
1286                } catch (InvocationTargetException e) {
1287                    InvalidClassException exc = new InvalidClassException(
1288                        currentClass.getName(),
1289                        "InvocationTargetException accessing no-arg constructor");
1290                    exc.initCause( e ) ;
1291                    throw exc ;
1292                } catch (UnsupportedOperationException e) {
1293                    InvalidClassException exc = new InvalidClassException(
1294                        currentClass.getName(),
1295                        "UnsupportedOperationException accessing no-arg constructor");
1296                    exc.initCause( e ) ;
1297                    throw exc ;
1298                } catch (InstantiationException e) {
1299                    InvalidClassException exc = new InvalidClassException(
1300                        currentClass.getName(),
1301                        "InstantiationException accessing no-arg constructor");
1302                    exc.initCause( e ) ;
1303                    throw exc ;
1304                }
1305            } else {
1306                /*
1307                 * This is your basic diff pattern, made simpler
1308                 * because reordering is not allowed.
1309                 */
1310                for (currdesc = currentClassDesc, currclass = currentClass;
1311                     currdesc != null && currdesc.isSerializable();   /*sun.4296963 ibm.11861*/
1312
1313                     currdesc = currdesc.getSuperclass()) {
1314
1315                    /*
1316                     * Search the classes to see if the class of this
1317                     * descriptor appears further up the hierarchy. Until
1318                     * it's found assume its an inserted class.  If it's
1319                     * not found, its the descriptor's class that has been
1320                     * removed.
1321                     */
1322                    Class cc = currdesc.forClass();
1323                    Class cl;
1324                    for (cl = currclass; cl != null; cl = cl.getSuperclass()) {
1325                        if (cc == cl) {
1326                            // found a superclass that matches this descriptor
1327                            break;
1328                        } else {
1329                            /* Ignore a class that doesn't match.  No
1330                             * action is needed since it is already
1331                             * initialized.
1332                             */
1333                        }
1334                    } // end : for (cl = currclass; cl != null; cl = cl.getSuperclass())
1335                    /* Test if there is room for this new entry.
1336                     * If not, double the size of the arrays and copy the contents.
1337                     */
1338                    spClass++;
1339                    if (spClass >= classes.length) {
1340                        int newlen = classes.length * 2;
1341                        Class[] newclasses = new Class[newlen];
1342                        ObjectStreamClass[] newclassdesc = new ObjectStreamClass[newlen];
1343
1344                        System.arraycopy(classes, 0,
1345                                         newclasses, 0,
1346                                         classes.length);
1347                        System.arraycopy(classdesc, 0,
1348                                         newclassdesc, 0,
1349                                         classes.length);
1350
1351                        classes = newclasses;
1352                        classdesc = newclassdesc;
1353                    }
1354
1355                    if (cl == null) {
1356                        /* Class not found corresponding to this descriptor.
1357                         * Pop off all the extra classes pushed.
1358                         * Push the descriptor and a null class.
1359                         */
1360                        classdesc[spClass] = currdesc;
1361                        classes[spClass] = null;
1362                    } else {
1363                        /* Current class descriptor matches current class.
1364                         * Some classes may have been inserted.
1365                         * Record the match and advance the class, continue
1366                         * with the next descriptor.
1367                         */
1368                        classdesc[spClass] = currdesc;
1369                        classes[spClass] = cl;
1370                        currclass = cl.getSuperclass();
1371                    }
1372                } // end : for (currdesc = currentClassDesc, currclass = currentClass;
1373
1374                /* Allocate a new object.
1375                 */
1376                try {
1377                    currentObject = (currentClass == null) ?
1378                        null : currentClassDesc.newInstance();
1379
1380                    // Store this object and its beginning position
1381                    // since there might be indirections to it while
1382                    // it's been unmarshalled.
1383                    activeRecursionMgr.addObject(offset, currentObject);
1384                } catch (InvocationTargetException e) {
1385                    InvalidClassException exc = new InvalidClassException(
1386                        currentClass.getName(),
1387                        "InvocationTargetException accessing no-arg constructor");
1388                    exc.initCause( e ) ;
1389                    throw exc ;
1390                } catch (UnsupportedOperationException e) {
1391                    InvalidClassException exc = new InvalidClassException(
1392                        currentClass.getName(),
1393                        "UnsupportedOperationException accessing no-arg constructor");
1394                    exc.initCause( e ) ;
1395                    throw exc ;
1396                } catch (InstantiationException e) {
1397                    InvalidClassException exc = new InvalidClassException(
1398                        currentClass.getName(),
1399                        "InstantiationException accessing no-arg constructor");
1400                    exc.initCause( e ) ;
1401                    throw exc ;
1402                }
1403
1404                Enumeration fvdsList = getOrderedDescriptions(repositoryID, sender).elements();
1405
1406                while((fvdsList.hasMoreElements()) && (spClass > spBase)) {
1407                    FullValueDescription fvd = (FullValueDescription)fvdsList.nextElement();
1408                    // d4365188: backward compatability
1409                    String repIDForFVD = vhandler.getClassName(fvd.id);
1410                    String repIDForClass = vhandler.getClassName(vhandler.getRMIRepositoryID(currentClass));
1411
1412                    while ((spClass > spBase) &&
1413                           (!repIDForFVD.equals(repIDForClass))) {
1414                        int pos = findNextClass(repIDForFVD, classes, spClass, spBase);
1415                        if (pos != -1) {
1416                            spClass = pos;
1417                            currclass = currentClass = classes[spClass];
1418                            repIDForClass = vhandler.getClassName(vhandler.getRMIRepositoryID(currentClass));
1419                        }
1420                        else { // Read and throw away one level of the fvdslist
1421
1422                            // This seems to mean that the sender had a superclass that
1423                            // we don't have
1424
1425                            if (fvd.is_custom) {
1426
1427                                readFormatVersion();
1428                                boolean calledDefaultWriteObject = readBoolean();
1429
1430                                if (calledDefaultWriteObject)
1431                                    inputClassFields(null, null, null, fvd.members, sender);
1432
1433                                if (getStreamFormatVersion() == 2) {
1434
1435                                    ((ValueInputStream)getOrbStream()).start_value();
1436                                    ((ValueInputStream)getOrbStream()).end_value();
1437                                }
1438
1439                                // WARNING: If stream format version is 1 and there's
1440                                // optional data, we'll get some form of exception down
1441                                // the line or data corruption.
1442
1443                            } else {
1444
1445                                inputClassFields(null, currentClass, null, fvd.members, sender);
1446                            }
1447
1448                            if (fvdsList.hasMoreElements()){
1449                                fvd = (FullValueDescription)fvdsList.nextElement();
1450                                repIDForFVD = vhandler.getClassName(fvd.id);
1451                            }
1452                            else return currentObject;
1453                        }
1454                    }
1455
1456                    currdesc = currentClassDesc = ObjectStreamClass.lookup(currentClass);
1457
1458                    if (!repIDForClass.equals("java.lang.Object")) {
1459
1460                        // If the sender used custom marshaling, then it should have put
1461                        // the two bytes on the wire indicating stream format version
1462                        // and whether or not the writeObject method called
1463                        // defaultWriteObject/writeFields.
1464
1465                        ReadObjectState oldState = readObjectState;
1466                        setState(DEFAULT_STATE);
1467
1468                        try {
1469
1470                            if (fvd.is_custom) {
1471
1472                                // Read format version
1473                                readFormatVersion();
1474
1475                                // Read defaultWriteObject indicator
1476                                boolean calledDefaultWriteObject = readBoolean();
1477
1478                                readObjectState.beginUnmarshalCustomValue(this,
1479                                                                          calledDefaultWriteObject,
1480                                                                          currentClassDesc.hasReadObject());
1481                            }
1482
1483                            boolean usedReadObject = false;
1484
1485                            // Always use readObject if it exists, and fall back to default
1486                            // unmarshaling if it doesn't.
1487                            try {
1488
1489                                if (!fvd.is_custom && currentClassDesc.hasReadObject())
1490                                    setState(IN_READ_OBJECT_REMOTE_NOT_CUSTOM_MARSHALED);
1491
1492                                // See the definition of defaultReadObjectFVDMembers
1493                                // for more information.  This concerns making sure
1494                                // we use the remote FVD's members in defaultReadObject.
1495                                defaultReadObjectFVDMembers = fvd.members;
1496                                usedReadObject = invokeObjectReader(currentClassDesc,
1497                                                                    currentObject,
1498                                                                    currentClass);
1499
1500                            } finally {
1501                                defaultReadObjectFVDMembers = null;
1502                            }
1503
1504                            // Note that the !usedReadObject !calledDefaultWriteObject
1505                            // case is handled by the beginUnmarshalCustomValue method
1506                            // of the default state
1507                            if (!usedReadObject || readObjectState == IN_READ_OBJECT_DEFAULTS_SENT)
1508                                inputClassFields(currentObject, currentClass, currdesc, fvd.members, sender);
1509
1510                            if (fvd.is_custom)
1511                                readObjectState.endUnmarshalCustomValue(this);
1512
1513                        } finally {
1514                            setState(oldState);
1515                        }
1516
1517                        currclass = currentClass = classes[--spClass];
1518
1519                    } else {
1520
1521                        // The remaining hierarchy of the local class does not match the sender's FVD.
1522                        // So, use remaining FVDs to read data off wire.  If any remaining FVDs indicate
1523                        // custom marshaling, throw MARSHAL error.
1524                        inputClassFields(null, currentClass, null, fvd.members, sender);
1525
1526                        while (fvdsList.hasMoreElements()){
1527                            fvd = (FullValueDescription)fvdsList.nextElement();
1528
1529                            if (fvd.is_custom)
1530                                skipCustomUsingFVD(fvd.members, sender);
1531                            else
1532                                inputClassFields(null, currentClass, null, fvd.members, sender);
1533                        }
1534
1535                    }
1536
1537                } // end : while(fvdsList.hasMoreElements())
1538                while (fvdsList.hasMoreElements()){
1539
1540                    FullValueDescription fvd = (FullValueDescription)fvdsList.nextElement();
1541                    if (fvd.is_custom)
1542                        skipCustomUsingFVD(fvd.members, sender);
1543                    else
1544                        throwAwayData(fvd.members, sender);
1545                }
1546            }
1547
1548            return currentObject;
1549        }
1550        finally {
1551                // Make sure we exit at the same stack level as when we started.
1552                spClass = spBase;
1553
1554                // We've completed deserializing this object.  Any
1555                // future indirections will be handled correctly at the
1556                // CDR level.  The ActiveRecursionManager only deals with
1557                // objects currently being deserialized.
1558                activeRecursionMgr.removeObject(offset);
1559            }
1560
1561        }
1562
1563    /**
1564     * This input method uses FullValueDescriptions retrieved from the sender's runtime to
1565     * read in the data.  This method is capable of throwing out data not applicable to client's fields.
1566     *
1567     * NOTE : If the local description indicates custom marshaling and the remote type's FVD also
1568     * indicates custom marsahling than the local type is used to read the data off the wire.  However,
1569     * if either says custom while the other does not, a MARSHAL error is thrown.  Externalizable is
1570     * a form of custom marshaling.
1571     *
1572     */
1573    private Object skipObjectUsingFVD(String repositoryID,
1574                                      com.sun.org.omg.SendingContext.CodeBase sender)
1575        throws IOException, ClassNotFoundException
1576    {
1577
1578        Enumeration fvdsList = getOrderedDescriptions(repositoryID, sender).elements();
1579
1580        while(fvdsList.hasMoreElements()) {
1581            FullValueDescription fvd = (FullValueDescription)fvdsList.nextElement();
1582            String repIDForFVD = vhandler.getClassName(fvd.id);
1583
1584            if (!repIDForFVD.equals("java.lang.Object")) {
1585                if (fvd.is_custom) {
1586
1587                    readFormatVersion();
1588
1589                    boolean calledDefaultWriteObject = readBoolean();
1590
1591                    if (calledDefaultWriteObject)
1592                        inputClassFields(null, null, null, fvd.members, sender);
1593
1594                    if (getStreamFormatVersion() == 2) {
1595
1596                        ((ValueInputStream)getOrbStream()).start_value();
1597                        ((ValueInputStream)getOrbStream()).end_value();
1598                    }
1599
1600                    // WARNING: If stream format version is 1 and there's
1601                    // optional data, we'll get some form of exception down
1602                    // the line.
1603
1604                } else {
1605                    // Use default marshaling
1606                    inputClassFields(null, null, null, fvd.members, sender);
1607                }
1608            }
1609
1610        } // end : while(fvdsList.hasMoreElements())
1611        return null;
1612
1613    }
1614
1615    ///////////////////
1616
1617    private int findNextClass(String classname, Class classes[], int _spClass, int _spBase){
1618
1619        for (int i = _spClass; i > _spBase; i--){
1620            if (classname.equals(classes[i].getName())) {
1621                return i;
1622            }
1623        }
1624
1625        return -1;
1626    }
1627
1628    /*
1629     * Invoke the readObject method if present.  Assumes that in the case of custom
1630     * marshaling, the format version and defaultWriteObject indicator were already
1631     * removed.
1632     */
1633    private boolean invokeObjectReader(ObjectStreamClass osc, Object obj, Class aclass)
1634        throws InvalidClassException, StreamCorruptedException,
1635               ClassNotFoundException, IOException
1636    {
1637        try {
1638            return osc.invokeReadObject( obj, this ) ;
1639        } catch (InvocationTargetException e) {
1640            Throwable t = e.getTargetException();
1641            if (t instanceof ClassNotFoundException)
1642                throw (ClassNotFoundException)t;
1643            else if (t instanceof IOException)
1644                throw (IOException)t;
1645            else if (t instanceof RuntimeException)
1646                throw (RuntimeException) t;
1647            else if (t instanceof Error)
1648                throw (Error) t;
1649            else
1650                // XXX I18N, logging needed.
1651                throw new Error("internal error");
1652        }
1653    }
1654
1655    /*
1656     * Reset the stream to be just like it was after the constructor.
1657     */
1658    private void resetStream() throws IOException {
1659
1660        if (classes == null)
1661            classes = new Class[20];
1662        else {
1663            for (int i = 0; i < classes.length; i++)
1664                classes[i] = null;
1665        }
1666        if (classdesc == null)
1667            classdesc = new ObjectStreamClass[20];
1668        else {
1669            for (int i = 0; i < classdesc.length; i++)
1670                classdesc[i] = null;
1671        }
1672        spClass = 0;
1673
1674        if (callbacks != null)
1675            callbacks.setSize(0);       // discard any pending callbacks
1676    }
1677
1678    /**
1679     * Factored out of inputClassFields  This reads a primitive value and sets it
1680     * in the field of o described by the ObjectStreamField field.
1681     *
1682     * Note that reflection cannot be used here, because reflection cannot be used
1683     * to set final fields.
1684     */
1685    private void inputPrimitiveField(Object o, Class cl, ObjectStreamField field)
1686        throws InvalidClassException, IOException {
1687
1688        try {
1689            switch (field.getTypeCode()) {
1690                case 'B':
1691                    byte byteValue = orbStream.read_octet();
1692                    if (field.getField() != null) {
1693                        bridge.putByte( o, field.getFieldID(), byteValue ) ;
1694                        //reflective code: field.getField().setByte( o, byteValue ) ;
1695                    }
1696                    break;
1697                case 'Z':
1698                    boolean booleanValue = orbStream.read_boolean();
1699                    if (field.getField() != null) {
1700                        bridge.putBoolean( o, field.getFieldID(), booleanValue ) ;
1701                        //reflective code: field.getField().setBoolean( o, booleanValue ) ;
1702                    }
1703                    break;
1704                case 'C':
1705                    char charValue = orbStream.read_wchar();
1706                    if (field.getField() != null) {
1707                        bridge.putChar( o, field.getFieldID(), charValue ) ;
1708                        //reflective code: field.getField().setChar( o, charValue ) ;
1709                    }
1710                    break;
1711                case 'S':
1712                    short shortValue = orbStream.read_short();
1713                    if (field.getField() != null) {
1714                        bridge.putShort( o, field.getFieldID(), shortValue ) ;
1715                        //reflective code: field.getField().setShort( o, shortValue ) ;
1716                    }
1717                    break;
1718                case 'I':
1719                    int intValue = orbStream.read_long();
1720                    if (field.getField() != null) {
1721                        bridge.putInt( o, field.getFieldID(), intValue ) ;
1722                        //reflective code: field.getField().setInt( o, intValue ) ;
1723                    }
1724                    break;
1725                case 'J':
1726                    long longValue = orbStream.read_longlong();
1727                    if (field.getField() != null) {
1728                        bridge.putLong( o, field.getFieldID(), longValue ) ;
1729                        //reflective code: field.getField().setLong( o, longValue ) ;
1730                    }
1731                    break;
1732                case 'F' :
1733                    float floatValue = orbStream.read_float();
1734                    if (field.getField() != null) {
1735                        bridge.putFloat( o, field.getFieldID(), floatValue ) ;
1736                        //reflective code: field.getField().setFloat( o, floatValue ) ;
1737                    }
1738                    break;
1739                case 'D' :
1740                    double doubleValue = orbStream.read_double();
1741                    if (field.getField() != null) {
1742                        bridge.putDouble( o, field.getFieldID(), doubleValue ) ;
1743                        //reflective code: field.getField().setDouble( o, doubleValue ) ;
1744                    }
1745                    break;
1746                default:
1747                    // XXX I18N, logging needed.
1748                    throw new InvalidClassException(cl.getName());
1749            }
1750        } catch (IllegalArgumentException e) {
1751            /* This case should never happen. If the field types
1752               are not the same, InvalidClassException is raised when
1753               matching the local class to the serialized ObjectStreamClass. */
1754            ClassCastException cce = new ClassCastException("Assigning instance of class " +
1755                                         field.getType().getName() +
1756                                         " to field " +
1757                                         currentClassDesc.getName() + '#' +
1758                                         field.getField().getName());
1759            cce.initCause( e ) ;
1760            throw cce ;
1761        }
1762     }
1763
1764    private Object inputObjectField(org.omg.CORBA.ValueMember field,
1765                                    com.sun.org.omg.SendingContext.CodeBase sender)
1766        throws IndirectionException, ClassNotFoundException, IOException,
1767               StreamCorruptedException {
1768
1769        Object objectValue = null;
1770        Class type = null;
1771        String id = field.id;
1772
1773        try {
1774            type = vhandler.getClassFromType(id);
1775        } catch(ClassNotFoundException cnfe) {
1776            // Make sure type = null
1777            type = null;
1778        }
1779
1780        String signature = null;
1781        if (type != null)
1782            signature = ValueUtility.getSignature(field);
1783
1784        if (signature != null && (signature.equals("Ljava/lang/Object;") ||
1785                                  signature.equals("Ljava/io/Serializable;") ||
1786                                  signature.equals("Ljava/io/Externalizable;"))) {
1787            objectValue = javax.rmi.CORBA.Util.readAny(orbStream);
1788        } else {
1789            // Decide what method call to make based on the type. If
1790            // it is a type for which we need to load a stub, convert
1791            // the type to the correct stub type.
1792            //
1793            // NOTE : Since FullValueDescription does not allow us
1794            // to ask whether something is an interface we do not
1795            // have the ability to optimize this check.
1796
1797            int callType = ValueHandlerImpl.kValueType;
1798
1799            if (!vhandler.isSequence(id)) {
1800
1801                if (field.type.kind().value() == kRemoteTypeCode.kind().value()) {
1802
1803                    // RMI Object reference...
1804                    callType = ValueHandlerImpl.kRemoteType;
1805
1806                } else {
1807
1808                    // REVISIT.  If we don't have the local class,
1809                    // we should probably verify that it's an RMI type,
1810                    // query the remote FVD, and use is_abstract.
1811                    // Our FVD seems to get NullPointerExceptions for any
1812                    // non-RMI types.
1813
1814                    // This uses the local class in the same way as
1815                    // inputObjectField(ObjectStreamField) does.  REVISIT
1816                    // inputObjectField(ObjectStreamField)'s loadStubClass
1817                    // logic.  Assumption is that the given type cannot
1818                    // evolve to become a CORBA abstract interface or
1819                    // a RMI abstract interface.
1820
1821                    if (type != null && type.isInterface() &&
1822                        (vhandler.isAbstractBase(type) ||
1823                         ObjectStreamClassCorbaExt.isAbstractInterface(type))) {
1824
1825                        callType = ValueHandlerImpl.kAbstractType;
1826                    }
1827                }
1828            }
1829
1830            // Now that we have used the FVD of the field to determine the proper course
1831            // of action, it is ok to use the type (Class) from this point forward since
1832            // the rep. id for this read will also follow on the wire.
1833
1834            switch (callType) {
1835                case ValueHandlerImpl.kRemoteType:
1836                    if (type != null)
1837                        objectValue = Utility.readObjectAndNarrow(orbStream, type);
1838                    else
1839                        objectValue = orbStream.read_Object();
1840                    break;
1841                case ValueHandlerImpl.kAbstractType:
1842                    if (type != null)
1843                        objectValue = Utility.readAbstractAndNarrow(orbStream, type);
1844                    else
1845                        objectValue = orbStream.read_abstract_interface();
1846                    break;
1847                case ValueHandlerImpl.kValueType:
1848                    if (type != null)
1849                        objectValue = orbStream.read_value(type);
1850                    else
1851                                            objectValue = orbStream.read_value();
1852                    break;
1853                default:
1854                    // XXX I18N, logging needed.
1855                    throw new StreamCorruptedException("Unknown callType: " + callType);
1856            }
1857        }
1858
1859        return objectValue;
1860    }
1861
1862    /**
1863     * Factored out of inputClassFields and reused in
1864     * inputCurrentClassFieldsForReadFields.
1865     *
1866     * Reads the field (which of an Object type as opposed to a primitive)
1867     * described by ObjectStreamField field and returns it.
1868     */
1869    private Object inputObjectField(ObjectStreamField field)
1870        throws InvalidClassException, StreamCorruptedException,
1871               ClassNotFoundException, IndirectionException, IOException {
1872
1873        if (ObjectStreamClassCorbaExt.isAny(field.getTypeString())) {
1874            return javax.rmi.CORBA.Util.readAny(orbStream);
1875        }
1876
1877        Object objectValue = null;
1878
1879        // fields have an API to provide the actual class
1880        // corresponding to the data type
1881        // Class type = osc.forClass();
1882        Class fieldType = field.getType();
1883        Class actualType = fieldType; // This may change if stub loaded.
1884
1885        // Decide what method call to make based on the fieldType. If
1886        // it is a type for which we need to load a stub, convert
1887        // the type to the correct stub type.
1888
1889        int callType = ValueHandlerImpl.kValueType;
1890        boolean narrow = false;
1891
1892        if (fieldType.isInterface()) {
1893            boolean loadStubClass = false;
1894
1895            if (java.rmi.Remote.class.isAssignableFrom(fieldType)) {
1896
1897                // RMI Object reference...
1898                callType = ValueHandlerImpl.kRemoteType;
1899
1900            } else if (org.omg.CORBA.Object.class.isAssignableFrom(fieldType)){
1901
1902                // IDL Object reference...
1903                callType = ValueHandlerImpl.kRemoteType;
1904                loadStubClass = true;
1905
1906            } else if (vhandler.isAbstractBase(fieldType)) {
1907                // IDL Abstract Object reference...
1908
1909                callType = ValueHandlerImpl.kAbstractType;
1910                loadStubClass = true;
1911            } else if (ObjectStreamClassCorbaExt.isAbstractInterface(fieldType)) {
1912                // RMI Abstract Object reference...
1913
1914                callType = ValueHandlerImpl.kAbstractType;
1915            }
1916
1917            if (loadStubClass) {
1918                try {
1919                    String codebase = Util.getCodebase(fieldType);
1920                    String repID = vhandler.createForAnyType(fieldType);
1921                    Class stubType =
1922                        Utility.loadStubClass(repID, codebase, fieldType);
1923                    actualType = stubType;
1924                } catch (ClassNotFoundException e) {
1925                    narrow = true;
1926                }
1927            } else {
1928                narrow = true;
1929            }
1930        }
1931
1932        switch (callType) {
1933            case ValueHandlerImpl.kRemoteType:
1934                if (!narrow)
1935                    objectValue = (Object)orbStream.read_Object(actualType);
1936                else
1937                    objectValue = Utility.readObjectAndNarrow(orbStream, actualType);
1938                break;
1939            case ValueHandlerImpl.kAbstractType:
1940                if (!narrow)
1941                    objectValue = (Object)orbStream.read_abstract_interface(actualType);
1942                else
1943                    objectValue = Utility.readAbstractAndNarrow(orbStream, actualType);
1944                break;
1945            case ValueHandlerImpl.kValueType:
1946                objectValue = (Object)orbStream.read_value(actualType);
1947                break;
1948            default:
1949                // XXX I18N, logging needed.
1950                throw new StreamCorruptedException("Unknown callType: " + callType);
1951        }
1952
1953        return objectValue;
1954    }
1955
1956    private final boolean mustUseRemoteValueMembers() {
1957        return defaultReadObjectFVDMembers != null;
1958    }
1959
1960    void readFields(java.util.Map fieldToValueMap)
1961        throws InvalidClassException, StreamCorruptedException,
1962               ClassNotFoundException, IOException {
1963
1964        if (mustUseRemoteValueMembers()) {
1965            inputRemoteMembersForReadFields(fieldToValueMap);
1966        } else
1967            inputCurrentClassFieldsForReadFields(fieldToValueMap);
1968    }
1969
1970    private final void inputRemoteMembersForReadFields(java.util.Map fieldToValueMap)
1971        throws InvalidClassException, StreamCorruptedException,
1972               ClassNotFoundException, IOException {
1973
1974        // Must have this local variable since defaultReadObjectFVDMembers
1975        // may get mangled by recursion.
1976        ValueMember fields[] = defaultReadObjectFVDMembers;
1977
1978        try {
1979
1980            for (int i = 0; i < fields.length; i++) {
1981
1982                switch (fields[i].type.kind().value()) {
1983
1984                case TCKind._tk_octet:
1985                    byte byteValue = orbStream.read_octet();
1986                    fieldToValueMap.put(fields[i].name, new Byte(byteValue));
1987                    break;
1988                case TCKind._tk_boolean:
1989                    boolean booleanValue = orbStream.read_boolean();
1990                    fieldToValueMap.put(fields[i].name, new Boolean(booleanValue));
1991                    break;
1992                case TCKind._tk_char:
1993                    // Backwards compatibility.  Older Sun ORBs sent
1994                    // _tk_char even though they read and wrote wchars
1995                    // correctly.
1996                    //
1997                    // Fall through to the _tk_wchar case.
1998                case TCKind._tk_wchar:
1999                    char charValue = orbStream.read_wchar();
2000                    fieldToValueMap.put(fields[i].name, new Character(charValue));
2001                    break;
2002                case TCKind._tk_short:
2003                    short shortValue = orbStream.read_short();
2004                    fieldToValueMap.put(fields[i].name, new Short(shortValue));
2005                    break;
2006                case TCKind._tk_long:
2007                    int intValue = orbStream.read_long();
2008                    fieldToValueMap.put(fields[i].name, new Integer(intValue));
2009                    break;
2010                case TCKind._tk_longlong:
2011                    long longValue = orbStream.read_longlong();
2012                    fieldToValueMap.put(fields[i].name, new Long(longValue));
2013                    break;
2014                case TCKind._tk_float:
2015                    float floatValue = orbStream.read_float();
2016                    fieldToValueMap.put(fields[i].name, new Float(floatValue));
2017                    break;
2018                case TCKind._tk_double:
2019                    double doubleValue = orbStream.read_double();
2020                    fieldToValueMap.put(fields[i].name, new Double(doubleValue));
2021                    break;
2022                case TCKind._tk_value:
2023                case TCKind._tk_objref:
2024                case TCKind._tk_value_box:
2025                    Object objectValue = null;
2026                    try {
2027                        objectValue = inputObjectField(fields[i],
2028                                                       cbSender);
2029
2030                    } catch (IndirectionException cdrie) {
2031                        // The CDR stream had never seen the given offset before,
2032                        // so check the recursion manager (it will throw an
2033                        // IOException if it doesn't have a reference, either).
2034                        objectValue = activeRecursionMgr.getObject(cdrie.offset);
2035                    }
2036
2037                    fieldToValueMap.put(fields[i].name, objectValue);
2038                    break;
2039                default:
2040                    // XXX I18N, logging needed.
2041                    throw new StreamCorruptedException("Unknown kind: "
2042                                                       + fields[i].type.kind().value());
2043                }
2044            }
2045        } catch (Throwable t) {
2046            StreamCorruptedException result = new StreamCorruptedException(t.getMessage());
2047            result.initCause(t);
2048            throw result;
2049        }
2050    }
2051
2052    /**
2053     * Called from InputStreamHook.
2054     *
2055     * Reads the fields of the current class (could be the ones
2056     * queried from the remote FVD) and puts them in
2057     * the given Map, name to value.  Wraps primitives in the
2058     * corresponding java.lang Objects.
2059     */
2060    private final void inputCurrentClassFieldsForReadFields(java.util.Map fieldToValueMap)
2061        throws InvalidClassException, StreamCorruptedException,
2062               ClassNotFoundException, IOException {
2063
2064        ObjectStreamField[] fields = currentClassDesc.getFieldsNoCopy();
2065
2066        int primFields = fields.length - currentClassDesc.objFields;
2067
2068        // Handle the primitives first
2069        for (int i = 0; i < primFields; ++i) {
2070
2071            switch (fields[i].getTypeCode()) {
2072                case 'B':
2073                    byte byteValue = orbStream.read_octet();
2074                    fieldToValueMap.put(fields[i].getName(),
2075                                        new Byte(byteValue));
2076                    break;
2077                case 'Z':
2078                   boolean booleanValue = orbStream.read_boolean();
2079                   fieldToValueMap.put(fields[i].getName(),
2080                                       new Boolean(booleanValue));
2081                   break;
2082                case 'C':
2083                    char charValue = orbStream.read_wchar();
2084                    fieldToValueMap.put(fields[i].getName(),
2085                                        new Character(charValue));
2086                    break;
2087                case 'S':
2088                    short shortValue = orbStream.read_short();
2089                    fieldToValueMap.put(fields[i].getName(),
2090                                        new Short(shortValue));
2091                    break;
2092                case 'I':
2093                    int intValue = orbStream.read_long();
2094                    fieldToValueMap.put(fields[i].getName(),
2095                                        new Integer(intValue));
2096                    break;
2097                case 'J':
2098                    long longValue = orbStream.read_longlong();
2099                    fieldToValueMap.put(fields[i].getName(),
2100                                        new Long(longValue));
2101                    break;
2102                case 'F' :
2103                    float floatValue = orbStream.read_float();
2104                    fieldToValueMap.put(fields[i].getName(),
2105                                        new Float(floatValue));
2106                    break;
2107                case 'D' :
2108                    double doubleValue = orbStream.read_double();
2109                    fieldToValueMap.put(fields[i].getName(),
2110                                        new Double(doubleValue));
2111                    break;
2112                default:
2113                    // XXX I18N, logging needed.
2114                    throw new InvalidClassException(currentClassDesc.getName());
2115            }
2116        }
2117
2118        /* Read and set object fields from the input stream. */
2119        if (currentClassDesc.objFields > 0) {
2120            for (int i = primFields; i < fields.length; i++) {
2121                Object objectValue = null;
2122                try {
2123                    objectValue = inputObjectField(fields[i]);
2124                } catch(IndirectionException cdrie) {
2125                    // The CDR stream had never seen the given offset before,
2126                    // so check the recursion manager (it will throw an
2127                    // IOException if it doesn't have a reference, either).
2128                    objectValue = activeRecursionMgr.getObject(cdrie.offset);
2129                }
2130
2131                fieldToValueMap.put(fields[i].getName(), objectValue);
2132            }
2133        }
2134    }
2135
2136    /*
2137     * Read the fields of the specified class from the input stream and set
2138     * the values of the fields in the specified object. If the specified
2139     * object is null, just consume the fields without setting any values. If
2140     * any ObjectStreamField does not have a reflected Field, don't try to set
2141     * that field in the object.
2142     *
2143     * REVISIT -- This code doesn't do what the comment says to when
2144     * getField() is null!
2145     */
2146    private void inputClassFields(Object o, Class<?> cl,
2147                                  ObjectStreamField[] fields,
2148                                  com.sun.org.omg.SendingContext.CodeBase sender)
2149        throws InvalidClassException, StreamCorruptedException,
2150               ClassNotFoundException, IOException
2151    {
2152
2153        int primFields = fields.length - currentClassDesc.objFields;
2154
2155        // this will leave primitives in the inputstream
2156        // should really consume and discard where necessary
2157        if (o != null) {
2158            for (int i = 0; i < primFields; ++i) {
2159                inputPrimitiveField(o, cl, fields[i]);
2160            }
2161        }
2162
2163        /* Read and set object fields from the input stream. */
2164        if (currentClassDesc.objFields > 0) {
2165            for (int i = primFields; i < fields.length; i++) {
2166                Object objectValue = null;
2167
2168                try {
2169                    objectValue = inputObjectField(fields[i]);
2170                } catch(IndirectionException cdrie) {
2171                    // The CDR stream had never seen the given offset before,
2172                    // so check the recursion manager (it will throw an
2173                    // IOException if it doesn't have a reference, either).
2174                    objectValue = activeRecursionMgr.getObject(cdrie.offset);
2175                }
2176
2177                if ((o == null) || (fields[i].getField() == null)) {
2178                    continue;
2179                }
2180
2181                try {
2182                    Class<?> fieldCl = fields[i].getClazz();
2183                    if ((objectValue != null)
2184                            && (!fieldCl.isAssignableFrom(
2185                                    objectValue.getClass()))) {
2186                        throw new IllegalArgumentException("Field mismatch");
2187                    }
2188                    Field declaredClassField = null;
2189                    final String inputStreamFieldName = fields[i].getName();
2190                    try {
2191                        declaredClassField = getDeclaredField( cl, inputStreamFieldName);
2192                    } catch (PrivilegedActionException paEx) {
2193                        throw new IllegalArgumentException(
2194                            (NoSuchFieldException) paEx.getException());
2195                    } catch (SecurityException secEx) {
2196                        throw new IllegalArgumentException(secEx);
2197                    } catch (NullPointerException npEx) {
2198                        continue;
2199                    } catch (NoSuchFieldException e) {
2200                        continue;
2201                    }
2202
2203                    if (declaredClassField == null) {
2204                        continue;
2205                    }
2206
2207                    Class<?> declaredFieldClass = declaredClassField.getType();
2208
2209                    // check input field type is a declared field type
2210                    // input field is a subclass of the declared field
2211                    if (!declaredFieldClass.isAssignableFrom(fieldCl)) {
2212                        throw new IllegalArgumentException(
2213                                "Field Type mismatch");
2214                    }
2215                    if (objectValue != null && !fieldCl.isInstance(objectValue)) {
2216                        throw new IllegalArgumentException();
2217                    }
2218                    bridge.putObject( o, fields[i].getFieldID(), objectValue ) ;
2219                    // reflective code: fields[i].getField().set( o, objectValue ) ;
2220                } catch (IllegalArgumentException iaEx) {
2221                    String objectValueClassName = "null";
2222                    String currentClassDescClassName = "null";
2223                    String fieldName = "null";
2224                    if (objectValue != null) {
2225                        objectValueClassName = objectValue.getClass().getName();
2226                    }
2227                    if (currentClassDesc != null) {
2228                        currentClassDescClassName = currentClassDesc.getName();
2229                    }
2230                    if (fields[i] != null && fields[i].getField() != null) {
2231                        fieldName = fields[i].getField().getName();
2232                    }
2233                    ClassCastException ccEx = new ClassCastException(
2234                            "Assigning instance of class " + objectValueClassName
2235                                    + " to field " + currentClassDescClassName + '#' + fieldName);
2236                    ccEx.initCause( iaEx ) ;
2237                    throw ccEx ;
2238                }
2239            } // end : for loop
2240            }
2241        }
2242
2243    /*
2244     * Read the fields of the specified class from the input stream and set
2245     * the values of the fields in the specified object. If the specified
2246     * object is null, just consume the fields without setting any values. If
2247     * any ObjectStreamField does not have a reflected Field, don't try to set
2248     * that field in the object.
2249     */
2250    private void inputClassFields(Object o, Class cl,
2251                                  ObjectStreamClass osc,
2252                                  ValueMember[] fields,
2253                                  com.sun.org.omg.SendingContext.CodeBase sender)
2254        throws InvalidClassException, StreamCorruptedException,
2255               ClassNotFoundException, IOException
2256    {
2257        try{
2258            for (int i = 0; i < fields.length; ++i) {
2259                try {
2260                    switch (fields[i].type.kind().value()) {
2261                    case TCKind._tk_octet:
2262                        byte byteValue = orbStream.read_octet();
2263                        if ((o != null) && osc.hasField(fields[i]))
2264                        setByteField(o, cl, fields[i].name, byteValue);
2265                        break;
2266                    case TCKind._tk_boolean:
2267                        boolean booleanValue = orbStream.read_boolean();
2268                        if ((o != null) && osc.hasField(fields[i]))
2269                        setBooleanField(o, cl, fields[i].name, booleanValue);
2270                        break;
2271                    case TCKind._tk_char:
2272                        // Backwards compatibility.  Older Sun ORBs sent
2273                        // _tk_char even though they read and wrote wchars
2274                        // correctly.
2275                        //
2276                        // Fall through to the _tk_wchar case.
2277                    case TCKind._tk_wchar:
2278                        char charValue = orbStream.read_wchar();
2279                        if ((o != null) && osc.hasField(fields[i]))
2280                        setCharField(o, cl, fields[i].name, charValue);
2281                        break;
2282                    case TCKind._tk_short:
2283                        short shortValue = orbStream.read_short();
2284                        if ((o != null) && osc.hasField(fields[i]))
2285                        setShortField(o, cl, fields[i].name, shortValue);
2286                        break;
2287                    case TCKind._tk_long:
2288                        int intValue = orbStream.read_long();
2289                        if ((o != null) && osc.hasField(fields[i]))
2290                        setIntField(o, cl, fields[i].name, intValue);
2291                        break;
2292                    case TCKind._tk_longlong:
2293                        long longValue = orbStream.read_longlong();
2294                        if ((o != null) && osc.hasField(fields[i]))
2295                        setLongField(o, cl, fields[i].name, longValue);
2296                        break;
2297                    case TCKind._tk_float:
2298                        float floatValue = orbStream.read_float();
2299                        if ((o != null) && osc.hasField(fields[i]))
2300                        setFloatField(o, cl, fields[i].name, floatValue);
2301                        break;
2302                    case TCKind._tk_double:
2303                        double doubleValue = orbStream.read_double();
2304                        if ((o != null) && osc.hasField(fields[i]))
2305                        setDoubleField(o, cl, fields[i].name, doubleValue);
2306                        break;
2307                    case TCKind._tk_value:
2308                    case TCKind._tk_objref:
2309                    case TCKind._tk_value_box:
2310                        Object objectValue = null;
2311                        try {
2312                            objectValue = inputObjectField(fields[i], sender);
2313                        } catch (IndirectionException cdrie) {
2314                            // The CDR stream had never seen the given offset before,
2315                            // so check the recursion manager (it will throw an
2316                            // IOException if it doesn't have a reference, either).
2317                            objectValue = activeRecursionMgr.getObject(cdrie.offset);
2318                        }
2319
2320                        if (o == null)
2321                            continue;
2322                        try {
2323                            if (osc.hasField(fields[i])){
2324                                setObjectField(o,
2325                                               cl,
2326                                               fields[i].name,
2327                                               objectValue);
2328                            } else {
2329                                // REVISIT.  Convert to a log message.
2330                                // This is a normal case when fields have
2331                                // been added as part of evolution, but
2332                                // silently skipping can make it hard to
2333                                // debug if there's an error
2334//                                 System.out.println("**** warning, not setting field: "
2335//                                                    + fields[i].name
2336//                                                    + " since not on class "
2337//                                                    + osc.getName());
2338
2339                            }
2340                        } catch (IllegalArgumentException e) {
2341                            // XXX I18N, logging needed.
2342                            ClassCastException cce = new ClassCastException("Assigning instance of class " +
2343                                objectValue.getClass().getName() + " to field " + fields[i].name);
2344                            cce.initCause(e) ;
2345                            throw cce ;
2346                        }
2347                        break;
2348                    default:
2349                        // XXX I18N, logging needed.
2350                        throw new StreamCorruptedException("Unknown kind: "
2351                                                           + fields[i].type.kind().value());
2352                    }
2353                } catch (IllegalArgumentException e) {
2354                    /* This case should never happen. If the field types
2355                       are not the same, InvalidClassException is raised when
2356                       matching the local class to the serialized ObjectStreamClass. */
2357                    // XXX I18N, logging needed.
2358                    ClassCastException cce = new ClassCastException("Assigning instance of class " + fields[i].id +
2359                        " to field " + currentClassDesc.getName() + '#' + fields[i].name);
2360                    cce.initCause( e ) ;
2361                    throw cce ;
2362                }
2363            }
2364        } catch(Throwable t){
2365            // XXX I18N, logging needed.
2366            StreamCorruptedException sce = new StreamCorruptedException(t.getMessage());
2367            sce.initCause(t) ;
2368            throw sce ;
2369        }
2370    }
2371
2372    private void skipCustomUsingFVD(ValueMember[] fields,
2373                                    com.sun.org.omg.SendingContext.CodeBase sender)
2374                                    throws InvalidClassException, StreamCorruptedException,
2375                                           ClassNotFoundException, IOException
2376    {
2377        readFormatVersion();
2378        boolean calledDefaultWriteObject = readBoolean();
2379
2380        if (calledDefaultWriteObject)
2381            throwAwayData(fields, sender);
2382
2383        if (getStreamFormatVersion() == 2) {
2384
2385            ((ValueInputStream)getOrbStream()).start_value();
2386            ((ValueInputStream)getOrbStream()).end_value();
2387        }
2388    }
2389
2390    /*
2391     * Read the fields of the specified class from the input stream throw data away.
2392     * This must handle same switch logic as above.
2393     */
2394    private void throwAwayData(ValueMember[] fields,
2395                               com.sun.org.omg.SendingContext.CodeBase sender)
2396        throws InvalidClassException, StreamCorruptedException,
2397               ClassNotFoundException, IOException {
2398
2399        for (int i = 0; i < fields.length; ++i) {
2400
2401            try {
2402
2403                switch (fields[i].type.kind().value()) {
2404                case TCKind._tk_octet:
2405                    orbStream.read_octet();
2406                    break;
2407                case TCKind._tk_boolean:
2408                    orbStream.read_boolean();
2409                    break;
2410                case TCKind._tk_char:
2411                    // Backwards compatibility.  Older Sun ORBs sent
2412                    // _tk_char even though they read and wrote wchars
2413                    // correctly.
2414                    //
2415                    // Fall through to the _tk_wchar case.
2416                case TCKind._tk_wchar:
2417                    orbStream.read_wchar();
2418                    break;
2419                case TCKind._tk_short:
2420                    orbStream.read_short();
2421                    break;
2422                case TCKind._tk_long:
2423                    orbStream.read_long();
2424                    break;
2425                case TCKind._tk_longlong:
2426                    orbStream.read_longlong();
2427                    break;
2428                case TCKind._tk_float:
2429                    orbStream.read_float();
2430                    break;
2431                case TCKind._tk_double:
2432                    orbStream.read_double();
2433                    break;
2434                case TCKind._tk_value:
2435                case TCKind._tk_objref:
2436                case TCKind._tk_value_box:
2437                    Class type = null;
2438                    String id = fields[i].id;
2439
2440                    try {
2441                        type = vhandler.getClassFromType(id);
2442                    }
2443                    catch(ClassNotFoundException cnfe){
2444                        // Make sure type = null
2445                        type = null;
2446                    }
2447                    String signature = null;
2448                    if (type != null)
2449                        signature = ValueUtility.getSignature(fields[i]);
2450
2451                    // Read value
2452                    try {
2453                        if ((signature != null) && ( signature.equals("Ljava/lang/Object;") ||
2454                                                     signature.equals("Ljava/io/Serializable;") ||
2455                                                     signature.equals("Ljava/io/Externalizable;")) ) {
2456                            javax.rmi.CORBA.Util.readAny(orbStream);
2457                        }
2458                        else {
2459                            // Decide what method call to make based on the type.
2460                            //
2461                            // NOTE : Since FullValueDescription does not allow us
2462                            // to ask whether something is an interface we do not
2463                            // have the ability to optimize this check.
2464
2465                            int callType = ValueHandlerImpl.kValueType;
2466
2467                            if (!vhandler.isSequence(id)) {
2468                                FullValueDescription fieldFVD = sender.meta(fields[i].id);
2469                                if (kRemoteTypeCode == fields[i].type) {
2470
2471                                    // RMI Object reference...
2472                                    callType = ValueHandlerImpl.kRemoteType;
2473                                } else if (fieldFVD.is_abstract) {
2474                                    // RMI Abstract Object reference...
2475
2476                                    callType = ValueHandlerImpl.kAbstractType;
2477                                }
2478                            }
2479
2480                            // Now that we have used the FVD of the field to determine the proper course
2481                            // of action, it is ok to use the type (Class) from this point forward since
2482                            // the rep. id for this read will also follow on the wire.
2483
2484                            switch (callType) {
2485                            case ValueHandlerImpl.kRemoteType:
2486                                orbStream.read_Object();
2487                                break;
2488                            case ValueHandlerImpl.kAbstractType:
2489                                orbStream.read_abstract_interface();
2490                                break;
2491                            case ValueHandlerImpl.kValueType:
2492                                if (type != null) {
2493                                    orbStream.read_value(type);
2494                                } else {
2495                                    orbStream.read_value();
2496                                }
2497                                break;
2498                            default:
2499                                // XXX I18N, logging needed.
2500                                throw new StreamCorruptedException("Unknown callType: "
2501                                                                   + callType);
2502                            }
2503                        }
2504
2505                    }
2506                    catch(IndirectionException cdrie) {
2507                        // Since we are throwing this away, don't bother handling recursion.
2508                        continue;
2509                    }
2510
2511                    break;
2512                default:
2513                    // XXX I18N, logging needed.
2514                    throw new StreamCorruptedException("Unknown kind: "
2515                                                       + fields[i].type.kind().value());
2516
2517                }
2518            } catch (IllegalArgumentException e) {
2519                /* This case should never happen. If the field types
2520                   are not the same, InvalidClassException is raised when
2521                   matching the local class to the serialized ObjectStreamClass. */
2522                // XXX I18N, logging needed.
2523                ClassCastException cce = new ClassCastException("Assigning instance of class " +
2524                    fields[i].id + " to field " + currentClassDesc.getName() +
2525                    '#' + fields[i].name);
2526                cce.initCause(e) ;
2527                throw cce ;
2528            }
2529        }
2530    }
2531
2532    private static void setObjectField(Object o, Class<?> c, String fieldName, Object v) {
2533        try {
2534            Field fld = getDeclaredField( c, fieldName ) ;
2535            Class fieldCl = fld.getType();
2536            if(v != null && !fieldCl.isInstance(v)) {
2537                throw new Exception();
2538            }
2539            long key = bridge.objectFieldOffset( fld ) ;
2540            bridge.putObject( o, key, v ) ;
2541        } catch (Exception e) {
2542            if (o != null) {
2543                throw utilWrapper.errorSetObjectField( e, fieldName,
2544                    o.toString(),
2545                    v.toString() ) ;
2546            } else {
2547                throw utilWrapper.errorSetObjectField( e, fieldName,
2548                    "null " + c.getName() + " object",
2549                    v.toString() ) ;
2550            }
2551        }
2552    }
2553
2554    private static void setBooleanField(Object o, Class<?> c, String fieldName, boolean v)
2555    {
2556        try {
2557            Field fld = getDeclaredField( c, fieldName ) ;
2558            if ((fld != null) && (fld.getType() == Boolean.TYPE)) {
2559                long key = bridge.objectFieldOffset( fld ) ;
2560                bridge.putBoolean( o, key, v ) ;
2561            } else {
2562                throw new InvalidObjectException("Field Type mismatch");
2563            }
2564        } catch (Exception e) {
2565            if (o != null) {
2566            throw utilWrapper.errorSetBooleanField( e, fieldName,
2567                o.toString(),
2568                new Boolean(v) ) ;
2569            } else {
2570                throw utilWrapper.errorSetBooleanField( e, fieldName,
2571                    "null " + c.getName() + " object",
2572                    new Boolean(v) ) ;
2573            }
2574        }
2575    }
2576
2577    private static void setByteField(Object o, Class<?> c, String fieldName, byte v)
2578    {
2579        try {
2580            Field fld = getDeclaredField( c, fieldName ) ;
2581            if ((fld != null) && (fld.getType() == Byte.TYPE)) {
2582                long key = bridge.objectFieldOffset( fld ) ;
2583                bridge.putByte( o, key, v ) ;
2584            } else {
2585                throw new InvalidObjectException("Field Type mismatch");
2586            }
2587        } catch (Exception e) {
2588            if (o != null) {
2589                throw utilWrapper.errorSetByteField( e, fieldName,
2590                    o.toString(),
2591                    new Byte(v) ) ;
2592            } else {
2593                throw utilWrapper.errorSetByteField( e, fieldName,
2594                    "null " + c.getName() + " object",
2595                    new Byte(v) ) ;
2596            }
2597        }
2598    }
2599
2600    private static void setCharField(Object o, Class<?> c, String fieldName, char v)
2601    {
2602        try {
2603            Field fld = getDeclaredField( c, fieldName ) ;
2604            if ((fld != null) && (fld.getType() == Character.TYPE)) {
2605                long key = bridge.objectFieldOffset( fld ) ;
2606                bridge.putChar( o, key, v ) ;
2607            } else {
2608                throw new InvalidObjectException("Field Type mismatch");
2609            }
2610        } catch (Exception e) {
2611            if (o != null) {
2612                throw utilWrapper.errorSetCharField( e, fieldName,
2613                    o.toString(),
2614                    new Character(v) ) ;
2615            } else {
2616                throw utilWrapper.errorSetCharField( e, fieldName,
2617                    "null " + c.getName() + " object",
2618                    new Character(v) ) ;
2619            }
2620        }
2621    }
2622
2623    private static void setShortField(Object o, Class<?> c, String fieldName, short v)
2624    {
2625        try {
2626            Field fld = getDeclaredField( c, fieldName ) ;
2627            if ((fld != null) && (fld.getType() == Short.TYPE)) {
2628                long key = bridge.objectFieldOffset( fld ) ;
2629                bridge.putShort( o, key, v ) ;
2630            } else {
2631                throw new InvalidObjectException("Field Type mismatch");
2632            }
2633        } catch (Exception e) {
2634            if (o != null) {
2635            throw utilWrapper.errorSetShortField( e, fieldName,
2636                o.toString(),
2637                new Short(v) ) ;
2638            } else {
2639                throw utilWrapper.errorSetShortField( e, fieldName,
2640                    "null " + c.getName() + " object",
2641                    new Short(v) ) ;
2642            }
2643        }
2644    }
2645
2646    private static void setIntField(Object o, Class<?> c, String fieldName, int v)
2647    {
2648        try {
2649            Field fld = getDeclaredField( c, fieldName ) ;
2650            if ((fld != null) && (fld.getType() == Integer.TYPE)) {
2651                long key = bridge.objectFieldOffset( fld ) ;
2652                bridge.putInt( o, key, v ) ;
2653            } else {
2654                throw new InvalidObjectException("Field Type mismatch");
2655            }
2656        } catch (Exception e) {
2657            if (o != null) {
2658                throw utilWrapper.errorSetIntField( e, fieldName,
2659                    o.toString(),
2660                    new Integer(v) ) ;
2661            } else {
2662                throw utilWrapper.errorSetIntField( e, fieldName,
2663                    "null " + c.getName() + " object",
2664                    new Integer(v) ) ;
2665            }
2666        }
2667    }
2668
2669    private static void setLongField(Object o, Class<?> c, String fieldName, long v)
2670    {
2671        try {
2672            Field fld = getDeclaredField( c, fieldName ) ;
2673            if ((fld != null) && (fld.getType() == Long.TYPE)) {
2674                long key = bridge.objectFieldOffset( fld ) ;
2675                bridge.putLong( o, key, v ) ;
2676            } else {
2677                throw new InvalidObjectException("Field Type mismatch");
2678            }
2679        } catch (Exception e) {
2680            if (o != null) {
2681                throw utilWrapper.errorSetLongField( e, fieldName,
2682                    o.toString(),
2683                    new Long(v) ) ;
2684            } else {
2685                throw utilWrapper.errorSetLongField( e, fieldName,
2686                    "null " + c.getName() + " object",
2687                    new Long(v) ) ;
2688            }
2689        }
2690    }
2691
2692    private static void setFloatField(Object o, Class<?> c, String fieldName, float v)
2693    {
2694        try {
2695            Field fld = getDeclaredField( c, fieldName ) ;
2696            if ((fld != null) && (fld.getType() == Float.TYPE)) {
2697                long key = bridge.objectFieldOffset( fld ) ;
2698                bridge.putFloat( o, key, v ) ;
2699            } else {
2700                throw new InvalidObjectException("Field Type mismatch");
2701            }
2702        } catch (Exception e) {
2703            if (o != null) {
2704                throw utilWrapper.errorSetFloatField( e, fieldName,
2705                    o.toString(),
2706                    new Float(v) ) ;
2707            } else {
2708                throw utilWrapper.errorSetFloatField( e, fieldName,
2709                    "null " + c.getName() + " object",
2710                    new Float(v) ) ;
2711            }
2712        }
2713    }
2714
2715    private static void setDoubleField(Object o, Class<?> c, String fieldName, double v)
2716    {
2717        try {
2718            Field fld = getDeclaredField( c, fieldName ) ;
2719            if ((fld != null) && (fld.getType() == Double.TYPE)) {
2720                long key = bridge.objectFieldOffset( fld ) ;
2721                bridge.putDouble( o, key, v ) ;
2722            } else {
2723                throw new InvalidObjectException("Field Type mismatch");
2724            }
2725        } catch (Exception e) {
2726            if (o != null) {
2727                throw utilWrapper.errorSetDoubleField( e, fieldName,
2728                    o.toString(),
2729                    new Double(v) ) ;
2730            } else {
2731                throw utilWrapper.errorSetDoubleField( e, fieldName,
2732                    "null " + c.getName() + " object",
2733                    new Double(v) ) ;
2734            }
2735        }
2736    }
2737
2738
2739    private static Field getDeclaredField(final Class<?> c,
2740                                            final String fieldName)
2741        throws PrivilegedActionException, NoSuchFieldException, SecurityException {
2742        if (System.getSecurityManager() == null) {
2743            return c.getDeclaredField(fieldName);
2744        } else {
2745            return AccessController
2746                    .doPrivileged(new PrivilegedExceptionAction<Field>() {
2747                        public Field run() throws NoSuchFieldException {
2748                            return c.getDeclaredField(fieldName);
2749                        }
2750                    });
2751        }
2752    }
2753
2754    /**
2755     * This class maintains a map of stream position to
2756     * an Object currently being deserialized.  It is used
2757     * to handle the cases where the are indirections to
2758     * an object on the recursion stack.  The CDR level
2759     * handles indirections to objects previously seen
2760     * (and completely deserialized) in the stream.
2761     */
2762    static class ActiveRecursionManager
2763    {
2764        private Map<Integer, Object> offsetToObjectMap;
2765
2766        public ActiveRecursionManager() {
2767            // A hash map is unsynchronized and allows
2768            // null values
2769            offsetToObjectMap = new HashMap<>();
2770        }
2771
2772        // Called right after allocating a new object.
2773        // Offset is the starting position in the stream
2774        // of the object.
2775        public void addObject(int offset, Object value) {
2776            offsetToObjectMap.put(new Integer(offset), value);
2777        }
2778
2779        // If the given starting position doesn't refer
2780        // to the beginning of an object currently being
2781        // deserialized, this throws an IOException.
2782        // Otherwise, it returns a reference to the
2783        // object.
2784        public Object getObject(int offset) throws IOException {
2785            Integer position = new Integer(offset);
2786
2787            if (!offsetToObjectMap.containsKey(position))
2788                // XXX I18N, logging needed.
2789                throw new IOException("Invalid indirection to offset "
2790                                      + offset);
2791
2792            return offsetToObjectMap.get(position);
2793        }
2794
2795        // Called when an object has been completely
2796        // deserialized, so it should no longer be in
2797        // this mapping.  The CDR level can handle
2798        // further indirections.
2799        public void removeObject(int offset) {
2800            offsetToObjectMap.remove(new Integer(offset));
2801        }
2802
2803        // If the given offset doesn't map to an Object,
2804        // then it isn't an indirection to an object
2805        // currently being deserialized.
2806        public boolean containsObject(int offset) {
2807            return offsetToObjectMap.containsKey(new Integer(offset));
2808        }
2809    }
2810}
2811