a223189b06
Co-authored-by: Paul Sandoz <psandoz@openjdk.org> Co-authored-by: Jorn Vernee <jvernee@openjdk.org> Co-authored-by: Vladimir Ivanov <vlivanov@openjdk.org> Co-authored-by: Athijegannathan Sundararajan <sundar@openjdk.org> Co-authored-by: Chris Hegarty <chegar@openjdk.org> Reviewed-by: psandoz, chegar, mchung, vlivanov
778 lines
40 KiB
Java
778 lines
40 KiB
Java
/*
|
|
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
|
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
|
*
|
|
* This code is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License version 2 only, as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* This code is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
|
* version 2 for more details (a copy is included in the LICENSE file that
|
|
* accompanied this code).
|
|
*
|
|
* You should have received a copy of the GNU General Public License version
|
|
* 2 along with this work; if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
*
|
|
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
|
* or visit www.oracle.com if you need additional information or have any
|
|
* questions.
|
|
*
|
|
*/
|
|
|
|
/*
|
|
* @test
|
|
* @library ../
|
|
* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
|
|
* @modules jdk.incubator.foreign/jdk.internal.foreign
|
|
* jdk.incubator.foreign/jdk.internal.foreign.abi
|
|
* jdk.incubator.foreign/jdk.internal.foreign.abi.aarch64
|
|
* jdk.incubator.foreign/jdk.internal.foreign.abi.x64.windows
|
|
* jdk.incubator.foreign/jdk.internal.foreign.abi.x64.sysv
|
|
* @run testng/othervm --enable-native-access=ALL-UNNAMED VaListTest
|
|
*/
|
|
|
|
import jdk.incubator.foreign.*;
|
|
import jdk.incubator.foreign.CLinker.VaList;
|
|
import jdk.internal.foreign.abi.aarch64.AArch64Linker;
|
|
import jdk.internal.foreign.abi.x64.sysv.SysVx64Linker;
|
|
import jdk.internal.foreign.abi.x64.windows.Windowsx64Linker;
|
|
import org.testng.annotations.DataProvider;
|
|
import org.testng.annotations.Test;
|
|
|
|
import java.lang.invoke.MethodHandle;
|
|
import java.lang.invoke.MethodHandleProxies;
|
|
import java.lang.invoke.MethodHandles;
|
|
import java.lang.invoke.MethodType;
|
|
import java.lang.invoke.VarHandle;
|
|
import java.util.function.BiFunction;
|
|
import java.util.function.Consumer;
|
|
import java.util.function.Function;
|
|
import java.util.stream.DoubleStream;
|
|
import java.util.stream.IntStream;
|
|
|
|
import static jdk.incubator.foreign.CLinker.C_DOUBLE;
|
|
import static jdk.incubator.foreign.CLinker.C_FLOAT;
|
|
import static jdk.incubator.foreign.CLinker.C_INT;
|
|
import static jdk.incubator.foreign.CLinker.C_LONG_LONG;
|
|
import static jdk.incubator.foreign.CLinker.C_POINTER;
|
|
import static jdk.incubator.foreign.CLinker.C_VA_LIST;
|
|
import static jdk.incubator.foreign.MemoryLayout.PathElement.groupElement;
|
|
import static jdk.incubator.foreign.MemoryLayouts.JAVA_INT;
|
|
import static jdk.internal.foreign.PlatformLayouts.*;
|
|
import static org.testng.Assert.*;
|
|
|
|
public class VaListTest extends NativeTestHelper {
|
|
|
|
private static final CLinker abi = CLinker.getInstance();
|
|
private static final LibraryLookup lookup = LibraryLookup.ofLibrary("VaList");
|
|
|
|
private static final MethodHandle MH_sumInts = link("sumInts",
|
|
MethodType.methodType(int.class, int.class, VaList.class),
|
|
FunctionDescriptor.of(C_INT, C_INT, C_VA_LIST));
|
|
private static final MethodHandle MH_sumDoubles = link("sumDoubles",
|
|
MethodType.methodType(double.class, int.class, VaList.class),
|
|
FunctionDescriptor.of(C_DOUBLE, C_INT, C_VA_LIST));
|
|
private static final MethodHandle MH_getInt = link("getInt",
|
|
MethodType.methodType(int.class, VaList.class),
|
|
FunctionDescriptor.of(C_INT, C_VA_LIST));
|
|
private static final MethodHandle MH_sumStruct = link("sumStruct",
|
|
MethodType.methodType(int.class, VaList.class),
|
|
FunctionDescriptor.of(C_INT, C_VA_LIST));
|
|
private static final MethodHandle MH_sumBigStruct = link("sumBigStruct",
|
|
MethodType.methodType(long.class, VaList.class),
|
|
FunctionDescriptor.of(C_LONG_LONG, C_VA_LIST));
|
|
private static final MethodHandle MH_sumHugeStruct = link("sumHugeStruct",
|
|
MethodType.methodType(long.class, VaList.class),
|
|
FunctionDescriptor.of(C_LONG_LONG, C_VA_LIST));
|
|
private static final MethodHandle MH_sumFloatStruct = link("sumFloatStruct",
|
|
MethodType.methodType(float.class, VaList.class),
|
|
FunctionDescriptor.of(C_FLOAT, C_VA_LIST));
|
|
private static final MethodHandle MH_sumStack = link("sumStack",
|
|
MethodType.methodType(void.class, MemoryAddress.class, MemoryAddress.class, VaList.class),
|
|
FunctionDescriptor.ofVoid(C_POINTER, C_POINTER, C_VA_LIST));
|
|
|
|
private static MethodHandle link(String symbol, MethodType mt, FunctionDescriptor fd) {
|
|
return abi.downcallHandle(lookup.lookup(symbol).get(), mt, fd);
|
|
}
|
|
|
|
private static MethodHandle linkVaListCB(String symbol) {
|
|
return link(symbol,
|
|
MethodType.methodType(void.class, MemoryAddress.class),
|
|
FunctionDescriptor.ofVoid(C_POINTER));
|
|
|
|
}
|
|
|
|
private static final Function<Consumer<VaList.Builder>, VaList> winVaListFactory
|
|
= actions -> Windowsx64Linker.newVaList(actions, ResourceScope.newConfinedScope());
|
|
private static final Function<Consumer<VaList.Builder>, VaList> sysvVaListFactory
|
|
= actions -> SysVx64Linker.newVaList(actions, ResourceScope.newConfinedScope());
|
|
private static final Function<Consumer<VaList.Builder>, VaList> aarch64VaListFactory
|
|
= actions -> AArch64Linker.newVaList(actions, ResourceScope.newConfinedScope());
|
|
private static final Function<Consumer<VaList.Builder>, VaList> platformVaListFactory
|
|
= (builder) -> VaList.make(builder, ResourceScope.newConfinedScope());
|
|
|
|
private static final BiFunction<Consumer<VaList.Builder>, NativeScope, VaList> winVaListScopedFactory
|
|
= (builder, scope) -> Windowsx64Linker.newVaList(builder, scope.scope());
|
|
private static final BiFunction<Consumer<VaList.Builder>, NativeScope, VaList> sysvVaListScopedFactory
|
|
= (builder, scope) -> SysVx64Linker.newVaList(builder, scope.scope());
|
|
private static final BiFunction<Consumer<VaList.Builder>, NativeScope, VaList> aarch64VaListScopedFactory
|
|
= (builder, scope) -> AArch64Linker.newVaList(builder, scope.scope());
|
|
private static final BiFunction<Consumer<VaList.Builder>, NativeScope, VaList> platformVaListScopedFactory
|
|
= (builder, scope) -> VaList.make(builder, scope.scope());
|
|
|
|
@DataProvider
|
|
@SuppressWarnings("unchecked")
|
|
public static Object[][] sumInts() {
|
|
Function<MemoryLayout, BiFunction<Integer, VaList, Integer>> sumIntsJavaFact = layout ->
|
|
(num, list) -> IntStream.generate(() -> list.vargAsInt(layout)).limit(num).sum();
|
|
BiFunction<Integer, VaList, Integer> sumIntsNative
|
|
= MethodHandleProxies.asInterfaceInstance(BiFunction.class, MH_sumInts);
|
|
return new Object[][]{
|
|
{ winVaListFactory, sumIntsJavaFact.apply(Win64.C_INT), Win64.C_INT },
|
|
{ sysvVaListFactory, sumIntsJavaFact.apply(SysV.C_INT), SysV.C_INT },
|
|
{ aarch64VaListFactory, sumIntsJavaFact.apply(AArch64.C_INT), AArch64.C_INT },
|
|
{ platformVaListFactory, sumIntsNative, C_INT },
|
|
};
|
|
}
|
|
|
|
@Test(dataProvider = "sumInts")
|
|
public void testIntSum(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
|
|
BiFunction<Integer, VaList, Integer> sumInts,
|
|
ValueLayout intLayout) {
|
|
VaList vaList = vaListFactory.apply(b ->
|
|
b.vargFromInt(intLayout, 10)
|
|
.vargFromInt(intLayout, 15)
|
|
.vargFromInt(intLayout, 20));
|
|
int x = sumInts.apply(3, vaList);
|
|
assertEquals(x, 45);
|
|
vaList.scope().close();
|
|
}
|
|
|
|
@DataProvider
|
|
@SuppressWarnings("unchecked")
|
|
public static Object[][] sumDoubles() {
|
|
Function<MemoryLayout, BiFunction<Integer, VaList, Double>> sumDoublesJavaFact = layout ->
|
|
(num, list) -> DoubleStream.generate(() -> list.vargAsDouble(layout)).limit(num).sum();
|
|
BiFunction<Integer, VaList, Double> sumDoublesNative
|
|
= MethodHandleProxies.asInterfaceInstance(BiFunction.class, MH_sumDoubles);
|
|
return new Object[][]{
|
|
{ winVaListFactory, sumDoublesJavaFact.apply(Win64.C_DOUBLE), Win64.C_DOUBLE },
|
|
{ sysvVaListFactory, sumDoublesJavaFact.apply(SysV.C_DOUBLE), SysV.C_DOUBLE },
|
|
{ aarch64VaListFactory, sumDoublesJavaFact.apply(AArch64.C_DOUBLE), AArch64.C_DOUBLE },
|
|
{ platformVaListFactory, sumDoublesNative, C_DOUBLE },
|
|
};
|
|
}
|
|
|
|
@Test(dataProvider = "sumDoubles")
|
|
public void testDoubleSum(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
|
|
BiFunction<Integer, VaList, Double> sumDoubles,
|
|
ValueLayout doubleLayout) {
|
|
VaList vaList = vaListFactory.apply(b ->
|
|
b.vargFromDouble(doubleLayout, 3.0D)
|
|
.vargFromDouble(doubleLayout, 4.0D)
|
|
.vargFromDouble(doubleLayout, 5.0D));
|
|
double x = sumDoubles.apply(3, vaList);
|
|
assertEquals(x, 12.0D);
|
|
vaList.scope().close();
|
|
}
|
|
|
|
@DataProvider
|
|
@SuppressWarnings("unchecked")
|
|
public static Object[][] pointers() {
|
|
Function<MemoryLayout, Function<VaList, Integer>> getIntJavaFact = layout ->
|
|
list -> {
|
|
MemoryAddress ma = list.vargAsAddress(layout);
|
|
return MemoryAccess.getIntAtOffset(MemorySegment.globalNativeSegment(), ma.toRawLongValue());
|
|
};
|
|
Function<VaList, Integer> getIntNative = MethodHandleProxies.asInterfaceInstance(Function.class, MH_getInt);
|
|
return new Object[][]{
|
|
{ winVaListFactory, getIntJavaFact.apply(Win64.C_POINTER), Win64.C_POINTER },
|
|
{ sysvVaListFactory, getIntJavaFact.apply(SysV.C_POINTER), SysV.C_POINTER },
|
|
{ aarch64VaListFactory, getIntJavaFact.apply(AArch64.C_POINTER), AArch64.C_POINTER },
|
|
{ platformVaListFactory, getIntNative, C_POINTER },
|
|
};
|
|
}
|
|
|
|
@Test(dataProvider = "pointers")
|
|
public void testVaListMemoryAddress(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
|
|
Function<VaList, Integer> getFromPointer,
|
|
ValueLayout pointerLayout) {
|
|
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
|
MemorySegment msInt = MemorySegment.allocateNative(JAVA_INT, scope);
|
|
MemoryAccess.setInt(msInt, 10);
|
|
VaList vaList = vaListFactory.apply(b -> b.vargFromAddress(pointerLayout, msInt.address()));
|
|
int x = getFromPointer.apply(vaList);
|
|
assertEquals(x, 10);
|
|
vaList.scope().close();
|
|
}
|
|
}
|
|
|
|
interface TriFunction<S, T, U, R> {
|
|
R apply(S s, T t, U u);
|
|
}
|
|
|
|
@DataProvider
|
|
@SuppressWarnings("unchecked")
|
|
public static Object[][] structs() {
|
|
TriFunction<MemoryLayout, VarHandle, VarHandle, Function<VaList, Integer>> sumStructJavaFact
|
|
= (pointLayout, VH_Point_x, VH_Point_y) ->
|
|
list -> {
|
|
MemorySegment struct = list.vargAsSegment(pointLayout, ResourceScope.newImplicitScope());
|
|
int x = (int) VH_Point_x.get(struct);
|
|
int y = (int) VH_Point_y.get(struct);
|
|
return x + y;
|
|
};
|
|
|
|
TriFunction<MemoryLayout, VarHandle, VarHandle, Function<VaList, Integer>> sumStructNativeFact
|
|
= (pointLayout, VH_Point_x, VH_Point_y) ->
|
|
MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumStruct);
|
|
|
|
TriFunction<Function<Consumer<VaList.Builder>, VaList>, MemoryLayout,
|
|
TriFunction<MemoryLayout, VarHandle, VarHandle, Function<VaList, Integer>>, Object[]> argsFact
|
|
= (vaListFact, intLayout, sumStructFact) -> {
|
|
GroupLayout pointLayout = MemoryLayout.structLayout(
|
|
intLayout.withName("x"),
|
|
intLayout.withName("y")
|
|
);
|
|
VarHandle VH_Point_x = pointLayout.varHandle(int.class, groupElement("x"));
|
|
VarHandle VH_Point_y = pointLayout.varHandle(int.class, groupElement("y"));
|
|
return new Object[] { vaListFact, sumStructFact.apply(pointLayout, VH_Point_x, VH_Point_y),
|
|
pointLayout, VH_Point_x, VH_Point_y };
|
|
};
|
|
return new Object[][]{
|
|
argsFact.apply(winVaListFactory, Win64.C_INT, sumStructJavaFact),
|
|
argsFact.apply(sysvVaListFactory, SysV.C_INT, sumStructJavaFact),
|
|
argsFact.apply(aarch64VaListFactory, AArch64.C_INT, sumStructJavaFact),
|
|
argsFact.apply(platformVaListFactory, C_INT, sumStructNativeFact),
|
|
};
|
|
}
|
|
|
|
@Test(dataProvider = "structs")
|
|
public void testStruct(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
|
|
Function<VaList, Integer> sumStruct,
|
|
GroupLayout Point_LAYOUT, VarHandle VH_Point_x, VarHandle VH_Point_y) {
|
|
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
|
MemorySegment struct = MemorySegment.allocateNative(Point_LAYOUT, scope);
|
|
VH_Point_x.set(struct, 5);
|
|
VH_Point_y.set(struct, 10);
|
|
|
|
VaList vaList = vaListFactory.apply(b -> b.vargFromSegment(Point_LAYOUT, struct));
|
|
int sum = sumStruct.apply(vaList);
|
|
assertEquals(sum, 15);
|
|
vaList.scope().close();
|
|
}
|
|
}
|
|
|
|
@DataProvider
|
|
@SuppressWarnings("unchecked")
|
|
public static Object[][] bigStructs() {
|
|
TriFunction<MemoryLayout, VarHandle, VarHandle, Function<VaList, Long>> sumStructJavaFact
|
|
= (BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y) ->
|
|
list -> {
|
|
MemorySegment struct = list.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope());
|
|
long x = (long) VH_BigPoint_x.get(struct);
|
|
long y = (long) VH_BigPoint_y.get(struct);
|
|
return x + y;
|
|
};
|
|
|
|
TriFunction<MemoryLayout, VarHandle, VarHandle, Function<VaList, Long>> sumStructNativeFact
|
|
= (pointLayout, VH_BigPoint_x, VH_BigPoint_y) ->
|
|
MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumBigStruct);
|
|
|
|
TriFunction<Function<Consumer<VaList.Builder>, VaList>, MemoryLayout,
|
|
TriFunction<MemoryLayout, VarHandle, VarHandle, Function<VaList, Long>>, Object[]> argsFact
|
|
= (vaListFact, longLongLayout, sumBigStructFact) -> {
|
|
GroupLayout BigPoint_LAYOUT = MemoryLayout.structLayout(
|
|
longLongLayout.withName("x"),
|
|
longLongLayout.withName("y")
|
|
);
|
|
VarHandle VH_BigPoint_x = BigPoint_LAYOUT.varHandle(long.class, groupElement("x"));
|
|
VarHandle VH_BigPoint_y = BigPoint_LAYOUT.varHandle(long.class, groupElement("y"));
|
|
return new Object[] { vaListFact, sumBigStructFact.apply(BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y),
|
|
BigPoint_LAYOUT, VH_BigPoint_x, VH_BigPoint_y };
|
|
};
|
|
return new Object[][]{
|
|
argsFact.apply(winVaListFactory, Win64.C_LONG_LONG, sumStructJavaFact),
|
|
argsFact.apply(sysvVaListFactory, SysV.C_LONG_LONG, sumStructJavaFact),
|
|
argsFact.apply(aarch64VaListFactory, AArch64.C_LONG_LONG, sumStructJavaFact),
|
|
argsFact.apply(platformVaListFactory, C_LONG_LONG, sumStructNativeFact),
|
|
};
|
|
}
|
|
|
|
@Test(dataProvider = "bigStructs")
|
|
public void testBigStruct(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
|
|
Function<VaList, Long> sumBigStruct,
|
|
GroupLayout BigPoint_LAYOUT, VarHandle VH_BigPoint_x, VarHandle VH_BigPoint_y) {
|
|
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
|
MemorySegment struct = MemorySegment.allocateNative(BigPoint_LAYOUT, scope);
|
|
VH_BigPoint_x.set(struct, 5);
|
|
VH_BigPoint_y.set(struct, 10);
|
|
|
|
VaList vaList = vaListFactory.apply(b -> b.vargFromSegment(BigPoint_LAYOUT, struct));
|
|
long sum = sumBigStruct.apply(vaList);
|
|
assertEquals(sum, 15);
|
|
vaList.scope().close();
|
|
}
|
|
}
|
|
|
|
@DataProvider
|
|
@SuppressWarnings("unchecked")
|
|
public static Object[][] floatStructs() {
|
|
TriFunction<MemoryLayout, VarHandle, VarHandle, Function<VaList, Float>> sumStructJavaFact
|
|
= (FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y) ->
|
|
list -> {
|
|
MemorySegment struct = list.vargAsSegment(FloatPoint_LAYOUT, ResourceScope.newImplicitScope());
|
|
float x = (float) VH_FloatPoint_x.get(struct);
|
|
float y = (float) VH_FloatPoint_y.get(struct);
|
|
return x + y;
|
|
};
|
|
|
|
TriFunction<MemoryLayout, VarHandle, VarHandle, Function<VaList, Float>> sumStructNativeFact
|
|
= (pointLayout, VH_FloatPoint_x, VH_FloatPoint_y) ->
|
|
MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumFloatStruct);
|
|
|
|
TriFunction<Function<Consumer<VaList.Builder>, VaList>, MemoryLayout,
|
|
TriFunction<MemoryLayout, VarHandle, VarHandle, Function<VaList, Float>>, Object[]> argsFact
|
|
= (vaListFact, floatLayout, sumFloatStructFact) -> {
|
|
GroupLayout FloatPoint_LAYOUT = MemoryLayout.structLayout(
|
|
floatLayout.withName("x"),
|
|
floatLayout.withName("y")
|
|
);
|
|
VarHandle VH_FloatPoint_x = FloatPoint_LAYOUT.varHandle(float.class, groupElement("x"));
|
|
VarHandle VH_FloatPoint_y = FloatPoint_LAYOUT.varHandle(float.class, groupElement("y"));
|
|
return new Object[] { vaListFact, sumFloatStructFact.apply(FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y),
|
|
FloatPoint_LAYOUT, VH_FloatPoint_x, VH_FloatPoint_y };
|
|
};
|
|
return new Object[][]{
|
|
argsFact.apply(winVaListFactory, Win64.C_FLOAT, sumStructJavaFact),
|
|
argsFact.apply(sysvVaListFactory, SysV.C_FLOAT, sumStructJavaFact),
|
|
argsFact.apply(aarch64VaListFactory, AArch64.C_FLOAT, sumStructJavaFact),
|
|
argsFact.apply(platformVaListFactory, C_FLOAT, sumStructNativeFact),
|
|
};
|
|
}
|
|
|
|
@Test(dataProvider = "floatStructs")
|
|
public void testFloatStruct(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
|
|
Function<VaList, Float> sumFloatStruct,
|
|
GroupLayout FloatPoint_LAYOUT,
|
|
VarHandle VH_FloatPoint_x, VarHandle VH_FloatPoint_y) {
|
|
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
|
MemorySegment struct = MemorySegment.allocateNative(FloatPoint_LAYOUT, scope);
|
|
VH_FloatPoint_x.set(struct, 1.234f);
|
|
VH_FloatPoint_y.set(struct, 3.142f);
|
|
|
|
VaList vaList = vaListFactory.apply(b -> b.vargFromSegment(FloatPoint_LAYOUT, struct));
|
|
float sum = sumFloatStruct.apply(vaList);
|
|
assertEquals(sum, 4.376f, 0.00001f);
|
|
vaList.scope().close();
|
|
}
|
|
}
|
|
|
|
interface QuadFunc<T0, T1, T2, T3, R> {
|
|
R apply(T0 t0, T1 t1, T2 t2, T3 t3);
|
|
}
|
|
|
|
@DataProvider
|
|
@SuppressWarnings("unchecked")
|
|
public static Object[][] hugeStructs() {
|
|
QuadFunc<MemoryLayout, VarHandle, VarHandle, VarHandle, Function<VaList, Long>> sumStructJavaFact
|
|
= (HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z) ->
|
|
list -> {
|
|
MemorySegment struct = list.vargAsSegment(HugePoint_LAYOUT, ResourceScope.newImplicitScope());
|
|
long x = (long) VH_HugePoint_x.get(struct);
|
|
long y = (long) VH_HugePoint_y.get(struct);
|
|
long z = (long) VH_HugePoint_z.get(struct);
|
|
return x + y + z;
|
|
};
|
|
|
|
QuadFunc<MemoryLayout, VarHandle, VarHandle, VarHandle, Function<VaList, Long>> sumStructNativeFact
|
|
= (pointLayout, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z) ->
|
|
MethodHandleProxies.asInterfaceInstance(Function.class, MH_sumHugeStruct);
|
|
|
|
TriFunction<Function<Consumer<VaList.Builder>, VaList>, MemoryLayout,
|
|
QuadFunc<MemoryLayout, VarHandle, VarHandle, VarHandle, Function<VaList, Long>>, Object[]> argsFact
|
|
= (vaListFact, longLongLayout, sumBigStructFact) -> {
|
|
GroupLayout HugePoint_LAYOUT = MemoryLayout.structLayout(
|
|
longLongLayout.withName("x"),
|
|
longLongLayout.withName("y"),
|
|
longLongLayout.withName("z")
|
|
);
|
|
VarHandle VH_HugePoint_x = HugePoint_LAYOUT.varHandle(long.class, groupElement("x"));
|
|
VarHandle VH_HugePoint_y = HugePoint_LAYOUT.varHandle(long.class, groupElement("y"));
|
|
VarHandle VH_HugePoint_z = HugePoint_LAYOUT.varHandle(long.class, groupElement("z"));
|
|
return new Object[] { vaListFact,
|
|
sumBigStructFact.apply(HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z),
|
|
HugePoint_LAYOUT, VH_HugePoint_x, VH_HugePoint_y, VH_HugePoint_z };
|
|
};
|
|
return new Object[][]{
|
|
argsFact.apply(winVaListFactory, Win64.C_LONG_LONG, sumStructJavaFact),
|
|
argsFact.apply(sysvVaListFactory, SysV.C_LONG_LONG, sumStructJavaFact),
|
|
argsFact.apply(aarch64VaListFactory, AArch64.C_LONG_LONG, sumStructJavaFact),
|
|
argsFact.apply(platformVaListFactory, C_LONG_LONG, sumStructNativeFact),
|
|
};
|
|
}
|
|
|
|
@Test(dataProvider = "hugeStructs")
|
|
public void testHugeStruct(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
|
|
Function<VaList, Long> sumHugeStruct,
|
|
GroupLayout HugePoint_LAYOUT,
|
|
VarHandle VH_HugePoint_x, VarHandle VH_HugePoint_y, VarHandle VH_HugePoint_z) {
|
|
// On AArch64 a struct needs to be larger than 16 bytes to be
|
|
// passed by reference.
|
|
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
|
MemorySegment struct = MemorySegment.allocateNative(HugePoint_LAYOUT, scope);
|
|
VH_HugePoint_x.set(struct, 1);
|
|
VH_HugePoint_y.set(struct, 2);
|
|
VH_HugePoint_z.set(struct, 3);
|
|
|
|
VaList vaList = vaListFactory.apply(b -> b.vargFromSegment(HugePoint_LAYOUT, struct));
|
|
long sum = sumHugeStruct.apply(vaList);
|
|
assertEquals(sum, 6);
|
|
vaList.scope().close();
|
|
}
|
|
}
|
|
|
|
public interface SumStackFunc {
|
|
void invoke(MemorySegment longSum, MemorySegment doubleSum, VaList list);
|
|
}
|
|
|
|
@DataProvider
|
|
public static Object[][] sumStack() {
|
|
BiFunction<MemoryLayout, MemoryLayout, SumStackFunc> sumStackJavaFact = (longLayout, doubleLayout) ->
|
|
(longSum, doubleSum, list) -> {
|
|
long lSum = 0L;
|
|
for (int i = 0; i < 16; i++) {
|
|
lSum += list.vargAsLong(longLayout);
|
|
}
|
|
MemoryAccess.setLong(longSum, lSum);
|
|
double dSum = 0D;
|
|
for (int i = 0; i < 16; i++) {
|
|
dSum += list.vargAsDouble(doubleLayout);
|
|
}
|
|
MemoryAccess.setDouble(doubleSum, dSum);
|
|
};
|
|
SumStackFunc sumStackNative = (longSum, doubleSum, list) -> {
|
|
try {
|
|
MH_sumStack.invokeExact(longSum.address(), doubleSum.address(), list);
|
|
} catch (Throwable ex) {
|
|
throw new AssertionError(ex);
|
|
}
|
|
};
|
|
return new Object[][]{
|
|
{ winVaListFactory, sumStackJavaFact.apply(Win64.C_LONG_LONG, Win64.C_DOUBLE), Win64.C_LONG_LONG, Win64.C_DOUBLE },
|
|
{ sysvVaListFactory, sumStackJavaFact.apply(SysV.C_LONG_LONG, SysV.C_DOUBLE), SysV.C_LONG_LONG, SysV.C_DOUBLE },
|
|
{ aarch64VaListFactory, sumStackJavaFact.apply(AArch64.C_LONG_LONG, AArch64.C_DOUBLE), AArch64.C_LONG_LONG, AArch64.C_DOUBLE },
|
|
{ platformVaListFactory, sumStackNative, C_LONG_LONG, C_DOUBLE },
|
|
};
|
|
}
|
|
|
|
@Test(dataProvider = "sumStack")
|
|
public void testStack(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
|
|
SumStackFunc sumStack,
|
|
ValueLayout longLayout,
|
|
ValueLayout doubleLayout) {
|
|
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
|
MemorySegment longSum = MemorySegment.allocateNative(longLayout, scope);
|
|
MemorySegment doubleSum = MemorySegment.allocateNative(doubleLayout, scope);
|
|
MemoryAccess.setLong(longSum, 0L);
|
|
MemoryAccess.setDouble(doubleSum, 0D);
|
|
|
|
VaList list = vaListFactory.apply(b -> {
|
|
for (long l = 1; l <= 16L; l++) {
|
|
b.vargFromLong(longLayout, l);
|
|
}
|
|
for (double d = 1; d <= 16D; d++) {
|
|
b.vargFromDouble(doubleLayout, d);
|
|
}
|
|
});
|
|
|
|
try {
|
|
sumStack.invoke(longSum, doubleSum, list);
|
|
} finally {
|
|
list.scope().close();
|
|
}
|
|
|
|
long lSum = MemoryAccess.getLong(longSum);
|
|
double dSum = MemoryAccess.getDouble(doubleSum);
|
|
|
|
assertEquals(lSum, 136L);
|
|
assertEquals(dSum, 136D);
|
|
}
|
|
}
|
|
|
|
@Test(dataProvider = "upcalls")
|
|
public void testUpcall(MethodHandle target, MethodHandle callback) throws Throwable {
|
|
FunctionDescriptor desc = FunctionDescriptor.ofVoid(C_VA_LIST);
|
|
try (ResourceScope scope = ResourceScope.newConfinedScope()) {
|
|
MemoryAddress stub = abi.upcallStub(callback, desc, scope);
|
|
target.invokeExact(stub.address());
|
|
}
|
|
}
|
|
|
|
@DataProvider
|
|
public Object[][] emptyVaLists() {
|
|
return new Object[][] {
|
|
{ Windowsx64Linker.emptyVaList() },
|
|
{ winVaListFactory.apply(b -> {}) },
|
|
{ SysVx64Linker.emptyVaList() },
|
|
{ sysvVaListFactory.apply(b -> {}) },
|
|
{ AArch64Linker.emptyVaList() },
|
|
{ aarch64VaListFactory.apply(b -> {}) },
|
|
};
|
|
}
|
|
|
|
@Test(expectedExceptions = UnsupportedOperationException.class,
|
|
expectedExceptionsMessageRegExp = ".*Scope cannot be closed.*",
|
|
dataProvider = "emptyVaLists")
|
|
public void testEmptyNotCloseable(VaList emptyList) {
|
|
emptyList.scope().close();
|
|
}
|
|
|
|
@DataProvider
|
|
@SuppressWarnings("unchecked")
|
|
public static Object[][] sumIntsScoped() {
|
|
Function<MemoryLayout, BiFunction<Integer, VaList, Integer>> sumIntsJavaFact = layout ->
|
|
(num, list) -> IntStream.generate(() -> list.vargAsInt(layout)).limit(num).sum();
|
|
BiFunction<Integer, VaList, Integer> sumIntsNative
|
|
= MethodHandleProxies.asInterfaceInstance(BiFunction.class, MH_sumInts);
|
|
return new Object[][]{
|
|
{ winVaListScopedFactory, sumIntsJavaFact.apply(Win64.C_INT), Win64.C_INT },
|
|
{ sysvVaListScopedFactory, sumIntsJavaFact.apply(SysV.C_INT), SysV.C_INT },
|
|
{ aarch64VaListScopedFactory, sumIntsJavaFact.apply(AArch64.C_INT), AArch64.C_INT },
|
|
{ platformVaListScopedFactory, sumIntsNative, C_INT },
|
|
};
|
|
}
|
|
|
|
@Test(dataProvider = "sumIntsScoped")
|
|
public void testScopedVaList(BiFunction<Consumer<VaList.Builder>, NativeScope, VaList> vaListFactory,
|
|
BiFunction<Integer, VaList, Integer> sumInts,
|
|
ValueLayout intLayout) {
|
|
VaList listLeaked;
|
|
try (NativeScope scope = new NativeScope()) {
|
|
VaList list = vaListFactory.apply(b -> b.vargFromInt(intLayout, 4)
|
|
.vargFromInt(intLayout, 8),
|
|
scope);
|
|
int x = sumInts.apply(2, list);
|
|
assertEquals(x, 12);
|
|
listLeaked = list;
|
|
}
|
|
assertFalse(listLeaked.scope().isAlive());
|
|
}
|
|
|
|
@Test(dataProvider = "structs")
|
|
public void testScopeMSRead(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
|
|
Function<VaList, Integer> sumStruct, // ignored
|
|
GroupLayout Point_LAYOUT, VarHandle VH_Point_x, VarHandle VH_Point_y) {
|
|
MemorySegment pointOut;
|
|
try (NativeScope scope = new NativeScope()) {
|
|
try (ResourceScope innerScope = ResourceScope.newConfinedScope()) {
|
|
MemorySegment pointIn = MemorySegment.allocateNative(Point_LAYOUT, innerScope);
|
|
VH_Point_x.set(pointIn, 3);
|
|
VH_Point_y.set(pointIn, 6);
|
|
VaList list = vaListFactory.apply(b -> b.vargFromSegment(Point_LAYOUT, pointIn));
|
|
pointOut = list.vargAsSegment(Point_LAYOUT, scope);
|
|
assertEquals((int) VH_Point_x.get(pointOut), 3);
|
|
assertEquals((int) VH_Point_y.get(pointOut), 6);
|
|
list.scope().close();
|
|
assertTrue(pointOut.scope().isAlive()); // after VaList freed
|
|
}
|
|
assertTrue(pointOut.scope().isAlive()); // after inner scope freed
|
|
}
|
|
assertFalse(pointOut.scope().isAlive()); // after outer scope freed
|
|
}
|
|
|
|
@DataProvider
|
|
public Object[][] copy() {
|
|
return new Object[][] {
|
|
{ winVaListFactory, Win64.C_INT },
|
|
{ sysvVaListFactory, SysV.C_INT },
|
|
{ aarch64VaListFactory, AArch64.C_INT },
|
|
};
|
|
}
|
|
|
|
@Test(dataProvider = "copy")
|
|
public void testCopy(Function<Consumer<VaList.Builder>, VaList> vaListFactory, ValueLayout intLayout) {
|
|
VaList list = vaListFactory.apply(b -> b.vargFromInt(intLayout, 4)
|
|
.vargFromInt(intLayout, 8));
|
|
VaList copy = list.copy();
|
|
assertEquals(copy.vargAsInt(intLayout), 4);
|
|
assertEquals(copy.vargAsInt(intLayout), 8);
|
|
|
|
// try { // this logic only works on Windows!
|
|
// int x = copy.vargAsInt(intLayout);
|
|
// fail();
|
|
// } catch (IndexOutOfBoundsException ex) {
|
|
// // ok - we exhausted the list
|
|
// }
|
|
|
|
assertEquals(list.vargAsInt(intLayout), 4);
|
|
assertEquals(list.vargAsInt(intLayout), 8);
|
|
list.scope().close();
|
|
}
|
|
|
|
@Test(dataProvider = "copy",
|
|
expectedExceptions = IllegalStateException.class)
|
|
public void testCopyUnusableAfterOriginalClosed(Function<Consumer<VaList.Builder>, VaList> vaListFactory,
|
|
ValueLayout intLayout) {
|
|
VaList list = vaListFactory.apply(b -> b.vargFromInt(intLayout, 4)
|
|
.vargFromInt(intLayout, 8));
|
|
VaList copy = list.copy();
|
|
list.scope().close();
|
|
|
|
copy.vargAsInt(intLayout); // should throw
|
|
}
|
|
|
|
@DataProvider
|
|
public static Object[][] upcalls() {
|
|
GroupLayout BigPoint_LAYOUT = MemoryLayout.structLayout(
|
|
C_LONG_LONG.withName("x"),
|
|
C_LONG_LONG.withName("y")
|
|
);
|
|
VarHandle VH_BigPoint_x = BigPoint_LAYOUT.varHandle(long.class, groupElement("x"));
|
|
VarHandle VH_BigPoint_y = BigPoint_LAYOUT.varHandle(long.class, groupElement("y"));
|
|
GroupLayout Point_LAYOUT = MemoryLayout.structLayout(
|
|
C_INT.withName("x"),
|
|
C_INT.withName("y")
|
|
);
|
|
VarHandle VH_Point_x = Point_LAYOUT.varHandle(int.class, groupElement("x"));
|
|
VarHandle VH_Point_y = Point_LAYOUT.varHandle(int.class, groupElement("y"));
|
|
GroupLayout FloatPoint_LAYOUT = MemoryLayout.structLayout(
|
|
C_FLOAT.withName("x"),
|
|
C_FLOAT.withName("y")
|
|
);
|
|
VarHandle VH_FloatPoint_x = FloatPoint_LAYOUT.varHandle(float.class, groupElement("x"));
|
|
VarHandle VH_FloatPoint_y = FloatPoint_LAYOUT.varHandle(float.class, groupElement("y"));
|
|
GroupLayout HugePoint_LAYOUT = MemoryLayout.structLayout(
|
|
C_LONG_LONG.withName("x"),
|
|
C_LONG_LONG.withName("y"),
|
|
C_LONG_LONG.withName("z")
|
|
);
|
|
VarHandle VH_HugePoint_x = HugePoint_LAYOUT.varHandle(long.class, groupElement("x"));
|
|
VarHandle VH_HugePoint_y = HugePoint_LAYOUT.varHandle(long.class, groupElement("y"));
|
|
VarHandle VH_HugePoint_z = HugePoint_LAYOUT.varHandle(long.class, groupElement("z"));
|
|
|
|
return new Object[][]{
|
|
{ linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> {
|
|
MemorySegment struct = vaList.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope());
|
|
assertEquals((long) VH_BigPoint_x.get(struct), 8);
|
|
assertEquals((long) VH_BigPoint_y.get(struct), 16);
|
|
})},
|
|
{ linkVaListCB("upcallBigStruct"), VaListConsumer.mh(vaList -> {
|
|
VaList copy = vaList.copy();
|
|
MemorySegment struct = vaList.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope());
|
|
assertEquals((long) VH_BigPoint_x.get(struct), 8);
|
|
assertEquals((long) VH_BigPoint_y.get(struct), 16);
|
|
|
|
VH_BigPoint_x.set(struct, 0);
|
|
VH_BigPoint_y.set(struct, 0);
|
|
|
|
// should be independent
|
|
struct = copy.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope());
|
|
assertEquals((long) VH_BigPoint_x.get(struct), 8);
|
|
assertEquals((long) VH_BigPoint_y.get(struct), 16);
|
|
})},
|
|
{ linkVaListCB("upcallStruct"), VaListConsumer.mh(vaList -> {
|
|
MemorySegment struct = vaList.vargAsSegment(Point_LAYOUT, ResourceScope.newImplicitScope());
|
|
assertEquals((int) VH_Point_x.get(struct), 5);
|
|
assertEquals((int) VH_Point_y.get(struct), 10);
|
|
})},
|
|
{ linkVaListCB("upcallHugeStruct"), VaListConsumer.mh(vaList -> {
|
|
MemorySegment struct = vaList.vargAsSegment(HugePoint_LAYOUT, ResourceScope.newImplicitScope());
|
|
assertEquals((long) VH_HugePoint_x.get(struct), 1);
|
|
assertEquals((long) VH_HugePoint_y.get(struct), 2);
|
|
assertEquals((long) VH_HugePoint_z.get(struct), 3);
|
|
})},
|
|
{ linkVaListCB("upcallFloatStruct"), VaListConsumer.mh(vaList -> {
|
|
MemorySegment struct = vaList.vargAsSegment(FloatPoint_LAYOUT, ResourceScope.newImplicitScope());
|
|
assertEquals((float) VH_FloatPoint_x.get(struct), 1.0f);
|
|
assertEquals((float) VH_FloatPoint_y.get(struct), 2.0f);
|
|
})},
|
|
{ linkVaListCB("upcallMemoryAddress"), VaListConsumer.mh(vaList -> {
|
|
MemoryAddress intPtr = vaList.vargAsAddress(C_POINTER);
|
|
MemorySegment ms = intPtr.asSegment(C_INT.byteSize(), ResourceScope.globalScope());
|
|
int x = MemoryAccess.getInt(ms);
|
|
assertEquals(x, 10);
|
|
})},
|
|
{ linkVaListCB("upcallDoubles"), VaListConsumer.mh(vaList -> {
|
|
assertEquals(vaList.vargAsDouble(C_DOUBLE), 3.0);
|
|
assertEquals(vaList.vargAsDouble(C_DOUBLE), 4.0);
|
|
assertEquals(vaList.vargAsDouble(C_DOUBLE), 5.0);
|
|
})},
|
|
{ linkVaListCB("upcallInts"), VaListConsumer.mh(vaList -> {
|
|
assertEquals(vaList.vargAsInt(C_INT), 10);
|
|
assertEquals(vaList.vargAsInt(C_INT), 15);
|
|
assertEquals(vaList.vargAsInt(C_INT), 20);
|
|
})},
|
|
{ linkVaListCB("upcallStack"), VaListConsumer.mh(vaList -> {
|
|
// skip all registers
|
|
for (long l = 1; l <= 16; l++) {
|
|
assertEquals(vaList.vargAsLong(C_LONG_LONG), l);
|
|
}
|
|
for (double d = 1; d <= 16; d++) {
|
|
assertEquals(vaList.vargAsDouble(C_DOUBLE), d);
|
|
}
|
|
|
|
// test some arbitrary values on the stack
|
|
assertEquals((byte) vaList.vargAsInt(C_INT), (byte) 1);
|
|
assertEquals((char) vaList.vargAsInt(C_INT), 'a');
|
|
assertEquals((short) vaList.vargAsInt(C_INT), (short) 3);
|
|
assertEquals(vaList.vargAsInt(C_INT), 4);
|
|
assertEquals(vaList.vargAsLong(C_LONG_LONG), 5L);
|
|
assertEquals((float) vaList.vargAsDouble(C_DOUBLE), 6.0F);
|
|
assertEquals(vaList.vargAsDouble(C_DOUBLE), 7.0D);
|
|
assertEquals((byte) vaList.vargAsInt(C_INT), (byte) 8);
|
|
assertEquals((char) vaList.vargAsInt(C_INT), 'b');
|
|
assertEquals((short) vaList.vargAsInt(C_INT), (short) 10);
|
|
assertEquals(vaList.vargAsInt(C_INT), 11);
|
|
assertEquals(vaList.vargAsLong(C_LONG_LONG), 12L);
|
|
assertEquals((float) vaList.vargAsDouble(C_DOUBLE), 13.0F);
|
|
assertEquals(vaList.vargAsDouble(C_DOUBLE), 14.0D);
|
|
|
|
MemorySegment point = vaList.vargAsSegment(Point_LAYOUT, ResourceScope.newImplicitScope());
|
|
assertEquals((int) VH_Point_x.get(point), 5);
|
|
assertEquals((int) VH_Point_y.get(point), 10);
|
|
|
|
VaList copy = vaList.copy();
|
|
MemorySegment bigPoint = vaList.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope());
|
|
assertEquals((long) VH_BigPoint_x.get(bigPoint), 15);
|
|
assertEquals((long) VH_BigPoint_y.get(bigPoint), 20);
|
|
|
|
VH_BigPoint_x.set(bigPoint, 0);
|
|
VH_BigPoint_y.set(bigPoint, 0);
|
|
|
|
// should be independent
|
|
MemorySegment struct = copy.vargAsSegment(BigPoint_LAYOUT, ResourceScope.newImplicitScope());
|
|
assertEquals((long) VH_BigPoint_x.get(struct), 15);
|
|
assertEquals((long) VH_BigPoint_y.get(struct), 20);
|
|
})},
|
|
// test skip
|
|
{ linkVaListCB("upcallStack"), VaListConsumer.mh(vaList -> {
|
|
vaList.skip(C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG);
|
|
assertEquals(vaList.vargAsLong(C_LONG_LONG), 5L);
|
|
vaList.skip(C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG);
|
|
assertEquals(vaList.vargAsLong(C_LONG_LONG), 10L);
|
|
vaList.skip(C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG, C_LONG_LONG);
|
|
assertEquals(vaList.vargAsDouble(C_DOUBLE), 1.0D);
|
|
vaList.skip(C_DOUBLE, C_DOUBLE, C_DOUBLE, C_DOUBLE);
|
|
assertEquals(vaList.vargAsDouble(C_DOUBLE), 6.0D);
|
|
})},
|
|
};
|
|
}
|
|
|
|
interface VaListConsumer {
|
|
void accept(VaList list);
|
|
|
|
static MethodHandle mh(VaListConsumer instance) {
|
|
try {
|
|
return MethodHandles.lookup().findVirtual(VaListConsumer.class, "accept",
|
|
MethodType.methodType(void.class, VaList.class)).bindTo(instance);
|
|
} catch (ReflectiveOperationException e) {
|
|
throw new InternalError(e);
|
|
}
|
|
}
|
|
}
|
|
}
|