我正在使用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来执行分析,而不是检测现有的字节码。我希望在不改变字节码的情况下找到一种发现方法,如果可能的话。
答案 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.html和http://www.eclipse.org/aspectj/。
在定义切入点之后,您会写一些建议,只需使用thisJoinPoint变量打印出当前执行位置。然后,当你运行你的程序时,你会得到一个很好的日志来记录每个字段的获取/设置。
这将需要运行时或编译时编织,这意味着字节码操作。希望这会有所帮助...