JDK-8172817 : LambdaMetafactory should use the referenced class of static implMethods
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 9
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • Submitted: 2017-01-14
  • Updated: 2018-09-27
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.
Other
tbdUnresolved
Related Reports
Blocks :  
Blocks :  
Description
When the 'implMethod' passed to LambdaMetafactory is a static method, the call to 'revealDirect' on the MethodHandle can result in an unexpected access error if the declaring class of the MethodHandle is inaccessible while the original referenced class is accessible: see JDK-8172815.

Once that bug is fixed, the bytecode generated by InnerClassLambdaMetafactory will use the inaccessible declaring class in its generated bytecode, potentially leading to internal resolution errors or other surprises. The appropriate class to use in the bytecode is the original *referenced* class. Unfortunately, per JDK-8068253, this is not made available by the MethodHandle API.

Once *that* bug is fixed, LambdaMetafactory should be updated to use the referenced class, not the declaring class.

A test case, which ought to run without error:

--- 

package p1; 
class A { 
    public static void staticMethod() {} 
    public void instanceMethod() {} 
} 

--- 

package p1; 
public class B extends A {} 

--- 

package p2; 
public class C extends p1.B {} 

--- 


import java.lang.invoke.*;

public class Test {

    public static void main(String... args) throws Throwable {
        MethodType toVoid = MethodType.methodType(void.class);
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle mh = lookup.findStatic(p2.C.class, "staticMethod", toVoid);
        
        System.out.println("Begin linkage...");
        CallSite cs = LambdaMetafactory.metafactory(lookup, "run", MethodType.methodType(Runnable.class), toVoid, mh, toVoid);
        System.out.println("Begin capture...");
        Runnable r = (Runnable) cs.getTarget().invokeExact();
        System.out.println("Begin invocation...");
        r.run();
        System.out.println("Done");
    }

}

Comments
We also need the referenced class for invokespecial. A test case: --- interface I { default void defaultMethod() {} } --- interface J extends I {} --- import java.lang.invoke.*; public class Test implements J { @Override public void defaultMethod() { throw new RuntimeException("Shouldn't get here"); } public static void main(String... args) throws Throwable { MethodType toVoid = MethodType.methodType(void.class); MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle mh = lookup.findSpecial(J.class, "defaultMethod", toVoid, Test.class); System.out.println("Begin linkage..."); CallSite cs = LambdaMetafactory.metafactory(lookup, "run", MethodType.methodType(Runnable.class, Test.class), toVoid, mh, toVoid); System.out.println("Begin capture..."); Runnable r = (Runnable) cs.getTarget().invokeExact(new Test()); System.out.println("Begin invocation..."); r.run(); System.out.println("Done"); } } (The generated class should reference J.defaultMethod, not I.defaultMethod or Test.defaultMethod.)
09-02-2017

In the other direction on the software stack, when this is fixed javac can be updated so that it no longer generates "bridge" lambdas for method references like 'p2.C::staticMethod'. I haven't created a bug, but one can be created as a followup.
14-01-2017