src/java.base/share/classes/java/io/ObjectInputStream.java
Print this page
@@ -38,10 +38,11 @@
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,10 +267,20 @@
* 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,10 +384,13 @@
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();
+ if (requiresFreeze) {
+ freeze();
+ }
}
return obj;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
@@ -463,10 +477,13 @@
if (ex != null) {
throw ex;
}
if (depth == 0) {
vlist.doCallbacks();
+ if (requiresFreeze) {
+ freeze();
+ }
}
return obj;
} finally {
passHandle = outerHandle;
if (closed && depth == 0) {
@@ -1876,12 +1893,16 @@
*/
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,10 +1949,11 @@
{
slotDesc.invokeReadObjectNoData(obj);
}
}
}
+ requiresFreeze = freeze;
}
/**
* Skips over all block data and objects until TC_ENDBLOCKDATA is
* encountered.
@@ -2355,10 +2377,19 @@
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