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 junit TestLambdaOps
 28  * @run main Unreflect TestLambdaOps
 29  * @run junit TestLambdaOps
 30  */
 31 
 32 import jdk.incubator.code.*;
 33 import jdk.incubator.code.Reflect;
 34 import jdk.incubator.code.dialect.core.CoreOp;
 35 import jdk.incubator.code.dialect.java.JavaOp;
 36 import jdk.incubator.code.dialect.java.JavaOp.LambdaOp;
 37 import jdk.incubator.code.dialect.java.MethodRef;
 38 import jdk.incubator.code.interpreter.Interpreter;
 39 import org.junit.jupiter.api.Assertions;
 40 import org.junit.jupiter.api.Test;
 41 
 42 import java.lang.invoke.MethodHandles;
 43 import java.lang.reflect.Method;
 44 import java.util.ArrayList;
 45 import java.util.List;
 46 import java.util.Optional;
 47 import java.util.function.*;
 48 import java.util.stream.Stream;
 49 
 50 import static jdk.incubator.code.dialect.core.CoreOp.*;
 51 import static jdk.incubator.code.dialect.core.CoreType.functionType;
 52 import static jdk.incubator.code.dialect.java.JavaType.*;
 53 
 54 public class TestLambdaOps {
 55     static class Builder {
 56         static final MethodRef ACCEPT_METHOD = MethodRef.method(type(Builder.class), "accept",
 57                 INT, CoreOp.QuotedOp.QUOTED_OP_TYPE);
 58 
 59         static int accept(Quoted<?> l) {
 60             Assertions.assertEquals(l.capturedValues().size(), 1);
 61             Assertions.assertEquals(l.capturedValues().values().iterator().next(), 1);
 62 
 63             List<Object> arguments = new ArrayList<>();
 64             arguments.add(42);
 65             arguments.addAll(l.capturedValues().values());
 66             int r = (int) Interpreter.invoke(MethodHandles.lookup(), (Op & Op.Invokable) l.op(),
 67                     arguments);
 68             return r;
 69         }
 70     }
 71 
 72     @Test
 73     public void testQuotedWithCapture() {
 74         // functional type = (int)int
 75         FuncOp f = func("f", functionType(INT, INT))
 76                 .body(block -> {
 77                     Block.Parameter i = block.parameters().get(0);
 78 
 79                     // functional type = (int)int
 80                     // op type = ()Quoted<LambdaOp>
 81                     QuotedOp qop = quoted(block.parentBody(), qblock -> {
 82                         return JavaOp.lambda(qblock.parentBody(),
 83                                 functionType(INT, INT), type(IntUnaryOperator.class))
 84                                 .body(lblock -> {
 85                                     Block.Parameter li = lblock.parameters().get(0);
 86 
 87                                     lblock.op(return_(
 88                                             // capture i from function's body
 89                                             lblock.op(JavaOp.add(i, li))
 90                                     ));
 91                                 });
 92                     });
 93                     Op.Result lquoted = block.op(qop);
 94 
 95                     Op.Result or = block.op(JavaOp.invoke(Builder.ACCEPT_METHOD, lquoted));
 96                     block.op(return_(or));
 97                 });
 98 
 99         System.out.println(f.toText());
100 
101         int ir = (int) Interpreter.invoke(MethodHandles.lookup(), f, 1);
102         Assertions.assertEquals(43, ir);
103     }
104 
105     static final MethodRef INT_UNARY_OPERATOR_METHOD = MethodRef.method(
106             IntUnaryOperator.class, "applyAsInt",
107             int.class, int.class);
108 
109     @Test
110     public void testWithCapture() {
111         // functional type = (int)int
112         FuncOp f = func("f", functionType(INT, INT))
113                 .body(block -> {
114                     Block.Parameter i = block.parameters().get(0);
115 
116                     // functional type = (int)int
117                     // op type = ()IntUnaryOperator
118                     //   captures i
119                     LambdaOp lambda = JavaOp.lambda(block.parentBody(),
120                             functionType(INT, INT), type(IntUnaryOperator.class))
121                             .body(lblock -> {
122                                 Block.Parameter li = lblock.parameters().get(0);
123 
124                                 lblock.op(return_(
125                                         lblock.op(JavaOp.add(i, li))));
126                             });
127                     Op.Result fi = block.op(lambda);
128 
129                     Op.Result fortyTwo = block.op(constant(INT, 42));
130                     Op.Result or = block.op(JavaOp.invoke(INT_UNARY_OPERATOR_METHOD, fi, fortyTwo));
131                     block.op(return_(or));
132                 });
133 
134         System.out.println(f.toText());
135 
136         int ir = (int) Interpreter.invoke(MethodHandles.lookup(), f, 1);
137         Assertions.assertEquals(43, ir);
138     }
139 
140     static int f(int i) {
141         IntUnaryOperator fi = li -> {
142             return i + li;
143         };
144 
145         int fortyTwo = 42;
146         int or = fi.applyAsInt(fortyTwo);
147         return or;
148     }
149 
150     @Test
151     public void testLambdaModel() {
152         Runnable r = (@Reflect Runnable) () -> {};
153         Op qop = Op.ofLambda(r).get().op();
154         Op top = qop.ancestorOp().ancestorOp();
155         Assertions.assertTrue(top instanceof CoreOp.FuncOp);
156 
157         CoreOp.FuncOp fop = (CoreOp.FuncOp) top;
158         System.out.println(fop.toText());
159         Assertions.assertEquals(fop.invokableType().returnType(), parameterized(type(Quoted.class), type(Op.class)));
160     }
161 
162     @Reflect
163     static IntSupplier quote(int i) {
164         return () -> i;
165     }
166 
167     @Test
168     public void testQuoted() {
169         FuncOp g = getFuncOp("quote");
170         System.out.println(g.toText());
171 
172         {
173             IntSupplier op = (IntSupplier) Interpreter.invoke(MethodHandles.lookup(), g, 42);
174             Assertions.assertEquals(42, op.getAsInt());
175 
176             Quoted<LambdaOp> q = Op.ofLambda(op).get();
177             System.out.println(q.op().toText());
178             Assertions.assertEquals(1, q.capturedValues().size());
179             Assertions.assertEquals(42, ((Var<?>)q.capturedValues().values().iterator().next()).value());
180 
181             int r = (int) Interpreter.invoke(MethodHandles.lookup(), q.op(),
182                     new ArrayList<>(q.capturedValues().sequencedValues()));
183             Assertions.assertEquals(42, r);
184 
185             r = (int) Interpreter.invoke(MethodHandles.lookup(), q.op(),
186                     List.of(CoreOp.Var.of(0)));
187             Assertions.assertEquals(0, r);
188         }
189 
190         {
191             IntSupplier op = quote(42);
192             Assertions.assertEquals(42, op.getAsInt());
193 
194             Quoted<LambdaOp> q = Op.ofLambda(op).get();
195             System.out.println(q.op().toText());
196             System.out.print(q.capturedValues().values());
197             Assertions.assertEquals(1, q.capturedValues().size());
198             Assertions.assertEquals(42, ((Var<?>)q.capturedValues().values().iterator().next()).value());
199 
200             int r = (int) Interpreter.invoke(MethodHandles.lookup(), q.op(),
201                     new ArrayList<>(q.capturedValues().sequencedValues()));
202             Assertions.assertEquals(42, r);
203 
204             r = (int) Interpreter.invoke(MethodHandles.lookup(), q.op(),
205                     List.of(CoreOp.Var.of(0)));
206             Assertions.assertEquals(0, r);
207         }
208     }
209 
210     static CoreOp.FuncOp getFuncOp(String name) {
211         Optional<Method> om = Stream.of(TestLambdaOps.class.getDeclaredMethods())
212                 .filter(m -> m.getName().equals(name))
213                 .findFirst();
214 
215         Method m = om.get();
216         return Op.ofMethod(m).get();
217     }
218 
219     @Test
220     public void testToFuncOp() {
221         int a = 4, b = 3, c = 6;
222         IntUnaryOperator f = (@Reflect IntUnaryOperator) (d) -> {d += 2 * a + (b % 2) + (int) Math.exp(c); return d;};
223         LambdaOp qop = Op.ofLambda(f).get().op();
224         FuncOp funcOp = qop.toFuncOp(null);
225         int funcOpRes = (int) Interpreter.invoke(MethodHandles.lookup(), funcOp, 1, 4, 3, 6);
226         int lambdaRes = f.applyAsInt(1);
227         Assertions.assertEquals(funcOpRes, lambdaRes);
228     }
229 }