1 /*
  2  * Copyright (c) 2024, 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 /*
 25  * @test
 26  * @modules jdk.incubator.code
 27  * @run testng TestLambdaOps
 28  */
 29 
 30 import org.testng.Assert;
 31 import org.testng.annotations.DataProvider;
 32 import org.testng.annotations.Test;
 33 
 34 import jdk.incubator.code.*;
 35 import jdk.incubator.code.op.CoreOp;
 36 import jdk.incubator.code.op.CoreOp.FuncOp;
 37 import jdk.incubator.code.op.CoreOp.LambdaOp;
 38 import jdk.incubator.code.type.MethodRef;
 39 import jdk.incubator.code.interpreter.Interpreter;
 40 import java.lang.invoke.MethodHandles;
 41 import java.lang.reflect.Method;
 42 import jdk.incubator.code.CodeReflection;
 43 import java.util.*;
 44 import java.util.function.BiFunction;
 45 import java.util.function.Function;
 46 import java.util.function.IntSupplier;
 47 import java.util.function.IntUnaryOperator;
 48 import java.util.stream.Stream;
 49 
 50 import static jdk.incubator.code.op.CoreOp.*;
 51 import static jdk.incubator.code.op.CoreOp.constant;
 52 import static jdk.incubator.code.type.FunctionType.functionType;
 53 import static jdk.incubator.code.type.JavaType.INT;
 54 import static jdk.incubator.code.type.JavaType.type;
 55 
 56 @Test
 57 public class TestLambdaOps {
 58     static class Builder {
 59         static final MethodRef ACCEPT_METHOD = MethodRef.method(type(Builder.class), "accept",
 60                 INT, CoreOp.QuotedOp.QUOTED_TYPE);
 61 
 62         static int accept(Quoted l) {
 63             Assert.assertEquals(1, l.capturedValues().size());
 64             Assert.assertEquals(1, l.capturedValues().values().iterator().next());
 65 
 66             List<Object> arguments = new ArrayList<>();
 67             arguments.add(42);
 68             arguments.addAll(l.capturedValues().values());
 69             int r = (int) Interpreter.invoke(MethodHandles.lookup(), (Op & Op.Invokable) l.op(),
 70                     arguments);
 71             return r;
 72         }
 73     }
 74 
 75     @Test
 76     public void testQuotedWithCapture() {
 77         // functional type = (int)int
 78         FuncOp f = func("f", functionType(INT, INT))
 79                 .body(block -> {
 80                     Block.Parameter i = block.parameters().get(0);
 81 
 82                     // functional type = (int)int
 83                     // op type = ()Quoted<LambdaOp>
 84                     QuotedOp qop = quoted(block.parentBody(), qblock -> {
 85                         return lambda(qblock.parentBody(),
 86                                 functionType(INT, INT), type(IntUnaryOperator.class))
 87                                 .body(lblock -> {
 88                                     Block.Parameter li = lblock.parameters().get(0);
 89 
 90                                     lblock.op(_return(
 91                                             // capture i from function's body
 92                                             lblock.op(add(i, li))
 93                                     ));
 94                                 });
 95                     });
 96                     Op.Result lquoted = block.op(qop);
 97 
 98                     Op.Result or = block.op(invoke(Builder.ACCEPT_METHOD, lquoted));
 99                     block.op(_return(or));
100                 });
101 
102         f.writeTo(System.out);
103 
104         int ir = (int) Interpreter.invoke(MethodHandles.lookup(), f, 1);
105         Assert.assertEquals(ir, 43);
106     }
107 
108     static final MethodRef INT_UNARY_OPERATOR_METHOD = MethodRef.method(
109             IntUnaryOperator.class, "applyAsInt",
110             int.class, int.class);
111 
112     @Test
113     public void testWithCapture() {
114         // functional type = (int)int
115         FuncOp f = func("f", functionType(INT, INT))
116                 .body(block -> {
117                     Block.Parameter i = block.parameters().get(0);
118 
119                     // functional type = (int)int
120                     // op type = ()IntUnaryOperator
121                     //   captures i
122                     LambdaOp lambda = lambda(block.parentBody(),
123                             functionType(INT, INT), type(IntUnaryOperator.class))
124                             .body(lblock -> {
125                                 Block.Parameter li = lblock.parameters().get(0);
126 
127                                 lblock.op(_return(
128                                         lblock.op(add(i, li))));
129                             });
130                     Op.Result fi = block.op(lambda);
131 
132                     Op.Result fortyTwo = block.op(constant(INT, 42));
133                     Op.Result or = block.op(invoke(INT_UNARY_OPERATOR_METHOD, fi, fortyTwo));
134                     block.op(_return(or));
135                 });
136 
137         f.writeTo(System.out);
138 
139         int ir = (int) Interpreter.invoke(MethodHandles.lookup(), f, 1);
140         Assert.assertEquals(ir, 43);
141     }
142 
143     static int f(int i) {
144         IntUnaryOperator fi = li -> {
145             return i + li;
146         };
147 
148         int fortyTwo = 42;
149         int or = fi.applyAsInt(fortyTwo);
150         return or;
151     }
152 
153     @Test
154     public void testQuotableModel() {
155         Quotable quotable = (Runnable & Quotable) () -> {};
156         Op qop = quotable.quoted().op();
157         Op top = qop.ancestorBody().parentOp().ancestorBody().parentOp();
158         Assert.assertTrue(top instanceof CoreOp.FuncOp);
159 
160         CoreOp.FuncOp fop = (CoreOp.FuncOp) top;
161         Assert.assertEquals(type(Quoted.class), fop.invokableType().returnType());
162     }
163 
164     @FunctionalInterface
165     public interface QuotableIntSupplier extends IntSupplier, Quotable {
166     }
167 
168     @CodeReflection
169     static QuotableIntSupplier quote(int i) {
170         QuotableIntSupplier s = () -> i;
171         return s;
172     }
173 
174     @Test
175     public void testQuote() {
176         FuncOp g = getFuncOp("quote");
177         g.writeTo(System.out);
178 
179         {
180             QuotableIntSupplier op = (QuotableIntSupplier) Interpreter.invoke(MethodHandles.lookup(), g, 42);
181             Assert.assertEquals(op.getAsInt(), 42);
182 
183             Quoted q = op.quoted();
184             q.op().writeTo(System.out);
185             Assert.assertEquals(q.capturedValues().size(), 1);
186             Assert.assertEquals(((Var<?>)q.capturedValues().values().iterator().next()).value(), 42);
187 
188             int r = (int) Interpreter.invoke(MethodHandles.lookup(), (LambdaOp) q.op(),
189                     new ArrayList<>(q.capturedValues().sequencedValues()));
190             Assert.assertEquals(r, 42);
191 
192             r = (int) Interpreter.invoke(MethodHandles.lookup(), (LambdaOp) q.op(),
193                     List.of(CoreOp.Var.of(0)));
194             Assert.assertEquals(r, 0);
195         }
196 
197         {
198             QuotableIntSupplier op = quote(42);
199             Assert.assertEquals(op.getAsInt(), 42);
200 
201             Quoted q = op.quoted();
202             q.op().writeTo(System.out);
203             System.out.print(q.capturedValues().values());
204             Assert.assertEquals(q.capturedValues().size(), 1);
205             Assert.assertEquals(((Var<?>)q.capturedValues().values().iterator().next()).value(), 42);
206 
207             int r = (int) Interpreter.invoke(MethodHandles.lookup(), (LambdaOp) q.op(),
208                     new ArrayList<>(q.capturedValues().sequencedValues()));
209             Assert.assertEquals(r, 42);
210 
211             r = (int) Interpreter.invoke(MethodHandles.lookup(), (LambdaOp) q.op(),
212                     List.of(CoreOp.Var.of(0)));
213             Assert.assertEquals(r, 0);
214         }
215     }
216 
217 
218     interface QuotableIntUnaryOperator extends IntUnaryOperator, Quotable {}
219 
220     interface QuotableFunction<T, R> extends Function<T, R>, Quotable {}
221 
222     interface QuotableBiFunction<T, U, R> extends BiFunction<T, U, R>, Quotable {}
223 
224     @DataProvider
225     Iterator<Quotable> methodRefLambdas() {
226         return List.of(
227                 (QuotableIntUnaryOperator) TestLambdaOps::m1,
228                 (QuotableIntUnaryOperator) TestLambdaOps::m2,
229                 (QuotableFunction<Integer, Integer>) TestLambdaOps::m1,
230                 (QuotableFunction<Integer, Integer>) TestLambdaOps::m2,
231                 (QuotableIntUnaryOperator) this::m3,
232                 (QuotableBiFunction<TestLambdaOps, Integer, Integer>) TestLambdaOps::m4
233         ).iterator();
234     }
235 
236     @Test(dataProvider = "methodRefLambdas")
237     public void testIsMethodReference(Quotable q) {
238         Quoted quoted = q.quoted();
239         CoreOp.LambdaOp lop = (CoreOp.LambdaOp) quoted.op();
240         Assert.assertTrue(lop.methodReference().isPresent());
241     }
242 
243     static int m1(int i) {
244         return i;
245     }
246 
247     static Integer m2(Integer i) {
248         return i;
249     }
250 
251     int m3(int i) {
252         return i;
253     }
254 
255     static int m4(TestLambdaOps tl, int i) {
256         return i;
257     }
258 
259 
260     static CoreOp.FuncOp getFuncOp(String name) {
261         Optional<Method> om = Stream.of(TestLambdaOps.class.getDeclaredMethods())
262                 .filter(m -> m.getName().equals(name))
263                 .findFirst();
264 
265         Method m = om.get();
266         return Op.ofMethod(m).get();
267     }
268 }