< prev index next >

test/jdk/java/foreign/StdLibTest.java

Print this page

 22  */
 23 
 24 /*
 25  * @test
 26  * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
 27  * @run testng/othervm --enable-native-access=ALL-UNNAMED StdLibTest
 28  */
 29 
 30 import java.lang.invoke.MethodHandle;
 31 import java.lang.invoke.MethodHandles;
 32 import java.lang.invoke.MethodType;
 33 import java.time.Instant;
 34 import java.time.LocalDateTime;
 35 import java.time.ZoneOffset;
 36 import java.time.ZonedDateTime;
 37 import java.util.ArrayList;
 38 import java.util.Arrays;
 39 import java.util.Collections;
 40 import java.util.LinkedHashSet;
 41 import java.util.List;
 42 import java.util.Optional;
 43 import java.util.Set;
 44 import java.util.function.BiConsumer;
 45 import java.util.function.Function;
 46 import java.util.stream.Collectors;
 47 import java.util.stream.Stream;
 48 
 49 import jdk.incubator.foreign.*;
 50 
 51 import static jdk.incubator.foreign.MemoryAccess.*;
 52 
 53 import org.testng.annotations.*;
 54 
 55 import static jdk.incubator.foreign.CLinker.*;
 56 import static org.testng.Assert.*;
 57 
 58 @Test
 59 public class StdLibTest {
 60 
 61     final static CLinker abi = CLinker.getInstance();
 62 
 63     private StdLibHelper stdLibHelper = new StdLibHelper();
 64 
 65     @Test(dataProvider = "stringPairs")
 66     void test_strcat(String s1, String s2) throws Throwable {
 67         assertEquals(stdLibHelper.strcat(s1, s2), s1 + s2);
 68     }
 69 
 70     @Test(dataProvider = "stringPairs")
 71     void test_strcmp(String s1, String s2) throws Throwable {
 72         assertEquals(Math.signum(stdLibHelper.strcmp(s1, s2)), Math.signum(s1.compareTo(s2)));
 73     }
 74 
 75     @Test(dataProvider = "strings")
 76     void test_puts(String s) throws Throwable {
 77         assertTrue(stdLibHelper.puts(s) >= 0);
 78     }
 79 
 80     @Test(dataProvider = "strings")
 81     void test_strlen(String s) throws Throwable {

138         assertEquals(found, expected.length());
139     }
140 
141     @Test(dataProvider = "printfArgs")
142     void test_vprintf(List<PrintfArg> args) throws Throwable {
143         String formatArgs = args.stream()
144                 .map(a -> a.format)
145                 .collect(Collectors.joining(","));
146 
147         String formatString = "hello(" + formatArgs + ")\n";
148 
149         String expected = String.format(formatString, args.stream()
150                 .map(a -> a.javaValue).toArray());
151 
152         int found = stdLibHelper.vprintf(formatString, args);
153         assertEquals(found, expected.length());
154     }
155 
156     static class StdLibHelper {
157 
158         static final SymbolLookup LOOKUP = CLinker.systemLookup();
159 
160         final static MethodHandle strcat = abi.downcallHandle(LOOKUP.lookup("strcat").get(),
161                 MethodType.methodType(MemoryAddress.class, MemoryAddress.class, MemoryAddress.class),
162                 FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER));
163 
164         final static MethodHandle strcmp = abi.downcallHandle(LOOKUP.lookup("strcmp").get(),
165                 MethodType.methodType(int.class, MemoryAddress.class, MemoryAddress.class),
166                 FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
167 
168         final static MethodHandle puts = abi.downcallHandle(LOOKUP.lookup("puts").get(),
169                 MethodType.methodType(int.class, MemoryAddress.class),
170                 FunctionDescriptor.of(C_INT, C_POINTER));
171 
172         final static MethodHandle strlen = abi.downcallHandle(LOOKUP.lookup("strlen").get(),
173                 MethodType.methodType(int.class, MemoryAddress.class),
174                 FunctionDescriptor.of(C_INT, C_POINTER));
175 
176         final static MethodHandle gmtime = abi.downcallHandle(LOOKUP.lookup("gmtime").get(),
177                 MethodType.methodType(MemoryAddress.class, MemoryAddress.class),
178                 FunctionDescriptor.of(C_POINTER, C_POINTER));
179 
180         final static MethodHandle qsort = abi.downcallHandle(LOOKUP.lookup("qsort").get(),
181                 MethodType.methodType(void.class, MemoryAddress.class, long.class, long.class, MemoryAddress.class),
182                 FunctionDescriptor.ofVoid(C_POINTER, C_LONG_LONG, C_LONG_LONG, C_POINTER));
183 
184         final static FunctionDescriptor qsortComparFunction = FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER);
185 
186         final static MethodHandle qsortCompar;
187 
188         final static MethodHandle rand = abi.downcallHandle(LOOKUP.lookup("rand").get(),
189                 MethodType.methodType(int.class),
190                 FunctionDescriptor.of(C_INT));
191 
192         final static MethodHandle vprintf = abi.downcallHandle(LOOKUP.lookup("vprintf").get(),
193                 MethodType.methodType(int.class, MemoryAddress.class, VaList.class),
194                 FunctionDescriptor.of(C_INT, C_POINTER, C_VA_LIST));
195 
196         final static MemoryAddress printfAddr = LOOKUP.lookup("printf").get();
197 
198         final static FunctionDescriptor printfBase = FunctionDescriptor.of(C_INT, C_POINTER);
199 
200         static {
201             try {
202                 //qsort upcall handle
203                 qsortCompar = MethodHandles.lookup().findStatic(StdLibTest.StdLibHelper.class, "qsortCompare",
204                         MethodType.methodType(int.class, MemorySegment.class, MemoryAddress.class, MemoryAddress.class));
205             } catch (ReflectiveOperationException ex) {
206                 throw new IllegalStateException(ex);
207             }
208         }
209 
210         String strcat(String s1, String s2) throws Throwable {
211             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
212                 MemorySegment buf = MemorySegment.allocateNative(s1.length() + s2.length() + 1, scope);
213                 MemorySegment other = toCString(s2, scope);
214                 char[] chars = s1.toCharArray();
215                 for (long i = 0 ; i < chars.length ; i++) {
216                     setByteAtOffset(buf, i, (byte)chars[(int)i]);
217                 }
218                 setByteAtOffset(buf, chars.length, (byte)'\0');
219                 return toJavaString(((MemoryAddress)strcat.invokeExact(buf.address(), other.address())));
220             }
221         }
222 
223         int strcmp(String s1, String s2) throws Throwable {
224             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
225                 MemorySegment ns1 = toCString(s1, scope);
226                 MemorySegment ns2 = toCString(s2, scope);
227                 return (int)strcmp.invokeExact(ns1.address(), ns2.address());

228             }
229         }
230 
231         int puts(String msg) throws Throwable {
232             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
233                 MemorySegment s = toCString(msg, scope);
234                 return (int)puts.invokeExact(s.address());

235             }
236         }
237 
238         int strlen(String msg) throws Throwable {
239             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
240                 MemorySegment s = toCString(msg, scope);
241                 return (int)strlen.invokeExact(s.address());

242             }
243         }
244 
245         Tm gmtime(long arg) throws Throwable {
246             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
247                 MemorySegment time = MemorySegment.allocateNative(8, scope);
248                 setLong(time, arg);
249                 return new Tm((MemoryAddress)gmtime.invokeExact(time.address()));
250             }
251         }
252 
253         static class Tm {
254 
255             //Tm pointer should never be freed directly, as it points to shared memory
256             private final MemorySegment base;
257 
258             static final long SIZE = 56;
259 
260             Tm(MemoryAddress addr) {
261                 this.base = addr.asSegment(SIZE, ResourceScope.globalScope());
262             }
263 
264             int sec() {
265                 return getIntAtOffset(base, 0);
266             }
267             int min() {
268                 return getIntAtOffset(base, 4);
269             }
270             int hour() {
271                 return getIntAtOffset(base, 8);
272             }
273             int mday() {
274                 return getIntAtOffset(base, 12);
275             }
276             int mon() {
277                 return getIntAtOffset(base, 16);
278             }
279             int year() {
280                 return getIntAtOffset(base, 20);
281             }
282             int wday() {
283                 return getIntAtOffset(base, 24);
284             }
285             int yday() {
286                 return getIntAtOffset(base, 28);
287             }
288             boolean isdst() {
289                 byte b = getByteAtOffset(base, 32);
290                 return b != 0;
291             }
292         }
293 
294         int[] qsort(int[] arr) throws Throwable {
295             //init native array
296             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
297                 SegmentAllocator allocator = SegmentAllocator.ofScope(scope);
298                 MemorySegment nativeArr = allocator.allocateArray(C_INT, arr);
299 
300                 //call qsort
301                 MemoryAddress qsortUpcallStub = abi.upcallStub(qsortCompar.bindTo(nativeArr), qsortComparFunction, scope);
302 
303                 qsort.invokeExact(nativeArr.address(), (long)arr.length, C_INT.byteSize(), qsortUpcallStub);
304 
305                 //convert back to Java array
306                 return nativeArr.toIntArray();
307             }
308         }
309 
310         static int qsortCompare(MemorySegment base, MemoryAddress addr1, MemoryAddress addr2) {
311             return getIntAtOffset(base, addr1.segmentOffset(base)) -
312                    getIntAtOffset(base, addr2.segmentOffset(base));
313         }
314 
315         int rand() throws Throwable {
316             return (int)rand.invokeExact();
317         }
318 
319         int printf(String format, List<PrintfArg> args) throws Throwable {
320             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
321                 MemorySegment formatStr = toCString(format, scope);
322                 return (int)specializedPrintf(args).invokeExact(formatStr.address(),

323                         args.stream().map(a -> a.nativeValue(scope)).toArray());
324             }
325         }
326 
327         int vprintf(String format, List<PrintfArg> args) throws Throwable {
328             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
329                 MemorySegment formatStr = toCString(format, scope);

330                 VaList vaList = VaList.make(b -> args.forEach(a -> a.accept(b, scope)), scope);
331                 return (int)vprintf.invokeExact(formatStr.address(), vaList);
332             }
333         }
334 
335         private MethodHandle specializedPrintf(List<PrintfArg> args) {
336             //method type
337             MethodType mt = MethodType.methodType(int.class, MemoryAddress.class);
338             FunctionDescriptor fd = printfBase;

339             for (PrintfArg arg : args) {
340                 mt = mt.appendParameterTypes(arg.carrier);
341                 fd = fd.withAppendedArgumentLayouts(arg.layout);
342             }
343             MethodHandle mh = abi.downcallHandle(printfAddr, mt, fd);

344             return mh.asSpreader(1, Object[].class, args.size());
345         }
346     }
347 
348     /*** data providers ***/
349 
350     @DataProvider
351     public static Object[][] ints() {
352         return perms(0, new Integer[] { 0, 1, 2, 3, 4 }).stream()
353                 .map(l -> new Object[] { l })
354                 .toArray(Object[][]::new);
355     }
356 
357     @DataProvider
358     public static Object[][] strings() {
359         return perms(0, new String[] { "a", "b", "c" }).stream()
360                 .map(l -> new Object[] { String.join("", l) })
361                 .toArray(Object[][]::new);
362     }
363 

