8303582: Reduce duplication in jdk/java/foreign tests
Reviewed-by: mcimadamore
This commit is contained in:
parent
8e2014527e
commit
dccfe8a2ee
@ -29,17 +29,22 @@ package jdk.internal.foreign;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.StructLayout;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Supplier;
|
||||
import jdk.internal.access.SharedSecrets;
|
||||
import jdk.internal.foreign.abi.SharedUtils;
|
||||
import jdk.internal.vm.annotation.ForceInline;
|
||||
import sun.invoke.util.Wrapper;
|
||||
|
||||
import static java.lang.foreign.ValueLayout.JAVA_BYTE;
|
||||
|
||||
/**
|
||||
@ -199,4 +204,41 @@ public final class Utils {
|
||||
throw new IllegalArgumentException("Invalid alignment constraint : " + byteAlignment);
|
||||
}
|
||||
}
|
||||
|
||||
private static long computePadding(long offset, long align) {
|
||||
boolean isAligned = offset == 0 || offset % align == 0;
|
||||
if (isAligned) {
|
||||
return 0;
|
||||
} else {
|
||||
long gap = offset % align;
|
||||
return align - gap;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@return return a struct layout constructed from the given elements, with padding
|
||||
* computed automatically so that they are naturally aligned}.
|
||||
*
|
||||
* @param elements the structs' fields
|
||||
*/
|
||||
public static StructLayout computePaddedStructLayout(MemoryLayout... elements) {
|
||||
long offset = 0L;
|
||||
List<MemoryLayout> layouts = new ArrayList<>();
|
||||
long align = 0;
|
||||
for (MemoryLayout l : elements) {
|
||||
long padding = computePadding(offset, l.bitAlignment());
|
||||
if (padding != 0) {
|
||||
layouts.add(MemoryLayout.paddingLayout(padding));
|
||||
offset += padding;
|
||||
}
|
||||
layouts.add(l);
|
||||
align = Math.max(align, l.bitAlignment());
|
||||
offset += l.bitSize();
|
||||
}
|
||||
long padding = computePadding(offset, align);
|
||||
if (padding != 0) {
|
||||
layouts.add(MemoryLayout.paddingLayout(padding));
|
||||
}
|
||||
return MemoryLayout.structLayout(layouts.toArray(MemoryLayout[]::new));
|
||||
}
|
||||
}
|
||||
|
@ -24,21 +24,17 @@
|
||||
|
||||
import java.lang.foreign.*;
|
||||
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Stack;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import jdk.internal.foreign.Utils;
|
||||
import org.testng.annotations.*;
|
||||
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class CallGeneratorHelper extends NativeTestHelper {
|
||||
|
||||
static final List<MemoryLayout> STACK_PREFIX_LAYOUTS = Stream.concat(
|
||||
@ -56,33 +52,6 @@ public class CallGeneratorHelper extends NativeTestHelper {
|
||||
static final int MAX_PARAMS = 3;
|
||||
static final int CHUNK_SIZE = 600;
|
||||
|
||||
public static void assertStructEquals(MemorySegment actual, MemorySegment expected, MemoryLayout layout) {
|
||||
assertEquals(actual.byteSize(), expected.byteSize());
|
||||
GroupLayout g = (GroupLayout) layout;
|
||||
for (MemoryLayout field : g.memberLayouts()) {
|
||||
if (field instanceof ValueLayout) {
|
||||
VarHandle vh = g.varHandle(MemoryLayout.PathElement.groupElement(field.name().orElseThrow()));
|
||||
assertEquals(vh.get(actual), vh.get(expected));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Class<?> vhCarrier(MemoryLayout layout) {
|
||||
if (layout instanceof ValueLayout) {
|
||||
if (isIntegral(layout)) {
|
||||
if (layout.bitSize() == 64) {
|
||||
return long.class;
|
||||
}
|
||||
return int.class;
|
||||
} else if (layout.bitSize() == 32) {
|
||||
return float.class;
|
||||
}
|
||||
return double.class;
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected layout: " + layout);
|
||||
}
|
||||
}
|
||||
|
||||
enum Ret {
|
||||
VOID,
|
||||
NON_VOID
|
||||
@ -140,25 +109,10 @@ public class CallGeneratorHelper extends NativeTestHelper {
|
||||
|
||||
MemoryLayout layout(List<StructFieldType> fields) {
|
||||
if (this == STRUCT) {
|
||||
long offset = 0L;
|
||||
List<MemoryLayout> layouts = new ArrayList<>();
|
||||
long align = 0;
|
||||
for (StructFieldType field : fields) {
|
||||
MemoryLayout l = field.layout();
|
||||
long padding = offset % l.bitAlignment();
|
||||
if (padding != 0) {
|
||||
layouts.add(MemoryLayout.paddingLayout(padding));
|
||||
offset += padding;
|
||||
}
|
||||
layouts.add(l.withName("field" + offset));
|
||||
align = Math.max(align, l.bitAlignment());
|
||||
offset += l.bitSize();
|
||||
}
|
||||
long padding = offset % align;
|
||||
if (padding != 0) {
|
||||
layouts.add(MemoryLayout.paddingLayout(padding));
|
||||
}
|
||||
return MemoryLayout.structLayout(layouts.toArray(new MemoryLayout[0]));
|
||||
return Utils.computePaddedStructLayout(
|
||||
IntStream.range(0, fields.size())
|
||||
.mapToObj(i -> fields.get(i).layout().withName("f" + i))
|
||||
.toArray(MemoryLayout[]::new));
|
||||
} else {
|
||||
return layout;
|
||||
}
|
||||
@ -376,79 +330,6 @@ public class CallGeneratorHelper extends NativeTestHelper {
|
||||
|
||||
//helper methods
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
static Object makeArg(MemoryLayout layout, List<Consumer<Object>> checks, boolean check) throws ReflectiveOperationException {
|
||||
if (layout instanceof GroupLayout) {
|
||||
MemorySegment segment = MemorySegment.allocateNative(layout, SegmentScope.auto());
|
||||
initStruct(segment, (GroupLayout)layout, checks, check);
|
||||
return segment;
|
||||
} else if (isPointer(layout)) {
|
||||
MemorySegment segment = MemorySegment.allocateNative(1L, SegmentScope.auto());
|
||||
if (check) {
|
||||
checks.add(o -> {
|
||||
try {
|
||||
assertEquals(o, segment);
|
||||
} catch (Throwable ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
return segment;
|
||||
} else if (layout instanceof ValueLayout) {
|
||||
if (isIntegral(layout)) {
|
||||
if (check) {
|
||||
checks.add(o -> assertEquals(o, 42));
|
||||
}
|
||||
return 42;
|
||||
} else if (layout.bitSize() == 32) {
|
||||
if (check) {
|
||||
checks.add(o -> assertEquals(o, 12f));
|
||||
}
|
||||
return 12f;
|
||||
} else {
|
||||
if (check) {
|
||||
checks.add(o -> assertEquals(o, 24d));
|
||||
}
|
||||
return 24d;
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected layout: " + layout);
|
||||
}
|
||||
}
|
||||
|
||||
static void initStruct(MemorySegment str, GroupLayout g, List<Consumer<Object>> checks, boolean check) throws ReflectiveOperationException {
|
||||
for (MemoryLayout l : g.memberLayouts()) {
|
||||
if (l instanceof PaddingLayout) continue;
|
||||
VarHandle accessor = g.varHandle(MemoryLayout.PathElement.groupElement(l.name().get()));
|
||||
List<Consumer<Object>> fieldsCheck = new ArrayList<>();
|
||||
Object value = makeArg(l, fieldsCheck, check);
|
||||
//set value
|
||||
accessor.set(str, value);
|
||||
//add check
|
||||
if (check) {
|
||||
assertTrue(fieldsCheck.size() == 1);
|
||||
checks.add(o -> {
|
||||
MemorySegment actual = (MemorySegment)o;
|
||||
try {
|
||||
fieldsCheck.get(0).accept(accessor.get(actual));
|
||||
} catch (Throwable ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static Class<?> carrier(MemoryLayout layout) {
|
||||
if (layout instanceof GroupLayout) {
|
||||
return MemorySegment.class;
|
||||
} if (layout instanceof ValueLayout valueLayout) {
|
||||
return valueLayout.carrier();
|
||||
} else {
|
||||
throw new IllegalStateException("Unexpected layout: " + layout);
|
||||
}
|
||||
}
|
||||
|
||||
MethodHandle downcallHandle(Linker abi, MemorySegment symbol, SegmentAllocator allocator, FunctionDescriptor descriptor) {
|
||||
MethodHandle mh = abi.downcallHandle(symbol, descriptor);
|
||||
if (descriptor.returnLayout().isPresent() && descriptor.returnLayout().get() instanceof GroupLayout) {
|
||||
|
@ -43,6 +43,8 @@ import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ThreadLocalRandom;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.UnaryOperator;
|
||||
@ -56,8 +58,14 @@ public class NativeTestHelper {
|
||||
public static final boolean IS_WINDOWS = System.getProperty("os.name").startsWith("Windows");
|
||||
|
||||
private static final MethodHandle MH_SAVER;
|
||||
private static final RandomGenerator DEFAULT_RANDOM;
|
||||
|
||||
static {
|
||||
int seed = Integer.getInteger("NativeTestHelper.DEFAULT_RANDOM.seed", ThreadLocalRandom.current().nextInt());
|
||||
System.out.println("NativeTestHelper::DEFAULT_RANDOM.seed = " + seed);
|
||||
System.out.println("Re-run with '-DNativeTestHelper.DEFAULT_RANDOM.seed=" + seed + "' to reproduce");
|
||||
DEFAULT_RANDOM = new Random(seed);
|
||||
|
||||
try {
|
||||
MH_SAVER = MethodHandles.lookup().findStatic(NativeTestHelper.class, "saver",
|
||||
MethodType.methodType(Object.class, Object[].class, List.class, AtomicReference.class, SegmentAllocator.class, int.class));
|
||||
@ -115,7 +123,7 @@ public class NativeTestHelper {
|
||||
*/
|
||||
public static final ValueLayout.OfAddress C_POINTER = ValueLayout.ADDRESS.withBitAlignment(64).asUnbounded();
|
||||
|
||||
private static final Linker LINKER = Linker.nativeLinker();
|
||||
public static final Linker LINKER = Linker.nativeLinker();
|
||||
|
||||
private static final MethodHandle FREE = LINKER.downcallHandle(
|
||||
LINKER.defaultLookup().find("free").get(), FunctionDescriptor.ofVoid(C_POINTER));
|
||||
@ -158,6 +166,10 @@ public class NativeTestHelper {
|
||||
|
||||
public record TestValue (Object value, Consumer<Object> check) {}
|
||||
|
||||
public static TestValue genTestValue(MemoryLayout layout, SegmentAllocator allocator) {
|
||||
return genTestValue(DEFAULT_RANDOM, layout, allocator);
|
||||
}
|
||||
|
||||
public static TestValue genTestValue(RandomGenerator random, MemoryLayout layout, SegmentAllocator allocator) {
|
||||
if (layout instanceof StructLayout struct) {
|
||||
MemorySegment segment = allocator.allocate(struct);
|
||||
|
@ -22,6 +22,7 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
@ -29,17 +30,15 @@ import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class TestDowncallBase extends CallGeneratorHelper {
|
||||
|
||||
static Linker LINKER = Linker.nativeLinker();
|
||||
|
||||
Object doCall(MemorySegment symbol, SegmentAllocator allocator, FunctionDescriptor descriptor, Object[] args) throws Throwable {
|
||||
MethodHandle mh = downcallHandle(LINKER, symbol, allocator, descriptor);
|
||||
Object res = mh.invokeWithArguments(args);
|
||||
return res;
|
||||
return mh.invokeWithArguments(args);
|
||||
}
|
||||
|
||||
static FunctionDescriptor function(Ret ret, List<ParamType> params, List<StructFieldType> fields, List<MemoryLayout> prefix) {
|
||||
@ -50,15 +49,17 @@ public class TestDowncallBase extends CallGeneratorHelper {
|
||||
FunctionDescriptor.of(paramLayouts[prefix.size()], paramLayouts);
|
||||
}
|
||||
|
||||
static Object[] makeArgs(List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks, List<MemoryLayout> prefix) throws ReflectiveOperationException {
|
||||
Object[] args = new Object[prefix.size() + params.size()];
|
||||
static Object[] makeArgs(Arena arena, FunctionDescriptor descriptor, List<Consumer<Object>> checks, int returnIdx) {
|
||||
List<MemoryLayout> argLayouts = descriptor.argumentLayouts();
|
||||
TestValue[] args = new TestValue[argLayouts.size()];
|
||||
int argNum = 0;
|
||||
for (MemoryLayout layout : prefix) {
|
||||
args[argNum++] = makeArg(layout, null, false);
|
||||
for (MemoryLayout layout : argLayouts) {
|
||||
args[argNum++] = genTestValue(layout, arena);
|
||||
}
|
||||
for (int i = 0 ; i < params.size() ; i++) {
|
||||
args[argNum++] = makeArg(params.get(i).layout(fields), checks, i == 0);
|
||||
|
||||
if (descriptor.returnLayout().isPresent()) {
|
||||
checks.add(args[returnIdx].check());
|
||||
}
|
||||
return args;
|
||||
return Stream.of(args).map(TestValue::value).toArray();
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
* @test
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestDowncallBase
|
||||
*
|
||||
* @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies
|
||||
@ -62,8 +63,8 @@ public class TestDowncallScope extends TestDowncallBase {
|
||||
List<Consumer<Object>> checks = new ArrayList<>();
|
||||
MemorySegment addr = findNativeOrThrow(fName);
|
||||
FunctionDescriptor descriptor = function(ret, paramTypes, fields);
|
||||
Object[] args = makeArgs(paramTypes, fields, checks);
|
||||
try (Arena arena = Arena.openShared()) {
|
||||
Object[] args = makeArgs(arena, descriptor, checks);
|
||||
boolean needsScope = descriptor.returnLayout().map(GroupLayout.class::isInstance).orElse(false);
|
||||
SegmentAllocator allocator = needsScope ?
|
||||
SegmentAllocator.nativeAllocator(arena.scope()) :
|
||||
@ -83,8 +84,8 @@ public class TestDowncallScope extends TestDowncallBase {
|
||||
return function(ret, params, fields, List.of());
|
||||
}
|
||||
|
||||
static Object[] makeArgs(List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks) throws ReflectiveOperationException {
|
||||
return makeArgs(params, fields, checks, List.of());
|
||||
static Object[] makeArgs(Arena arena, FunctionDescriptor descriptor, List<Consumer<Object>> checks) {
|
||||
return makeArgs(arena, descriptor, checks, 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
* @test
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestDowncallBase
|
||||
*
|
||||
* @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies
|
||||
@ -34,11 +35,7 @@
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
@ -58,11 +55,11 @@ public class TestDowncallStack extends TestDowncallBase {
|
||||
List<Consumer<Object>> checks = new ArrayList<>();
|
||||
MemorySegment addr = findNativeOrThrow("s" + fName);
|
||||
FunctionDescriptor descriptor = functionStack(ret, paramTypes, fields);
|
||||
Object[] args = makeArgsStack(paramTypes, fields, checks);
|
||||
try (Arena arena = Arena.openShared()) {
|
||||
Object[] args = makeArgsStack(arena, descriptor, checks);
|
||||
boolean needsScope = descriptor.returnLayout().map(GroupLayout.class::isInstance).orElse(false);
|
||||
SegmentAllocator allocator = needsScope ?
|
||||
SegmentAllocator.nativeAllocator(arena.scope()) :
|
||||
arena :
|
||||
THROWING_ALLOCATOR;
|
||||
Object res = doCall(addr, allocator, descriptor, args);
|
||||
if (ret == CallGeneratorHelper.Ret.NON_VOID) {
|
||||
@ -79,8 +76,8 @@ public class TestDowncallStack extends TestDowncallBase {
|
||||
return function(ret, params, fields, STACK_PREFIX_LAYOUTS);
|
||||
}
|
||||
|
||||
static Object[] makeArgsStack(List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks) throws ReflectiveOperationException {
|
||||
return makeArgs(params, fields, checks, STACK_PREFIX_LAYOUTS);
|
||||
static Object[] makeArgsStack(Arena arena, FunctionDescriptor descriptor, List<Consumer<Object>> checks) {
|
||||
return makeArgs(arena, descriptor, checks, STACK_PREFIX_LAYOUTS.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -34,6 +34,7 @@
|
||||
/* @test id=UpcallHighArity-FF
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallHighArity
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -46,6 +47,7 @@
|
||||
/* @test id=UpcallHighArity-TF
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallHighArity
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -58,6 +60,7 @@
|
||||
/* @test id=UpcallHighArity-FT
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallHighArity
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -70,6 +73,7 @@
|
||||
/* @test id=UpcallHighArity-TT
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallHighArity
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -82,6 +86,7 @@
|
||||
/* @test id=DowncallScope-F
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestDowncallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -93,6 +98,7 @@
|
||||
/* @test id=DowncallScope-T
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestDowncallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -104,6 +110,7 @@
|
||||
/* @test id=DowncallStack-F
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestDowncallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -115,6 +122,7 @@
|
||||
/* @test id=DowncallStack-T
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestDowncallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -126,6 +134,7 @@
|
||||
/* @test id=UpcallScope-FF
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -138,6 +147,7 @@
|
||||
/* @test id=UpcallScope-TF
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -150,6 +160,7 @@
|
||||
/* @test id=UpcallScope-FT
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -162,6 +173,7 @@
|
||||
/* @test id=UpcallScope-TT
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -174,6 +186,7 @@
|
||||
/* @test id=UpcallAsync-FF
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -186,6 +199,7 @@
|
||||
/* @test id=UpcallAsync-TF
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -198,6 +212,7 @@
|
||||
/* @test id=UpcallAsync-FT
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -210,6 +225,7 @@
|
||||
/* @test id=UpcallAsync-TT
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -222,6 +238,7 @@
|
||||
/* @test id=UpcallStack-FF
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -234,6 +251,7 @@
|
||||
/* @test id=UpcallStack-TF
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -246,6 +264,7 @@
|
||||
/* @test id=UpcallStack-FT
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -258,6 +277,7 @@
|
||||
/* @test id=UpcallStack-TT
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
@ -271,6 +291,7 @@
|
||||
* @test id=VarArgs
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper
|
||||
*
|
||||
* @run testng/othervm/native/manual
|
||||
|
@ -26,6 +26,7 @@
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @requires !vm.musl
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies
|
||||
@ -33,21 +34,17 @@
|
||||
* TestUpcallAsync
|
||||
*/
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.*;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class TestUpcallAsync extends TestUpcallBase {
|
||||
@ -60,30 +57,36 @@ public class TestUpcallAsync extends TestUpcallBase {
|
||||
@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
|
||||
public void testUpcallsAsync(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
|
||||
List<Consumer<Object>> returnChecks = new ArrayList<>();
|
||||
List<Consumer<Object[]>> argChecks = new ArrayList<>();
|
||||
List<Consumer<Object>> argChecks = new ArrayList<>();
|
||||
MemorySegment addr = findNativeOrThrow(fName);
|
||||
try (Arena arena = Arena.openShared()) {
|
||||
FunctionDescriptor descriptor = function(ret, paramTypes, fields);
|
||||
MethodHandle mh = downcallHandle(ABI, addr, arena, descriptor);
|
||||
Object[] args = makeArgs(SegmentScope.auto(), ret, paramTypes, fields, returnChecks, argChecks);
|
||||
MethodHandle mh = downcallHandle(LINKER, addr, arena, descriptor);
|
||||
AtomicReference<Object[]> capturedArgs = new AtomicReference<>();
|
||||
Object[] args = makeArgs(capturedArgs, arena, descriptor, returnChecks, argChecks, 0);
|
||||
|
||||
mh = mh.asSpreader(Object[].class, args.length);
|
||||
mh = MethodHandles.insertArguments(mh, 0, (Object) args);
|
||||
FunctionDescriptor callbackDesc = descriptor.returnLayout()
|
||||
.map(FunctionDescriptor::of)
|
||||
.orElse(FunctionDescriptor.ofVoid());
|
||||
MemorySegment callback = ABI.upcallStub(mh, callbackDesc, arena.scope());
|
||||
MemorySegment callback = LINKER.upcallStub(mh, callbackDesc, arena.scope());
|
||||
|
||||
MethodHandle invoker = asyncInvoker(ret, ret == Ret.VOID ? null : paramTypes.get(0), fields);
|
||||
|
||||
Object res = (descriptor.returnLayout().isPresent() &&
|
||||
descriptor.returnLayout().get() instanceof GroupLayout)
|
||||
? invoker.invoke(arena.scope(), callback)
|
||||
? invoker.invoke(arena, callback)
|
||||
: invoker.invoke(callback);
|
||||
argChecks.forEach(c -> c.accept(args));
|
||||
|
||||
if (ret == Ret.NON_VOID) {
|
||||
returnChecks.forEach(c -> c.accept(res));
|
||||
}
|
||||
|
||||
Object[] capturedArgsArr = capturedArgs.get();
|
||||
for (int i = 0; i < capturedArgsArr.length; i++) {
|
||||
argChecks.get(i).accept(capturedArgsArr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -93,7 +96,7 @@ public class TestUpcallAsync extends TestUpcallBase {
|
||||
if (ret == Ret.VOID) {
|
||||
String name = "call_async_V";
|
||||
return INVOKERS.computeIfAbsent(name, symbol ->
|
||||
ABI.downcallHandle(
|
||||
LINKER.downcallHandle(
|
||||
findNativeOrThrow(symbol),
|
||||
FunctionDescriptor.ofVoid(C_POINTER)));
|
||||
}
|
||||
@ -106,7 +109,7 @@ public class TestUpcallAsync extends TestUpcallBase {
|
||||
MemoryLayout returnLayout = returnType.layout(fields);
|
||||
FunctionDescriptor desc = FunctionDescriptor.of(returnLayout, C_POINTER);
|
||||
|
||||
return ABI.downcallHandle(invokerSymbol, desc);
|
||||
return LINKER.downcallHandle(invokerSymbol, desc);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -22,52 +22,18 @@
|
||||
*
|
||||
*/
|
||||
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.*;
|
||||
import java.lang.foreign.Arena;
|
||||
|
||||
import org.testng.annotations.BeforeClass;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static java.lang.invoke.MethodHandles.insertArguments;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
public abstract class TestUpcallBase extends CallGeneratorHelper {
|
||||
|
||||
static Linker ABI = Linker.nativeLinker();
|
||||
|
||||
private static MethodHandle DUMMY;
|
||||
private static MethodHandle PASS_AND_SAVE;
|
||||
|
||||
static {
|
||||
try {
|
||||
DUMMY = MethodHandles.lookup().findStatic(TestUpcallBase.class, "dummy", MethodType.methodType(void.class));
|
||||
PASS_AND_SAVE = MethodHandles.lookup().findStatic(TestUpcallBase.class, "passAndSave",
|
||||
MethodType.methodType(Object.class, Object[].class, AtomicReference.class, int.class, List.class));
|
||||
} catch (Throwable ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static MemorySegment DUMMY_STUB;
|
||||
|
||||
@BeforeClass
|
||||
void setup() {
|
||||
DUMMY_STUB = ABI.upcallStub(DUMMY, FunctionDescriptor.ofVoid(), SegmentScope.auto());
|
||||
}
|
||||
|
||||
static FunctionDescriptor function(Ret ret, List<ParamType> params, List<StructFieldType> fields) {
|
||||
return function(ret, params, fields, List.of());
|
||||
}
|
||||
@ -81,87 +47,34 @@ public abstract class TestUpcallBase extends CallGeneratorHelper {
|
||||
FunctionDescriptor.of(layouts[prefix.size()], layouts);
|
||||
}
|
||||
|
||||
static Object[] makeArgs(SegmentScope session, Ret ret, List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks, List<Consumer<Object[]>> argChecks) throws ReflectiveOperationException {
|
||||
return makeArgs(session, ret, params, fields, checks, argChecks, List.of());
|
||||
}
|
||||
static Object[] makeArgs(AtomicReference<Object[]> capturedArgs, Arena arena, FunctionDescriptor downcallDescriptor,
|
||||
List<Consumer<Object>> checks, List<Consumer<Object>> argChecks, int numPrefixArgs) {
|
||||
MemoryLayout[] upcallArgLayouts = downcallDescriptor.argumentLayouts()
|
||||
.subList(0, downcallDescriptor.argumentLayouts().size() - 1) // drop CB layout
|
||||
.toArray(MemoryLayout[]::new);
|
||||
|
||||
static Object[] makeArgs(SegmentScope session, Ret ret, List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks, List<Consumer<Object[]>> argChecks, List<MemoryLayout> prefix) throws ReflectiveOperationException {
|
||||
Object[] args = new Object[prefix.size() + params.size() + 1];
|
||||
int argNum = 0;
|
||||
for (MemoryLayout layout : prefix) {
|
||||
args[argNum++] = makeArg(layout, null, false);
|
||||
}
|
||||
for (int i = 0 ; i < params.size() ; i++) {
|
||||
args[argNum++] = makeArg(params.get(i).layout(fields), checks, i == 0);
|
||||
}
|
||||
args[argNum] = makeCallback(session, ret, params, fields, checks, argChecks, prefix);
|
||||
return args;
|
||||
}
|
||||
|
||||
static MemorySegment makeCallback(SegmentScope session, Ret ret, List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks, List<Consumer<Object[]>> argChecks, List<MemoryLayout> prefix) {
|
||||
if (params.isEmpty()) {
|
||||
return DUMMY_STUB;
|
||||
}
|
||||
|
||||
AtomicReference<Object[]> box = new AtomicReference<>();
|
||||
List<MemoryLayout> layouts = new ArrayList<>();
|
||||
layouts.addAll(prefix);
|
||||
for (int i = 0 ; i < params.size() ; i++) {
|
||||
layouts.add(params.get(i).layout(fields));
|
||||
}
|
||||
MethodHandle mh = insertArguments(PASS_AND_SAVE, 1, box, prefix.size(), layouts);
|
||||
mh = mh.asCollector(Object[].class, prefix.size() + params.size());
|
||||
|
||||
for(int i = 0; i < prefix.size(); i++) {
|
||||
mh = mh.asType(mh.type().changeParameterType(i, carrier(prefix.get(i))));
|
||||
}
|
||||
|
||||
for (int i = 0; i < params.size(); i++) {
|
||||
ParamType pt = params.get(i);
|
||||
MemoryLayout layout = pt.layout(fields);
|
||||
Class<?> carrier = carrier(layout);
|
||||
mh = mh.asType(mh.type().changeParameterType(prefix.size() + i, carrier));
|
||||
|
||||
final int finalI = prefix.size() + i;
|
||||
if (layout instanceof GroupLayout) {
|
||||
argChecks.add(o -> assertStructEquals((MemorySegment) box.get()[finalI], (MemorySegment) o[finalI], layout));
|
||||
} else {
|
||||
argChecks.add(o -> assertEquals(box.get()[finalI], o[finalI]));
|
||||
TestValue[] args = new TestValue[upcallArgLayouts.length];
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
MemoryLayout layout = upcallArgLayouts[i];
|
||||
TestValue testValue = genTestValue(layout, arena);
|
||||
args[i] = testValue;
|
||||
if (i >= numPrefixArgs) {
|
||||
argChecks.add(testValue.check());
|
||||
}
|
||||
}
|
||||
|
||||
ParamType firstParam = params.get(0);
|
||||
MemoryLayout firstlayout = firstParam.layout(fields);
|
||||
Class<?> firstCarrier = carrier(firstlayout);
|
||||
if (firstlayout instanceof GroupLayout) {
|
||||
checks.add(o -> assertStructEquals((MemorySegment) box.get()[prefix.size()], (MemorySegment) o, firstlayout));
|
||||
int returnedArgIdx;
|
||||
FunctionDescriptor upcallDescriptor;
|
||||
if (downcallDescriptor.returnLayout().isPresent()) {
|
||||
returnedArgIdx = numPrefixArgs;
|
||||
upcallDescriptor = FunctionDescriptor.of(downcallDescriptor.returnLayout().get(), upcallArgLayouts);
|
||||
checks.add(args[returnedArgIdx].check());
|
||||
} else {
|
||||
checks.add(o -> assertEquals(o, box.get()[prefix.size()]));
|
||||
returnedArgIdx = -1;
|
||||
upcallDescriptor = FunctionDescriptor.ofVoid(upcallArgLayouts);
|
||||
}
|
||||
|
||||
mh = mh.asType(mh.type().changeReturnType(ret == Ret.VOID ? void.class : firstCarrier));
|
||||
|
||||
MemoryLayout[] paramLayouts = Stream.concat(prefix.stream(), params.stream().map(p -> p.layout(fields))).toArray(MemoryLayout[]::new);
|
||||
FunctionDescriptor func = ret != Ret.VOID
|
||||
? FunctionDescriptor.of(firstlayout, paramLayouts)
|
||||
: FunctionDescriptor.ofVoid(paramLayouts);
|
||||
return ABI.upcallStub(mh, func, session);
|
||||
}
|
||||
|
||||
static Object passAndSave(Object[] o, AtomicReference<Object[]> ref, int retArg, List<MemoryLayout> layouts) {
|
||||
for (int i = 0; i < o.length; i++) {
|
||||
if (layouts.get(i) instanceof GroupLayout) {
|
||||
MemorySegment ms = (MemorySegment) o[i];
|
||||
MemorySegment copy = MemorySegment.allocateNative(ms.byteSize(), SegmentScope.auto());
|
||||
copy.copyFrom(ms);
|
||||
o[i] = copy;
|
||||
}
|
||||
}
|
||||
ref.set(o);
|
||||
return o[retArg];
|
||||
}
|
||||
|
||||
static void dummy() {
|
||||
//do nothing
|
||||
MemorySegment callback = makeArgSaverCB(upcallDescriptor, arena, capturedArgs, returnedArgIdx);
|
||||
return Stream.concat(Stream.of(args).map(TestValue::value), Stream.of(callback)).toArray();
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,7 @@
|
||||
* @test
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallHighArity
|
||||
*
|
||||
* @run testng/othervm/native
|
||||
@ -33,28 +34,20 @@
|
||||
* TestUpcallHighArity
|
||||
*/
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.*;
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class TestUpcallHighArity extends CallGeneratorHelper {
|
||||
static final MethodHandle MH_do_upcall;
|
||||
static final MethodHandle MH_passAndSave;
|
||||
static final Linker LINKER = Linker.nativeLinker();
|
||||
|
||||
// struct S_PDI { void* p0; double p1; int p2; };
|
||||
@ -66,60 +59,37 @@ public class TestUpcallHighArity extends CallGeneratorHelper {
|
||||
);
|
||||
|
||||
static {
|
||||
try {
|
||||
System.loadLibrary("TestUpcallHighArity");
|
||||
MH_do_upcall = LINKER.downcallHandle(
|
||||
findNativeOrThrow("do_upcall"),
|
||||
FunctionDescriptor.ofVoid(C_POINTER,
|
||||
S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER,
|
||||
S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER,
|
||||
S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER,
|
||||
S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER)
|
||||
);
|
||||
MH_passAndSave = MethodHandles.lookup().findStatic(TestUpcallHighArity.class, "passAndSave",
|
||||
MethodType.methodType(void.class, Object[].class, AtomicReference.class, List.class));
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new InternalError(e);
|
||||
}
|
||||
}
|
||||
|
||||
static void passAndSave(Object[] o, AtomicReference<Object[]> ref, List<MemoryLayout> layouts) {
|
||||
for (int i = 0; i < o.length; i++) {
|
||||
if (layouts.get(i) instanceof GroupLayout) {
|
||||
MemorySegment ms = (MemorySegment) o[i];
|
||||
MemorySegment copy = MemorySegment.allocateNative(ms.byteSize(), SegmentScope.auto());
|
||||
copy.copyFrom(ms);
|
||||
o[i] = copy;
|
||||
}
|
||||
}
|
||||
ref.set(o);
|
||||
System.loadLibrary("TestUpcallHighArity");
|
||||
MH_do_upcall = LINKER.downcallHandle(
|
||||
findNativeOrThrow("do_upcall"),
|
||||
FunctionDescriptor.ofVoid(C_POINTER,
|
||||
S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER,
|
||||
S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER,
|
||||
S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER,
|
||||
S_PDI_LAYOUT, C_INT, C_DOUBLE, C_POINTER)
|
||||
);
|
||||
}
|
||||
|
||||
@Test(dataProvider = "args")
|
||||
public void testUpcall(MethodHandle downcall, MethodType upcallType,
|
||||
FunctionDescriptor upcallDescriptor) throws Throwable {
|
||||
AtomicReference<Object[]> capturedArgs = new AtomicReference<>();
|
||||
MethodHandle target = MethodHandles.insertArguments(MH_passAndSave, 1, capturedArgs, upcallDescriptor.argumentLayouts())
|
||||
.asCollector(Object[].class, upcallType.parameterCount())
|
||||
.asType(upcallType);
|
||||
try (Arena arena = Arena.openConfined()) {
|
||||
MemorySegment upcallStub = LINKER.upcallStub(target, upcallDescriptor, arena.scope());
|
||||
Object[] args = new Object[upcallType.parameterCount() + 1];
|
||||
args[0] = upcallStub;
|
||||
args[0] = makeArgSaverCB(upcallDescriptor, arena, capturedArgs, -1);
|
||||
List<MemoryLayout> argLayouts = upcallDescriptor.argumentLayouts();
|
||||
List<Consumer<Object>> checks = new ArrayList<>();
|
||||
for (int i = 1; i < args.length; i++) {
|
||||
args[i] = makeArg(argLayouts.get(i - 1), null, false);
|
||||
TestValue testValue = genTestValue(argLayouts.get(i - 1), arena);
|
||||
args[i] = testValue.value();
|
||||
checks.add(testValue.check());
|
||||
}
|
||||
|
||||
downcall.invokeWithArguments(args);
|
||||
|
||||
Object[] capturedArgsArr = capturedArgs.get();
|
||||
for (int i = 0; i < capturedArgsArr.length; i++) {
|
||||
if (upcallDescriptor.argumentLayouts().get(i) instanceof GroupLayout) {
|
||||
assertStructEquals((MemorySegment) capturedArgsArr[i], (MemorySegment) args[i + 1], argLayouts.get(i));
|
||||
} else {
|
||||
assertEquals(capturedArgsArr[i], args[i + 1], "For index " + i);
|
||||
}
|
||||
checks.get(i).accept(capturedArgsArr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
* @test
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies
|
||||
@ -33,6 +34,7 @@
|
||||
*/
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
@ -40,6 +42,7 @@ import org.testng.annotations.Test;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class TestUpcallScope extends TestUpcallBase {
|
||||
@ -51,17 +54,24 @@ public class TestUpcallScope extends TestUpcallBase {
|
||||
@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
|
||||
public void testUpcalls(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
|
||||
List<Consumer<Object>> returnChecks = new ArrayList<>();
|
||||
List<Consumer<Object[]>> argChecks = new ArrayList<>();
|
||||
List<Consumer<Object>> argChecks = new ArrayList<>();
|
||||
MemorySegment addr = findNativeOrThrow(fName);
|
||||
try (Arena arena = Arena.openConfined()) {
|
||||
MethodHandle mh = downcallHandle(ABI, addr, arena, function(ret, paramTypes, fields));
|
||||
Object[] args = makeArgs(arena.scope(), ret, paramTypes, fields, returnChecks, argChecks);
|
||||
Object[] callArgs = args;
|
||||
Object res = mh.invokeWithArguments(callArgs);
|
||||
argChecks.forEach(c -> c.accept(args));
|
||||
FunctionDescriptor descriptor = function(ret, paramTypes, fields);
|
||||
MethodHandle mh = downcallHandle(LINKER, addr, arena, descriptor);
|
||||
AtomicReference<Object[]> capturedArgs = new AtomicReference<>();
|
||||
Object[] args = makeArgs(capturedArgs, arena, descriptor, returnChecks, argChecks, 0);
|
||||
|
||||
Object res = mh.invokeWithArguments(args);
|
||||
|
||||
if (ret == Ret.NON_VOID) {
|
||||
returnChecks.forEach(c -> c.accept(res));
|
||||
}
|
||||
|
||||
Object[] capturedArgsArr = capturedArgs.get();
|
||||
for (int i = 0; i < capturedArgsArr.length; i++) {
|
||||
argChecks.get(i).accept(capturedArgsArr[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@
|
||||
* @test
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @build NativeTestHelper CallGeneratorHelper TestUpcallBase
|
||||
*
|
||||
* @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies
|
||||
@ -35,13 +36,13 @@
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class TestUpcallStack extends TestUpcallBase {
|
||||
@ -51,19 +52,29 @@ public class TestUpcallStack extends TestUpcallBase {
|
||||
}
|
||||
|
||||
@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
|
||||
public void testUpcallsStack(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
|
||||
public void testUpcallsStack(int count, String fName, Ret ret, List<ParamType> paramTypes,
|
||||
List<StructFieldType> fields) throws Throwable {
|
||||
List<Consumer<Object>> returnChecks = new ArrayList<>();
|
||||
List<Consumer<Object[]>> argChecks = new ArrayList<>();
|
||||
List<Consumer<Object>> argChecks = new ArrayList<>();
|
||||
MemorySegment addr = findNativeOrThrow("s" + fName);
|
||||
try (Arena arena = Arena.openConfined()) {
|
||||
MethodHandle mh = downcallHandle(ABI, addr, arena, functionStack(ret, paramTypes, fields));
|
||||
Object[] args = makeArgsStack(arena.scope(), ret, paramTypes, fields, returnChecks, argChecks);
|
||||
Object[] callArgs = args;
|
||||
Object res = mh.invokeWithArguments(callArgs);
|
||||
argChecks.forEach(c -> c.accept(args));
|
||||
FunctionDescriptor descriptor = functionStack(ret, paramTypes, fields);
|
||||
MethodHandle mh = downcallHandle(LINKER, addr, arena, descriptor);
|
||||
AtomicReference<Object[]> capturedArgs = new AtomicReference<>();
|
||||
Object[] args = makeArgsStack(capturedArgs, arena, descriptor, returnChecks, argChecks);
|
||||
|
||||
Object res = mh.invokeWithArguments(args);
|
||||
|
||||
if (ret == Ret.NON_VOID) {
|
||||
returnChecks.forEach(c -> c.accept(res));
|
||||
}
|
||||
|
||||
Object[] capturedArgsArr = capturedArgs.get();
|
||||
for (int capturedIdx = STACK_PREFIX_LAYOUTS.size(), checkIdx = 0;
|
||||
capturedIdx < capturedArgsArr.length;
|
||||
capturedIdx++, checkIdx++) {
|
||||
argChecks.get(checkIdx).accept(capturedArgsArr[capturedIdx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -71,8 +82,9 @@ public class TestUpcallStack extends TestUpcallBase {
|
||||
return function(ret, params, fields, STACK_PREFIX_LAYOUTS);
|
||||
}
|
||||
|
||||
static Object[] makeArgsStack(SegmentScope session, Ret ret, List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks, List<Consumer<Object[]>> argChecks) throws ReflectiveOperationException {
|
||||
return makeArgs(session, ret, params, fields, checks, argChecks, STACK_PREFIX_LAYOUTS);
|
||||
static Object[] makeArgsStack(AtomicReference<Object[]> capturedArgs, Arena session, FunctionDescriptor descriptor,
|
||||
List<Consumer<Object>> checks, List<Consumer<Object>> argChecks) {
|
||||
return makeArgs(capturedArgs, session, descriptor, checks, argChecks, STACK_PREFIX_LAYOUTS.size());
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,31 +26,29 @@
|
||||
* @test
|
||||
* @enablePreview
|
||||
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64" | os.arch == "riscv64"
|
||||
* @modules java.base/jdk.internal.foreign
|
||||
* @run testng/othervm --enable-native-access=ALL-UNNAMED -Dgenerator.sample.factor=17 TestVarArgs
|
||||
*/
|
||||
|
||||
import java.lang.foreign.Arena;
|
||||
import java.lang.foreign.Linker;
|
||||
import java.lang.foreign.FunctionDescriptor;
|
||||
import java.lang.foreign.GroupLayout;
|
||||
import java.lang.foreign.MemoryLayout;
|
||||
import java.lang.foreign.MemorySegment;
|
||||
import java.lang.foreign.PaddingLayout;
|
||||
import java.lang.foreign.ValueLayout;
|
||||
|
||||
import org.testng.annotations.DataProvider;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.lang.foreign.SegmentAllocator;
|
||||
import java.lang.foreign.SegmentScope;
|
||||
import java.lang.invoke.MethodHandle;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.lang.invoke.MethodType;
|
||||
import java.lang.invoke.VarHandle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static java.lang.foreign.MemoryLayout.PathElement.*;
|
||||
import static org.testng.Assert.*;
|
||||
|
||||
public class TestVarArgs extends CallGeneratorHelper {
|
||||
|
||||
@ -73,13 +71,13 @@ public class TestVarArgs extends CallGeneratorHelper {
|
||||
@Test(dataProvider = "variadicFunctions")
|
||||
public void testVarArgs(int count, String fName, Ret ret, // ignore this stuff
|
||||
List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
|
||||
List<Arg> args = makeArgs(paramTypes, fields);
|
||||
|
||||
try (Arena arena = Arena.openConfined()) {
|
||||
List<Arg> args = makeArgs(arena, paramTypes, fields);
|
||||
MethodHandle checker = MethodHandles.insertArguments(MH_CHECK, 2, args);
|
||||
MemorySegment writeBack = LINKER.upcallStub(checker, FunctionDescriptor.ofVoid(C_INT, C_POINTER), arena.scope());
|
||||
MemorySegment callInfo = MemorySegment.allocateNative(CallInfo.LAYOUT, arena.scope());;
|
||||
MemorySegment argIDs = MemorySegment.allocateNative(MemoryLayout.sequenceLayout(args.size(), C_INT), arena.scope());;
|
||||
MemorySegment callInfo = arena.allocate(CallInfo.LAYOUT);
|
||||
MemoryLayout layout = MemoryLayout.sequenceLayout(args.size(), C_INT);
|
||||
MemorySegment argIDs = arena.allocate(layout);
|
||||
|
||||
MemorySegment callInfoPtr = callInfo;
|
||||
|
||||
@ -103,7 +101,7 @@ public class TestVarArgs extends CallGeneratorHelper {
|
||||
List<Object> argValues = new ArrayList<>();
|
||||
argValues.add(callInfoPtr); // call info
|
||||
argValues.add(args.size()); // size
|
||||
args.forEach(a -> argValues.add(a.value));
|
||||
args.forEach(a -> argValues.add(a.value()));
|
||||
|
||||
downcallHandle.invokeWithArguments(argValues);
|
||||
|
||||
@ -163,16 +161,15 @@ public class TestVarArgs extends CallGeneratorHelper {
|
||||
return downcalls.toArray(new Object[0][]);
|
||||
}
|
||||
|
||||
private static List<Arg> makeArgs(List<ParamType> paramTypes, List<StructFieldType> fields) throws ReflectiveOperationException {
|
||||
private static List<Arg> makeArgs(Arena arena, List<ParamType> paramTypes, List<StructFieldType> fields) {
|
||||
List<Arg> args = new ArrayList<>();
|
||||
for (ParamType pType : paramTypes) {
|
||||
MemoryLayout layout = pType.layout(fields);
|
||||
List<Consumer<Object>> checks = new ArrayList<>();
|
||||
Object arg = makeArg(layout, checks, true);
|
||||
TestValue testValue = genTestValue(layout, arena);
|
||||
Arg.NativeType type = Arg.NativeType.of(pType.type(fields));
|
||||
args.add(pType == ParamType.STRUCT
|
||||
? Arg.structArg(type, layout, arg, checks)
|
||||
: Arg.primitiveArg(type, layout, arg, checks));
|
||||
? Arg.structArg(type, layout, testValue)
|
||||
: Arg.primitiveArg(type, layout, testValue));
|
||||
}
|
||||
return args;
|
||||
}
|
||||
@ -181,11 +178,10 @@ public class TestVarArgs extends CallGeneratorHelper {
|
||||
Arg varArg = args.get(index);
|
||||
MemoryLayout layout = varArg.layout;
|
||||
MethodHandle getter = varArg.getter;
|
||||
List<Consumer<Object>> checks = varArg.checks;
|
||||
try (Arena arena = Arena.openConfined()) {
|
||||
MemorySegment seg = MemorySegment.ofAddress(ptr.address(), layout.byteSize(), arena.scope());
|
||||
Object obj = getter.invoke(seg);
|
||||
checks.forEach(check -> check.accept(obj));
|
||||
varArg.check(obj);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
@ -208,26 +204,33 @@ public class TestVarArgs extends CallGeneratorHelper {
|
||||
}
|
||||
|
||||
private static final class Arg {
|
||||
private final TestValue value;
|
||||
|
||||
final NativeType id;
|
||||
final MemoryLayout layout;
|
||||
final Object value;
|
||||
final MethodHandle getter;
|
||||
final List<Consumer<Object>> checks;
|
||||
|
||||
private Arg(NativeType id, MemoryLayout layout, Object value, MethodHandle getter, List<Consumer<Object>> checks) {
|
||||
private Arg(NativeType id, MemoryLayout layout, TestValue value, MethodHandle getter) {
|
||||
this.id = id;
|
||||
this.layout = layout;
|
||||
this.value = value;
|
||||
this.getter = getter;
|
||||
this.checks = checks;
|
||||
}
|
||||
|
||||
private static Arg primitiveArg(NativeType id, MemoryLayout layout, Object value, List<Consumer<Object>> checks) {
|
||||
return new Arg(id, layout, value, layout.varHandle().toMethodHandle(VarHandle.AccessMode.GET), checks);
|
||||
private static Arg primitiveArg(NativeType id, MemoryLayout layout, TestValue value) {
|
||||
return new Arg(id, layout, value, layout.varHandle().toMethodHandle(VarHandle.AccessMode.GET));
|
||||
}
|
||||
|
||||
private static Arg structArg(NativeType id, MemoryLayout layout, Object value, List<Consumer<Object>> checks) {
|
||||
return new Arg(id, layout, value, MethodHandles.identity(MemorySegment.class), checks);
|
||||
private static Arg structArg(NativeType id, MemoryLayout layout, TestValue value) {
|
||||
return new Arg(id, layout, value, MethodHandles.identity(MemorySegment.class));
|
||||
}
|
||||
|
||||
public void check(Object actual) {
|
||||
value.check().accept(actual);
|
||||
}
|
||||
|
||||
public Object value() {
|
||||
return value.value();
|
||||
}
|
||||
|
||||
enum NativeType {
|
||||
|
Loading…
Reference in New Issue
Block a user