Skip to content

Commit 011784e

Browse files
committed
Correctly handle invokeSpecial for method handle constructors
1 parent b1dbe23 commit 011784e

File tree

7 files changed

+184
-109
lines changed

7 files changed

+184
-109
lines changed

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/invoke/Target_java_lang_invoke_MemberName.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ public final class Target_java_lang_invoke_MemberName {
6161
@Alias
6262
public native boolean isField();
6363

64+
@Alias
65+
public native boolean isInvocable();
66+
6467
@Alias
6568
public native Class<?> getDeclaringClass();
6669

@@ -70,6 +73,9 @@ public final class Target_java_lang_invoke_MemberName {
7073
@Alias
7174
public native Class<?> getFieldType();
7275

76+
@Alias
77+
public native MethodType getInvocationType();
78+
7379
@Alias
7480
public native byte getReferenceKind();
7581

substratevm/src/com.oracle.svm.core/src/com/oracle/svm/core/methodhandles/Target_java_lang_invoke_MethodHandle.java

Lines changed: 130 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@
2828

2929
import java.lang.invoke.MethodHandle;
3030
import java.lang.invoke.MethodType;
31+
import java.lang.reflect.AccessibleObject;
3132
import java.lang.reflect.Constructor;
3233
import java.lang.reflect.Field;
3334
import java.lang.reflect.InvocationTargetException;
35+
import java.lang.reflect.Member;
3436
import java.lang.reflect.Method;
3537
import java.lang.reflect.Modifier;
3638
import java.util.Arrays;
@@ -45,10 +47,12 @@
4547
import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode;
4648
import com.oracle.svm.core.invoke.MethodHandleUtils;
4749
import com.oracle.svm.core.invoke.Target_java_lang_invoke_MemberName;
50+
import com.oracle.svm.core.reflect.SubstrateAccessor;
51+
import com.oracle.svm.core.reflect.SubstrateConstructorAccessor;
4852
import com.oracle.svm.core.reflect.SubstrateMethodAccessor;
4953
import com.oracle.svm.core.reflect.target.Target_java_lang_reflect_AccessibleObject;
54+
import com.oracle.svm.core.reflect.target.Target_java_lang_reflect_Constructor;
5055
import com.oracle.svm.core.reflect.target.Target_java_lang_reflect_Method;
51-
import com.oracle.svm.core.reflect.target.Target_jdk_internal_reflect_MethodAccessor;
5256
import com.oracle.svm.core.util.VMError;
5357

5458
import sun.invoke.util.ValueConversions;
@@ -118,22 +122,22 @@ Object invokeExact(Object... args) throws Throwable {
118122

119123
@Substitute(polymorphicSignature = true)
120124
static Object linkToVirtual(Object... args) throws Throwable {
121-
return Util_java_lang_invoke_MethodHandle.linkTo(true, args);
125+
return Util_java_lang_invoke_MethodHandle.linkTo(args);
122126
}
123127

124128
@Substitute(polymorphicSignature = true)
125129
static Object linkToStatic(Object... args) throws Throwable {
126-
return Util_java_lang_invoke_MethodHandle.linkTo(false, args);
130+
return Util_java_lang_invoke_MethodHandle.linkTo(args);
127131
}
128132

129133
@Substitute(polymorphicSignature = true)
130134
static Object linkToInterface(Object... args) throws Throwable {
131-
return Util_java_lang_invoke_MethodHandle.linkTo(true, args);
135+
return Util_java_lang_invoke_MethodHandle.linkTo(args);
132136
}
133137

134138
@Substitute(polymorphicSignature = true)
135139
static Object linkToSpecial(Object... args) throws Throwable {
136-
return Util_java_lang_invoke_MethodHandle.linkTo(true, args);
140+
return Util_java_lang_invoke_MethodHandle.linkTo(args);
137141
}
138142

139143
@Substitute(polymorphicSignature = true)
@@ -158,13 +162,10 @@ void maybeCustomize() {
158162
}
159163

160164
final class Util_java_lang_invoke_MethodHandle {
161-
static Object linkTo(boolean hasReceiver, Object... args) throws Throwable {
165+
static Object linkTo(Object... args) throws Throwable {
162166
assert args.length > 0;
163167
Target_java_lang_invoke_MemberName memberName = (Target_java_lang_invoke_MemberName) args[args.length - 1];
164-
MethodType methodType = memberName.getMethodType();
165-
if (hasReceiver) {
166-
methodType = methodType.insertParameterTypes(0, memberName.getDeclaringClass());
167-
}
168+
MethodType methodType = memberName.getInvocationType();
168169
return MethodHandleUtils.cast(invokeInternal(memberName, methodType, Arrays.copyOf(args, args.length - 1)), methodType.returnType());
169170
}
170171

@@ -179,95 +180,129 @@ static Object invokeInternal(Target_java_lang_invoke_MemberName memberName, Meth
179180
}
180181

181182
if (memberName.intrinsic != null) { /* Intrinsic call */
182-
assert memberName.reflectAccess == null;
183+
VMError.guarantee(memberName.reflectAccess == null);
183184
return memberName.intrinsic.execute(args);
184-
} else if (memberName.isField()) { /* Field access */
185-
Target_java_lang_reflect_AccessibleObject executable = SubstrateUtil.cast(memberName.reflectAccess, Target_java_lang_reflect_AccessibleObject.class);
186-
187-
/* Access control was already performed by the JDK code calling invokeBasic */
188-
boolean oldOverride = executable.override;
189-
executable.override = true;
190-
try {
191-
Field field = (Field) memberName.reflectAccess;
192-
byte refKind = memberName.getReferenceKind();
193-
if (Modifier.isStatic(field.getModifiers())) {
194-
if (refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_getStatic) {
195-
assert (args == null || args.length == 0) && field.canAccess(null);
196-
return field.get(null);
197-
} else if (refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_putStatic) {
198-
assert args.length == 1 && field.canAccess(null);
199-
Object value = args[0];
200-
field.set(null, value);
201-
return null;
202-
} else {
203-
throw VMError.shouldNotReachHere("Wrong reference kind for static field access: " + memberName.getReferenceKind());
204-
}
205-
} else {
206-
if (refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_getField) {
207-
assert args.length == 1 && field.canAccess(args[0]);
208-
Object receiver = args[0];
209-
return field.get(receiver);
210-
} else if (refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_putField) {
211-
assert args.length == 2 && field.canAccess(args[0]);
212-
Object receiver = args[0];
213-
Object value = args[1];
214-
field.set(receiver, value);
215-
return null;
216-
} else {
217-
throw VMError.shouldNotReachHere("Wrong reference kind for instance field access: " + memberName.getReferenceKind());
218-
}
219-
}
220-
} finally {
221-
executable.override = oldOverride;
222-
}
223-
} else { /* Method or constructor invocation */
224-
assert args.length == methodType.parameterCount();
225-
for (int i = 0; i < args.length; ++i) {
226-
Class<?> expectedParamType = methodType.parameterType(i);
227-
if (expectedParamType.isPrimitive()) {
228-
Wrapper destWrapper = Wrapper.forPrimitiveType(expectedParamType);
229-
Wrapper srcWrapper = Wrapper.forWrapperType(args[i].getClass());
230-
if (destWrapper != srcWrapper) {
231-
/* We can't rely on automatic casting for the argument */
232-
Target_java_lang_invoke_MethodHandle typeConverter = SubstrateUtil.cast(ValueConversions.convertPrimitive(srcWrapper, destWrapper),
233-
Target_java_lang_invoke_MethodHandle.class);
234-
args[i] = typeConverter.invokeBasic(args[i]);
235-
}
236-
}
185+
}
186+
187+
Target_java_lang_reflect_AccessibleObject reflectAccess = SubstrateUtil.cast(memberName.reflectAccess, Target_java_lang_reflect_AccessibleObject.class);
188+
189+
/* Access control was already performed by the JDK code calling invokeBasic */
190+
boolean oldOverride = reflectAccess.override;
191+
reflectAccess.override = true;
192+
try {
193+
byte refKind = memberName.getReferenceKind();
194+
/* This cannot be a switch as the REF_ aliases are not declared as final. */
195+
if (refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_getField) {
196+
checkArgs(args, 1, "getField");
197+
Object receiver = args[0];
198+
Field field = asField(memberName, receiver);
199+
return field.get(receiver);
200+
} else if (refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_getStatic) {
201+
checkArgs(args, 0, "getStatic");
202+
Field field = asField(memberName, null);
203+
return field.get(null);
204+
} else if (refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_putField) {
205+
checkArgs(args, 2, "putField");
206+
Object receiver = args[0];
207+
Object value = args[1];
208+
Field field = asField(memberName, receiver);
209+
field.set(receiver, value);
210+
return null;
211+
} else if (refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_putStatic) {
212+
checkArgs(args, 1, "putStatic");
213+
Object value = args[0];
214+
Field field = asField(memberName, null);
215+
field.set(null, value);
216+
return null;
217+
} else if (refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_invokeVirtual ||
218+
refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_invokeInterface) {
219+
convertArgs(args, methodType);
220+
Object receiver = args[0];
221+
Object[] invokeArgs = Arrays.copyOfRange(args, 1, args.length);
222+
Method method = asMethod(memberName, receiver);
223+
return method.invoke(receiver, invokeArgs);
224+
} else if (refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_invokeStatic) {
225+
convertArgs(args, methodType);
226+
Method method = asMethod(memberName, null);
227+
return method.invoke(null, args);
228+
} else if (refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_invokeSpecial) {
229+
convertArgs(args, methodType);
230+
Object receiver = args[0];
231+
Object[] invokeArgs = Arrays.copyOfRange(args, 1, args.length);
232+
SubstrateAccessor accessor = getAccessor(memberName);
233+
Object returnValue = accessor.invokeSpecial(receiver, invokeArgs);
234+
return methodType.returnType() == void.class ? null : returnValue;
235+
} else if (refKind == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_newInvokeSpecial) {
236+
convertArgs(args, methodType);
237+
Constructor<?> constructor = asConstructor(memberName);
238+
return constructor.newInstance(args);
239+
} else {
240+
throw VMError.shouldNotReachHere("Unknown method handle reference kind: " + refKind);
237241
}
242+
} catch (InvocationTargetException e) {
243+
/* Exceptions are thrown unchanged from method handles */
244+
throw e.getCause();
245+
} finally {
246+
reflectAccess.override = oldOverride;
247+
}
248+
}
249+
250+
private static Field asField(Target_java_lang_invoke_MemberName memberName, Object receiver) {
251+
VMError.guarantee(memberName.isField(), "Cannot perform field operations on an executable");
252+
Field field = (Field) memberName.reflectAccess;
253+
checkMember(field, receiver);
254+
return field;
255+
}
256+
257+
private static Method asMethod(Target_java_lang_invoke_MemberName memberName, Object receiver) {
258+
VMError.guarantee(memberName.isMethod(), "Cannot perform method operations on a field or constructor");
259+
Method method = (Method) memberName.reflectAccess;
260+
checkMember(method, receiver);
261+
return method;
262+
}
263+
264+
private static Constructor<?> asConstructor(Target_java_lang_invoke_MemberName memberName) {
265+
VMError.guarantee(memberName.isConstructor(), "Cannot perform constructor operations on a field or constructor");
266+
Constructor<?> constructor = (Constructor<?>) memberName.reflectAccess;
267+
checkMember(constructor, null);
268+
return constructor;
269+
}
270+
271+
private static <T extends AccessibleObject & Member> void checkMember(T member, Object receiver) {
272+
if (!(member instanceof Constructor<?>)) {
273+
boolean isStatic = receiver == null;
274+
VMError.guarantee(Modifier.isStatic(member.getModifiers()) == isStatic,
275+
"Cannot perform %s operation on a %s member".formatted(isStatic ? "static" : "non-static", isStatic ? "non-static" : "static"));
276+
}
277+
VMError.guarantee(member.canAccess(receiver), "The member should have been made accessible by the method handle implementation");
278+
}
279+
280+
private static SubstrateAccessor getAccessor(Target_java_lang_invoke_MemberName memberName) {
281+
VMError.guarantee(memberName.isInvocable(), "Cannot perform invokeSpecial on a field");
282+
if (memberName.isMethod()) {
283+
return SubstrateUtil.cast(SubstrateUtil.cast(memberName.reflectAccess, Target_java_lang_reflect_Method.class).acquireMethodAccessor(), SubstrateMethodAccessor.class);
284+
} else {
285+
return SubstrateUtil.cast(SubstrateUtil.cast(memberName.reflectAccess, Target_java_lang_reflect_Constructor.class).acquireConstructorAccessor(), SubstrateConstructorAccessor.class);
286+
}
287+
}
288+
289+
private static void checkArgs(Object[] args, int expectedLength, String methodName) {
290+
VMError.guarantee((expectedLength == 0 && args == null) || args.length == expectedLength, "%s requires exactly %d arguments".formatted(methodName, expectedLength));
291+
}
238292

239-
Target_java_lang_reflect_AccessibleObject executable = SubstrateUtil.cast(memberName.reflectAccess, Target_java_lang_reflect_AccessibleObject.class);
240-
241-
/* Access control was already performed by the JDK code calling invokeBasic */
242-
boolean oldOverride = executable.override;
243-
executable.override = true;
244-
try {
245-
if (memberName.isConstructor()) {
246-
Constructor<?> constructor = (Constructor<?>) memberName.reflectAccess;
247-
assert constructor.canAccess(null);
248-
return constructor.newInstance(args);
249-
} else {
250-
Method method = (Method) memberName.reflectAccess;
251-
if (Modifier.isStatic(method.getModifiers())) {
252-
assert method.canAccess(null);
253-
return method.invoke(null, args);
254-
} else {
255-
assert method.canAccess(args[0]);
256-
Object receiver = args[0];
257-
Object[] invokeArgs = Arrays.copyOfRange(args, 1, args.length);
258-
if (memberName.getReferenceKind() == Target_java_lang_invoke_MethodHandleNatives_Constants.REF_invokeSpecial) {
259-
Target_jdk_internal_reflect_MethodAccessor accessor = SubstrateUtil.cast(method, Target_java_lang_reflect_Method.class).acquireMethodAccessor();
260-
return SubstrateUtil.cast(accessor, SubstrateMethodAccessor.class).invokeSpecial(receiver, invokeArgs);
261-
} else {
262-
return method.invoke(receiver, invokeArgs);
263-
}
264-
}
293+
private static void convertArgs(Object[] args, MethodType methodType) throws Throwable {
294+
assert args.length == methodType.parameterCount();
295+
for (int i = 0; i < args.length; ++i) {
296+
Class<?> expectedParamType = methodType.parameterType(i);
297+
if (expectedParamType.isPrimitive()) {
298+
Wrapper destWrapper = Wrapper.forPrimitiveType(expectedParamType);
299+
Wrapper srcWrapper = Wrapper.forWrapperType(args[i].getClass());
300+
if (destWrapper != srcWrapper) {
301+
/* We can't rely on automatic casting for the argument */
302+
Target_java_lang_invoke_MethodHandle typeConverter = SubstrateUtil.cast(ValueConversions.convertPrimitive(srcWrapper, destWrapper),
303+
Target_java_lang_invoke_MethodHandle.class);
304+
args[i] = typeConverter.invokeBasic(args[i]);
265305
}
266-
} catch (InvocationTargetException e) {
267-
/* Exceptions are thrown unchanged from method handles */
268-
throw e.getCause();
269-
} finally {
270-
executable.override = oldOverride;
271306
}
272307
}
273308
}

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

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,4 +92,12 @@ public CFunctionPointer getExpandSignature() {
9292
public ResolvedJavaMethod getTargetMethod() {
9393
return targetMethod;
9494
}
95+
96+
public Object invokeSpecial(Object obj, Object[] args) {
97+
CFunctionPointer target = directTarget;
98+
if (target.isNull()) {
99+
throw new IllegalArgumentException("Cannot do invokespecial for an abstract method");
100+
}
101+
return ((ReflectionAccessorHolder.MethodInvokeFunctionPointer) expandSignature).invoke(obj, args, target);
102+
}
95103
}

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

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626

2727
import java.lang.reflect.Executable;
2828

29+
import org.graalvm.nativeimage.Platform;
30+
import org.graalvm.nativeimage.Platforms;
2931
import org.graalvm.nativeimage.c.function.CFunctionPointer;
3032

3133
import com.oracle.svm.core.classinitialization.EnsureClassInitializedNode;
@@ -39,15 +41,36 @@
3941
@InternalVMMethod
4042
public final class SubstrateConstructorAccessor extends SubstrateAccessor implements ConstructorAccessor {
4143

42-
public SubstrateConstructorAccessor(Executable member, CFunctionPointer expandSignature, CFunctionPointer directTarget, ResolvedJavaMethod targetMethod, DynamicHub initializeBeforeInvoke) {
44+
private final CFunctionPointer factoryMethodTarget;
45+
46+
@Platforms(Platform.HOSTED_ONLY.class) //
47+
private final ResolvedJavaMethod factoryMethod;
48+
49+
public SubstrateConstructorAccessor(Executable member, CFunctionPointer expandSignature, CFunctionPointer directTarget, ResolvedJavaMethod targetMethod, CFunctionPointer factoryMethodTarget,
50+
ResolvedJavaMethod factoryMethod, DynamicHub initializeBeforeInvoke) {
4351
super(member, expandSignature, directTarget, targetMethod, initializeBeforeInvoke);
52+
this.factoryMethodTarget = factoryMethodTarget;
53+
this.factoryMethod = factoryMethod;
54+
}
55+
56+
@Platforms(Platform.HOSTED_ONLY.class)
57+
public ResolvedJavaMethod getFactoryMethod() {
58+
return factoryMethod;
4459
}
4560

4661
@Override
4762
public Object newInstance(Object[] args) {
4863
if (initializeBeforeInvoke != null) {
4964
EnsureClassInitializedNode.ensureClassInitialized(DynamicHub.toClass(initializeBeforeInvoke));
5065
}
51-
return ((MethodInvokeFunctionPointer) expandSignature).invoke(null, args, directTarget);
66+
return ((MethodInvokeFunctionPointer) expandSignature).invoke(null, args, factoryMethodTarget);
67+
}
68+
69+
@Override
70+
public Object invokeSpecial(Object obj, Object[] args) {
71+
if (initializeBeforeInvoke != null) {
72+
EnsureClassInitializedNode.ensureClassInitialized(DynamicHub.toClass(initializeBeforeInvoke));
73+
}
74+
return super.invokeSpecial(obj, args);
5275
}
5376
}

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

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,12 @@ public Object invoke(Object obj, Object[] args, Class<?> caller) {
156156
}
157157
}
158158

159+
@Override
159160
public Object invokeSpecial(Object obj, Object[] args) {
160161
if (callerSensitiveAdapter) {
161162
throw VMError.shouldNotReachHere("Cannot invoke method that has a @CallerSensitiveAdapter without an explicit caller");
162163
}
163164
preInvoke(obj);
164-
165-
CFunctionPointer target = directTarget;
166-
if (target.isNull()) {
167-
throw new IllegalArgumentException("Cannot do invokespecial for an abstract method");
168-
}
169-
return ((MethodInvokeFunctionPointer) expandSignature).invoke(obj, args, target);
165+
return super.invokeSpecial(obj, args);
170166
}
171167
}

0 commit comments

Comments
 (0)