384             instants[i] = new Object[] { instant };
385         }
386         return instants;
387     }
388 
389     @DataProvider
390     public static Object[][] printfArgs() {
391         ArrayList<List<PrintfArg>> res = new ArrayList<>();
392         List<List<PrintfArg>> perms = new ArrayList<>(perms(0, PrintfArg.values()));
393         for (int i = 0 ; i < 100 ; i++) {
394             Collections.shuffle(perms);
395             res.addAll(perms);
396         }
397         return res.stream()
398                 .map(l -> new Object[] { l })
399                 .toArray(Object[][]::new);
400     }
401 
402     enum PrintfArg implements BiConsumer<VaList.Builder, ResourceScope> {
403 
404         INTEGRAL(int.class, asVarArg(C_INT), "%d", scope -> 42, 42, VaList.Builder::vargFromInt),
405         STRING(MemoryAddress.class, asVarArg(C_POINTER), "%s", scope -> toCString("str", scope).address(), "str", VaList.Builder::vargFromAddress),
406         CHAR(byte.class, asVarArg(C_CHAR), "%c", scope -> (byte) 'h', 'h', (builder, layout, value) -> builder.vargFromInt(C_INT, (int)value)),
407         DOUBLE(double.class, asVarArg(C_DOUBLE), "%.4f", scope ->1.2345d, 1.2345d, VaList.Builder::vargFromDouble);




408 
409         final Class<?> carrier;
410         final ValueLayout layout;
411         final String format;
412         final Function<ResourceScope, ?> nativeValueFactory;
413         final Object javaValue;
414         @SuppressWarnings("rawtypes")
415         final VaListBuilderCall builderCall;
416 
417         <Z> PrintfArg(Class<?> carrier, ValueLayout layout, String format, Function<ResourceScope, Z> nativeValueFactory, Object javaValue, VaListBuilderCall<Z> builderCall) {
418             this.carrier = carrier;
419             this.layout = layout;
420             this.format = format;
421             this.nativeValueFactory = nativeValueFactory;
422             this.javaValue = javaValue;
423             this.builderCall = builderCall;
424         }
425 
426         @Override
427         @SuppressWarnings("unchecked")
428         public void accept(VaList.Builder builder, ResourceScope scope) {
429             builderCall.build(builder, layout, nativeValueFactory.apply(scope));
430         }
431 
432         interface VaListBuilderCall<V> {
433             void build(VaList.Builder builder, ValueLayout layout, V value);
434         }
435 
436         public Object nativeValue(ResourceScope scope) {
437             return nativeValueFactory.apply(scope);
438         }
439     }
440 
441     static <Z> Set<List<Z>> perms(int count, Z[] arr) {
442         if (count == arr.length) {
443             return Set.of(List.of());
444         } else {
445             return Arrays.stream(arr)
446                     .flatMap(num -> {
447                         Set<List<Z>> perms = perms(count + 1, arr);
448                         return Stream.concat(
449                                 //take n
450                                 perms.stream().map(l -> {
451                                     List<Z> li = new ArrayList<>(l);
452                                     li.add(num);
453                                     return li;

 22  */
 23 
 24 /*
 25  * @test
 26  * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
 27  * @run testng/othervm --enable-native-access=ALL-UNNAMED StdLibTest
 28  */
 29 
 30 import java.lang.invoke.MethodHandle;
 31 import java.lang.invoke.MethodHandles;
 32 import java.lang.invoke.MethodType;
 33 import java.time.Instant;
 34 import java.time.LocalDateTime;
 35 import java.time.ZoneOffset;
 36 import java.time.ZonedDateTime;
 37 import java.util.ArrayList;
 38 import java.util.Arrays;
 39 import java.util.Collections;
 40 import java.util.LinkedHashSet;
 41 import java.util.List;

 42 import java.util.Set;
 43 import java.util.function.BiConsumer;
 44 import java.util.function.Function;
 45 import java.util.stream.Collectors;
 46 import java.util.stream.Stream;
 47 
 48 import jdk.incubator.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 CLinker abi = CLinker.systemCLinker();
 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     }
 75 
 76     @Test(dataProvider = "strings")
 77     void test_strlen(String s) throws Throwable {

134         assertEquals(found, expected.length());
135     }
136 
137     @Test(dataProvider = "printfArgs")
138     void test_vprintf(List<PrintfArg> args) throws Throwable {
139         String formatArgs = args.stream()
140                 .map(a -> a.format)
141                 .collect(Collectors.joining(","));
142 
143         String formatString = "hello(" + formatArgs + ")\n";
144 
145         String expected = String.format(formatString, args.stream()
146                 .map(a -> a.javaValue).toArray());
147 
148         int found = stdLibHelper.vprintf(formatString, args);
149         assertEquals(found, expected.length());
150     }
151 
152     static class StdLibHelper {
153 
154         final static MethodHandle strcat = abi.downcallHandle(abi.lookup("strcat").get(),
155                 FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER))
156                 .asType(MethodType.methodType(MemoryAddress.class, MemorySegment.class, MemorySegment.class)); // exact signature match


157 
158         final static MethodHandle strcmp = abi.downcallHandle(abi.lookup("strcmp").get(),

159                 FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
160 
161         final static MethodHandle puts = abi.downcallHandle(abi.lookup("puts").get(),

162                 FunctionDescriptor.of(C_INT, C_POINTER));
163 
164         final static MethodHandle strlen = abi.downcallHandle(abi.lookup("strlen").get(),

165                 FunctionDescriptor.of(C_INT, C_POINTER));
166 
167         final static MethodHandle gmtime = abi.downcallHandle(abi.lookup("gmtime").get(),

168                 FunctionDescriptor.of(C_POINTER, C_POINTER));
169 
170         final static MethodHandle qsort = abi.downcallHandle(abi.lookup("qsort").get(),

171                 FunctionDescriptor.ofVoid(C_POINTER, C_LONG_LONG, C_LONG_LONG, C_POINTER));
172 
173         final static FunctionDescriptor qsortComparFunction = FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER);
174 
175         final static MethodHandle qsortCompar;
176 
177         final static MethodHandle rand = abi.downcallHandle(abi.lookup("rand").get(),

178                 FunctionDescriptor.of(C_INT));
179 
180         final static MethodHandle vprintf = abi.downcallHandle(abi.lookup("vprintf").get(),
181                 FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));

182 
183         final static NativeSymbol printfAddr = abi.lookup("printf").get();
184 
185         final static FunctionDescriptor printfBase = FunctionDescriptor.of(C_INT, C_POINTER);
186 
187         static {
188             try {
189                 //qsort upcall handle
190                 qsortCompar = MethodHandles.lookup().findStatic(StdLibTest.StdLibHelper.class, "qsortCompare",
191                         MethodType.methodType(int.class, MemoryAddress.class, MemoryAddress.class));
192             } catch (ReflectiveOperationException ex) {
193                 throw new IllegalStateException(ex);
194             }
195         }
196 
197         String strcat(String s1, String s2) throws Throwable {
198             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
199                 var malloc = SegmentAllocator.nativeAllocator(scope);
200                 MemorySegment buf = malloc.allocate(s1.length() + s2.length() + 1);
201                 buf.setUtf8String(0, s1);
202                 MemorySegment other = malloc.allocateUtf8String(s2);
203                 return ((MemoryAddress)strcat.invokeExact(buf, other)).getUtf8String(0);



204             }
205         }
206 
207         int strcmp(String s1, String s2) throws Throwable {
208             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
209                 var malloc = SegmentAllocator.nativeAllocator(scope);
210                 MemorySegment ns1 = malloc.allocateUtf8String(s1);
211                 MemorySegment ns2 = malloc.allocateUtf8String(s2);
212                 return (int)strcmp.invoke(ns1, ns2);
213             }
214         }
215 
216         int puts(String msg) throws Throwable {
217             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
218                 var malloc = SegmentAllocator.nativeAllocator(scope);
219                 MemorySegment s = malloc.allocateUtf8String(msg);
220                 return (int)puts.invoke(s);
221             }
222         }
223 
224         int strlen(String msg) throws Throwable {
225             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
226                 var malloc = SegmentAllocator.nativeAllocator(scope);
227                 MemorySegment s = malloc.allocateUtf8String(msg);
228                 return (int)strlen.invoke(s);
229             }
230         }
231 
232         Tm gmtime(long arg) throws Throwable {
233             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
234                 MemorySegment time = MemorySegment.allocateNative(8, scope);
235                 time.set(C_LONG_LONG, 0, arg);
236                 return new Tm((MemoryAddress)gmtime.invoke(time));
237             }
238         }
239 
240         static class Tm {
241 
242             //Tm pointer should never be freed directly, as it points to shared memory
243             private final MemorySegment base;
244 
245             static final long SIZE = 56;
246 
247             Tm(MemoryAddress addr) {
248                 this.base = MemorySegment.ofAddressNative(addr, SIZE, ResourceScope.globalScope());
249             }
250 
251             int sec() {
252                 return base.get(C_INT, 0);
253             }
254             int min() {
255                 return base.get(C_INT, 4);
256             }
257             int hour() {
258                 return base.get(C_INT, 8);
259             }
260             int mday() {
261                 return base.get(C_INT, 12);
262             }
263             int mon() {
264                 return base.get(C_INT, 16);
265             }
266             int year() {
267                 return base.get(C_INT, 20);
268             }
269             int wday() {
270                 return base.get(C_INT, 24);
271             }
272             int yday() {
273                 return base.get(C_INT, 28);
274             }
275             boolean isdst() {
276                 return base.get(C_BOOL, 32);

277             }
278         }
279 
280         int[] qsort(int[] arr) throws Throwable {
281             //init native array
282             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
283                 var malloc = SegmentAllocator.nativeAllocator(scope);
284                 MemorySegment nativeArr = malloc.allocateArray(C_INT, arr);
285 
286                 //call qsort
287                 NativeSymbol qsortUpcallStub = abi.upcallStub(qsortCompar, qsortComparFunction, scope);
288 
289                 qsort.invoke(nativeArr, (long)arr.length, C_INT.byteSize(), qsortUpcallStub);
290 
291                 //convert back to Java array
292                 return nativeArr.toArray(C_INT);
293             }
294         }
295 
296         static int qsortCompare(MemoryAddress addr1, MemoryAddress addr2) {
297             return addr1.get(C_INT, 0) -
298                    addr2.get(C_INT, 0);
299         }
300 
301         int rand() throws Throwable {
302             return (int)rand.invokeExact();
303         }
304 
305         int printf(String format, List<PrintfArg> args) throws Throwable {
306             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
307                 var malloc = SegmentAllocator.nativeAllocator(scope);
308                 MemorySegment formatStr = malloc.allocateUtf8String(format);
309                 return (int)specializedPrintf(args).invoke(formatStr,
310                         args.stream().map(a -> a.nativeValue(scope)).toArray());
311             }
312         }
313 
314         int vprintf(String format, List<PrintfArg> args) throws Throwable {
315             try (ResourceScope scope = ResourceScope.newConfinedScope()) {
316                 var malloc = SegmentAllocator.nativeAllocator(scope);
317                 MemorySegment formatStr = malloc.allocateUtf8String(format);
318                 VaList vaList = VaList.make(b -> args.forEach(a -> a.accept(b, scope)), scope);
319                 return (int)vprintf.invoke(formatStr, vaList);
320             }
321         }
322 
323         private MethodHandle specializedPrintf(List<PrintfArg> args) {
324             //method type
325             MethodType mt = MethodType.methodType(int.class, MemoryAddress.class);
326             FunctionDescriptor fd = printfBase;
327             List<MemoryLayout> variadicLayouts = new ArrayList<>(args.size());
328             for (PrintfArg arg : args) {
329                 mt = mt.appendParameterTypes(arg.carrier);
330                 variadicLayouts.add(arg.layout);
331             }
332             MethodHandle mh = abi.downcallHandle(printfAddr,
333                     fd.asVariadic(variadicLayouts.toArray(new MemoryLayout[args.size()])));
334             return mh.asSpreader(1, Object[].class, args.size());
335         }
336     }
337 
338     /*** data providers ***/
339 
340     @DataProvider
341     public static Object[][] ints() {
342         return perms(0, new Integer[] { 0, 1, 2, 3, 4 }).stream()
343                 .map(l -> new Object[] { l })
344                 .toArray(Object[][]::new);
345     }
346 
347     @DataProvider
348     public static Object[][] strings() {
349         return perms(0, new String[] { "a", "b", "c" }).stream()
350                 .map(l -> new Object[] { String.join("", l) })
351                 .toArray(Object[][]::new);
352     }
353 

