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 }