Search code examples
javajvmbytecodeinstructions

Oracle JVM: invokespecial instruction


I'm confused about the paragraph below (source Oracle JVM specification for invokespecial instruction).

If all of the following are true, let C be the direct superclass of the current class:

  1. The resolved method is not an instance initialization method (§2.9).
  2. If the symbolic reference names a class (not an interface), then that class is a superclass of the current class.
  3. The ACC_SUPER flag is set for the class file (§4.1).

I made some labels (notation: //label//) in the first paragraph of invokespecial instruction description. I used this labels in questions below.

The unsigned indexbyte1 and indexbyte2 are used to construct an index into the run-time constant pool of the current class //current class// (§2.6), where the value of the index is (indexbyte1 << 8) | indexbyte2. The run-time constant pool item at that index must be a symbolic reference to a method or an interface method (§5.1), which gives the name //method name// and descriptor //method descriptor// (§4.3.3) of the method as well as a symbolic reference to the class //referenced class// or interface in which the method is to be found. The named method is resolved //resolved method// (§5.4.3.3, §5.4.3.4).


Based on labels I now check the paragraph with conditions.

  1. The resolved method is not an instance initialization method (§2.9).

Question 1: So, I check if the //method name// doesn't equal "<init>" or "<clinit>", right?

  1. If the symbolic reference names a class (not an interface), then that class is a superclass of the current class.

Question 2: What this means? //Referenced class// must be direct or non-direct superclass (superclass of superclass and so on) of //current class//?

  1. The ACC_SUPER flag is set for the class file (§4.1).

Question 3: Class file of which class? //Referenced class//?


Solution

    1. The resolved method is not an instance initialization method (§2.9).

    So, I check if the //method name// doesn't equal "<init>" or "<clinit>", right?

    Correct. Well, to be more precise “is not an instance initialization method” only implies that the method name is not <init>. But since invoking methods named <clinit> is forbidden in general, it is correct to conclude that the method name must be neither, <init> nor <clinit>.

    1. If the symbolic reference names a class (not an interface), then that class is a superclass of the current class.

    What this means? //Referenced class// must be direct or non-direct superclass (superclass of superclass and so on) of //current class//?

    Yes, if the referenced class is not an interface, it must be a superclass of the current class. Note that this is a condition which implies the “let C be the direct superclass of the current class” stated before the enumeration. For more, see below.

    It’s also worth noting that for non-interface target types, it is mandatory that the target type denotes the current class or a super class of the current class as defined in 4.9.2. Structural Constraints.

    1. The ACC_SUPER flag is set for the class file (§4.1).

    Class file of which class? //Referenced class//?

    The class file of the current class, i.e. the class containing the invokespecial instruction. Note that this is a requirement that is de-facto not existent. You’ll notice when following the link to §4.1:

    The ACC_SUPER flag indicates which of two alternative semantics is to be expressed by the invokespecial instruction (§invokespecial) if it appears in this class or interface. Compilers to the instruction set of the Java Virtual Machine should set the ACC_SUPER flag. In Java SE 8 and above, the Java Virtual Machine considers the ACC_SUPER flag to be set in every class file, regardless of the actual value of the flag in the class file and the version of the class file.

    The ACC_SUPER flag exists for backward compatibility with code compiled by older compilers for the Java programming language. In JDK releases prior to 1.0.2, the compiler generated access_flags in which the flag now representing ACC_SUPER had no assigned meaning, and Oracle's Java Virtual Machine implementation ignored the flag if it was set.

    So when the JVM “considers the ACC_SUPER flag to be set in every class file”, the requirement that the flag has to be set doesn’t need to be checked…

    The historical aspect also explains the meaning of the above. The invokespecial instruction is used to implement super.methodName(…) invocations. In the early 1.0 implementations, the compiler resolved the target class containing the declaration of the method and the JVM invoked that method, ignoring any overriding methods. This creates two problems. First, malicious code could bypass overriding methods in a type hierarchy by using appropriate invokespecial instructions pointing to a supertype of that method. Further, it created dependencies to the super class hierarchy, restricting possible changes.

    The current rule is that the compiler will always encode super.methodName(…) as targeting the direct superclass of the class containing that invocation, regardless of where in the superclass hierarchy the actual declaration has been found at compile time. At runtime, when the JVM encounters such an invokespecial instruction, it will search for the most specific method within the supertype hierarchy, respecting any overrides within it. This ensures that within one class, there will be only dependencies to its direct superclass (unless there are other explicit references to more abstract types in the code) and it allows refactoring, like moving methods up or down the type hierarchy, without affecting compatibility.

    The literal meaning of the enumerated conditions is that the referenced class might be any superclass (the standard compiler only generates class files using the direct super class) to fulfill the condition under which the direct superclass will be used for resolving the method in any case. This includes the situation when executing class files compiled for Java 1.0 before 1.0.2, now that the absence of ACC_SUPER is ignored. The only other case supported for non-interface types is the case that the target type matches the current class exactly, which is used to invoke private methods.

    The ACC_FLAG was introduced for backward compatibility. It’s presence denoted that the new behavior should be used, but as stated above, recent JVMs support only the “new” behavior, which is the state of the art for twenty years now.