JDK-8019635 : Use MethodType for LambdaMetafactory contract rather than MethodHandle
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Priority: P2
  • Status: Closed
  • Resolution: Duplicate
  • Submitted: 2013-07-02
  • Updated: 2017-05-19
  • Resolved: 2013-07-10
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.
Related Reports
Blocks :  
Duplicate :  
The metafactory parameter "MethodHandle samMethod" is confusing: what we're trying to encode is a MethodType + a name; but MethodHandles also carry with them stuff we don't care about (and must not, by contract): a class, an invocation mode, and implicit method resolution.

(Diversion: does the class in the MethodHandle tell the MF what the generated class's superinterface should be? no, that comes from the invokedType ("()->SuperInterface").)

When there are multiple methods to override, this can be confusing -- it suggests that the samMethod gets different treatment than the other methods (which are just MethodTypes), while in reality they are equal peers.

But, more importantly, the fact that resolution happens raises the possibility of resolution errors.  For example, the package-access limitation I find myself running into a lot lately:

package p1; interface I { void m(); }
package p1; public interface J extends I {}
package p2; ... p1.J j = () -> {};

This causes an IllegalAccessError, because javac mentions I in the MethodHandle pointing to I.m.  There is no error in an anonymous class, because the class doesn't mention I.

We could tweak the compiler to point to J.m instead, but that raises other problems: what happens if J.m is ambiguous?

package p1; interface I { void m(); }
package p1; interface K { default void m() {} }
package p1; public interface J extends I,K {}

(This would be a compile-time error, but you can get there with separate compilation...)

Generally, it doesn't seem right that we are asking the VM to do some computation that i) is irrelevant, and ii) can cause errors.  It's a neat trick to wrap up the method name in this way, but seems to have more baggage than was intended.  So perhaps we should bite the bullet and just pass the method name as a String instead?

(I've thought about using "invokedName", which is already there and "wasted" with "lambda$", although I get that, morally, the name of the method is "makeMeAFunction", not "apply" or whatever.  I don't know what the invokedynamic conventions are for using/abusing that name.)
If changes are being made, it's worth considering whether name changes should happen, too. For example, the spelling/capitalization of "metafactory" is inconsistent between the class name and method name.

Some followup discussion from emails: If, on the other hand, we _want_ the ambiguity errors, etc., then at the very least we need to use MethodHandles consistently for all methods passed to the altMetafactory, rather than just for one of them. ... I consider the current behavior unstable, because i) naming an interface that is not an immediate super is an accessibility-related bug; and ii) the choice of one method to be a MethodHandle while the rest are MethodTypes is arbitrary, and probably nondeterministic. ... interface I<X,Y extends Number> { void m(X arg); void m(Y arg); } I<Integer,Integer> func = (Integer i) -> {}; There are two methods to override, with two different descriptors ('m(Object)V', 'm(Number)V'). The compiler must encode one as a MethodHandle, and one as a MethodType. Which one is the MethodHandle? interface I { void m(); } interface J { void m(); } Object func = (Serializable & I & J) () -> {}; There are two methods to override, with the same descriptor but different interfaces. Which one is the MethodHandle? In both cases, at runtime the MethodHandle method will be resolved, while the MethodType method could disappear, become inaccessible, have a bad class loader, etc., and we wouldn't care. ... We're already past FF, and we want to avoid an unnecessary flag day where code compiled with the developer preview release doesn't work with production JDKs.

I made this a P2 because the change would impact various components and invalidate previously-compiled lambda code. So, the sooner it is addressed, the better. (But feel free to re-prioritize as appropriate.)