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