1 /*
  2  * Copyright (c) 2026, Oracle and/or its affiliates. All rights reserved.
  3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  * This code is free software; you can redistribute it and/or modify it
  6  * under the terms of the GNU General Public License version 2 only, as
  7  * published by the Free Software Foundation.
  8  *
  9  * This code is distributed in the hope that it will be useful, but WITHOUT
 10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  * version 2 for more details (a copy is included in the LICENSE file that
 13  * accompanied this code).
 14  *
 15  * You should have received a copy of the GNU General Public License version
 16  * 2 along with this work; if not, write to the Free Software Foundation,
 17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  * or visit www.oracle.com if you need additional information or have any
 21  * questions.
 22  */
 23 
 24 import java.lang.invoke.MethodHandles;
 25 import java.lang.reflect.Method;
 26 import java.util.Optional;
 27 import java.util.stream.Stream;
 28 import jdk.incubator.code.Reflect;
 29 import jdk.incubator.code.Op;
 30 import jdk.incubator.code.bytecode.BytecodeGenerator;
 31 import org.junit.jupiter.api.Assertions;
 32 import org.junit.jupiter.api.Test;
 33 import resolve.Parent;
 34 
 35 /*
 36  * @test
 37  * @bug 8392556
 38  * @summary Test for code reflection with method reference expressions.
 39  * @library ../lib/
 40  * @library ../
 41  * @modules jdk.incubator.code
 42  * @enablePreview
 43  * @run junit TestMethodReferenceAccess
 44  */
 45 public class TestMethodReferenceAccess {
 46 
 47     @Reflect
 48     public static Parent direct(Parent p) {
 49         return p.setValue("ok");
 50     }
 51 
 52     @Reflect
 53     public static Parent genericReference(Parent p) {
 54         Optional.of("ok").ifPresent(p::setValue);
 55         return p;
 56     }
 57 
 58     @Reflect
 59     public static int nonGenericReference(Parent p) {
 60         return Optional.of("ok").map(p::length).get();
 61     }
 62 
 63     @Reflect
 64     public static <T extends Parent> T genericReferenceWithTypeVarReceiver(T p) {
 65         Optional.of("ok").ifPresent(p::setValue);
 66         return p;
 67     }
 68 
 69     @Reflect
 70     public static <T extends Parent & Runnable> T genericReferenceWithIntersectionTypeVarReceiver(T p) {
 71         Optional.of("ok").ifPresent(p::setValue);
 72         return p;
 73     }
 74 
 75     @Test
 76     public void testDirectCall() throws Throwable {
 77         var parent = new Parent();
 78         var op = Op.ofMethod(getMethod(TestMethodReferenceAccess.class, "direct").orElseThrow()).orElseThrow();
 79         var handle = BytecodeGenerator.generate(MethodHandles.lookup(), op);
 80         Assertions.assertEquals(TestMethodReferenceAccess.direct(parent), handle.invoke(parent));
 81         Assertions.assertEquals(TestMethodReferenceAccess.direct(parent), Interpreter.invoke(MethodHandles.lookup(), op, parent));
 82     }
 83 
 84     @Test
 85     public void testGenericMethodReference() throws Throwable {
 86         var parent = new Parent();
 87         var op = Op.ofMethod(getMethod(TestMethodReferenceAccess.class, "genericReference").orElseThrow()).orElseThrow();
 88         var handle = BytecodeGenerator.generate(MethodHandles.lookup(), op);
 89         Assertions.assertEquals(TestMethodReferenceAccess.genericReference(parent), handle.invoke(parent));
 90         Assertions.assertEquals(TestMethodReferenceAccess.genericReference(parent), Interpreter.invoke(MethodHandles.lookup(), op, parent));
 91     }
 92 
 93     @Test
 94     public void testNonGenericMethodReference() throws Throwable {
 95         var parent = new Parent();
 96         var op = Op.ofMethod(getMethod(TestMethodReferenceAccess.class, "nonGenericReference").orElseThrow()).orElseThrow();
 97         var handle = BytecodeGenerator.generate(MethodHandles.lookup(), op);
 98         Assertions.assertEquals(TestMethodReferenceAccess.nonGenericReference(parent), handle.invoke(parent));
 99         Assertions.assertEquals(TestMethodReferenceAccess.nonGenericReference(parent), Interpreter.invoke(MethodHandles.lookup(), op, parent));
100     }
101 
102     @Test
103     public void testGenericMethodReferenceWithTypeVarReceiver() throws Throwable {
104         var parent = new Parent();
105         var op = Op.ofMethod(getMethod(TestMethodReferenceAccess.class, "genericReferenceWithTypeVarReceiver").orElseThrow()).orElseThrow();
106         var handle = BytecodeGenerator.generate(MethodHandles.lookup(), op);
107         Assertions.assertEquals(TestMethodReferenceAccess.genericReferenceWithTypeVarReceiver(parent), handle.invoke(parent));
108         Assertions.assertEquals(TestMethodReferenceAccess.genericReferenceWithTypeVarReceiver(parent), Interpreter.invoke(MethodHandles.lookup(), op, parent));
109     }
110 
111     @Test
112     public void testGenericMethodReferenceWithIntersectionTypeVarReceiver() throws Throwable {
113         var parent = new Parent();
114         var op = Op.ofMethod(getMethod(TestMethodReferenceAccess.class, "genericReferenceWithIntersectionTypeVarReceiver").orElseThrow()).orElseThrow();
115         var handle = BytecodeGenerator.generate(MethodHandles.lookup(), op);
116         Assertions.assertEquals(TestMethodReferenceAccess.genericReferenceWithIntersectionTypeVarReceiver(parent), handle.invoke(parent));
117         Assertions.assertEquals(TestMethodReferenceAccess.genericReferenceWithIntersectionTypeVarReceiver(parent), Interpreter.invoke(MethodHandles.lookup(), op, parent));
118     }
119 
120     static Optional<Method> getMethod(Class<?> c, String name) {
121         return Stream.of(c.getDeclaredMethods())
122                 .filter(m -> m.getName().equals(name))
123                 .findFirst();
124     }
125 }