Java 类文件上的 ACC_SUPER 访问标志的用途是什么?

发布于 2024-12-28 07:33:32 字数 1136 浏览 3 评论 0原文

invokespecial JVM 指令用于在创建新对象时调用初始化方法 ()。指令的描述表明(但没有澄清)是否调用超类的构造函数或当前类的构造函数的决定取决于在指令中设置的 ACC_SUPER 标志的状态。 class 文件。

来自 Sun JVM 规范:

接下来,除非满足以下所有条件,否则将选择调用已解析的方法:

  • 为当前类设置 ACC_SUPER 标志(请参见表 4.1“类访问和属性修饰符”)。

-- 来源 (invokespecial 操作码定义)

ACC_SUPER 标志的设置指示 Java 虚拟机要表达其 invokespecial 指令的两种替代语义中的哪一种; ACC_SUPER 标志的存在是为了向后兼容 Sun 的旧版 Java 编程语言编译器所编译的代码。 Java 虚拟机的所有新实现都应该实现本规范中记录的 invokespecial 语义。所有新的 Java 虚拟机指令集编译器都应该设置 ACC_SUPER 标志。 Sun 的较旧编译器生成未设置 ACC_SUPER 的 ClassFile 标志。 Sun 较旧的 Java 虚拟机实现会忽略该标志(如果已设置)。

-- (ClassFile< /code> 格式)

定义指出该标志是为了向后兼容旧编译器。然而,它与 Sun 较旧的 Java 虚拟机实现在设置时忽略该标志相矛盾。

该标志是否仍与 invokespecial 操作码一起使用?据我所知,它似乎没有任何目的,而且我找不到任何资源来表明它曾经有过目的。

谢谢。

