1 /*
  2  *  Copyright (c) 2020, 2021, 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 /*
 26  * @test
 27  * @library ../
 28  * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
 29  * @modules jdk.incubator.foreign/jdk.internal.foreign
 30  *          jdk.incubator.foreign/jdk.internal.foreign.abi
 31  *          jdk.incubator.foreign/jdk.internal.foreign.abi.aarch64.linux
 32  *          jdk.incubator.foreign/jdk.internal.foreign.abi.aarch64.macos
 33  *          jdk.incubator.foreign/jdk.internal.foreign.abi.x64.windows
 34  *          jdk.incubator.foreign/jdk.internal.foreign.abi.x64.sysv
 35  * @run testng/othervm --enable-native-access=ALL-UNNAMED VaListTest
 36  */
 37 
 38 import jdk.incubator.foreign.*;
 39 import jdk.incubator.foreign.VaList;
 40 import jdk.internal.foreign.abi.aarch64.linux.LinuxAArch64Linker;
 41 import jdk.internal.foreign.abi.aarch64.macos.MacOsAArch64Linker;
 42 import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
 43 import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
 44 import org.testng.annotations.DataProvider;
 45 import org.testng.annotations.Test;
 46 
 47 import java.lang.invoke.MethodHandle;
 48 import java.lang.invoke.MethodHandleProxies;
 49 import java.lang.invoke.MethodHandles;
 50 import java.lang.invoke.MethodType;
 51 import java.lang.invoke.VarHandle;
 52 import java.util.function.BiFunction;
 53 import java.util.function.Consumer;
 54 import java.util.function.Function;
 55 import java.util.stream.DoubleStream;
 56 import java.util.stream.IntStream;
 57 
 58 import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement;
 59 import static jdk.incubator.foreign.ValueLayout.JAVA_DOUBLE;
 60 import static jdk.incubator.foreign.ValueLayout.JAVA_INT;
 61 import static jdk.incubator.foreign.ValueLayout.JAVA_LONG;
 62 import static jdk.internal.foreign.PlatformLayouts.*;
 63 import static org.testng.Assert.*;
 64 
 65 public class VaListTest extends NativeTestHelper {
 66 
 67     private static final CLinker abi = CLinker.systemCLinker();
 68     static {
 69         System.loadLibrary("VaList");
 70     }
 71 
 72     static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup();
 73 
 74     private static final MethodHandle ADDRESS_TO_VALIST;
 75 
 76     static {
 77         try {
 78             ADDRESS_TO_VALIST = MethodHandles.lookup().findStatic(VaList.class, "ofAddress", MethodType.methodType(VaList.class, MemoryAddress.class, ResourceScope.class));
 79         } catch (Throwable ex) {
 80             throw new ExceptionInInitializerError(ex);
 81         }
 82     }
 83 
 84 
 85     private static final MethodHandle MH_sumInts = linkVaList("sumInts",
 86             FunctionDescriptor.of(C_INT, C_INT, C_POINTER));
 87     private static final MethodHandle MH_sumDoubles = linkVaList("sumDoubles",
 88             FunctionDescriptor.of(C_DOUBLE, C_INT, C_POINTER));
 89     private static final MethodHandle MH_getInt = linkVaList("getInt",
 90             FunctionDescriptor.of(C_INT, C_POINTER));
 91     private static final MethodHandle MH_sumStruct = linkVaList("sumStruct",
 92             FunctionDescriptor.of(C_INT, C_POINTER));
 93     private static final MethodHandle MH_sumBigStruct = linkVaList("sumBigStruct",
 94             FunctionDescriptor.of(C_LONG_LONG, C_POINTER));
 95     private static final MethodHandle MH_sumHugeStruct = linkVaList("sumHugeStruct",
 96             FunctionDescriptor.of(C_LONG_LONG, C_POINTER));
 97     private static final MethodHandle MH_sumFloatStruct = linkVaList("sumFloatStruct",
 98             FunctionDescriptor.of(C_FLOAT, C_POINTER));
 99     private static final MethodHandle MH_sumStack = linkVaList("sumStack",
100             FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_POINTER));
101 
102     private static MethodHandle link(String symbol, FunctionDescriptor fd) {
103         return linkInternal(symbol, fd);
104     }
105 
106     private static MethodHandle linkVaList(String symbol, FunctionDescriptor fd) {
107         return linkInternal(symbol, fd);
108     }
109 
110 
111     private static MethodHandle linkInternal(String symbol, FunctionDescriptor fd) {
112         return abi.downcallHandle(LOOKUP.lookup(symbol).get(), fd);
113     }
114 
115     private static MethodHandle linkVaListCB(String symbol) {
116         return link(symbol,
117                 FunctionDescriptor.ofVoid(C_POINTER));
118 
119     }
120 
121     private static final Function<Consumer<VaList.Builder>, VaList> winVaListFactory
122             = actions -> Windowsx64Linker.newVaList(actions, ResourceScope.newConfinedScope());
123     private static final Function<Consumer<VaList.Builder>, VaList> sysvVaListFactory
124             = actions -> SysVx64Linker.newVaList(actions, ResourceScope.newConfinedScope());
125     private static final Function<Consumer<VaList.Builder>, VaList> linuxAArch64VaListFactory
126             = actions -> LinuxAArch64Linker.newVaList(actions, ResourceScope.newConfinedScope());
127     private static final Function<Consumer<VaList.Builder>, VaList> macAArch64VaListFactory
128             = actions -> MacOsAArch64Linker.newVaList(actions, ResourceScope.newConfinedScope());
129     private static final Function<Consumer<VaList.Builder>, VaList> platformVaListFactory
130             = (builder) -> VaList.make(builder, ResourceScope.newConfinedScope());
131 
132     private static final BiFunction<Consumer<VaList.Builder>, ResourceScope, VaList> winVaListScopedFactory
133             = (builder, scope) -> Windowsx64Linker.newVaList(builder, scope);
134     private static final BiFunction<Consumer<VaList.Builder>, ResourceScope, VaList> sysvVaListScopedFactory
135             = (builder, scope) -> SysVx64Linker.newVaList(builder, scope);
136     private static final BiFunction<Consumer<VaList.Builder>, ResourceScope, VaList> linuxAArch64VaListScopedFactory
137             = (builder, scope) -> LinuxAArch64Linker.newVaList(builder, scope);
138     private static final BiFunction<Consumer<VaList.Builder>, ResourceScope, VaList> macAArch64VaListScopedFactory
139             = (builder, scope) -> MacOsAArch64Linker.newVaList(builder, scope);
140     private static final BiFunction<Consumer<VaList.Builder>, ResourceScope, VaList> platformVaListScopedFactory
141             = (builder, scope) -> VaList.make(builder, scope);
142 
143     @DataProvider
144     @SuppressWarnings("unchecked")
145     public static Object[][] sumInts() {
146         Function<ValueLayout.OfInt, BiFunction<Integer, VaList, Integer>> sumIntsJavaFact = layout ->
147                 (num, list) -> IntStream.generate(() -> list.nextVarg(layout)).limit(num).sum();
148         BiFunction<Integer, VaList, Integer> sumIntsNative
149                 = MethodHandleProxies.asInterfaceInstance(BiFunction.class, MH_sumInts);
150         return new Object[][]{
151                 { winVaListFactory,          sumIntsJavaFact.apply(Win64.C_INT),   Win64.C_INT   },
152                 { sysvVaListFactory,         sumIntsJavaFact.apply(SysV.C_INT),    SysV.C_INT    },
153                 { linuxAArch64VaListFactory, sumIntsJavaFact.apply(AArch64.C_INT), AArch64.C_INT },
154                 { macAArch64VaListFactory,   sumIntsJavaFact.apply(AArch64.C_INT), AArch64.C_INT },
155                 { platformVaListFactory,     sumIntsNative,                        C_INT         },
156         };
157     }
158 
159     @Test(dataProvider = "sumInts")
160     public void testIntSum(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
161                            BiFunction<Integer, VaList, Integer> sumInts,
162                            ValueLayout.OfInt intLayout) {
163         VaList vaList = vaListFactory.apply(b ->
164             b.addVarg(intLayout, 10)
165                     .addVarg(intLayout, 15)
166                     .addVarg(intLayout, 20));
167         int x = sumInts.apply(3, vaList);
168         assertEquals(x, 45);
169         vaList.scope().close();
170     }
171 
172     @DataProvider
173     @SuppressWarnings("unchecked")
174     public static Object[][] sumDoubles() {
175         Function<ValueLayout.OfDouble, BiFunction<Integer, VaList, Double>> sumDoublesJavaFact  = layout ->
176                 (num, list) -> DoubleStream.generate(() -> list.nextVarg(layout)).limit(num).sum();
177         BiFunction<Integer, VaList, Double> sumDoublesNative
178                 = MethodHandleProxies.asInterfaceInstance(BiFunction.class, MH_sumDoubles);
179         return new Object[][]{
180                 { winVaListFactory,          sumDoublesJavaFact.apply(Win64.C_DOUBLE),   Win64.C_DOUBLE   },
181                 { sysvVaListFactory,         sumDoublesJavaFact.apply(SysV.C_DOUBLE),    SysV.C_DOUBLE    },
182                 { linuxAArch64VaListFactory, sumDoublesJavaFact.apply(AArch64.C_DOUBLE), AArch64.C_DOUBLE },
183                 { macAArch64VaListFactory,   sumDoublesJavaFact.apply(AArch64.C_DOUBLE), AArch64.C_DOUBLE },
184                 { platformVaListFactory,     sumDoublesNative,                           C_DOUBLE         },
185         };
186     }
187 
188     @Test(dataProvider = "sumDoubles")
189     public void testDoubleSum(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
190                               BiFunction<Integer, VaList, Double> sumDoubles,
191                               ValueLayout.OfDouble doubleLayout) {
192         VaList vaList = vaListFactory.apply(b ->
193             b.addVarg(doubleLayout, 3.0D)
194                     .addVarg(doubleLayout, 4.0D)
195                     .addVarg(doubleLayout, 5.0D));
196         double x = sumDoubles.apply(3, vaList);
197         assertEquals(x, 12.0D);
198         vaList.scope().close();
199     }
200 
201     @DataProvider
202     @SuppressWarnings("unchecked")
203     public static Object[][] pointers() {
204         Function<ValueLayout.OfAddress, Function<VaList, Integer>> getIntJavaFact = layout ->
205                 list -> {
206                     MemoryAddress ma = list.nextVarg(layout);
207                     return ma.get(JAVA_INT, 0);
208                 };
209         Function<VaList, Integer> getIntNative = MethodHandleProxies.asInterfaceInstance(Function.class, MH_getInt);
210         return new Object[][]{
211                 { winVaListFactory,          getIntJavaFact.apply(Win64.C_POINTER),   Win64.C_POINTER   },
212                 { sysvVaListFactory,         getIntJavaFact.apply(SysV.C_POINTER),    SysV.C_POINTER    },
213                 { linuxAArch64VaListFactory, getIntJavaFact.apply(AArch64.C_POINTER), AArch64.C_POINTER },
214                 { macAArch64VaListFactory,   getIntJavaFact.apply(AArch64.C_POINTER), AArch64.C_POINTER },
215                 { platformVaListFactory,     getIntNative,                            C_POINTER         },
216         };
217     }
218 
219     @Test(dataProvider = "pointers")
220     public void testVaListMemoryAddress(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
221                                         Function<VaList, Integer> getFromPointer,
222                                         ValueLayout.OfAddress pointerLayout) {
223         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
224             MemorySegment msInt = MemorySegment.allocateNative(JAVA_INT, scope);
225             msInt.set(JAVA_INT, 0, 10);
226             VaList vaList = vaListFactory.apply(b -> b.addVarg(pointerLayout, msInt.address()));
227             int x = getFromPointer.apply(vaList);
228             assertEquals(x, 10);
229             vaList.scope().close();
230         }
231     }
232 
233     interface TriFunction<S, T, U, R> {
234         R apply(S s, T t, U u);
235     }
236 
237     @DataProvider
238     @SuppressWarnings("unchecked")
239     public static Object[][] structs() {
240         TriFunction<GroupLayout, VarHandle, VarHandle, Function<VaList, Integer>> sumStructJavaFact
241                 = (pointLayout, VH_Point_x, VH_Point_y) ->
242                 list -> {
243                     MemorySegment struct = MemorySegment.allocateNative(pointLayout, ResourceScope.newImplicitScope());
244                     list.nextVarg(pointLayout, SegmentAllocator.prefixAllocator(struct));
245                     int x = (int) VH_Point_x.get(struct);
246                     int y = (int) VH_Point_y.get(struct);
247                     return x + y;
248                 };
249 
250         TriFunction<GroupLayout, VarHandle, VarHandle, Function<VaList, Integer>> sumStructNativeFact
251                 = (pointLayout, VH_Point_x, VH_Point_y) ->
252                 MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumStruct);
253 
254         TriFunction<Function<Consumer<VaList.Builder>, VaList>, MemoryLayout,
255                 TriFunction<GroupLayout, VarHandle, VarHandle, Function<VaList, Integer>>, Object[]> argsFact
256                 = (vaListFact, intLayout, sumStructFact) -> {
257             GroupLayout pointLayout =  MemoryLayout.structLayout(
258                     intLayout.withName("x"),
259                     intLayout.withName("y")
260             );
261             VarHandle VH_Point_x = pointLayout.varHandle(groupElement("x"));
262             VarHandle VH_Point_y = pointLayout.varHandle(groupElement("y"));
263             return new Object[] { vaListFact, sumStructFact.apply(pointLayout, VH_Point_x, VH_Point_y),
264                     pointLayout, VH_Point_x, VH_Point_y  };
265         };
266         return new Object[][]{
267                 argsFact.apply(winVaListFactory,          Win64.C_INT,   sumStructJavaFact),
268                 argsFact.apply(sysvVaListFactory,         SysV.C_INT,    sumStructJavaFact),
269                 argsFact.apply(linuxAArch64VaListFactory, AArch64.C_INT, sumStructJavaFact),
270                 argsFact.apply(macAArch64VaListFactory,   AArch64.C_INT, sumStructJavaFact),
271                 argsFact.apply(platformVaListFactory,     C_INT,         sumStructNativeFact),
272         };
273     }
274 
275     @Test(dataProvider = "structs")
276     public void testStruct(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
277                            Function<VaList, Integer> sumStruct,
278                            GroupLayout Point_LAYOUT, VarHandle VH_Point_x, VarHandle VH_Point_y) {
279         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
280             MemorySegment struct = MemorySegment.allocateNative(Point_LAYOUT, scope);
281             VH_Point_x.set(struct, 5);
282             VH_Point_y.set(struct, 10);
283 
284             VaList vaList = vaListFactory.apply(b -> b.addVarg(Point_LAYOUT, struct));
285             int sum = sumStruct.apply(vaList);
286             assertEquals(sum, 15);
287             vaList.scope().close();
288         }
289     }
290 
291     @DataProvider
292     @SuppressWarnings("unchecked")
293     public static Object[][] bigStructs() {
294         TriFunction<GroupLayout, VarHandle, VarHandle, Function<VaList, Long>> sumStructJavaFact
295                 = (BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y) ->
296                 list -> {
297                     MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, ResourceScope.newImplicitScope());
298                     list.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct));
299                     long x = (long) VH_BigPoint_x.get(struct);
300                     long y = (long) VH_BigPoint_y.get(struct);
301                     return x + y;
302                 };
303 
304         TriFunction<GroupLayout, VarHandle, VarHandle, Function<VaList, Long>> sumStructNativeFact
305                 = (pointLayout, VH_BigPoint_x, VH_BigPoint_y) ->
306                 MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumBigStruct);
307 
308         TriFunction<Function<Consumer<VaList.Builder>, VaList>, MemoryLayout,
309                 TriFunction<GroupLayout, VarHandle, VarHandle, Function<VaList, Long>>, Object[]> argsFact
310                 = (vaListFact, longLongLayout, sumBigStructFact) -> {
311             GroupLayout BigPoint_LAYOUT =  MemoryLayout.structLayout(
312                     longLongLayout.withName("x"),
313                     longLongLayout.withName("y")
314             );
315             VarHandle VH_BigPoint_x = BigPoint_LAYOUT.varHandle(groupElement("x"));
316             VarHandle VH_BigPoint_y = BigPoint_LAYOUT.varHandle(groupElement("y"));
317             return new Object[] { vaListFact, sumBigStructFact.apply(BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y),
318                     BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y  };
319         };
320         return new Object[][]{
321                 argsFact.apply(winVaListFactory,          Win64.C_LONG_LONG,   sumStructJavaFact),
322                 argsFact.apply(sysvVaListFactory,         SysV.C_LONG_LONG,    sumStructJavaFact),
323                 argsFact.apply(linuxAArch64VaListFactory, AArch64.C_LONG_LONG, sumStructJavaFact),
324                 argsFact.apply(macAArch64VaListFactory,   AArch64.C_LONG_LONG, sumStructJavaFact),
325                 argsFact.apply(platformVaListFactory,     C_LONG_LONG,         sumStructNativeFact),
326         };
327     }
328 
329     @Test(dataProvider = "bigStructs")
330     public void testBigStruct(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
331                               Function<VaList, Long> sumBigStruct,
332                               GroupLayout BigPoint_LAYOUT, VarHandle VH_BigPoint_x, VarHandle VH_BigPoint_y) {
333         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
334             MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, scope);
335             VH_BigPoint_x.set(struct, 5);
336             VH_BigPoint_y.set(struct, 10);
337 
338             VaList vaList = vaListFactory.apply(b -> b.addVarg(BigPoint_LAYOUT, struct));
339             long sum = sumBigStruct.apply(vaList);
340             assertEquals(sum, 15);
341             vaList.scope().close();
342         }
343     }
344 
345     @DataProvider
346     @SuppressWarnings("unchecked")
347     public static Object[][] floatStructs() {
348         TriFunction<GroupLayout, VarHandle, VarHandle, Function<VaList, Float>> sumStructJavaFact
349                 = (FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y) ->
350                 list -> {
351                     MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, ResourceScope.newImplicitScope());
352                     list.nextVarg(FloatPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct));
353                     float x = (float) VH_FloatPoint_x.get(struct);
354                     float y = (float) VH_FloatPoint_y.get(struct);
355                     return x + y;
356                 };
357 
358         TriFunction<GroupLayout, VarHandle, VarHandle, Function<VaList, Float>> sumStructNativeFact
359                 = (pointLayout, VH_FloatPoint_x, VH_FloatPoint_y) ->
360                 MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumFloatStruct);
361 
362         TriFunction<Function<Consumer<VaList.Builder>, VaList>, MemoryLayout,
363                 TriFunction<GroupLayout, VarHandle, VarHandle, Function<VaList, Float>>, Object[]> argsFact
364                 = (vaListFact, floatLayout, sumFloatStructFact) -> {
365             GroupLayout FloatPoint_LAYOUT = MemoryLayout.structLayout(
366                     floatLayout.withName("x"),
367                     floatLayout.withName("y")
368             );
369             VarHandle VH_FloatPoint_x = FloatPoint_LAYOUT.varHandle(groupElement("x"));
370             VarHandle VH_FloatPoint_y = FloatPoint_LAYOUT.varHandle(groupElement("y"));
371             return new Object[] { vaListFact, sumFloatStructFact.apply(FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y),
372                     FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y  };
373         };
374         return new Object[][]{
375                 argsFact.apply(winVaListFactory,          Win64.C_FLOAT,   sumStructJavaFact),
376                 argsFact.apply(sysvVaListFactory,         SysV.C_FLOAT,    sumStructJavaFact),
377                 argsFact.apply(linuxAArch64VaListFactory, AArch64.C_FLOAT, sumStructJavaFact),
378                 argsFact.apply(macAArch64VaListFactory,   AArch64.C_FLOAT, sumStructJavaFact),
379                 argsFact.apply(platformVaListFactory,     C_FLOAT,         sumStructNativeFact),
380         };
381     }
382 
383     @Test(dataProvider = "floatStructs")
384     public void testFloatStruct(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
385                                 Function<VaList, Float> sumFloatStruct,
386                                 GroupLayout FloatPoint_LAYOUT,
387                                 VarHandle VH_FloatPoint_x, VarHandle VH_FloatPoint_y) {
388         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
389             MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, scope);
390             VH_FloatPoint_x.set(struct, 1.234f);
391             VH_FloatPoint_y.set(struct, 3.142f);
392 
393             VaList vaList = vaListFactory.apply(b -> b.addVarg(FloatPoint_LAYOUT, struct));
394             float sum = sumFloatStruct.apply(vaList);
395             assertEquals(sum, 4.376f, 0.00001f);
396             vaList.scope().close();
397         }
398     }
399 
400     interface QuadFunc<T0, T1, T2, T3, R> {
401         R apply(T0 t0, T1 t1, T2 t2, T3 t3);
402     }
403 
404     @DataProvider
405     @SuppressWarnings("unchecked")
406     public static Object[][] hugeStructs() {
407         QuadFunc<GroupLayout, VarHandle, VarHandle, VarHandle, Function<VaList, Long>> sumStructJavaFact
408                 = (HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z) ->
409                 list -> {
410                     MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, ResourceScope.newImplicitScope());
411                     list.nextVarg(HugePoint_LAYOUT, SegmentAllocator.prefixAllocator(struct));
412                     long x = (long) VH_HugePoint_x.get(struct);
413                     long y = (long) VH_HugePoint_y.get(struct);
414                     long z = (long) VH_HugePoint_z.get(struct);
415                     return x + y + z;
416                 };
417 
418         QuadFunc<GroupLayout, VarHandle, VarHandle, VarHandle, Function<VaList, Long>> sumStructNativeFact
419                 = (pointLayout, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z) ->
420                 MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumHugeStruct);
421 
422         TriFunction<Function<Consumer<VaList.Builder>, VaList>, MemoryLayout,
423                 QuadFunc<GroupLayout, VarHandle, VarHandle, VarHandle, Function<VaList, Long>>, Object[]> argsFact
424                 = (vaListFact, longLongLayout, sumBigStructFact) -> {
425             GroupLayout HugePoint_LAYOUT = MemoryLayout.structLayout(
426                     longLongLayout.withName("x"),
427                     longLongLayout.withName("y"),
428                     longLongLayout.withName("z")
429             );
430             VarHandle VH_HugePoint_x = HugePoint_LAYOUT.varHandle(groupElement("x"));
431             VarHandle VH_HugePoint_y = HugePoint_LAYOUT.varHandle(groupElement("y"));
432             VarHandle VH_HugePoint_z = HugePoint_LAYOUT.varHandle(groupElement("z"));
433             return new Object[] { vaListFact,
434                     sumBigStructFact.apply(HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z),
435                     HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z  };
436         };
437         return new Object[][]{
438                 argsFact.apply(winVaListFactory,          Win64.C_LONG_LONG,   sumStructJavaFact),
439                 argsFact.apply(sysvVaListFactory,         SysV.C_LONG_LONG,    sumStructJavaFact),
440                 argsFact.apply(linuxAArch64VaListFactory, AArch64.C_LONG_LONG, sumStructJavaFact),
441                 argsFact.apply(macAArch64VaListFactory,   AArch64.C_LONG_LONG, sumStructJavaFact),
442                 argsFact.apply(platformVaListFactory,     C_LONG_LONG,         sumStructNativeFact),
443         };
444     }
445 
446     @Test(dataProvider = "hugeStructs")
447     public void testHugeStruct(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
448                                Function<VaList, Long> sumHugeStruct,
449                                GroupLayout HugePoint_LAYOUT,
450                                VarHandle VH_HugePoint_x, VarHandle VH_HugePoint_y, VarHandle VH_HugePoint_z) {
451         // On AArch64 a struct needs to be larger than 16 bytes to be
452         // passed by reference.
453         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
454             MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, scope);
455             VH_HugePoint_x.set(struct, 1);
456             VH_HugePoint_y.set(struct, 2);
457             VH_HugePoint_z.set(struct, 3);
458 
459             VaList vaList = vaListFactory.apply(b -> b.addVarg(HugePoint_LAYOUT, struct));
460             long sum = sumHugeStruct.apply(vaList);
461             assertEquals(sum, 6);
462             vaList.scope().close();
463         }
464     }
465 
466     public interface SumStackFunc {
467         void invoke(MemorySegment longSum, MemorySegment doubleSum, VaList list);
468     }
469 
470     @DataProvider
471     public static Object[][] sumStack() {
472         BiFunction<ValueLayout.OfLong, ValueLayout.OfDouble, SumStackFunc> sumStackJavaFact = (longLayout, doubleLayout) ->
473                 (longSum, doubleSum, list) -> {
474                     long lSum = 0L;
475                     for (int i = 0; i < 16; i++) {
476                         lSum += list.nextVarg(longLayout);
477                     }
478                     longSum.set(JAVA_LONG, 0, lSum);
479                     double dSum = 0D;
480                     for (int i = 0; i < 16; i++) {
481                         dSum += list.nextVarg(doubleLayout);
482                     }
483                     doubleSum.set(JAVA_DOUBLE, 0, dSum);
484                 };
485         SumStackFunc sumStackNative = (longSum, doubleSum, list) -> {
486             try {
487                 MH_sumStack.invoke(longSum, doubleSum, list);
488             } catch (Throwable ex) {
489                 throw new AssertionError(ex);
490             }
491         };
492         return new Object[][]{
493                 { winVaListFactory,           sumStackJavaFact.apply(Win64.C_LONG_LONG, Win64.C_DOUBLE),     Win64.C_LONG_LONG,   Win64.C_DOUBLE   },
494                 { sysvVaListFactory,          sumStackJavaFact.apply(SysV.C_LONG_LONG, SysV.C_DOUBLE),       SysV.C_LONG_LONG,    SysV.C_DOUBLE    },
495                 { linuxAArch64VaListFactory,  sumStackJavaFact.apply(AArch64.C_LONG_LONG, AArch64.C_DOUBLE), AArch64.C_LONG_LONG, AArch64.C_DOUBLE },
496                 { macAArch64VaListFactory,    sumStackJavaFact.apply(AArch64.C_LONG_LONG, AArch64.C_DOUBLE), AArch64.C_LONG_LONG, AArch64.C_DOUBLE },
497                 { platformVaListFactory,      sumStackNative,                                                C_LONG_LONG,         C_DOUBLE         },
498         };
499     }
500 
501     @Test(dataProvider = "sumStack")
502     public void testStack(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
503                           SumStackFunc sumStack,
504                           ValueLayout.OfLong longLayout,
505                           ValueLayout.OfDouble doubleLayout) {
506         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
507             MemorySegment longSum = MemorySegment.allocateNative(longLayout, scope);
508             MemorySegment doubleSum = MemorySegment.allocateNative(doubleLayout, scope);
509             longSum.set(JAVA_LONG, 0, 0L);
510             doubleSum.set(JAVA_DOUBLE, 0, 0D);
511 
512             VaList list = vaListFactory.apply(b -> {
513                 for (long l = 1; l <= 16L; l++) {
514                     b.addVarg(longLayout, l);
515                 }
516                 for (double d = 1; d <= 16D; d++) {
517                     b.addVarg(doubleLayout, d);
518                 }
519             });
520 
521             try {
522                 sumStack.invoke(longSum, doubleSum, list);
523             } finally {
524                 list.scope().close();
525             }
526 
527             long lSum = longSum.get(JAVA_LONG, 0);
528             double dSum = doubleSum.get(JAVA_DOUBLE, 0);
529 
530             assertEquals(lSum, 136L);
531             assertEquals(dSum, 136D);
532         }
533     }
534 
535     @Test(dataProvider = "upcalls")
536     public void testUpcall(MethodHandle target, MethodHandle callback) throws Throwable {
537         FunctionDescriptor desc = FunctionDescriptor.ofVoid(C_POINTER);
538         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
539             NativeSymbol stub = abi.upcallStub(callback, desc, scope);
540             target.invoke(stub);
541         }
542     }
543 
544     @DataProvider
545     public Object[][] emptyVaLists() {
546         return new Object[][] {
547                 { Windowsx64Linker.emptyVaList()           },
548                 { winVaListFactory.apply(b -> {})          },
549                 { SysVx64Linker.emptyVaList()              },
550                 { sysvVaListFactory.apply(b -> {})         },
551                 { LinuxAArch64Linker.emptyVaList()         },
552                 { linuxAArch64VaListFactory.apply(b -> {}) },
553                 { MacOsAArch64Linker.emptyVaList()         },
554                 { macAArch64VaListFactory.apply(b -> {})   },
555         };
556     }
557 
558     @Test(expectedExceptions = UnsupportedOperationException.class,
559             expectedExceptionsMessageRegExp = ".*Scope cannot be closed.*",
560             dataProvider = "emptyVaLists")
561     public void testEmptyNotCloseable(VaList emptyList) {
562         emptyList.scope().close();
563     }
564 
565     @DataProvider
566     @SuppressWarnings("unchecked")
567     public static Object[][] sumIntsScoped() {
568         Function<ValueLayout.OfInt, BiFunction<Integer, VaList, Integer>> sumIntsJavaFact = layout ->
569                 (num, list) -> IntStream.generate(() -> list.nextVarg(layout)).limit(num).sum();
570         BiFunction<Integer, VaList, Integer> sumIntsNative
571                 = MethodHandleProxies.asInterfaceInstance(BiFunction.class, MH_sumInts);
572         return new Object[][]{
573                 { winVaListScopedFactory,          sumIntsJavaFact.apply(Win64.C_INT),   Win64.C_INT   },
574                 { sysvVaListScopedFactory,         sumIntsJavaFact.apply(SysV.C_INT),    SysV.C_INT    },
575                 { linuxAArch64VaListScopedFactory, sumIntsJavaFact.apply(AArch64.C_INT), AArch64.C_INT },
576                 { macAArch64VaListScopedFactory,   sumIntsJavaFact.apply(AArch64.C_INT), AArch64.C_INT },
577                 { platformVaListScopedFactory,     sumIntsNative,                        C_INT         },
578         };
579     }
580 
581     @Test(dataProvider = "sumIntsScoped")
582     public void testScopedVaList(BiFunction<Consumer<VaList.Builder>, ResourceScope, VaList> vaListFactory,
583                                  BiFunction<Integer, VaList, Integer> sumInts,
584                                  ValueLayout.OfInt intLayout) {
585         VaList listLeaked;
586         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
587             VaList list = vaListFactory.apply(b -> b.addVarg(intLayout, 4)
588                             .addVarg(intLayout, 8), scope);
589             int x = sumInts.apply(2, list);
590             assertEquals(x, 12);
591             listLeaked = list;
592         }
593         assertFalse(listLeaked.scope().isAlive());
594     }
595 
596     @Test(dataProvider = "structs")
597     public void testScopeMSRead(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
598                                 Function<VaList, Integer> sumStruct, // ignored
599                                 GroupLayout Point_LAYOUT, VarHandle VH_Point_x, VarHandle VH_Point_y) {
600         MemorySegment pointOut;
601         try (ResourceScope scope = ResourceScope.newConfinedScope()) {
602             try (ResourceScope innerScope = ResourceScope.newConfinedScope()) {
603                 MemorySegment pointIn = MemorySegment.allocateNative(Point_LAYOUT, innerScope);
604                 VH_Point_x.set(pointIn, 3);
605                 VH_Point_y.set(pointIn, 6);
606                 VaList list = vaListFactory.apply(b -> b.addVarg(Point_LAYOUT, pointIn));
607                 pointOut = MemorySegment.allocateNative(Point_LAYOUT, scope);
608                 list.nextVarg(Point_LAYOUT, SegmentAllocator.prefixAllocator(pointOut));
609                 assertEquals((int) VH_Point_x.get(pointOut), 3);
610                 assertEquals((int) VH_Point_y.get(pointOut), 6);
611                 list.scope().close();
612                 assertTrue(pointOut.scope().isAlive()); // after VaList freed
613             }
614             assertTrue(pointOut.scope().isAlive()); // after inner scope freed
615         }
616         assertFalse(pointOut.scope().isAlive()); // after outer scope freed
617     }
618 
619     @DataProvider
620     public Object[][] copy() {
621         return new Object[][] {
622                 { winVaListFactory,          Win64.C_INT   },
623                 { sysvVaListFactory,         SysV.C_INT    },
624                 { linuxAArch64VaListFactory, AArch64.C_INT },
625                 { macAArch64VaListFactory,   AArch64.C_INT },
626         };
627     }
628 
629     @Test(dataProvider = "copy")
630     public void testCopy(Function<Consumer<VaList.Builder>, VaList> vaListFactory, ValueLayout.OfInt intLayout) {
631         VaList list = vaListFactory.apply(b -> b.addVarg(intLayout, 4)
632                 .addVarg(intLayout, 8));
633         VaList  copy = list.copy();
634         assertEquals(copy.nextVarg(intLayout), 4);
635         assertEquals(copy.nextVarg(intLayout), 8);
636 
637 //        try { // this logic only works on Windows!
638 //            int x = copy.vargAsInt(intLayout);
639 //            fail();
640 //        } catch (IndexOutOfBoundsException ex) {
641 //            // ok - we exhausted the list
642 //        }
643 
644         assertEquals(list.nextVarg(intLayout), 4);
645         assertEquals(list.nextVarg(intLayout), 8);
646         list.scope().close();
647     }
648 
649     @Test(dataProvider = "copy",
650             expectedExceptions = IllegalStateException.class)
651     public void testCopyUnusableAfterOriginalClosed(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
652                                                     ValueLayout.OfInt intLayout) {
653         VaList list = vaListFactory.apply(b -> b.addVarg(intLayout, 4)
654                 .addVarg(intLayout, 8));
655         VaList copy = list.copy();
656         list.scope().close();
657 
658         copy.nextVarg(intLayout); // should throw
659     }
660 
661     @DataProvider
662     public static Object[][] upcalls() {
663         GroupLayout BigPoint_LAYOUT = MemoryLayout.structLayout(
664                 C_LONG_LONG.withName("x"),
665                 C_LONG_LONG.withName("y")
666         );
667         VarHandle VH_BigPoint_x = BigPoint_LAYOUT.varHandle(groupElement("x"));
668         VarHandle VH_BigPoint_y = BigPoint_LAYOUT.varHandle(groupElement("y"));
669         GroupLayout Point_LAYOUT = MemoryLayout.structLayout(
670                 C_INT.withName("x"),
671                 C_INT.withName("y")
672         );
673         VarHandle VH_Point_x = Point_LAYOUT.varHandle(groupElement("x"));
674         VarHandle VH_Point_y = Point_LAYOUT.varHandle(groupElement("y"));
675         GroupLayout FloatPoint_LAYOUT = MemoryLayout.structLayout(
676                 C_FLOAT.withName("x"),
677                 C_FLOAT.withName("y")
678         );
679         VarHandle VH_FloatPoint_x = FloatPoint_LAYOUT.varHandle(groupElement("x"));
680         VarHandle VH_FloatPoint_y = FloatPoint_LAYOUT.varHandle(groupElement("y"));
681         GroupLayout HugePoint_LAYOUT = MemoryLayout.structLayout(
682                 C_LONG_LONG.withName("x"),
683                 C_LONG_LONG.withName("y"),
684                 C_LONG_LONG.withName("z")
685         );
686         VarHandle VH_HugePoint_x = HugePoint_LAYOUT.varHandle(groupElement("x"));
687         VarHandle VH_HugePoint_y = HugePoint_LAYOUT.varHandle(groupElement("y"));
688         VarHandle VH_HugePoint_z = HugePoint_LAYOUT.varHandle(groupElement("z"));
689 
690         return new Object[][]{
691                 { linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> {
692                     MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, ResourceScope.newImplicitScope());
693                     vaList.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct));
694                     assertEquals((long) VH_BigPoint_x.get(struct), 8);
695                     assertEquals((long) VH_BigPoint_y.get(struct), 16);
696                 })},
697                 { linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> {
698                     VaList copy = vaList.copy();
699                     MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, ResourceScope.newImplicitScope());
700                     vaList.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct));
701                     assertEquals((long) VH_BigPoint_x.get(struct), 8);
702                     assertEquals((long) VH_BigPoint_y.get(struct), 16);
703 
704                     VH_BigPoint_x.set(struct, 0);
705                     VH_BigPoint_y.set(struct, 0);
706 
707                     // should be independent
708                     copy.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct));
709                     assertEquals((long) VH_BigPoint_x.get(struct), 8);
710                     assertEquals((long) VH_BigPoint_y.get(struct), 16);
711                 })},
712                 { linkVaListCB("upcallBigStructPlusScalar"), VaListConsumer.mh(vaList -> {
713                     MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, ResourceScope.newImplicitScope());
714                     vaList.nextVarg(BigPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct));
715                     assertEquals((long) VH_BigPoint_x.get(struct), 8);
716                     assertEquals((long) VH_BigPoint_y.get(struct), 16);
717 
718                     assertEquals(vaList.nextVarg(C_LONG_LONG), 42);
719                 })},
720                 { linkVaListCB("upcallBigStructPlusScalar"), VaListConsumer.mh(vaList -> {
721                     vaList.skip(BigPoint_LAYOUT);
722                     assertEquals(vaList.nextVarg(C_LONG_LONG), 42);
723                 })},
724                 { linkVaListCB("upcallStruct"), VaListConsumer.mh(vaList -> {
725                     MemorySegment struct = MemorySegment.allocateNative(Point_LAYOUT, ResourceScope.newImplicitScope());
726                     vaList.nextVarg(Point_LAYOUT, SegmentAllocator.prefixAllocator(struct));
727                     assertEquals((int) VH_Point_x.get(struct), 5);
728                     assertEquals((int) VH_Point_y.get(struct), 10);
729                 })},
730                 { linkVaListCB("upcallHugeStruct"), VaListConsumer.mh(vaList -> {
731                     MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, ResourceScope.newImplicitScope());
732                     vaList.nextVarg(HugePoint_LAYOUT, SegmentAllocator.prefixAllocator(struct));
733                     assertEquals((long) VH_HugePoint_x.get(struct), 1);
734                     assertEquals((long) VH_HugePoint_y.get(struct), 2);
735                     assertEquals((long) VH_HugePoint_z.get(struct), 3);
736                 })},
737                 { linkVaListCB("upcallFloatStruct"), VaListConsumer.mh(vaList -> {
738                     MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, ResourceScope.newImplicitScope());
739                     vaList.nextVarg(FloatPoint_LAYOUT, SegmentAllocator.prefixAllocator(struct));
740                     assertEquals((float) VH_FloatPoint_x.get(struct), 1.0f);
741                     assertEquals((float) VH_FloatPoint_y.get(struct), 2.0f);
742                 })},
743                 { linkVaListCB("upcallMemoryAddress"), VaListConsumer.mh(vaList -> {
744                     MemoryAddress intPtr = vaList.nextVarg(C_POINTER);
745                     MemorySegment ms = MemorySegment.ofAddressNative(intPtr, C_INT.byteSize(), ResourceScope.globalScope());
746                     int x = ms.get(JAVA_INT, 0);
747                     assertEquals(x, 10);
748                 })},
749                 { linkVaListCB("upcallDoubles"), VaListConsumer.mh(vaList -> {
750                     assertEquals(vaList.nextVarg(C_DOUBLE), 3.0);
751                     assertEquals(vaList.nextVarg(C_DOUBLE), 4.0);
752                     assertEquals(vaList.nextVarg(C_DOUBLE), 5.0);
753                 })},
754                 { linkVaListCB("upcallInts"), VaListConsumer.mh(vaList -> {
755                     assertEquals(vaList.nextVarg(C_INT), 10);
756                     assertEquals(vaList.nextVarg(C_INT), 15);
757                     assertEquals(vaList.nextVarg(C_INT), 20);
758                 })},
759                 { linkVaListCB("upcallStack"), VaListConsumer.mh(vaList -> {
760                     // skip all registers
761                     for (long l = 1; l <= 16; l++) {
762                         assertEquals(vaList.nextVarg(C_LONG_LONG), l);
763                     }
764                     for (double d = 1; d <= 16; d++) {
765                         assertEquals(vaList.nextVarg(C_DOUBLE), d);
766                     }
767 
768                     // test some arbitrary values on the stack
769                     assertEquals((byte) vaList.nextVarg(C_INT), (byte) 1);
770                     assertEquals((char) vaList.nextVarg(C_INT), 'a');
771                     assertEquals((short) vaList.nextVarg(C_INT), (short) 3);
772                     assertEquals(vaList.nextVarg(C_INT), 4);
773                     assertEquals(vaList.nextVarg(C_LONG_LONG), 5L);
774                     assertEquals((float) vaList.nextVarg(C_DOUBLE), 6.0F);
775                     assertEquals(vaList.nextVarg(C_DOUBLE), 7.0D);
776                     assertEquals((byte) vaList.nextVarg(C_INT), (byte) 8);
777                     assertEquals((char) vaList.nextVarg(C_INT), 'b');
778                     assertEquals((short) vaList.nextVarg(C_INT), (short) 10);
779                     assertEquals(vaList.nextVarg(C_INT), 11);
780                     assertEquals(vaList.nextVarg(C_LONG_LONG), 12L);
781                     assertEquals((float) vaList.nextVarg(C_DOUBLE), 13.0F);
782                     assertEquals(vaList.nextVarg(C_DOUBLE), 14.0D);
783 
784                     MemorySegment buffer = MemorySegment.allocateNative(BigPoint_LAYOUT, ResourceScope.newImplicitScope());
785                     SegmentAllocator bufferAllocator = SegmentAllocator.prefixAllocator(buffer);
786 
787                     MemorySegment point = vaList.nextVarg(Point_LAYOUT, bufferAllocator);
788                     assertEquals((int) VH_Point_x.get(point), 5);
789                     assertEquals((int) VH_Point_y.get(point), 10);
790 
791                     VaList copy = vaList.copy();
792                     MemorySegment bigPoint = vaList.nextVarg(BigPoint_LAYOUT, bufferAllocator);
793                     assertEquals((long) VH_BigPoint_x.get(bigPoint), 15);
794                     assertEquals((long) VH_BigPoint_y.get(bigPoint), 20);
795 
796                     VH_BigPoint_x.set(bigPoint, 0);
797                     VH_BigPoint_y.set(bigPoint, 0);
798 
799                     // should be independent
800                     MemorySegment struct = copy.nextVarg(BigPoint_LAYOUT, bufferAllocator);
801                     assertEquals((long) VH_BigPoint_x.get(struct), 15);
802                     assertEquals((long) VH_BigPoint_y.get(struct), 20);
803                 })},
804                 // test skip
805                 { linkVaListCB("upcallStack"), VaListConsumer.mh(vaList -> {
806                     vaList.skip(C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG);
807                     assertEquals(vaList.nextVarg(C_LONG_LONG), 5L);
808                     vaList.skip(C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG);
809                     assertEquals(vaList.nextVarg(C_LONG_LONG), 10L);
810                     vaList.skip(C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG);
811                     assertEquals(vaList.nextVarg(C_DOUBLE), 1.0D);
812                     vaList.skip(C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE);
813                     assertEquals(vaList.nextVarg(C_DOUBLE), 6.0D);
814                 })},
815         };
816     }
817 
818     interface VaListConsumer {
819         void accept(VaList list);
820 
821         static MethodHandle mh(VaListConsumer instance) {
822             try {
823                 MethodHandle handle = MethodHandles.lookup().findVirtual(VaListConsumer.class, "accept",
824                         MethodType.methodType(void.class, VaList.class)).bindTo(instance);
825                 return MethodHandles.filterArguments(handle, 0,
826                         MethodHandles.insertArguments(ADDRESS_TO_VALIST, 1, ResourceScope.newConfinedScope()));
827             } catch (ReflectiveOperationException e) {
828                 throw new InternalError(e);
829             }
830         }
831     }
832 }