1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *     http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */
17 
18 package java.io;
19 
20 import java.lang.reflect.Field;
21 import java.lang.reflect.InvocationTargetException;
22 import java.lang.reflect.Method;
23 import java.lang.reflect.Proxy;
24 import java.nio.ByteOrder;
25 import java.nio.charset.ModifiedUtf8;
26 import java.util.List;
27 import libcore.io.Memory;
28 import libcore.io.SizeOf;
29 
30 /**
31  * A specialized {@link OutputStream} that is able to write (serialize) Java
32  * objects as well as primitive data types (int, byte, char etc.). The data can
33  * later be loaded using an ObjectInputStream.
34  *
35  * @see ObjectInputStream
36  * @see ObjectOutput
37  * @see Serializable
38  * @see Externalizable
39  */
40 public class ObjectOutputStream extends OutputStream implements ObjectOutput, ObjectStreamConstants {
41 
42     /*
43      * Mask to zero SC_BLOC_DATA bit.
44      */
45     private static final byte NOT_SC_BLOCK_DATA = (byte) (SC_BLOCK_DATA ^ 0xFF);
46 
47     /*
48      * How many nested levels to writeObject.
49      */
50     private int nestedLevels;
51 
52     /*
53      * Where we write to
54      */
55     private DataOutputStream output;
56 
57     /*
58      * If object replacement is enabled or not
59      */
60     private boolean enableReplace;
61 
62     /*
63      * Where we write primitive types to
64      */
65     private DataOutputStream primitiveTypes;
66 
67     /*
68      * Where the write primitive types are actually written to
69      */
70     private ByteArrayOutputStream primitiveTypesBuffer;
71 
72     /*
73      * Table mapping Object -> Integer (handle)
74      */
75     private SerializationHandleMap objectsWritten;
76 
77     /*
78      * All objects are assigned an ID (integer handle)
79      */
80     private int currentHandle;
81 
82     /*
83      * Used by defaultWriteObject
84      */
85     private Object currentObject;
86 
87     /*
88      * Used by defaultWriteObject
89      */
90     private ObjectStreamClass currentClass;
91 
92     /*
93      * Either ObjectStreamConstants.PROTOCOL_VERSION_1 or
94      * ObjectStreamConstants.PROTOCOL_VERSION_2
95      */
96     private int protocolVersion;
97 
98     /*
99      * Used to keep track of the PutField object for the class/object being
100      * written
101      */
102     private EmulatedFieldsForDumping currentPutField;
103 
104     /*
105      * Allows the receiver to decide if it needs to call writeObjectOverride
106      */
107     private boolean subclassOverridingImplementation;
108 
109     /*
110      * Descriptor for java.lang.reflect.Proxy
111      */
112     private final ObjectStreamClass proxyClassDesc = ObjectStreamClass.lookup(Proxy.class);
113 
114     /**
115      * PutField is an inner class to provide access to the persistent fields
116      * that are written to the target stream.
117      */
118     public static abstract class PutField {
119         /**
120          * Puts the value of the boolean field identified by {@code name} to the
121          * persistent field.
122          *
123          * @param name
124          *            the name of the field to serialize.
125          * @param value
126          *            the value that is put to the persistent field.
127          */
put(String name, boolean value)128         public abstract void put(String name, boolean value);
129 
130         /**
131          * Puts the value of the character field identified by {@code name} to
132          * the persistent field.
133          *
134          * @param name
135          *            the name of the field to serialize.
136          * @param value
137          *            the value that is put to the persistent field.
138          */
put(String name, char value)139         public abstract void put(String name, char value);
140 
141         /**
142          * Puts the value of the byte field identified by {@code name} to the
143          * persistent field.
144          *
145          * @param name
146          *            the name of the field to serialize.
147          * @param value
148          *            the value that is put to the persistent field.
149          */
put(String name, byte value)150         public abstract void put(String name, byte value);
151 
152         /**
153          * Puts the value of the short field identified by {@code name} to the
154          * persistent field.
155          *
156          * @param name
157          *            the name of the field to serialize.
158          * @param value
159          *            the value that is put to the persistent field.
160          */
put(String name, short value)161         public abstract void put(String name, short value);
162 
163         /**
164          * Puts the value of the integer field identified by {@code name} to the
165          * persistent field.
166          *
167          * @param name
168          *            the name of the field to serialize.
169          * @param value
170          *            the value that is put to the persistent field.
171          */
put(String name, int value)172         public abstract void put(String name, int value);
173 
174         /**
175          * Puts the value of the long field identified by {@code name} to the
176          * persistent field.
177          *
178          * @param name
179          *            the name of the field to serialize.
180          * @param value
181          *            the value that is put to the persistent field.
182          */
put(String name, long value)183         public abstract void put(String name, long value);
184 
185         /**
186          * Puts the value of the float field identified by {@code name} to the
187          * persistent field.
188          *
189          * @param name
190          *            the name of the field to serialize.
191          * @param value
192          *            the value that is put to the persistent field.
193          */
put(String name, float value)194         public abstract void put(String name, float value);
195 
196         /**
197          * Puts the value of the double field identified by {@code name} to the
198          * persistent field.
199          *
200          * @param name
201          *            the name of the field to serialize.
202          * @param value
203          *            the value that is put to the persistent field.
204          */
put(String name, double value)205         public abstract void put(String name, double value);
206 
207         /**
208          * Puts the value of the Object field identified by {@code name} to the
209          * persistent field.
210          *
211          * @param name
212          *            the name of the field to serialize.
213          * @param value
214          *            the value that is put to the persistent field.
215          */
put(String name, Object value)216         public abstract void put(String name, Object value);
217 
218         /**
219          * Writes the fields to the target stream {@code out}.
220          *
221          * @param out
222          *            the target stream
223          * @throws IOException
224          *             if an error occurs while writing to the target stream.
225          * @deprecated This method is unsafe and may corrupt the target stream.
226          *             Use ObjectOutputStream#writeFields() instead.
227          */
228         @Deprecated
write(ObjectOutput out)229         public abstract void write(ObjectOutput out) throws IOException;
230     }
231 
232     /**
233      * Constructs a new {@code ObjectOutputStream}. This default constructor can
234      * be used by subclasses that do not want to use the public constructor if
235      * it allocates unneeded data.
236      *
237      * @throws IOException
238      *             if an error occurs when creating this stream.
239      */
ObjectOutputStream()240     protected ObjectOutputStream() throws IOException {
241         /*
242          * WARNING - we should throw IOException if not called from a subclass
243          * according to the JavaDoc. Add the test.
244          */
245         this.subclassOverridingImplementation = true;
246     }
247 
248     /**
249      * Constructs a new ObjectOutputStream that writes to the OutputStream
250      * {@code output}.
251      *
252      * @param output
253      *            the non-null OutputStream to filter writes on.
254      *
255      * @throws IOException
256      *             if an error occurs while writing the object stream
257      *             header
258      */
ObjectOutputStream(OutputStream output)259     public ObjectOutputStream(OutputStream output) throws IOException {
260         this.output = (output instanceof DataOutputStream) ? (DataOutputStream) output
261                 : new DataOutputStream(output);
262         this.enableReplace = false;
263         this.protocolVersion = PROTOCOL_VERSION_2;
264         this.subclassOverridingImplementation = false;
265 
266         resetState();
267         // So write...() methods can be used by
268         // subclasses during writeStreamHeader()
269         primitiveTypes = this.output;
270         // Has to be done here according to the specification
271         writeStreamHeader();
272         primitiveTypes = null;
273     }
274 
275     /**
276      * Writes optional information for class {@code aClass} to the output
277      * stream. This optional data can be read when deserializing the class
278      * descriptor (ObjectStreamClass) for this class from an input stream. By
279      * default, no extra data is saved.
280      *
281      * @param aClass
282      *            the class to annotate.
283      * @throws IOException
284      *             if an error occurs while writing to the target stream.
285      * @see ObjectInputStream#resolveClass(ObjectStreamClass)
286      */
annotateClass(Class<?> aClass)287     protected void annotateClass(Class<?> aClass) throws IOException {
288         // By default no extra info is saved. Subclasses can override
289     }
290 
291     /**
292      * Writes optional information for a proxy class to the target stream. This
293      * optional data can be read when deserializing the proxy class from an
294      * input stream. By default, no extra data is saved.
295      *
296      * @param aClass
297      *            the proxy class to annotate.
298      * @throws IOException
299      *             if an error occurs while writing to the target stream.
300      * @see ObjectInputStream#resolveProxyClass(String[])
301      */
annotateProxyClass(Class<?> aClass)302     protected void annotateProxyClass(Class<?> aClass) throws IOException {
303         // By default no extra info is saved. Subclasses can override
304     }
305 
306     /**
307      * Do the necessary work to see if the receiver can be used to write
308      * primitive types like int, char, etc.
309      */
checkWritePrimitiveTypes()310     private void checkWritePrimitiveTypes() {
311         if (primitiveTypes == null) {
312             // If we got here we have no Stream previously created
313             // WARNING - if the stream does not grow, this code is wrong
314             primitiveTypesBuffer = new ByteArrayOutputStream(128);
315             primitiveTypes = new DataOutputStream(primitiveTypesBuffer);
316         }
317     }
318 
319     /**
320      * Closes this stream. Any buffered data is flushed. This implementation
321      * closes the target stream.
322      *
323      * @throws IOException
324      *             if an error occurs while closing this stream.
325      */
326     @Override
close()327     public void close() throws IOException {
328         // First flush what is needed (primitive data, etc)
329         flush();
330         output.close();
331     }
332 
333     /**
334      * Computes the collection of emulated fields that users can manipulate to
335      * store a representation different than the one declared by the class of
336      * the object being dumped.
337      *
338      * @see #writeFields
339      * @see #writeFieldValues(EmulatedFieldsForDumping)
340      */
computePutField()341     private void computePutField() {
342         currentPutField = new EmulatedFieldsForDumping(this, currentClass);
343     }
344 
345     /**
346      * Default method to write objects to this stream. Serializable fields
347      * defined in the object's class and superclasses are written to the output
348      * stream.
349      *
350      * @throws IOException
351      *             if an error occurs while writing to the target stream.
352      * @throws NotActiveException
353      *             if this method is not called from {@code writeObject()}.
354      * @see ObjectInputStream#defaultReadObject
355      */
defaultWriteObject()356     public void defaultWriteObject() throws IOException {
357         if (currentObject == null) {
358             throw new NotActiveException();
359         }
360         writeFieldValues(currentObject, currentClass);
361     }
362 
363     /**
364      * Writes buffered data to the target stream. This is similar to {@code
365      * flush} but the flush is not propagated to the target stream.
366      *
367      * @throws IOException
368      *             if an error occurs while writing to the target stream.
369      */
drain()370     protected void drain() throws IOException {
371         if (primitiveTypes == null || primitiveTypesBuffer == null) {
372             return;
373         }
374 
375         // If we got here we have a Stream previously created
376         int offset = 0;
377         byte[] written = primitiveTypesBuffer.toByteArray();
378         // Normalize the primitive data
379         while (offset < written.length) {
380             int toWrite = written.length - offset > 1024 ? 1024
381                     : written.length - offset;
382             if (toWrite < 256) {
383                 output.writeByte(TC_BLOCKDATA);
384                 output.writeByte((byte) toWrite);
385             } else {
386                 output.writeByte(TC_BLOCKDATALONG);
387                 output.writeInt(toWrite);
388             }
389 
390             // write primitive types we had and the marker of end-of-buffer
391             output.write(written, offset, toWrite);
392             offset += toWrite;
393         }
394 
395         // and now we're clean to a state where we can write an object
396         primitiveTypes = null;
397         primitiveTypesBuffer = null;
398     }
399 
400     /**
401      * Dumps the parameter {@code obj} only if it is {@code null}
402      * or an object that has already been dumped previously.
403      *
404      * @param obj
405      *            Object to check if an instance previously dumped by this
406      *            stream.
407      * @return -1 if it is an instance which has not been dumped yet (and this
408      *         method does nothing). The handle if {@code obj} is an
409      *         instance which has been dumped already.
410      *
411      * @throws IOException
412      *             If an error occurs attempting to save {@code null} or
413      *             a cyclic reference.
414      */
dumpCycle(Object obj)415     private int dumpCycle(Object obj) throws IOException {
416         // If the object has been saved already, save its handle only
417         int handle = objectsWritten.get(obj);
418         if (handle != -1) {
419             writeCyclicReference(handle);
420             return handle;
421         }
422         return -1;
423     }
424 
425     /**
426      * Enables object replacement for this stream. By default this is not
427      * enabled. Only trusted subclasses (loaded with system class loader) are
428      * allowed to change this status.
429      *
430      * @param enable
431      *            {@code true} to enable object replacement; {@code false} to
432      *            disable it.
433      * @return the previous setting.
434      * @see #replaceObject
435      * @see ObjectInputStream#enableResolveObject
436      */
enableReplaceObject(boolean enable)437     protected boolean enableReplaceObject(boolean enable) {
438         boolean originalValue = enableReplace;
439         enableReplace = enable;
440         return originalValue;
441     }
442 
443     /**
444      * Writes buffered data to the target stream and calls the {@code flush}
445      * method of the target stream.
446      *
447      * @throws IOException
448      *             if an error occurs while writing to or flushing the output
449      *             stream.
450      */
451     @Override
flush()452     public void flush() throws IOException {
453         drain();
454         output.flush();
455     }
456 
457     /**
458      * Return the next handle to be used to indicate cyclic
459      * references being saved to the stream.
460      *
461      * @return the next handle to represent the next cyclic reference
462      */
nextHandle()463     private int nextHandle() {
464         return currentHandle++;
465     }
466 
467     /**
468      * Gets this stream's {@code PutField} object. This object provides access
469      * to the persistent fields that are eventually written to the output
470      * stream. It is used to transfer the values from the fields of the object
471      * that is currently being written to the persistent fields.
472      *
473      * @return the PutField object from which persistent fields can be accessed
474      *         by name.
475      * @throws IOException
476      *             if an I/O error occurs.
477      * @throws NotActiveException
478      *             if this method is not called from {@code writeObject()}.
479      * @see ObjectInputStream#defaultReadObject
480      */
putFields()481     public PutField putFields() throws IOException {
482         if (currentObject == null) {
483             throw new NotActiveException();
484         }
485         if (currentPutField == null) {
486             computePutField();
487         }
488         return currentPutField;
489     }
490 
registerObjectWritten(Object obj)491     private int registerObjectWritten(Object obj) {
492         int handle = nextHandle();
493         objectsWritten.put(obj, handle);
494         return handle;
495     }
496 
497     /**
498      * Remove the unshared object from the table, and restore any previous
499      * handle.
500      *
501      * @param obj
502      *            Non-null object being dumped.
503      * @param previousHandle
504      *            The handle of the previous identical object dumped
505      */
removeUnsharedReference(Object obj, int previousHandle)506     private void removeUnsharedReference(Object obj, int previousHandle) {
507         if (previousHandle != -1) {
508             objectsWritten.put(obj, previousHandle);
509         } else {
510             objectsWritten.remove(obj);
511         }
512     }
513 
514     /**
515      * Allows trusted subclasses to substitute the specified original {@code
516      * object} with a new object. Object substitution has to be activated first
517      * with calling {@code enableReplaceObject(true)}. This implementation just
518      * returns {@code object}.
519      *
520      * @param object
521      *            the original object for which a replacement may be defined.
522      * @return the replacement object for {@code object}.
523      * @throws IOException
524      *             if any I/O error occurs while creating the replacement
525      *             object.
526      * @see #enableReplaceObject
527      * @see ObjectInputStream#enableResolveObject
528      * @see ObjectInputStream#resolveObject
529      */
replaceObject(Object object)530     protected Object replaceObject(Object object) throws IOException {
531         // By default no object replacement. Subclasses can override
532         return object;
533     }
534 
535     /**
536      * Resets the state of this stream. A marker is written to the stream, so
537      * that the corresponding input stream will also perform a reset at the same
538      * point. Objects previously written are no longer remembered, so they will
539      * be written again (instead of a cyclical reference) if found in the object
540      * graph.
541      *
542      * @throws IOException
543      *             if {@code reset()} is called during the serialization of an
544      *             object.
545      */
reset()546     public void reset() throws IOException {
547         // First we flush what we have
548         drain();
549         /*
550          * And dump a reset marker, so that the ObjectInputStream can reset
551          * itself at the same point
552          */
553         output.writeByte(TC_RESET);
554         // Now we reset ourselves
555         resetState();
556     }
557 
558     /**
559      * Reset the collection of objects already dumped by the receiver. If the
560      * objects are found again in the object graph, the receiver will dump them
561      * again, instead of a handle (cyclic reference).
562      *
563      */
resetSeenObjects()564     private void resetSeenObjects() {
565         objectsWritten = new SerializationHandleMap();
566         currentHandle = baseWireHandle;
567     }
568 
569     /**
570      * Reset the receiver. The collection of objects already dumped by the
571      * receiver is reset, and internal structures are also reset so that the
572      * receiver knows it is in a fresh clean state.
573      *
574      */
resetState()575     private void resetState() {
576         resetSeenObjects();
577         nestedLevels = 0;
578     }
579 
580     /**
581      * Sets the specified protocol version to be used by this stream.
582      *
583      * @param version
584      *            the protocol version to be used. Use a {@code
585      *            PROTOCOL_VERSION_x} constant from {@code
586      *            java.io.ObjectStreamConstants}.
587      * @throws IllegalArgumentException
588      *             if an invalid {@code version} is specified.
589      * @throws IOException
590      *             if an I/O error occurs.
591      * @see ObjectStreamConstants#PROTOCOL_VERSION_1
592      * @see ObjectStreamConstants#PROTOCOL_VERSION_2
593      */
useProtocolVersion(int version)594     public void useProtocolVersion(int version) throws IOException {
595         if (!objectsWritten.isEmpty()) {
596             throw new IllegalStateException("Cannot set protocol version when stream in use");
597         }
598         if (version != ObjectStreamConstants.PROTOCOL_VERSION_1
599                 && version != ObjectStreamConstants.PROTOCOL_VERSION_2) {
600             throw new IllegalArgumentException("Unknown protocol: " + version);
601         }
602         protocolVersion = version;
603     }
604 
605     /**
606      * Writes {@code count} bytes from the byte array {@code buffer} starting at
607      * offset {@code index} to the target stream. Blocks until all bytes are
608      * written.
609      *
610      * @param buffer
611      *            the buffer to write.
612      * @param offset
613      *            the index of the first byte in {@code buffer} to write.
614      * @param length
615      *            the number of bytes from {@code buffer} to write to the output
616      *            stream.
617      * @throws IOException
618      *             if an error occurs while writing to the target stream.
619      */
620     @Override
write(byte[] buffer, int offset, int length)621     public void write(byte[] buffer, int offset, int length) throws IOException {
622         checkWritePrimitiveTypes();
623         primitiveTypes.write(buffer, offset, length);
624     }
625 
626     /**
627      * Writes a single byte to the target stream. Only the least significant
628      * byte of the integer {@code value} is written to the stream. Blocks until
629      * the byte is actually written.
630      *
631      * @param value
632      *            the byte to write.
633      * @throws IOException
634      *             if an error occurs while writing to the target stream.
635      */
636     @Override
write(int value)637     public void write(int value) throws IOException {
638         checkWritePrimitiveTypes();
639         primitiveTypes.write(value);
640     }
641 
642     /**
643      * Writes a boolean to the target stream.
644      *
645      * @param value
646      *            the boolean value to write to the target stream.
647      * @throws IOException
648      *             if an error occurs while writing to the target stream.
649      */
writeBoolean(boolean value)650     public void writeBoolean(boolean value) throws IOException {
651         checkWritePrimitiveTypes();
652         primitiveTypes.writeBoolean(value);
653     }
654 
655     /**
656      * Writes a byte (8 bit) to the target stream.
657      *
658      * @param value
659      *            the byte to write to the target stream.
660      * @throws IOException
661      *             if an error occurs while writing to the target stream.
662      */
writeByte(int value)663     public void writeByte(int value) throws IOException {
664         checkWritePrimitiveTypes();
665         primitiveTypes.writeByte(value);
666     }
667 
668     /**
669      * Writes the string {@code value} as a sequence of bytes to the target
670      * stream. Only the least significant byte of each character in the string
671      * is written.
672      *
673      * @param value
674      *            the string to write to the target stream.
675      * @throws IOException
676      *             if an error occurs while writing to the target stream.
677      */
writeBytes(String value)678     public void writeBytes(String value) throws IOException {
679         checkWritePrimitiveTypes();
680         primitiveTypes.writeBytes(value);
681     }
682 
683     /**
684      * Writes a character (16 bit) to the target stream.
685      *
686      * @param value
687      *            the character to write to the target stream.
688      * @throws IOException
689      *             if an error occurs while writing to the target stream.
690      */
writeChar(int value)691     public void writeChar(int value) throws IOException {
692         checkWritePrimitiveTypes();
693         primitiveTypes.writeChar(value);
694     }
695 
696     /**
697      * Writes the string {@code value} as a sequence of characters to the target
698      * stream.
699      *
700      * @param value
701      *            the string to write to the target stream.
702      * @throws IOException
703      *             if an error occurs while writing to the target stream.
704      */
writeChars(String value)705     public void writeChars(String value) throws IOException {
706         checkWritePrimitiveTypes();
707         primitiveTypes.writeChars(value);
708     }
709 
710     /**
711      * Write a class descriptor {@code classDesc} (an
712      * {@code ObjectStreamClass}) to the stream.
713      *
714      * @param classDesc
715      *            The class descriptor (an {@code ObjectStreamClass}) to
716      *            be dumped
717      * @param unshared
718      *            Write the object unshared
719      * @return the handle assigned to the class descriptor
720      *
721      * @throws IOException
722      *             If an IO exception happened when writing the class
723      *             descriptor.
724      */
writeClassDesc(ObjectStreamClass classDesc, boolean unshared)725     private int writeClassDesc(ObjectStreamClass classDesc, boolean unshared) throws IOException {
726         if (classDesc == null) {
727             writeNull();
728             return -1;
729         }
730         int handle = -1;
731         if (!unshared) {
732             handle = dumpCycle(classDesc);
733         }
734         if (handle == -1) {
735             Class<?> classToWrite = classDesc.forClass();
736             int previousHandle = -1;
737             if (unshared) {
738                 previousHandle = objectsWritten.get(classDesc);
739             }
740             // If we got here, it is a new (non-null) classDesc that will have
741             // to be registered as well
742             handle = registerObjectWritten(classDesc);
743 
744             if (classDesc.isProxy()) {
745                 output.writeByte(TC_PROXYCLASSDESC);
746                 Class<?>[] interfaces = classToWrite.getInterfaces();
747                 output.writeInt(interfaces.length);
748                 for (int i = 0; i < interfaces.length; i++) {
749                     output.writeUTF(interfaces[i].getName());
750                 }
751                 annotateProxyClass(classToWrite);
752                 output.writeByte(TC_ENDBLOCKDATA);
753                 writeClassDesc(proxyClassDesc, false);
754                 if (unshared) {
755                     // remove reference to unshared object
756                     removeUnsharedReference(classDesc, previousHandle);
757                 }
758                 return handle;
759             }
760 
761             output.writeByte(TC_CLASSDESC);
762             if (protocolVersion == PROTOCOL_VERSION_1) {
763                 writeNewClassDesc(classDesc);
764             } else {
765                 // So write...() methods can be used by
766                 // subclasses during writeClassDescriptor()
767                 primitiveTypes = output;
768                 writeClassDescriptor(classDesc);
769                 primitiveTypes = null;
770             }
771             // Extra class info (optional)
772             annotateClass(classToWrite);
773             drain(); // flush primitive types in the annotation
774             output.writeByte(TC_ENDBLOCKDATA);
775             writeClassDesc(classDesc.getSuperclass(), unshared);
776             if (unshared) {
777                 // remove reference to unshared object
778                 removeUnsharedReference(classDesc, previousHandle);
779             }
780         }
781         return handle;
782     }
783 
784     /**
785      * Writes a handle representing a cyclic reference (object previously
786      * dumped).
787      */
writeCyclicReference(int handle)788     private void writeCyclicReference(int handle) throws IOException {
789         output.writeByte(TC_REFERENCE);
790         output.writeInt(handle);
791     }
792 
793     /**
794      * Writes a double (64 bit) to the target stream.
795      *
796      * @param value
797      *            the double to write to the target stream.
798      * @throws IOException
799      *             if an error occurs while writing to the target stream.
800      */
writeDouble(double value)801     public void writeDouble(double value) throws IOException {
802         checkWritePrimitiveTypes();
803         primitiveTypes.writeDouble(value);
804     }
805 
806     /**
807      * Writes a collection of field descriptors (name, type name, etc) for the
808      * class descriptor {@code classDesc} (an
809      * {@code ObjectStreamClass})
810      *
811      * @param classDesc
812      *            The class descriptor (an {@code ObjectStreamClass})
813      *            for which to write field information
814      * @param externalizable
815      *            true if the descriptors are externalizable
816      *
817      * @throws IOException
818      *             If an IO exception happened when writing the field
819      *             descriptors.
820      *
821      * @see #writeObject(Object)
822      */
writeFieldDescriptors(ObjectStreamClass classDesc, boolean externalizable)823     private void writeFieldDescriptors(ObjectStreamClass classDesc, boolean externalizable) throws IOException {
824         Class<?> loadedClass = classDesc.forClass();
825         ObjectStreamField[] fields = null;
826         int fieldCount = 0;
827 
828         // The fields of String are not needed since Strings are treated as
829         // primitive types
830         if (!externalizable && loadedClass != ObjectStreamClass.STRINGCLASS) {
831             fields = classDesc.fields();
832             fieldCount = fields.length;
833         }
834 
835         // Field count
836         output.writeShort(fieldCount);
837         // Field names
838         for (int i = 0; i < fieldCount; i++) {
839             ObjectStreamField f = fields[i];
840             boolean wasPrimitive = f.writeField(output);
841             if (!wasPrimitive) {
842                 writeObject(f.getTypeString());
843             }
844         }
845     }
846 
847     /**
848      * Writes the fields of the object currently being written to the target
849      * stream. The field values are buffered in the currently active {@code
850      * PutField} object, which can be accessed by calling {@code putFields()}.
851      *
852      * @throws IOException
853      *             if an error occurs while writing to the target stream.
854      * @throws NotActiveException
855      *             if there are no fields to write to the target stream.
856      * @see #putFields
857      */
writeFields()858     public void writeFields() throws IOException {
859         // Has to have fields to write
860         if (currentPutField == null) {
861             throw new NotActiveException();
862         }
863         writeFieldValues(currentPutField);
864     }
865 
866     /**
867      * Writes a collection of field values for the emulated fields
868      * {@code emulatedFields}
869      *
870      * @param emulatedFields
871      *            an {@code EmulatedFieldsForDumping}, concrete subclass
872      *            of {@code PutField}
873      *
874      * @throws IOException
875      *             If an IO exception happened when writing the field values.
876      *
877      * @see #writeFields
878      * @see #writeObject(Object)
879      */
writeFieldValues(EmulatedFieldsForDumping emulatedFields)880     private void writeFieldValues(EmulatedFieldsForDumping emulatedFields) throws IOException {
881         // Access internal fields which we can set/get. Users can't do this.
882         EmulatedFields accessibleSimulatedFields = emulatedFields.emulatedFields();
883         for (EmulatedFields.ObjectSlot slot : accessibleSimulatedFields.slots()) {
884             Object fieldValue = slot.getFieldValue();
885             Class<?> type = slot.getField().getType();
886             if (type == int.class) {
887                 output.writeInt(fieldValue != null ? ((Integer) fieldValue).intValue() : 0);
888             } else if (type == byte.class) {
889                 output.writeByte(fieldValue != null ? ((Byte) fieldValue).byteValue() : 0);
890             } else if (type == char.class) {
891                 output.writeChar(fieldValue != null ? ((Character) fieldValue).charValue() : 0);
892             } else if (type == short.class) {
893                 output.writeShort(fieldValue != null ? ((Short) fieldValue).shortValue() : 0);
894             } else if (type == boolean.class) {
895                 output.writeBoolean(fieldValue != null ? ((Boolean) fieldValue).booleanValue() : false);
896             } else if (type == long.class) {
897                 output.writeLong(fieldValue != null ? ((Long) fieldValue).longValue() : 0);
898             } else if (type == float.class) {
899                 output.writeFloat(fieldValue != null ? ((Float) fieldValue).floatValue() : 0);
900             } else if (type == double.class) {
901                 output.writeDouble(fieldValue != null ? ((Double) fieldValue).doubleValue() : 0);
902             } else {
903                 // Either array or Object
904                 writeObject(fieldValue);
905             }
906         }
907     }
908 
909     /**
910      * Writes a collection of field values for the fields described by class
911      * descriptor {@code classDesc} (an {@code ObjectStreamClass}).
912      * This is the default mechanism, when emulated fields (an
913      * {@code PutField}) are not used. Actual values to dump are fetched
914      * directly from object {@code obj}.
915      *
916      * @param obj
917      *            Instance from which to fetch field values to dump.
918      * @param classDesc
919      *            A class descriptor (an {@code ObjectStreamClass})
920      *            defining which fields should be dumped.
921      *
922      * @throws IOException
923      *             If an IO exception happened when writing the field values.
924      *
925      * @see #writeObject(Object)
926      */
writeFieldValues(Object obj, ObjectStreamClass classDesc)927     private void writeFieldValues(Object obj, ObjectStreamClass classDesc) throws IOException {
928         for (ObjectStreamField fieldDesc : classDesc.fields()) {
929             try {
930                 Class<?> type = fieldDesc.getTypeInternal();
931                 Field field = classDesc.checkAndGetReflectionField(fieldDesc);
932                 if (field == null) {
933                     throw new InvalidClassException(classDesc.getName()
934                             + " doesn't have a serializable field " + fieldDesc.getName()
935                             + " of type " + type);
936                 }
937                 if (type == byte.class) {
938                     output.writeByte(field.getByte(obj));
939                 } else if (type == char.class) {
940                     output.writeChar(field.getChar(obj));
941                 } else if (type == double.class) {
942                     output.writeDouble(field.getDouble(obj));
943                 } else if (type == float.class) {
944                     output.writeFloat(field.getFloat(obj));
945                 } else if (type == int.class) {
946                     output.writeInt(field.getInt(obj));
947                 } else if (type == long.class) {
948                     output.writeLong(field.getLong(obj));
949                 } else if (type == short.class) {
950                     output.writeShort(field.getShort(obj));
951                 } else if (type == boolean.class) {
952                     output.writeBoolean(field.getBoolean(obj));
953                 } else {
954                     // Reference types (including arrays).
955                     Object objField = field.get(obj);
956                     if (fieldDesc.isUnshared()) {
957                         writeUnshared(objField);
958                     } else {
959                         writeObject(objField);
960                     }
961                 }
962             } catch (IllegalAccessException iae) {
963                 // ObjectStreamField should have called setAccessible(true).
964                 throw new AssertionError(iae);
965             } catch (NoSuchFieldError nsf) {
966                 // The user defined serialPersistentFields but did not provide
967                 // the glue to transfer values in writeObject, so we ended up using
968                 // the default mechanism but failed to set the emulated field.
969                 throw new InvalidClassException(classDesc.getName());
970             }
971         }
972     }
973 
974     /**
975      * Writes a float (32 bit) to the target stream.
976      *
977      * @param value
978      *            the float to write to the target stream.
979      * @throws IOException
980      *             if an error occurs while writing to the target stream.
981      */
writeFloat(float value)982     public void writeFloat(float value) throws IOException {
983         checkWritePrimitiveTypes();
984         primitiveTypes.writeFloat(value);
985     }
986 
987     /**
988      * Walks the hierarchy of classes described by class descriptor
989      * {@code classDesc} and writes the field values corresponding to
990      * fields declared by the corresponding class descriptor. The instance to
991      * fetch field values from is {@code object}. If the class
992      * (corresponding to class descriptor {@code classDesc}) defines
993      * private instance method {@code writeObject} it will be used to
994      * dump field values.
995      *
996      * @param object
997      *            Instance from which to fetch field values to dump.
998      * @param classDesc
999      *            A class descriptor (an {@code ObjectStreamClass})
1000      *            defining which fields should be dumped.
1001      *
1002      * @throws IOException
1003      *             If an IO exception happened when writing the field values in
1004      *             the hierarchy.
1005      * @throws NotActiveException
1006      *             If the given object is not active
1007      *
1008      * @see #defaultWriteObject
1009      * @see #writeObject(Object)
1010      */
writeHierarchy(Object object, ObjectStreamClass classDesc)1011     private void writeHierarchy(Object object, ObjectStreamClass classDesc)
1012             throws IOException, NotActiveException {
1013         if (object == null) {
1014             throw new NotActiveException();
1015         }
1016 
1017         // Fields are written from class closest to Object to leaf class
1018         // (down the chain)
1019         List<ObjectStreamClass> hierarchy = classDesc.getHierarchy();
1020         for (int i = 0, end = hierarchy.size(); i < end; ++i) {
1021             ObjectStreamClass osc = hierarchy.get(i);
1022             // Have to do this before calling defaultWriteObject or anything
1023             // that calls defaultWriteObject
1024             currentObject = object;
1025             currentClass = osc;
1026 
1027             // See if the object has a writeObject method. If so, run it
1028             try {
1029                 boolean executed = false;
1030                 if (osc.hasMethodWriteObject()) {
1031                     final Method method = osc.getMethodWriteObject();
1032                     try {
1033                         method.invoke(object, new Object[] { this });
1034                         executed = true;
1035                     } catch (InvocationTargetException e) {
1036                         Throwable ex = e.getTargetException();
1037                         if (ex instanceof RuntimeException) {
1038                             throw (RuntimeException) ex;
1039                         } else if (ex instanceof Error) {
1040                             throw (Error) ex;
1041                         }
1042                         throw (IOException) ex;
1043                     } catch (IllegalAccessException e) {
1044                         throw new RuntimeException(e.toString());
1045                     }
1046                 }
1047 
1048                 if (executed) {
1049                     drain();
1050                     output.writeByte(TC_ENDBLOCKDATA);
1051                 } else {
1052                     // If the object did not have a writeMethod, call
1053                     // defaultWriteObject
1054                     defaultWriteObject();
1055                 }
1056             } finally {
1057                 // Cleanup, needs to run always so that we can later detect
1058                 // invalid calls to defaultWriteObject
1059                 currentObject = null;
1060                 currentClass = null;
1061                 currentPutField = null;
1062             }
1063         }
1064     }
1065 
1066     /**
1067      * Writes an integer (32 bit) to the target stream.
1068      *
1069      * @param value
1070      *            the integer to write to the target stream.
1071      * @throws IOException
1072      *             if an error occurs while writing to the target stream.
1073      */
writeInt(int value)1074     public void writeInt(int value) throws IOException {
1075         checkWritePrimitiveTypes();
1076         primitiveTypes.writeInt(value);
1077     }
1078 
1079     /**
1080      * Writes a long (64 bit) to the target stream.
1081      *
1082      * @param value
1083      *            the long to write to the target stream.
1084      * @throws IOException
1085      *             if an error occurs while writing to the target stream.
1086      */
writeLong(long value)1087     public void writeLong(long value) throws IOException {
1088         checkWritePrimitiveTypes();
1089         primitiveTypes.writeLong(value);
1090     }
1091 
1092     /**
1093      * Write array {@code array} of class {@code arrayClass} with
1094      * component type {@code componentType} into the receiver. It is
1095      * assumed the array has not been dumped yet. Returns
1096      * the handle for this object (array) which is dumped here.
1097      *
1098      * @param array
1099      *            The array object to dump
1100      * @param arrayClass
1101      *            A {@code java.lang.Class} representing the class of the
1102      *            array
1103      * @param componentType
1104      *            A {@code java.lang.Class} representing the array
1105      *            component type
1106      * @return the handle assigned to the array
1107      *
1108      * @throws IOException
1109      *             If an IO exception happened when writing the array.
1110      */
writeNewArray(Object array, Class<?> arrayClass, ObjectStreamClass arrayClDesc, Class<?> componentType, boolean unshared)1111     private int writeNewArray(Object array, Class<?> arrayClass, ObjectStreamClass arrayClDesc,
1112             Class<?> componentType, boolean unshared) throws IOException {
1113         output.writeByte(TC_ARRAY);
1114         writeClassDesc(arrayClDesc, false);
1115 
1116         int handle = nextHandle();
1117         if (!unshared) {
1118             objectsWritten.put(array, handle);
1119         }
1120 
1121         // Now we have code duplication just because Java is typed. We have to
1122         // write N elements and assign to array positions, but we must typecast
1123         // the array first, and also call different methods depending on the
1124         // elements.
1125 
1126         if (componentType.isPrimitive()) {
1127             if (componentType == int.class) {
1128                 int[] intArray = (int[]) array;
1129                 output.writeInt(intArray.length);
1130                 for (int i = 0; i < intArray.length; i++) {
1131                     output.writeInt(intArray[i]);
1132                 }
1133             } else if (componentType == byte.class) {
1134                 byte[] byteArray = (byte[]) array;
1135                 output.writeInt(byteArray.length);
1136                 output.write(byteArray, 0, byteArray.length);
1137             } else if (componentType == char.class) {
1138                 char[] charArray = (char[]) array;
1139                 output.writeInt(charArray.length);
1140                 for (int i = 0; i < charArray.length; i++) {
1141                     output.writeChar(charArray[i]);
1142                 }
1143             } else if (componentType == short.class) {
1144                 short[] shortArray = (short[]) array;
1145                 output.writeInt(shortArray.length);
1146                 for (int i = 0; i < shortArray.length; i++) {
1147                     output.writeShort(shortArray[i]);
1148                 }
1149             } else if (componentType == boolean.class) {
1150                 boolean[] booleanArray = (boolean[]) array;
1151                 output.writeInt(booleanArray.length);
1152                 for (int i = 0; i < booleanArray.length; i++) {
1153                     output.writeBoolean(booleanArray[i]);
1154                 }
1155             } else if (componentType == long.class) {
1156                 long[] longArray = (long[]) array;
1157                 output.writeInt(longArray.length);
1158                 for (int i = 0; i < longArray.length; i++) {
1159                     output.writeLong(longArray[i]);
1160                 }
1161             } else if (componentType == float.class) {
1162                 float[] floatArray = (float[]) array;
1163                 output.writeInt(floatArray.length);
1164                 for (int i = 0; i < floatArray.length; i++) {
1165                     output.writeFloat(floatArray[i]);
1166                 }
1167             } else if (componentType == double.class) {
1168                 double[] doubleArray = (double[]) array;
1169                 output.writeInt(doubleArray.length);
1170                 for (int i = 0; i < doubleArray.length; i++) {
1171                     output.writeDouble(doubleArray[i]);
1172                 }
1173             } else {
1174                 throw new InvalidClassException("Wrong base type in " + arrayClass.getName());
1175             }
1176         } else {
1177             // Array of Objects
1178             Object[] objectArray = (Object[]) array;
1179             output.writeInt(objectArray.length);
1180             for (int i = 0; i < objectArray.length; i++) {
1181                 // TODO: This place is the opportunity for enhancement
1182                 //      We can implement writing elements through fast-path,
1183                 //      without setting up the context (see writeObject()) for
1184                 //      each element with public API
1185                 writeObject(objectArray[i]);
1186             }
1187         }
1188         return handle;
1189     }
1190 
1191     /**
1192      * Write class {@code object} into the receiver. It is assumed the
1193      * class has not been dumped yet. Classes are not really dumped, but a class
1194      * descriptor ({@code ObjectStreamClass}) that corresponds to them.
1195      * Returns the handle for this object (class) which is dumped here.
1196      *
1197      * @param object
1198      *            The {@code java.lang.Class} object to dump
1199      * @return the handle assigned to the class being dumped
1200      *
1201      * @throws IOException
1202      *             If an IO exception happened when writing the class.
1203      */
writeNewClass(Class<?> object, boolean unshared)1204     private int writeNewClass(Class<?> object, boolean unshared) throws IOException {
1205         output.writeByte(TC_CLASS);
1206 
1207         // Instances of java.lang.Class are always Serializable, even if their
1208         // instances aren't (e.g. java.lang.Object.class).
1209         // We cannot call lookup because it returns null if the parameter
1210         // represents instances that cannot be serialized, and that is not what
1211         // we want.
1212         ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(object);
1213 
1214         // The handle for the classDesc is NOT the handle for the class object
1215         // being dumped. We must allocate a new handle and return it.
1216         if (clDesc.isEnum()) {
1217             writeEnumDesc(clDesc, unshared);
1218         } else {
1219             writeClassDesc(clDesc, unshared);
1220         }
1221 
1222         int handle = nextHandle();
1223         if (!unshared) {
1224             objectsWritten.put(object, handle);
1225         }
1226 
1227         return handle;
1228     }
1229 
1230     /**
1231      * Write class descriptor {@code classDesc} into the receiver. It is
1232      * assumed the class descriptor has not been dumped yet. The class
1233      * descriptors for the superclass chain will be dumped as well. Returns
1234      * the handle for this object (class descriptor) which is dumped here.
1235      *
1236      * @param classDesc
1237      *            The {@code ObjectStreamClass} object to dump
1238      *
1239      * @throws IOException
1240      *             If an IO exception happened when writing the class
1241      *             descriptor.
1242      */
writeNewClassDesc(ObjectStreamClass classDesc)1243     private void writeNewClassDesc(ObjectStreamClass classDesc)
1244             throws IOException {
1245         output.writeUTF(classDesc.getName());
1246         output.writeLong(classDesc.getSerialVersionUID());
1247         byte flags = classDesc.getFlags();
1248 
1249         boolean externalizable = classDesc.isExternalizable();
1250 
1251         if (externalizable) {
1252             if (protocolVersion == PROTOCOL_VERSION_1) {
1253                 flags &= NOT_SC_BLOCK_DATA;
1254             } else {
1255                 // Change for 1.2. Objects can be saved in old format
1256                 // (PROTOCOL_VERSION_1) or in the 1.2 format (PROTOCOL_VERSION_2).
1257                 flags |= SC_BLOCK_DATA;
1258             }
1259         }
1260         output.writeByte(flags);
1261         if ((SC_ENUM | SC_SERIALIZABLE) != classDesc.getFlags()) {
1262             writeFieldDescriptors(classDesc, externalizable);
1263         } else {
1264             // enum write no fields
1265             output.writeShort(0);
1266         }
1267     }
1268 
1269     /**
1270      * Writes a class descriptor to the target stream.
1271      *
1272      * @param classDesc
1273      *            the class descriptor to write to the target stream.
1274      * @throws IOException
1275      *             if an error occurs while writing to the target stream.
1276      */
writeClassDescriptor(ObjectStreamClass classDesc)1277     protected void writeClassDescriptor(ObjectStreamClass classDesc)
1278             throws IOException {
1279         writeNewClassDesc(classDesc);
1280     }
1281 
1282     /**
1283      * Write exception {@code ex} into the receiver. It is assumed the
1284      * exception has not been dumped yet. Returns
1285      * the handle for this object (exception) which is dumped here.
1286      * This is used to dump the exception instance that happened (if any) when
1287      * dumping the original object graph. The set of seen objects will be reset
1288      * just before and just after dumping this exception object.
1289      *
1290      * When exceptions are found normally in the object graph, they are dumped
1291      * as a regular object, and not by this method. In that case, the set of
1292      * "known objects" is not reset.
1293      *
1294      * @param ex
1295      *            Exception object to dump
1296      *
1297      * @throws IOException
1298      *             If an IO exception happened when writing the exception
1299      *             object.
1300      */
writeNewException(Exception ex)1301     private void writeNewException(Exception ex) throws IOException {
1302         output.writeByte(TC_EXCEPTION);
1303         resetSeenObjects();
1304         writeObjectInternal(ex, false, false, false); // No replacements
1305         resetSeenObjects();
1306     }
1307 
1308     /**
1309      * Write object {@code object} of class {@code theClass} into
1310      * the receiver. It is assumed the object has not been dumped yet.
1311      * Return the handle for this object which
1312      * is dumped here.
1313      *
1314      * If the object implements {@code Externalizable} its
1315      * {@code writeExternal} is called. Otherwise, all fields described
1316      * by the class hierarchy is dumped. Each class can define how its declared
1317      * instance fields are dumped by defining a private method
1318      * {@code writeObject}
1319      *
1320      * @param object
1321      *            The object to dump
1322      * @param theClass
1323      *            A {@code java.lang.Class} representing the class of the
1324      *            object
1325      * @param unshared
1326      *            Write the object unshared
1327      * @return the handle assigned to the object
1328      *
1329      * @throws IOException
1330      *             If an IO exception happened when writing the object.
1331      */
writeNewObject(Object object, Class<?> theClass, ObjectStreamClass clDesc, boolean unshared)1332     private int writeNewObject(Object object, Class<?> theClass, ObjectStreamClass clDesc,
1333             boolean unshared) throws IOException {
1334         // Not String, not null, not array, not cyclic reference
1335 
1336         EmulatedFieldsForDumping originalCurrentPutField = currentPutField; // save
1337         currentPutField = null; // null it, to make sure one will be computed if
1338         // needed
1339 
1340         boolean externalizable = clDesc.isExternalizable();
1341         boolean serializable = clDesc.isSerializable();
1342         if (!externalizable && !serializable) {
1343             // Object is neither externalizable nor serializable. Error
1344             throw new NotSerializableException(theClass.getName());
1345         }
1346 
1347         // Either serializable or externalizable, now we can save info
1348         output.writeByte(TC_OBJECT);
1349         writeClassDesc(clDesc, false);
1350         int previousHandle = -1;
1351         if (unshared) {
1352             previousHandle = objectsWritten.get(object);
1353         }
1354 
1355         int handle = registerObjectWritten(object);
1356 
1357         // This is how we know what to do in defaultWriteObject. And it is also
1358         // used by defaultWriteObject to check if it was called from an invalid
1359         // place.
1360         // It allows writeExternal to call defaultWriteObject and have it work.
1361         currentObject = object;
1362         currentClass = clDesc;
1363         try {
1364             if (externalizable) {
1365                 boolean noBlockData = protocolVersion == PROTOCOL_VERSION_1;
1366                 if (noBlockData) {
1367                     primitiveTypes = output;
1368                 }
1369                 // Object is externalizable, just call its own method
1370                 ((Externalizable) object).writeExternal(this);
1371                 if (noBlockData) {
1372                     primitiveTypes = null;
1373                 } else {
1374                     // Similar to the code in writeHierarchy when object
1375                     // implements writeObject.
1376                     // Any primitive data has to be flushed and a tag must be
1377                     // written
1378                     drain();
1379                     output.writeByte(TC_ENDBLOCKDATA);
1380                 }
1381             } else { // If it got here, it has to be Serializable
1382                 // Object is serializable. Walk the class chain writing the
1383                 // fields
1384                 writeHierarchy(object, currentClass);
1385             }
1386         } finally {
1387             // Cleanup, needs to run always so that we can later detect invalid
1388             // calls to defaultWriteObject
1389             if (unshared) {
1390                 // remove reference to unshared object
1391                 removeUnsharedReference(object, previousHandle);
1392             }
1393             currentObject = null;
1394             currentClass = null;
1395             currentPutField = originalCurrentPutField;
1396         }
1397 
1398         return handle;
1399     }
1400 
1401     /**
1402      * Write String {@code object} into the receiver. It is assumed the
1403      * String has not been dumped yet. Returns the handle for this object (String) which is dumped here.
1404      * Strings are saved encoded with {@link DataInput modified UTF-8}.
1405      *
1406      * @param object
1407      *            the string to dump.
1408      * @return the handle assigned to the String being dumped
1409      *
1410      * @throws IOException
1411      *             If an IO exception happened when writing the String.
1412      */
writeNewString(String object, boolean unshared)1413     private int writeNewString(String object, boolean unshared) throws IOException {
1414         long count = ModifiedUtf8.countBytes(object, false);
1415         byte[] buffer;
1416         int offset = 0;
1417         if (count <= 0xffff) {
1418             buffer = new byte[1 + SizeOf.SHORT + (int) count];
1419             buffer[offset++] = TC_STRING;
1420             Memory.pokeShort(buffer, offset, (short) count, ByteOrder.BIG_ENDIAN);
1421             offset += SizeOf.SHORT;
1422         } else {
1423             buffer = new byte[1 + SizeOf.LONG + (int) count];
1424             buffer[offset++] = TC_LONGSTRING;
1425             Memory.pokeLong(buffer, offset, count, ByteOrder.BIG_ENDIAN);
1426             offset += SizeOf.LONG;
1427         }
1428         ModifiedUtf8.encode(buffer, offset, object);
1429         output.write(buffer, 0, buffer.length);
1430 
1431         int handle = nextHandle();
1432         if (!unshared) {
1433             objectsWritten.put(object, handle);
1434         }
1435 
1436         return handle;
1437     }
1438 
1439     /**
1440      * Write a special tag that indicates the value {@code null} into the
1441      * receiver.
1442      *
1443      * @throws IOException
1444      *             If an IO exception happened when writing the tag for
1445      *             {@code null}.
1446      */
writeNull()1447     private void writeNull() throws IOException {
1448         output.writeByte(TC_NULL);
1449     }
1450 
1451     /**
1452      * Writes an object to the target stream.
1453      *
1454      * @param object
1455      *            the object to write to the target stream.
1456      * @throws IOException
1457      *             if an error occurs while writing to the target stream.
1458      * @see ObjectInputStream#readObject()
1459      */
writeObject(Object object)1460     public final void writeObject(Object object) throws IOException {
1461         writeObject(object, false);
1462     }
1463 
1464     /**
1465      * Writes an unshared object to the target stream. This method is identical
1466      * to {@code writeObject}, except that it always writes a new object to the
1467      * stream versus the use of back-referencing for identical objects by
1468      * {@code writeObject}.
1469      *
1470      * @param object
1471      *            the object to write to the target stream.
1472      * @throws IOException
1473      *             if an error occurs while writing to the target stream.
1474      * @see ObjectInputStream#readUnshared()
1475      */
writeUnshared(Object object)1476     public void writeUnshared(Object object) throws IOException {
1477         writeObject(object, true);
1478     }
1479 
writeObject(Object object, boolean unshared)1480     private void writeObject(Object object, boolean unshared) throws IOException {
1481         boolean setOutput = (primitiveTypes == output);
1482         if (setOutput) {
1483             primitiveTypes = null;
1484         }
1485         // This is the specified behavior in JDK 1.2. Very bizarre way to allow
1486         // behavior overriding.
1487         if (subclassOverridingImplementation && !unshared) {
1488             writeObjectOverride(object);
1489             return;
1490         }
1491 
1492         try {
1493             // First we need to flush primitive types if they were written
1494             drain();
1495             // Actual work, and class-based replacement should be computed
1496             // if needed.
1497             writeObjectInternal(object, unshared, true, true);
1498             if (setOutput) {
1499                 primitiveTypes = output;
1500             }
1501         } catch (IOException ioEx1) {
1502             // This will make it pass through until the top caller. Only the top caller writes the
1503             // exception (where it can).
1504             if (nestedLevels == 0) {
1505                 try {
1506                     writeNewException(ioEx1);
1507                 } catch (IOException ioEx2) {
1508                     // If writing the exception to the output stream causes another exception there
1509                     // is no need to propagate the second exception or generate a third exception,
1510                     // both of which might obscure details of the root cause.
1511                 }
1512             }
1513             throw ioEx1; // and then we propagate the original exception
1514         }
1515     }
1516 
1517     /**
1518      * Write object {@code object} into the receiver's underlying stream.
1519      *
1520      * @param object
1521      *            The object to write
1522      * @param unshared
1523      *            Write the object unshared
1524      * @param computeClassBasedReplacement
1525      *            A boolean indicating if class-based replacement should be
1526      *            computed (if supported) for the object.
1527      * @param computeStreamReplacement
1528      *            A boolean indicating if stream-based replacement should be
1529      *            computed (if supported) for the object.
1530      * @return the handle assigned to the final object being dumped
1531      *
1532      * @throws IOException
1533      *             If an IO exception happened when writing the object
1534      *
1535      * @see ObjectInputStream#readObject()
1536      */
writeObjectInternal(Object object, boolean unshared, boolean computeClassBasedReplacement, boolean computeStreamReplacement)1537     private int writeObjectInternal(Object object, boolean unshared,
1538             boolean computeClassBasedReplacement,
1539             boolean computeStreamReplacement) throws IOException {
1540 
1541         if (object == null) {
1542             writeNull();
1543             return -1;
1544         }
1545         if (!unshared) {
1546             int handle = dumpCycle(object);
1547             if (handle != -1) {
1548                 return handle; // cyclic reference
1549             }
1550         }
1551 
1552         // Non-null object, first time seen...
1553         Class<?> objClass = object.getClass();
1554         ObjectStreamClass clDesc = ObjectStreamClass.lookupStreamClass(objClass);
1555 
1556         nestedLevels++;
1557         try {
1558 
1559             if (!(enableReplace && computeStreamReplacement)) {
1560                 // Is it a Class ?
1561                 if (objClass == ObjectStreamClass.CLASSCLASS) {
1562                     return writeNewClass((Class<?>) object, unshared);
1563                 }
1564                 // Is it an ObjectStreamClass ?
1565                 if (objClass == ObjectStreamClass.OBJECTSTREAMCLASSCLASS) {
1566                     return writeClassDesc((ObjectStreamClass) object, unshared);
1567                 }
1568             }
1569 
1570             if (clDesc.isSerializable() && computeClassBasedReplacement) {
1571                 if (clDesc.hasMethodWriteReplace()){
1572                     Method methodWriteReplace = clDesc.getMethodWriteReplace();
1573                     Object replObj;
1574                     try {
1575                         replObj = methodWriteReplace.invoke(object, (Object[]) null);
1576                     } catch (IllegalAccessException iae) {
1577                         replObj = object;
1578                     } catch (InvocationTargetException ite) {
1579                         // WARNING - Not sure this is the right thing to do
1580                         // if we can't run the method
1581                         Throwable target = ite.getTargetException();
1582                         if (target instanceof ObjectStreamException) {
1583                             throw (ObjectStreamException) target;
1584                         } else if (target instanceof Error) {
1585                             throw (Error) target;
1586                         } else {
1587                             throw (RuntimeException) target;
1588                         }
1589                     }
1590                     if (replObj != object) {
1591                         // All over, class-based replacement off this time.
1592                         int replacementHandle = writeObjectInternal(replObj, false, false,
1593                                 computeStreamReplacement);
1594                         // Make the original object also map to the same
1595                         // handle.
1596                         if (replacementHandle != -1) {
1597                             objectsWritten.put(object, replacementHandle);
1598                         }
1599                         return replacementHandle;
1600                     }
1601                 }
1602 
1603             }
1604 
1605             // We get here either if class-based replacement was not needed or
1606             // if it was needed but produced the same object or if it could not
1607             // be computed.
1608             if (enableReplace && computeStreamReplacement) {
1609                 // Now we compute the stream-defined replacement.
1610                 Object streamReplacement = replaceObject(object);
1611                 if (streamReplacement != object) {
1612                     // All over, class-based replacement off this time.
1613                     int replacementHandle = writeObjectInternal(streamReplacement, false,
1614                             computeClassBasedReplacement, false);
1615                     // Make the original object also map to the same handle.
1616                     if (replacementHandle != -1) {
1617                         objectsWritten.put(object, replacementHandle);
1618                     }
1619                     return replacementHandle;
1620                 }
1621             }
1622 
1623             // We get here if stream-based replacement produced the same object
1624 
1625             // Is it a Class ?
1626             if (objClass == ObjectStreamClass.CLASSCLASS) {
1627                 return writeNewClass((Class<?>) object, unshared);
1628             }
1629 
1630             // Is it an ObjectStreamClass ?
1631             if (objClass == ObjectStreamClass.OBJECTSTREAMCLASSCLASS) {
1632                 return writeClassDesc((ObjectStreamClass) object, unshared);
1633             }
1634 
1635             // Is it a String ? (instanceof, but == is faster)
1636             if (objClass == ObjectStreamClass.STRINGCLASS) {
1637                 return writeNewString((String) object, unshared);
1638             }
1639 
1640             // Is it an Array ?
1641             if (objClass.isArray()) {
1642                 return writeNewArray(object, objClass, clDesc, objClass
1643                         .getComponentType(), unshared);
1644             }
1645 
1646             if (object instanceof Enum) {
1647                 return writeNewEnum(object, objClass, unshared);
1648             }
1649 
1650             // Not a String or Class or Array. Default procedure.
1651             return writeNewObject(object, objClass, clDesc, unshared);
1652         } finally {
1653             nestedLevels--;
1654         }
1655     }
1656 
1657     // write for Enum Class Desc only, which is different from other classes
writeEnumDesc(ObjectStreamClass classDesc, boolean unshared)1658     private ObjectStreamClass writeEnumDesc(ObjectStreamClass classDesc, boolean unshared)
1659             throws IOException {
1660         // write classDesc, classDesc for enum is different
1661 
1662         // set flag for enum, the flag is (SC_SERIALIZABLE | SC_ENUM)
1663         classDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM));
1664         int previousHandle = -1;
1665         if (unshared) {
1666             previousHandle = objectsWritten.get(classDesc);
1667         }
1668         int handle = -1;
1669         if (!unshared) {
1670             handle = dumpCycle(classDesc);
1671         }
1672         if (handle == -1) {
1673             Class<?> classToWrite = classDesc.forClass();
1674             // If we got here, it is a new (non-null) classDesc that will have
1675             // to be registered as well
1676             registerObjectWritten(classDesc);
1677 
1678             output.writeByte(TC_CLASSDESC);
1679             if (protocolVersion == PROTOCOL_VERSION_1) {
1680                 writeNewClassDesc(classDesc);
1681             } else {
1682                 // So write...() methods can be used by
1683                 // subclasses during writeClassDescriptor()
1684                 primitiveTypes = output;
1685                 writeClassDescriptor(classDesc);
1686                 primitiveTypes = null;
1687             }
1688             // Extra class info (optional)
1689             annotateClass(classToWrite);
1690             drain(); // flush primitive types in the annotation
1691             output.writeByte(TC_ENDBLOCKDATA);
1692             // write super class
1693             ObjectStreamClass superClassDesc = classDesc.getSuperclass();
1694             if (superClassDesc != null) {
1695                 // super class is also enum
1696                 superClassDesc.setFlags((byte) (SC_SERIALIZABLE | SC_ENUM));
1697                 writeEnumDesc(superClassDesc, unshared);
1698             } else {
1699                 output.writeByte(TC_NULL);
1700             }
1701             if (unshared) {
1702                 // remove reference to unshared object
1703                 removeUnsharedReference(classDesc, previousHandle);
1704             }
1705         }
1706         return classDesc;
1707     }
1708 
writeNewEnum(Object object, Class<?> theClass, boolean unshared)1709     private int writeNewEnum(Object object, Class<?> theClass, boolean unshared) throws IOException {
1710         // write new Enum
1711         EmulatedFieldsForDumping originalCurrentPutField = currentPutField; // save
1712         // null it, to make sure one will be computed if needed
1713         currentPutField = null;
1714 
1715         output.writeByte(TC_ENUM);
1716         while (theClass != null && !theClass.isEnum()) {
1717             // write enum only
1718             theClass = theClass.getSuperclass();
1719         }
1720         ObjectStreamClass classDesc = ObjectStreamClass.lookup(theClass);
1721         writeEnumDesc(classDesc, unshared);
1722 
1723         int previousHandle = -1;
1724         if (unshared) {
1725             previousHandle = objectsWritten.get(object);
1726         }
1727         int handle = registerObjectWritten(object);
1728 
1729         ObjectStreamField[] fields = classDesc.getSuperclass().fields();
1730         // Only write field "name" for enum class, which is the second field of
1731         // enum, that is fields[1]. Ignore all non-fields and fields.length < 2
1732         if (fields != null && fields.length > 1) {
1733             Field field = classDesc.getSuperclass().checkAndGetReflectionField(fields[1]);
1734             if (field == null) {
1735                 throw new NoSuchFieldError();
1736             }
1737             try {
1738                 String str = (String) field.get(object);
1739                 int strHandle = -1;
1740                 if (!unshared) {
1741                     strHandle = dumpCycle(str);
1742                 }
1743                 if (strHandle == -1) {
1744                     writeNewString(str, unshared);
1745                 }
1746             } catch (IllegalAccessException iae) {
1747                 throw new AssertionError(iae);
1748             }
1749         }
1750 
1751         if (unshared) {
1752             // remove reference to unshared object
1753             removeUnsharedReference(object, previousHandle);
1754         }
1755         currentPutField = originalCurrentPutField;
1756         return handle;
1757     }
1758 
1759     /**
1760      * Method to be overridden by subclasses to write {@code object} to the
1761      * target stream.
1762      *
1763      * @param object
1764      *            the object to write to the target stream.
1765      * @throws IOException
1766      *             if an error occurs while writing to the target stream.
1767      */
writeObjectOverride(Object object)1768     protected void writeObjectOverride(Object object) throws IOException {
1769         if (!subclassOverridingImplementation) {
1770             // Subclasses must override.
1771             throw new IOException();
1772         }
1773     }
1774 
1775     /**
1776      * Writes a short (16 bit) to the target stream.
1777      *
1778      * @param value
1779      *            the short to write to the target stream.
1780      * @throws IOException
1781      *             if an error occurs while writing to the target stream.
1782      */
writeShort(int value)1783     public void writeShort(int value) throws IOException {
1784         checkWritePrimitiveTypes();
1785         primitiveTypes.writeShort(value);
1786     }
1787 
1788     /**
1789      * Writes the {@link ObjectOutputStream} header to the target stream.
1790      *
1791      * @throws IOException
1792      *             if an error occurs while writing to the target stream.
1793      */
writeStreamHeader()1794     protected void writeStreamHeader() throws IOException {
1795         output.writeShort(STREAM_MAGIC);
1796         output.writeShort(STREAM_VERSION);
1797     }
1798 
1799     /**
1800      * Writes a string encoded with {@link DataInput modified UTF-8} to the
1801      * target stream.
1802      *
1803      * @param value
1804      *            the string to write to the target stream.
1805      * @throws IOException
1806      *             if an error occurs while writing to the target stream.
1807      */
writeUTF(String value)1808     public void writeUTF(String value) throws IOException {
1809         checkWritePrimitiveTypes();
1810         primitiveTypes.writeUTF(value);
1811     }
1812 }
1813