Home | History | Annotate | Download | only in io
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  * Copyright (c) 1996, 2010, Oracle and/or its affiliates. All rights reserved.
      4  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
      5  *
      6  * This code is free software; you can redistribute it and/or modify it
      7  * under the terms of the GNU General Public License version 2 only, as
      8  * published by the Free Software Foundation.  Oracle designates this
      9  * particular file as subject to the "Classpath" exception as provided
     10  * by Oracle in the LICENSE file that accompanied this code.
     11  *
     12  * This code is distributed in the hope that it will be useful, but WITHOUT
     13  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     14  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
     15  * version 2 for more details (a copy is included in the LICENSE file that
     16  * accompanied this code).
     17  *
     18  * You should have received a copy of the GNU General Public License version
     19  * 2 along with this work; if not, write to the Free Software Foundation,
     20  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
     21  *
     22  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
     23  * or visit www.oracle.com if you need additional information or have any
     24  * questions.
     25  */
     26 
     27 package java.io;
     28 
     29 import java.io.ObjectStreamClass.WeakClassKey;
     30 import java.lang.ref.ReferenceQueue;
     31 import java.security.AccessController;
     32 import java.security.PrivilegedAction;
     33 import java.util.ArrayList;
     34 import java.util.Arrays;
     35 import java.util.List;
     36 import java.util.concurrent.ConcurrentHashMap;
     37 import java.util.concurrent.ConcurrentMap;
     38 import static java.io.ObjectStreamClass.processQueue;
     39 import java.io.SerialCallbackContext;
     40 
     41 import sun.reflect.misc.ReflectUtil;
     42 
     43 /**
     44  * An ObjectOutputStream writes primitive data types and graphs of Java objects
     45  * to an OutputStream.  The objects can be read (reconstituted) using an
     46  * ObjectInputStream.  Persistent storage of objects can be accomplished by
     47  * using a file for the stream.  If the stream is a network socket stream, the
     48  * objects can be reconstituted on another host or in another process.
     49  *
     50  * <p>Only objects that support the java.io.Serializable interface can be
     51  * written to streams.  The class of each serializable object is encoded
     52  * including the class name and signature of the class, the values of the
     53  * object's fields and arrays, and the closure of any other objects referenced
     54  * from the initial objects.
     55  *
     56  * <p>The method writeObject is used to write an object to the stream.  Any
     57  * object, including Strings and arrays, is written with writeObject. Multiple
     58  * objects or primitives can be written to the stream.  The objects must be
     59  * read back from the corresponding ObjectInputstream with the same types and
     60  * in the same order as they were written.
     61  *
     62  * <p>Primitive data types can also be written to the stream using the
     63  * appropriate methods from DataOutput. Strings can also be written using the
     64  * writeUTF method.
     65  *
     66  * <p>The default serialization mechanism for an object writes the class of the
     67  * object, the class signature, and the values of all non-transient and
     68  * non-static fields.  References to other objects (except in transient or
     69  * static fields) cause those objects to be written also. Multiple references
     70  * to a single object are encoded using a reference sharing mechanism so that
     71  * graphs of objects can be restored to the same shape as when the original was
     72  * written.
     73  *
     74  * <p>For example to write an object that can be read by the example in
     75  * ObjectInputStream:
     76  * <br>
     77  * <pre>
     78  *      FileOutputStream fos = new FileOutputStream("t.tmp");
     79  *      ObjectOutputStream oos = new ObjectOutputStream(fos);
     80  *
     81  *      oos.writeInt(12345);
     82  *      oos.writeObject("Today");
     83  *      oos.writeObject(new Date());
     84  *
     85  *      oos.close();
     86  * </pre>
     87  *
     88  * <p>Classes that require special handling during the serialization and
     89  * deserialization process must implement special methods with these exact
     90  * signatures:
     91  * <br>
     92  * <pre>
     93  * private void readObject(java.io.ObjectInputStream stream)
     94  *     throws IOException, ClassNotFoundException;
     95  * private void writeObject(java.io.ObjectOutputStream stream)
     96  *     throws IOException
     97  * private void readObjectNoData()
     98  *     throws ObjectStreamException;
     99  * </pre>
    100  *
    101  * <p>The writeObject method is responsible for writing the state of the object
    102  * for its particular class so that the corresponding readObject method can
    103  * restore it.  The method does not need to concern itself with the state
    104  * belonging to the object's superclasses or subclasses.  State is saved by
    105  * writing the individual fields to the ObjectOutputStream using the
    106  * writeObject method or by using the methods for primitive data types
    107  * supported by DataOutput.
    108  *
    109  * <p>Serialization does not write out the fields of any object that does not
    110  * implement the java.io.Serializable interface.  Subclasses of Objects that
    111  * are not serializable can be serializable. In this case the non-serializable
    112  * class must have a no-arg constructor to allow its fields to be initialized.
    113  * In this case it is the responsibility of the subclass to save and restore
    114  * the state of the non-serializable class. It is frequently the case that the
    115  * fields of that class are accessible (public, package, or protected) or that
    116  * there are get and set methods that can be used to restore the state.
    117  *
    118  * <p>Serialization of an object can be prevented by implementing writeObject
    119  * and readObject methods that throw the NotSerializableException.  The
    120  * exception will be caught by the ObjectOutputStream and abort the
    121  * serialization process.
    122  *
    123  * <p>Implementing the Externalizable interface allows the object to assume
    124  * complete control over the contents and format of the object's serialized
    125  * form.  The methods of the Externalizable interface, writeExternal and
    126  * readExternal, are called to save and restore the objects state.  When
    127  * implemented by a class they can write and read their own state using all of
    128  * the methods of ObjectOutput and ObjectInput.  It is the responsibility of
    129  * the objects to handle any versioning that occurs.
    130  *
    131  * <p>Enum constants are serialized differently than ordinary serializable or
    132  * externalizable objects.  The serialized form of an enum constant consists
    133  * solely of its name; field values of the constant are not transmitted.  To
    134  * serialize an enum constant, ObjectOutputStream writes the string returned by
    135  * the constant's name method.  Like other serializable or externalizable
    136  * objects, enum constants can function as the targets of back references
    137  * appearing subsequently in the serialization stream.  The process by which
    138  * enum constants are serialized cannot be customized; any class-specific
    139  * writeObject and writeReplace methods defined by enum types are ignored
    140  * during serialization.  Similarly, any serialPersistentFields or
    141  * serialVersionUID field declarations are also ignored--all enum types have a
    142  * fixed serialVersionUID of 0L.
    143  *
    144  * <p>Primitive data, excluding serializable fields and externalizable data, is
    145  * written to the ObjectOutputStream in block-data records. A block data record
    146  * is composed of a header and data. The block data header consists of a marker
    147  * and the number of bytes to follow the header.  Consecutive primitive data
    148  * writes are merged into one block-data record.  The blocking factor used for
    149  * a block-data record will be 1024 bytes.  Each block-data record will be
    150  * filled up to 1024 bytes, or be written whenever there is a termination of
    151  * block-data mode.  Calls to the ObjectOutputStream methods writeObject,
    152  * defaultWriteObject and writeFields initially terminate any existing
    153  * block-data record.
    154  *
    155  * @author      Mike Warres
    156  * @author      Roger Riggs
    157  * @see java.io.DataOutput
    158  * @see java.io.ObjectInputStream
    159  * @see java.io.Serializable
    160  * @see java.io.Externalizable
    161  * @see <a href="{@docRoot}openjdk-redirect.html?v=8&path=/platform/serialization/spec/output.html">Object Serialization Specification, Section 2, Object Output Classes</a>
    162  * @since       JDK1.1
    163  */
    164 public class ObjectOutputStream
    165     extends OutputStream implements ObjectOutput, ObjectStreamConstants
    166 {
    167 
    168     private static class Caches {
    169         /** cache of subclass security audit results */
    170         static final ConcurrentMap<WeakClassKey,Boolean> subclassAudits =
    171             new ConcurrentHashMap<>();
    172 
    173         /** queue for WeakReferences to audited subclasses */
    174         static final ReferenceQueue<Class<?>> subclassAuditsQueue =
    175             new ReferenceQueue<>();
    176     }
    177 
    178     /** filter stream for handling block data conversion */
    179     private final BlockDataOutputStream bout;
    180     /** obj -> wire handle map */
    181     private final HandleTable handles;
    182     /** obj -> replacement obj map */
    183     private final ReplaceTable subs;
    184     /** stream protocol version */
    185     private int protocol = PROTOCOL_VERSION_2;
    186     /** recursion depth */
    187     private int depth;
    188 
    189     /** buffer for writing primitive field values */
    190     private byte[] primVals;
    191 
    192     /** if true, invoke writeObjectOverride() instead of writeObject() */
    193     private final boolean enableOverride;
    194     /** if true, invoke replaceObject() */
    195     private boolean enableReplace;
    196 
    197     // values below valid only during upcalls to writeObject()/writeExternal()
    198     /**
    199      * Context during upcalls to class-defined writeObject methods; holds
    200      * object currently being serialized and descriptor for current class.
    201      * Null when not during writeObject upcall.
    202      */
    203     private SerialCallbackContext curContext;
    204     /** current PutField object */
    205     private PutFieldImpl curPut;
    206 
    207     /** custom storage for debug trace info */
    208     private final DebugTraceInfoStack debugInfoStack;
    209 
    210     /**
    211      * value of "sun.io.serialization.extendedDebugInfo" property,
    212      * as true or false for extended information about exception's place
    213      */
    214     private static final boolean extendedDebugInfo = false;
    215 
    216     /**
    217      * Creates an ObjectOutputStream that writes to the specified OutputStream.
    218      * This constructor writes the serialization stream header to the
    219      * underlying stream; callers may wish to flush the stream immediately to
    220      * ensure that constructors for receiving ObjectInputStreams will not block
    221      * when reading the header.
    222      *
    223      * <p>If a security manager is installed, this constructor will check for
    224      * the "enableSubclassImplementation" SerializablePermission when invoked
    225      * directly or indirectly by the constructor of a subclass which overrides
    226      * the ObjectOutputStream.putFields or ObjectOutputStream.writeUnshared
    227      * methods.
    228      *
    229      * @param   out output stream to write to
    230      * @throws  IOException if an I/O error occurs while writing stream header
    231      * @throws  SecurityException if untrusted subclass illegally overrides
    232      *          security-sensitive methods
    233      * @throws  NullPointerException if <code>out</code> is <code>null</code>
    234      * @since   1.4
    235      * @see     ObjectOutputStream#ObjectOutputStream()
    236      * @see     ObjectOutputStream#putFields()
    237      * @see     ObjectInputStream#ObjectInputStream(InputStream)
    238      */
    239     public ObjectOutputStream(OutputStream out) throws IOException {
    240         verifySubclass();
    241         bout = new BlockDataOutputStream(out);
    242         handles = new HandleTable(10, (float) 3.00);
    243         subs = new ReplaceTable(10, (float) 3.00);
    244         enableOverride = false;
    245         writeStreamHeader();
    246         bout.setBlockDataMode(true);
    247         if (extendedDebugInfo) {
    248             debugInfoStack = new DebugTraceInfoStack();
    249         } else {
    250             debugInfoStack = null;
    251         }
    252     }
    253 
    254     /**
    255      * Provide a way for subclasses that are completely reimplementing
    256      * ObjectOutputStream to not have to allocate private data just used by
    257      * this implementation of ObjectOutputStream.
    258      *
    259      * <p>If there is a security manager installed, this method first calls the
    260      * security manager's <code>checkPermission</code> method with a
    261      * <code>SerializablePermission("enableSubclassImplementation")</code>
    262      * permission to ensure it's ok to enable subclassing.
    263      *
    264      * @throws  SecurityException if a security manager exists and its
    265      *          <code>checkPermission</code> method denies enabling
    266      *          subclassing.
    267      * @see SecurityManager#checkPermission
    268      * @see java.io.SerializablePermission
    269      */
    270     protected ObjectOutputStream() throws IOException, SecurityException {
    271         SecurityManager sm = System.getSecurityManager();
    272         if (sm != null) {
    273             sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
    274         }
    275         bout = null;
    276         handles = null;
    277         subs = null;
    278         enableOverride = true;
    279         debugInfoStack = null;
    280     }
    281 
    282     /**
    283      * Specify stream protocol version to use when writing the stream.
    284      *
    285      * <p>This routine provides a hook to enable the current version of
    286      * Serialization to write in a format that is backwards compatible to a
    287      * previous version of the stream format.
    288      *
    289      * <p>Every effort will be made to avoid introducing additional
    290      * backwards incompatibilities; however, sometimes there is no
    291      * other alternative.
    292      *
    293      * @param   version use ProtocolVersion from java.io.ObjectStreamConstants.
    294      * @throws  IllegalStateException if called after any objects
    295      *          have been serialized.
    296      * @throws  IllegalArgumentException if invalid version is passed in.
    297      * @throws  IOException if I/O errors occur
    298      * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
    299      * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_2
    300      * @since   1.2
    301      */
    302     public void useProtocolVersion(int version) throws IOException {
    303         if (handles.size() != 0) {
    304             // REMIND: implement better check for pristine stream?
    305             throw new IllegalStateException("stream non-empty");
    306         }
    307         switch (version) {
    308             case PROTOCOL_VERSION_1:
    309             case PROTOCOL_VERSION_2:
    310                 protocol = version;
    311                 break;
    312 
    313             default:
    314                 throw new IllegalArgumentException(
    315                     "unknown version: " + version);
    316         }
    317     }
    318 
    319     /**
    320      * Write the specified object to the ObjectOutputStream.  The class of the
    321      * object, the signature of the class, and the values of the non-transient
    322      * and non-static fields of the class and all of its supertypes are
    323      * written.  Default serialization for a class can be overridden using the
    324      * writeObject and the readObject methods.  Objects referenced by this
    325      * object are written transitively so that a complete equivalent graph of
    326      * objects can be reconstructed by an ObjectInputStream.
    327      *
    328      * <p>Exceptions are thrown for problems with the OutputStream and for
    329      * classes that should not be serialized.  All exceptions are fatal to the
    330      * OutputStream, which is left in an indeterminate state, and it is up to
    331      * the caller to ignore or recover the stream state.
    332      *
    333      * @throws  InvalidClassException Something is wrong with a class used by
    334      *          serialization.
    335      * @throws  NotSerializableException Some object to be serialized does not
    336      *          implement the java.io.Serializable interface.
    337      * @throws  IOException Any exception thrown by the underlying
    338      *          OutputStream.
    339      */
    340     public final void writeObject(Object obj) throws IOException {
    341         if (enableOverride) {
    342             writeObjectOverride(obj);
    343             return;
    344         }
    345         try {
    346             writeObject0(obj, false);
    347         } catch (IOException ex) {
    348             if (depth == 0) {
    349                 /* ----- BEGIN android -----
    350                 writeFatalException(ex);*/
    351                 try {
    352                     writeFatalException(ex);
    353 
    354                 } catch (IOException ex2) {
    355                     // If writing the exception to the output stream causes another exception there
    356                     // is no need to propagate the second exception or generate a third exception,
    357                     // both of which might obscure details of the root cause.
    358                 }
    359                 // ----- END android -----
    360             }
    361             throw ex;
    362         }
    363     }
    364 
    365     /**
    366      * Method used by subclasses to override the default writeObject method.
    367      * This method is called by trusted subclasses of ObjectInputStream that
    368      * constructed ObjectInputStream using the protected no-arg constructor.
    369      * The subclass is expected to provide an override method with the modifier
    370      * "final".
    371      *
    372      * @param   obj object to be written to the underlying stream
    373      * @throws  IOException if there are I/O errors while writing to the
    374      *          underlying stream
    375      * @see #ObjectOutputStream()
    376      * @see #writeObject(Object)
    377      * @since 1.2
    378      */
    379     protected void writeObjectOverride(Object obj) throws IOException {
    380         /* ----- BEGIN android ----- */
    381         if (!enableOverride) {
    382             // Subclasses must override.
    383             throw new IOException();
    384         }
    385         /* ----- END android ----- */
    386     }
    387 
    388     /**
    389      * Writes an "unshared" object to the ObjectOutputStream.  This method is
    390      * identical to writeObject, except that it always writes the given object
    391      * as a new, unique object in the stream (as opposed to a back-reference
    392      * pointing to a previously serialized instance).  Specifically:
    393      * <ul>
    394      *   <li>An object written via writeUnshared is always serialized in the
    395      *       same manner as a newly appearing object (an object that has not
    396      *       been written to the stream yet), regardless of whether or not the
    397      *       object has been written previously.
    398      *
    399      *   <li>If writeObject is used to write an object that has been previously
    400      *       written with writeUnshared, the previous writeUnshared operation
    401      *       is treated as if it were a write of a separate object.  In other
    402      *       words, ObjectOutputStream will never generate back-references to
    403      *       object data written by calls to writeUnshared.
    404      * </ul>
    405      * While writing an object via writeUnshared does not in itself guarantee a
    406      * unique reference to the object when it is deserialized, it allows a
    407      * single object to be defined multiple times in a stream, so that multiple
    408      * calls to readUnshared by the receiver will not conflict.  Note that the
    409      * rules described above only apply to the base-level object written with
    410      * writeUnshared, and not to any transitively referenced sub-objects in the
    411      * object graph to be serialized.
    412      *
    413      * <p>ObjectOutputStream subclasses which override this method can only be
    414      * constructed in security contexts possessing the
    415      * "enableSubclassImplementation" SerializablePermission; any attempt to
    416      * instantiate such a subclass without this permission will cause a
    417      * SecurityException to be thrown.
    418      *
    419      * @param   obj object to write to stream
    420      * @throws  NotSerializableException if an object in the graph to be
    421      *          serialized does not implement the Serializable interface
    422      * @throws  InvalidClassException if a problem exists with the class of an
    423      *          object to be serialized
    424      * @throws  IOException if an I/O error occurs during serialization
    425      * @since 1.4
    426      */
    427     public void writeUnshared(Object obj) throws IOException {
    428         try {
    429             writeObject0(obj, true);
    430         } catch (IOException ex) {
    431             if (depth == 0) {
    432                 writeFatalException(ex);
    433             }
    434             throw ex;
    435         }
    436     }
    437 
    438     /**
    439      * Write the non-static and non-transient fields of the current class to
    440      * this stream.  This may only be called from the writeObject method of the
    441      * class being serialized. It will throw the NotActiveException if it is
    442      * called otherwise.
    443      *
    444      * @throws  IOException if I/O errors occur while writing to the underlying
    445      *          <code>OutputStream</code>
    446      */
    447     public void defaultWriteObject() throws IOException {
    448         if ( curContext == null ) {
    449             throw new NotActiveException("not in call to writeObject");
    450         }
    451         Object curObj = curContext.getObj();
    452         ObjectStreamClass curDesc = curContext.getDesc();
    453         bout.setBlockDataMode(false);
    454         defaultWriteFields(curObj, curDesc);
    455         bout.setBlockDataMode(true);
    456     }
    457 
    458     /**
    459      * Retrieve the object used to buffer persistent fields to be written to
    460      * the stream.  The fields will be written to the stream when writeFields
    461      * method is called.
    462      *
    463      * @return  an instance of the class Putfield that holds the serializable
    464      *          fields
    465      * @throws  IOException if I/O errors occur
    466      * @since 1.2
    467      */
    468     public ObjectOutputStream.PutField putFields() throws IOException {
    469         if (curPut == null) {
    470             if (curContext == null) {
    471                 throw new NotActiveException("not in call to writeObject");
    472             }
    473             Object curObj = curContext.getObj();
    474             ObjectStreamClass curDesc = curContext.getDesc();
    475             curPut = new PutFieldImpl(curDesc);
    476         }
    477         return curPut;
    478     }
    479 
    480     /**
    481      * Write the buffered fields to the stream.
    482      *
    483      * @throws  IOException if I/O errors occur while writing to the underlying
    484      *          stream
    485      * @throws  NotActiveException Called when a classes writeObject method was
    486      *          not called to write the state of the object.
    487      * @since 1.2
    488      */
    489     public void writeFields() throws IOException {
    490         if (curPut == null) {
    491             throw new NotActiveException("no current PutField object");
    492         }
    493         bout.setBlockDataMode(false);
    494         curPut.writeFields();
    495         bout.setBlockDataMode(true);
    496     }
    497 
    498     /**
    499      * Reset will disregard the state of any objects already written to the
    500      * stream.  The state is reset to be the same as a new ObjectOutputStream.
    501      * The current point in the stream is marked as reset so the corresponding
    502      * ObjectInputStream will be reset at the same point.  Objects previously
    503      * written to the stream will not be refered to as already being in the
    504      * stream.  They will be written to the stream again.
    505      *
    506      * @throws  IOException if reset() is invoked while serializing an object.
    507      */
    508     public void reset() throws IOException {
    509         if (depth != 0) {
    510             throw new IOException("stream active");
    511         }
    512         bout.setBlockDataMode(false);
    513         bout.writeByte(TC_RESET);
    514         clear();
    515         bout.setBlockDataMode(true);
    516     }
    517 
    518     /**
    519      * Subclasses may implement this method to allow class data to be stored in
    520      * the stream. By default this method does nothing.  The corresponding
    521      * method in ObjectInputStream is resolveClass.  This method is called
    522      * exactly once for each unique class in the stream.  The class name and
    523      * signature will have already been written to the stream.  This method may
    524      * make free use of the ObjectOutputStream to save any representation of
    525      * the class it deems suitable (for example, the bytes of the class file).
    526      * The resolveClass method in the corresponding subclass of
    527      * ObjectInputStream must read and use any data or objects written by
    528      * annotateClass.
    529      *
    530      * @param   cl the class to annotate custom data for
    531      * @throws  IOException Any exception thrown by the underlying
    532      *          OutputStream.
    533      */
    534     protected void annotateClass(Class<?> cl) throws IOException {
    535     }
    536 
    537     /**
    538      * Subclasses may implement this method to store custom data in the stream
    539      * along with descriptors for dynamic proxy classes.
    540      *
    541      * <p>This method is called exactly once for each unique proxy class
    542      * descriptor in the stream.  The default implementation of this method in
    543      * <code>ObjectOutputStream</code> does nothing.
    544      *
    545      * <p>The corresponding method in <code>ObjectInputStream</code> is
    546      * <code>resolveProxyClass</code>.  For a given subclass of
    547      * <code>ObjectOutputStream</code> that overrides this method, the
    548      * <code>resolveProxyClass</code> method in the corresponding subclass of
    549      * <code>ObjectInputStream</code> must read any data or objects written by
    550      * <code>annotateProxyClass</code>.
    551      *
    552      * @param   cl the proxy class to annotate custom data for
    553      * @throws  IOException any exception thrown by the underlying
    554      *          <code>OutputStream</code>
    555      * @see ObjectInputStream#resolveProxyClass(String[])
    556      * @since   1.3
    557      */
    558     protected void annotateProxyClass(Class<?> cl) throws IOException {
    559     }
    560 
    561     /**
    562      * This method will allow trusted subclasses of ObjectOutputStream to
    563      * substitute one object for another during serialization. Replacing
    564      * objects is disabled until enableReplaceObject is called. The
    565      * enableReplaceObject method checks that the stream requesting to do
    566      * replacement can be trusted.  The first occurrence of each object written
    567      * into the serialization stream is passed to replaceObject.  Subsequent
    568      * references to the object are replaced by the object returned by the
    569      * original call to replaceObject.  To ensure that the private state of
    570      * objects is not unintentionally exposed, only trusted streams may use
    571      * replaceObject.
    572      *
    573      * <p>The ObjectOutputStream.writeObject method takes a parameter of type
    574      * Object (as opposed to type Serializable) to allow for cases where
    575      * non-serializable objects are replaced by serializable ones.
    576      *
    577      * <p>When a subclass is replacing objects it must insure that either a
    578      * complementary substitution must be made during deserialization or that
    579      * the substituted object is compatible with every field where the
    580      * reference will be stored.  Objects whose type is not a subclass of the
    581      * type of the field or array element abort the serialization by raising an
    582      * exception and the object is not be stored.
    583      *
    584      * <p>This method is called only once when each object is first
    585      * encountered.  All subsequent references to the object will be redirected
    586      * to the new object. This method should return the object to be
    587      * substituted or the original object.
    588      *
    589      * <p>Null can be returned as the object to be substituted, but may cause
    590      * NullReferenceException in classes that contain references to the
    591      * original object since they may be expecting an object instead of
    592      * null.
    593      *
    594      * @param   obj the object to be replaced
    595      * @return  the alternate object that replaced the specified one
    596      * @throws  IOException Any exception thrown by the underlying
    597      *          OutputStream.
    598      */
    599     protected Object replaceObject(Object obj) throws IOException {
    600         return obj;
    601     }
    602 
    603     /**
    604      * Enable the stream to do replacement of objects in the stream.  When
    605      * enabled, the replaceObject method is called for every object being
    606      * serialized.
    607      *
    608      * <p>If <code>enable</code> is true, and there is a security manager
    609      * installed, this method first calls the security manager's
    610      * <code>checkPermission</code> method with a
    611      * <code>SerializablePermission("enableSubstitution")</code> permission to
    612      * ensure it's ok to enable the stream to do replacement of objects in the
    613      * stream.
    614      *
    615      * @param   enable boolean parameter to enable replacement of objects
    616      * @return  the previous setting before this method was invoked
    617      * @throws  SecurityException if a security manager exists and its
    618      *          <code>checkPermission</code> method denies enabling the stream
    619      *          to do replacement of objects in the stream.
    620      * @see SecurityManager#checkPermission
    621      * @see java.io.SerializablePermission
    622      */
    623     protected boolean enableReplaceObject(boolean enable)
    624         throws SecurityException
    625     {
    626         if (enable == enableReplace) {
    627             return enable;
    628         }
    629         if (enable) {
    630             SecurityManager sm = System.getSecurityManager();
    631             if (sm != null) {
    632                 sm.checkPermission(SUBSTITUTION_PERMISSION);
    633             }
    634         }
    635         enableReplace = enable;
    636         return !enableReplace;
    637     }
    638 
    639     /**
    640      * The writeStreamHeader method is provided so subclasses can append or
    641      * prepend their own header to the stream.  It writes the magic number and
    642      * version to the stream.
    643      *
    644      * @throws  IOException if I/O errors occur while writing to the underlying
    645      *          stream
    646      */
    647     protected void writeStreamHeader() throws IOException {
    648         bout.writeShort(STREAM_MAGIC);
    649         bout.writeShort(STREAM_VERSION);
    650     }
    651 
    652     /**
    653      * Write the specified class descriptor to the ObjectOutputStream.  Class
    654      * descriptors are used to identify the classes of objects written to the
    655      * stream.  Subclasses of ObjectOutputStream may override this method to
    656      * customize the way in which class descriptors are written to the
    657      * serialization stream.  The corresponding method in ObjectInputStream,
    658      * <code>readClassDescriptor</code>, should then be overridden to
    659      * reconstitute the class descriptor from its custom stream representation.
    660      * By default, this method writes class descriptors according to the format
    661      * defined in the Object Serialization specification.
    662      *
    663      * <p>Note that this method will only be called if the ObjectOutputStream
    664      * is not using the old serialization stream format (set by calling
    665      * ObjectOutputStream's <code>useProtocolVersion</code> method).  If this
    666      * serialization stream is using the old format
    667      * (<code>PROTOCOL_VERSION_1</code>), the class descriptor will be written
    668      * internally in a manner that cannot be overridden or customized.
    669      *
    670      * @param   desc class descriptor to write to the stream
    671      * @throws  IOException If an I/O error has occurred.
    672      * @see java.io.ObjectInputStream#readClassDescriptor()
    673      * @see #useProtocolVersion(int)
    674      * @see java.io.ObjectStreamConstants#PROTOCOL_VERSION_1
    675      * @since 1.3
    676      */
    677     protected void writeClassDescriptor(ObjectStreamClass desc)
    678         throws IOException
    679     {
    680         desc.writeNonProxy(this);
    681     }
    682 
    683     /**
    684      * Writes a byte. This method will block until the byte is actually
    685      * written.
    686      *
    687      * @param   val the byte to be written to the stream
    688      * @throws  IOException If an I/O error has occurred.
    689      */
    690     public void write(int val) throws IOException {
    691         bout.write(val);
    692     }
    693 
    694     /**
    695      * Writes an array of bytes. This method will block until the bytes are
    696      * actually written.
    697      *
    698      * @param   buf the data to be written
    699      * @throws  IOException If an I/O error has occurred.
    700      */
    701     public void write(byte[] buf) throws IOException {
    702         bout.write(buf, 0, buf.length, false);
    703     }
    704 
    705     /**
    706      * Writes a sub array of bytes.
    707      *
    708      * @param   buf the data to be written
    709      * @param   off the start offset in the data
    710      * @param   len the number of bytes that are written
    711      * @throws  IOException If an I/O error has occurred.
    712      */
    713     public void write(byte[] buf, int off, int len) throws IOException {
    714         if (buf == null) {
    715             throw new NullPointerException();
    716         }
    717         int endoff = off + len;
    718         if (off < 0 || len < 0 || endoff > buf.length || endoff < 0) {
    719             throw new IndexOutOfBoundsException();
    720         }
    721         bout.write(buf, off, len, false);
    722     }
    723 
    724     /**
    725      * Flushes the stream. This will write any buffered output bytes and flush
    726      * through to the underlying stream.
    727      *
    728      * @throws  IOException If an I/O error has occurred.
    729      */
    730     public void flush() throws IOException {
    731         bout.flush();
    732     }
    733 
    734     /**
    735      * Drain any buffered data in ObjectOutputStream.  Similar to flush but
    736      * does not propagate the flush to the underlying stream.
    737      *
    738      * @throws  IOException if I/O errors occur while writing to the underlying
    739      *          stream
    740      */
    741     protected void drain() throws IOException {
    742         bout.drain();
    743     }
    744 
    745     /**
    746      * Closes the stream. This method must be called to release any resources
    747      * associated with the stream.
    748      *
    749      * @throws  IOException If an I/O error has occurred.
    750      */
    751     public void close() throws IOException {
    752         flush();
    753         // http://b/28159133
    754         // clear();
    755         bout.close();
    756     }
    757 
    758     /**
    759      * Writes a boolean.
    760      *
    761      * @param   val the boolean to be written
    762      * @throws  IOException if I/O errors occur while writing to the underlying
    763      *          stream
    764      */
    765     public void writeBoolean(boolean val) throws IOException {
    766         bout.writeBoolean(val);
    767     }
    768 
    769     /**
    770      * Writes an 8 bit byte.
    771      *
    772      * @param   val the byte value to be written
    773      * @throws  IOException if I/O errors occur while writing to the underlying
    774      *          stream
    775      */
    776     public void writeByte(int val) throws IOException  {
    777         bout.writeByte(val);
    778     }
    779 
    780     /**
    781      * Writes a 16 bit short.
    782      *
    783      * @param   val the short value to be written
    784      * @throws  IOException if I/O errors occur while writing to the underlying
    785      *          stream
    786      */
    787     public void writeShort(int val)  throws IOException {
    788         bout.writeShort(val);
    789     }
    790 
    791     /**
    792      * Writes a 16 bit char.
    793      *
    794      * @param   val the char value to be written
    795      * @throws  IOException if I/O errors occur while writing to the underlying
    796      *          stream
    797      */
    798     public void writeChar(int val)  throws IOException {
    799         bout.writeChar(val);
    800     }
    801 
    802     /**
    803      * Writes a 32 bit int.
    804      *
    805      * @param   val the integer value to be written
    806      * @throws  IOException if I/O errors occur while writing to the underlying
    807      *          stream
    808      */
    809     public void writeInt(int val)  throws IOException {
    810         bout.writeInt(val);
    811     }
    812 
    813     /**
    814      * Writes a 64 bit long.
    815      *
    816      * @param   val the long value to be written
    817      * @throws  IOException if I/O errors occur while writing to the underlying
    818      *          stream
    819      */
    820     public void writeLong(long val)  throws IOException {
    821         bout.writeLong(val);
    822     }
    823 
    824     /**
    825      * Writes a 32 bit float.
    826      *
    827      * @param   val the float value to be written
    828      * @throws  IOException if I/O errors occur while writing to the underlying
    829      *          stream
    830      */
    831     public void writeFloat(float val) throws IOException {
    832         bout.writeFloat(val);
    833     }
    834 
    835     /**
    836      * Writes a 64 bit double.
    837      *
    838      * @param   val the double value to be written
    839      * @throws  IOException if I/O errors occur while writing to the underlying
    840      *          stream
    841      */
    842     public void writeDouble(double val) throws IOException {
    843         bout.writeDouble(val);
    844     }
    845 
    846     /**
    847      * Writes a String as a sequence of bytes.
    848      *
    849      * @param   str the String of bytes to be written
    850      * @throws  IOException if I/O errors occur while writing to the underlying
    851      *          stream
    852      */
    853     public void writeBytes(String str) throws IOException {
    854         bout.writeBytes(str);
    855     }
    856 
    857     /**
    858      * Writes a String as a sequence of chars.
    859      *
    860      * @param   str the String of chars to be written
    861      * @throws  IOException if I/O errors occur while writing to the underlying
    862      *          stream
    863      */
    864     public void writeChars(String str) throws IOException {
    865         bout.writeChars(str);
    866     }
    867 
    868     /**
    869      * Primitive data write of this String in
    870      * <a href="DataInput.html#modified-utf-8">modified UTF-8</a>
    871      * format.  Note that there is a
    872      * significant difference between writing a String into the stream as
    873      * primitive data or as an Object. A String instance written by writeObject
    874      * is written into the stream as a String initially. Future writeObject()
    875      * calls write references to the string into the stream.
    876      *
    877      * @param   str the String to be written
    878      * @throws  IOException if I/O errors occur while writing to the underlying
    879      *          stream
    880      */
    881     public void writeUTF(String str) throws IOException {
    882         bout.writeUTF(str);
    883     }
    884 
    885     /**
    886      * Provide programmatic access to the persistent fields to be written
    887      * to ObjectOutput.
    888      *
    889      * @since 1.2
    890      */
    891     public static abstract class PutField {
    892 
    893         /**
    894          * Put the value of the named boolean field into the persistent field.
    895          *
    896          * @param  name the name of the serializable field
    897          * @param  val the value to assign to the field
    898          * @throws IllegalArgumentException if <code>name</code> does not
    899          * match the name of a serializable field for the class whose fields
    900          * are being written, or if the type of the named field is not
    901          * <code>boolean</code>
    902          */
    903         public abstract void put(String name, boolean val);
    904 
    905         /**
    906          * Put the value of the named byte field into the persistent field.
    907          *
    908          * @param  name the name of the serializable field
    909          * @param  val the value to assign to the field
    910          * @throws IllegalArgumentException if <code>name</code> does not
    911          * match the name of a serializable field for the class whose fields
    912          * are being written, or if the type of the named field is not
    913          * <code>byte</code>
    914          */
    915         public abstract void put(String name, byte val);
    916 
    917         /**
    918          * Put the value of the named char field into the persistent field.
    919          *
    920          * @param  name the name of the serializable field
    921          * @param  val the value to assign to the field
    922          * @throws IllegalArgumentException if <code>name</code> does not
    923          * match the name of a serializable field for the class whose fields
    924          * are being written, or if the type of the named field is not
    925          * <code>char</code>
    926          */
    927         public abstract void put(String name, char val);
    928 
    929         /**
    930          * Put the value of the named short field into the persistent field.
    931          *
    932          * @param  name the name of the serializable field
    933          * @param  val the value to assign to the field
    934          * @throws IllegalArgumentException if <code>name</code> does not
    935          * match the name of a serializable field for the class whose fields
    936          * are being written, or if the type of the named field is not
    937          * <code>short</code>
    938          */
    939         public abstract void put(String name, short val);
    940 
    941         /**
    942          * Put the value of the named int field into the persistent field.
    943          *
    944          * @param  name the name of the serializable field
    945          * @param  val the value to assign to the field
    946          * @throws IllegalArgumentException if <code>name</code> does not
    947          * match the name of a serializable field for the class whose fields
    948          * are being written, or if the type of the named field is not
    949          * <code>int</code>
    950          */
    951         public abstract void put(String name, int val);
    952 
    953         /**
    954          * Put the value of the named long field into the persistent field.
    955          *
    956          * @param  name the name of the serializable field
    957          * @param  val the value to assign to the field
    958          * @throws IllegalArgumentException if <code>name</code> does not
    959          * match the name of a serializable field for the class whose fields
    960          * are being written, or if the type of the named field is not
    961          * <code>long</code>
    962          */
    963         public abstract void put(String name, long val);
    964 
    965         /**
    966          * Put the value of the named float field into the persistent field.
    967          *
    968          * @param  name the name of the serializable field
    969          * @param  val the value to assign to the field
    970          * @throws IllegalArgumentException if <code>name</code> does not
    971          * match the name of a serializable field for the class whose fields
    972          * are being written, or if the type of the named field is not
    973          * <code>float</code>
    974          */
    975         public abstract void put(String name, float val);
    976 
    977         /**
    978          * Put the value of the named double field into the persistent field.
    979          *
    980          * @param  name the name of the serializable field
    981          * @param  val the value to assign to the field
    982          * @throws IllegalArgumentException if <code>name</code> does not
    983          * match the name of a serializable field for the class whose fields
    984          * are being written, or if the type of the named field is not
    985          * <code>double</code>
    986          */
    987         public abstract void put(String name, double val);
    988 
    989         /**
    990          * Put the value of the named Object field into the persistent field.
    991          *
    992          * @param  name the name of the serializable field
    993          * @param  val the value to assign to the field
    994          *         (which may be <code>null</code>)
    995          * @throws IllegalArgumentException if <code>name</code> does not
    996          * match the name of a serializable field for the class whose fields
    997          * are being written, or if the type of the named field is not a
    998          * reference type
    999          */
   1000         public abstract void put(String name, Object val);
   1001 
   1002         /**
   1003          * Write the data and fields to the specified ObjectOutput stream,
   1004          * which must be the same stream that produced this
   1005          * <code>PutField</code> object.
   1006          *
   1007          * @param  out the stream to write the data and fields to
   1008          * @throws IOException if I/O errors occur while writing to the
   1009          *         underlying stream
   1010          * @throws IllegalArgumentException if the specified stream is not
   1011          *         the same stream that produced this <code>PutField</code>
   1012          *         object
   1013          * @deprecated This method does not write the values contained by this
   1014          *         <code>PutField</code> object in a proper format, and may
   1015          *         result in corruption of the serialization stream.  The
   1016          *         correct way to write <code>PutField</code> data is by
   1017          *         calling the {@link java.io.ObjectOutputStream#writeFields()}
   1018          *         method.
   1019          */
   1020         @Deprecated
   1021         public abstract void write(ObjectOutput out) throws IOException;
   1022     }
   1023 
   1024 
   1025     /**
   1026      * Returns protocol version in use.
   1027      */
   1028     int getProtocolVersion() {
   1029         return protocol;
   1030     }
   1031 
   1032     /**
   1033      * Writes string without allowing it to be replaced in stream.  Used by
   1034      * ObjectStreamClass to write class descriptor type strings.
   1035      */
   1036     void writeTypeString(String str) throws IOException {
   1037         int handle;
   1038         if (str == null) {
   1039             writeNull();
   1040         } else if ((handle = handles.lookup(str)) != -1) {
   1041             writeHandle(handle);
   1042         } else {
   1043             writeString(str, false);
   1044         }
   1045     }
   1046 
   1047     /**
   1048      * Verifies that this (possibly subclass) instance can be constructed
   1049      * without violating security constraints: the subclass must not override
   1050      * security-sensitive non-final methods, or else the
   1051      * "enableSubclassImplementation" SerializablePermission is checked.
   1052      */
   1053     private void verifySubclass() {
   1054         Class cl = getClass();
   1055         if (cl == ObjectOutputStream.class) {
   1056             return;
   1057         }
   1058         SecurityManager sm = System.getSecurityManager();
   1059         if (sm == null) {
   1060             return;
   1061         }
   1062         processQueue(Caches.subclassAuditsQueue, Caches.subclassAudits);
   1063         WeakClassKey key = new WeakClassKey(cl, Caches.subclassAuditsQueue);
   1064         Boolean result = Caches.subclassAudits.get(key);
   1065         if (result == null) {
   1066             result = Boolean.valueOf(auditSubclass(cl));
   1067             Caches.subclassAudits.putIfAbsent(key, result);
   1068         }
   1069         if (result.booleanValue()) {
   1070             return;
   1071         }
   1072         sm.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
   1073     }
   1074 
   1075     /**
   1076      * Performs reflective checks on given subclass to verify that it doesn't
   1077      * override security-sensitive non-final methods.  Returns true if subclass
   1078      * is "safe", false otherwise.
   1079      */
   1080     private static boolean auditSubclass(final Class subcl) {
   1081         Boolean result = AccessController.doPrivileged(
   1082             new PrivilegedAction<Boolean>() {
   1083                 public Boolean run() {
   1084                     for (Class cl = subcl;
   1085                          cl != ObjectOutputStream.class;
   1086                          cl = cl.getSuperclass())
   1087                     {
   1088                         try {
   1089                             cl.getDeclaredMethod(
   1090                                 "writeUnshared", new Class[] { Object.class });
   1091                             return Boolean.FALSE;
   1092                         } catch (NoSuchMethodException ex) {
   1093                         }
   1094                         try {
   1095                             cl.getDeclaredMethod("putFields", (Class[]) null);
   1096                             return Boolean.FALSE;
   1097                         } catch (NoSuchMethodException ex) {
   1098                         }
   1099                     }
   1100                     return Boolean.TRUE;
   1101                 }
   1102             }
   1103         );
   1104         return result.booleanValue();
   1105     }
   1106 
   1107     /**
   1108      * Clears internal data structures.
   1109      */
   1110     private void clear() {
   1111         subs.clear();
   1112         handles.clear();
   1113     }
   1114 
   1115     /**
   1116      * Underlying writeObject/writeUnshared implementation.
   1117      */
   1118     private void writeObject0(Object obj, boolean unshared)
   1119         throws IOException
   1120     {
   1121         boolean oldMode = bout.setBlockDataMode(false);
   1122         depth++;
   1123         try {
   1124             // handle previously written and non-replaceable objects
   1125             int h;
   1126             if ((obj = subs.lookup(obj)) == null) {
   1127                 writeNull();
   1128                 return;
   1129             } else if (!unshared && (h = handles.lookup(obj)) != -1) {
   1130                 writeHandle(h);
   1131                 return;
   1132             /* ----- BEGIN android -----
   1133             } else if (obj instanceof Class) {
   1134                 writeClass((Class) obj, unshared);
   1135                 return;
   1136             } else if (obj instanceof ObjectStreamClass) {
   1137                 writeClassDesc((ObjectStreamClass) obj, unshared);
   1138                 return;
   1139               ----- END android ----- */
   1140             }
   1141 
   1142             // check for replacement object
   1143             Object orig = obj;
   1144             Class cl = obj.getClass();
   1145             ObjectStreamClass desc;
   1146 
   1147             /* ----- BEGIN android -----
   1148             for (;;) {
   1149                 // REMIND: skip this check for strings/arrays?
   1150                 Class repCl;
   1151                 desc = ObjectStreamClass.lookup(cl, true);
   1152                 if (!desc.hasWriteReplaceMethod() ||
   1153                     (obj = desc.invokeWriteReplace(obj)) == null ||
   1154                     (repCl = obj.getClass()) == cl)
   1155                 {
   1156                     break;
   1157                 }
   1158                 cl = repCl;
   1159                 desc = ObjectStreamClass.lookup(cl, true);
   1160                 break;
   1161             }*/
   1162             // Do only one replace pass
   1163 
   1164             Class repCl;
   1165             desc = ObjectStreamClass.lookup(cl, true);
   1166             if (desc.hasWriteReplaceMethod() &&
   1167                 (obj = desc.invokeWriteReplace(obj)) != null &&
   1168                 (repCl = obj.getClass()) != cl)
   1169             {
   1170                 cl = repCl;
   1171                 desc = ObjectStreamClass.lookup(cl, true);
   1172             }
   1173             // ----- END android -----
   1174 
   1175             if (enableReplace) {
   1176                 Object rep = replaceObject(obj);
   1177                 if (rep != obj && rep != null) {
   1178                     cl = rep.getClass();
   1179                     desc = ObjectStreamClass.lookup(cl, true);
   1180                 }
   1181                 obj = rep;
   1182             }
   1183 
   1184             // if object replaced, run through original checks a second time
   1185             if (obj != orig) {
   1186                 subs.assign(orig, obj);
   1187                 if (obj == null) {
   1188                     writeNull();
   1189                     return;
   1190                 } else if (!unshared && (h = handles.lookup(obj)) != -1) {
   1191                     writeHandle(h);
   1192                     return;
   1193                 /* ----- BEGIN android -----
   1194                 } else if (obj instanceof Class) {
   1195                     writeClass((Class) obj, unshared);
   1196                     return;
   1197                 } else if (obj instanceof ObjectStreamClass) {
   1198                     writeClassDesc((ObjectStreamClass) obj, unshared);
   1199                     return;
   1200                   ----- END android -----*/
   1201                 }
   1202             }
   1203 
   1204             // remaining cases
   1205             // ----- BEGIN android -----
   1206             if (obj instanceof Class) {
   1207                 writeClass((Class) obj, unshared);
   1208             } else if (obj instanceof ObjectStreamClass) {
   1209                 writeClassDesc((ObjectStreamClass) obj, unshared);
   1210             // ----- END android -----
   1211             } else if (obj instanceof String) {
   1212                 writeString((String) obj, unshared);
   1213             } else if (cl.isArray()) {
   1214                 writeArray(obj, desc, unshared);
   1215             } else if (obj instanceof Enum) {
   1216                 writeEnum((Enum) obj, desc, unshared);
   1217             } else if (obj instanceof Serializable) {
   1218                 writeOrdinaryObject(obj, desc, unshared);
   1219             } else {
   1220                 if (extendedDebugInfo) {
   1221                     throw new NotSerializableException(
   1222                         cl.getName() + "\n" + debugInfoStack.toString());
   1223                 } else {
   1224                     throw new NotSerializableException(cl.getName());
   1225                 }
   1226             }
   1227         } finally {
   1228             depth--;
   1229             bout.setBlockDataMode(oldMode);
   1230         }
   1231     }
   1232 
   1233     /**
   1234      * Writes null code to stream.
   1235      */
   1236     private void writeNull() throws IOException {
   1237         bout.writeByte(TC_NULL);
   1238     }
   1239 
   1240     /**
   1241      * Writes given object handle to stream.
   1242      */
   1243     private void writeHandle(int handle) throws IOException {
   1244         bout.writeByte(TC_REFERENCE);
   1245         bout.writeInt(baseWireHandle + handle);
   1246     }
   1247 
   1248     /**
   1249      * Writes representation of given class to stream.
   1250      */
   1251     private void writeClass(Class cl, boolean unshared) throws IOException {
   1252         bout.writeByte(TC_CLASS);
   1253         writeClassDesc(ObjectStreamClass.lookup(cl, true), false);
   1254         handles.assign(unshared ? null : cl);
   1255     }
   1256 
   1257     /**
   1258      * Writes representation of given class descriptor to stream.
   1259      */
   1260     private void writeClassDesc(ObjectStreamClass desc, boolean unshared)
   1261         throws IOException
   1262     {
   1263         int handle;
   1264         if (desc == null) {
   1265             writeNull();
   1266         } else if (!unshared && (handle = handles.lookup(desc)) != -1) {
   1267             writeHandle(handle);
   1268         } else if (desc.isProxy()) {
   1269             writeProxyDesc(desc, unshared);
   1270         } else {
   1271             writeNonProxyDesc(desc, unshared);
   1272         }
   1273     }
   1274 
   1275     private boolean isCustomSubclass() {
   1276         // Return true if this class is a custom subclass of ObjectOutputStream
   1277         return getClass().getClassLoader()
   1278                    != ObjectOutputStream.class.getClassLoader();
   1279     }
   1280 
   1281     /**
   1282      * Writes class descriptor representing a dynamic proxy class to stream.
   1283      */
   1284     private void writeProxyDesc(ObjectStreamClass desc, boolean unshared)
   1285         throws IOException
   1286     {
   1287         bout.writeByte(TC_PROXYCLASSDESC);
   1288         handles.assign(unshared ? null : desc);
   1289 
   1290         Class cl = desc.forClass();
   1291         Class[] ifaces = cl.getInterfaces();
   1292         bout.writeInt(ifaces.length);
   1293         for (int i = 0; i < ifaces.length; i++) {
   1294             bout.writeUTF(ifaces[i].getName());
   1295         }
   1296 
   1297         bout.setBlockDataMode(true);
   1298         if (isCustomSubclass()) {
   1299             ReflectUtil.checkPackageAccess(cl);
   1300         }
   1301         annotateProxyClass(cl);
   1302         bout.setBlockDataMode(false);
   1303         bout.writeByte(TC_ENDBLOCKDATA);
   1304 
   1305         writeClassDesc(desc.getSuperDesc(), false);
   1306     }
   1307 
   1308     /**
   1309      * Writes class descriptor representing a standard (i.e., not a dynamic
   1310      * proxy) class to stream.
   1311      */
   1312     private void writeNonProxyDesc(ObjectStreamClass desc, boolean unshared)
   1313         throws IOException
   1314     {
   1315         bout.writeByte(TC_CLASSDESC);
   1316         handles.assign(unshared ? null : desc);
   1317 
   1318         if (protocol == PROTOCOL_VERSION_1) {
   1319             // do not invoke class descriptor write hook with old protocol
   1320             desc.writeNonProxy(this);
   1321         } else {
   1322             writeClassDescriptor(desc);
   1323         }
   1324 
   1325         Class cl = desc.forClass();
   1326         bout.setBlockDataMode(true);
   1327         if (isCustomSubclass()) {
   1328             ReflectUtil.checkPackageAccess(cl);
   1329         }
   1330         annotateClass(cl);
   1331         bout.setBlockDataMode(false);
   1332         bout.writeByte(TC_ENDBLOCKDATA);
   1333 
   1334         writeClassDesc(desc.getSuperDesc(), false);
   1335     }
   1336 
   1337     /**
   1338      * Writes given string to stream, using standard or long UTF format
   1339      * depending on string length.
   1340      */
   1341     private void writeString(String str, boolean unshared) throws IOException {
   1342         handles.assign(unshared ? null : str);
   1343         long utflen = bout.getUTFLength(str);
   1344         if (utflen <= 0xFFFF) {
   1345             bout.writeByte(TC_STRING);
   1346             bout.writeUTF(str, utflen);
   1347         } else {
   1348             bout.writeByte(TC_LONGSTRING);
   1349             bout.writeLongUTF(str, utflen);
   1350         }
   1351     }
   1352 
   1353     /**
   1354      * Writes given array object to stream.
   1355      */
   1356     private void writeArray(Object array,
   1357                             ObjectStreamClass desc,
   1358                             boolean unshared)
   1359         throws IOException
   1360     {
   1361         bout.writeByte(TC_ARRAY);
   1362         writeClassDesc(desc, false);
   1363         handles.assign(unshared ? null : array);
   1364 
   1365         Class ccl = desc.forClass().getComponentType();
   1366         if (ccl.isPrimitive()) {
   1367             if (ccl == Integer.TYPE) {
   1368                 int[] ia = (int[]) array;
   1369                 bout.writeInt(ia.length);
   1370                 bout.writeInts(ia, 0, ia.length);
   1371             } else if (ccl == Byte.TYPE) {
   1372                 byte[] ba = (byte[]) array;
   1373                 bout.writeInt(ba.length);
   1374                 bout.write(ba, 0, ba.length, true);
   1375             } else if (ccl == Long.TYPE) {
   1376                 long[] ja = (long[]) array;
   1377                 bout.writeInt(ja.length);
   1378                 bout.writeLongs(ja, 0, ja.length);
   1379             } else if (ccl == Float.TYPE) {
   1380                 float[] fa = (float[]) array;
   1381                 bout.writeInt(fa.length);
   1382                 bout.writeFloats(fa, 0, fa.length);
   1383             } else if (ccl == Double.TYPE) {
   1384                 double[] da = (double[]) array;
   1385                 bout.writeInt(da.length);
   1386                 bout.writeDoubles(da, 0, da.length);
   1387             } else if (ccl == Short.TYPE) {
   1388                 short[] sa = (short[]) array;
   1389                 bout.writeInt(sa.length);
   1390                 bout.writeShorts(sa, 0, sa.length);
   1391             } else if (ccl == Character.TYPE) {
   1392                 char[] ca = (char[]) array;
   1393                 bout.writeInt(ca.length);
   1394                 bout.writeChars(ca, 0, ca.length);
   1395             } else if (ccl == Boolean.TYPE) {
   1396                 boolean[] za = (boolean[]) array;
   1397                 bout.writeInt(za.length);
   1398                 bout.writeBooleans(za, 0, za.length);
   1399             } else {
   1400                 throw new InternalError();
   1401             }
   1402         } else {
   1403             Object[] objs = (Object[]) array;
   1404             int len = objs.length;
   1405             bout.writeInt(len);
   1406             if (extendedDebugInfo) {
   1407                 debugInfoStack.push(
   1408                     "array (class \"" + array.getClass().getName() +
   1409                     "\", size: " + len  + ")");
   1410             }
   1411             try {
   1412                 for (int i = 0; i < len; i++) {
   1413                     if (extendedDebugInfo) {
   1414                         debugInfoStack.push(
   1415                             "element of array (index: " + i + ")");
   1416                     }
   1417                     try {
   1418                         writeObject0(objs[i], false);
   1419                     } finally {
   1420                         if (extendedDebugInfo) {
   1421                             debugInfoStack.pop();
   1422                         }
   1423                     }
   1424                 }
   1425             } finally {
   1426                 if (extendedDebugInfo) {
   1427                     debugInfoStack.pop();
   1428                 }
   1429             }
   1430         }
   1431     }
   1432 
   1433     /**
   1434      * Writes given enum constant to stream.
   1435      */
   1436     private void writeEnum(Enum en,
   1437                            ObjectStreamClass desc,
   1438                            boolean unshared)
   1439         throws IOException
   1440     {
   1441         bout.writeByte(TC_ENUM);
   1442         ObjectStreamClass sdesc = desc.getSuperDesc();
   1443         writeClassDesc((sdesc.forClass() == Enum.class) ? desc : sdesc, false);
   1444         handles.assign(unshared ? null : en);
   1445         writeString(en.name(), false);
   1446     }
   1447 
   1448     /**
   1449      * Writes representation of a "ordinary" (i.e., not a String, Class,
   1450      * ObjectStreamClass, array, or enum constant) serializable object to the
   1451      * stream.
   1452      */
   1453     private void writeOrdinaryObject(Object obj,
   1454                                      ObjectStreamClass desc,
   1455                                      boolean unshared)
   1456         throws IOException
   1457     {
   1458         if (extendedDebugInfo) {
   1459             debugInfoStack.push(
   1460                 (depth == 1 ? "root " : "") + "object (class \"" +
   1461                 obj.getClass().getName() + "\", " + obj.toString() + ")");
   1462         }
   1463         try {
   1464             desc.checkSerialize();
   1465 
   1466             bout.writeByte(TC_OBJECT);
   1467             writeClassDesc(desc, false);
   1468             handles.assign(unshared ? null : obj);
   1469             if (desc.isExternalizable() && !desc.isProxy()) {
   1470                 writeExternalData((Externalizable) obj);
   1471             } else {
   1472                 writeSerialData(obj, desc);
   1473             }
   1474         } finally {
   1475             if (extendedDebugInfo) {
   1476                 debugInfoStack.pop();
   1477             }
   1478         }
   1479     }
   1480 
   1481     /**
   1482      * Writes externalizable data of given object by invoking its
   1483      * writeExternal() method.
   1484      */
   1485     private void writeExternalData(Externalizable obj) throws IOException {
   1486         PutFieldImpl oldPut = curPut;
   1487         curPut = null;
   1488 
   1489         if (extendedDebugInfo) {
   1490             debugInfoStack.push("writeExternal data");
   1491         }
   1492         SerialCallbackContext oldContext = curContext;
   1493         try {
   1494             curContext = null;
   1495             if (protocol == PROTOCOL_VERSION_1) {
   1496                 obj.writeExternal(this);
   1497             } else {
   1498                 bout.setBlockDataMode(true);
   1499                 obj.writeExternal(this);
   1500                 bout.setBlockDataMode(false);
   1501                 bout.writeByte(TC_ENDBLOCKDATA);
   1502             }
   1503         } finally {
   1504             curContext = oldContext;
   1505             if (extendedDebugInfo) {
   1506                 debugInfoStack.pop();
   1507             }
   1508         }
   1509 
   1510         curPut = oldPut;
   1511     }
   1512 
   1513     /**
   1514      * Writes instance data for each serializable class of given object, from
   1515      * superclass to subclass.
   1516      */
   1517     private void writeSerialData(Object obj, ObjectStreamClass desc)
   1518         throws IOException
   1519     {
   1520         ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
   1521         for (int i = 0; i < slots.length; i++) {
   1522             ObjectStreamClass slotDesc = slots[i].desc;
   1523             if (slotDesc.hasWriteObjectMethod()) {
   1524                 PutFieldImpl oldPut = curPut;
   1525                 curPut = null;
   1526                 SerialCallbackContext oldContext = curContext;
   1527 
   1528                 if (extendedDebugInfo) {
   1529                     debugInfoStack.push(
   1530                         "custom writeObject data (class \"" +
   1531                         slotDesc.getName() + "\")");
   1532                 }
   1533                 try {
   1534                     curContext = new SerialCallbackContext(obj, slotDesc);
   1535                     bout.setBlockDataMode(true);
   1536                     slotDesc.invokeWriteObject(obj, this);
   1537                     bout.setBlockDataMode(false);
   1538                     bout.writeByte(TC_ENDBLOCKDATA);
   1539                 } finally {
   1540                     curContext.setUsed();
   1541                     curContext = oldContext;
   1542                     if (extendedDebugInfo) {
   1543                         debugInfoStack.pop();
   1544                     }
   1545                 }
   1546 
   1547                 curPut = oldPut;
   1548             } else {
   1549                 defaultWriteFields(obj, slotDesc);
   1550             }
   1551         }
   1552     }
   1553 
   1554     /**
   1555      * Fetches and writes values of serializable fields of given object to
   1556      * stream.  The given class descriptor specifies which field values to
   1557      * write, and in which order they should be written.
   1558      */
   1559     private void defaultWriteFields(Object obj, ObjectStreamClass desc)
   1560         throws IOException
   1561     {
   1562         // REMIND: perform conservative isInstance check here?
   1563         desc.checkDefaultSerialize();
   1564 
   1565         int primDataSize = desc.getPrimDataSize();
   1566         if (primVals == null || primVals.length < primDataSize) {
   1567             primVals = new byte[primDataSize];
   1568         }
   1569         desc.getPrimFieldValues(obj, primVals);
   1570         bout.write(primVals, 0, primDataSize, false);
   1571 
   1572         ObjectStreamField[] fields = desc.getFields(false);
   1573         Object[] objVals = new Object[desc.getNumObjFields()];
   1574         int numPrimFields = fields.length - objVals.length;
   1575         desc.getObjFieldValues(obj, objVals);
   1576         for (int i = 0; i < objVals.length; i++) {
   1577             if (extendedDebugInfo) {
   1578                 debugInfoStack.push(
   1579                     "field (class \"" + desc.getName() + "\", name: \"" +
   1580                     fields[numPrimFields + i].getName() + "\", type: \"" +
   1581                     fields[numPrimFields + i].getType() + "\")");
   1582             }
   1583             try {
   1584                 writeObject0(objVals[i],
   1585                              fields[numPrimFields + i].isUnshared());
   1586             } finally {
   1587                 if (extendedDebugInfo) {
   1588                     debugInfoStack.pop();
   1589                 }
   1590             }
   1591         }
   1592     }
   1593 
   1594     /**
   1595      * Attempts to write to stream fatal IOException that has caused
   1596      * serialization to abort.
   1597      */
   1598     private void writeFatalException(IOException ex) throws IOException {
   1599         /*
   1600          * Note: the serialization specification states that if a second
   1601          * IOException occurs while attempting to serialize the original fatal
   1602          * exception to the stream, then a StreamCorruptedException should be
   1603          * thrown (section 2.1).  However, due to a bug in previous
   1604          * implementations of serialization, StreamCorruptedExceptions were
   1605          * rarely (if ever) actually thrown--the "root" exceptions from
   1606          * underlying streams were thrown instead.  This historical behavior is
   1607          * followed here for consistency.
   1608          */
   1609         clear();
   1610         boolean oldMode = bout.setBlockDataMode(false);
   1611         try {
   1612             bout.writeByte(TC_EXCEPTION);
   1613             writeObject0(ex, false);
   1614             clear();
   1615         } finally {
   1616             bout.setBlockDataMode(oldMode);
   1617         }
   1618     }
   1619 
   1620     /**
   1621      * Converts specified span of float values into byte values.
   1622      */
   1623     // REMIND: remove once hotspot inlines Float.floatToIntBits
   1624     private static native void floatsToBytes(float[] src, int srcpos,
   1625                                              byte[] dst, int dstpos,
   1626                                              int nfloats);
   1627 
   1628     /**
   1629      * Converts specified span of double values into byte values.
   1630      */
   1631     // REMIND: remove once hotspot inlines Double.doubleToLongBits
   1632     private static native void doublesToBytes(double[] src, int srcpos,
   1633                                               byte[] dst, int dstpos,
   1634                                               int ndoubles);
   1635 
   1636     /**
   1637      * Default PutField implementation.
   1638      */
   1639     private class PutFieldImpl extends PutField {
   1640 
   1641         /** class descriptor describing serializable fields */
   1642         private final ObjectStreamClass desc;
   1643         /** primitive field values */
   1644         private final byte[] primVals;
   1645         /** object field values */
   1646         private final Object[] objVals;
   1647 
   1648         /**
   1649          * Creates PutFieldImpl object for writing fields defined in given
   1650          * class descriptor.
   1651          */
   1652         PutFieldImpl(ObjectStreamClass desc) {
   1653             this.desc = desc;
   1654             primVals = new byte[desc.getPrimDataSize()];
   1655             objVals = new Object[desc.getNumObjFields()];
   1656         }
   1657 
   1658         public void put(String name, boolean val) {
   1659             Bits.putBoolean(primVals, getFieldOffset(name, Boolean.TYPE), val);
   1660         }
   1661 
   1662         public void put(String name, byte val) {
   1663             primVals[getFieldOffset(name, Byte.TYPE)] = val;
   1664         }
   1665 
   1666         public void put(String name, char val) {
   1667             Bits.putChar(primVals, getFieldOffset(name, Character.TYPE), val);
   1668         }
   1669 
   1670         public void put(String name, short val) {
   1671             Bits.putShort(primVals, getFieldOffset(name, Short.TYPE), val);
   1672         }
   1673 
   1674         public void put(String name, int val) {
   1675             Bits.putInt(primVals, getFieldOffset(name, Integer.TYPE), val);
   1676         }
   1677 
   1678         public void put(String name, float val) {
   1679             Bits.putFloat(primVals, getFieldOffset(name, Float.TYPE), val);
   1680         }
   1681 
   1682         public void put(String name, long val) {
   1683             Bits.putLong(primVals, getFieldOffset(name, Long.TYPE), val);
   1684         }
   1685 
   1686         public void put(String name, double val) {
   1687             Bits.putDouble(primVals, getFieldOffset(name, Double.TYPE), val);
   1688         }
   1689 
   1690         public void put(String name, Object val) {
   1691             objVals[getFieldOffset(name, Object.class)] = val;
   1692         }
   1693 
   1694         // deprecated in ObjectOutputStream.PutField
   1695         public void write(ObjectOutput out) throws IOException {
   1696             /*
   1697              * Applications should *not* use this method to write PutField
   1698              * data, as it will lead to stream corruption if the PutField
   1699              * object writes any primitive data (since block data mode is not
   1700              * unset/set properly, as is done in OOS.writeFields()).  This
   1701              * broken implementation is being retained solely for behavioral
   1702              * compatibility, in order to support applications which use
   1703              * OOS.PutField.write() for writing only non-primitive data.
   1704              *
   1705              * Serialization of unshared objects is not implemented here since
   1706              * it is not necessary for backwards compatibility; also, unshared
   1707              * semantics may not be supported by the given ObjectOutput
   1708              * instance.  Applications which write unshared objects using the
   1709              * PutField API must use OOS.writeFields().
   1710              */
   1711             if (ObjectOutputStream.this != out) {
   1712                 throw new IllegalArgumentException("wrong stream");
   1713             }
   1714             out.write(primVals, 0, primVals.length);
   1715 
   1716             ObjectStreamField[] fields = desc.getFields(false);
   1717             int numPrimFields = fields.length - objVals.length;
   1718             // REMIND: warn if numPrimFields > 0?
   1719             for (int i = 0; i < objVals.length; i++) {
   1720                 if (fields[numPrimFields + i].isUnshared()) {
   1721                     throw new IOException("cannot write unshared object");
   1722                 }
   1723                 out.writeObject(objVals[i]);
   1724             }
   1725         }
   1726 
   1727         /**
   1728          * Writes buffered primitive data and object fields to stream.
   1729          */
   1730         void writeFields() throws IOException {
   1731             bout.write(primVals, 0, primVals.length, false);
   1732 
   1733             ObjectStreamField[] fields = desc.getFields(false);
   1734             int numPrimFields = fields.length - objVals.length;
   1735             for (int i = 0; i < objVals.length; i++) {
   1736                 if (extendedDebugInfo) {
   1737                     debugInfoStack.push(
   1738                         "field (class \"" + desc.getName() + "\", name: \"" +
   1739                         fields[numPrimFields + i].getName() + "\", type: \"" +
   1740                         fields[numPrimFields + i].getType() + "\")");
   1741                 }
   1742                 try {
   1743                     writeObject0(objVals[i],
   1744                                  fields[numPrimFields + i].isUnshared());
   1745                 } finally {
   1746                     if (extendedDebugInfo) {
   1747                         debugInfoStack.pop();
   1748                     }
   1749                 }
   1750             }
   1751         }
   1752 
   1753         /**
   1754          * Returns offset of field with given name and type.  A specified type
   1755          * of null matches all types, Object.class matches all non-primitive
   1756          * types, and any other non-null type matches assignable types only.
   1757          * Throws IllegalArgumentException if no matching field found.
   1758          */
   1759         private int getFieldOffset(String name, Class type) {
   1760             ObjectStreamField field = desc.getField(name, type);
   1761             if (field == null) {
   1762                 throw new IllegalArgumentException("no such field " + name +
   1763                                                    " with type " + type);
   1764             }
   1765             return field.getOffset();
   1766         }
   1767     }
   1768 
   1769     /**
   1770      * Buffered output stream with two modes: in default mode, outputs data in
   1771      * same format as DataOutputStream; in "block data" mode, outputs data
   1772      * bracketed by block data markers (see object serialization specification
   1773      * for details).
   1774      */
   1775     private static class BlockDataOutputStream
   1776         extends OutputStream implements DataOutput
   1777     {
   1778         /** maximum data block length */
   1779         private static final int MAX_BLOCK_SIZE = 1024;
   1780         /** maximum data block header length */
   1781         private static final int MAX_HEADER_SIZE = 5;
   1782         /** (tunable) length of char buffer (for writing strings) */
   1783         private static final int CHAR_BUF_SIZE = 256;
   1784 
   1785         /** buffer for writing general/block data */
   1786         private final byte[] buf = new byte[MAX_BLOCK_SIZE];
   1787         /** buffer for writing block data headers */
   1788         private final byte[] hbuf = new byte[MAX_HEADER_SIZE];
   1789         /** char buffer for fast string writes */
   1790         private final char[] cbuf = new char[CHAR_BUF_SIZE];
   1791 
   1792         /** block data mode */
   1793         private boolean blkmode = false;
   1794         /** current offset into buf */
   1795         private int pos = 0;
   1796 
   1797         /** underlying output stream */
   1798         private final OutputStream out;
   1799         /** loopback stream (for data writes that span data blocks) */
   1800         private final DataOutputStream dout;
   1801 
   1802         /**
   1803          * Indicates that this stream was closed and that a warning must be logged once if an
   1804          * attempt is made to write to it and the underlying stream does not throw an exception.
   1805          *
   1806          * <p>This will be set back to false when a warning is logged to ensure that the log is not
   1807          * flooded with warnings.
   1808          *
   1809          * http://b/28159133
   1810          */
   1811         private boolean warnOnceWhenWriting;
   1812 
   1813         /**
   1814          * Creates new BlockDataOutputStream on top of given underlying stream.
   1815          * Block data mode is turned off by default.
   1816          */
   1817         BlockDataOutputStream(OutputStream out) {
   1818             this.out = out;
   1819             dout = new DataOutputStream(this);
   1820         }
   1821 
   1822         /**
   1823          * Sets block data mode to the given mode (true == on, false == off)
   1824          * and returns the previous mode value.  If the new mode is the same as
   1825          * the old mode, no action is taken.  If the new mode differs from the
   1826          * old mode, any buffered data is flushed before switching to the new
   1827          * mode.
   1828          */
   1829         boolean setBlockDataMode(boolean mode) throws IOException {
   1830             if (blkmode == mode) {
   1831                 return blkmode;
   1832             }
   1833             drain();
   1834             blkmode = mode;
   1835             return !blkmode;
   1836         }
   1837 
   1838         /**
   1839          * Returns true if the stream is currently in block data mode, false
   1840          * otherwise.
   1841          */
   1842         boolean getBlockDataMode() {
   1843             return blkmode;
   1844         }
   1845 
   1846         /**
   1847          * Warns if the stream has been closed.
   1848          *
   1849          * <p>This is called after data has been written to the underlying stream in order to allow
   1850          * the underlying stream to detect and fail if an attempt is made to write to a closed
   1851          * stream. That ensures that this will only log a warning if the underlying stream does not
   1852          * so it will not log unnecessary warnings.
   1853          */
   1854         private void warnIfClosed() {
   1855             if (warnOnceWhenWriting) {
   1856                 System.logW("The app is relying on undefined behavior. Attempting to write to a"
   1857                         + " closed ObjectOutputStream could produce corrupt output in a future"
   1858                         + " release of Android.", new IOException("Stream Closed"));
   1859                 // Set back to false so no more messages are logged unless the stream is closed
   1860                 // again.
   1861                 warnOnceWhenWriting = false;
   1862             }
   1863         }
   1864 
   1865         /* ----------------- generic output stream methods ----------------- */
   1866         /*
   1867          * The following methods are equivalent to their counterparts in
   1868          * OutputStream, except that they partition written data into data
   1869          * blocks when in block data mode.
   1870          */
   1871 
   1872         public void write(int b) throws IOException {
   1873             if (pos >= MAX_BLOCK_SIZE) {
   1874                 drain();
   1875             }
   1876             buf[pos++] = (byte) b;
   1877         }
   1878 
   1879         public void write(byte[] b) throws IOException {
   1880             write(b, 0, b.length, false);
   1881         }
   1882 
   1883         public void write(byte[] b, int off, int len) throws IOException {
   1884             write(b, off, len, false);
   1885         }
   1886 
   1887         public void flush() throws IOException {
   1888             drain();
   1889             out.flush();
   1890         }
   1891 
   1892         public void close() throws IOException {
   1893             flush();
   1894             out.close();
   1895             warnOnceWhenWriting = true;
   1896         }
   1897 
   1898         /**
   1899          * Writes specified span of byte values from given array.  If copy is
   1900          * true, copies the values to an intermediate buffer before writing
   1901          * them to underlying stream (to avoid exposing a reference to the
   1902          * original byte array).
   1903          */
   1904         void write(byte[] b, int off, int len, boolean copy)
   1905             throws IOException
   1906         {
   1907             if (!(copy || blkmode)) {           // write directly
   1908                 drain();
   1909                 out.write(b, off, len);
   1910                 warnIfClosed();
   1911                 return;
   1912             }
   1913 
   1914             while (len > 0) {
   1915                 if (pos >= MAX_BLOCK_SIZE) {
   1916                     drain();
   1917                 }
   1918                 if (len >= MAX_BLOCK_SIZE && !copy && pos == 0) {
   1919                     // avoid unnecessary copy
   1920                     writeBlockHeader(MAX_BLOCK_SIZE);
   1921                     out.write(b, off, MAX_BLOCK_SIZE);
   1922                     off += MAX_BLOCK_SIZE;
   1923                     len -= MAX_BLOCK_SIZE;
   1924                 } else {
   1925                     int wlen = Math.min(len, MAX_BLOCK_SIZE - pos);
   1926                     System.arraycopy(b, off, buf, pos, wlen);
   1927                     pos += wlen;
   1928                     off += wlen;
   1929                     len -= wlen;
   1930                 }
   1931             }
   1932             warnIfClosed();
   1933         }
   1934 
   1935         /**
   1936          * Writes all buffered data from this stream to the underlying stream,
   1937          * but does not flush underlying stream.
   1938          */
   1939         void drain() throws IOException {
   1940             if (pos == 0) {
   1941                 return;
   1942             }
   1943             if (blkmode) {
   1944                 writeBlockHeader(pos);
   1945             }
   1946             out.write(buf, 0, pos);
   1947             pos = 0;
   1948             warnIfClosed();
   1949         }
   1950 
   1951         /**
   1952          * Writes block data header.  Data blocks shorter than 256 bytes are
   1953          * prefixed with a 2-byte header; all others start with a 5-byte
   1954          * header.
   1955          */
   1956         private void writeBlockHeader(int len) throws IOException {
   1957             if (len <= 0xFF) {
   1958                 hbuf[0] = TC_BLOCKDATA;
   1959                 hbuf[1] = (byte) len;
   1960                 out.write(hbuf, 0, 2);
   1961             } else {
   1962                 hbuf[0] = TC_BLOCKDATALONG;
   1963                 Bits.putInt(hbuf, 1, len);
   1964                 out.write(hbuf, 0, 5);
   1965             }
   1966             warnIfClosed();
   1967         }
   1968 
   1969 
   1970         /* ----------------- primitive data output methods ----------------- */
   1971         /*
   1972          * The following methods are equivalent to their counterparts in
   1973          * DataOutputStream, except that they partition written data into data
   1974          * blocks when in block data mode.
   1975          */
   1976 
   1977         public void writeBoolean(boolean v) throws IOException {
   1978             if (pos >= MAX_BLOCK_SIZE) {
   1979                 drain();
   1980             }
   1981             Bits.putBoolean(buf, pos++, v);
   1982         }
   1983 
   1984         public void writeByte(int v) throws IOException {
   1985             if (pos >= MAX_BLOCK_SIZE) {
   1986                 drain();
   1987             }
   1988             buf[pos++] = (byte) v;
   1989         }
   1990 
   1991         public void writeChar(int v) throws IOException {
   1992             if (pos + 2 <= MAX_BLOCK_SIZE) {
   1993                 Bits.putChar(buf, pos, (char) v);
   1994                 pos += 2;
   1995             } else {
   1996                 dout.writeChar(v);
   1997             }
   1998         }
   1999 
   2000         public void writeShort(int v) throws IOException {
   2001             if (pos + 2 <= MAX_BLOCK_SIZE) {
   2002                 Bits.putShort(buf, pos, (short) v);
   2003                 pos += 2;
   2004             } else {
   2005                 dout.writeShort(v);
   2006             }
   2007         }
   2008 
   2009         public void writeInt(int v) throws IOException {
   2010             if (pos + 4 <= MAX_BLOCK_SIZE) {
   2011                 Bits.putInt(buf, pos, v);
   2012                 pos += 4;
   2013             } else {
   2014                 dout.writeInt(v);
   2015             }
   2016         }
   2017 
   2018         public void writeFloat(float v) throws IOException {
   2019             if (pos + 4 <= MAX_BLOCK_SIZE) {
   2020                 Bits.putFloat(buf, pos, v);
   2021                 pos += 4;
   2022             } else {
   2023                 dout.writeFloat(v);
   2024             }
   2025         }
   2026 
   2027         public void writeLong(long v) throws IOException {
   2028             if (pos + 8 <= MAX_BLOCK_SIZE) {
   2029                 Bits.putLong(buf, pos, v);
   2030                 pos += 8;
   2031             } else {
   2032                 dout.writeLong(v);
   2033             }
   2034         }
   2035 
   2036         public void writeDouble(double v) throws IOException {
   2037             if (pos + 8 <= MAX_BLOCK_SIZE) {
   2038                 Bits.putDouble(buf, pos, v);
   2039                 pos += 8;
   2040             } else {
   2041                 dout.writeDouble(v);
   2042             }
   2043         }
   2044 
   2045         public void writeBytes(String s) throws IOException {
   2046             int endoff = s.length();
   2047             int cpos = 0;
   2048             int csize = 0;
   2049             for (int off = 0; off < endoff; ) {
   2050                 if (cpos >= csize) {
   2051                     cpos = 0;
   2052                     csize = Math.min(endoff - off, CHAR_BUF_SIZE);
   2053                     s.getChars(off, off + csize, cbuf, 0);
   2054                 }
   2055                 if (pos >= MAX_BLOCK_SIZE) {
   2056                     drain();
   2057                 }
   2058                 int n = Math.min(csize - cpos, MAX_BLOCK_SIZE - pos);
   2059                 int stop = pos + n;
   2060                 while (pos < stop) {
   2061                     buf[pos++] = (byte) cbuf[cpos++];
   2062                 }
   2063                 off += n;
   2064             }
   2065         }
   2066 
   2067         public void writeChars(String s) throws IOException {
   2068             int endoff = s.length();
   2069             for (int off = 0; off < endoff; ) {
   2070                 int csize = Math.min(endoff - off, CHAR_BUF_SIZE);
   2071                 s.getChars(off, off + csize, cbuf, 0);
   2072                 writeChars(cbuf, 0, csize);
   2073                 off += csize;
   2074             }
   2075         }
   2076 
   2077         public void writeUTF(String s) throws IOException {
   2078             writeUTF(s, getUTFLength(s));
   2079         }
   2080 
   2081 
   2082         /* -------------- primitive data array output methods -------------- */
   2083         /*
   2084          * The following methods write out spans of primitive data values.
   2085          * Though equivalent to calling the corresponding primitive write
   2086          * methods repeatedly, these methods are optimized for writing groups
   2087          * of primitive data values more efficiently.
   2088          */
   2089 
   2090         void writeBooleans(boolean[] v, int off, int len) throws IOException {
   2091             int endoff = off + len;
   2092             while (off < endoff) {
   2093                 if (pos >= MAX_BLOCK_SIZE) {
   2094                     drain();
   2095                 }
   2096                 int stop = Math.min(endoff, off + (MAX_BLOCK_SIZE - pos));
   2097                 while (off < stop) {
   2098                     Bits.putBoolean(buf, pos++, v[off++]);
   2099                 }
   2100             }
   2101         }
   2102 
   2103         void writeChars(char[] v, int off, int len) throws IOException {
   2104             int limit = MAX_BLOCK_SIZE - 2;
   2105             int endoff = off + len;
   2106             while (off < endoff) {
   2107                 if (pos <= limit) {
   2108                     int avail = (MAX_BLOCK_SIZE - pos) >> 1;
   2109                     int stop = Math.min(endoff, off + avail);
   2110                     while (off < stop) {
   2111                         Bits.putChar(buf, pos, v[off++]);
   2112                         pos += 2;
   2113                     }
   2114                 } else {
   2115                     dout.writeChar(v[off++]);
   2116                 }
   2117             }
   2118         }
   2119 
   2120         void writeShorts(short[] v, int off, int len) throws IOException {
   2121             int limit = MAX_BLOCK_SIZE - 2;
   2122             int endoff = off + len;
   2123             while (off < endoff) {
   2124                 if (pos <= limit) {
   2125                     int avail = (MAX_BLOCK_SIZE - pos) >> 1;
   2126                     int stop = Math.min(endoff, off + avail);
   2127                     while (off < stop) {
   2128                         Bits.putShort(buf, pos, v[off++]);
   2129                         pos += 2;
   2130                     }
   2131                 } else {
   2132                     dout.writeShort(v[off++]);
   2133                 }
   2134             }
   2135         }
   2136 
   2137         void writeInts(int[] v, int off, int len) throws IOException {
   2138             int limit = MAX_BLOCK_SIZE - 4;
   2139             int endoff = off + len;
   2140             while (off < endoff) {
   2141                 if (pos <= limit) {
   2142                     int avail = (MAX_BLOCK_SIZE - pos) >> 2;
   2143                     int stop = Math.min(endoff, off + avail);
   2144                     while (off < stop) {
   2145                         Bits.putInt(buf, pos, v[off++]);
   2146                         pos += 4;
   2147                     }
   2148                 } else {
   2149                     dout.writeInt(v[off++]);
   2150                 }
   2151             }
   2152         }
   2153 
   2154         void writeFloats(float[] v, int off, int len) throws IOException {
   2155             int limit = MAX_BLOCK_SIZE - 4;
   2156             int endoff = off + len;
   2157             while (off < endoff) {
   2158                 if (pos <= limit) {
   2159                     int avail = (MAX_BLOCK_SIZE - pos) >> 2;
   2160                     int chunklen = Math.min(endoff - off, avail);
   2161                     floatsToBytes(v, off, buf, pos, chunklen);
   2162                     off += chunklen;
   2163                     pos += chunklen << 2;
   2164                 } else {
   2165                     dout.writeFloat(v[off++]);
   2166                 }
   2167             }
   2168         }
   2169 
   2170         void writeLongs(long[] v, int off, int len) throws IOException {
   2171             int limit = MAX_BLOCK_SIZE - 8;
   2172             int endoff = off + len;
   2173             while (off < endoff) {
   2174                 if (pos <= limit) {
   2175                     int avail = (MAX_BLOCK_SIZE - pos) >> 3;
   2176                     int stop = Math.min(endoff, off + avail);
   2177                     while (off < stop) {
   2178                         Bits.putLong(buf, pos, v[off++]);
   2179                         pos += 8;
   2180                     }
   2181                 } else {
   2182                     dout.writeLong(v[off++]);
   2183                 }
   2184             }
   2185         }
   2186 
   2187         void writeDoubles(double[] v, int off, int len) throws IOException {
   2188             int limit = MAX_BLOCK_SIZE - 8;
   2189             int endoff = off + len;
   2190             while (off < endoff) {
   2191                 if (pos <= limit) {
   2192                     int avail = (MAX_BLOCK_SIZE - pos) >> 3;
   2193                     int chunklen = Math.min(endoff - off, avail);
   2194                     doublesToBytes(v, off, buf, pos, chunklen);
   2195                     off += chunklen;
   2196                     pos += chunklen << 3;
   2197                 } else {
   2198                     dout.writeDouble(v[off++]);
   2199                 }
   2200             }
   2201         }
   2202 
   2203         /**
   2204          * Returns the length in bytes of the UTF encoding of the given string.
   2205          */
   2206         long getUTFLength(String s) {
   2207             int len = s.length();
   2208             long utflen = 0;
   2209             for (int off = 0; off < len; ) {
   2210                 int csize = Math.min(len - off, CHAR_BUF_SIZE);
   2211                 s.getChars(off, off + csize, cbuf, 0);
   2212                 for (int cpos = 0; cpos < csize; cpos++) {
   2213                     char c = cbuf[cpos];
   2214                     if (c >= 0x0001 && c <= 0x007F) {
   2215                         utflen++;
   2216                     } else if (c > 0x07FF) {
   2217                         utflen += 3;
   2218                     } else {
   2219                         utflen += 2;
   2220                     }
   2221                 }
   2222                 off += csize;
   2223             }
   2224             return utflen;
   2225         }
   2226 
   2227         /**
   2228          * Writes the given string in UTF format.  This method is used in
   2229          * situations where the UTF encoding length of the string is already
   2230          * known; specifying it explicitly avoids a prescan of the string to
   2231          * determine its UTF length.
   2232          */
   2233         void writeUTF(String s, long utflen) throws IOException {
   2234             if (utflen > 0xFFFFL) {
   2235                 throw new UTFDataFormatException();
   2236             }
   2237             writeShort((int) utflen);
   2238             if (utflen == (long) s.length()) {
   2239                 writeBytes(s);
   2240             } else {
   2241                 writeUTFBody(s);
   2242             }
   2243         }
   2244 
   2245         /**
   2246          * Writes given string in "long" UTF format.  "Long" UTF format is
   2247          * identical to standard UTF, except that it uses an 8 byte header
   2248          * (instead of the standard 2 bytes) to convey the UTF encoding length.
   2249          */
   2250         void writeLongUTF(String s) throws IOException {
   2251             writeLongUTF(s, getUTFLength(s));
   2252         }
   2253 
   2254         /**
   2255          * Writes given string in "long" UTF format, where the UTF encoding
   2256          * length of the string is already known.
   2257          */
   2258         void writeLongUTF(String s, long utflen) throws IOException {
   2259             writeLong(utflen);
   2260             if (utflen == (long) s.length()) {
   2261                 writeBytes(s);
   2262             } else {
   2263                 writeUTFBody(s);
   2264             }
   2265         }
   2266 
   2267         /**
   2268          * Writes the "body" (i.e., the UTF representation minus the 2-byte or
   2269          * 8-byte length header) of the UTF encoding for the given string.
   2270          */
   2271         private void writeUTFBody(String s) throws IOException {
   2272             int limit = MAX_BLOCK_SIZE - 3;
   2273             int len = s.length();
   2274             for (int off = 0; off < len; ) {
   2275                 int csize = Math.min(len - off, CHAR_BUF_SIZE);
   2276                 s.getChars(off, off + csize, cbuf, 0);
   2277                 for (int cpos = 0; cpos < csize; cpos++) {
   2278                     char c = cbuf[cpos];
   2279                     if (pos <= limit) {
   2280                         if (c <= 0x007F && c != 0) {
   2281                             buf[pos++] = (byte) c;
   2282                         } else if (c > 0x07FF) {
   2283                             buf[pos + 2] = (byte) (0x80 | ((c >> 0) & 0x3F));
   2284                             buf[pos + 1] = (byte) (0x80 | ((c >> 6) & 0x3F));
   2285                             buf[pos + 0] = (byte) (0xE0 | ((c >> 12) & 0x0F));
   2286                             pos += 3;
   2287                         } else {
   2288                             buf[pos + 1] = (byte) (0x80 | ((c >> 0) & 0x3F));
   2289                             buf[pos + 0] = (byte) (0xC0 | ((c >> 6) & 0x1F));
   2290                             pos += 2;
   2291                         }
   2292                     } else {    // write one byte at a time to normalize block
   2293                         if (c <= 0x007F && c != 0) {
   2294                             write(c);
   2295                         } else if (c > 0x07FF) {
   2296                             write(0xE0 | ((c >> 12) & 0x0F));
   2297                             write(0x80 | ((c >> 6) & 0x3F));
   2298                             write(0x80 | ((c >> 0) & 0x3F));
   2299                         } else {
   2300                             write(0xC0 | ((c >> 6) & 0x1F));
   2301                             write(0x80 | ((c >> 0) & 0x3F));
   2302                         }
   2303                     }
   2304                 }
   2305                 off += csize;
   2306             }
   2307         }
   2308     }
   2309 
   2310     /**
   2311      * Lightweight identity hash table which maps objects to integer handles,
   2312      * assigned in ascending order.
   2313      */
   2314     private static class HandleTable {
   2315 
   2316         /* number of mappings in table/next available handle */
   2317         private int size;
   2318         /* size threshold determining when to expand hash spine */
   2319         private int threshold;
   2320         /* factor for computing size threshold */
   2321         private final float loadFactor;
   2322         /* maps hash value -> candidate handle value */
   2323         private int[] spine;
   2324         /* maps handle value -> next candidate handle value */
   2325         private int[] next;
   2326         /* maps handle value -> associated object */
   2327         private Object[] objs;
   2328 
   2329         /**
   2330          * Creates new HandleTable with given capacity and load factor.
   2331          */
   2332         HandleTable(int initialCapacity, float loadFactor) {
   2333             this.loadFactor = loadFactor;
   2334             spine = new int[initialCapacity];
   2335             next = new int[initialCapacity];
   2336             objs = new Object[initialCapacity];
   2337             threshold = (int) (initialCapacity * loadFactor);
   2338             clear();
   2339         }
   2340 
   2341         /**
   2342          * Assigns next available handle to given object, and returns handle
   2343          * value.  Handles are assigned in ascending order starting at 0.
   2344          */
   2345         int assign(Object obj) {
   2346             if (size >= next.length) {
   2347                 growEntries();
   2348             }
   2349             if (size >= threshold) {
   2350                 growSpine();
   2351             }
   2352             insert(obj, size);
   2353             return size++;
   2354         }
   2355 
   2356         /**
   2357          * Looks up and returns handle associated with given object, or -1 if
   2358          * no mapping found.
   2359          */
   2360         int lookup(Object obj) {
   2361             if (size == 0) {
   2362                 return -1;
   2363             }
   2364             int index = hash(obj) % spine.length;
   2365             for (int i = spine[index]; i >= 0; i = next[i]) {
   2366                 if (objs[i] == obj) {
   2367                     return i;
   2368                 }
   2369             }
   2370             return -1;
   2371         }
   2372 
   2373         /**
   2374          * Resets table to its initial (empty) state.
   2375          */
   2376         void clear() {
   2377             Arrays.fill(spine, -1);
   2378             Arrays.fill(objs, 0, size, null);
   2379             size = 0;
   2380         }
   2381 
   2382         /**
   2383          * Returns the number of mappings currently in table.
   2384          */
   2385         int size() {
   2386             return size;
   2387         }
   2388 
   2389         /**
   2390          * Inserts mapping object -> handle mapping into table.  Assumes table
   2391          * is large enough to accommodate new mapping.
   2392          */
   2393         private void insert(Object obj, int handle) {
   2394             int index = hash(obj) % spine.length;
   2395             objs[handle] = obj;
   2396             next[handle] = spine[index];
   2397             spine[index] = handle;
   2398         }
   2399 
   2400         /**
   2401          * Expands the hash "spine" -- equivalent to increasing the number of
   2402          * buckets in a conventional hash table.
   2403          */
   2404         private void growSpine() {
   2405             spine = new int[(spine.length << 1) + 1];
   2406             threshold = (int) (spine.length * loadFactor);
   2407             Arrays.fill(spine, -1);
   2408             for (int i = 0; i < size; i++) {
   2409                 insert(objs[i], i);
   2410             }
   2411         }
   2412 
   2413         /**
   2414          * Increases hash table capacity by lengthening entry arrays.
   2415          */
   2416         private void growEntries() {
   2417             int newLength = (next.length << 1) + 1;
   2418             int[] newNext = new int[newLength];
   2419             System.arraycopy(next, 0, newNext, 0, size);
   2420             next = newNext;
   2421 
   2422             Object[] newObjs = new Object[newLength];
   2423             System.arraycopy(objs, 0, newObjs, 0, size);
   2424             objs = newObjs;
   2425         }
   2426 
   2427         /**
   2428          * Returns hash value for given object.
   2429          */
   2430         private int hash(Object obj) {
   2431             return System.identityHashCode(obj) & 0x7FFFFFFF;
   2432         }
   2433     }
   2434 
   2435     /**
   2436      * Lightweight identity hash table which maps objects to replacement
   2437      * objects.
   2438      */
   2439     private static class ReplaceTable {
   2440 
   2441         /* maps object -> index */
   2442         private final HandleTable htab;
   2443         /* maps index -> replacement object */
   2444         private Object[] reps;
   2445 
   2446         /**
   2447          * Creates new ReplaceTable with given capacity and load factor.
   2448          */
   2449         ReplaceTable(int initialCapacity, float loadFactor) {
   2450             htab = new HandleTable(initialCapacity, loadFactor);
   2451             reps = new Object[initialCapacity];
   2452         }
   2453 
   2454         /**
   2455          * Enters mapping from object to replacement object.
   2456          */
   2457         void assign(Object obj, Object rep) {
   2458             int index = htab.assign(obj);
   2459             while (index >= reps.length) {
   2460                 grow();
   2461             }
   2462             reps[index] = rep;
   2463         }
   2464 
   2465         /**
   2466          * Looks up and returns replacement for given object.  If no
   2467          * replacement is found, returns the lookup object itself.
   2468          */
   2469         Object lookup(Object obj) {
   2470             int index = htab.lookup(obj);
   2471             return (index >= 0) ? reps[index] : obj;
   2472         }
   2473 
   2474         /**
   2475          * Resets table to its initial (empty) state.
   2476          */
   2477         void clear() {
   2478             Arrays.fill(reps, 0, htab.size(), null);
   2479             htab.clear();
   2480         }
   2481 
   2482         /**
   2483          * Returns the number of mappings currently in table.
   2484          */
   2485         int size() {
   2486             return htab.size();
   2487         }
   2488 
   2489         /**
   2490          * Increases table capacity.
   2491          */
   2492         private void grow() {
   2493             Object[] newReps = new Object[(reps.length << 1) + 1];
   2494             System.arraycopy(reps, 0, newReps, 0, reps.length);
   2495             reps = newReps;
   2496         }
   2497     }
   2498 
   2499     /**
   2500      * Stack to keep debug information about the state of the
   2501      * serialization process, for embedding in exception messages.
   2502      */
   2503     private static class DebugTraceInfoStack {
   2504         private final List<String> stack;
   2505 
   2506         DebugTraceInfoStack() {
   2507             stack = new ArrayList<>();
   2508         }
   2509 
   2510         /**
   2511          * Removes all of the elements from enclosed list.
   2512          */
   2513         void clear() {
   2514             stack.clear();
   2515         }
   2516 
   2517         /**
   2518          * Removes the object at the top of enclosed list.
   2519          */
   2520         void pop() {
   2521             stack.remove(stack.size()-1);
   2522         }
   2523 
   2524         /**
   2525          * Pushes a String onto the top of enclosed list.
   2526          */
   2527         void push(String entry) {
   2528             stack.add("\t- " + entry);
   2529         }
   2530 
   2531         /**
   2532          * Returns a string representation of this object
   2533          */
   2534         public String toString() {
   2535             StringBuilder buffer = new StringBuilder();
   2536             if (!stack.isEmpty()) {
   2537                 for(int i = stack.size(); i > 0; i-- ) {
   2538                     buffer.append(stack.get(i-1) + ((i != 1) ? "\n" : ""));
   2539                 }
   2540             }
   2541             return buffer.toString();
   2542         }
   2543     }
   2544 
   2545 }
   2546