8303582: Reduce duplication in jdk/java/foreign tests

Reviewed-by: mcimadamore
This commit is contained in:
Jorn Vernee 2023-03-06 14:52:04 +00:00
parent 8e2014527e
commit dccfe8a2ee
13 changed files with 230 additions and 364 deletions

View File

@ -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));
}
}

View File

@ -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) {

View File

@ -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);

View File

@ -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();
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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

View File

@ -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);
});
}

View File

@ -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();
}
}

View File

@ -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]);
}
}
}

View File

@ -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]);
}
}
}

View File

@ -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());
}
}

View File

@ -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 {