--- old/src/java.base/share/classes/java/io/ObjectInputStream.java 2015-02-24 10:47:57.000000000 +0000 +++ new/src/java.base/share/classes/java/io/ObjectInputStream.java 2015-02-24 10:47:57.000000000 +0000 @@ -40,6 +40,7 @@ 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; /** @@ -268,6 +269,16 @@ 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 @@ -375,6 +386,9 @@ } if (depth == 0) { vlist.doCallbacks(); + if (requiresFreeze) { + freeze(); + } } return obj; } finally { @@ -465,6 +479,9 @@ } if (depth == 0) { vlist.doCallbacks(); + if (requiresFreeze) { + freeze(); + } } return obj; } finally { @@ -1878,8 +1895,12 @@ 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 && @@ -1930,6 +1951,7 @@ } } } + requiresFreeze = freeze; } /** @@ -2357,6 +2379,15 @@ } } + 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 --- old/src/java.base/share/classes/java/io/ObjectStreamClass.java 2015-02-24 10:47:59.000000000 +0000 +++ new/src/java.base/share/classes/java/io/ObjectStreamClass.java 2015-02-24 10:47:59.000000000 +0000 @@ -1262,6 +1262,14 @@ } /** + * Returns true if the field reflector has one, or more, underlying final + * fields. + */ + boolean hasFinalField() { + return fieldRefl.hasFinal(); + } + + /** * Calculates and sets serializable field offsets, as well as primitive * data size and object field count totals. Throws InvalidClassException * if fields are illegally ordered. @@ -1886,6 +1894,8 @@ private final char[] typeCodes; /** field types */ private final Class[] types; + /** true if at least one underlying field is final */ + private final boolean hasFinal; /** * Constructs FieldReflector capable of setting/getting values from the @@ -1904,12 +1914,19 @@ ArrayList> typeList = new ArrayList<>(); Set usedKeys = new HashSet<>(); - + boolean hf = false; for (int i = 0; i < nfields; i++) { ObjectStreamField f = fields[i]; Field rf = f.getField(); - long key = (rf != null) ? - unsafe.objectFieldOffset(rf) : Unsafe.INVALID_FIELD_OFFSET; + long key; + if (rf != null) { + key = unsafe.objectFieldOffset(rf); + if (!hf && isFinal(rf)) { + hf = true; + } + } else { + key = Unsafe.INVALID_FIELD_OFFSET; + } readKeys[i] = key; writeKeys[i] = usedKeys.add(key) ? key : Unsafe.INVALID_FIELD_OFFSET; @@ -1920,10 +1937,19 @@ } } + hasFinal = hf; types = typeList.toArray(new Class[typeList.size()]); numPrimFields = nfields - types.length; } + private static boolean isFinal(Field field) { + return (field.getModifiers() & Modifier.FINAL) != 0; + } + + public boolean hasFinal() { + return hasFinal; + } + /** * Returns list of ObjectStreamFields representing fields operated on * by this reflector. The shared/unshared values and Field objects --- old/src/java.base/share/classes/java/math/BigDecimal.java 2015-02-24 10:48:01.000000000 +0000 +++ new/src/java.base/share/classes/java/math/BigDecimal.java 2015-02-24 10:48:01.000000000 +0000 @@ -3740,8 +3740,8 @@ throw new ExceptionInInitializerError(ex); } } - static void setIntCompactVolatile(BigDecimal bd, long val) { - unsafe.putLongVolatile(bd, intCompactOffset, val); + static void setIntCompact(BigDecimal bd, long val) { + unsafe.putLong(bd, intCompactOffset, val); } static void setIntValVolatile(BigDecimal bd, BigInteger val) { @@ -3765,7 +3765,7 @@ throw new java.io.StreamCorruptedException(message); // [all values of scale are now allowed] } - UnsafeHolder.setIntCompactVolatile(this, compactValFor(intVal)); + UnsafeHolder.setIntCompact(this, compactValFor(intVal)); } /** --- old/src/java.base/share/classes/java/math/BigInteger.java 2015-02-24 10:48:03.000000000 +0000 +++ new/src/java.base/share/classes/java/math/BigInteger.java 2015-02-24 10:48:03.000000000 +0000 @@ -4368,11 +4368,11 @@ } static void putSign(BigInteger bi, int sign) { - unsafe.putIntVolatile(bi, signumOffset, sign); + unsafe.putInt(bi, signumOffset, sign); } static void putMag(BigInteger bi, int[] magnitude) { - unsafe.putObjectVolatile(bi, magOffset, magnitude); + unsafe.putObject(bi, magOffset, magnitude); } }