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