【发布时间】:2017-12-06 13:02:38
【问题描述】:
所以我有一些已插入“虚拟方法调用”的类;即具有空主体的专用类中的静态方法。
这个想法是把在方法调用之前被压入堆栈的参数,将它们存储在局部变量中,然后用实际的实现替换方法调用。
要查看本地人的处理方式,我运行
A.java
package asmvisit;
public class A {
long y;
public long doSomething(int x, A a){
if(a == null){
this.y = (long)x;
return -1L;
}
else{
long old = y;
this.y += (long)x;
return old;
}
}
}
通过文本编辑器(帖子底部的代码)。
正如您在输出中看到的(也在帖子底部),局部变量
LOCALVARIABLE old J L4 L6 3
LOCALVARIABLE this Lasmvisit/A; L0 L6 0
LOCALVARIABLE x I L0 L6 1
LOCALVARIABLE a Lasmvisit/A; L0 L6 2
在方法的最后被访问。
从技术上讲,我们可以更早地访问它们,但我明白为什么在任意位置插入本地人可能会搞砸编号 - 以及随之而来的程序。
所以在我看来,添加更多局部变量的唯一安全方法是通过每个方法运行两次:
- 曾经除了计算局部变量访问次数之外什么都不做
- 一旦实际修改代码,跟踪本地“生成”,但将实际生成(即访问本地)延迟到
visitMaxs之前,使用计数器跟踪新本地将结束的索引有。
是否有不需要两次通过的更简单的替代方案?
文本编辑器
package asmvisit;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.util.Printer;
import org.objectweb.asm.util.Textifier;
import org.objectweb.asm.util.TraceMethodVisitor;
import java.io.PrintWriter;
import java.util.Arrays;
public class MyClassVisitor extends ClassVisitor {
public MyClassVisitor(ClassVisitor cv) {
super(Opcodes.ASM5, cv);
}
@Override
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) {
System.out.println(String.format("\nvisitMethod: %d, %s, %s, %s, %s", access,name,desc,signature, Arrays.toString(exceptions)));
Printer p = new Textifier(api) {
@Override
public void visitMethodEnd() {
PrintWriter pw = new PrintWriter(System.out);
print(pw); // print it after it has been visited
pw.flush();
}
};
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
if(mv != null){
return new TraceMethodVisitor(mv,p);
}
return mv;
}
}
输出
visitMethod: 1, <init>, ()V, null, null
L0
LINENUMBER 3 L0
ALOAD 0
INVOKESPECIAL java/lang/Object.<init> ()V
RETURN
L1
LOCALVARIABLE this Lasmvisit/A; L0 L1 0
MAXSTACK = 1
MAXLOCALS = 1
visitMethod: 1, doSomething, (ILasmvisit/A;)J, null, null
L0
LINENUMBER 7 L0
ALOAD 2
IFNONNULL L1
L2
LINENUMBER 8 L2
ALOAD 0
ILOAD 1
I2L
PUTFIELD asmvisit/A.y : J
L3
LINENUMBER 9 L3
LDC -1
LRETURN
L1
LINENUMBER 12 L1
FRAME SAME
ALOAD 0
GETFIELD asmvisit/A.y : J
LSTORE 3
L4
LINENUMBER 13 L4
ALOAD 0
DUP
GETFIELD asmvisit/A.y : J
ILOAD 1
I2L
LADD
PUTFIELD asmvisit/A.y : J
L5
LINENUMBER 14 L5
LLOAD 3
LRETURN
L6
LOCALVARIABLE old J L4 L6 3
LOCALVARIABLE this Lasmvisit/A; L0 L6 0
LOCALVARIABLE x I L0 L6 1
LOCALVARIABLE a Lasmvisit/A; L0 L6 2
MAXSTACK = 5
MAXLOCALS = 5
【问题讨论】:
-
如果你想消除对空体方法的调用,那么为什么不首先检测所有这些方法然后解析它们的签名以计算参数的数量,然后替换相应的
push-es 和invokestatic-s 与nop-s 的适当计数?在这种情况下,nops 允许您保持方法主体中的所有偏移量不变。 -
@user882813:我想,你误会了。被调用的方法是空的,因为它只是一个占位符(它可以被声明为
native)。在调用站点,将注入一个实际的重要实现。
标签: java bytecode instrumentation java-bytecode-asm