src/java.base/share/classes/java/io/ObjectInputStream.java
Print this page
@@ -250,13 +250,10 @@
/** scratch field for passing handle values up/down call stack */
private int passHandle = NULL_HANDLE;
/** flag set when at end of field value block with no TC_ENDBLOCKDATA */
private boolean defaultDataEnd = false;
- /** buffer for reading primitive field values */
- private byte[] primVals;
-
/** if true, invoke readObjectOverride() instead of readObject() */
private final boolean enableOverride;
/** if true, invoke resolveObject() */
private boolean enableResolve;
@@ -495,11 +492,15 @@
throw new NotActiveException("not in call to readObject");
}
Object curObj = ctx.getObj();
ObjectStreamClass curDesc = ctx.getDesc();
bin.setBlockDataMode(false);
- defaultReadFields(curObj, curDesc);
+ FieldValues vals = defaultReadFields(curObj, curDesc);
+ if (curObj != null) {
+ defaultCheckFieldValues(curObj, curDesc, vals);
+ defaultSetFieldValues(curObj, curDesc, vals);
+ }
bin.setBlockDataMode(true);
if (!curDesc.hasWriteObjectData()) {
/*
* Fix for 4360508: since stream does not contain terminating
* TC_ENDBLOCKDATA tag, set flag so that reading code elsewhere
@@ -1876,10 +1877,30 @@
*/
private void readSerialData(Object obj, ObjectStreamClass desc)
throws IOException
{
ObjectStreamClass.ClassDataSlot[] slots = desc.getClassDataLayout();
+ // Best effort Failure Atomicity; slotValues will be non-null if field
+ // values can be set after reading all field data in the hierarchy.
+ // Field values can only be set after reading all data if there are no
+ // user observable methods in the hierarchy, readObject(NoData). The
+ // top most Serializable class in the hierarchy can be skipped.
+ FieldValues[] slotValues = null;
+
+ boolean hasSpecialReadMethod = false;
+ for (int i = 1; i < slots.length; i++) {
+ ObjectStreamClass slotDesc = slots[i].desc;
+ if (slotDesc.hasReadObjectMethod()
+ || slotDesc.hasReadObjectNoDataMethod()) {
+ hasSpecialReadMethod = true;
+ break;
+ }
+ }
+ // No special read methods, can store values and defer setting.
+ if (!hasSpecialReadMethod)
+ slotValues = new FieldValues[slots.length];
+
for (int i = 0; i < slots.length; i++) {
ObjectStreamClass slotDesc = slots[i].desc;
if (slots[i].hasData) {
if (obj != null &&
@@ -1912,11 +1933,17 @@
* readObject() method when calling defaultReadObject() or
* readFields(); clear it to restore normal read behavior.
*/
defaultDataEnd = false;
} else {
- defaultReadFields(obj, slotDesc);
+ FieldValues vals = defaultReadFields(obj, slotDesc);
+ if (slotValues != null) {
+ slotValues[i] = vals;
+ } else if (obj != null) {
+ defaultCheckFieldValues(obj, slotDesc, vals);
+ defaultSetFieldValues(obj, slotDesc, vals);
+ }
}
if (slotDesc.hasWriteObjectData()) {
skipCustomData();
} else {
bin.setBlockDataMode(false);
@@ -1928,10 +1955,23 @@
{
slotDesc.invokeReadObjectNoData(obj);
}
}
}
+
+ if (obj != null && slotValues != null) {
+ // Check that the non-primitive types are assignable for all slots
+ // before assigning.
+ for (int i = 0; i < slots.length; i++) {
+ if (slotValues[i] != null)
+ defaultCheckFieldValues(obj, slots[i].desc, slotValues[i]);
+ }
+ for (int i = 0; i < slots.length; i++) {
+ if (slotValues[i] != null)
+ defaultSetFieldValues(obj, slots[i].desc, slotValues[i]);
+ }
+ }
}
/**
* Skips over all block data and objects until TC_ENDBLOCKDATA is
* encountered.
@@ -1959,52 +1999,78 @@
break;
}
}
}
+ private class FieldValues {
+ final byte[] primValues;
+ final Object[] objValues;
+
+ FieldValues(byte[] primValues, Object[] objValues) {
+ this.primValues = primValues;
+ this.objValues = objValues;
+ }
+ }
+
/**
* Reads in values of serializable fields declared by given class
- * descriptor. If obj is non-null, sets field values in obj. Expects that
- * passHandle is set to obj's handle before this method is called.
+ * descriptor. Expects that passHandle is set to obj's handle before this
+ * method is called.
*/
- private void defaultReadFields(Object obj, ObjectStreamClass desc)
+ private FieldValues defaultReadFields(Object obj, ObjectStreamClass desc)
throws IOException
{
Class<?> cl = desc.forClass();
if (cl != null && obj != null && !cl.isInstance(obj)) {
throw new ClassCastException();
}
+ byte[] primVals = null;
int primDataSize = desc.getPrimDataSize();
if (primDataSize > 0) {
- if (primVals == null || primVals.length < primDataSize) {
primVals = new byte[primDataSize];
- }
bin.readFully(primVals, 0, primDataSize, false);
- if (obj != null) {
- desc.setPrimFieldValues(obj, primVals);
- }
}
+ Object[] objVals = null;
int numObjFields = desc.getNumObjFields();
if (numObjFields > 0) {
int objHandle = passHandle;
ObjectStreamField[] fields = desc.getFields(false);
- Object[] objVals = new Object[numObjFields];
+ objVals = new Object[numObjFields];
int numPrimFields = fields.length - objVals.length;
for (int i = 0; i < objVals.length; i++) {
ObjectStreamField f = fields[numPrimFields + i];
objVals[i] = readObject0(f.isUnshared());
if (f.getField() != null) {
handles.markDependency(objHandle, passHandle);
}
}
- if (obj != null) {
- desc.setObjFieldValues(obj, objVals);
- }
passHandle = objHandle;
}
+
+ return new FieldValues(primVals, objVals);
+ }
+
+ /** Throws ClassCastException if any value is not assignable. */
+ private void defaultCheckFieldValues(Object obj, ObjectStreamClass desc,
+ FieldValues values) {
+ Object[] objectValues = values.objValues;
+ if (objectValues != null)
+ desc.checkObjFieldValueTypes(obj, objectValues);
+ }
+
+ /** Sets field values in obj. */
+ private void defaultSetFieldValues(Object obj, ObjectStreamClass desc,
+ FieldValues values) {
+ byte[] primValues = values.primValues;
+ Object[] objectValues = values.objValues;
+
+ if (primValues != null)
+ desc.setPrimFieldValues(obj, primValues);
+ if (objectValues != null)
+ desc.setObjFieldValues(obj, objectValues);
}
/**
* Reads in and returns IOException that caused serialization to abort.
* All stream state is discarded prior to reading in fatal exception. Sets