Please review this change for a change to the JSR 292 implementation:

http://cr.openjdk.java.net/~jrose/8024599/webrev.00/

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. 

Detail: 

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, 5.4.3.5: 

> 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.

Thanks,
— John

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.

Reply via email to