374             instants[i] = new Object[] { instant };
375         }
376         return instants;
377     }
378 
379     @DataProvider
380     public static Object[][] printfArgs() {
381         ArrayList<List<PrintfArg>> res = new ArrayList<>();
382         List<List<PrintfArg>> perms = new ArrayList<>(perms(0, PrintfArg.values()));
383         for (int i = 0 ; i < 100 ; i++) {
384             Collections.shuffle(perms);
385             res.addAll(perms);
386         }
387         return res.stream()
388                 .map(l -> new Object[] { l })
389                 .toArray(Object[][]::new);
390     }
391 
392     enum PrintfArg implements BiConsumer<VaList.Builder, ResourceScope> {
393 
394         INTEGRAL(int.class, C_INT, "%d", scope -> 42, 42, VaList.Builder::addVarg),
395         STRING(MemoryAddress.class, C_POINTER, "%s", scope -> {
396             var segment = MemorySegment.allocateNative(4, scope);
397             segment.setUtf8String(0, "str");
398             return segment.address();
399         }, "str", VaList.Builder::addVarg),
400         CHAR(byte.class, C_CHAR, "%c", scope -> (byte) 'h', 'h', (builder, layout, value) -> builder.addVarg(C_INT, (int)value)),
401         DOUBLE(double.class, C_DOUBLE, "%.4f", scope ->1.2345d, 1.2345d, VaList.Builder::addVarg);
402 
403         final Class<?> carrier;
404         final ValueLayout layout;
405         final String format;
406         final Function<ResourceScope, ?> nativeValueFactory;
407         final Object javaValue;
408         @SuppressWarnings("rawtypes")
409         final VaListBuilderCall builderCall;
410 
411         <Z, L extends ValueLayout> PrintfArg(Class<?> carrier, L layout, String format, Function<ResourceScope, Z> nativeValueFactory, Object javaValue, VaListBuilderCall<Z, L> builderCall) {
412             this.carrier = carrier;
413             this.layout = layout;
414             this.format = format;
415             this.nativeValueFactory = nativeValueFactory;
416             this.javaValue = javaValue;
417             this.builderCall = builderCall;
418         }
419 
420         @Override
421         @SuppressWarnings("unchecked")
422         public void accept(VaList.Builder builder, ResourceScope scope) {
423             builderCall.build(builder, layout, nativeValueFactory.apply(scope));
424         }
425 
426         interface VaListBuilderCall<V, L> {
427             void build(VaList.Builder builder, L layout, V value);
428         }
429 
430         public Object nativeValue(ResourceScope scope) {
431             return nativeValueFactory.apply(scope);
432         }
433     }
434 
435     static <Z> Set<List<Z>> perms(int count, Z[] arr) {
436         if (count == arr.length) {
437             return Set.of(List.of());
438         } else {
439             return Arrays.stream(arr)
440                     .flatMap(num -> {
441                         Set<List<Z>> perms = perms(count + 1, arr);
442                         return Stream.concat(
443                                 //take n
444                                 perms.stream().map(l -> {
445                                     List<Z> li = new ArrayList<>(l);
446                                     li.add(num);
447                                     return li;
< prev index next >