从invokedynamic调用Java varargs方法

从invokedynamic调用Java varargs方法,java,jvm,bytecode,java-bytecode-asm,invokedynamic,Java,Jvm,Bytecode,Java Bytecode Asm,Invokedynamic,我想从Java动态调用本机方法。 因为方法签名在编译时是未知的,所以我为大多数具有相同签名的基本返回类型创建了泛型本机方法: 类NativeHook{ 公共静态本机int callInt(字符串funcName、对象…funcArgs); 公共静态本机void callVoid(字符串funcName、对象…funcArgs); 公共静态本机对象callObject(字符串funcName、对象…funcArgs); 私有静态方法句柄getNativeMethod(字符串callName,类re

我想从Java动态调用本机方法。 因为方法签名在编译时是未知的,所以我为大多数具有相同签名的基本返回类型创建了泛型本机方法:

类NativeHook{
公共静态本机int callInt(字符串funcName、对象…funcArgs);
公共静态本机void callVoid(字符串funcName、对象…funcArgs);
公共静态本机对象callObject(字符串funcName、对象…funcArgs);
私有静态方法句柄getNativeMethod(字符串callName,类returnType){
返回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(“我的函数名”).asVarargsCollector(Object[].class);
//返回NativeHook.callInt(“我的函数名”,1,2,3)
调用参数(1,2,3);
我使用这个引导方法间接引用invokedynamic中的
callXXX
方法,其工作方式与上面相同:

publicstaticcallsite引导(MethodHandles.Lookup调用者,字符串名,MethodType类型){
if(type.returnType()==int.class){
MethodHandle callInt=getNativeMethod(“callInt”,int.class);
返回新的ConstantCallSite(callInt.bindTo(name).asVarargsCollector(Object[].class));
}
}
然后使用InvokedDynamic完成调用,如下所示:

mv.visitIntInsn(双推,1);
visitIntInsn中压(双脉冲,2);
visitIntInsn中压(双脉冲,3);
mv.visitInvokeDynamicInsn(“我的函数名”,“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,它像常规方法一样接受参数,然后调用vararg
callXXX
方法?

在我们找到的语句中

调用站点目标的类型必须完全等于从调用的类型描述符派生并传递给引导方法的类型

因此,就
invoke
而言,兼容是不够的,但它必须与
invokeExact
兼容

应用
.asVarargsCollector(Object[].class)
后,可以
调用
句柄,但它与确切的签名不匹配。但我们可以通过以下方式进行调整:

如果当前方法是变量arity方法,则句柄参数列表转换可能涉及将多个参数转换并收集到一个数组中,如图所示

这意味着
asVarargsCollector
asType
的组合应该可以工作。但是我们也可以考虑在相同的方法文档中提到的<代码>调用< <代码> > <代码>调用KueCuxe<代码>:

这种方法提供了与普通的、不精确的行为之间的关键区别。当调用者的类型描述符与被调用者的类型描述符完全匹配时,这两个方法执行相同的步骤,但当类型不同时,plain也会调用
asType
(或某些内部等效项),以便匹配调用者和被调用者的类型

换句话说,如果
invoke
工作成功,那么
asType
转换也必须能够满足
invokeExact
的要求

我们可以证明:

MethodHandles.Lookup l=MethodHandles.Lookup();
MethodHandle h=l.bind(System.out,“printf”,
MethodType.MethodType(PrintStream.class、String.class、Object[].class));
h=h.bindTo(“%s%s%s%n”).asVarargsCollector(对象[].class);
试一试{
System.out.println(“调用(1,2,3):”;
h、 调用(1,2,3);
}捕获(可丢弃的t){
系统输出打印ln(t);
}
试一试{
System.out.println(“\nInvokeXact(1,2,3):”;
h、 invokeExact(1,2,3);
}捕获(可丢弃的t){
系统输出打印ln(t);
}
MethodType=MethodType.MethodType(void.class、int.class、int.class、int.class);
试一试{
System.out.println(“\n.asType(type).invokeExact(1,2,3):”;
h、 asType(type).invokeExact(1,2,3);
}捕获(可丢弃的t){
系统输出打印ln(t);
}
invoke(1,2,3):
1 2 3
invokeExact(1,2,3):
java.lang.invoke.ErrorMethodTypeException:应为(对象[])PrintStream,但发现(int,int,int)void
.asType(type).invokeExact(1,2,3):
1 2 3
bootstrap方法确实已经收到所需的
MethodType
作为第三个参数,因此它所需要做的就是使用该类型应用
。asType(type)

调用站点目标的类型必须完全等于从调用的类型描述符派生并传递给引导方法的类型

因此,就
invoke
而言,兼容是不够的,但它必须与
invokeExact
兼容

应用
.asVarargsCollector(Object[].class)
后,可以
调用
句柄,但它与确切的签名不匹配。但我们可以通过以下方式进行调整:

如果当前方法是变量arity方法,则句柄参数列表转换可能涉及将多个参数转换并收集到一个数组中,如图所示

这意味着
asVarargsCollector
asType
的组合应该可以工作。但是我们也可以考虑在相同的方法文档中提到的<代码>调用< <代码> > <代码>调用KueCuxe<代码>:

这种方法提供了与普通的、不精确的行为之间的关键区别。当调用方调用时,这两个方法执行相同的步骤