src/java.base/share/classes/java/io/ObjectInputStream.java

Print this page

        

*** 38,47 **** --- 38,48 ---- import java.util.Arrays; import java.util.HashMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import static java.io.ObjectStreamClass.processQueue; + import sun.misc.Unsafe; import sun.reflect.misc.ReflectUtil; /** * An ObjectInputStream deserializes primitive data and objects previously * written using an ObjectOutputStream.
*** 266,275 **** --- 267,286 ---- * Null when not during readObject upcall. */ private SerialCallbackContext curContext; /** + * Tracks whether deserialization of an object in the graph may have set + * a final field, and therefore requires a freeze action before returning + * the graph of objects from the topmost readObject call. + * Nested calls to readObject do not issue freeze actions because the + * sub-graph returned from a nested call is not guaranteed to be fully + * initialized yet (possible cycles). + */ + private boolean requiresFreeze; + + /** * Creates an ObjectInputStream that reads from the specified InputStream. * A serialization stream header is read from the stream and verified. * This constructor will block until the corresponding ObjectOutputStream * has written and flushed the header. *
*** 373,382 **** --- 384,396 ---- if (ex != null) { throw ex; } if (depth == 0) { vlist.doCallbacks(); + if (requiresFreeze) { + freeze(); + } } return obj; } finally { passHandle = outerHandle; if (closed && depth == 0) {
*** 463,472 **** --- 477,489 ---- if (ex != null) { throw ex; } if (depth == 0) { vlist.doCallbacks(); + if (requiresFreeze) { + freeze(); + } } return obj; } finally { passHandle = outerHandle; if (closed && depth == 0) {
*** 1876,1887 **** --- 1893,1908 ---- */ private void readSerialData(Object obj, ObjectStreamClass desc) throws IOException { ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout(); + boolean freeze = false; for (int i = 0; i < slots.length; i++) { ObjectStreamClass slotDesc = slots[i].desc; + if (!freeze && slotDesc.hasFinalField()) { + freeze = true; + } if (slots[i].hasData) { if (obj != null && slotDesc.hasReadObjectMethod() && handles.lookupException(passHandle) == null)
*** 1928,1937 **** --- 1949,1959 ---- { slotDesc.invokeReadObjectNoData(obj); } } } + requiresFreeze = freeze; } /** * Skips over all block data and objects until TC_ENDBLOCKDATA is * encountered.
*** 2355,2364 **** --- 2377,2395 ---- public void close() throws IOException { in.close(); } } + private static final Unsafe UNSAFE = Unsafe.getUnsafe(); + + private void freeze() { + // Issue a storestore|storeload fence, which is at least sufficient + // to provide final-freeze semantics. + UNSAFE.storeFence(); + requiresFreeze = false; + } + /** * Input stream with two modes: in default mode, inputs data written in the * same format as DataOutputStream; in "block data" mode, inputs data * bracketed by block data markers (see object serialization specification * for details). Buffering depends on block data mode: when in default