如何检查字节码操作PUTFIELD是否使用ObjectWeb ASM重新分配属于'this'对象的字段?

时间:2010-08-28 15:58:40

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

我正在使用ASM字节码操作框架对Java代码执行静态分析。我希望检测何时重新分配对象的字段,即何时出现这种代码:

class MyObject {
    private int value;
    void setValue(int newValue) { this.value = newValue; }
}

使用以下代码(在实现ClassVisitor的类中)可以检测到上述情况:

@Override
public void visitFieldInsn(int opcode, String owner, String name, String desc) {
    if(opcode == Opcodes.PUTFIELD) {
        // do whatever here
    }
}

但是,无论拥有该字段的对象如何,都会调用此代码。我想找到在this对象上执行PUTFIELD操作的更具体的情况。例如,我想区分第一个代码片段和这样的代码:

public MyObject createNewObjectWithDifferentField() {
    MyObject newObject = new MyObject();
    newObject.value = 43;
    return newObject;
}

在上面的例子中,PUTFIELD操作仍然执行,但是它在一个局部变量(newObject)而不是this对象上。这将取决于赋值时堆栈的状态,但我遇到了几个不同的场景,其中字节码完全不同,我正在寻找处理这种复杂性的方法。

如何检查PUTFIELD是否重新分配属于this对象的字段?


修改

我正在使用ASM来执行分析,而不是检测现有的字节码。我希望在不改变字节码的情况下找到一种发现方法,如果可能的话。

3 个答案:

答案 0 :(得分:2)

我认为一般情况下这是不可能的。考虑:

class MyObject {
  private int value;
  void mymethod1() {
    mymethod2(Math.random() > 0.5 ? this : new MyObject());
  }

  void mymethod2(MyObject that) {
    that.value = 1;
  }
}

在更简单的情况下,您可以将堆栈追溯回ALOAD 0,在实例方法中引用this

答案 1 :(得分:0)

我从未使用过ASM,但是,我有使用字节码操作的经验。

在PUTFIELD指令之前,堆栈如下所示:

|...,object_ref,value

|...,object_ref,value1,value2(如果字段的类型是double或long)

在第一种情况下,您可以在PUTFIELD之前插入以下说明:

1: DUP2
2: POP
3: ALOAD_0
4: IF_ACMPNE X
5: put your code here
...
...
X: PUTFIELD

指令(1)复制object_ref和堆栈上的值。 (2)删除该值。 (3)加载'this'引用。 (4)如果'this'等于object_ref执行你的代码,否则什么也不做,跳转到PUTFIELD。

对于第二种情况(长字段或双字段),您可以使用这一系列的字节码指令

1: DUP2_X1
2: POP2
3: DUP
4: ALOAD_0
5: IF_ACMPNE 7
6: put your code here
...
...
7: DUP_X2
8: POP
9: PUTFIELD

答案 2 :(得分:0)

另一种方法(运行时):

您可以使用AspectJ并为您的类设置字段集/获取切入点。请参阅:http://www.eclipse.org/aspectj/doc/released/progguide/semantics-pointcuts.htmlhttp://www.eclipse.org/aspectj/

在定义切入点之后,您会写一些建议,只需使用thisJoinPoint变量打印出当前执行位置。然后,当你运行你的程序时,你会得到一个很好的日志来记录每个字段的获取/设置。

这将需要运行时或编译时编织,这意味着字节码操作。希望这会有所帮助...