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