ASM ByteCode:在 visitMethodInsn 之前加上另一个调用
ASM ByteCode: prepending visitMethodInsn with another invocation
假设我想在调用某个感兴趣的方法之前调用一个(日志记录)方法。这意味着在侦听 visitMethodInsn 时,堆栈中已经填充了感兴趣的方法的参数。
是否可以将当前堆栈存储在某个地方,调用日志并重新填充堆栈?我是否遗漏了任何明显的堆栈突变运算符?或者我真的需要:
- 临时将堆栈存储在变量中
- 在我的(可能不适用的)日志记录调用之后缓冲所有堆栈突变调用,直到方法调用重播缓冲区?
例子:给定原码
public static void main()
{
doSomethingUnrelated();
methodOfInterest(); // line 5 for example
doSomethingUnrelated();
}
生成的代码应该是这样的:
public static void main()
{
doSomethingUnrelated();
Logger.log("methodOfInterest", "main", 5);
methodOfInterest();
doSomethingUnrelated();
}
public Logger
{
public static void log(String method, String callee, int line)
{ ... }
}
上下文:我的实际 ASM MethodVisitor 如下所示:
class UsageClassMethodVisitor extends MethodVisitor implements Opcodes
{
private final String fileName;
private final String visitedClass;
private final String visitedMethod;
private int lineNumber;
UsageClassMethodVisitor(MethodVisitor mv, String fileName, String visitedClass, String visitedMethod, boolean isStatic)
{
super(Opcodes.ASM5, mv);
this.fileName = fileName;
this.visitedClass = visitedClass;
this.visitedMethod = visitedMethod;
}
@Override
public void visitLineNumber(int i, Label label) {
lineNumber = i;
super.visitLineNumber(i, label);
}
@Override
public void visitMethodInsn(int access, String ownerClass, String method, String signature, boolean isInterface) {
if(ownerClass.contains("org/example/package/")) {
System.out.printf("prepending to visitMethodInsn(%s, %s, %s, %b) @ %s.%s:%d\n",
ownerClass, method, signature, isInterface,
visitedClass, visitedMethod, lineNumber);
super.visitLdcInsn(fileName);
super.visitLdcInsn(visitedClass);
super.visitLdcInsn(visitedMethod);
super.visitLdcInsn(lineNumber);
super.visitMethodInsn(Opcodes.INVOKESTATIC,
Hook.ACCESS_OWNER_NAME,
Hook.ACCESS_METHOD_NAME,
Hook.ACCESS_METHOD_DESC, false);
}
super.visitMethodInsn(access, ownerClass, method, signature, isInterface);
}
}
但显然这会引发错误,因为调用时堆栈的长度为 8 而不是 4:
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
org.using.package.Main.main([Ljava/lang/String;)V @13: invokestatic
Reason:
Type integer (current frame, stack[8]) is not assignable to 'java/lang/String'
Current Frame:
bci: @13
flags: { }
locals: { '[Ljava/lang/String;' }
stack: { long, long_2nd, long, long_2nd, 'java/util/concurrent/TimeUnit', 'java/lang/String', 'java/lang/String', 'java/lang/String', integer }
没有理由将堆栈存储在任何地方,因为,堆栈很好,一个堆栈。您只需推送日志参数,调用日志记录函数,弹出结果,原始堆栈仍然存在。
假设我想在调用某个感兴趣的方法之前调用一个(日志记录)方法。这意味着在侦听 visitMethodInsn 时,堆栈中已经填充了感兴趣的方法的参数。
是否可以将当前堆栈存储在某个地方,调用日志并重新填充堆栈?我是否遗漏了任何明显的堆栈突变运算符?或者我真的需要:
- 临时将堆栈存储在变量中
- 在我的(可能不适用的)日志记录调用之后缓冲所有堆栈突变调用,直到方法调用重播缓冲区?
例子:给定原码
public static void main()
{
doSomethingUnrelated();
methodOfInterest(); // line 5 for example
doSomethingUnrelated();
}
生成的代码应该是这样的:
public static void main()
{
doSomethingUnrelated();
Logger.log("methodOfInterest", "main", 5);
methodOfInterest();
doSomethingUnrelated();
}
public Logger
{
public static void log(String method, String callee, int line)
{ ... }
}
上下文:我的实际 ASM MethodVisitor 如下所示:
class UsageClassMethodVisitor extends MethodVisitor implements Opcodes
{
private final String fileName;
private final String visitedClass;
private final String visitedMethod;
private int lineNumber;
UsageClassMethodVisitor(MethodVisitor mv, String fileName, String visitedClass, String visitedMethod, boolean isStatic)
{
super(Opcodes.ASM5, mv);
this.fileName = fileName;
this.visitedClass = visitedClass;
this.visitedMethod = visitedMethod;
}
@Override
public void visitLineNumber(int i, Label label) {
lineNumber = i;
super.visitLineNumber(i, label);
}
@Override
public void visitMethodInsn(int access, String ownerClass, String method, String signature, boolean isInterface) {
if(ownerClass.contains("org/example/package/")) {
System.out.printf("prepending to visitMethodInsn(%s, %s, %s, %b) @ %s.%s:%d\n",
ownerClass, method, signature, isInterface,
visitedClass, visitedMethod, lineNumber);
super.visitLdcInsn(fileName);
super.visitLdcInsn(visitedClass);
super.visitLdcInsn(visitedMethod);
super.visitLdcInsn(lineNumber);
super.visitMethodInsn(Opcodes.INVOKESTATIC,
Hook.ACCESS_OWNER_NAME,
Hook.ACCESS_METHOD_NAME,
Hook.ACCESS_METHOD_DESC, false);
}
super.visitMethodInsn(access, ownerClass, method, signature, isInterface);
}
}
但显然这会引发错误,因为调用时堆栈的长度为 8 而不是 4:
Exception in thread "main" java.lang.VerifyError: Bad type on operand stack
Exception Details:
Location:
org.using.package.Main.main([Ljava/lang/String;)V @13: invokestatic
Reason:
Type integer (current frame, stack[8]) is not assignable to 'java/lang/String'
Current Frame:
bci: @13
flags: { }
locals: { '[Ljava/lang/String;' }
stack: { long, long_2nd, long, long_2nd, 'java/util/concurrent/TimeUnit', 'java/lang/String', 'java/lang/String', 'java/lang/String', integer }
没有理由将堆栈存储在任何地方,因为,堆栈很好,一个堆栈。您只需推送日志参数,调用日志记录函数,弹出结果,原始堆栈仍然存在。