JDK-8349179 : 4.10.1.9 invokespecial: rewrittenUninitializedType has wrong result type
  • Type: Bug
  • Component: specification
  • Sub-Component: vm
  • Affected Version: 8,24
  • Priority: P4
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2025-02-01
  • 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.
JDK 25
25Fixed
Related Reports
Relates :  
Relates :  
Description
rewrittenUninitializedType(uninitializedThis, Environment,
                           MethodClass, MethodClass) :-
    MethodClass = class(MethodClassName, CurrentLoader),
    thisClass(Environment, class(thisClassName, thisLoader)),
    superclassChain(thisClassName, thisLoader, [MethodClass | Rest]).

This says that, for an invokespecial of a superclass <init> method, the type 'unintializedThis' should be replaced with 'MethodClass', the type of the *superclass*. But that's incorrect: the new type should be the type of the *current class*.

This bug appears to have been introduced by JDK-7160765, which recognized the need to constrain the invocation to the current class or the superclass. But it inadvertently changed the result type at the same time.

Ever since JDK-8138821, which spun off the 'uninitializedThis' case separately, there hasn't really been a need for a 'rewrittenUninitializedType' predicate that works on all uninitialized types. It may make sense to strip this check down to a simpler 'validInvokespecialTarget' or something like that, and leave it to the 'instructionIsTypeSafe' rule to always map 'uninitializedThis' to 'CurrentClass'.
Comments
For uniformity with the other invokespecial rule (and invoke instructions generally), we can also include the uninitialized type in the StackArgList for both of these rules and eliminate the TempFrame variable: reverse([uninitializedThis | OperandArgList], StackArgList), canPop(StackFrame, StackArgList, frame(Locals, OperandStack, Flags)), reverse([uninitialized(Address) | OperandArgList], StackArgList), canPop(StackFrame, StackArgList, frame(Locals, OperandStack, Flags)), (This leaves Address unbound until the 'canPop' application, which should be just fine in Prolog.)
05-05-2025

Changes to the narrative text (completing the job of splitting the two cases that was started in JDK-8138821): Or: - MethodName is <init>. - Descriptor specifies a void return type. - One can validly pop types matching the argument types given in Descriptor and ~~an uninitialized type, UninitializedArg,~~ **`uninitializedThis`** off the incoming operand stack, yielding OperandStack. - **MethodClassType is the current class or the direct superclass of the current class.** - The outgoing type state is derived from the incoming type state by first replacing the incoming operand stack with OperandStack and then replacing all instances of ~~UninitializedArg~~ **`uninitializedThis`** with the type of ~~instance being initialized~~ **the current class**. - ~~If the instruction calls an instance initialization method on a class instance created by an earlier new instruction, and the method is protected, the usage conforms to the special rules governing access to protected members (§4.10.1.8).~~ // insert `uninitializedThis` rules here **Or:** - **MethodName is <init>.** - **Descriptor specifies a void return type.** - **One can validly pop types matching the argument types given in Descriptor and an uninitialized type, uninitialized(Address), off the incoming operand stack, yielding OperandStack. - **The instruction at `Address` created a new `MethodClassType`.** - **The outgoing type state is derived from the incoming type state by first replacing the incoming operand stack with OperandStack and then replacing all instances of `uninitialized(Address)` with `MethodClassType`.** - **If the method is protected, the usage conforms to the special rules governing access to protected members (§4.10.1.8).** // insert `uninitialized(Address)` rules here ~~To compute what type the uninitialized argument's type needs to be rewritten to, there are two cases:~~ - ~~If we are initializing an object within its constructor, its type is initially uninitializedThis. This type will be rewritten to the type of the class of the <init> method.~~ - ~~The second case arises from initialization of an object created by new. The uninitialized arg type is rewritten to MethodClass, the type of the method holder of <init>. We check whether there really is a new instruction at Address.~~
13-02-2025

Revised rules (after applying the changes from JDK-8323557; also taking some inspiration from JDK-8122946): instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame, NextStackFrame, ExceptionStackFrame) :- CP = method(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, MethodClassType, This),~~ ~~rewrittenInitializationFlags(uninitializedThis, Flags, NextFlags),~~ **validThisInitClassType(Environment, MethodClassType),** **thisType(Environment, This),** substitute(uninitializedThis, This, OperandStack, NextOperandStack), substitute(uninitializedThis, This, Locals, NextLocals), substitute(uninitializedThis, top, Locals, ExceptionLocals), NextStackFrame = frame(NextLocals, NextOperandStack, ~~NextFlags~~ **[]**), ExceptionStackFrame = frame(ExceptionLocals, [], Flags). **validThisInitClassType(Environment, class(MethodClassName, _)) :- thisClass(Environment, ThisClass), classClassName(ThisClass, MethodClassName).** **validThisInitClassType(Environment, class(MethodClassName, _)) :- thisClass(Environment, ThisClass), classSuperClassName(ThisClass, MethodClassName).** instructionIsTypeSafe(invokespecial(CP), Environment, _Offset, StackFrame, NextStackFrame, ExceptionStackFrame) :- CP = method(MethodClassName, '`<init>`', Descriptor), parseMethodDescriptor(Descriptor, OperandArgList, void), reverse(OperandArgList, StackArgList), canPop(StackFrame, StackArgList, TempFrame), TempFrame = frame(Locals, [uninitialized(Address) | OperandStack], Flags), ~~rewrittenUninitializedType(uninitialized(Address), Environment, MethodClassType, This),~~ ~~rewrittenInitializationFlags(uninitialized(Address), Flags, NextFlags),~~ **allInstructions(Environment, Instructions),** **member(instruction(Address, new(MethodClassType)), Instructions),** substitute(uninitialized(Address), ~~This~~ **MethodClassType**, OperandStack, NextOperandStack), substitute(uninitialized(Address), ~~This~~ **MethodClassType**, Locals, NextLocals), substitute(uninitialized(Address), top, Locals, ExceptionLocals), NextStackFrame = frame(NextLocals, NextOperandStack, ~~NextFlags~~ **Flags**), ExceptionStackFrame = frame(ExceptionLocals, [], Flags), passesProtectedCheck(Environment, MethodClassType, '&init;', Descriptor, NextStackFrame). ~~rewrittenUninitializedType(uninitializedThis, Environment, MethodClassType, MethodClassType) :- thisType(Environment, MethodClassType).~~ ~~rewrittenUninitializedType(uninitializedThis, Environment, MethodClassType, MethodClassType) :- MethodClassType = class(MethodClassName, _), thisClass(Environment, ThisClass), classSuperClassName(ThisClass, MethodClassName).~~ ~~rewrittenUninitializedType(uninitialized(Address), Environment, MethodClassType, MethodClassType) :- allInstructions(Environment, Instructions), member(instruction(Address, new(MethodClassType)), Instructions).~~
13-02-2025