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