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