在旧的Java版本中,根据this,有一个较少受限制的invokespecial版本,称为invokenonvirtual。该指令将允许您在不进行虚拟查找的情况下调用实例方法,如果我理解正确,这些方法可能属于当前实例的不可分配的类。通过invokespecial,此更改有所更改,因为它仅用于调用超级,私有或初始化方法。
我的问题是:Java 8(或更高版本)是否可以绕过invokespecial指令的那些结构约束[4.9.2]并在不进行虚拟查找的情况下调用其他类(例如,不是作业与当前课程兼容)
我可以使用no-verify标志来禁用验证过程,但是还有更优雅的方法吗?
答案 0 :(得分:3)
您正在混淆两个不同的问题。 invokenonvirtual
和inokespecial
相同并且一直存在。它们只是同一操作码的两个不同名称。
您链接的问题是关于一个单独的功能ACC_SUPER
,它具有悠久而复杂的历史。
基本上,在Java的早期版本中,超级调用的编译被破坏了。编译超类调用时,它将在编译时将invokespecial
插入到超类方法 中。如果以后更改了类层次结构,则即使在层次结构的中间点插入了新的覆盖,它仍会尝试调用其编译后要调用的方法。
请注意,这仍然不允许您在不相关的类中调用方法-该问题仅与编译后在同一继承更改中添加的同一方法的不同替代有关。
Java作者意识到错误后,他们更新了JVM对invokespecial
的处理,以正确处理超级调用。现在,无论指令中指定了哪种方法,它都会在链接/运行时遍历超类层次结构并调用适当的方法。
但是,他们担心在旧版本的Java下编译的代码依赖于损坏的行为,因此为了向后兼容,他们添加了新的类文件标志ACC_SUPER
。如果在类上设置了标志,则它具有新的(正确的)行为,如果没有,则使用旧的行为。由于旧的类文件是在标记存在之前编译的,因此不会设置它。同时,对编译器进行了更新,将所有新类都设置为ACC_SUPER
,每个人都很高兴...
直到2011年为止。事实证明,java.lang.Thread
具有一种安全敏感的方法,用户不应调用该方法。为了防止人们从Thread
的子类中调用它,他们覆盖了它以引发异常。但是,someone realized表示,黑客可以在没有Thread
标志的情况下定义ACC_SUPER
的子类,从而跳过安全检查并调用该方法的危险版本,并脱离Java沙箱。
不幸的是,解决此安全漏洞的唯一方法是完全删除ACC_SUPER
功能-即将每个类都视为已设置了标志,而无论实际上是否设置了标志。急忙在Java 7更新13中对其进行了修复,在Java 8中,该规范本身已更改为文档ACC_SUPER
不再起作用。
因此答案是否定的,没有办法在7u13之后的任何Java版本中获得超级调用的旧行为,或者实现安全修复程序的任何更新。