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