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

发布于 2024-09-16 12:21:40 字数 1085 浏览 9 评论 0原文

我正在使用 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 仅执行分析,而不是检测现有字节码。如果可能的话,我希望找到一种在不改变字节码的情况下发现这一点的方法。

I am using the ASM bytecode manipulation framework to perform static analysis on Java code. I wish to detect when fields of an object are reassigned, i.e. when this kind of code occurs:

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

Using the following code (in a class implementing ClassVisitor) can detect the above situation:

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

However, this code is called regardless of the object which owns the field. I would like to find the more specific case where the PUTFIELD operation is executed on the this object. For example, I want to distinguish between the first code snippet, and code such as this:

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

In the above case, the PUTFIELD operation is still executed, but here it's on a local variable (newObject) rather than the this object. This will depend on the state of the stack at the time of the assignment, but I have came across a few different scenarios where the bytecode is totally different, and I'm looking for ways to handle this complexity.

How do I check that PUTFIELD is reassigning a field belonging to this object?


Edit

I'm using ASM to perform analysis only, rather than instrumenting existing bytecode. Preferably I'd like to find a way of discovering this without altering the bytecode, if possible.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

怕倦 2024-09-23 12:21:40

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

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

I think that in general case it's impossible. Consider:

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

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

In simpler cases you can track the stack back to ALOAD 0, which in an instance method refers to this.

允世 2024-09-23 12:21:40

我从未使用过 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

I've never used ASM, however, I have experience with bytecode manipulation.

Right before the PUTFIELD instruction, the stack looks like this:

|...,object_ref,value

or

|...,object_ref,value1,value2 (if the type of the field is double or long)

Taking the first case, you can insert the following instructions before the PUTFIELD:

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

Instruction (1) duplicates the object_ref and the value on the stack. (2) removes the value. (3) loads the 'this' reference. (4) If 'this' is equal to the object_ref execute your code, else do nothing and jump to the PUTFIELD.

For the second case (long or double field) you can use this series of bytecode instructions

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
骄兵必败 2024-09-23 12:21:40

另一种方法(运行时):

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

定义切入点后,您将编写一些建议,通过使用 thisJoinPoint 变量简单地打印出当前执行位置。然后,当运行您的程序时,您将获得一个有关字段获取/设置的详细日志。

这将需要运行时或编译时编织,这意味着无论哪种方式都需要进行字节码操作。希望这有帮助...

An alternative approach (runtime):

You could use AspectJ and set up field set/get pointcuts for your Class. See: http://www.eclipse.org/aspectj/doc/released/progguide/semantics-pointcuts.html and http://www.eclipse.org/aspectj/.

After defining your pointcuts, you'd write some advice that simply prints out the current location of execution by using the thisJoinPoint variable. Then, when running your program, you'd have a nice log of everywhere the fields were get/set.

This would require either runtime or compile time weaving which means bytecode manipulations either way. Hope this helps...

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文