我读到某个地方,当“invokevirtual”调用一个方法时, 从堆栈顶部获取对象引用,后跟参数。 我需要以某种方式打印对象引用。有可能吗?
答案 0 :(得分:2)
所以,我不打算为你做这件事,因为实际的代码很烦人而且乏味,如果你真的真的感兴趣,你应该自己学习如何做。但我会尽力提供帮助并为您提供一些指导。
首先,您要阅读ASM tutorials here。
我将在下面写的字节代码格式来自ASMIfier,因为它更加清晰。我会完全忽略javap,因为它更加迂腐和详细,但如果你想知道它实际上在向你显示什么,那么你应该阅读Java ClassFile format。
实际上,无论如何,你应该首先做到这一点,只是为了确保你的背景知识有所填写。
所以,这就是你想要做的事情的简要介绍。您将要编写一个查找INVOKEVIRTUAL操作码实例的ClassWriter。
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)),并编写了一个重组字节代码的类访问者。
您可以使用方法描述符来确定插入DUP / INVOKE以打印方法对象目标所需的前导堆栈推送指令(ALOAD,LDC)的深度。例如,System.out.println的方法描述符是[Ljava / lang / String;] V这意味着该方法接受一个字符串数组并返回void。因此,您需要在堆栈中返回1以找到对象目标。反过来,你的字节码看起来像这样:
快乐的字节码twiddling。
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