Skip to content

Commit 5cea53d

Browse files
Mandy Chungliach
andcommitted
8315810: Reimplement sun.reflect.ReflectionFactory::newConstructorForSerialization with method handles
Co-authored-by: Chen Liang <liach@openjdk.org> Reviewed-by: rriggs
1 parent eb1f67b commit 5cea53d

File tree

10 files changed

+140
-40
lines changed

10 files changed

+140
-40
lines changed

src/java.base/share/classes/java/io/ObjectStreamClass.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,19 +1034,25 @@ Object newInstance()
10341034
new AccessControlContext(domains));
10351035
} catch (UndeclaredThrowableException x) {
10361036
Throwable cause = x.getCause();
1037-
if (cause instanceof InstantiationException)
1038-
throw (InstantiationException) cause;
1039-
if (cause instanceof InvocationTargetException)
1040-
throw (InvocationTargetException) cause;
1041-
if (cause instanceof IllegalAccessException)
1042-
throw (IllegalAccessException) cause;
1037+
if (cause instanceof InstantiationException ie)
1038+
throw ie;
1039+
if (cause instanceof InvocationTargetException ite)
1040+
throw ite;
1041+
if (cause instanceof IllegalAccessException iae)
1042+
throw iae;
10431043
// not supposed to happen
10441044
throw x;
10451045
}
10461046
}
10471047
} catch (IllegalAccessException ex) {
10481048
// should not occur, as access checks have been suppressed
10491049
throw new InternalError(ex);
1050+
} catch (InvocationTargetException ex) {
1051+
Throwable cause = ex.getCause();
1052+
if (cause instanceof Error err)
1053+
throw err;
1054+
else
1055+
throw ex;
10501056
} catch (InstantiationError err) {
10511057
var ex = new InstantiationException();
10521058
ex.initCause(err);

src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,11 @@ static DirectMethodHandle make(Class<?> refc, MemberName member) {
128128
}
129129
static DirectMethodHandle make(MemberName member) {
130130
if (member.isConstructor())
131-
return makeAllocator(member);
131+
return makeAllocator(member.getDeclaringClass(), member);
132132
return make(member.getDeclaringClass(), member);
133133
}
134-
private static DirectMethodHandle makeAllocator(MemberName ctor) {
134+
static DirectMethodHandle makeAllocator(Class<?> instanceClass, MemberName ctor) {
135135
assert(ctor.isConstructor() && ctor.getName().equals("<init>"));
136-
Class<?> instanceClass = ctor.getDeclaringClass();
137136
ctor = ctor.asConstructor();
138137
assert(ctor.isConstructor() && ctor.getReferenceKind() == REF_newInvokeSpecial) : ctor;
139138
MethodType mtype = ctor.getMethodType().changeReturnType(instanceClass);

src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1647,6 +1647,12 @@ public MethodHandle reflectiveInvoker(Class<?> caller) {
16471647
public Class<?>[] exceptionTypes(MethodHandle handle) {
16481648
return VarHandles.exceptionTypes(handle);
16491649
}
1650+
1651+
@Override
1652+
public MethodHandle serializableConstructor(Class<?> decl, Constructor<?> ctorToCall) throws IllegalAccessException {
1653+
return IMPL_LOOKUP.serializableConstructor(decl, ctorToCall);
1654+
}
1655+
16501656
});
16511657
}
16521658

src/java.base/share/classes/java/lang/invoke/MethodHandles.java

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3525,6 +3525,33 @@ public MethodHandle unreflectConstructor(Constructor<?> c) throws IllegalAccessE
35253525
return lookup.getDirectConstructorNoSecurityManager(ctor.getDeclaringClass(), ctor);
35263526
}
35273527

3528+
/*
3529+
* Produces a method handle that is capable of creating instances of the given class
3530+
* and instantiated by the given constructor. No security manager check.
3531+
*
3532+
* This method should only be used by ReflectionFactory::newConstructorForSerialization.
3533+
*/
3534+
/* package-private */ MethodHandle serializableConstructor(Class<?> decl, Constructor<?> c) throws IllegalAccessException {
3535+
MemberName ctor = new MemberName(c);
3536+
assert(ctor.isConstructor() && constructorInSuperclass(decl, c));
3537+
checkAccess(REF_newInvokeSpecial, decl, ctor);
3538+
assert(!MethodHandleNatives.isCallerSensitive(ctor)); // maybeBindCaller not relevant here
3539+
return DirectMethodHandle.makeAllocator(decl, ctor).setVarargs(ctor);
3540+
}
3541+
3542+
private static boolean constructorInSuperclass(Class<?> decl, Constructor<?> ctor) {
3543+
if (decl == ctor.getDeclaringClass())
3544+
return true;
3545+
3546+
Class<?> cl = decl;
3547+
while ((cl = cl.getSuperclass()) != null) {
3548+
if (cl == ctor.getDeclaringClass()) {
3549+
return true;
3550+
}
3551+
}
3552+
return false;
3553+
}
3554+
35283555
/**
35293556
* Produces a method handle giving read access to a reflected field.
35303557
* The type of the method handle will have a return type of the field's

src/java.base/share/classes/jdk/internal/access/JavaLangInvokeAccess.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,4 +162,12 @@ public interface JavaLangInvokeAccess {
162162
* @return an array of exceptions, or {@code null}.
163163
*/
164164
Class<?>[] exceptionTypes(MethodHandle handle);
165+
166+
/**
167+
* Returns a method handle that allocates an instance of the given class
168+
* and then invoke the given constructor of one of its superclasses.
169+
*
170+
* This method should only be used by ReflectionFactory::newConstructorForSerialization.
171+
*/
172+
MethodHandle serializableConstructor(Class<?> decl, Constructor<?> ctorToCall) throws IllegalAccessException;
165173
}

