我一直在尝试通过玩 ASM 中的跳转来了解堆栈映射框架在 Java 中是如何工作的。我创建了一个简单的方法来尝试一些事情:(用 Krakatau 反汇编):
L0: ldc 'hello'
L2: astore_1
L3: getstatic Field java/lang/System out Ljava/io/PrintStream;
L6: new java/lang/StringBuilder
L9: dup
L10: invokespecial Method java/lang/StringBuilder <init> ()V
L13: ldc 'concat1'
L15: invokevirtual Method java/lang/StringBuilder append (Ljava/lang/String;)Ljava/lang/StringBuilder;
L18: aload_1
L19: invokevirtual Method java/lang/StringBuilder append (Ljava/lang/String;)Ljava/lang/StringBuilder;
L22: invokevirtual Method java/lang/StringBuilder toString ()Ljava/lang/String;
L25: invokevirtual Method java/io/PrintStream println (Ljava/lang/String;)V
L28: getstatic Field java/lang/System out Ljava/io/PrintStream;
L31: new java/lang/StringBuilder
L34: dup
L35: invokespecial Method java/lang/StringBuilder <init> ()V
L38: ldc 'concat2'
L40: invokevirtual Method java/lang/StringBuilder append (Ljava/lang/String;)Ljava/lang/StringBuilder;
L43: aload_1
L44: invokevirtual Method java/lang/StringBuilder append (Ljava/lang/String;)Ljava/lang/StringBuilder;
L47: invokevirtual Method java/lang/StringBuilder toString ()Ljava/lang/String;
L50: invokevirtual Method java/io/PrintStream println (Ljava/lang/String;)V
L53: return
它所做的只是创建一个StringBuilder
将一些字符串与变量连接起来。
由于 L35 的 invokespecial 调用与 L10 的 invokespecial 调用具有完全相同的堆栈,因此我决定ICONST_1; IFEQ L10
使用 ASM 在 L35 之前添加一个序列。
当我拆卸时(再次使用 Krakatau),我发现结果很奇怪。ASM 计算出 L10 的堆栈帧为:
.stack full
locals Object [Ljava/lang/String; Object java/lang/String
stack Object java/io/PrintStream Top Top
.end stack
代替
stack Object java/io/PrintStream Object java/lang/StringBuilder Object java/lang/StringBuilder
正如我所料。
此外,这个类也不会通过验证,因为无法调用StringBuilder#<init>
。Top
根据 ASM 手册,Top
指的是一个未初始化的值,但从跳转位置和之前的代码来看,它似乎并没有在代码中未初始化。我不明白跳跃有什么问题。
我插入的跳转是否有问题,以某种方式使该类无法计算帧?这可能是 ASM 的 ClassWriter 的错误吗?