JDK-8122946 : 4.10.1.9: invoke{special,static} can invoke an interface method
  • Type: Bug
  • Component: specification
  • Sub-Component: vm
  • Affected Version: 8,9
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2015-06-17
  • Updated: 2025-06-11
  • Resolved: 2025-06-11
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
tbdFixed
Related Reports
Relates :  
Relates :  
Relates :  
Description
JVMS 8 modified the 'invokespecial' and 'invokestatic' instructions so that they can refer to an InterfaceMethodref in the constant pool rather than a (class) Methodref.

4.10.1.9 was not modified to reflect this enhancement.  As a result, the 'instructionIsTypeSafe' rules for these instructions assert "CP = method(...)", implying that the constant pool entry is a Methodref.  (See 4.10.1.3 for the definition of 'method', distinct from 'imethod'.)

This inconsistency is most easily addressed by duplicating the 'instructionIsTypeSafe' rules, adding a 'imethod' variant.  We could also modify just the clause, using a parenthesized disjunction; or we could introduce a 'anymethod' rule that encompasses both kinds of constant pool entries.
Comments
Proposed changes to the <init> invokespecial rules (while interface methods are useless here, HotSpot allows them in verification and delays errors until resolution/run time): instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame, NextStackFrame, ExceptionStackFrame) :- ~~CP = method(MethodClassName, '`<init>`', Descriptor),~~ **(CP = method(MethodClassName, '`<init>`', Descriptor) ;** **CP = imethod(MethodClassName, '`<init>`', Descriptor)),** parseMethodDescriptor(Descriptor, OperandArgList, void), reverse(OperandArgList, StackArgList), canPop(StackFrame, StackArgList, TempFrame), TempFrame = frame(Locals, [uninitializedThis | OperandStack], Flags), currentClassLoader(Environment, CurrentLoader), rewrittenUninitializedType(uninitializedThis, Environment, class(MethodClassName, CurrentLoader), This), rewrittenInitializationFlags(uninitializedThis, Flags, NextFlags), substitute(uninitializedThis, This, OperandStack, NextOperandStack), substitute(uninitializedThis, This, Locals, NextLocals), NextStackFrame = frame(NextLocals, NextOperandStack, NextFlags), ExceptionStackFrame = frame(Locals, [], Flags). instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame, NextStackFrame, ExceptionStackFrame) :- ~~CP = method(MethodClassName, '`<init>`', Descriptor),~~ **(CP = method(MethodClassName, '`<init>`', Descriptor) ;** **CP = imethod(MethodClassName, '`<init>`', Descriptor)),** parseMethodDescriptor(Descriptor, OperandArgList, void), reverse(OperandArgList, StackArgList), canPop(StackFrame, StackArgList, TempFrame), TempFrame = frame(Locals, [uninitialized(Address) | OperandStack], Flags), currentClassLoader(Environment, CurrentLoader), rewrittenUninitializedType(uninitialized(Address), Environment, class(MethodClassName, CurrentLoader), This), rewrittenInitializationFlags(uninitialized(Address), Flags, NextFlags), substitute(uninitialized(Address), This, OperandStack, NextOperandStack), substitute(uninitialized(Address), This, Locals, NextLocals), NextStackFrame = frame(NextLocals, NextOperandStack, NextFlags), ExceptionStackFrame = frame(Locals, [], Flags), passesProtectedCheck(Environment, MethodClassName, '`<init>`', Descriptor, NextStackFrame).
11-01-2024

