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