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