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