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 }