我想,我正在尝试做一些相对简单的事情。以 doSomething(int) 方法的以下 Java 字节码为例:
public java.lang.String doSomething(int i);
0 iload_1 [i]
1 invokestatic MyHelper.doSomething(int) : Java.lang.String
4 areturn
这个字节码几乎只将调用转发给静态助手。
我现在要做的是使用Javassist 将invokestatic 替换为invokedynamic。我知道如何用 ASM 做到这一点,因此只是假设我出于纯粹的好奇想知道它是如何工作的。以下是我的一些问题:
1) 以下是否正确:我不能使用 javassist CtMethod.instrument() 或 CtMethod.insertAt() 方法,因为这些方法需要一个包含有效 Java 表达式的字符串,而我不能用 Java 语法编写一个 invokedynamic?
2)invokestatic 的参数就像invokevirtual 或invokestatic 的参数一样处理,对吧?我的意思是,您将参数放在invokedynamic 之前的堆栈中,就像您为invokevirtual 所做的那样?
3) 是否有使用 Javassist 创建调用动态字节码的示例代码(或者你能想出一些)?
这是我目前所知道的:你可以创建一个字节码对象,它有一个 addInvokedynamic() 方法。但这需要 BootstrapMethodsAttribute 中的 BootstrapMethod 索引。BootstrapMethod 反过来期望常量池中的方法句柄信息的索引,该索引需要方法引用等。所以本质上你必须自己管理整个常量池条目。没关系,但我担心我做错了,以后会引入奇怪的问题。有没有更简单的方法来做到这一点(一个辅助方法左右)?我的代码大致看起来像这样(我并没有真正“重写”上面的调用静态但是:
void transform(CtMethod ctmethod, String originalCallDescriptor) {
MethodInfo mInfo = ctmethod.getMethodInfo();
ConstPool pool = ctmethod.getDeclaringClass().getClassFile().getConstPool();
/* add info about the static bootstrap method to the constant pool*/
int mRefIdx = /*somehow create a method reference entry*/
int mHandleIdx = constPool.addMethodHandleInfo(ConstPool.REF_invokeStatic, mRefIdx);
/* create bootstrap methods attribute; there can only be one per class file! */
BootstrapMethodsAttribute.BootstrapMethod[] bms = new BootstrapMethodsAttribute.BootstrapMethod[] {
new BootstrapMethodsAttribute.BootstrapMethod(mHandleIdx, new int[] {})
};
BootstrapMethodsAttribute bmsAttribute = new BootstrapMethodsAttribute(constPool, bms);
mInfo.addAttribute(bmsAttribute);
//... and then later, finally
Bytecode bc = new Bytecode(constPool);
... push parameters ...
bc.addInvokedynamic(0 /*index in bms array*/, mInfo.getName(), originalCallDescriptor);
//replace the original method body with the one containing invokedynamic
mInfo.removeCodeAttribute();
mInfo.setCodeAttribute(bc.toCodeAttribute());
}
非常感谢您的帮助和时间!