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