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 * @enablePreview
27 * @requires jdk.foreign.linker != "UNSUPPORTED"
28 * @run testng/othervm --enable-native-access=ALL-UNNAMED StdLibTest
29 */
30
31 import java.lang.invoke.MethodHandle;
32 import java.lang.invoke.MethodHandles;
33 import java.lang.invoke.MethodType;
34 import java.time.Instant;
35 import java.time.LocalDateTime;
36 import java.time.ZoneOffset;
37 import java.time.ZonedDateTime;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.Collections;
41 import java.util.LinkedHashSet;
42 import java.util.List;
43 import java.util.Set;
44 import java.util.function.Function;
45 import java.util.stream.Collectors;
46 import java.util.stream.Stream;
47
48 import java.lang.foreign.*;
49
50 import org.testng.annotations.*;
51
52 import static org.testng.Assert.*;
53
54 @Test
55 public class StdLibTest extends NativeTestHelper {
56
57 final static Linker abi = Linker.nativeLinker();
58
59 private StdLibHelper stdLibHelper = new StdLibHelper();
60
61 @Test(dataProvider = "stringPairs")
62 void test_strcat(String s1, String s2) throws Throwable {
63 assertEquals(stdLibHelper.strcat(s1, s2), s1 + s2);
64 }
65
66 @Test(dataProvider = "stringPairs")
67 void test_strcmp(String s1, String s2) throws Throwable {
68 assertEquals(Math.signum(stdLibHelper.strcmp(s1, s2)), Math.signum(s1.compareTo(s2)));
69 }
70
71 @Test(dataProvider = "strings")
72 void test_puts(String s) throws Throwable {
73 assertTrue(stdLibHelper.puts(s) >= 0);
74 }
104 Arrays.sort(input);
105 assertEquals(sorted, input);
106 }
107 }
108
109 @Test
110 void test_rand() throws Throwable {
111 int val = stdLibHelper.rand();
112 for (int i = 0 ; i < 100 ; i++) {
113 int newVal = stdLibHelper.rand();
114 if (newVal != val) {
115 return; //ok
116 }
117 val = newVal;
118 }
119 fail("All values are the same! " + val);
120 }
121
122 @Test(dataProvider = "printfArgs")
123 void test_printf(List<PrintfArg> args) throws Throwable {
124 String formatArgs = args.stream()
125 .map(a -> a.format)
126 .collect(Collectors.joining(","));
127
128 String formatString = "hello(" + formatArgs + ")\n";
129
130 String expected = String.format(formatString, args.stream()
131 .map(a -> a.javaValue).toArray());
132
133 int found = stdLibHelper.printf(formatString, args);
134 assertEquals(found, expected.length());
135 }
136
137 @Test
138 void testSystemLibraryBadLookupName() {
139 assertTrue(LINKER.defaultLookup().find("strlen\u0000foobar").isEmpty());
140 }
141
142 static class StdLibHelper {
143
144 final static MethodHandle strcat = abi.downcallHandle(abi.defaultLookup().find("strcat").get(),
145 FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER));
146
147 final static MethodHandle strcmp = abi.downcallHandle(abi.defaultLookup().find("strcmp").get(),
148 FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
149
150 final static MethodHandle puts = abi.downcallHandle(abi.defaultLookup().find("puts").get(),
151 FunctionDescriptor.of(C_INT, C_POINTER));
152
153 final static MethodHandle strlen = abi.downcallHandle(abi.defaultLookup().find("strlen").get(),
154 FunctionDescriptor.of(C_INT, C_POINTER));
155
156 final static MethodHandle gmtime = abi.downcallHandle(abi.defaultLookup().find("gmtime").get(),
157 FunctionDescriptor.of(C_POINTER.withTargetLayout(Tm.LAYOUT), C_POINTER));
158
159 final static MethodHandle qsort = abi.downcallHandle(abi.defaultLookup().find("qsort").get(),
160 FunctionDescriptor.ofVoid(C_POINTER, C_LONG_LONG, C_LONG_LONG, C_POINTER));
161
162 final static FunctionDescriptor qsortComparFunction = FunctionDescriptor.of(C_INT,
163 C_POINTER.withTargetLayout(C_INT), C_POINTER.withTargetLayout(C_INT));
164
165 final static MethodHandle qsortCompar;
166
167 final static MethodHandle rand = abi.downcallHandle(abi.defaultLookup().find("rand").get(),
168 FunctionDescriptor.of(C_INT));
169
170 final static MethodHandle vprintf = abi.downcallHandle(abi.defaultLookup().find("vprintf").get(),
171 FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
172
173 final static MemorySegment printfAddr = abi.defaultLookup().find("printf").get();
174
175 final static FunctionDescriptor printfBase = FunctionDescriptor.of(C_INT, C_POINTER);
176
177 static {
178 try {
179 //qsort upcall handle
180 qsortCompar = MethodHandles.lookup().findStatic(StdLibTest.StdLibHelper.class, "qsortCompare",
181 qsortComparFunction.toMethodType());
182 } catch (ReflectiveOperationException ex) {
183 throw new IllegalStateException(ex);
184 }
185 }
186
187 String strcat(String s1, String s2) throws Throwable {
188 try (var arena = Arena.ofConfined()) {
189 MemorySegment buf = arena.allocate(s1.length() + s2.length() + 1);
190 buf.setUtf8String(0, s1);
191 MemorySegment other = arena.allocateUtf8String(s2);
192 return ((MemorySegment)strcat.invokeExact(buf, other)).getUtf8String(0);
193 }
194 }
195
196 int strcmp(String s1, String s2) throws Throwable {
197 try (var arena = Arena.ofConfined()) {
198 MemorySegment ns1 = arena.allocateUtf8String(s1);
199 MemorySegment ns2 = arena.allocateUtf8String(s2);
200 return (int)strcmp.invokeExact(ns1, ns2);
201 }
202 }
203
204 int puts(String msg) throws Throwable {
205 try (var arena = Arena.ofConfined()) {
206 MemorySegment s = arena.allocateUtf8String(msg);
207 return (int)puts.invokeExact(s);
208 }
209 }
210
211 int strlen(String msg) throws Throwable {
212 try (var arena = Arena.ofConfined()) {
213 MemorySegment s = arena.allocateUtf8String(msg);
214 return (int)strlen.invokeExact(s);
215 }
216 }
217
218 Tm gmtime(long arg) throws Throwable {
219 try (var arena = Arena.ofConfined()) {
220 MemorySegment time = arena.allocate(8);
221 time.set(C_LONG_LONG, 0, arg);
222 return new Tm((MemorySegment)gmtime.invokeExact(time));
223 }
224 }
225
226 static class Tm {
227
228 //Tm pointer should never be freed directly, as it points to shared memory
229 private final MemorySegment base;
230
231 static final MemoryLayout LAYOUT = MemoryLayout.structLayout(
232 C_INT.withName("sec"),
233 C_INT.withName("min"),
260 int mon() {
261 return base.get(C_INT, 16);
262 }
263 int year() {
264 return base.get(C_INT, 20);
265 }
266 int wday() {
267 return base.get(C_INT, 24);
268 }
269 int yday() {
270 return base.get(C_INT, 28);
271 }
272 boolean isdst() {
273 return base.get(C_BOOL, 32);
274 }
275 }
276
277 int[] qsort(int[] arr) throws Throwable {
278 //init native array
279 try (var arena = Arena.ofConfined()) {
280 MemorySegment nativeArr = arena.allocateArray(C_INT, arr);
281
282 //call qsort
283 MemorySegment qsortUpcallStub = abi.upcallStub(qsortCompar, qsortComparFunction, arena);
284
285 qsort.invokeExact(nativeArr, (long)arr.length, C_INT.byteSize(), qsortUpcallStub);
286
287 //convert back to Java array
288 return nativeArr.toArray(C_INT);
289 }
290 }
291
292 static int qsortCompare(MemorySegment addr1, MemorySegment addr2) {
293 return addr1.get(C_INT, 0) -
294 addr2.get(C_INT, 0);
295 }
296
297 int rand() throws Throwable {
298 return (int)rand.invokeExact();
299 }
300
301 int printf(String format, List<PrintfArg> args) throws Throwable {
302 try (var arena = Arena.ofConfined()) {
303 MemorySegment formatStr = arena.allocateUtf8String(format);
304 return (int)specializedPrintf(args).invokeExact(formatStr,
305 args.stream().map(a -> a.nativeValue(arena)).toArray());
306 }
307 }
308
309 private MethodHandle specializedPrintf(List<PrintfArg> args) {
310 //method type
311 MethodType mt = MethodType.methodType(int.class, MemorySegment.class);
312 FunctionDescriptor fd = printfBase;
313 List<MemoryLayout> variadicLayouts = new ArrayList<>(args.size());
314 for (PrintfArg arg : args) {
315 mt = mt.appendParameterTypes(arg.carrier);
316 variadicLayouts.add(arg.layout);
317 }
318 Linker.Option varargIndex = Linker.Option.firstVariadicArg(fd.argumentLayouts().size());
319 MethodHandle mh = abi.downcallHandle(printfAddr,
320 fd.appendArgumentLayouts(variadicLayouts.toArray(new MemoryLayout[args.size()])),
321 varargIndex);
322 return mh.asSpreader(1, Object[].class, args.size());
323 }
361 Instant instant = start.plusSeconds((long)(Math.random() * (end.getEpochSecond() - start.getEpochSecond())));
362 instants[i] = new Object[] { instant };
363 }
364 return instants;
365 }
366
367 @DataProvider
368 public static Object[][] printfArgs() {
369 ArrayList<List<PrintfArg>> res = new ArrayList<>();
370 List<List<PrintfArg>> perms = new ArrayList<>(perms(0, PrintfArg.values()));
371 for (int i = 0 ; i < 100 ; i++) {
372 Collections.shuffle(perms);
373 res.addAll(perms);
374 }
375 return res.stream()
376 .map(l -> new Object[] { l })
377 .toArray(Object[][]::new);
378 }
379
380 enum PrintfArg {
381 INT(int.class, C_INT, "%d", arena -> 42, 42),
382 LONG(long.class, C_LONG_LONG, "%d", arena -> 84L, 84L),
383 DOUBLE(double.class, C_DOUBLE, "%.4f", arena -> 1.2345d, 1.2345d),
384 STRING(MemorySegment.class, C_POINTER, "%s", arena -> arena.allocateUtf8String("str"), "str");
385
386 final Class<?> carrier;
387 final ValueLayout layout;
388 final String format;
389 final Function<Arena, ?> nativeValueFactory;
390 final Object javaValue;
391
392 <Z, L extends ValueLayout> PrintfArg(Class<?> carrier, L layout, String format, Function<Arena, Z> nativeValueFactory, Object javaValue) {
393 this.carrier = carrier;
394 this.layout = layout;
395 this.format = format;
396 this.nativeValueFactory = nativeValueFactory;
397 this.javaValue = javaValue;
398 }
399
400 public Object nativeValue(Arena arena) {
401 return nativeValueFactory.apply(arena);
402 }
403 }
404
405 static <Z> Set<List<Z>> perms(int count, Z[] arr) {
406 if (count == arr.length) {
407 return Set.of(List.of());
408 } else {
409 return Arrays.stream(arr)
410 .flatMap(num -> {
411 Set<List<Z>> perms = perms(count + 1, arr);
412 return Stream.concat(
413 //take n
414 perms.stream().map(l -> {
415 List<Z> li = new ArrayList<>(l);
|
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/othervm --enable-native-access=ALL-UNNAMED StdLibTest
27 */
28
29 import java.lang.invoke.MethodHandle;
30 import java.lang.invoke.MethodHandles;
31 import java.lang.invoke.MethodType;
32 import java.time.Instant;
33 import java.time.LocalDateTime;
34 import java.time.ZoneOffset;
35 import java.time.ZonedDateTime;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.Collections;
39 import java.util.LinkedHashSet;
40 import java.util.List;
41 import java.util.Set;
42 import java.util.function.Function;
43 import java.util.stream.Collectors;
44 import java.util.stream.Stream;
45
46 import java.lang.foreign.*;
47
48 import org.testng.annotations.*;
49
50 import static org.testng.Assert.*;
51
52 public class StdLibTest extends NativeTestHelper {
53
54 final static Linker abi = Linker.nativeLinker();
55
56 private StdLibHelper stdLibHelper = new StdLibHelper();
57
58 @Test(dataProvider = "stringPairs")
59 void test_strcat(String s1, String s2) throws Throwable {
60 assertEquals(stdLibHelper.strcat(s1, s2), s1 + s2);
61 }
62
63 @Test(dataProvider = "stringPairs")
64 void test_strcmp(String s1, String s2) throws Throwable {
65 assertEquals(Math.signum(stdLibHelper.strcmp(s1, s2)), Math.signum(s1.compareTo(s2)));
66 }
67
68 @Test(dataProvider = "strings")
69 void test_puts(String s) throws Throwable {
70 assertTrue(stdLibHelper.puts(s) >= 0);
71 }
101 Arrays.sort(input);
102 assertEquals(sorted, input);
103 }
104 }
105
106 @Test
107 void test_rand() throws Throwable {
108 int val = stdLibHelper.rand();
109 for (int i = 0 ; i < 100 ; i++) {
110 int newVal = stdLibHelper.rand();
111 if (newVal != val) {
112 return; //ok
113 }
114 val = newVal;
115 }
116 fail("All values are the same! " + val);
117 }
118
119 @Test(dataProvider = "printfArgs")
120 void test_printf(List<PrintfArg> args) throws Throwable {
121 String javaFormatArgs = args.stream()
122 .map(a -> a.javaFormat)
123 .collect(Collectors.joining(","));
124 String nativeFormatArgs = args.stream()
125 .map(a -> a.nativeFormat)
126 .collect(Collectors.joining(","));
127
128 String javaFormatString = "hello(" + javaFormatArgs + ")\n";
129 String nativeFormatString = "hello(" + nativeFormatArgs + ")\n";
130
131 String expected = String.format(javaFormatString, args.stream()
132 .map(a -> a.javaValue).toArray());
133
134 int found = stdLibHelper.printf(nativeFormatString, args);
135 assertEquals(found, expected.length());
136 }
137
138 @Test
139 void testSystemLibraryBadLookupName() {
140 assertTrue(LINKER.defaultLookup().find("strlen\u0000foobar").isEmpty());
141 }
142
143 static class StdLibHelper {
144
145 final static MethodHandle strcat = abi.downcallHandle(abi.defaultLookup().find("strcat").get(),
146 FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER));
147
148 final static MethodHandle strcmp = abi.downcallHandle(abi.defaultLookup().find("strcmp").get(),
149 FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
150
151 final static MethodHandle puts = abi.downcallHandle(abi.defaultLookup().find("puts").get(),
152 FunctionDescriptor.of(C_INT, C_POINTER));
153
154 final static MethodHandle strlen = abi.downcallHandle(abi.defaultLookup().find("strlen").get(),
155 FunctionDescriptor.of(C_INT, C_POINTER));
156
157 final static MethodHandle gmtime = abi.downcallHandle(abi.defaultLookup().find("gmtime").get(),
158 FunctionDescriptor.of(C_POINTER.withTargetLayout(Tm.LAYOUT), C_POINTER));
159
160 // void qsort( void *ptr, size_t count, size_t size, int (*comp)(const void *, const void *) );
161 final static MethodHandle qsort = abi.downcallHandle(abi.defaultLookup().find("qsort").get(),
162 FunctionDescriptor.ofVoid(C_POINTER, C_SIZE_T, C_SIZE_T, C_POINTER));
163
164 final static FunctionDescriptor qsortComparFunction = FunctionDescriptor.of(C_INT,
165 C_POINTER.withTargetLayout(C_INT), C_POINTER.withTargetLayout(C_INT));
166
167 final static MethodHandle qsortCompar;
168
169 final static MethodHandle rand = abi.downcallHandle(abi.defaultLookup().find("rand").get(),
170 FunctionDescriptor.of(C_INT));
171
172 final static MethodHandle vprintf = abi.downcallHandle(abi.defaultLookup().find("vprintf").get(),
173 FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
174
175 final static MemorySegment printfAddr = abi.defaultLookup().find("printf").get();
176
177 final static FunctionDescriptor printfBase = FunctionDescriptor.of(C_INT, C_POINTER);
178
179 static {
180 try {
181 //qsort upcall handle
182 qsortCompar = MethodHandles.lookup().findStatic(StdLibTest.StdLibHelper.class, "qsortCompare",
183 qsortComparFunction.toMethodType());
184 } catch (ReflectiveOperationException ex) {
185 throw new IllegalStateException(ex);
186 }
187 }
188
189 String strcat(String s1, String s2) throws Throwable {
190 try (var arena = Arena.ofConfined()) {
191 MemorySegment buf = arena.allocate(s1.length() + s2.length() + 1);
192 buf.setString(0, s1);
193 MemorySegment other = arena.allocateFrom(s2);
194 return ((MemorySegment)strcat.invokeExact(buf, other)).getString(0);
195 }
196 }
197
198 int strcmp(String s1, String s2) throws Throwable {
199 try (var arena = Arena.ofConfined()) {
200 MemorySegment ns1 = arena.allocateFrom(s1);
201 MemorySegment ns2 = arena.allocateFrom(s2);
202 return (int)strcmp.invokeExact(ns1, ns2);
203 }
204 }
205
206 int puts(String msg) throws Throwable {
207 try (var arena = Arena.ofConfined()) {
208 MemorySegment s = arena.allocateFrom(msg);
209 return (int)puts.invokeExact(s);
210 }
211 }
212
213 int strlen(String msg) throws Throwable {
214 try (var arena = Arena.ofConfined()) {
215 MemorySegment s = arena.allocateFrom(msg);
216 return (int)strlen.invokeExact(s);
217 }
218 }
219
220 Tm gmtime(long arg) throws Throwable {
221 try (var arena = Arena.ofConfined()) {
222 MemorySegment time = arena.allocate(8);
223 time.set(C_LONG_LONG, 0, arg);
224 return new Tm((MemorySegment)gmtime.invokeExact(time));
225 }
226 }
227
228 static class Tm {
229
230 //Tm pointer should never be freed directly, as it points to shared memory
231 private final MemorySegment base;
232
233 static final MemoryLayout LAYOUT = MemoryLayout.structLayout(
234 C_INT.withName("sec"),
235 C_INT.withName("min"),
262 int mon() {
263 return base.get(C_INT, 16);
264 }
265 int year() {
266 return base.get(C_INT, 20);
267 }
268 int wday() {
269 return base.get(C_INT, 24);
270 }
271 int yday() {
272 return base.get(C_INT, 28);
273 }
274 boolean isdst() {
275 return base.get(C_BOOL, 32);
276 }
277 }
278
279 int[] qsort(int[] arr) throws Throwable {
280 //init native array
281 try (var arena = Arena.ofConfined()) {
282 MemorySegment nativeArr = arena.allocateFrom(C_INT, arr);
283
284 //call qsort
285 MemorySegment qsortUpcallStub = abi.upcallStub(qsortCompar, qsortComparFunction, arena);
286
287 // both of these fit in an int
288 // automatically widen them to long on x64
289 int count = arr.length;
290 int size = (int) C_INT.byteSize();
291 qsort.invoke(nativeArr, count, size, qsortUpcallStub);
292
293 //convert back to Java array
294 return nativeArr.toArray(C_INT);
295 }
296 }
297
298 static int qsortCompare(MemorySegment addr1, MemorySegment addr2) {
299 return addr1.get(C_INT, 0) -
300 addr2.get(C_INT, 0);
301 }
302
303 int rand() throws Throwable {
304 return (int)rand.invokeExact();
305 }
306
307 int printf(String format, List<PrintfArg> args) throws Throwable {
308 try (var arena = Arena.ofConfined()) {
309 MemorySegment formatStr = arena.allocateFrom(format);
310 return (int)specializedPrintf(args).invokeExact(formatStr,
311 args.stream().map(a -> a.nativeValue(arena)).toArray());
312 }
313 }
314
315 private MethodHandle specializedPrintf(List<PrintfArg> args) {
316 //method type
317 MethodType mt = MethodType.methodType(int.class, MemorySegment.class);
318 FunctionDescriptor fd = printfBase;
319 List<MemoryLayout> variadicLayouts = new ArrayList<>(args.size());
320 for (PrintfArg arg : args) {
321 mt = mt.appendParameterTypes(arg.carrier);
322 variadicLayouts.add(arg.layout);
323 }
324 Linker.Option varargIndex = Linker.Option.firstVariadicArg(fd.argumentLayouts().size());
325 MethodHandle mh = abi.downcallHandle(printfAddr,
326 fd.appendArgumentLayouts(variadicLayouts.toArray(new MemoryLayout[args.size()])),
327 varargIndex);
328 return mh.asSpreader(1, Object[].class, args.size());
329 }
367 Instant instant = start.plusSeconds((long)(Math.random() * (end.getEpochSecond() - start.getEpochSecond())));
368 instants[i] = new Object[] { instant };
369 }
370 return instants;
371 }
372
373 @DataProvider
374 public static Object[][] printfArgs() {
375 ArrayList<List<PrintfArg>> res = new ArrayList<>();
376 List<List<PrintfArg>> perms = new ArrayList<>(perms(0, PrintfArg.values()));
377 for (int i = 0 ; i < 100 ; i++) {
378 Collections.shuffle(perms);
379 res.addAll(perms);
380 }
381 return res.stream()
382 .map(l -> new Object[] { l })
383 .toArray(Object[][]::new);
384 }
385
386 enum PrintfArg {
387 INT(int.class, C_INT, "%d", "%d", arena -> 42, 42),
388 LONG(long.class, C_LONG_LONG, "%lld", "%d", arena -> 84L, 84L),
389 DOUBLE(double.class, C_DOUBLE, "%.4f", "%.4f", arena -> 1.2345d, 1.2345d),
390 STRING(MemorySegment.class, C_POINTER, "%s", "%s", arena -> arena.allocateFrom("str"), "str");
391
392 final Class<?> carrier;
393 final ValueLayout layout;
394 final String nativeFormat;
395 final String javaFormat;
396 final Function<Arena, ?> nativeValueFactory;
397 final Object javaValue;
398
399 <Z, L extends ValueLayout> PrintfArg(Class<?> carrier, L layout, String nativeFormat, String javaFormat,
400 Function<Arena, Z> nativeValueFactory, Object javaValue) {
401 this.carrier = carrier;
402 this.layout = layout;
403 this.nativeFormat = nativeFormat;
404 this.javaFormat = javaFormat;
405 this.nativeValueFactory = nativeValueFactory;
406 this.javaValue = javaValue;
407 }
408
409 public Object nativeValue(Arena arena) {
410 return nativeValueFactory.apply(arena);
411 }
412 }
413
414 static <Z> Set<List<Z>> perms(int count, Z[] arr) {
415 if (count == arr.length) {
416 return Set.of(List.of());
417 } else {
418 return Arrays.stream(arr)
419 .flatMap(num -> {
420 Set<List<Z>> perms = perms(count + 1, arr);
421 return Stream.concat(
422 //take n
423 perms.stream().map(l -> {
424 List<Z> li = new ArrayList<>(l);
|