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 @DataProvider
218 public static Object[][] upcalls() {
219 return new Object[][]{
220 { linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> {
221 try (MemorySegment struct = vaList.vargAsSegment(BigPoint_LAYOUT)) {
222 assertEquals((long) VH_BigPoint_x.get(struct.baseAddress()), 8);
223 assertEquals((long) VH_BigPoint_y.get(struct.baseAddress()), 16);
224 }
225 })},
226 { linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> {
227 VaList copy = vaList.copy();
228 try (MemorySegment struct = vaList.vargAsSegment(BigPoint_LAYOUT)) {
229 assertEquals((long) VH_BigPoint_x.get(struct.baseAddress()), 8);
230 assertEquals((long) VH_BigPoint_y.get(struct.baseAddress()), 16);
231
232 VH_BigPoint_x.set(struct.baseAddress(), 0);
233 VH_BigPoint_y.set(struct.baseAddress(), 0);
234 }
235
236 // should be independent
237 try (MemorySegment struct = copy.vargAsSegment(BigPoint_LAYOUT)) {
238 assertEquals((long) VH_BigPoint_x.get(struct.baseAddress()), 8);
239 assertEquals((long) VH_BigPoint_y.get(struct.baseAddress()), 16);
240 }
241 })},
242 { linkVaListCB("upcallStruct"), VaListConsumer.mh(vaList -> {
243 try (MemorySegment struct = vaList.vargAsSegment(Point_LAYOUT)) {
244 assertEquals((int) VH_Point_x.get(struct.baseAddress()), 5);
245 assertEquals((int) VH_Point_y.get(struct.baseAddress()), 10);
246 }
247 })},
248 { linkVaListCB("upcallMemoryAddress"), VaListConsumer.mh(vaList -> {
249 MemoryAddress intPtr = vaList.vargAsAddress(C_POINTER);
250 MemorySegment ms = MemorySegment.ofNativeRestricted(intPtr, C_INT.byteSize(),
251 Thread.currentThread(), null, null);
252 int x = (int) VH_int.get(ms.baseAddress());
253 assertEquals(x, 10);
254 })},
255 { linkVaListCB("upcallDoubles"), VaListConsumer.mh(vaList -> {
256 assertEquals(vaList.vargAsDouble(C_DOUBLE), 3.0);
257 assertEquals(vaList.vargAsDouble(C_DOUBLE), 4.0);
258 assertEquals(vaList.vargAsDouble(C_DOUBLE), 5.0);
259 })},
260 { linkVaListCB("upcallInts"), VaListConsumer.mh(vaList -> {
261 assertEquals(vaList.vargAsInt(C_INT), 10);
262 assertEquals(vaList.vargAsInt(C_INT), 15);
263 assertEquals(vaList.vargAsInt(C_INT), 20);
264 })},
265 { linkVaListCB("upcallStack"), VaListConsumer.mh(vaList -> {
266 // skip all registers
267 assertEquals(vaList.vargAsLong(C_LONGLONG), 1L); // 1st windows arg read from shadow space
268 assertEquals(vaList.vargAsLong(C_LONGLONG), 2L); // 2nd windows arg read from shadow space
269 assertEquals(vaList.vargAsLong(C_LONGLONG), 3L); // windows 1st stack arg (int/float)
270 assertEquals(vaList.vargAsLong(C_LONGLONG), 4L);
271 assertEquals(vaList.vargAsLong(C_LONGLONG), 5L);
272 assertEquals(vaList.vargAsLong(C_LONGLONG), 6L);
273 assertEquals(vaList.vargAsLong(C_LONGLONG), 7L); // sysv 1st int stack arg
274 assertEquals(vaList.vargAsLong(C_LONGLONG), 8L);
275 assertEquals(vaList.vargAsLong(C_LONGLONG), 9L);
276 assertEquals(vaList.vargAsLong(C_LONGLONG), 10L);
277 assertEquals(vaList.vargAsLong(C_LONGLONG), 11L);
278 assertEquals(vaList.vargAsLong(C_LONGLONG), 12L);
279 assertEquals(vaList.vargAsLong(C_LONGLONG), 13L);
280 assertEquals(vaList.vargAsLong(C_LONGLONG), 14L);
281 assertEquals(vaList.vargAsLong(C_LONGLONG), 15L);
282 assertEquals(vaList.vargAsLong(C_LONGLONG), 16L);
283 assertEquals(vaList.vargAsDouble(C_DOUBLE), 1.0D);
284 assertEquals(vaList.vargAsDouble(C_DOUBLE), 2.0D);
285 assertEquals(vaList.vargAsDouble(C_DOUBLE), 3.0D);
286 assertEquals(vaList.vargAsDouble(C_DOUBLE), 4.0D);
287 assertEquals(vaList.vargAsDouble(C_DOUBLE), 5.0D);
288 assertEquals(vaList.vargAsDouble(C_DOUBLE), 6.0D);
289 assertEquals(vaList.vargAsDouble(C_DOUBLE), 7.0D);
290 assertEquals(vaList.vargAsDouble(C_DOUBLE), 8.0D);
291 assertEquals(vaList.vargAsDouble(C_DOUBLE), 9.0D); // sysv 1st float stack arg
292 assertEquals(vaList.vargAsDouble(C_DOUBLE), 10.0D);
293 assertEquals(vaList.vargAsDouble(C_DOUBLE), 11.0D);
294 assertEquals(vaList.vargAsDouble(C_DOUBLE), 12.0D);
295 assertEquals(vaList.vargAsDouble(C_DOUBLE), 13.0D);
296 assertEquals(vaList.vargAsDouble(C_DOUBLE), 14.0D);
297 assertEquals(vaList.vargAsDouble(C_DOUBLE), 15.0D);
298 assertEquals(vaList.vargAsDouble(C_DOUBLE), 16.0D);
299
300 // test some arbitrary values on the stack
301 assertEquals((byte) vaList.vargAsInt(C_INT), (byte) 1);
302 assertEquals((char) vaList.vargAsInt(C_INT), 'a');
303 assertEquals((short) vaList.vargAsInt(C_INT), (short) 3);
304 assertEquals(vaList.vargAsInt(C_INT), 4);
305 assertEquals(vaList.vargAsLong(C_LONGLONG), 5L);
306 assertEquals((float) vaList.vargAsDouble(C_DOUBLE), 6.0F);
307 assertEquals(vaList.vargAsDouble(C_DOUBLE), 7.0D);
308 assertEquals((byte) vaList.vargAsInt(C_INT), (byte) 8);
309 assertEquals((char) vaList.vargAsInt(C_INT), 'b');
310 assertEquals((short) vaList.vargAsInt(C_INT), (short) 10);
311 assertEquals(vaList.vargAsInt(C_INT), 11);
312 assertEquals(vaList.vargAsLong(C_LONGLONG), 12L);
313 assertEquals((float) vaList.vargAsDouble(C_DOUBLE), 13.0F);
314 assertEquals(vaList.vargAsDouble(C_DOUBLE), 14.0D);
315
316 try (MemorySegment point = vaList.vargAsSegment(Point_LAYOUT)) {
317 assertEquals((int) VH_Point_x.get(point.baseAddress()), 5);
318 assertEquals((int) VH_Point_y.get(point.baseAddress()), 10);
319 }
320
321 VaList copy = vaList.copy();
322 try (MemorySegment bigPoint = vaList.vargAsSegment(BigPoint_LAYOUT)) {
323 assertEquals((long) VH_BigPoint_x.get(bigPoint.baseAddress()), 15);
324 assertEquals((long) VH_BigPoint_y.get(bigPoint.baseAddress()), 20);
325
326 VH_BigPoint_x.set(bigPoint.baseAddress(), 0);
327 VH_BigPoint_y.set(bigPoint.baseAddress(), 0);
328 }
329
330 // should be independent
331 try (MemorySegment struct = copy.vargAsSegment(BigPoint_LAYOUT)) {
332 assertEquals((long) VH_BigPoint_x.get(struct.baseAddress()), 15);
333 assertEquals((long) VH_BigPoint_y.get(struct.baseAddress()), 20);
334 }
335 })},
336 // test skip
337 { linkVaListCB("upcallStack"), VaListConsumer.mh(vaList -> {
338 vaList.skip(C_LONGLONG, C_LONGLONG, C_LONGLONG, C_LONGLONG);
339 assertEquals(vaList.vargAsLong(C_LONGLONG), 5L);
340 vaList.skip(C_LONGLONG, C_LONGLONG, C_LONGLONG, C_LONGLONG);
341 assertEquals(vaList.vargAsLong(C_LONGLONG), 10L);
342 vaList.skip(C_LONGLONG, C_LONGLONG, C_LONGLONG, C_LONGLONG, C_LONGLONG, C_LONGLONG);
343 assertEquals(vaList.vargAsDouble(C_DOUBLE), 1.0D);
344 vaList.skip(C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE);
345 assertEquals(vaList.vargAsDouble(C_DOUBLE), 6.0D);
346 })},
347 };
348 }
349
350 interface VaListConsumer {
351 void accept(CSupport.VaList list);
352
353 static MethodHandle mh(VaListConsumer instance) {
354 try {
355 return MethodHandles.lookup().findVirtual(VaListConsumer.class, "accept",
356 MethodType.methodType(void.class, VaList.class)).bindTo(instance);
357 } catch (ReflectiveOperationException e) {
358 throw new InternalError(e);
359 }
360 }
361 }
362
363 }