RFR (S) 8024599: JSR 292 direct method handles need to respect initialization rules for static members
john.r.rose at oracle.com
Fri Sep 13 02:24:04 UTC 2013
Please review this change for a change to the JSR 292 implementation:
Summary: Align MH semantic with bytecode behavior of constructor and static member accesses, regarding <clinit> invocation.
The change is to javadoc and unit tests, documenting and testing some corner cases of JSR 292 APIs.
Bug Description: When using lookupStatic to create a direct method handle for a static method, class initialization needs to be specified to align with the underly bytecode behavior of the invokestatic instruction.
The JDK8 and 7u40 implementations do this, but the specification claims that the call to lookupStatic itself performs class initialization. Although this may have been the behavior of early versions of JDK 7, it is an error.
The javadoc headers to Lookup and MethodHandle state that method handles emulate bytecode behaviors. The most precise specification of this emulation is in the JVMS, 220.127.116.11:
> Calling this method handle on a valid set of arguments has exactly the same effect and returns the same result (if any) as the corresponding bytecode behavior.
Also the doc for the "invokevirtual" instruction says:
> ...if the method handle to be invoked has bytecode behavior, the Java Virtual Machine invokes the method handle as if by execution of the bytecode behavior associated with the method handle's kind.
And the doc for the "invokestatic" instruction describes the placement of the lazy class initialization:
> On successful resolution of the method, the class that declared the resolved method is initialized (§5.5) if that class has not already been initialized.
This puts the class initialization action near the border between resolution (link time) and execution (run time). The JDK 7 behavior acts as if the initialization action were part of resolution (link time), in that it claims to perform class initialization before the method handle is returned from findStatic (etc.).
But on a closer reading of the JVMS, that is wrong. Note that errors (and presumably side effects) from class initialization are classified as runtime effects, not linktime effects:
> Run-time Exceptions Otherwise, if execution of this invokestatic instruction causes initialization of the referenced class, invokestatic may throw an Error as detailed in §5.5.
In other words, the original JDK 7 specification of eager initalization poorly aligns with (poorly emulates) the bytecode behavior, contrary to the overall pronouncements of the JSR 292 specification.
Compatibility risk for better alignment is small, since most users of JSR 292 create method handles lazily during BSM (i.e., invokedynamic bootstrap method) execution. It will not matter to them (except to reduce surprise due to semantic mislignment) if the initialization action is moved from the BSM to the first execution of the MH produced by the BSM.
It appears that the at least one other implementation, the "JSR 292 Backport", performs static member class initialization on first execution of bytecode behavior, because it uses reflection and weaving of "*static" instructions. This is no accident: The coherence and usefulness of JSR 292 relies on close alignment of member access semantics among the various modes, direct bytecode execution and various old and new reflective APIs.
Besides spec. coherence, a key problem with the "eager" class initialization (in findStatic) is usability. Eager class initialization is impossible to undo. It is also difficult to predict and avoid. Users who want it and don't have it can call Class.forName with an argument of 'true'. Users who don't want it and get it (in early JDK 7 and perhaps other systems) are out of luck.
P.S. Since this is a change which oriented toward JSR 292 functionality, the review request is to mlvm-dev and core-libs-dev.
Changes which are oriented toward performance will go to mlvm-dev and hotspot-compiler-dev.
More information about the core-libs-dev