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