The invokespecial JVM instruction is used for calling initialisation methods (<init>) when creating new objects. The description of the instruction suggests (but doesn't clarify) that the decision on whether to call the constructor of a superclass or a constructor of the current class depends on the state of the ACC_SUPER flag set within the class file.

From the Sun JVM Specification:

Next, the resolved method is selected for invocation unless all of the following conditions are true:

  • The ACC_SUPER flag (see Table 4.1, "Class access and property modifiers") is set for the current class.

-- Source (invokespecial opcode definition)

The setting of the ACC_SUPER flag indicates which of two alternative semantics for its invokespecial instruction the Java virtual machine is to express; the ACC_SUPER flag exists for backward compatibility for code compiled by Sun's older compilers for the Java programming language. All new implementations of the Java virtual machine should implement the semantics for invokespecial documented in this specification. All new compilers to the instruction set of the Java virtual machine should set the ACC_SUPER flag. Sun's older compilers generated ClassFile flags with ACC_SUPER unset. Sun's older Java virtual machine implementations ignore the flag if it is set.

-- Source (ClassFile format)

The definition states that the flag is for backward compatibility with old compilers. However it goes on to contradict with Sun's older Java virtual machine implementations ignore the flag if it is set.

Is the flag still used with the invokespecial opcode? From what I can tell, it seems to hold no purpose and I can't find a resource to suggest it ever did.

Thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

疾风者 2025-01-04 07:33:32

引入 ACC_SUPER 是为了纠正调用超级方法的问题。 ACC_SUPER 标志将一个类标记为针对操作码 183 指令的更改语义进行编译的。它的用途与类文件版本号的用途类似,因为它允许 JVM 检测某个类是否是针对该指令的较旧或较新语义进行编译的。 Java 1.0.2 没有设置并忽略 ACC_SUPER,而 Java 1.1 及更高版本总是设置 ACC_SUPER。

在 Java 1.1 之前,操作码 183 的字节码指令(现在称为 invokespecial)被称为 invokenonvirtual,并且具有部分不同的规范。每当必须在没有虚拟方法查找的情况下调用实例方法时,就会使用它。私有方法、实例初始值设定项(构造函数)以及在 super 上实现方法调用就是这种情况。但后一种情况导致了类库不断发展的问题。

字节码中的方法引用 (CONSTANT_Methodref_info) 不仅定义了方法的名称、参数和返回类型,还定义了它所属的类。操作码 183 获取这样一个方法引用参数,旨在直接从指定类调用引用的方法,而无需进一步查找。在调用 super 的情况下,编译器有责任解析实现此方法的最接近的超类,并在字节代码中生成对其的引用。

从 Java 1.1 开始,它被更改为基本上忽略 CONSTANT_Methodref_info 中引用的类,而是在 JVM 中查找具有给定方法名称和签名的最接近的超级方法。这通常在类加载时或在指令执行或第一次 JIT 编译之前完成。

以下是为什么需要进行此更改的示例。在 Java 1.0.2 中,AWT 类 ContainerComponent 是这样定义的:

class Component
{
    public void paint( Graphics g ) {}
}

class Container extends Component
{
    // inherits paint from Component but doesn't override it
}

在 Java 1.1 中,类 Container 被更改为拥有自己的实现paint 的:

class Container extends Component
{
    public void paint( Graphics g ) {/*...*/}
}

现在,当您有 Container 的直接或间接子类调用 super.paint(g) 并将其编译为1.0.2 它生成了Component.paintinvokenonvirtual 指令,因为这是第一个具有此方法的父级。但是,如果您在也具有 Container.paint 的 JVM 上使用此编译类,它仍然会调用 Component.paint,这不是您所期望的。

另一方面,当您编译 1.1 的类并在 1.0.2 JVM 上执行它时,它会抛出 AbstractMethodError ,或者对于那个时代的 VM 来说更有可能只是崩溃。为了避免崩溃,您必须编写 ((Component)super).paint(g) 并使用 1.1 编译器对其进行编译,才能在任一虚拟机中获得所需的行为。这将设置 ACC_SUPER 但仍生成调用 Component.paint 的指令。 1.0.2 VM 会忽略 ACC_SUPER 并直接调用 Component.paint ,这很好,而 1.1 VM 会找到 ACC_SUPER 集,从而自行进行查找,这将使其调用 Container。 Paint 即使字节码方法引用是 Component.paint

您可以在 ikvm.net 博客上的这篇旧帖子

ACC_SUPER was introduced to correct a problem with the invocation of super methods. The ACC_SUPER flag marks a class as compiled for the changed semantics of the opcode 183 instruction. It's purpose is similar to that of the class file version number as it allows the JVM to detect whether a class was compiled for the older or newer semantics of that instruction. Java 1.0.2 did not set and ignored ACC_SUPER while Java 1.1 and later always sets ACC_SUPER.

Before Java 1.1 the byte code instruction with opcode 183 that is now called invokespecial was called invokenonvirtual and had a partially different specification. It was used whenever instance methods had to be invoked without a virtual method lookup. This was the case for private methods, instance initializers (constructors) and to implement method invocations on super. But the latter case caused problems with evolving class libraries.

A method reference in byte code (CONSTANT_Methodref_info) not only defines the name and the argument and return types of a method but also the class to which it belongs. Opcode 183 gets such a method reference parameter and was meant to directly invoke the referenced method from the specified class without further lookups. In the case of invocations on super it was the compilers responsibility to resolve the closest super class that implements this method and generate a reference to it into the byte code.

Since Java 1.1 it was changed to essentially ignore the class referenced in CONSTANT_Methodref_info and to instead do the lookup for the closest super method with the given method name and signature in the JVM. This is usually done now when the class gets loaded or right before the instruction is executed or JIT compiled the first time.

Here is an example why this change was neccessary. In Java 1.0.2 the AWT classes Container and Component were defined this way:

class Component
{
    public void paint( Graphics g ) {}
}

class Container extends Component
{
    // inherits paint from Component but doesn't override it
}

In Java 1.1 the class Container was changed to have its own implementation of paint:

class Container extends Component
{
    public void paint( Graphics g ) {/*...*/}
}

Now when you had a direct or indirect subclass of Container that made a call on super.paint(g) and compiled it for 1.0.2 it generated a invokenonvirtual instruction for Component.paint since this was the first parent that had this method. But if you used this compiled class on a JVM that also had Container.paint it would still have called Component.paint which is not what you would expect.

On the other hand, when you compiled the class for 1.1 and executed it on a 1.0.2 JVM it would throw a AbstractMethodError or more likely for VMs of that era simply crash. To avoid the crash you had to write ((Component)super).paint(g) and compile it with a 1.1 compiler to get the desired behaviour in either VM. This would set ACC_SUPER but still generate the instruction to call Component.paint. A 1.0.2 VM would ignore ACC_SUPER and go straight to invoke Component.paint which is fine while a 1.1 VM would find ACC_SUPER set and thus do the lookup itself which would make it invoke Container.paint even though the byte code method reference was Component.paint.

You can find more about this in this old post on the ikvm.net weblog.

懵少女 2025-01-04 07:33:32

自 Java 8 以来,ACC_SUPER 标志没有执行任何操作。

根据 JEP 草案:更好定义的 JVM类文件验证

将不再指定 ACC_SUPER 标志,该标志自 Java 8 起无效。

虽然我还没有找到一个简洁、易于链接的权威来源,但似乎该功能在 Java 8 之前已在 Java 7 安全更新中被删除。

The ACC_SUPER flag has not done anything since Java 8.

Per JEP draft: Better-defined JVM class file validation:

The ACC_SUPER flag, which has no effect since Java 8, will no longer be specified.

While I haven't found a single concise easily-linkable authoritative source, it seems that the functionality was removed prior to Java 8, in a Java 7 security update.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文