diff --git a/src/java.base/share/classes/jdk/internal/foreign/Utils.java b/src/java.base/share/classes/jdk/internal/foreign/Utils.java index facde705729..bc94b10dbf4 100644 --- a/src/java.base/share/classes/jdk/internal/foreign/Utils.java +++ b/src/java.base/share/classes/jdk/internal/foreign/Utils.java @@ -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 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)); + } } diff --git a/test/jdk/java/foreign/CallGeneratorHelper.java b/test/jdk/java/foreign/CallGeneratorHelper.java index e1ef460603b..ee3930a56e7 100644 --- a/test/jdk/java/foreign/CallGeneratorHelper.java +++ b/test/jdk/java/foreign/CallGeneratorHelper.java @@ -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 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 fields) { if (this == STRUCT) { - long offset = 0L; - List 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> 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> 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> 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) { diff --git a/test/jdk/java/foreign/NativeTestHelper.java b/test/jdk/java/foreign/NativeTestHelper.java index 73dbb0149f2..1d925791ce6 100644 --- a/test/jdk/java/foreign/NativeTestHelper.java +++ b/test/jdk/java/foreign/NativeTestHelper.java @@ -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 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); diff --git a/test/jdk/java/foreign/TestDowncallBase.java b/test/jdk/java/foreign/TestDowncallBase.java index 14d8928afc8..de230e8cfac 100644 --- a/test/jdk/java/foreign/TestDowncallBase.java +++ b/test/jdk/java/foreign/TestDowncallBase.java @@ -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 params, List fields, List prefix) { @@ -50,15 +49,17 @@ public class TestDowncallBase extends CallGeneratorHelper { FunctionDescriptor.of(paramLayouts[prefix.size()], paramLayouts); } - static Object[] makeArgs(List params, List fields, List> checks, List prefix) throws ReflectiveOperationException { - Object[] args = new Object[prefix.size() + params.size()]; + static Object[] makeArgs(Arena arena, FunctionDescriptor descriptor, List> checks, int returnIdx) { + List 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(); } } diff --git a/test/jdk/java/foreign/TestDowncallScope.java b/test/jdk/java/foreign/TestDowncallScope.java index 9932eda7e9b..15d34df96c1 100644 --- a/test/jdk/java/foreign/TestDowncallScope.java +++ b/test/jdk/java/foreign/TestDowncallScope.java @@ -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> 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 params, List fields, List> checks) throws ReflectiveOperationException { - return makeArgs(params, fields, checks, List.of()); + static Object[] makeArgs(Arena arena, FunctionDescriptor descriptor, List> checks) { + return makeArgs(arena, descriptor, checks, 0); } } diff --git a/test/jdk/java/foreign/TestDowncallStack.java b/test/jdk/java/foreign/TestDowncallStack.java index b8d746bb5c8..4d9eb250bbb 100644 --- a/test/jdk/java/foreign/TestDowncallStack.java +++ b/test/jdk/java/foreign/TestDowncallStack.java @@ -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> 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 params, List fields, List> checks) throws ReflectiveOperationException { - return makeArgs(params, fields, checks, STACK_PREFIX_LAYOUTS); + static Object[] makeArgsStack(Arena arena, FunctionDescriptor descriptor, List> checks) { + return makeArgs(arena, descriptor, checks, STACK_PREFIX_LAYOUTS.size()); } } diff --git a/test/jdk/java/foreign/TestMatrix.java b/test/jdk/java/foreign/TestMatrix.java index 80ef3afc07a..9012a140107 100644 --- a/test/jdk/java/foreign/TestMatrix.java +++ b/test/jdk/java/foreign/TestMatrix.java @@ -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 diff --git a/test/jdk/java/foreign/TestUpcallAsync.java b/test/jdk/java/foreign/TestUpcallAsync.java index 31b650b694e..f07902dbbc9 100644 --- a/test/jdk/java/foreign/TestUpcallAsync.java +++ b/test/jdk/java/foreign/TestUpcallAsync.java @@ -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 paramTypes, List fields) throws Throwable { List> returnChecks = new ArrayList<>(); - List> argChecks = new ArrayList<>(); + List> 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 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); }); } diff --git a/test/jdk/java/foreign/TestUpcallBase.java b/test/jdk/java/foreign/TestUpcallBase.java index 85da103a801..e768ade8577 100644 --- a/test/jdk/java/foreign/TestUpcallBase.java +++ b/test/jdk/java/foreign/TestUpcallBase.java @@ -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 params, List 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 params, List fields, List> checks, List> argChecks) throws ReflectiveOperationException { - return makeArgs(session, ret, params, fields, checks, argChecks, List.of()); - } + static Object[] makeArgs(AtomicReference capturedArgs, Arena arena, FunctionDescriptor downcallDescriptor, + List> checks, List> 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 params, List fields, List> checks, List> argChecks, List 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 params, List fields, List> checks, List> argChecks, List prefix) { - if (params.isEmpty()) { - return DUMMY_STUB; - } - - AtomicReference box = new AtomicReference<>(); - List 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 ref, int retArg, List 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(); } } diff --git a/test/jdk/java/foreign/TestUpcallHighArity.java b/test/jdk/java/foreign/TestUpcallHighArity.java index 95e3caf10b6..fa7145c9985 100644 --- a/test/jdk/java/foreign/TestUpcallHighArity.java +++ b/test/jdk/java/foreign/TestUpcallHighArity.java @@ -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 ref, List 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 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 argLayouts = upcallDescriptor.argumentLayouts(); + List> 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]); } } } diff --git a/test/jdk/java/foreign/TestUpcallScope.java b/test/jdk/java/foreign/TestUpcallScope.java index 5be119fe911..8479115d735 100644 --- a/test/jdk/java/foreign/TestUpcallScope.java +++ b/test/jdk/java/foreign/TestUpcallScope.java @@ -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 paramTypes, List fields) throws Throwable { List> returnChecks = new ArrayList<>(); - List> argChecks = new ArrayList<>(); + List> 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 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]); + } } } diff --git a/test/jdk/java/foreign/TestUpcallStack.java b/test/jdk/java/foreign/TestUpcallStack.java index 445e8612a0b..4f6eb86abee 100644 --- a/test/jdk/java/foreign/TestUpcallStack.java +++ b/test/jdk/java/foreign/TestUpcallStack.java @@ -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 paramTypes, List fields) throws Throwable { + public void testUpcallsStack(int count, String fName, Ret ret, List paramTypes, + List fields) throws Throwable { List> returnChecks = new ArrayList<>(); - List> argChecks = new ArrayList<>(); + List> 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 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 params, List fields, List> checks, List> argChecks) throws ReflectiveOperationException { - return makeArgs(session, ret, params, fields, checks, argChecks, STACK_PREFIX_LAYOUTS); + static Object[] makeArgsStack(AtomicReference capturedArgs, Arena session, FunctionDescriptor descriptor, + List> checks, List> argChecks) { + return makeArgs(capturedArgs, session, descriptor, checks, argChecks, STACK_PREFIX_LAYOUTS.size()); } } diff --git a/test/jdk/java/foreign/TestVarArgs.java b/test/jdk/java/foreign/TestVarArgs.java index ca2545300b8..dfa93f57abd 100644 --- a/test/jdk/java/foreign/TestVarArgs.java +++ b/test/jdk/java/foreign/TestVarArgs.java @@ -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 paramTypes, List fields) throws Throwable { - List args = makeArgs(paramTypes, fields); - try (Arena arena = Arena.openConfined()) { + List 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 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 makeArgs(List paramTypes, List fields) throws ReflectiveOperationException { + private static List makeArgs(Arena arena, List paramTypes, List fields) { List args = new ArrayList<>(); for (ParamType pType : paramTypes) { MemoryLayout layout = pType.layout(fields); - List> 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> 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> checks; - private Arg(NativeType id, MemoryLayout layout, Object value, MethodHandle getter, List> 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> 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> 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 {