< prev index next > src/java.base/share/classes/java/io/ObjectStreamClass.java
Print this page
package java.io;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
+ import java.lang.reflect.AccessFlag;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
+ import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.RecordComponent;
import java.lang.reflect.UndeclaredThrowableException;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
+ import jdk.internal.MigratedValueClass;
import jdk.internal.event.SerializationMisdeclarationEvent;
import jdk.internal.misc.Unsafe;
import jdk.internal.reflect.CallerSensitive;
import jdk.internal.reflect.Reflection;
import jdk.internal.reflect.ReflectionFactory;
* serialVersionUID of the class. The ObjectStreamClass for a specific class
* loaded in this Java VM can be found/created using the lookup method.
*
* <p>The algorithm to compute the SerialVersionUID is described in
* <a href="{@docRoot}/../specs/serialization/class.html#stream-unique-identifiers">
! * <cite>Java Object Serialization Specification,</cite> Section 4.6, "Stream Unique Identifiers"</a>.
*
* @spec serialization/index.html Java Object Serialization Specification
* @author Mike Warres
* @author Roger Riggs
* @see ObjectStreamField
* serialVersionUID of the class. The ObjectStreamClass for a specific class
* loaded in this Java VM can be found/created using the lookup method.
*
* <p>The algorithm to compute the SerialVersionUID is described in
* <a href="{@docRoot}/../specs/serialization/class.html#stream-unique-identifiers">
! * <cite>Java Object Serialization Specification</cite>, Section 4.6, "Stream Unique Identifiers"</a>.
*
* @spec serialization/index.html Java Object Serialization Specification
* @author Mike Warres
* @author Roger Riggs
* @see ObjectStreamField
private boolean isProxy;
/** true if represents enum type */
private boolean isEnum;
/** true if represents record type */
private boolean isRecord;
+ /** true if represents a value class */
+ private boolean isValue;
/** true if represented class implements Serializable */
private boolean serializable;
/** true if represented class implements Externalizable */
private boolean externalizable;
/** true if desc has data written by class-defined writeObject method */
this.cl = cl;
name = cl.getName();
isProxy = Proxy.isProxyClass(cl);
isEnum = Enum.class.isAssignableFrom(cl);
isRecord = cl.isRecord();
+ isValue = cl.isValue();
serializable = Serializable.class.isAssignableFrom(cl);
externalizable = Externalizable.class.isAssignableFrom(cl);
Class<?> superCl = cl.getSuperclass();
superDesc = (superCl != null) ? lookup(superCl, false) : null;
}
if (isRecord) {
canonicalCtr = canonicalRecordCtr(cl);
deserializationCtrs = new DeserializationConstructorsCache();
+ } else if (isValue) {
+ // Value object instance creation is specialized in newInstance()
+ cons = null;
} else if (externalizable) {
cons = getExternalizableConstructor(cl);
} else {
cons = getSerializableConstructor(cl);
writeObjectMethod = getPrivateMethod(cl, "writeObject",
}
if (deserializeEx == null) {
if (isEnum) {
deserializeEx = new ExceptionInfo(name, "enum type");
! } else if (cons == null && !isRecord) {
deserializeEx = new ExceptionInfo(name, "no valid constructor");
}
}
if (isRecord && canonicalCtr == null) {
deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
}
if (deserializeEx == null) {
if (isEnum) {
deserializeEx = new ExceptionInfo(name, "enum type");
! } else if (cons == null && !(isRecord | isValue)) {
deserializeEx = new ExceptionInfo(name, "no valid constructor");
}
}
if (isRecord && canonicalCtr == null) {
deserializeEx = new ExceptionInfo(name, "record canonical constructor not found");
numObjFields = model.numObjFields;
if (osc != null) {
localDesc = osc;
isRecord = localDesc.isRecord;
+ isValue = localDesc.isValue;
// canonical record constructor is shared
canonicalCtr = localDesc.canonicalCtr;
// cache of deserialization constructors is shared
deserializationCtrs = localDesc.deserializationCtrs;
writeObjectMethod = localDesc.writeObjectMethod;
boolean isSerializable() {
requireInitialized();
return serializable;
}
+ /**
+ * {@return {code true} if the class is a value class, {@code false} otherwise}
+ */
+ boolean isValue() {
+ requireInitialized();
+ return isValue;
+ }
+
/**
* Returns true if class descriptor represents externalizable class that
* has written its data in 1.2 (block data) format, false otherwise.
*/
boolean hasBlockExternalData() {
}
/**
* Returns true if represented class is serializable/externalizable and can
* be instantiated by the serialization runtime--i.e., if it is
! * externalizable and defines a public no-arg constructor, or if it is
* non-externalizable and its first non-serializable superclass defines an
! * accessible no-arg constructor. Otherwise, returns false.
*/
boolean isInstantiable() {
requireInitialized();
! return (cons != null);
}
/**
* Returns true if represented class is serializable (but not
* externalizable) and defines a conformant writeObject method. Otherwise,
}
/**
* Returns true if represented class is serializable/externalizable and can
* be instantiated by the serialization runtime--i.e., if it is
! * externalizable and defines a public no-arg constructor, if it is
* non-externalizable and its first non-serializable superclass defines an
! * accessible no-arg constructor, or if the class is a migrated value class.
+ * Otherwise, returns false.
*/
boolean isInstantiable() {
requireInitialized();
! return (cons != null |
+ (isValue && cl != null && cl.isAnnotationPresent(jdk.internal.MigratedValueClass.class)));
}
/**
* Returns true if represented class is serializable (but not
* externalizable) and defines a conformant writeObject method. Otherwise,
} catch (InstantiationError err) {
var ex = new InstantiationException();
ex.initCause(err);
throw ex;
}
! } else {
throw new UnsupportedOperationException();
}
}
/**
* Invokes the writeObject method of the represented serializable class.
* Throws UnsupportedOperationException if this class descriptor is not
* associated with a class, or if the class is externalizable,
* non-serializable or does not define writeObject.
} catch (InstantiationError err) {
var ex = new InstantiationException();
ex.initCause(err);
throw ex;
}
! } else if (isValue) {
+ // Start with a buffered default value.
+ return FieldReflector.newValueInstance(cl);
+ } else {
throw new UnsupportedOperationException();
}
}
+ /**
+ * Finish the initialization of a value object.
+ * @param obj an object (larval if a value object)
+ * @return the finished object
+ */
+ Object finishValue(Object obj) {
+ return (isValue) ? FieldReflector.finishValueInstance(obj) : obj;
+ }
+
/**
* Invokes the writeObject method of the represented serializable class.
* Throws UnsupportedOperationException if this class descriptor is not
* associated with a class, or if the class is externalizable,
* non-serializable or does not define writeObject.
try {
Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
cons.setAccessible(true);
return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
cons : null;
! } catch (NoSuchMethodException ex) {
return null;
}
}
/**
try {
Constructor<?> cons = cl.getDeclaredConstructor((Class<?>[]) null);
cons.setAccessible(true);
return ((cons.getModifiers() & Modifier.PUBLIC) != 0) ?
cons : null;
! } catch (NoSuchMethodException | InaccessibleObjectException ex) {
return null;
}
}
/**
/** field type codes */
private final char[] typeCodes;
/** field types */
private final Class<?>[] types;
+ /**
+ * Return a new instance of the class using Unsafe.uninitializedDefaultValue
+ * and buffer it.
+ * @param clazz The value class
+ * @return a buffered default value
+ */
+ static Object newValueInstance(Class<?> clazz) throws InstantiationException{
+ var accessFlags = clazz.accessFlags();
+ if (accessFlags.contains(AccessFlag.ABSTRACT) ||
+ accessFlags.contains(AccessFlag.IDENTITY)) {
+ throw new InstantiationException("Value class not instantiable: " + clazz.getName());
+ }
+ // may not be implicitly constructible; so allocate with Unsafe
+ Object obj = UNSAFE.uninitializedDefaultValue(clazz);
+ return UNSAFE.makePrivateBuffer(obj);
+ }
+
+ /**
+ * Finish a value object, clear the larval state and returning the value object.
+ * @param obj a buffered value object in a larval state
+ * @return the finished value object
+ */
+ static Object finishValueInstance(Object obj) {
+ assert (obj.getClass().isValue()) : "Should be a value class";
+ return UNSAFE.finishPrivateBuffer(obj);
+ }
+
/**
* Constructs FieldReflector capable of setting/getting values from the
* subset of fields whose ObjectStreamFields contain non-null
* reflective Field objects. ObjectStreamFields with null Fields are
* treated as filler, for which get operations return default values
/* assuming checkDefaultSerialize() has been called on the class
* descriptor this FieldReflector was obtained from, no field keys
* in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
*/
for (int i = numPrimFields; i < fields.length; i++) {
vals[offsets[i]] = switch (typeCodes[i]) {
! case 'L', '[' -> UNSAFE.getReference(obj, readKeys[i]);
default -> throw new InternalError();
};
}
}
/* assuming checkDefaultSerialize() has been called on the class
* descriptor this FieldReflector was obtained from, no field keys
* in array should be equal to Unsafe.INVALID_FIELD_OFFSET.
*/
for (int i = numPrimFields; i < fields.length; i++) {
+ Field f = fields[i].getField();
vals[offsets[i]] = switch (typeCodes[i]) {
! case 'L', '[' ->
+ UNSAFE.isFlatField(f)
+ ? UNSAFE.getValue(obj, readKeys[i], f.getType())
+ : UNSAFE.getReference(obj, readKeys[i]);
default -> throw new InternalError();
};
}
}
if (key == Unsafe.INVALID_FIELD_OFFSET) {
continue; // discard value
}
switch (typeCodes[i]) {
case 'L', '[' -> {
Object val = vals[offsets[i]];
if (val != null &&
!types[i - numPrimFields].isInstance(val))
{
- Field f = fields[i].getField();
throw new ClassCastException(
"cannot assign instance of " +
val.getClass().getName() + " to field " +
f.getDeclaringClass().getName() + "." +
f.getName() + " of type " +
f.getType().getName() + " in instance of " +
obj.getClass().getName());
}
! if (!dryRun)
! UNSAFE.putReference(obj, key, val);
}
default -> throw new InternalError();
}
}
}
if (key == Unsafe.INVALID_FIELD_OFFSET) {
continue; // discard value
}
switch (typeCodes[i]) {
case 'L', '[' -> {
+ Field f = fields[i].getField();
Object val = vals[offsets[i]];
if (val != null &&
!types[i - numPrimFields].isInstance(val))
{
throw new ClassCastException(
"cannot assign instance of " +
val.getClass().getName() + " to field " +
f.getDeclaringClass().getName() + "." +
f.getName() + " of type " +
f.getType().getName() + " in instance of " +
obj.getClass().getName());
}
! if (!dryRun) {
! if (UNSAFE.isFlatField(f)) {
+ UNSAFE.putValue(obj, key, f.getType(), val);
+ } else {
+ UNSAFE.putReference(obj, key, val);
+ }
+ }
}
default -> throw new InternalError();
}
}
}
< prev index next >