gpt4 book ai didi

java - 使用Javassist生成Invokedynamic

转载 作者:塔克拉玛干 更新时间:2023-11-02 08:18:54 26 4
gpt4 key购买 nike

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

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创建invokedynamic字节码的示例代码(或者您可以提出一些示例代码)?

到目前为止,这是我所知道的:您可以创建一个具有方法addInvokedynamic()的Bytecode对象。但这需要BootstrapMethodsAttribute中的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语法编写invokedynamic?

你是对的。

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

问题2

对invokestatic的参数的处理方式与invokevirtual或invokestatic的参数一样,对吗?我的意思是,您将参数放在invokedynamic之前的堆栈上,就像您要为invokevirtual进行处理一样吗?

我不知道我是否完全理解您在这里真正要问的内容(您在第一句话中反复调用了static)。我认为您要问的是-如果我错了,请纠正我-是,invokedynamic中的参数是否以与invokevirtual和invokestatic中相同的方式处理。使您能够简单地将invokevirtual和invokestatic切换为invokedynamic。我认为是在回答这个问题时...

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

SideNote

您可能已经知道这一点,但是我添加了这些附加信息,以防其他人陷入此问题,并想知道为什么invokestatic和invokevirtual处理堆栈的方式不同。

  • invokestatic操作码用于调用类中的静态方法,这意味着在编译时,JVM确切地知道如何进行方法调用链接。
  • 另一方面,当对对象实例进行方法调用时,将使用invokedynamic操作码。由于编译时无法知道将方法调用链接到何处,因此只有在JVM知道正确的对象引用时才能在运行时将其链接。


  • 当对操作码的工作方式有疑问时,我的建议是检查 JVM instruction set中有关 JVM specification的章节(链接是针对JVM 7的,当前版本是JVM 7)。

    我刚刚检查了我们在这里讨论的3个操作码。

    两种操作码 invokestaticinvokedynamic具有相同的操作数堆栈定义:
    ..., [arg1, [arg2 ...]] →

    ...

    如前所述, invokevirtual具有不同的操作数堆栈定义,即:
    ..., objectref, [arg1, [arg2 ...]] →

    ...

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

    为了更好地理解这种情况,我的建议是使用 java.lang.invoke包以Java编写示例,这将允许您创建使用invokedynamic操作码的Java字节码。并在编译完类之后,使用命令 javap -l -c -v -p检查生成的字节码。

    问题3

    是否有使用Javassist创建invokedynamic字节码的示例代码(或者您可以提出一些示例代码)?

    不是我知道的。我也用谷歌搜索了一下(您可能也已经搜索过),但是我什么也没找到。我认为您发布的内容将给出javassist的第一个代码示例:)

    更多注意事项

    因此,基本上,您必须自己管理整个常量池条目。可以,但是我担心我做得不好,以后会介绍一些奇怪的问题

    只要您使用 ConstPool类管理常量池,javassist就会为您处理所有事情而不会产生问题。

    此外,如果创建损坏的内容池,最经常(最有可能总是发生)的情况是,一旦尝试加载类或调用修改的方法,就会出现ClassFormatException错误。我要说的是,这种情况要么有效,要么无效。

    我无法想到这样的情况:当您不怎么期望时,可能会隐藏某种奇怪的错误,等待那令人讨厌的时刻困扰着您(注意,我说我不会想到,这并不意味着他们不会存在)。我什至会冒风险地说,只要您可以加载该类并调用它的方法而不会导致JVM崩溃,这是相当安全的。

    有没有更简单的方法可以做到这一点(一种辅助方法)?

    我不这么认为。 Javassist在字节码修改方面为您提供了很多帮助,但是当您使用更高级别的API时(例如,编写Java代码并注入该代码或移动/复制CtMethods,Ctclasses等)。当您使用必须处理所有字节码的低级API时,您几乎是一个人。

    我知道您所寻找的可能不是一个答案,但我希望我对这个问题有所了解。

    关于java - 使用Javassist生成Invokedynamic,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15409377/

    26 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com