1 /*
  2  *  Copyright (c) 2020, 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  * @run testng/othervm -Dforeign.restricted=permit VaListTest
 28  */
 29 
 30 import jdk.incubator.foreign.CSupport;
 31 import jdk.incubator.foreign.CSupport.VaList;
 32 import jdk.incubator.foreign.ForeignLinker;
 33 import jdk.incubator.foreign.FunctionDescriptor;
 34 import jdk.incubator.foreign.GroupLayout;
 35 import jdk.incubator.foreign.LibraryLookup;
 36 import jdk.incubator.foreign.MemoryAddress;
 37 import jdk.incubator.foreign.MemoryLayout;
 38 import jdk.incubator.foreign.MemorySegment;
 39 import org.testng.annotations.DataProvider;
 40 import org.testng.annotations.Test;
 41 
 42 import java.lang.invoke.MethodHandle;
 43 import java.lang.invoke.MethodHandles;
 44 import java.lang.invoke.MethodType;
 45 import java.lang.invoke.VarHandle;
 46 
 47 import static jdk.incubator.foreign.CSupport.C_DOUBLE;
 48 import static jdk.incubator.foreign.CSupport.C_INT;
 49 import static jdk.incubator.foreign.CSupport.C_LONGLONG;
 50 import static jdk.incubator.foreign.CSupport.C_POINTER;
 51 import static jdk.incubator.foreign.CSupport.C_VA_LIST;
 52 import static jdk.incubator.foreign.CSupport.Win64.asVarArg;
 53 import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement;
 54 import static org.testng.Assert.assertEquals;
 55 
 56 public class VaListTest {
 57 
 58     private static final ForeignLinker abi = CSupport.getSystemLinker();
 59     private static final LibraryLookup lookup = LibraryLookup.ofLibrary("VaList");
 60 
 61     private static final VarHandle VH_int = C_INT.varHandle(int.class);
 62 
 63     private static final MethodHandle MH_sumInts = link("sumInts",
 64             MethodType.methodType(int.class, int.class, VaList.class),
 65             FunctionDescriptor.of(C_INT, C_INT, CSupport.C_VA_LIST));
 66     private static final MethodHandle MH_sumDoubles = link("sumDoubles",
 67             MethodType.methodType(double.class, int.class, VaList.class),
 68             FunctionDescriptor.of(C_DOUBLE, C_INT, CSupport.C_VA_LIST));
 69     private static final MethodHandle MH_getInt = link("getInt",
 70             MethodType.methodType(int.class, VaList.class),
 71             FunctionDescriptor.of(C_INT, C_VA_LIST));
 72     private static final MethodHandle MH_sumStruct = link("sumStruct",
 73             MethodType.methodType(int.class, VaList.class),
 74             FunctionDescriptor.of(C_INT, C_VA_LIST));
 75     private static final MethodHandle MH_sumBigStruct = link("sumBigStruct",
 76             MethodType.methodType(long.class, VaList.class),
 77             FunctionDescriptor.of(C_LONGLONG, C_VA_LIST));
 78     private static final MethodHandle MH_sumStack = link("sumStack",
 79             MethodType.methodType(void.class, MemoryAddress.class, MemoryAddress.class, int.class,
 80                 long.class, long.class, long.class, long.class,
 81                 long.class, long.class, long.class, long.class,
 82                 long.class, long.class, long.class, long.class,
 83                 long.class, long.class, long.class, long.class,
 84                 double.class, double.class, double.class, double.class,
 85                 double.class, double.class, double.class, double.class,
 86                 double.class, double.class, double.class, double.class,
 87                 double.class, double.class, double.class, double.class
 88             ),
 89             FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_INT,
 90                 asVarArg(C_LONGLONG), asVarArg(C_LONGLONG), asVarArg(C_LONGLONG), asVarArg(C_LONGLONG),
 91                 asVarArg(C_LONGLONG), asVarArg(C_LONGLONG), asVarArg(C_LONGLONG), asVarArg(C_LONGLONG),
 92                 asVarArg(C_LONGLONG), asVarArg(C_LONGLONG), asVarArg(C_LONGLONG), asVarArg(C_LONGLONG),
 93                 asVarArg(C_LONGLONG), asVarArg(C_LONGLONG), asVarArg(C_LONGLONG), asVarArg(C_LONGLONG),
 94                 asVarArg(C_DOUBLE), asVarArg(C_DOUBLE), asVarArg(C_DOUBLE), asVarArg(C_DOUBLE),
 95                 asVarArg(C_DOUBLE), asVarArg(C_DOUBLE), asVarArg(C_DOUBLE), asVarArg(C_DOUBLE),
 96                 asVarArg(C_DOUBLE), asVarArg(C_DOUBLE), asVarArg(C_DOUBLE), asVarArg(C_DOUBLE),
 97                 asVarArg(C_DOUBLE), asVarArg(C_DOUBLE), asVarArg(C_DOUBLE), asVarArg(C_DOUBLE)
 98             ));
 99 
