使用旧语义调用特殊:调用其他 class 的实例方法

Invokespecial with old semantics: call instance method of other class

根据 this 在旧 java 版本中有一个限制较少的 invokespecial 版本称为 invokenonvirtual。这条指令可以让你在没有虚拟查找的情况下调用实例方法,如果我没看错,这些方法可能属于当前方法的不可分配 classes。使用 invokespecial 时,这发生了变化,因为它仅用于调用超级、私有或初始化方法。

我的问题是:java 8(或更高版本)有没有办法绕过 invokespecial 指令的那些结构约束 [4.9.2] 并在没有虚拟查找的情况下调用不同 class(即与当前 class 分配不兼容的 class)

我可以使用 no-verify 标志来禁用验证过程,但是有更优雅的方法吗?

您将两个不同的问题混为一谈。 invokenonvirtualinokespecial 是一样的,而且一直都是。它们只是相同操作码的两个不同名称。

您链接的问题是在谈论一个单独的功能 ACC_SUPER,它有自己悠久而复杂的历史。

基本上,在 Java 的早期版本中,超级调用的编译被破坏了。编译 superclass 调用时,它会在编译时 向 superclass 方法 插入一个 invokespecial。如果稍后更改了 class 层次结构,它仍会尝试调用它被编译为调用的方法,即使在层次结构的中间点插入了新的覆盖也是如此。

请注意,这仍然不允许您在不相关的 classes 中调用方法 - 该问题仅与编译后在相同继承更改中添加的相同方法的不同覆盖有关。

Java 作者意识到他们的错误后,他们更新了 JVM 对 invokespecial 的处理以正确处理超级调用。现在,无论指令中指定了哪个方法,它都会遍历 link/runtime 的 superclass 层次结构并调用适当的方法。

但是,他们担心在旧版本 Java 下编译的代码依赖于损坏的行为,因此为了向后兼容,他们添加了一个新的 class 文件标志,ACC_SUPER.如果标志设置在 class 上,它有新的(正确的)行为,如果没有,它使用旧的行为。由于旧 class 文件是在该标志存在之前编译的,因此它们不会设置它。同时,编译器更新为在所有新的 classes 上设置 ACC_SUPER,每个人都很高兴...

直到 2011 年。原来 java.lang.Thread 有一个安全敏感的方法,用户不应该调用。为了防止人们从 Thread 的子 class 中调用它,他们覆盖了它以抛出异常。但是,someone realized 黑客可以定义 Thread 的子 class 而无需 ACC_SUPER 标志,从而跳过安全检查并调用该方法的危险版本并突围Java 沙箱。

不幸的是,修复此安全漏洞的唯一方法是完全删除 ACC_SUPER 功能 - 也就是说,将每个 class 都视为设置了标志,无论它实际上是否设置了标志不是。它在 Java 7 update 13 中被匆忙修复,在 Java 8 中,规范本身被更改为记录 ACC_SUPER 不再有任何影响。

所以答案是否定的,在 7u13 之后的任何 Java 版本或实施安全修复的任何更新中,都无法获得超级调用的旧行为。