src/java.base/share/classes/jdk/internal/reflect/DirectMethodHandleAccessor.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,8 @@ private boolean isIllegalArgument(RuntimeException ex) {
194194
private void checkReceiver(Object o) {
195195
// NOTE: will throw NullPointerException, as specified, if o is null
196196
if (!declaringClass.isAssignableFrom(o.getClass())) {
197-
throw new IllegalArgumentException("object is not an instance of declaring class");
197+
throw new IllegalArgumentException("object of type " + o.getClass().getName()
198+
+ " is not an instance of " + declaringClass.getName());
198199
}
199200
}
200201

src/java.base/share/classes/jdk/internal/reflect/MethodHandleAccessorFactory.java

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -101,23 +101,63 @@ static ConstructorAccessorImpl newConstructorAccessor(Constructor<?> ctor) {
101101
// Ensure class initialized outside the invocation of method handle
102102
// so that EIIE is propagated (not wrapped with ITE)
103103
ensureClassInitialized(ctor.getDeclaringClass());
104+
try {
105+
MethodHandle target = makeConstructorHandle(JLIA.unreflectConstructor(ctor));
106+
return DirectConstructorHandleAccessor.constructorAccessor(ctor, target);
107+
} catch (IllegalAccessException e) {
108+
throw new InternalError(e);
109+
}
110+
}
111+
112+
/**
113+
* Creates a ConstructorAccessor that is capable of creating instances
114+
* of the given class and instantiated by the given constructor.
115+
*
116+
* @param decl the class to instantiate
117+
* @param ctor the constructor to call
118+
* @return an accessible constructor
119+
*/
120+
static ConstructorAccessorImpl newSerializableConstructorAccessor(Class<?> decl, Constructor<?> ctor) {
121+
if (!constructorInSuperclass(decl, ctor)) {
122+
throw new UnsupportedOperationException(ctor + " not a superclass of " + decl.getName());
123+
}
104124

125+
// ExceptionInInitializerError may be thrown during class initialization
126+
// Ensure class initialized outside the invocation of method handle
127+
// so that EIIE is propagated (not wrapped with ITE)
128+
ensureClassInitialized(decl);
105129
try {
106-
MethodHandle mh = JLIA.unreflectConstructor(ctor);
107-
int paramCount = mh.type().parameterCount();
108-
MethodHandle target = mh.asFixedArity();
109-
MethodType mtype = specializedMethodTypeForConstructor(paramCount);
110-
if (paramCount > SPECIALIZED_PARAM_COUNT) {
111-
// spread the parameters only for the non-specialized case
112-
target = target.asSpreader(Object[].class, paramCount);
113-
}
114-
target = target.asType(mtype);
130+
MethodHandle target = makeConstructorHandle(JLIA.serializableConstructor(decl, ctor));
115131
return DirectConstructorHandleAccessor.constructorAccessor(ctor, target);
116132
} catch (IllegalAccessException e) {
117133
throw new InternalError(e);
118134
}
119135
}
120136

137+
private static boolean constructorInSuperclass(Class<?> decl, Constructor<?> ctor) {
138+
if (decl == ctor.getDeclaringClass())
139+
return true;
140+
141+
Class<?> cl = decl;
142+
while ((cl = cl.getSuperclass()) != null) {
143+
if (cl == ctor.getDeclaringClass()) {
144+
return true;
145+
}
146+
}
147+
return false;
148+
}
149+
150+
private static MethodHandle makeConstructorHandle(MethodHandle ctor) {
151+
int paramCount = ctor.type().parameterCount();
152+
MethodHandle target = ctor.asFixedArity();
153+
MethodType mtype = specializedMethodTypeForConstructor(paramCount);
154+
if (paramCount > SPECIALIZED_PARAM_COUNT) {
155+
// spread the parameters only for the non-specialized case
156+
target = target.asSpreader(Object[].class, paramCount);
157+
}
158+
return target.asType(mtype);
159+
}
160+
121161
/**
122162
* Creates a FieldAccessor for the given reflected field.
123163
*

src/java.base/share/classes/jdk/internal/reflect/ReflectionFactory.java

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -370,27 +370,27 @@ public final Constructor<?> newConstructorForSerialization(Class<?> cl) {
370370
private final Constructor<?> generateConstructor(Class<?> cl,
371371
Constructor<?> constructorToCall) {
372372

373-
374-
ConstructorAccessor acc = new SerializationConstructorAccessorGenerator().
375-
generateSerializationConstructor(cl,
373+
Constructor<?> ctor = newConstructor(constructorToCall.getDeclaringClass(),
376374
constructorToCall.getParameterTypes(),
375+
constructorToCall.getExceptionTypes(),
377376
constructorToCall.getModifiers(),
378-
constructorToCall.getDeclaringClass());
379-
Constructor<?> c = newConstructor(constructorToCall.getDeclaringClass(),
380-
constructorToCall.getParameterTypes(),
381-
constructorToCall.getExceptionTypes(),
382-
constructorToCall.getModifiers(),
383-
langReflectAccess.
384-
getConstructorSlot(constructorToCall),
385-
langReflectAccess.
386-
getConstructorSignature(constructorToCall),
387-
langReflectAccess.
388-
getConstructorAnnotations(constructorToCall),
389-
langReflectAccess.
390-
getConstructorParameterAnnotations(constructorToCall));
391-
setConstructorAccessor(c, acc);
392-
c.setAccessible(true);
393-
return c;
377+
langReflectAccess.getConstructorSlot(constructorToCall),
378+
langReflectAccess.getConstructorSignature(constructorToCall),
379+
langReflectAccess.getConstructorAnnotations(constructorToCall),
380+
langReflectAccess.getConstructorParameterAnnotations(constructorToCall));
381+
ConstructorAccessor acc;
382+
if (useOldSerializableConstructor()) {
383+
acc = new SerializationConstructorAccessorGenerator().
384+
generateSerializationConstructor(cl,
385+
constructorToCall.getParameterTypes(),
386+
constructorToCall.getModifiers(),
387+
constructorToCall.getDeclaringClass());
388+
} else {
389+
acc = MethodHandleAccessorFactory.newSerializableConstructorAccessor(cl, ctor);
390+
}
391+
setConstructorAccessor(ctor, acc);
392+
ctor.setAccessible(true);
393+
return ctor;
394394
}
395395

396396
public final MethodHandle readObjectForSerialization(Class<?> cl) {
@@ -548,6 +548,10 @@ static boolean useNativeAccessorOnly() {
548548
return config().useNativeAccessorOnly;
549549
}
550550

551+
static boolean useOldSerializableConstructor() {
552+
return config().useOldSerializableConstructor;
553+
}
554+
551555
private static boolean disableSerialConstructorChecks() {
552556
return config().disableSerialConstructorChecks;
553557
}
@@ -564,6 +568,7 @@ private static boolean disableSerialConstructorChecks() {
564568
private static @Stable Config config;
565569

566570
private static final Config DEFAULT_CONFIG = new Config(false, // useNativeAccessorOnly
571+
false, // useOldSerializeableConstructor
567572
false); // disableSerialConstructorChecks
568573

569574
/**
@@ -578,6 +583,7 @@ private static boolean disableSerialConstructorChecks() {
578583
* is to override them.
579584
*/
580585
private record Config(boolean useNativeAccessorOnly,
586+
boolean useOldSerializableConstructor,
581587
boolean disableSerialConstructorChecks) {
582588
}
583589

@@ -601,10 +607,12 @@ private static Config loadConfig() {
601607
Properties props = GetPropertyAction.privilegedGetProperties();
602608
boolean useNativeAccessorOnly =
603609
"true".equals(props.getProperty("jdk.reflect.useNativeAccessorOnly"));
610+
boolean useOldSerializableConstructor =
611+
"true".equals(props.getProperty("jdk.reflect.useOldSerializableConstructor"));
604612
boolean disableSerialConstructorChecks =
605613
"true".equals(props.getProperty("jdk.disableSerialConstructorChecks"));
606614

607-
return new Config(useNativeAccessorOnly, disableSerialConstructorChecks);
615+
return new Config(useNativeAccessorOnly, useOldSerializableConstructor, disableSerialConstructorChecks);
608616
}
609617

610618
/**

test/jdk/java/lang/reflect/MethodHandleAccessorsTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,7 @@ static void doTest(Field f, Object target, Object oldValue, Object newValue, Thr
397397
};
398398
private static final Throwable[] mismatched_target_type = new Throwable[] {
399399
new IllegalArgumentException("argument type mismatch"),
400-
new IllegalArgumentException("object is not an instance of declaring class"),
400+
new IllegalArgumentException("object of type java.lang.Object is not an instance of MethodHandleAccessorsTest"),
401401
};
402402
private static final Throwable[] cannot_get_field = new Throwable[] {
403403
new IllegalArgumentException("Can not get")

test/jdk/sun/reflect/ReflectionFactory/ReflectionFactoryTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,11 @@ static void testNonSerializableConstructor(Class<?> cl,
136136
}
137137
}
138138

139+
@Test(expectedExceptions = UnsupportedOperationException.class)
140+
static void testConstructorNotSuperClass() throws ReflectiveOperationException {
141+
factory.newConstructorForSerialization(Bar.class, Baz.class.getDeclaredConstructor());
142+
}
143+
139144
static class Foo {
140145
private int foo;
141146
public Foo() {

0 commit comments

Comments
 (0)