Skip to content

Commit a4a2845

Browse files
author
Christian Wimmer
committed
Simplify reflective constructor invocations by using FactoryMethod
1 parent 6d80ab5 commit a4a2845

File tree

6 files changed

+51
-152
lines changed

6 files changed

+51
-152
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/ReflectionAccessorHolder.java

Lines changed: 9 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,6 @@
3030

3131
import com.oracle.svm.core.annotate.NeverInline;
3232
import com.oracle.svm.core.jdk.InternalVMMethod;
33-
import com.oracle.svm.core.reflect.SubstrateConstructorAccessor.ConstructorNewInstanceFunctionPointer;
3433
import com.oracle.svm.core.reflect.SubstrateMethodAccessor.MethodInvokeFunctionPointer;
3534
import com.oracle.svm.core.util.VMError;
3635

@@ -47,21 +46,13 @@ public final class ReflectionAccessorHolder {
4746
* Signature prototype for invoking a method via a {@link SubstrateMethodAccessor}. Must match
4847
* the signature of {@link MethodInvokeFunctionPointer#invoke}
4948
*/
50-
static Object invokePrototype(boolean invokeSpecial, Object obj, Object[] args) {
51-
throw VMError.shouldNotReachHere("Only used as a prototype for generated methods");
52-
}
53-
54-
/**
55-
* Signature prototype for allocating a new instance via a {@link SubstrateConstructorAccessor}.
56-
* Must match the signature of {@link ConstructorNewInstanceFunctionPointer#invoke}
57-
*/
58-
static Object newInstancePrototype(Object[] args) {
49+
private static Object invokePrototype(boolean invokeSpecial, Object obj, Object[] args) {
5950
throw VMError.shouldNotReachHere("Only used as a prototype for generated methods");
6051
}
6152

6253
/*
6354
* Methods for throwing exceptions when a method or constructor is used in an illegal way. These
64-
* methods are invoked via function pointers, so must have the same signature as the prototypes
55+
* methods are invoked via function pointers, so must have the same signature as the prototype
6556
* above.
6657
*/
6758

@@ -70,7 +61,7 @@ private static void methodHandleInvokeError(boolean invokeSpecial, Object obj, O
7061
throw new InvocationTargetException(new UnsupportedOperationException("MethodHandle.invoke() and MethodHandle.invokeExact() cannot be invoked through reflection"));
7162
}
7263

73-
private static Object newInstanceError(Object[] args) throws InstantiationException {
64+
private static Object newInstanceError(boolean invokeSpecial, Object obj, Object[] args) throws InstantiationException {
7465
throw new InstantiationException("Only non-abstract instance classes can be instantiated using reflection");
7566
}
7667

@@ -80,30 +71,25 @@ private static Object newInstanceError(Object[] args) throws InstantiationExcept
8071
*/
8172

8273
@NeverInline("Exception slow path")
83-
private static InvocationTargetException throwInvocationTargetException(Throwable target) throws InvocationTargetException {
84-
throw new InvocationTargetException(target);
85-
}
86-
87-
@NeverInline("Exception slow path")
88-
private static void throwIllegalArgumentExceptionForMethod(Object member, Object obj, Object[] args) {
89-
throwIllegalArgumentException(member, false, obj, args);
74+
private static void throwIllegalArgumentExceptionWithReceiver(Object member, Object obj, Object[] args) {
75+
throwIllegalArgumentException(member, true, obj, args);
9076
}
9177

9278
@NeverInline("Exception slow path")
93-
private static void throwIllegalArgumentExceptionForConstructor(Object member, Object[] args) {
94-
throwIllegalArgumentException(member, true, null, args);
79+
private static void throwIllegalArgumentExceptionWithoutReceiver(Object member, Object[] args) {
80+
throwIllegalArgumentException(member, false, null, args);
9581
}
9682

9783
/**
9884
* We do not know which check in the generated method caused the exception, so we cannot print
9985
* detailed information about that. But printing the signature of the method and all the types
10086
* of the actual arguments should make it obvious what the problem is.
10187
*/
102-
private static void throwIllegalArgumentException(Object member, boolean constructor, Object obj, Object[] args) {
88+
private static void throwIllegalArgumentException(Object member, boolean withReceiver, Object obj, Object[] args) {
10389
String sep = System.lineSeparator();
10490
StringBuilder msg = new StringBuilder();
10591
msg.append("Illegal arguments for invoking ").append(member);
106-
if (!constructor) {
92+
if (withReceiver) {
10793
msg.append(sep).append(" obj: ").append(obj == null ? "null" : obj.getClass().getTypeName());
10894
}
10995
if (args == null) {

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/reflect/SubstrateConstructorAccessor.java

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,13 @@
3030

3131
import org.graalvm.nativeimage.c.function.CFunctionPointer;
3232

33-
import com.oracle.svm.core.annotate.InvokeJavaFunctionPointer;
3433
import com.oracle.svm.core.jdk.InternalVMMethod;
34+
import com.oracle.svm.core.reflect.SubstrateMethodAccessor.MethodInvokeFunctionPointer;
3535
import com.oracle.svm.core.util.VMError;
3636

3737
@InternalVMMethod
3838
public abstract class SubstrateConstructorAccessor {
3939

40-
interface ConstructorNewInstanceFunctionPointer extends CFunctionPointer {
41-
/** Must match the signature of {@link ReflectionAccessorHolder#newInstancePrototype}. */
42-
@InvokeJavaFunctionPointer
43-
Object invoke(Object[] args);
44-
}
45-
4640
private final Executable member;
4741
private final CFunctionPointer newInstanceFunctionPointer;
4842

@@ -52,11 +46,11 @@ protected SubstrateConstructorAccessor(Executable member, CFunctionPointer newIn
5246
}
5347

5448
public Object newInstance(Object[] args) {
55-
ConstructorNewInstanceFunctionPointer functionPointer = (ConstructorNewInstanceFunctionPointer) this.newInstanceFunctionPointer;
49+
MethodInvokeFunctionPointer functionPointer = (MethodInvokeFunctionPointer) this.newInstanceFunctionPointer;
5650
if (functionPointer.isNull()) {
5751
throw newInstanceError();
5852
}
59-
return functionPointer.invoke(args);
53+
return functionPointer.invoke(false, null, args);
6054
}
6155

6256
private RuntimeException newInstanceError() {

substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionFeature.java

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727
// Checkstyle: allow reflection
2828

2929
import java.lang.invoke.MethodHandle;
30-
import java.lang.reflect.Constructor;
3130
import java.lang.reflect.Executable;
3231
import java.lang.reflect.Field;
3332
import java.lang.reflect.Method;
@@ -85,9 +84,8 @@ public class ReflectionFeature implements GraalFeature {
8584
final Map<Executable, Object> accessors = new ConcurrentHashMap<>();
8685

8786
private static final Method invokePrototype = ReflectionUtil.lookupMethod(ReflectionAccessorHolder.class, "invokePrototype", boolean.class, Object.class, Object[].class);
88-
private static final Method newInstancePrototype = ReflectionUtil.lookupMethod(ReflectionAccessorHolder.class, "newInstancePrototype", Object[].class);
8987
private static final Method methodHandleInvokeErrorMethod = ReflectionUtil.lookupMethod(ReflectionAccessorHolder.class, "methodHandleInvokeError", boolean.class, Object.class, Object[].class);
90-
private static final Method newInstanceErrorMethod = ReflectionUtil.lookupMethod(ReflectionAccessorHolder.class, "newInstanceError", Object[].class);
88+
private static final Method newInstanceErrorMethod = ReflectionUtil.lookupMethod(ReflectionAccessorHolder.class, "newInstanceError", boolean.class, Object.class, Object[].class);
9189

9290
FeatureImpl.BeforeAnalysisAccessImpl analysisAccess;
9391

@@ -109,24 +107,24 @@ Object getOrCreateAccessor(Executable member) {
109107
* instances use function pointer calls to invocation stubs. The invocation stubs unpack the
110108
* Object[] array arguments and invoke the actual method.
111109
*
112-
* The stubs are methods with manually created Graal IR: {@link ReflectiveInvokeMethod} and
113-
* {@link ReflectiveNewInstanceMethod}. Since they are only invoked via function pointers and
114-
* never at a normal call site, they need to be registered for compilation manually. From the
115-
* point of view of the static analysis, they are root methods.
110+
* The stubs are methods with manually created Graal IR: {@link ReflectiveInvokeMethod}. Since
111+
* they are only invoked via function pointers and never at a normal call site, they need to be
112+
* registered for compilation manually. From the point of view of the static analysis, they are
113+
* root methods.
116114
*
117115
* {@link ConcurrentHashMap#computeIfAbsent} guarantees that this method is called only once per
118116
* member, so no further synchronization is necessary.
119117
*/
120118
private Object createAccessor(Executable member) {
121119
String name = SubstrateUtil.uniqueShortName(member);
120+
ResolvedJavaMethod prototype = analysisAccess.getMetaAccess().lookupJavaMethod(invokePrototype).getWrapped();
122121
if (member instanceof Method) {
123122
ResolvedJavaMethod invokeMethod;
124123
if (member.getDeclaringClass() == MethodHandle.class && (member.getName().equals("invoke") || member.getName().equals("invokeExact"))) {
125124
/* Method handles must not be invoked via reflection. */
126125
invokeMethod = analysisAccess.getMetaAccess().lookupJavaMethod(methodHandleInvokeErrorMethod);
127126
} else {
128-
ResolvedJavaMethod prototype = analysisAccess.getMetaAccess().lookupJavaMethod(invokePrototype).getWrapped();
129-
invokeMethod = createReflectiveInvokeMethod(name, prototype, (Method) member);
127+
invokeMethod = new ReflectiveInvokeMethod(name, prototype, member);
130128
}
131129
return ImageSingletons.lookup(SubstrateReflectionAccessorFactory.class).createMethodAccessor(member, register(invokeMethod));
132130

@@ -142,8 +140,7 @@ private Object createAccessor(Executable member) {
142140
*/
143141
newInstanceMethod = analysisAccess.getMetaAccess().lookupJavaMethod(newInstanceErrorMethod);
144142
} else {
145-
ResolvedJavaMethod prototype = analysisAccess.getMetaAccess().lookupJavaMethod(newInstancePrototype).getWrapped();
146-
newInstanceMethod = createReflectiveNewInstanceMethod(name, prototype, (Constructor<?>) member);
143+
newInstanceMethod = new ReflectiveInvokeMethod(name, prototype, member);
147144
}
148145
return ImageSingletons.lookup(SubstrateReflectionAccessorFactory.class).createConstructorAccessor(member, register(newInstanceMethod));
149146
}
@@ -155,14 +152,6 @@ private CFunctionPointer register(ResolvedJavaMethod method) {
155152
return new MethodPointer(aMethod);
156153
}
157154

158-
protected ResolvedJavaMethod createReflectiveInvokeMethod(String name, ResolvedJavaMethod prototype, Method method) {
159-
return new ReflectiveInvokeMethod(name, prototype, method);
160-
}
161-
162-
protected ResolvedJavaMethod createReflectiveNewInstanceMethod(String name, ResolvedJavaMethod prototype, Constructor<?> constructor) {
163-
return new ReflectiveNewInstanceMethod(name, prototype, constructor);
164-
}
165-
166155
protected void inspectAccessibleField(@SuppressWarnings("unused") Field field) {
167156
}
168157

substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectionGraphKit.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626

2727
// Checkstyle: allow reflection
2828

29+
import java.lang.reflect.Constructor;
2930
import java.lang.reflect.Executable;
31+
import java.lang.reflect.InvocationTargetException;
3032
import java.util.ArrayList;
3133
import java.util.Arrays;
3234
import java.util.HashMap;
@@ -60,12 +62,15 @@
6062
import org.graalvm.compiler.nodes.java.InstanceOfNode;
6163
import org.graalvm.compiler.nodes.type.StampTool;
6264

65+
import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess;
6366
import com.oracle.graal.pointsto.meta.HostedProviders;
6467
import com.oracle.svm.core.graal.nodes.LoweredDeadEndNode;
6568
import com.oracle.svm.core.meta.SubstrateObjectConstant;
6669
import com.oracle.svm.core.reflect.ReflectionAccessorHolder;
6770
import com.oracle.svm.core.util.VMError;
71+
import com.oracle.svm.hosted.code.FactoryMethodSupport;
6872
import com.oracle.svm.hosted.phases.HostedGraphKit;
73+
import com.oracle.svm.util.ReflectionUtil;
6974

7075
import jdk.vm.ci.meta.JavaKind;
7176
import jdk.vm.ci.meta.ResolvedJavaMethod;
@@ -137,20 +142,23 @@ public void emitIllegalArgumentException(Executable member, ValueNode obj, Value
137142
ResolvedJavaMethod targetMethod;
138143
ValueNode[] arguments;
139144
if (obj == null) {
140-
targetMethod = findMethod(ReflectionAccessorHolder.class, "throwIllegalArgumentExceptionForConstructor", true);
145+
targetMethod = findMethod(ReflectionAccessorHolder.class, "throwIllegalArgumentExceptionWithoutReceiver", true);
141146
arguments = new ValueNode[]{memberNode, args};
142147
} else {
143-
targetMethod = findMethod(ReflectionAccessorHolder.class, "throwIllegalArgumentExceptionForMethod", true);
148+
targetMethod = findMethod(ReflectionAccessorHolder.class, "throwIllegalArgumentExceptionWithReceiver", true);
144149
arguments = new ValueNode[]{memberNode, obj, args};
145150
}
146151
createJavaCallWithExceptionAndUnwind(CallTargetNode.InvokeKind.Static, targetMethod, arguments);
147152
append(new LoweredDeadEndNode());
148153
}
149154

155+
private static final Constructor<InvocationTargetException> invocationTargetExceptionConstructor = ReflectionUtil.lookupConstructor(InvocationTargetException.class, Throwable.class);
156+
150157
public void emitInvocationTargetException() {
151158
AbstractMergeNode merge = continueWithMerge(invocationTargetExceptionPaths);
152159
ValueNode exception = createPhi(invocationTargetExceptionPaths, merge);
153-
ResolvedJavaMethod throwInvocationTargetException = findMethod(ReflectionAccessorHolder.class, "throwInvocationTargetException", true);
160+
ResolvedJavaMethod throwInvocationTargetException = FactoryMethodSupport.singleton().lookup((UniverseMetaAccess) getMetaAccess(),
161+
getMetaAccess().lookupJavaMethod(invocationTargetExceptionConstructor), true);
154162
createJavaCallWithExceptionAndUnwind(CallTargetNode.InvokeKind.Static, throwInvocationTargetException, exception);
155163
append(new LoweredDeadEndNode());
156164
}

substratevm/src/com.oracle.svm.reflect/src/com/oracle/svm/reflect/hosted/ReflectiveInvokeMethod.java

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@
2929
import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_0;
3030
import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_0;
3131

32-
import java.lang.reflect.Method;
32+
import java.lang.reflect.Executable;
3333
import java.util.ArrayList;
3434
import java.util.List;
3535

@@ -55,10 +55,12 @@
5555
import org.graalvm.compiler.nodes.spi.CanonicalizerTool;
5656
import org.graalvm.compiler.phases.common.inlining.InliningUtil;
5757

58+
import com.oracle.graal.pointsto.infrastructure.UniverseMetaAccess;
5859
import com.oracle.graal.pointsto.meta.HostedProviders;
5960
import com.oracle.graal.pointsto.phases.SubstrateIntrinsicGraphBuilder;
6061
import com.oracle.svm.core.meta.SharedMethod;
6162
import com.oracle.svm.core.reflect.SubstrateMethodAccessor;
63+
import com.oracle.svm.hosted.code.FactoryMethodSupport;
6264
import com.oracle.svm.hosted.code.NonBytecodeStaticMethod;
6365

6466
import jdk.vm.ci.meta.JavaKind;
@@ -67,9 +69,9 @@
6769

6870
public class ReflectiveInvokeMethod extends NonBytecodeStaticMethod {
6971

70-
private final Method method;
72+
private final Executable method;
7173

72-
public ReflectiveInvokeMethod(String name, ResolvedJavaMethod prototype, Method method) {
74+
public ReflectiveInvokeMethod(String name, ResolvedJavaMethod prototype, Executable method) {
7375
super(name, prototype.getDeclaringClass(), prototype.getSignature(), prototype.getConstantPool());
7476
this.method = method;
7577
}
@@ -91,12 +93,21 @@ public StructuredGraph buildGraph(DebugContext ctx, ResolvedJavaMethod m, Hosted
9193
graphKit.getFrameState().clearLocals();
9294

9395
ResolvedJavaMethod targetMethod = providers.getMetaAccess().lookupJavaMethod(method);
96+
if (targetMethod.isStatic() || targetMethod.isConstructor()) {
97+
graphKit.emitEnsureInitializedCall(targetMethod.getDeclaringClass());
98+
}
99+
if (targetMethod.isConstructor()) {
100+
/*
101+
* For a constructor, we invoke a synthetic static factory method that combines both the
102+
* allocation and the constructor invocation.
103+
*/
104+
targetMethod = FactoryMethodSupport.singleton().lookup((UniverseMetaAccess) providers.getMetaAccess(), targetMethod, false);
105+
}
106+
94107
Class<?>[] argTypes = method.getParameterTypes();
95108
int receiverOffset = targetMethod.isStatic() ? 0 : 1;
96109
ValueNode[] args = new ValueNode[argTypes.length + receiverOffset];
97-
if (targetMethod.isStatic()) {
98-
graphKit.emitEnsureInitializedCall(targetMethod.getDeclaringClass());
99-
} else {
110+
if (!targetMethod.isStatic()) {
100111
/*
101112
* The specification explicitly demands a NullPointerException and not a
102113
* IllegalArgumentException when the receiver of a non-static method is null
@@ -112,11 +123,12 @@ public StructuredGraph buildGraph(DebugContext ctx, ResolvedJavaMethod m, Hosted
112123
graphKit.fillArgsArray(argumentArray, receiverOffset, args, argTypes);
113124

114125
InvokeKind invokeKind;
126+
assert !targetMethod.isConstructor() : "Constructors are already rewritten to static factory methods";
115127
if (targetMethod.isStatic()) {
116128
invokeKind = InvokeKind.Static;
117129
} else if (targetMethod.isInterface()) {
118130
invokeKind = InvokeKind.Interface;
119-
} else if (targetMethod.canBeStaticallyBound() || targetMethod.isConstructor()) {
131+
} else if (targetMethod.canBeStaticallyBound()) {
120132
invokeKind = InvokeKind.Special;
121133
} else {
122134
invokeKind = InvokeKind.Virtual;
@@ -172,7 +184,7 @@ public StructuredGraph buildGraph(DebugContext ctx, ResolvedJavaMethod m, Hosted
172184
}
173185
graphKit.createReturn(returnValue, JavaKind.Object);
174186

175-
graphKit.emitIllegalArgumentException(method, receiver, argumentArray);
187+
graphKit.emitIllegalArgumentException(method, invokeKind == InvokeKind.Static ? null : receiver, argumentArray);
176188
graphKit.emitInvocationTargetException();
177189

178190
for (InvokeWithExceptionNode invoke : invokes) {

0 commit comments

Comments
 (0)