【问题标题】:How ASM checks maximum stack size when duplicating operand stack (DUP_X1 and DUP operands)复制操作数堆栈(DUP_X1 和 DUP 操作数)时,ASM 如何检查最大堆栈大小
【发布时间】:2014-06-05 06:26:41
【问题描述】:

我正在尝试通过字节码操作(使用ASM framework)记录实例级别(或类级别)对象初始化或对象分配

类似这样的:

这是我声明一些实例变量的类:

class DeclarationTestClass1{

    /** Assume This Class has some public instance variables **/
    private MultipleDeclarationTestClass multiClass = null;
    private List<String> arrayList1_String = null;
    public static Hashtable<Integer,Integer> staticHashTable = null;


    public List<String> getArrayList1_String() {
        return arrayList1_String;
    }

    public void setArrayList1_String(List<String> arrayList1_String) {
        this.arrayList1_String = arrayList1_String;
    }
}

现在,我想做:

DeclarationTestClass1 object1 = new DeclarationTestClass1();
MultipleDeclarationTestClass object1 .object1_innerObject = new MultipleDeclarationTestClass();

RecordingClass.recordingMethod(object1.arrayList1_String = new ArrayList<String>());
RecordingClass.recordingMethod(object1 .object1_innerObject.someMap = new HashMap<String,String>());

现在,当我使用 ASMifier 看到所需类的输出时,我可以看到在调用实际的 putfield 之前,有一个 DUP_X1 被调用。

现在,我的理解是,由于实例级对象是通过另一个对象引用的(即:object1.arrayList1_String = new ArrayList&lt;String&gt;(),堆栈的第二个字包含它所引用的对象(即object1)。 如果我错了,请纠正我。

但是,我无法理解的是,在第二种情况下,当实例级对象通过另一个额外的对象(即:object1 .object1_innerObject.someMap = new HashMap&lt;String,String&gt;())被引用时,它在堆栈中还有另一个额外的对象,对吧?

所以我的问题是:

1) 为什么在第二种情况下也会调用DUP_X1

2) 我在 asm 中使用 COMPUTE_MAXS 标志,其中 asm 为我计算最大堆栈大小。那么,如果我不覆盖MethodVisitor 中的visitMaxs 方法,这种复制是否会产生影响。

非常感谢您在这方面的任何帮助。

【问题讨论】:

    标签: java bytecode java-bytecode-asm bytecode-manipulation


    【解决方案1】:

    作为Antimony has pointed out,无论您是否拥有x.y = foox.y.z.a.b.c = foo 形式的代码,堆栈大小都无关紧要。

    对于链的每个节点,getfield 指令将从堆栈中弹出字段所有者实例并将字段值压入堆栈(将成为下一个节点的所有者)。

    所以唯一有趣的部分是putfield。它将从堆栈中弹出 两个 值、字段所有者和新值。由于您使用的是 method(x.y=foo) 形式的表达式,因此您需要再次将该值传递给方法。

    所以需要一种dup,但是由于后续putfield会消耗两个item,所以必须将值的副本下推到字段所有者的下方:

    dup_x1:       …, owner, value]        → …, value, owner, value]
    putfield:     …, value, owner, value] → …, value]
    invokestatic: …, value]               → …]
    

    【讨论】:

    • 非常感谢。但是,您能否澄清问题的asm 部分?
    • 不清楚你是如何使用MethodWriter的。必须调用visitMax 方法来触发maxStackmaxLocals 的(重新)计算,但是如果您使用GeneratorAdapter 来修改代码,则不需要覆盖visitMaxs 作为GeneratorAdapter 会为你做正确的事。
    • 不,我没有使用GeneratorAdatper。但是,我正在使用COMPUTE_MAXS 标志,根据asm 文档是:Flag to automatically compute the maximum stack size and the maximum number of local variables of methods. If this flag is set, then the arguments of the visitMaxs method of the MethodVisitor returned by the visitMethod method will be ignored, and computed automatically from the signature and the bytecode of each method.
    • 那你的问题是什么?
    • 我只是想再次确认我是否正确。没关系,从我的角度来看,这真的很愚蠢。再次感谢您的解释。 (只是一个请求:如果您认为我的问题有效,请您投票)
    【解决方案2】:

    实际上,在 2 次间接超过 1 的情况下,堆栈上不会有任何额外内容,因为分配只需要最后一个对象。想一想。

    object1.object1_innerObject.someMap = new HashMap<String,String>())
    

    在这段代码中,您评估object1.object1_innerObject(称为t1),然后评估new HashMap&lt;String,String&gt;()(称为t2),最后执行分配,t1.someMap = t2

    等效的字节码是(假设 object1 在局部变量 slot 1 中)

    aload_1
    getfield object1type object1_innnerObject innerobjecttype
    new HashMap
    dup
    invokespecial java/util/HashMap <init> ()V
    setfield innerobjecttype someMap java/util/HashMap
    

    堆栈上最多有 3 个项目,但这与您只执行 object1.someMap 相同。

    【讨论】:

    • 非常感谢您提供清晰简洁的答案。现在,我只剩下一个查询了。如果我没有定义最大堆栈大小(通过覆盖visitMaxs 并在ClassWriter 中使用COMPUTE_MAXS 标志),堆栈大小的这种更改会产生任何问题吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-05
    • 2016-11-23
    • 2019-12-29
    • 1970-01-01
    • 2018-01-29
    • 2011-09-16
    • 2011-11-24
    相关资源
    最近更新 更多