我想从 Java 动态调用本机方法。因为方法签名在编译时是未知的,所以我为大多数具有相同签名的原始返回类型创建了通用本机方法:
class NativeHook {
public static native int callInt(String funcName, Object... funcArgs);
public static native void callVoid(String funcName, Object... funcArgs);
public static native Object callObject(String funcName, Object... funcArgs);
private static MethodHandle getNativeMethod(String callName, Class<?> returnType) {
return MethodHandles.lookup().findStatic(NativeHook.class, callName,
MethodType.methodType(returnType, String.class, Object[].class));
}
}
我希望创建一个 MethodHandle,然后调用匹配的callXXX
方法并传入装箱的方法,funcArgs
就好像它们是单独提供的一样。callXXX
可以像这样访问这些方法:
MethodHandle callInt = getNativeMethod("callInt", int.class);
MethodHandle boundCallInt = callInt.bindTo("my_c_function_name").asVarargsCollector(Object[].class);
// returns NativeHook.callInt("my_c_function_name", 1, 2, 3)
boundCallInt.invokeWithArguments(1, 2, 3);
我正在使用这个引导方法callXXX
在invokedynamic中间接引用这个方法,它的工作方式与上面相同:
public static CallSite bootstrap(MethodHandles.Lookup caller, String name, MethodType type) {
if (type.returnType() == int.class) {
MethodHandle callInt = getNativeMethod("callInt", int.class);
return new ConstantCallSite(callInt.bindTo(name).asVarargsCollector(Object[].class));
}
}
然后调用通过 invokedynamic 完成,如下所示:
mv.visitIntInsn(BIPUSH, 1);
mv.visitIntInsn(BIPUSH, 2);
mv.visitIntInsn(BIPUSH, 3);
mv.visitInvokeDynamicInsn("my_c_function_name", "(III)I", NativeHook.bootstrapHandle);
但是,这不能按预期工作并引发异常:
Caused by: java.lang.invoke.WrongMethodTypeException: MethodHandle(Object[])int should be of type (int,int,int)int
at java.lang.invoke.CallSite.wrongTargetType(CallSite.java:194)
at java.lang.invoke.CallSite.makeSite(CallSite.java:335)
... 16 more
如何构造一个正确的 MethodHandle,它像常规方法一样接受参数,然后调用 varargcallXXX
方法?