100     private static final VarHandle VH_long = C_LONGLONG.varHandle(long.class);
101     private static final VarHandle VH_double = C_DOUBLE.varHandle(double.class);
102 
103     private static MethodHandle link(String symbol, MethodType mt, FunctionDescriptor fd) {
104         try {
105             return abi.downcallHandle(lookup.lookup(symbol), mt, fd);
106         } catch (NoSuchMethodException e) {
107             throw new NoSuchMethodError(e.getMessage());
108         }
109     }
110 
111     private static MethodHandle linkVaListCB(String symbol) {
112         return link(symbol,
113             MethodType.methodType(void.class, MemoryAddress.class),
114             FunctionDescriptor.ofVoid(C_POINTER));
115 
116     }
117 
118     private static final GroupLayout BigPoint_LAYOUT = MemoryLayout.ofStruct(
119         C_LONGLONG.withName("x"),
120         C_LONGLONG.withName("y")
121     );
122     private static final VarHandle VH_BigPoint_x = BigPoint_LAYOUT.varHandle(long.class, groupElement("x"));
123     private static final VarHandle VH_BigPoint_y = BigPoint_LAYOUT.varHandle(long.class, groupElement("y"));
124     private static final GroupLayout Point_LAYOUT = MemoryLayout.ofStruct(
125         C_INT.withName("x"),
126         C_INT.withName("y")
127     );
128     private static final VarHandle VH_Point_x = Point_LAYOUT.varHandle(int.class, groupElement("x"));
129     private static final VarHandle VH_Point_y = Point_LAYOUT.varHandle(int.class, groupElement("y"));
130 
131     @Test
132     public void testIntSum() throws Throwable {
133         try (VaList vaList = VaList.make(b ->
134                 b.vargFromInt(C_INT, 10)
135                  .vargFromInt(C_INT, 15)
136                  .vargFromInt(C_INT, 20))) {
137             int x = (int) MH_sumInts.invokeExact(3, vaList);
138             assertEquals(x, 45);
139         }
140     }
141 
142     @Test
143     public void testDoubleSum() throws Throwable {
144         try (VaList vaList = VaList.make(b ->
145                 b.vargFromDouble(C_DOUBLE, 3.0D)
146                  .vargFromDouble(C_DOUBLE, 4.0D)
147                  .vargFromDouble(C_DOUBLE, 5.0D))) {
148             double x = (double) MH_sumDoubles.invokeExact(3, vaList);
149             assertEquals(x, 12.0D);
150         }
151     }
152 
153     @Test
154     public void testVaListMemoryAddress() throws Throwable {
155         try (MemorySegment msInt = MemorySegment.allocateNative(C_INT)) {
156             VH_int.set(msInt.baseAddress(), 10);
157             try (VaList vaList = VaList.make(b -> b.vargFromAddress(C_POINTER, msInt.baseAddress()))) {
158                 int x = (int) MH_getInt.invokeExact(vaList);
159                 assertEquals(x, 10);
160             }
161         }
162     }
163 
164     @Test
165     public void testWinStructByValue() throws Throwable {
166         try (MemorySegment struct = MemorySegment.allocateNative(Point_LAYOUT)) {
167             VH_Point_x.set(struct.baseAddress(), 5);
168             VH_Point_y.set(struct.baseAddress(), 10);
169 
170             try (VaList vaList = VaList.make(b -> b.vargFromSegment(Point_LAYOUT, struct))) {
171                 int sum = (int) MH_sumStruct.invokeExact(vaList);
172                 assertEquals(sum, 15);
173             }
174         }
175     }
176 
177     @Test
178     public void testWinStructByReference() throws Throwable {
179         try (MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT)) {
180             VH_BigPoint_x.set(struct.baseAddress(), 5);
181             VH_BigPoint_y.set(struct.baseAddress(), 10);
182 
183             try (VaList vaList = VaList.make(b -> b.vargFromSegment(BigPoint_LAYOUT, struct))) {
184                 long sum = (long) MH_sumBigStruct.invokeExact(vaList);
185                 assertEquals(sum, 15);
186             }
187         }
188     }
189 
190     @Test
191     public void testStack() throws Throwable {
192        try (MemorySegment longSum = MemorySegment.allocateNative(C_LONGLONG);
193             MemorySegment doubleSum = MemorySegment.allocateNative(C_DOUBLE)) {
194             VH_long.set(longSum.baseAddress(), 0L);
195             VH_double.set(doubleSum.baseAddress(), 0D);
196 
197             MH_sumStack.invokeExact(longSum.baseAddress(), doubleSum.baseAddress(), 32,
198                 1L, 2L, 3L, 4L, 5L, 6L, 7L, 8L, 9L, 10L, 11L, 12L, 13L, 14L, 15L, 16L,
199                 1D, 2D, 3D, 4D, 5D, 6D, 7D, 8D, 9D, 10D, 11D, 12D, 13D, 14D, 15D, 16D);
200 
201             long lSum = (long) VH_long.get(longSum.baseAddress());
202             double dSum = (double) VH_double.get(doubleSum.baseAddress());
203 
204             assertEquals(lSum, 136L);
205             assertEquals(dSum, 136D);
206         }
207     }
208 
209     @Test(dataProvider = "upcalls")
210     public void testUpcall(MethodHandle target, MethodHandle callback) throws Throwable {
211         FunctionDescriptor desc = FunctionDescriptor.ofVoid(C_VA_LIST);
212         try (MemorySegment stub = abi.upcallStub(callback, desc)) {
213             target.invokeExact(stub.baseAddress());
214         }
215     }
216 
217     @Test(expectedExceptions = UnsupportedOperationException.class,
218           expectedExceptionsMessageRegExp = ".*Empty VaList.*")
219     public void testEmptyNotCloseable() {
220         VaList list = VaList.empty();
221         list.close();
222     }
223 
224     @Test(expectedExceptions = UnsupportedOperationException.class,
225           expectedExceptionsMessageRegExp = ".*Empty VaList.*")
226     public void testEmptyVaListFromBuilderNotCloseable() {
227         VaList list = VaList.make(b -> {});
228         list.close();
229     }
230 
231     @DataProvider
232     public static Object[][] upcalls() {
233         return new Object[][]{
234             { linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> {
235                 try (MemorySegment struct = vaList.vargAsSegment(BigPoint_LAYOUT)) {
236                     assertEquals((long) VH_BigPoint_x.get(struct.baseAddress()), 8);
237                     assertEquals((long) VH_BigPoint_y.get(struct.baseAddress()), 16);
238                 }
239             })},
240             { linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> {
241                 VaList copy = vaList.copy();
242                 try (MemorySegment struct = vaList.vargAsSegment(BigPoint_LAYOUT)) {
243                     assertEquals((long) VH_BigPoint_x.get(struct.baseAddress()), 8);
244                     assertEquals((long) VH_BigPoint_y.get(struct.baseAddress()), 16);
245 
246                     VH_BigPoint_x.set(struct.baseAddress(), 0);
247                     VH_BigPoint_y.set(struct.baseAddress(), 0);
248                 }
249 
250                 // should be independent
251                 try (MemorySegment struct = copy.vargAsSegment(BigPoint_LAYOUT)) {
252                     assertEquals((long) VH_BigPoint_x.get(struct.baseAddress()), 8);
253                     assertEquals((long) VH_BigPoint_y.get(struct.baseAddress()), 16);
254                 }
255             })},
256             { linkVaListCB("upcallStruct"), VaListConsumer.mh(vaList -> {
257                 try (MemorySegment struct = vaList.vargAsSegment(Point_LAYOUT)) {
258                     assertEquals((int) VH_Point_x.get(struct.baseAddress()), 5);
259                     assertEquals((int) VH_Point_y.get(struct.baseAddress()), 10);
260                 }
261             })},
262             { linkVaListCB("upcallMemoryAddress"), VaListConsumer.mh(vaList -> {
263                 MemoryAddress intPtr = vaList.vargAsAddress(C_POINTER);
264                 MemorySegment ms = MemorySegment.ofNativeRestricted(intPtr, C_INT.byteSize(),
265                                                                     Thread.currentThread(), null, null);
266                 int x = (int) VH_int.get(ms.baseAddress());
267                 assertEquals(x, 10);
268             })},
269             { linkVaListCB("upcallDoubles"), VaListConsumer.mh(vaList -> {
270                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 3.0);
271                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 4.0);
272                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 5.0);
273             })},
274             { linkVaListCB("upcallInts"), VaListConsumer.mh(vaList -> {
275                 assertEquals(vaList.vargAsInt(C_INT), 10);
276                 assertEquals(vaList.vargAsInt(C_INT), 15);
277                 assertEquals(vaList.vargAsInt(C_INT), 20);
278             })},
279             { linkVaListCB("upcallStack"), VaListConsumer.mh(vaList -> {
280                 // skip all registers
281                 assertEquals(vaList.vargAsLong(C_LONGLONG), 1L); // 1st windows arg read from shadow space
282                 assertEquals(vaList.vargAsLong(C_LONGLONG), 2L); // 2nd windows arg read from shadow space
283                 assertEquals(vaList.vargAsLong(C_LONGLONG), 3L); // windows 1st stack arg (int/float)
284                 assertEquals(vaList.vargAsLong(C_LONGLONG), 4L);
285                 assertEquals(vaList.vargAsLong(C_LONGLONG), 5L);
286                 assertEquals(vaList.vargAsLong(C_LONGLONG), 6L);
287                 assertEquals(vaList.vargAsLong(C_LONGLONG), 7L); // sysv 1st int stack arg
288                 assertEquals(vaList.vargAsLong(C_LONGLONG), 8L);
289                 assertEquals(vaList.vargAsLong(C_LONGLONG), 9L);
290                 assertEquals(vaList.vargAsLong(C_LONGLONG), 10L);
291                 assertEquals(vaList.vargAsLong(C_LONGLONG), 11L);
292                 assertEquals(vaList.vargAsLong(C_LONGLONG), 12L);
293                 assertEquals(vaList.vargAsLong(C_LONGLONG), 13L);
294                 assertEquals(vaList.vargAsLong(C_LONGLONG), 14L);
295                 assertEquals(vaList.vargAsLong(C_LONGLONG), 15L);
296                 assertEquals(vaList.vargAsLong(C_LONGLONG), 16L);
297                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 1.0D);
298                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 2.0D);
299                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 3.0D);
300                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 4.0D);
301                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 5.0D);
302                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 6.0D);
303                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 7.0D);
304                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 8.0D);
305                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 9.0D); // sysv 1st float stack arg
306                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 10.0D);
307                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 11.0D);
308                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 12.0D);
309                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 13.0D);
310                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 14.0D);
311                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 15.0D);
312                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 16.0D);
313 
314                 // test some arbitrary values on the stack
315                 assertEquals((byte) vaList.vargAsInt(C_INT), (byte) 1);
316                 assertEquals((char) vaList.vargAsInt(C_INT), 'a');
317                 assertEquals((short) vaList.vargAsInt(C_INT), (short) 3);
318                 assertEquals(vaList.vargAsInt(C_INT), 4);
319                 assertEquals(vaList.vargAsLong(C_LONGLONG), 5L);
320                 assertEquals((float) vaList.vargAsDouble(C_DOUBLE), 6.0F);
321                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 7.0D);
322                 assertEquals((byte) vaList.vargAsInt(C_INT), (byte) 8);
323                 assertEquals((char) vaList.vargAsInt(C_INT), 'b');
324                 assertEquals((short) vaList.vargAsInt(C_INT), (short) 10);
325                 assertEquals(vaList.vargAsInt(C_INT), 11);
326                 assertEquals(vaList.vargAsLong(C_LONGLONG), 12L);
327                 assertEquals((float) vaList.vargAsDouble(C_DOUBLE), 13.0F);
328                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 14.0D);
329 
330                 try (MemorySegment point = vaList.vargAsSegment(Point_LAYOUT)) {
331                     assertEquals((int) VH_Point_x.get(point.baseAddress()), 5);
332                     assertEquals((int) VH_Point_y.get(point.baseAddress()), 10);
333                 }
334 
335                 VaList copy = vaList.copy();
336                 try (MemorySegment bigPoint = vaList.vargAsSegment(BigPoint_LAYOUT)) {
337                     assertEquals((long) VH_BigPoint_x.get(bigPoint.baseAddress()), 15);
338                     assertEquals((long) VH_BigPoint_y.get(bigPoint.baseAddress()), 20);
339 
340                     VH_BigPoint_x.set(bigPoint.baseAddress(), 0);
341                     VH_BigPoint_y.set(bigPoint.baseAddress(), 0);
342                 }
343 
344                 // should be independent
345                 try (MemorySegment struct = copy.vargAsSegment(BigPoint_LAYOUT)) {
346                     assertEquals((long) VH_BigPoint_x.get(struct.baseAddress()), 15);
347                     assertEquals((long) VH_BigPoint_y.get(struct.baseAddress()), 20);
348                 }
349             })},
350             // test skip
351             { linkVaListCB("upcallStack"), VaListConsumer.mh(vaList -> {
352                 vaList.skip(C_LONGLONG, C_LONGLONG, C_LONGLONG, C_LONGLONG);
353                 assertEquals(vaList.vargAsLong(C_LONGLONG), 5L);
354                 vaList.skip(C_LONGLONG, C_LONGLONG, C_LONGLONG, C_LONGLONG);
355                 assertEquals(vaList.vargAsLong(C_LONGLONG), 10L);
356                 vaList.skip(C_LONGLONG, C_LONGLONG, C_LONGLONG, C_LONGLONG, C_LONGLONG, C_LONGLONG);
357                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 1.0D);
358                 vaList.skip(C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE);
359                 assertEquals(vaList.vargAsDouble(C_DOUBLE), 6.0D);
360             })},
361         };
362     }
363 
364     interface VaListConsumer {
365         void accept(CSupport.VaList list);
366 
367         static MethodHandle mh(VaListConsumer instance) {
368             try {
369                 return MethodHandles.lookup().findVirtual(VaListConsumer.class, "accept",
370                     MethodType.methodType(void.class, VaList.class)).bindTo(instance);
371             } catch (ReflectiveOperationException e) {
372                 throw new InternalError(e);
373             }
374         }
375     }
376 
377 }