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 * @run testng TestLambdaOps 27 */ 28 29 import org.testng.Assert; 30 import org.testng.annotations.DataProvider; 31 import org.testng.annotations.Test; 32 33 import java.lang.reflect.code.*; 34 import java.lang.reflect.code.op.CoreOp; 35 import java.lang.reflect.code.op.CoreOp.FuncOp; 36 import java.lang.reflect.code.op.CoreOp.LambdaOp; 37 import java.lang.reflect.code.type.MethodRef; 38 import java.lang.reflect.code.interpreter.Interpreter; 39 import java.lang.invoke.MethodHandles; 40 import java.lang.reflect.Method; 41 import java.lang.runtime.CodeReflection; 42 import java.util.*; 43 import java.util.function.BiFunction; 44 import java.util.function.Function; 45 import java.util.function.IntSupplier; 46 import java.util.function.IntUnaryOperator; 47 import java.util.stream.Stream; 48 49 import static java.lang.reflect.code.op.CoreOp.*; 50 import static java.lang.reflect.code.op.CoreOp.constant; 51 import static java.lang.reflect.code.type.FunctionType.functionType; 52 import static java.lang.reflect.code.type.JavaType.INT; 53 import static java.lang.reflect.code.type.JavaType.type; 54 55 @Test 56 public class TestLambdaOps { 57 static class Builder { 58 static final MethodRef ACCEPT_METHOD = MethodRef.method(type(Builder.class), "accept", 59 INT, CoreOp.QuotedOp.QUOTED_TYPE); 60 61 static int accept(Quoted l) { 62 Assert.assertEquals(1, l.capturedValues().size()); 63 Assert.assertEquals(1, l.capturedValues().values().iterator().next()); 64 65 int r = (int) Interpreter.invoke(MethodHandles.lookup(), (Op & Op.Invokable) l.op(), 66 l.capturedValues(), 42); 67 return r; 68 } 69 } 70 71 @Test 72 public void testQuotedWithCapture() { 73 // functional type = (int)int 74 FuncOp f = func("f", functionType(INT, INT)) 75 .body(block -> { 76 Block.Parameter i = block.parameters().get(0); 77 78 // functional type = (int)int 79 // op type = ()Quoted<LambdaOp> 80 QuotedOp qop = quoted(block.parentBody(), qblock -> { 81 return lambda(qblock.parentBody(), 82 functionType(INT, INT), type(IntUnaryOperator.class)) 83 .body(lblock -> { 84 Block.Parameter li = lblock.parameters().get(0); 85 86 lblock.op(_return( 87 // capture i from function's body 88 lblock.op(add(i, li)) 89 )); 90 }); 91 }); 92 Op.Result lquoted = block.op(qop); 93 94 Op.Result or = block.op(invoke(Builder.ACCEPT_METHOD, lquoted)); 95 block.op(_return(or)); 96 }); 97 98 f.writeTo(System.out); 99 100 int ir = (int) Interpreter.invoke(MethodHandles.lookup(), f, 1); 101 Assert.assertEquals(ir, 43); 102 } 103 104 static final MethodRef INT_UNARY_OPERATOR_METHOD = MethodRef.method( 105 IntUnaryOperator.class, "applyAsInt", 106 int.class, int.class); 107 108 @Test 109 public void testWithCapture() { 110 // functional type = (int)int 111 FuncOp f = func("f", functionType(INT, INT)) 112 .body(block -> { 113 Block.Parameter i = block.parameters().get(0); 114 115 // functional type = (int)int 116 // op type = ()IntUnaryOperator 117 // captures i 118 LambdaOp lambda = lambda(block.parentBody(), 119 functionType(INT, INT), type(IntUnaryOperator.class)) 120 .body(lblock -> { 121 Block.Parameter li = lblock.parameters().get(0); 122 123 lblock.op(_return( 124 lblock.op(add(i, li)))); 125 }); 126 Op.Result fi = block.op(lambda); 127 128 Op.Result fortyTwo = block.op(constant(INT, 42)); 129 Op.Result or = block.op(invoke(INT_UNARY_OPERATOR_METHOD, fi, fortyTwo)); 130 block.op(_return(or)); 131 }); 132 133 f.writeTo(System.out); 134 135 int ir = (int) Interpreter.invoke(MethodHandles.lookup(), f, 1); 136 Assert.assertEquals(ir, 43); 137 } 138 139 static int f(int i) { 140 IntUnaryOperator fi = li -> { 141 return i + li; 142 }; 143 144 int fortyTwo = 42; 145 int or = fi.applyAsInt(fortyTwo); 146 return or; 147 } 148 149 @Test 150 public void testQuotableModel() { 151 Quotable quotable = (Runnable & Quotable) () -> {}; 152 Op qop = quotable.quoted().op(); 153 Op top = qop.ancestorBody().parentOp().ancestorBody().parentOp(); 154 Assert.assertTrue(top instanceof CoreOp.FuncOp); 155 156 CoreOp.FuncOp fop = (CoreOp.FuncOp) top; 157 Assert.assertEquals(type(Quoted.class), fop.invokableType().returnType()); 158 } 159 160 @FunctionalInterface 161 public interface QuotableIntSupplier extends IntSupplier, Quotable { 162 } 163 164 @CodeReflection 165 static QuotableIntSupplier quote(int i) { 166 QuotableIntSupplier s = () -> i; 167 return s; 168 } 169 170 @Test 171 public void testQuote() { 172 FuncOp g = getFuncOp("quote"); 173 g.writeTo(System.out); 174 175 { 176 QuotableIntSupplier op = (QuotableIntSupplier) Interpreter.invoke(MethodHandles.lookup(), g, 42); 177 Assert.assertEquals(op.getAsInt(), 42); 178 179 Quoted q = op.quoted(); 180 q.op().writeTo(System.out); 181 Assert.assertEquals(q.capturedValues().size(), 1); 182 Assert.assertEquals(((Var<?>)q.capturedValues().values().iterator().next()).value(), 42); 183 184 int r = (int) Interpreter.invoke(MethodHandles.lookup(), (LambdaOp) q.op(), q.capturedValues(), List.of()); 185 Assert.assertEquals(r, 42); 186 187 Map<Value, Object> cvs = Map.of( 188 q.capturedValues().keySet().iterator().next(), 189 CoreOp.Var.of(0) 190 ); 191 r = (int) Interpreter.invoke(MethodHandles.lookup(), (LambdaOp) q.op(), cvs, List.of()); 192 Assert.assertEquals(r, 0); 193 } 194 195 { 196 QuotableIntSupplier op = quote(42); 197 Assert.assertEquals(op.getAsInt(), 42); 198 199 Quoted q = op.quoted(); 200 q.op().writeTo(System.out); 201 System.out.print(q.capturedValues().values()); 202 Assert.assertEquals(q.capturedValues().size(), 1); 203 Assert.assertEquals(((Var<?>)q.capturedValues().values().iterator().next()).value(), 42); 204 205 int r = (int) Interpreter.invoke(MethodHandles.lookup(), (LambdaOp) q.op(), q.capturedValues(), List.of()); 206 Assert.assertEquals(r, 42); 207 208 Map<Value, Object> cvs = Map.of( 209 q.capturedValues().keySet().iterator().next(), 210 CoreOp.Var.of(0) 211 ); 212 r = (int) Interpreter.invoke(MethodHandles.lookup(), (LambdaOp) q.op(), cvs, List.of()); 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 m.getCodeModel().get(); 267 } 268 }