有什么方法可以打印调用 instance/Static 方法的对象引用,使用字节码检测

Is there any way to print the object ref that called an instance/Static method, Using byte-code instrumentation

我在某处读到,每当 "invokevirtual" 调用一个方法时, 对象引用从栈顶获取,后面是参数。 我需要以某种方式打印对象引用。可能吗?

所以,我不会为您做这件事,因为实际的代码既烦人又乏味,如果您真的很感兴趣,您应该自己学习如何做。但我会尽力提供帮助并为您提供一些指导。

首先,您需要阅读 ASM tutorials here

我下面要写的字节码格式来自ASMIfier,因为它更清晰。我将完全忽略 javap,因为它更加迂腐和详细,但如果您想知道它实际向您展示了什么,那么您应该阅读 Java ClassFile format.

实际上,无论如何你都应该先这样做,只是为了确保你的背景知识有所充实。

所以,这是您想要做的事情的简要说明。您将要编写一个 ClassWriter 来查找 INVOKEVIRTUAL 操作码的实例。

invokevirtual 以相反的顺序从堆栈中弹出值,因此最后一个参数在前,您最后调用的对象。您所指的 #38 也不是对象,它是对常量池的引用,其中包含方法名称和方法描述符对,用作元数据,因为 JVM 是类型安全的。

假设您有此代码:

包装样本;

public class JavaSimpleHelloWorld {
    public static void main(String[] args) {

        System.out.println("Hello World");
    }
}

如果你运行ASMIFier反对它,你会得到类似这样的主要方法(为简洁起见删减上下文)

  public static main([Ljava/lang/String;)V
   L0
    LINENUMBER 6 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    LDC "Hello World"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
   L1
    LINENUMBER 7 L1
    RETURN
   L2
    LOCALVARIABLE args [Ljava/lang/String; L0 L2 0
    MAXSTACK = 2
    MAXLOCALS = 1

因此,您实施某种静态转储方法 (public static final dump( Object o ) ),并编写一个 class 重组字节码的访问者。

您可以使用方法描述符来确定您需要在之前的堆栈推送指令(ALOAD、LDC、)中插入多深的 DUP/INVOKE 以打印您的方法对象目标。例如,System.out.println 的方法描述符是 [Ljava/lang/String;]V,这意味着该方法采用字符串数组和 returns void。所以你需要在堆栈中返回 1 来找到对象目标。反过来,您的字节码将如下所示:

快乐的字节码乱七八糟

  public static main([Ljava/lang/String;)V
   L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    DUP
    INVOKESTATIC my/staticutil/ClassThatDumps.dump (Ljava/lang/Object;)V
    LDC "Hello World"
    INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V
    RETURN
   L1
    LOCALVARIABLE args [Ljava/lang/String; L0 L1 0
    MAXSTACK = 2
    MAXLOCALS = 1