mirror of
https://github.com/openjdk/jdk.git
synced 2025-08-27 06:45:07 +02:00
8315810: Reimplement sun.reflect.ReflectionFactory::newConstructorForSerialization with method handles
Co-authored-by: Chen Liang <liach@openjdk.org> Reviewed-by: rriggs
This commit is contained in:
parent
eb1f67b160
commit
5cea53d372
10 changed files with 141 additions and 41 deletions
|
@ -1034,12 +1034,12 @@ public final class ObjectStreamClass implements Serializable {
|
||||||
new AccessControlContext(domains));
|
new AccessControlContext(domains));
|
||||||
} catch (UndeclaredThrowableException x) {
|
} catch (UndeclaredThrowableException x) {
|
||||||
Throwable cause = x.getCause();
|
Throwable cause = x.getCause();
|
||||||
if (cause instanceof InstantiationException)
|
if (cause instanceof InstantiationException ie)
|
||||||
throw (InstantiationException) cause;
|
throw ie;
|
||||||
if (cause instanceof InvocationTargetException)
|
if (cause instanceof InvocationTargetException ite)
|
||||||
throw (InvocationTargetException) cause;
|
throw ite;
|
||||||
if (cause instanceof IllegalAccessException)
|
if (cause instanceof IllegalAccessException iae)
|
||||||
throw (IllegalAccessException) cause;
|
throw iae;
|
||||||
// not supposed to happen
|
// not supposed to happen
|
||||||
throw x;
|
throw x;
|
||||||
}
|
}
|
||||||
|
@ -1047,6 +1047,12 @@ public final class ObjectStreamClass implements Serializable {
|
||||||
} catch (IllegalAccessException ex) {
|
} catch (IllegalAccessException ex) {
|
||||||
// should not occur, as access checks have been suppressed
|
// should not occur, as access checks have been suppressed
|
||||||
throw new InternalError(ex);
|
throw new InternalError(ex);
|
||||||
|
} catch (InvocationTargetException ex) {
|
||||||
|
Throwable cause = ex.getCause();
|
||||||
|
if (cause instanceof Error err)
|
||||||
|
throw err;
|
||||||
|
else
|
||||||
|
throw ex;
|
||||||
} catch (InstantiationError err) {
|
} catch (InstantiationError err) {
|
||||||
var ex = new InstantiationException();
|
var ex = new InstantiationException();
|
||||||
ex.initCause(err);
|
ex.initCause(err);
|
||||||
|
|
|
@ -128,12 +128,11 @@ sealed class DirectMethodHandle extends MethodHandle {
|
||||||
}
|
}
|
||||||
static DirectMethodHandle make(MemberName member) {
|
static DirectMethodHandle make(MemberName member) {
|
||||||
if (member.isConstructor())
|
if (member.isConstructor())
|
||||||
return makeAllocator(member);
|
return makeAllocator(member.getDeclaringClass(), member);
|
||||||
return make(member.getDeclaringClass(), member);
|
return make(member.getDeclaringClass(), member);
|
||||||
}
|
}
|
||||||
private static DirectMethodHandle makeAllocator(MemberName ctor) {
|
static DirectMethodHandle makeAllocator(Class<?> instanceClass, MemberName ctor) {
|
||||||
assert(ctor.isConstructor() && ctor.getName().equals("<init>"));
|
assert(ctor.isConstructor() && ctor.getName().equals("<init>"));
|
||||||
Class<?> instanceClass = ctor.getDeclaringClass();
|
|
||||||
ctor = ctor.asConstructor();
|
ctor = ctor.asConstructor();
|
||||||
assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
|
assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
|
||||||
MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass);
|
MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass);
|
||||||
|
|
|
@ -1647,6 +1647,12 @@ abstract class MethodHandleImpl {
|
||||||
public Class<?>[] exceptionTypes(MethodHandle handle) {
|
public Class<?>[] exceptionTypes(MethodHandle handle) {
|
||||||
return VarHandles.exceptionTypes(handle);
|
return VarHandles.exceptionTypes(handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MethodHandle serializableConstructor(Class<?> decl, Constructor<?> ctorToCall) throws IllegalAccessException {
|
||||||
|
return IMPL_LOOKUP.serializableConstructor(decl, ctorToCall);
|
||||||
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3525,6 +3525,33 @@ return mh1;
|
||||||
return lookup.getDirectConstructorNoSecurityManager(ctor.getDeclaringClass(), ctor);
|
return lookup.getDirectConstructorNoSecurityManager(ctor.getDeclaringClass(), ctor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Produces a method handle that is capable of creating instances of the given class
|
||||||
|
* and instantiated by the given constructor. No security manager check.
|
||||||
|
*
|
||||||
|
* This method should only be used by ReflectionFactory::newConstructorForSerialization.
|
||||||
|
*/
|
||||||
|
/* package-private */ MethodHandle serializableConstructor(Class<?> decl, Constructor<?> c) throws IllegalAccessException {
|
||||||
|
MemberName ctor = new MemberName(c);
|
||||||
|
assert(ctor.isConstructor() && constructorInSuperclass(decl, c));
|
||||||
|
checkAccess(REF_newInvokeSpecial, decl, ctor);
|
||||||
|
assert(!MethodHandleNatives.isCallerSensitive(ctor)); // maybeBindCaller not relevant here
|
||||||
|
return DirectMethodHandle.makeAllocator(decl, ctor).setVarargs(ctor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean constructorInSuperclass(Class<?> decl, Constructor<?> ctor) {
|
||||||
|
if (decl == ctor.getDeclaringClass())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Class<?> cl = decl;
|
||||||
|
while ((cl = cl.getSuperclass()) != null) {
|
||||||
|
if (cl == ctor.getDeclaringClass()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Produces a method handle giving read access to a reflected field.
|
* Produces a method handle giving read access to a reflected field.
|
||||||
* The type of the method handle will have a return type of the field's
|
* The type of the method handle will have a return type of the field's
|
||||||
|
|
|
@ -162,4 +162,12 @@ public interface JavaLangInvokeAccess {
|
||||||
* @return an array of exceptions, or {@code null}.
|
* @return an array of exceptions, or {@code null}.
|
||||||
*/
|
*/
|
||||||
Class<?>[] exceptionTypes(MethodHandle handle);
|
Class<?>[] exceptionTypes(MethodHandle handle);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a method handle that allocates an instance of the given class
|
||||||
|
* and then invoke the given constructor of one of its superclasses.
|
||||||
|
*
|
||||||
|
* This method should only be used by ReflectionFactory::newConstructorForSerialization.
|
||||||
|
*/
|
||||||
|
MethodHandle serializableConstructor(Class<?> decl, Constructor<?> ctorToCall) throws IllegalAccessException;
|
||||||
}
|
}
|
||||||
|
|
|
@ -194,7 +194,8 @@ class DirectMethodHandleAccessor extends MethodAccessorImpl {
|
||||||
private void checkReceiver(Object o) {
|
private void checkReceiver(Object o) {
|
||||||
// NOTE: will throw NullPointerException, as specified, if o is null
|
// NOTE: will throw NullPointerException, as specified, if o is null
|
||||||
if (!declaringClass.isAssignableFrom(o.getClass())) {
|
if (!declaringClass.isAssignableFrom(o.getClass())) {
|
||||||
throw new IllegalArgumentException("object is not an instance of declaring class");
|
throw new IllegalArgumentException("object of type " + o.getClass().getName()
|
||||||
|
+ " is not an instance of " + declaringClass.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -101,21 +101,61 @@ final class MethodHandleAccessorFactory {
|
||||||
// Ensure class initialized outside the invocation of method handle
|
// Ensure class initialized outside the invocation of method handle
|
||||||
// so that EIIE is propagated (not wrapped with ITE)
|
// so that EIIE is propagated (not wrapped with ITE)
|
||||||
ensureClassInitialized(ctor.getDeclaringClass());
|
ensureClassInitialized(ctor.getDeclaringClass());
|
||||||
|
|
||||||
try {
|
try {
|
||||||
MethodHandle mh = JLIA.unreflectConstructor(ctor);
|
MethodHandle target = makeConstructorHandle(JLIA.unreflectConstructor(ctor));
|
||||||
int paramCount = mh.type().parameterCount();
|
return DirectConstructorHandleAccessor.constructorAccessor(ctor, target);
|
||||||
MethodHandle target = mh.asFixedArity();
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new InternalError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a ConstructorAccessor that is capable of creating instances
|
||||||
|
* of the given class and instantiated by the given constructor.
|
||||||
|
*
|
||||||
|
* @param decl the class to instantiate
|
||||||
|
* @param ctor the constructor to call
|
||||||
|
* @return an accessible constructor
|
||||||
|
*/
|
||||||
|
static ConstructorAccessorImpl newSerializableConstructorAccessor(Class<?> decl, Constructor<?> ctor) {
|
||||||
|
if (!constructorInSuperclass(decl, ctor)) {
|
||||||
|
throw new UnsupportedOperationException(ctor + " not a superclass of " + decl.getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ExceptionInInitializerError may be thrown during class initialization
|
||||||
|
// Ensure class initialized outside the invocation of method handle
|
||||||
|
// so that EIIE is propagated (not wrapped with ITE)
|
||||||
|
ensureClassInitialized(decl);
|
||||||
|
try {
|
||||||
|
MethodHandle target = makeConstructorHandle(JLIA.serializableConstructor(decl, ctor));
|
||||||
|
return DirectConstructorHandleAccessor.constructorAccessor(ctor, target);
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
throw new InternalError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean constructorInSuperclass(Class<?> decl, Constructor<?> ctor) {
|
||||||
|
if (decl == ctor.getDeclaringClass())
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Class<?> cl = decl;
|
||||||
|
while ((cl = cl.getSuperclass()) != null) {
|
||||||
|
if (cl == ctor.getDeclaringClass()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MethodHandle makeConstructorHandle(MethodHandle ctor) {
|
||||||
|
int paramCount = ctor.type().parameterCount();
|
||||||
|
MethodHandle target = ctor.asFixedArity();
|
||||||
MethodType mtype = specializedMethodTypeForConstructor(paramCount);
|
MethodType mtype = specializedMethodTypeForConstructor(paramCount);
|
||||||
if (paramCount > SPECIALIZED_PARAM_COUNT) {
|
if (paramCount > SPECIALIZED_PARAM_COUNT) {
|
||||||
// spread the parameters only for the non-specialized case
|
// spread the parameters only for the non-specialized case
|
||||||
target = target.asSpreader(Object[].class, paramCount);
|
target = target.asSpreader(Object[].class, paramCount);
|
||||||
}
|
}
|
||||||
target = target.asType(mtype);
|
return target.asType(mtype);
|
||||||
return DirectConstructorHandleAccessor.constructorAccessor(ctor, target);
|
|
||||||
} catch (IllegalAccessException e) {
|
|
||||||
throw new InternalError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -370,27 +370,27 @@ public class ReflectionFactory {
|
||||||
private final Constructor<?> generateConstructor(Class<?> cl,
|
private final Constructor<?> generateConstructor(Class<?> cl,
|
||||||
Constructor<?> constructorToCall) {
|
Constructor<?> constructorToCall) {
|
||||||
|
|
||||||
|
Constructor<?> ctor = newConstructor(constructorToCall.getDeclaringClass(),
|
||||||
ConstructorAccessor acc = new SerializationConstructorAccessorGenerator().
|
constructorToCall.getParameterTypes(),
|
||||||
|
constructorToCall.getExceptionTypes(),
|
||||||
|
constructorToCall.getModifiers(),
|
||||||
|
langReflectAccess.getConstructorSlot(constructorToCall),
|
||||||
|
langReflectAccess.getConstructorSignature(constructorToCall),
|
||||||
|
langReflectAccess.getConstructorAnnotations(constructorToCall),
|
||||||
|
langReflectAccess.getConstructorParameterAnnotations(constructorToCall));
|
||||||
|
ConstructorAccessor acc;
|
||||||
|
if (useOldSerializableConstructor()) {
|
||||||
|
acc = new SerializationConstructorAccessorGenerator().
|
||||||
generateSerializationConstructor(cl,
|
generateSerializationConstructor(cl,
|
||||||
constructorToCall.getParameterTypes(),
|
constructorToCall.getParameterTypes(),
|
||||||
constructorToCall.getModifiers(),
|
constructorToCall.getModifiers(),
|
||||||
constructorToCall.getDeclaringClass());
|
constructorToCall.getDeclaringClass());
|
||||||
Constructor<?> c = newConstructor(constructorToCall.getDeclaringClass(),
|
} else {
|
||||||
constructorToCall.getParameterTypes(),
|
acc = MethodHandleAccessorFactory.newSerializableConstructorAccessor(cl, ctor);
|
||||||
constructorToCall.getExceptionTypes(),
|
}
|
||||||
constructorToCall.getModifiers(),
|
setConstructorAccessor(ctor, acc);
|
||||||
langReflectAccess.
|
ctor.setAccessible(true);
|
||||||
getConstructorSlot(constructorToCall),
|
return ctor;
|
||||||
langReflectAccess.
|
|
||||||
getConstructorSignature(constructorToCall),
|
|
||||||
langReflectAccess.
|
|
||||||
getConstructorAnnotations(constructorToCall),
|
|
||||||
langReflectAccess.
|
|
||||||
getConstructorParameterAnnotations(constructorToCall));
|
|
||||||
setConstructorAccessor(c, acc);
|
|
||||||
c.setAccessible(true);
|
|
||||||
return c;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public final MethodHandle readObjectForSerialization(Class<?> cl) {
|
public final MethodHandle readObjectForSerialization(Class<?> cl) {
|
||||||
|
@ -548,6 +548,10 @@ public class ReflectionFactory {
|
||||||
return config().useNativeAccessorOnly;
|
return config().useNativeAccessorOnly;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static boolean useOldSerializableConstructor() {
|
||||||
|
return config().useOldSerializableConstructor;
|
||||||
|
}
|
||||||
|
|
||||||
private static boolean disableSerialConstructorChecks() {
|
private static boolean disableSerialConstructorChecks() {
|
||||||
return config().disableSerialConstructorChecks;
|
return config().disableSerialConstructorChecks;
|
||||||
}
|
}
|
||||||
|
@ -564,6 +568,7 @@ public class ReflectionFactory {
|
||||||
private static @Stable Config config;
|
private static @Stable Config config;
|
||||||
|
|
||||||
private static final Config DEFAULT_CONFIG = new Config(false, // useNativeAccessorOnly
|
private static final Config DEFAULT_CONFIG = new Config(false, // useNativeAccessorOnly
|
||||||
|
false, // useOldSerializeableConstructor
|
||||||
false); // disableSerialConstructorChecks
|
false); // disableSerialConstructorChecks
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -578,6 +583,7 @@ public class ReflectionFactory {
|
||||||
* is to override them.
|
* is to override them.
|
||||||
*/
|
*/
|
||||||
private record Config(boolean useNativeAccessorOnly,
|
private record Config(boolean useNativeAccessorOnly,
|
||||||
|
boolean useOldSerializableConstructor,
|
||||||
boolean disableSerialConstructorChecks) {
|
boolean disableSerialConstructorChecks) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -601,10 +607,12 @@ public class ReflectionFactory {
|
||||||
Properties props = GetPropertyAction.privilegedGetProperties();
|
Properties props = GetPropertyAction.privilegedGetProperties();
|
||||||
boolean useNativeAccessorOnly =
|
boolean useNativeAccessorOnly =
|
||||||
"true".equals(props.getProperty("jdk.reflect.useNativeAccessorOnly"));
|
"true".equals(props.getProperty("jdk.reflect.useNativeAccessorOnly"));
|
||||||
|
boolean useOldSerializableConstructor =
|
||||||
|
"true".equals(props.getProperty("jdk.reflect.useOldSerializableConstructor"));
|
||||||
boolean disableSerialConstructorChecks =
|
boolean disableSerialConstructorChecks =
|
||||||
"true".equals(props.getProperty("jdk.disableSerialConstructorChecks"));
|
"true".equals(props.getProperty("jdk.disableSerialConstructorChecks"));
|
||||||
|
|
||||||
return new Config(useNativeAccessorOnly, disableSerialConstructorChecks);
|
return new Config(useNativeAccessorOnly, useOldSerializableConstructor, disableSerialConstructorChecks);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -397,7 +397,7 @@ public class MethodHandleAccessorsTest {
|
||||||
};
|
};
|
||||||
private static final Throwable[] mismatched_target_type = new Throwable[] {
|
private static final Throwable[] mismatched_target_type = new Throwable[] {
|
||||||
new IllegalArgumentException("argument type mismatch"),
|
new IllegalArgumentException("argument type mismatch"),
|
||||||
new IllegalArgumentException("object is not an instance of declaring class"),
|
new IllegalArgumentException("object of type java.lang.Object is not an instance of MethodHandleAccessorsTest"),
|
||||||
};
|
};
|
||||||
private static final Throwable[] cannot_get_field = new Throwable[] {
|
private static final Throwable[] cannot_get_field = new Throwable[] {
|
||||||
new IllegalArgumentException("Can not get")
|
new IllegalArgumentException("Can not get")
|
||||||
|
|
|
@ -136,6 +136,11 @@ public class ReflectionFactoryTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expectedExceptions = UnsupportedOperationException.class)
|
||||||
|
static void testConstructorNotSuperClass() throws ReflectiveOperationException {
|
||||||
|
factory.newConstructorForSerialization(Bar.class, Baz.class.getDeclaredConstructor());
|
||||||
|
}
|
||||||
|
|
||||||
static class Foo {
|
static class Foo {
|
||||||
private int foo;
|
private int foo;
|
||||||
public Foo() {
|
public Foo() {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue