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