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 * @summary Smoke test for captured values in quotable lambdas.
27 * @modules jdk.incubator.code
28 * @run junit TestCaptureQuotable
29 */
30
31 import org.junit.jupiter.api.Test;
32 import org.junit.jupiter.params.ParameterizedTest;
33 import org.junit.jupiter.params.provider.MethodSource;
34
35 import jdk.incubator.code.dialect.core.CoreOp.Var;
36 import jdk.incubator.code.Op;
37 import jdk.incubator.code.Quotable;
38 import jdk.incubator.code.Quoted;
39 import jdk.incubator.code.interpreter.Interpreter;
40 import java.lang.invoke.MethodHandles;
41 import java.util.ArrayList;
42 import java.util.Iterator;
43 import java.util.List;
44 import java.util.function.IntSupplier;
45 import java.util.function.IntUnaryOperator;
46 import java.util.function.ToIntFunction;
47 import java.util.stream.IntStream;
48
49 import static org.junit.jupiter.api.Assertions.assertEquals;
50
51 public class TestCaptureQuotable {
52
53 @ParameterizedTest
54 @MethodSource("ints")
55 public void testCaptureIntParam(int x) {
56 Quotable quotable = (Quotable & IntUnaryOperator)y -> x + y;
57 Quoted quoted = Op.ofQuotable(quotable).get();
58 assertEquals(1, quoted.capturedValues().size());
59 assertEquals(x, ((Var)quoted.capturedValues().values().iterator().next()).value());
60 List<Object> arguments = new ArrayList<>();
61 arguments.add(1);
62 arguments.addAll(quoted.capturedValues().values());
63 int res = (int)Interpreter.invoke(MethodHandles.lookup(), (Op & Op.Invokable) quoted.op(),
64 arguments);
65 assertEquals(x + 1, res);
66 }
67
68 @Test
69 public void testCaptureThisRefAndIntConstant() {
70 final int x = 100;
71 String hello = "hello";
72 Quotable quotable = (Quotable & ToIntFunction<Number>)y -> y.intValue() + hashCode() + hello.length() + x;
73 Quoted quoted = Op.ofQuotable(quotable).get();
74 assertEquals(3, quoted.capturedValues().size());
75 Iterator<Object> it = quoted.capturedValues().values().iterator();
76 assertEquals(this, it.next());
77 assertEquals(hello, ((Var)it.next()).value());
78 assertEquals(x, ((Var)it.next()).value());
79 List<Object> arguments = new ArrayList<>();
80 arguments.add(1);
81 arguments.addAll(quoted.capturedValues().values());
82 int res = (int)Interpreter.invoke(MethodHandles.lookup(), (Op & Op.Invokable) quoted.op(),
83 arguments);
84 assertEquals(x + 1 + hashCode() + hello.length(), res);
85 }
86
87 @Test
88 public void testCaptureThisInInvocationArg() {
89 Quotable quotable = (Quotable & ToIntFunction<Number>)y -> y.intValue() + Integer.valueOf(hashCode());
90 Quoted quoted = Op.ofQuotable(quotable).get();
91 assertEquals(1, quoted.capturedValues().size());
92 Iterator<Object> it = quoted.capturedValues().values().iterator();
93 assertEquals(this, it.next());
94 List<Object> arguments = new ArrayList<>();
95 arguments.add(1);
96 arguments.addAll(quoted.capturedValues().values());
97 int res = (int)Interpreter.invoke(MethodHandles.lookup(), (Op & Op.Invokable) quoted.op(),
98 arguments);
99 assertEquals(1 + hashCode(), res);
100 }
101
102 record R(int i) {}
103
104 @Test
105 public void testCaptureThisInNewArg() {
106 Quotable quotable = (Quotable & ToIntFunction<Number>)y -> y.intValue() + new R(hashCode()).i;
107 Quoted quoted = Op.ofQuotable(quotable).get();
108 assertEquals(1, quoted.capturedValues().size());
109 Iterator<Object> it = quoted.capturedValues().values().iterator();
110 assertEquals(this, it.next());
111 List<Object> arguments = new ArrayList<>();
112 arguments.add(1);
113 arguments.addAll(quoted.capturedValues().values());
114 int res = (int)Interpreter.invoke(MethodHandles.lookup(), (Op & Op.Invokable) quoted.op(),
115 arguments);
116 assertEquals(1 + hashCode(), res);
117 }
118
119 @Test
120 public void testCaptureMany() {
121 int[] ia = new int[8];
122 int i1 = ia[0] = 0;
123 int i2 = ia[1] = 1;
124 int i3 = ia[2] = 2;
125 int i4 = ia[3] = 3;
126 int i5 = ia[4] = 4;
127 int i6 = ia[5] = 5;
128 int i7 = ia[6] = 6;
129 int i8 = ia[7] = 7;
130
131 Quotable quotable = (Quotable & IntSupplier) () -> i1 + i2 + i3 + i4 + i5 + i6 + i7 + i8;
132 Quoted quoted = Op.ofQuotable(quotable).get();
133 assertEquals(ia.length, quoted.capturedValues().size());
134 assertEquals(new ArrayList<>(quoted.capturedValues().keySet()), quoted.op().capturedValues());
135 Iterator<Object> it = quoted.capturedValues().values().iterator();
136 int i = 0;
137 while (it.hasNext()) {
138 int actual = (int) ((Var)it.next()).value();
139 assertEquals(ia[i++], actual);
140 }
141 }
142
143 static class Context {
144 final int x;
145
146 Context(int x) {
147 this.x = x;
148 }
149
150 Quotable quotable() {
151 return (Quotable & IntUnaryOperator) y -> x + y;
152 }
153 }
154
155 @ParameterizedTest
156 @MethodSource("ints")
157 public void testCaptureIntField(int x) {
158 Context context = new Context(x);
159 Quotable quotable = context.quotable();
160 Quoted quoted = Op.ofQuotable(quotable).get();
161 assertEquals(1, quoted.capturedValues().size());
162 assertEquals(context, quoted.capturedValues().values().iterator().next());
163 List<Object> arguments = new ArrayList<>();
164 arguments.add(1);
165 arguments.addAll(quoted.capturedValues().values());
166 int res = (int)Interpreter.invoke(MethodHandles.lookup(), (Op & Op.Invokable) quoted.op(),
167 arguments);
168 assertEquals(x + 1, res);
169 }
170
171 public static IntStream ints() {
172 return IntStream.range(0, 50);
173 }
174
175 @ParameterizedTest
176 @MethodSource("ints")
177 public void testCaptureReferenceReceiver(int i) {
178 int prevCount = Box.count;
179 Quotable quotable = (Quotable & IntUnaryOperator)new Box(i)::add;
180 Quoted quoted = Op.ofQuotable(quotable).get();
181 assertEquals(prevCount + 1, Box.count); // no duplicate receiver computation!
182 assertEquals(1, quoted.capturedValues().size());
183 assertEquals(i, ((Box)((Var)quoted.capturedValues().values().iterator().next()).value()).i);
184 List<Object> arguments = new ArrayList<>();
185 arguments.add(1);
186 arguments.addAll(quoted.capturedValues().values());
187 int res = (int)Interpreter.invoke(MethodHandles.lookup(), (Op & Op.Invokable) quoted.op(),
188 arguments);
189 assertEquals(i + 1, res);
190 }
191
192 record Box(int i) {
193
194 static int count = 0;
195
196 Box {
197 count++; // keep track of side-effects
198 }
199
200 int add(int i) {
201 return i + this.i;
202 }
203 }
204 }