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 TestClosureOps
 27  */
 28 
 29 import org.testng.Assert;
 30 import org.testng.annotations.Test;
 31 
 32 import java.lang.reflect.code.Block;
 33 import java.lang.reflect.code.op.CoreOp;
 34 import java.lang.reflect.code.Op;
 35 import java.lang.reflect.code.Quoted;
 36 import java.lang.reflect.code.type.MethodRef;
 37 import java.lang.reflect.code.interpreter.Interpreter;
 38 import java.lang.invoke.MethodHandles;
 39 import java.lang.reflect.code.type.JavaType;
 40 
 41 import static java.lang.reflect.code.op.CoreOp._return;
 42 import static java.lang.reflect.code.op.CoreOp.add;
 43 import static java.lang.reflect.code.op.CoreOp.closure;
 44 import static java.lang.reflect.code.op.CoreOp.closureCall;
 45 import static java.lang.reflect.code.op.CoreOp.constant;
 46 import static java.lang.reflect.code.op.CoreOp.func;
 47 import static java.lang.reflect.code.op.CoreOp.quoted;
 48 import static java.lang.reflect.code.type.FunctionType.functionType;
 49 import static java.lang.reflect.code.type.JavaType.INT;
 50 import static java.lang.reflect.code.type.JavaType.type;
 51 
 52 public class TestClosureOps {
 53 
 54     static class Builder {
 55         static final MethodRef ACCEPT_METHOD = MethodRef.method(type(TestClosureOps.Builder.class), "accept",
 56                 INT, CoreOp.QuotedOp.QUOTED_TYPE);
 57 
 58         static int accept(Quoted c) {
 59             Assert.assertEquals(1, c.capturedValues().size());
 60             Assert.assertEquals(1, c.capturedValues().values().iterator().next());
 61 
 62             int r = (int) Interpreter.invoke(MethodHandles.lookup(), (Op & Op.Invokable) c.op(),
 63                     c.capturedValues(), 42);
 64             return r;
 65         }
 66     }
 67 
 68     @Test
 69     public void testQuotedWithCapture() {
 70         // functional type = (int)int
 71         CoreOp.FuncOp f = func("f", functionType(INT, INT))
 72                 .body(block -> {
 73                     Block.Parameter i = block.parameters().get(0);
 74 
 75                     // functional type = (int)int
 76                     // op descriptor = ()Quoted<ClosureOp>
 77                     CoreOp.QuotedOp qop = quoted(block.parentBody(), qblock -> {
 78                         return closure(qblock.parentBody(), functionType(INT, INT))
 79                                 .body(cblock -> {
 80                                     Block.Parameter ci = cblock.parameters().get(0);
 81 
 82                                     cblock.op(_return(
 83                                             // capture i from function's body
 84                                             cblock.op(add(i, ci))
 85                                     ));
 86                                 });
 87                     });
 88                     Op.Result cquoted = block.op(qop);
 89 
 90                     Op.Result or = block.op(CoreOp.invoke(TestClosureOps.Builder.ACCEPT_METHOD, cquoted));
 91                     block.op(_return(or));
 92                 });
 93 
 94         f.writeTo(System.out);
 95 
 96         int ir = (int) Interpreter.invoke(MethodHandles.lookup(), f, 1);
 97         Assert.assertEquals(ir, 43);
 98     }
 99 
100     @Test
101     public void testWithCapture() {
102         // functional type = (int)int
103         CoreOp.FuncOp f = func("f", functionType(INT, INT))
104                 .body(block -> {
105                     Block.Parameter i = block.parameters().get(0);
106 
107                     // functional type = (int)int
108                     //   captures i
109                     CoreOp.ClosureOp closure = CoreOp.closure(block.parentBody(),
110                                     functionType(INT, INT))
111                             .body(cblock -> {
112                                 Block.Parameter ci = cblock.parameters().get(0);
113 
114                                 cblock.op(_return(
115                                         cblock.op(add(i, ci))));
116                             });
117                     Op.Result c = block.op(closure);
118 
119                     Op.Result fortyTwo = block.op(constant(INT, 42));
120                     Op.Result or = block.op(closureCall(c, fortyTwo));
121                     block.op(_return(or));
122                 });
123 
124         f.writeTo(System.out);
125 
126         int ir = (int) Interpreter.invoke(MethodHandles.lookup(), f, 1);
127         Assert.assertEquals(ir, 43);
128     }
129 
130     @Test
131     public void testQuotableModel() {
132         Quoted quoted = () -> {};
133         Op qop = quoted.op();
134         Op top = qop.ancestorBody().parentOp().ancestorBody().parentOp();
135         Assert.assertTrue(top instanceof CoreOp.FuncOp);
136 
137         CoreOp.FuncOp fop = (CoreOp.FuncOp) top;
138         Assert.assertEquals(JavaType.type(Quoted.class), fop.invokableType().returnType());
139     }
140 }