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