用Java字节码交换双值和长值?

用Java字节码交换双值和长值?,java,jvm,java-bytecode-asm,Java,Jvm,Java Bytecode Asm,我正在用ASM开发一个Java插装引擎,在这种情况下,求值堆栈的顶部有一个特定的值,我想在现有值之前插入一个ref(这是getstatic的结果)。例如,考虑只有一个值的堆栈:value->,然后在getstatic之后,我希望堆栈变成这样:ref,value-> 要执行此行为,我必须插入以下字节码:getsatic和swap。对于ASM,我会做一些类似的事情(考虑MethodVisitor类型的mv): 问题是交换字节码不支持long和double值。因此,上面的代码对于单单词类型来说是可以的

我正在用ASM开发一个Java插装引擎,在这种情况下,求值堆栈的顶部有一个特定的值,我想在现有值之前插入一个ref(这是getstatic的结果)。例如,考虑只有一个值的堆栈:
value->
,然后在getstatic之后,我希望堆栈变成这样:
ref,value->

要执行此行为,我必须插入以下字节码:getsatic和swap。对于ASM,我会做一些类似的事情(考虑
MethodVisitor
类型的
mv
):

问题是交换字节码不支持
long
double
值。因此,上面的代码对于单单词类型来说是可以的,但是对于
long
double
类型来说,它不能正常工作


对于
long
double
类型,是否有任何简单的解决方案来解决此问题,并且不需要辅助本地值?

我将确保堆栈按其需要的顺序加载,而不需要交换。 在优化代码方面,我建议您尝试生成尽可能接近
javac
生成的代码,因为这是JVM优化的内容


如果不可能,您可以将值存储在局部变量中,按下
ref
并重新加载值。

最简单的解决方案是使用局部变量交换堆栈上的项。 比如:

DSTORE tmp
GETSTATIC ...
DLOAD tmp

使用序列dup2x2,pop2


从堆栈上的XXYY开始,dup2x2x2给你YYXXYY,pop2给你YYXX。

我们有一个双字值VV和ref值R。从VV。。。到VVR。。。您可以将R值添加到堆栈顶部(RVV…),然后使用dup_x2(RVVR…),然后使用pop(VVR…)。

这里有一个ASM实用程序函数,用于注入字节码以交换堆栈顶部任何类型的操作数:

public static void swap(MethodVisitor mv, Type stackTop, Type belowTop) {
    if (stackTop.getNumberSlots() == 1) {
        if (belowTop.getNumberSlots() == 1) {
            // Top = 1, below = 1
            mv.visitInsn(Opcodes.SWAP);
        } else {
            // Top = 1, below = 2
            mv.visitInsn(Opcodes.DUP_X2);
            mv.visitInsn(Opcodes.POP);
        }
    } else {
        if (belowTop.getNumberSlots() == 1) {
            // Top = 2, below = 1
            mv.visitInsn(Opcodes.DUP2_X1);
        } else {
            // Top = 2, below = 2
            mv.visitInsn(Opcodes.DUP2_X2);
        }
        mv.visitInsn(Opcodes.POP2);
    }
}
打电话。它是这样实现的:

public void swap(final Type prev, final Type type) {
    if (type.getSize() == 1) {
        if (prev.getSize() == 1) {
            swap(); // same as dupX1(), pop();
        } else {
            dupX2();
            pop();
        }
    } else {
        if (prev.getSize() == 1) {
            dup2X1();
            pop2();
        } else {
            dup2X2();
            pop2();
        }
    }
}

是的,这是一个选项,但我想要一个不需要辅助本地值的简单解决方案。按顺序将元素添加到堆栈中,以便它们不需要交换,即,交换代码中的顺序。这是不可能的。我正在拦截putfield字节码,此时我知道堆栈顶部有一个值。但要确定前一条指令在堆栈上为putfield留下了值并不容易。该值可能是表达式、方法调用或其他结果。因此,我无法更改指令的顺序,因为我不知道要更改哪些指令。实际上,这不应该是一个通用的答案,您必须提供详细信息。@EugeneKuleshov我不明白您的意思。javac就是这样做的,我不知道您认为应该提供什么细节。我想要一个不需要辅助本地值的简单解决方案。@FernandoMiguelCarvalho,恐怕这是不可能的。看看字节码命令:我们有用于从堆栈加载到堆栈的命令。我们有用于不同DUP和swap的命令,但在这里不适用。如果可以的话,JIT可能会把它优化掉。那是行不通的。long/double是一个双字值,ref是一个单字值。很抱歉,我误读了这个问题。在那种情况下,你对我的答案的精炼是正确的。
public void swap(final Type prev, final Type type) {
    if (type.getSize() == 1) {
        if (prev.getSize() == 1) {
            swap(); // same as dupX1(), pop();
        } else {
            dupX2();
            pop();
        }
    } else {
        if (prev.getSize() == 1) {
            dup2X1();
            pop2();
        } else {
            dup2X2();
            pop2();
        }
    }
}