Java 类编写器在ASM中计算\u帧
我一直试图通过在ASM中使用跳转来理解堆栈映射框架在Java中是如何工作的。我创建了一个简单的方法来尝试一些东西:(用喀拉喀托分解): 而不是Java 类编写器在ASM中计算\u帧,java,java-bytecode-asm,bytecode-manipulation,jvm-bytecode,Java,Java Bytecode Asm,Bytecode Manipulation,Jvm Bytecode,我一直试图通过在ASM中使用跳转来理解堆栈映射框架在Java中是如何工作的。我创建了一个简单的方法来尝试一些东西:(用喀拉喀托分解): 而不是 stack Object java/io/PrintStream Object java/lang/StringBuilder Object java/lang/StringBuilder 如我所料 此外,这个类也不会通过验证,因为不能在Top上调用StringBuilder。根据ASM手册,Top指的是一个未初始化的值,但从跳转位置和之前的代
stack Object java/io/PrintStream Object java/lang/StringBuilder Object java/lang/StringBuilder
如我所料
此外,这个类也不会通过验证,因为不能在Top
上调用StringBuilder
。根据ASM手册,Top
指的是一个未初始化的值,但从跳转位置和之前的代码来看,它似乎没有在代码中未初始化。我不明白跳伞有什么问题
我插入的跳转是否有什么问题使得类无法计算帧?这可能是ASM的类编写器的错误吗 新java/lang/StringBuilder不创建有效的
StringBuilder
,而是在堆栈映射框中创建一个与TOP
一起捐赠的单元化对象。在对象构造期间添加跳转指令时使用此值,例如:
new Foo(a ? b : c);
它被翻译成几个goto语句
在对象上调用构造函数时,首先将对象视为StringBuilder
,即invokespecial方法java/lang/StringBuilder()V
。JVM不支持在不同的位置初始化此对象,因为验证器只能查看TOP
类型,该类型不能反映所需的类型,而实际类型是一个统一的StringBuilder
。您可能会认为JVM应该支持这一点,但这需要更大的数组来包含堆栈映射帧,以反映类型和初始化状态,这可能无法证明Java语言甚至没有使用这种功能
为了说明这一点,请考虑以下情况:
new Foo
dup
.stack full
locals
stack Top Top
.end stack
invokespecial Bar <init> ()V
newfoo
重复
.堆满
当地人
栈顶
.端部堆栈
调用特殊条()V
如果JVM允许对
TOP
类型进行未经检查的初始化,那么这将是有效的,但是显然不应该允许您在Foo
上调用Bar
构造函数,因为未初始化的实例是特殊的。考虑到,当<>代码> DUP 引用时,您已经有两个对堆栈中相同实例的引用,您可能执行更多的堆栈操作,或者将引用传递给本地变量,并从那里复制到其他变量或再次推送。尽管如此,在以任何方式使用该引用之前,该引用的目标应该只初始化一次。要验证这一点,必须跟踪对象的标识,以便在对同一对象执行invokespecial
时,对该对象的所有这些引用都将从未初始化变为已初始化
Java编程语言并没有使用所有的可能性,而是用于法律代码,如new-Foo(new-Foo(new-Foo(),new-Foo(b?new-Foo(a):new-Foo(b,c))
,在进行分支时,它不应该忘记哪个Foo
实例已经初始化,哪个没有初始化
因此,每个未初始化的实例堆栈帧条目都绑定到创建它的new
指令在传输或复制时。只有在对其调用了invokespecial
之后,指向同一new
指令的所有引用才会变成声明类的普通实例,并且随后可以与其他类型兼容的条目合并
这意味着像您尝试实现的分支是不可能的。相同类型但由不同的new
指令创建的两个未初始化实例项是不兼容的。不兼容的类型合并到一个Top
项中,这基本上是一个不可用的项。如果您不要试图在分支目标中使用该条目,这样ASM在将它们合并到Top
时就不会有任何错误
请注意,这也意味着不允许任何类型的循环导致堆栈帧具有由同一
new
指令创建的多个未初始化实例。您似乎有点困惑。如果未初始化对象始终由Top
类型表示,则在合并时不可用重点,你自己的例子,newfoo(a?b:c)怎么样
是否应该工作?在这种情况下,验证器将检查应用于堆栈映射帧之外,但如果新建
指令和构造函数调用之间存在跳转目标,则无法进行验证。在方法调用上也会进行此类附加验证,例如Foo-Foo=this;Foo.protectedMethod()
无法在方法包之外工作。当您有类似于new Foo(a?b:c)的代码时;
,在new
指令和构造函数调用之间有一个分支目标。在字节码级别上,您甚至可以在这两者之间有循环。我刚刚验证了您甚至可以在这两者之间有异常处理。谢谢,每个未初始化的值都绑定到创建它的新语句,这似乎是k我就是。
new Foo(a ? b : c);
new Foo
dup
.stack full
locals
stack Top Top
.end stack
invokespecial Bar <init> ()V