Summary
If a class file contains a StackMapTable entry with an uninitializedThis type on the stack, the flagThisUninit verification flag should be set, preventing return until this is initialized.
Problem
In normal usage, an <init> method has, as its 0th local variable, an object of type uninitializedThis which has been newly-allocated and needs to be initialized. To properly initialize the object, the code is required by verification to invoke another <init> method (this() or super()), which replaces uninitializedThis with a normal LFoo; class type. Only then can the code return.
The uninitializedThis object does not have to remain in the 0th local variable position. It may be copied elsewhere, overwritten, or pushed/popped on the stack. But verification keeps track of the initialization state of the current object via a flagThisUninit flag, which can only be "cleared" with a this()/super() call. The return instruction cannot be used until the flag has been cleared.
The StackMapTable allows jump targets to declare their type state. This includes local variables or stack entries with type uninitializedThis. Whenever uninitializedThis appears as a local variable, the verifier implicitly sets flagThisUninit for that stack frame.
As a corner case, an uninitializedThis type may appear in a StackMapTable entry as the type of a stack value, but not as the type of a local variable. In this case, current rules say that flagThisUninit should not be set. This has three implications:
-
The code is unreachable: method execution begins with
flagThisUninitset, and the only way to unset it is to callsuper()/this(). But that call also removes anyuninitializedThistypes. So a candidate jump point will either have nouninitializedThison its stack, or will haveflagThisUninitset. Neither of those cases is compatible with the jump target's declared type state. -
The intended initialization timing of the code is ambiguous: if this code is meant to execute before the
super()/this()call, it should haveflagThisUninitset; if this code is meant to execute after thesuper()/this()call, it should not be operating on typeuninitializedThis. -
The code can legally
return, even though it has apparently not yet properly initializedthis. (Fortunately, nothing bad can happen, because the code is unreachable. But we're one bug away from violating a core invariant of object initialization.)
This StackMapTable corner case has no practical use, but introduces needless complexity when designing or trying to understand verification.
Solution
The solution is simple: for a StackMapTable entry that mentions uninitializedThis as one of its stack types, set the flagThisUninit flag.
For existing class files, if they contain unreachable code as described above, and if that unreachable code attempts to return, a new verification error will occur. This can be addressed by, e.g., removing the unreachable code.
Specification
See JVMS issue JDK-8375481. The rule in 4.10.1.4 is changed as follows:
If any ~~local variable in
Localshas the type~~ type inLocalsorOperandStackisuninitializedThis, thenFlagshas the single elementflagThisUninit, otherwiseFlagsis an empty list.
- csr of
-
JDK-8375483 Should set flagThisUninit whenever uninitializedThis is on the stack
-
- Open
-
- relates to
-
JDK-8375481 4.10.1.4: set flagThisUninit whenever uninitializedThis is on the stack
-
- Open
-