Proposed changes to the non-<init> invokespecial rules (I've also adopted the MethodClassName --> MethodClassType refactoring and related changes from JDK-8323557): Either: - MethodName is not <init>. - MethodName is not <clinit>. - **MethodClassType is the current class, a superclass of the current class, or a direct superinterface of the current class.** - One can validly replace types matching the current class and the argument types given in Descriptor on the incoming operand stack with the return type given in Descriptor, yielding the outgoing type state. - ~~One can validly replace types matching MethodClassType and the argument types given in Descriptor on the incoming operand stack with the return type given in Descriptor.~~ instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame, NextStackFrame, ExceptionStackFrame) :- ~~CP = method(MethodClassType, MethodName, Descriptor),~~ **(CP = method(MethodClassType, MethodName, Descriptor) ;** **CP = imethod(MethodClassType, MethodName, Descriptor)),** MethodName \= '`<init>`', MethodName \= '`<clinit>`', **validSpecialMethodClassType(Environment, MethodClassType),** parseMethodDescriptor(Descriptor, OperandArgList, ReturnType), thisType(Environment, ThisType), ~~isAssignable(ThisType, MethodClassType),~~ reverse([ThisType | OperandArgList], StackArgList), validTypeTransition(Environment, StackArgList, ReturnType, StackFrame, NextStackFrame), ~~reverse([MethodClassType | OperandArgList], StackArgList2),~~ ~~validTypeTransition(Environment, StackArgList2, ReturnType,~~ ~~StackFrame, _ResultStackFrame),~~ exceptionStackFrame(StackFrame, ExceptionStackFrame). **validSpecialMethodClassType(Environment, class(MethodClassName, L)) :-** **thisClass(Environment, ThisClass),** **classClassName(ThisClass, MethodClassName).** **validSpecialMethodClassType(Environment, class(MethodClassName, L)) :-** **thisClass(Environment, ThisClass),** **loadedSuperclasses(ThisClass, Supers),** **member(Super, Supers),** **classClassName(Super, MethodClassName),** **loadedClass(MethodClassName, L, Super).** **validSpecialMethodClassType(Environment, class(MethodClassName, L)) :-** **thisClass(Environment, ThisClass),** **classInterfaceNames(ThisClass, InterfaceNames),** **member(MethodClassName, InterfaceNames).** The ~~isAssignable~~ **validSpecialMethodClassType** clause enforces the structural constraint that invokespecial, for other than an instance initialization method, must name a method in the current class/interface or a superclass/superinterface. The ~~first~~ validTypeTransition clause enforces the structural constraint that invokespecial, for other than an instance initialization method, targets a receiver object of the current class or deeper. To see why, consider that StackArgList simulates the list of types on the operand stack expected by the method, starting with the current class (the class performing invokespecial). The actual types on the operand stack are in StackFrame. The effect of validTypeTransition is to pop the first type from the operand stack in StackFrame and check it is a subtype of the first term of StackArgList, namely the current class. Thus, the actual receiver type is compatible with the current class. A sharp-eyed reader might notice that enforcing this structural constraint supercedes the structural constraint pertaining to invokespecial of a protected method. Thus, the Prolog code above makes no reference to passesProtectedCheck (4.10.1.8), whereas the Prolog code for invokespecial of an instance initialization method uses passesProtectedCheck to ensure the actual receiver type is compatible with the current class when certain protected instance initialization methods are named. ~~The second validTypeTransition clause enforces the structural constraint that any method invocation instruction must target a receiver object whose type is compatible with the type named by the instruction. To see why, consider that StackArgList2 simulates the list of types on the operand stack expected by the method, starting with the type named by the instruction. Again, the actual types on the operand stack are in StackFrame, and the effect of validTypeTransition is to check the actual receiver type in StackFrame is compatible with the type named by the instruction in StackArgList2.~~ [notes: - Array types are syntactically allowed here, but the validSpecialMethodClassType clause will reject them. - The second 'validTypeTransition' check is redundant. Given that CurrentClass <: MethodClassType and the stack operand <: CurrentClass, there's no need to also check that the stack operand <: MethodClassType.]
11-01-2024

Proposed change to invokestatic rule: instructionIsTypeSafe(invokestatic(CP), Environment, _Offset, StackFrame, NextStackFrame, ExceptionStackFrame) :- ~~CP = method(_MethodClassName, MethodName, Descriptor),~~ **(CP = method(_MethodClassType, _MethodName, Descriptor) ;** **CP = imethod(_MethodClassType, _MethodName, Descriptor)),** MethodName \= '`<init>`', MethodName \= '`<clinit>`', parseMethodDescriptor(Descriptor, OperandArgList, ReturnType), reverse(OperandArgList, StackArgList), validTypeTransition(Environment, StackArgList, ReturnType, StackFrame, NextStackFrame), exceptionStackFrame(StackFrame, ExceptionStackFrame).
11-01-2024

Properly specifying invokespecial is tricky, because there is also the requirement that the referenced class be the current class, a superclass, or a direct superinterface (see 4.9.2). The existing rules enforce this with 'isAssignable', but that doesn't work on interface types (in the verification type system, any reference type 'isAssignable' to an interface type). In practice, Hotspot appears to perform the following check: - If a Methodref is used, test for isAssignable. - If an InterfaceMethodref is used, make sure it is named as a direct superinterface. A limitation of this approach is that it allows, for example, the name of a valid interface that is not related to the current class to appear in a Methodref. Verification succeeds, and no error occurs until resolution of the Methodref. That's some unnecessary complexity that doesn't quite align with 4.9.2. Instead, we should directly test for a valid class/interface name: the current class, a superclass, or a direct superinterface.
11-01-2024