使用Javassist生成InvokedDynamic

使用Javassist生成InvokedDynamic,java,code-generation,javassist,invokedynamic,Java,Code Generation,Javassist,Invokedynamic,我想我正在尝试做一些相对简单的事情。以doSomething(int)方法的以下Java字节码为例: 这个字节码几乎只将调用转发给静态助手 我现在要做的是使用Javassist将invokestatic替换为invokedynamic。我知道如何使用ASM实现这一点,因此,出于纯粹的好奇心,我只想知道它是如何工作的。以下是我的一些问题: 1) 以下内容是否正确:我无法使用javassist CtMethod.instrument()或CtMethod.insertAt()方法,因为这些方法需要包

我想我正在尝试做一些相对简单的事情。以doSomething(int)方法的以下Java字节码为例:

这个字节码几乎只将调用转发给静态助手

我现在要做的是使用Javassist将invokestatic替换为invokedynamic。我知道如何使用ASM实现这一点,因此,出于纯粹的好奇心,我只想知道它是如何工作的。以下是我的一些问题:

1) 以下内容是否正确:我无法使用javassist CtMethod.instrument()或CtMethod.insertAt()方法,因为这些方法需要包含有效Java表达式的字符串,而我无法用Java语法编写InvokedDynamic

2) invokestatic的参数处理方式与invokevirtual或invokestatic的参数相同,对吗?我的意思是,在invokedynamic之前将参数放入堆栈,就像在invokevirtual中一样

3) 是否有使用Javassist创建InvokedDynamic字节码的示例代码(或者您能想出一些示例代码)

这就是我到目前为止所知道的:您可以创建一个字节码对象,它有一个addInvokedynamic()方法。但这需要bootstrapmethods属性中的BootstrapMethod索引。BootstrapMethod反过来要求常量池中的方法句柄信息的索引,该索引需要方法引用,等等。因此,基本上您必须自己管理整个常量池条目。这没关系,但我担心我做得不对,以后会引入一些奇怪的问题。有没有更简单的方法(助手方法或其他方法)?我的代码大致如下(我并没有真正“重写”上面的invokestatic,但是:

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());

}

非常感谢您的帮助和时间!

我必须说,这是您在这里提出的一个非常有趣的问题。如果我的回答有点长,我很抱歉,但我已经尽力给您提供了尽可能多的(我认为是)有用的信息,以便帮助您和其他人解决这个问题

问题1 以下内容是否正确:我无法使用javassist CtMethod.instrument()或CtMethod.insertAt()方法,因为这些方法需要包含有效Java表达式的字符串,而我无法用Java语法编写InvokedDynamic

你说得对

CtMethod.insertAt()只能用于Java代码,不能用于字节码操作码。 另一方面,CtMethod.instrument()允许您处理字节码,甚至使用and以非常有限的方式对其进行修改。但正如我所说的,它们允许您更改的内容非常有限,并且对于您试图实现的内容,两个修饰符都无法帮助您

问题2 invokestatic的参数就像invokevirtual或invokestatic的参数一样进行处理,对吗?我的意思是,在invokedynamic之前将参数放入堆栈,就像对invokevirtual那样

我不知道我是否完全理解你在这里的真正要求(你在第一句话中重复了invokestatic)。我想你要问的是,如果InvokedDynamic中的参数处理方式与invokevirtual和invokestatic中的参数处理方式相同,那么请纠正我的错误。这使你能够简单地将invokevirtual和invokestatic切换为InvokedDynamic。我假设在回答此问题时

首先要注意的是,在处理堆栈时,invokevirtual和invokestatic本身是不同的。invokevirtual除了将所需的参数推入堆栈之外,与invokestatic一样,它还推动对象引用,以便可以链接方法调用


旁注

您可能已经知道这一点,但我正在添加这些附加信息,以防其他人遇到这个问题,并且想知道为什么invokestatic和invokevirtual处理堆栈的方式不同

  • invokestatic操作码用于调用类中的静态方法,这意味着在编译时JVM确切地知道如何进行方法调用链接

  • 另一方面,invokedynamic操作码用于对象实例的方法调用。由于编译时无法知道方法调用链接到何处,因此只有当JVM知道正确的对象引用时,才能在运行时链接该操作码


当对操作码的工作原理有疑问时,我的建议是查看中关于的章节(链接是针对JVM 7的,编写本文时使用的是当前版本)

我这样做只是为了检查我们在这里讨论的3个操作码

操作码和都具有相同的操作数堆栈定义:

..., [arg1, [arg2 ...]] →

...
正如我之前所说,它有一个不同的操作数堆栈定义,即:

..., objectref, [arg1, [arg2 ...]] →

...
我在这里的第一个假设(我必须警告您,我还没有深入研究InvokedDynamic操作代码)是,您不能像使用invokestatic那样简单地更改InvokedDynamic的invokevirtual。我这样说是因为InvokedDynamic不希望堆栈中有任何对象引用

为了更好地理解这种情况,我的建议是使用该包用Java编写一个示例,它将允许您创建使用InvokedDynamic操作码的Java字节码。编译类后,使用命令
javap-l-c-v-p
检查生成的字节码

问题3 是否有使用Javassist创建InvokedDynamic字节码的示例代码(或者您能想出一些示例代码)

我不知道。我也用谷歌搜索了一下(你可能也这么做了),但我什么也没找到。我想你的帖子将给出javassist的第一个代码示例:)

还有一些注释 因此,基本上您必须自己管理整个常量池条目。这没关系,但我担心我做得不对,以后会引入一些奇怪的问题

只要您使用该类来管理您的c
..., objectref, [arg1, [arg2 ...]] →

...