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 }