Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/test/jdk/java/foreign/StdLibTest.java
41144 views
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
* @test
26
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
27
* @run testng/othervm --enable-native-access=ALL-UNNAMED StdLibTest
28
*/
29
30
import java.lang.invoke.MethodHandle;
31
import java.lang.invoke.MethodHandles;
32
import java.lang.invoke.MethodType;
33
import java.time.Instant;
34
import java.time.LocalDateTime;
35
import java.time.ZoneOffset;
36
import java.time.ZonedDateTime;
37
import java.util.ArrayList;
38
import java.util.Arrays;
39
import java.util.Collections;
40
import java.util.LinkedHashSet;
41
import java.util.List;
42
import java.util.Optional;
43
import java.util.Set;
44
import java.util.function.BiConsumer;
45
import java.util.function.Function;
46
import java.util.stream.Collectors;
47
import java.util.stream.Stream;
48
49
import jdk.incubator.foreign.*;
50
51
import static jdk.incubator.foreign.MemoryAccess.*;
52
53
import org.testng.annotations.*;
54
55
import static jdk.incubator.foreign.CLinker.*;
56
import static org.testng.Assert.*;
57
58
@Test
59
public class StdLibTest {
60
61
final static CLinker abi = CLinker.getInstance();
62
63
private StdLibHelper stdLibHelper = new StdLibHelper();
64
65
@Test(dataProvider = "stringPairs")
66
void test_strcat(String s1, String s2) throws Throwable {
67
assertEquals(stdLibHelper.strcat(s1, s2), s1 + s2);
68
}
69
70
@Test(dataProvider = "stringPairs")
71
void test_strcmp(String s1, String s2) throws Throwable {
72
assertEquals(Math.signum(stdLibHelper.strcmp(s1, s2)), Math.signum(s1.compareTo(s2)));
73
}
74
75
@Test(dataProvider = "strings")
76
void test_puts(String s) throws Throwable {
77
assertTrue(stdLibHelper.puts(s) >= 0);
78
}
79
80
@Test(dataProvider = "strings")
81
void test_strlen(String s) throws Throwable {
82
assertEquals(stdLibHelper.strlen(s), s.length());
83
}
84
85
@Test(dataProvider = "instants")
86
void test_time(Instant instant) throws Throwable {
87
StdLibHelper.Tm tm = stdLibHelper.gmtime(instant.getEpochSecond());
88
LocalDateTime localTime = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
89
assertEquals(tm.sec(), localTime.getSecond());
90
assertEquals(tm.min(), localTime.getMinute());
91
assertEquals(tm.hour(), localTime.getHour());
92
//day pf year in Java has 1-offset
93
assertEquals(tm.yday(), localTime.getDayOfYear() - 1);
94
assertEquals(tm.mday(), localTime.getDayOfMonth());
95
//days of week starts from Sunday in C, but on Monday in Java, also account for 1-offset
96
assertEquals((tm.wday() + 6) % 7, localTime.getDayOfWeek().getValue() - 1);
97
//month in Java has 1-offset
98
assertEquals(tm.mon(), localTime.getMonth().getValue() - 1);
99
assertEquals(tm.isdst(), ZoneOffset.UTC.getRules()
100
.isDaylightSavings(Instant.ofEpochMilli(instant.getEpochSecond() * 1000)));
101
}
102
103
@Test(dataProvider = "ints")
104
void test_qsort(List<Integer> ints) throws Throwable {
105
if (ints.size() > 0) {
106
int[] input = ints.stream().mapToInt(i -> i).toArray();
107
int[] sorted = stdLibHelper.qsort(input);
108
Arrays.sort(input);
109
assertEquals(sorted, input);
110
}
111
}
112
113
@Test
114
void test_rand() throws Throwable {
115
int val = stdLibHelper.rand();
116
for (int i = 0 ; i < 100 ; i++) {
117
int newVal = stdLibHelper.rand();
118
if (newVal != val) {
119
return; //ok
120
}
121
val = newVal;
122
}
123
fail("All values are the same! " + val);
124
}
125
126
@Test(dataProvider = "printfArgs")
127
void test_printf(List<PrintfArg> args) throws Throwable {
128
String formatArgs = args.stream()
129
.map(a -> a.format)
130
.collect(Collectors.joining(","));
131
132
String formatString = "hello(" + formatArgs + ")\n";
133
134
String expected = String.format(formatString, args.stream()
135
.map(a -> a.javaValue).toArray());
136
137
int found = stdLibHelper.printf(formatString, args);
138
assertEquals(found, expected.length());
139
}
140
141
@Test(dataProvider = "printfArgs")
142
void test_vprintf(List<PrintfArg> args) throws Throwable {
143
String formatArgs = args.stream()
144
.map(a -> a.format)
145
.collect(Collectors.joining(","));
146
147
String formatString = "hello(" + formatArgs + ")\n";
148
149
String expected = String.format(formatString, args.stream()
150
.map(a -> a.javaValue).toArray());
151
152
int found = stdLibHelper.vprintf(formatString, args);
153
assertEquals(found, expected.length());
154
}
155
156
static class StdLibHelper {
157
158
static final SymbolLookup LOOKUP;
159
160
static {
161
System.loadLibrary("StdLib");
162
SymbolLookup stdLibLookup = SymbolLookup.loaderLookup();
163
MemorySegment funcs = stdLibLookup.lookup("funcs").get()
164
.asSegment(C_POINTER.byteSize() * 3, ResourceScope.newImplicitScope());
165
166
SymbolLookup fallbackLookup = name -> switch (name) {
167
case "printf" -> Optional.of(MemoryAccess.getAddressAtIndex(funcs, 0));
168
case "vprintf" -> Optional.of(MemoryAccess.getAddressAtIndex(funcs, 1));
169
case "gmtime" -> Optional.of(MemoryAccess.getAddressAtIndex(funcs, 2));
170
default -> Optional.empty();
171
};
172
173
LOOKUP = name -> CLinker.systemLookup().lookup(name).or(() -> fallbackLookup.lookup(name));
174
}
175
176
final static MethodHandle strcat = abi.downcallHandle(LOOKUP.lookup("strcat").get(),
177
MethodType.methodType(MemoryAddress.class, MemoryAddress.class, MemoryAddress.class),
178
FunctionDescriptor.of(C_POINTER, C_POINTER, C_POINTER));
179
180
final static MethodHandle strcmp = abi.downcallHandle(LOOKUP.lookup("strcmp").get(),
181
MethodType.methodType(int.class, MemoryAddress.class, MemoryAddress.class),
182
FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER));
183
184
final static MethodHandle puts = abi.downcallHandle(LOOKUP.lookup("puts").get(),
185
MethodType.methodType(int.class, MemoryAddress.class),
186
FunctionDescriptor.of(C_INT, C_POINTER));
187
188
final static MethodHandle strlen = abi.downcallHandle(LOOKUP.lookup("strlen").get(),
189
MethodType.methodType(int.class, MemoryAddress.class),
190
FunctionDescriptor.of(C_INT, C_POINTER));
191
192
final static MethodHandle gmtime = abi.downcallHandle(LOOKUP.lookup("gmtime").get(),
193
MethodType.methodType(MemoryAddress.class, MemoryAddress.class),
194
FunctionDescriptor.of(C_POINTER, C_POINTER));
195
196
final static MethodHandle qsort = abi.downcallHandle(LOOKUP.lookup("qsort").get(),
197
MethodType.methodType(void.class, MemoryAddress.class, long.class, long.class, MemoryAddress.class),
198
FunctionDescriptor.ofVoid(C_POINTER, C_LONG_LONG, C_LONG_LONG, C_POINTER));
199
200
final static FunctionDescriptor qsortComparFunction = FunctionDescriptor.of(C_INT, C_POINTER, C_POINTER);
201
202
final static MethodHandle qsortCompar;
203
204
final static MethodHandle rand = abi.downcallHandle(LOOKUP.lookup("rand").get(),
205
MethodType.methodType(int.class),
206
FunctionDescriptor.of(C_INT));
207
208
final static MethodHandle vprintf = abi.downcallHandle(LOOKUP.lookup("vprintf").get(),
209
MethodType.methodType(int.class, MemoryAddress.class, VaList.class),
210
FunctionDescriptor.of(C_INT, C_POINTER, C_VA_LIST));
211
212
final static MemoryAddress printfAddr = LOOKUP.lookup("printf").get();
213
214
final static FunctionDescriptor printfBase = FunctionDescriptor.of(C_INT, C_POINTER);
215
216
static {
217
try {
218
//qsort upcall handle
219
qsortCompar = MethodHandles.lookup().findStatic(StdLibTest.StdLibHelper.class, "qsortCompare",
220
MethodType.methodType(int.class, MemorySegment.class, MemoryAddress.class, MemoryAddress.class));
221
} catch (ReflectiveOperationException ex) {
222
throw new IllegalStateException(ex);
223
}
224
}
225
226
String strcat(String s1, String s2) throws Throwable {
227
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
228
MemorySegment buf = MemorySegment.allocateNative(s1.length() + s2.length() + 1, scope);
229
MemorySegment other = toCString(s2, scope);
230
char[] chars = s1.toCharArray();
231
for (long i = 0 ; i < chars.length ; i++) {
232
setByteAtOffset(buf, i, (byte)chars[(int)i]);
233
}
234
setByteAtOffset(buf, chars.length, (byte)'\0');
235
return toJavaString(((MemoryAddress)strcat.invokeExact(buf.address(), other.address())));
236
}
237
}
238
239
int strcmp(String s1, String s2) throws Throwable {
240
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
241
MemorySegment ns1 = toCString(s1, scope);
242
MemorySegment ns2 = toCString(s2, scope);
243
return (int)strcmp.invokeExact(ns1.address(), ns2.address());
244
}
245
}
246
247
int puts(String msg) throws Throwable {
248
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
249
MemorySegment s = toCString(msg, scope);
250
return (int)puts.invokeExact(s.address());
251
}
252
}
253
254
int strlen(String msg) throws Throwable {
255
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
256
MemorySegment s = toCString(msg, scope);
257
return (int)strlen.invokeExact(s.address());
258
}
259
}
260
261
Tm gmtime(long arg) throws Throwable {
262
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
263
MemorySegment time = MemorySegment.allocateNative(8, scope);
264
setLong(time, arg);
265
return new Tm((MemoryAddress)gmtime.invokeExact(time.address()));
266
}
267
}
268
269
static class Tm {
270
271
//Tm pointer should never be freed directly, as it points to shared memory
272
private final MemorySegment base;
273
274
static final long SIZE = 56;
275
276
Tm(MemoryAddress addr) {
277
this.base = addr.asSegment(SIZE, ResourceScope.globalScope());
278
}
279
280
int sec() {
281
return getIntAtOffset(base, 0);
282
}
283
int min() {
284
return getIntAtOffset(base, 4);
285
}
286
int hour() {
287
return getIntAtOffset(base, 8);
288
}
289
int mday() {
290
return getIntAtOffset(base, 12);
291
}
292
int mon() {
293
return getIntAtOffset(base, 16);
294
}
295
int year() {
296
return getIntAtOffset(base, 20);
297
}
298
int wday() {
299
return getIntAtOffset(base, 24);
300
}
301
int yday() {
302
return getIntAtOffset(base, 28);
303
}
304
boolean isdst() {
305
byte b = getByteAtOffset(base, 32);
306
return b != 0;
307
}
308
}
309
310
int[] qsort(int[] arr) throws Throwable {
311
//init native array
312
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
313
SegmentAllocator allocator = SegmentAllocator.ofScope(scope);
314
MemorySegment nativeArr = allocator.allocateArray(C_INT, arr);
315
316
//call qsort
317
MemoryAddress qsortUpcallStub = abi.upcallStub(qsortCompar.bindTo(nativeArr), qsortComparFunction, scope);
318
319
qsort.invokeExact(nativeArr.address(), (long)arr.length, C_INT.byteSize(), qsortUpcallStub);
320
321
//convert back to Java array
322
return nativeArr.toIntArray();
323
}
324
}
325
326
static int qsortCompare(MemorySegment base, MemoryAddress addr1, MemoryAddress addr2) {
327
return getIntAtOffset(base, addr1.segmentOffset(base)) -
328
getIntAtOffset(base, addr2.segmentOffset(base));
329
}
330
331
int rand() throws Throwable {
332
return (int)rand.invokeExact();
333
}
334
335
int printf(String format, List<PrintfArg> args) throws Throwable {
336
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
337
MemorySegment formatStr = toCString(format, scope);
338
return (int)specializedPrintf(args).invokeExact(formatStr.address(),
339
args.stream().map(a -> a.nativeValue(scope)).toArray());
340
}
341
}
342
343
int vprintf(String format, List<PrintfArg> args) throws Throwable {
344
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
345
MemorySegment formatStr = toCString(format, scope);
346
VaList vaList = VaList.make(b -> args.forEach(a -> a.accept(b, scope)), scope);
347
return (int)vprintf.invokeExact(formatStr.address(), vaList);
348
}
349
}
350
351
private MethodHandle specializedPrintf(List<PrintfArg> args) {
352
//method type
353
MethodType mt = MethodType.methodType(int.class, MemoryAddress.class);
354
FunctionDescriptor fd = printfBase;
355
for (PrintfArg arg : args) {
356
mt = mt.appendParameterTypes(arg.carrier);
357
fd = fd.withAppendedArgumentLayouts(arg.layout);
358
}
359
MethodHandle mh = abi.downcallHandle(printfAddr, mt, fd);
360
return mh.asSpreader(1, Object[].class, args.size());
361
}
362
}
363
364
/*** data providers ***/
365
366
@DataProvider
367
public static Object[][] ints() {
368
return perms(0, new Integer[] { 0, 1, 2, 3, 4 }).stream()
369
.map(l -> new Object[] { l })
370
.toArray(Object[][]::new);
371
}
372
373
@DataProvider
374
public static Object[][] strings() {
375
return perms(0, new String[] { "a", "b", "c" }).stream()
376
.map(l -> new Object[] { String.join("", l) })
377
.toArray(Object[][]::new);
378
}
379
380
@DataProvider
381
public static Object[][] stringPairs() {
382
Object[][] strings = strings();
383
Object[][] stringPairs = new Object[strings.length * strings.length][];
384
int pos = 0;
385
for (Object[] s1 : strings) {
386
for (Object[] s2 : strings) {
387
stringPairs[pos++] = new Object[] { s1[0], s2[0] };
388
}
389
}
390
return stringPairs;
391
}
392
393
@DataProvider
394
public static Object[][] instants() {
395
Instant start = ZonedDateTime.of(LocalDateTime.parse("2017-01-01T00:00:00"), ZoneOffset.UTC).toInstant();
396
Instant end = ZonedDateTime.of(LocalDateTime.parse("2017-12-31T00:00:00"), ZoneOffset.UTC).toInstant();
397
Object[][] instants = new Object[100][];
398
for (int i = 0 ; i < instants.length ; i++) {
399
Instant instant = start.plusSeconds((long)(Math.random() * (end.getEpochSecond() - start.getEpochSecond())));
400
instants[i] = new Object[] { instant };
401
}
402
return instants;
403
}
404
405
@DataProvider
406
public static Object[][] printfArgs() {
407
ArrayList<List<PrintfArg>> res = new ArrayList<>();
408
List<List<PrintfArg>> perms = new ArrayList<>(perms(0, PrintfArg.values()));
409
for (int i = 0 ; i < 100 ; i++) {
410
Collections.shuffle(perms);
411
res.addAll(perms);
412
}
413
return res.stream()
414
.map(l -> new Object[] { l })
415
.toArray(Object[][]::new);
416
}
417
418
enum PrintfArg implements BiConsumer<VaList.Builder, ResourceScope> {
419
420
INTEGRAL(int.class, asVarArg(C_INT), "%d", scope -> 42, 42, VaList.Builder::vargFromInt),
421
STRING(MemoryAddress.class, asVarArg(C_POINTER), "%s", scope -> toCString("str", scope).address(), "str", VaList.Builder::vargFromAddress),
422
CHAR(byte.class, asVarArg(C_CHAR), "%c", scope -> (byte) 'h', 'h', (builder, layout, value) -> builder.vargFromInt(C_INT, (int)value)),
423
DOUBLE(double.class, asVarArg(C_DOUBLE), "%.4f", scope ->1.2345d, 1.2345d, VaList.Builder::vargFromDouble);
424
425
final Class<?> carrier;
426
final ValueLayout layout;
427
final String format;
428
final Function<ResourceScope, ?> nativeValueFactory;
429
final Object javaValue;
430
@SuppressWarnings("rawtypes")
431
final VaListBuilderCall builderCall;
432
433
<Z> PrintfArg(Class<?> carrier, ValueLayout layout, String format, Function<ResourceScope, Z> nativeValueFactory, Object javaValue, VaListBuilderCall<Z> builderCall) {
434
this.carrier = carrier;
435
this.layout = layout;
436
this.format = format;
437
this.nativeValueFactory = nativeValueFactory;
438
this.javaValue = javaValue;
439
this.builderCall = builderCall;
440
}
441
442
@Override
443
@SuppressWarnings("unchecked")
444
public void accept(VaList.Builder builder, ResourceScope scope) {
445
builderCall.build(builder, layout, nativeValueFactory.apply(scope));
446
}
447
448
interface VaListBuilderCall<V> {
449
void build(VaList.Builder builder, ValueLayout layout, V value);
450
}
451
452
public Object nativeValue(ResourceScope scope) {
453
return nativeValueFactory.apply(scope);
454
}
455
}
456
457
static <Z> Set<List<Z>> perms(int count, Z[] arr) {
458
if (count == arr.length) {
459
return Set.of(List.of());
460
} else {
461
return Arrays.stream(arr)
462
.flatMap(num -> {
463
Set<List<Z>> perms = perms(count + 1, arr);
464
return Stream.concat(
465
//take n
466
perms.stream().map(l -> {
467
List<Z> li = new ArrayList<>(l);
468
li.add(num);
469
return li;
470
}),
471
//drop n
472
perms.stream());
473
}).collect(Collectors.toCollection(LinkedHashSet::new));
474
}
475
}
476
}
477
478