从invokedynamic调用Java varargs方法
我想从Java动态调用本机方法。 因为方法签名在编译时是未知的,所以我为大多数具有相同签名的基本返回类型创建了泛型本机方法:从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
类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,它像常规方法一样接受参数,然后调用varargcallXXX
方法?在我们找到的语句中
调用站点目标的类型必须完全等于从调用的类型描述符派生并传递给引导方法的类型
因此,就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<代码>:
这种方法提供了与普通的、不精确的行为之间的关键区别。当调用方调用时,这两个方法执行相同的步骤