JDK-8274848 : LambdaMetaFactory::metafactory on REF_invokeSpecial impl method has incorrect behavior
  • Type: Bug
  • Component: core-libs
  • Sub-Component: java.lang.invoke
  • Affected Version: 15,16,17,18
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2021-10-06
  • Updated: 2021-11-02
  • Resolved: 2021-10-28
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.
JDK 17 JDK 18
17.0.2Fixed 18 masterFixed
Related Reports
CSR :  
Relates :  
Description
Reported on https://mail.openjdk.java.net/pipermail/jdk-dev/2021-October/006099.html

This is to follow up the behavorial change on LambdaMetaFactory::metafactory when the implementation is REF_invokeSpecial method handle on a public/protected method in the target class.  This won't happen for classes compiled from javac since REF_invokeSpecial method handle is generated when the method is a private method whereas REF_invokeVirtual method handle would be generated on a public/protected.

To reproduce:
1. compile it with java 8
2. modify the access of testPrivate from private to public in .class file (do not modify any invokespecial and ref_invokespecial).
3. Java 14 without JEP 371, the method reference `this::testPrivate` with REF_invokeSpecial kind will be resolved to ExampleClass::testPrivate on a receiver of type ExampleClass$SubClass.   With JEP 371 change, the method reference `test::testPrivate` with REF_invokeSpecial kind will be resolved to ExampleClass$Subclass::testPrivate instead.


import java.util.function.Supplier;

public class ExampleClass {
    public static void main(String[] args) {
        System.out.println(new SubClass().test());
        System.out.println(new SubClass().testWithLMF());
    }

    public String test() {
        return this.testPrivate();
    }

    public String testWithLMF() {
        Supplier<String> supplier = this::testPrivate;
        return supplier.get();
    }

    private String testPrivate() {
        return "123";
    }

    public static class SubClass extends ExampleClass {
        public String testPrivate() {
            return "456";
        }
    }
}

Java 14:

$ java ExampleClass
123
123

Java 15:

$ java ExampleClass
123
456
Comments
JDK 17u Fix Request: This issue is a regression introduced by the implementation of JEP 371 (JDK-8238358). A lambda class, a nestmate of the original class, does not have access to use invokespecial on the original class's super methods. For such case, the lambda class will invoke the implementation method directly on the method handle (i.e. via MethodHandle::invoke). On the other hand, invokespecial was used to invoke private methods prior to Java 11 (JEP 181). Since Java 11, invokespecial is only used to invoke private nestmate constructor. invokevirtual or invokeinterface is used to the implementation method if it's not a constructor and therefore JDK-8238358 converts REF_invokeSpecial method handle to REF_invokeVirtual or REF_invokeInterface method handle for compatibility reason but such conversion should only apply if the method handle is private. The fix is trivial and do the conversion only if the implementation method is private. The commit is applied to jdk17u cleanly.
29-10-2021

Changeset: 21da2183 Author: Mandy Chung <mchung@openjdk.org> Date: 2021-10-28 22:24:56 +0000 URL: https://git.openjdk.java.net/jdk/commit/21da2183875feca3dbf4f1bd875b268a7fc8d560
28-10-2021

For classes compiled for a release prior to the nestmate support, the implementation method handle on a private method has to be REF_invokeSpecial reference kind. Since JDK 15, the lambda proxy class is a hidden class and a nestmate of the host class and LambdaMetaFactory adjusts it to REF_invokeVirtual or REF_invokeInterface for compatibility. LMF implementation should only adjust the reference kind if the implementation method is declared in the host class *and* also it's private. The current implementation only checks the implementation method declared in the host class of any access. The fix is to check if it's a private method in addition to the declaring class.
11-10-2021