Path: blob/master/test/jdk/java/foreign/TestUpcall.java
66643 views
/*1* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324/*25* @test26* @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"27* @modules jdk.incubator.foreign/jdk.internal.foreign28* @build NativeTestHelper CallGeneratorHelper TestUpcall29*30* @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies31* --enable-native-access=ALL-UNNAMED -Dgenerator.sample.factor=1732* TestUpcall33*/3435import jdk.incubator.foreign.CLinker;36import jdk.incubator.foreign.FunctionDescriptor;37import jdk.incubator.foreign.SymbolLookup;38import jdk.incubator.foreign.MemoryAddress;39import jdk.incubator.foreign.MemoryLayout;40import jdk.incubator.foreign.MemorySegment;4142import jdk.incubator.foreign.ResourceScope;43import org.testng.annotations.BeforeClass;44import org.testng.annotations.Test;4546import java.lang.invoke.MethodHandle;47import java.lang.invoke.MethodHandles;48import java.lang.invoke.MethodType;49import java.util.ArrayList;50import java.util.List;51import java.util.concurrent.atomic.AtomicReference;52import java.util.function.Consumer;53import java.util.stream.Collectors;5455import static java.lang.invoke.MethodHandles.insertArguments;56import static jdk.incubator.foreign.CLinker.C_POINTER;57import static org.testng.Assert.assertEquals;585960public class TestUpcall extends CallGeneratorHelper {6162static {63System.loadLibrary("TestUpcall");64}65static CLinker abi = CLinker.getInstance();6667static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup();6869static MethodHandle DUMMY;70static MethodHandle PASS_AND_SAVE;7172static {73try {74DUMMY = MethodHandles.lookup().findStatic(TestUpcall.class, "dummy", MethodType.methodType(void.class));75PASS_AND_SAVE = MethodHandles.lookup().findStatic(TestUpcall.class, "passAndSave",76MethodType.methodType(Object.class, Object[].class, AtomicReference.class));77} catch (Throwable ex) {78throw new IllegalStateException(ex);79}80}8182static MemoryAddress dummyStub;8384@BeforeClass85void setup() {86dummyStub = abi.upcallStub(DUMMY, FunctionDescriptor.ofVoid(), ResourceScope.newImplicitScope());87}8889@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)90public void testUpcalls(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {91List<Consumer<Object>> returnChecks = new ArrayList<>();92List<Consumer<Object[]>> argChecks = new ArrayList<>();93MemoryAddress addr = LOOKUP.lookup(fName).get();94MethodType mtype = methodType(ret, paramTypes, fields);95try (NativeScope scope = new NativeScope()) {96MethodHandle mh = abi.downcallHandle(addr, scope, mtype, function(ret, paramTypes, fields));97Object[] args = makeArgs(scope.scope(), ret, paramTypes, fields, returnChecks, argChecks);98Object[] callArgs = args;99Object res = mh.invokeWithArguments(callArgs);100argChecks.forEach(c -> c.accept(args));101if (ret == Ret.NON_VOID) {102returnChecks.forEach(c -> c.accept(res));103}104}105}106107@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)108public void testUpcallsNoScope(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {109List<Consumer<Object>> returnChecks = new ArrayList<>();110List<Consumer<Object[]>> argChecks = new ArrayList<>();111MemoryAddress addr = LOOKUP.lookup(fName).get();112MethodType mtype = methodType(ret, paramTypes, fields);113MethodHandle mh = abi.downcallHandle(addr, IMPLICIT_ALLOCATOR, mtype, function(ret, paramTypes, fields));114Object[] args = makeArgs(ResourceScope.newImplicitScope(), ret, paramTypes, fields, returnChecks, argChecks);115Object[] callArgs = args;116Object res = mh.invokeWithArguments(callArgs);117argChecks.forEach(c -> c.accept(args));118if (ret == Ret.NON_VOID) {119returnChecks.forEach(c -> c.accept(res));120}121}122123static MethodType methodType(Ret ret, List<ParamType> params, List<StructFieldType> fields) {124MethodType mt = ret == Ret.VOID ?125MethodType.methodType(void.class) : MethodType.methodType(paramCarrier(params.get(0).layout(fields)));126for (ParamType p : params) {127mt = mt.appendParameterTypes(paramCarrier(p.layout(fields)));128}129mt = mt.appendParameterTypes(MemoryAddress.class); //the callback130return mt;131}132133static FunctionDescriptor function(Ret ret, List<ParamType> params, List<StructFieldType> fields) {134List<MemoryLayout> paramLayouts = params.stream().map(p -> p.layout(fields)).collect(Collectors.toList());135paramLayouts.add(C_POINTER); // the callback136MemoryLayout[] layouts = paramLayouts.toArray(new MemoryLayout[0]);137return ret == Ret.VOID ?138FunctionDescriptor.ofVoid(layouts) :139FunctionDescriptor.of(layouts[0], layouts);140}141142static Object[] makeArgs(ResourceScope scope, Ret ret, List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks, List<Consumer<Object[]>> argChecks) throws ReflectiveOperationException {143Object[] args = new Object[params.size() + 1];144for (int i = 0 ; i < params.size() ; i++) {145args[i] = makeArg(params.get(i).layout(fields), checks, i == 0);146}147args[params.size()] = makeCallback(scope, ret, params, fields, checks, argChecks);148return args;149}150151@SuppressWarnings("unchecked")152static MemoryAddress makeCallback(ResourceScope scope, Ret ret, List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks, List<Consumer<Object[]>> argChecks) {153if (params.isEmpty()) {154return dummyStub.address();155}156157AtomicReference<Object[]> box = new AtomicReference<>();158MethodHandle mh = insertArguments(PASS_AND_SAVE, 1, box);159mh = mh.asCollector(Object[].class, params.size());160161for (int i = 0; i < params.size(); i++) {162ParamType pt = params.get(i);163MemoryLayout layout = pt.layout(fields);164Class<?> carrier = paramCarrier(layout);165mh = mh.asType(mh.type().changeParameterType(i, carrier));166167final int finalI = i;168if (carrier == MemorySegment.class) {169argChecks.add(o -> assertStructEquals((MemorySegment) box.get()[finalI], (MemorySegment) o[finalI], layout));170} else {171argChecks.add(o -> assertEquals(box.get()[finalI], o[finalI]));172}173}174175ParamType firstParam = params.get(0);176MemoryLayout firstlayout = firstParam.layout(fields);177Class<?> firstCarrier = paramCarrier(firstlayout);178179if (firstCarrier == MemorySegment.class) {180checks.add(o -> assertStructEquals((MemorySegment) box.get()[0], (MemorySegment) o, firstlayout));181} else {182checks.add(o -> assertEquals(o, box.get()[0]));183}184185mh = mh.asType(mh.type().changeReturnType(ret == Ret.VOID ? void.class : firstCarrier));186187MemoryLayout[] paramLayouts = params.stream().map(p -> p.layout(fields)).toArray(MemoryLayout[]::new);188FunctionDescriptor func = ret != Ret.VOID189? FunctionDescriptor.of(firstlayout, paramLayouts)190: FunctionDescriptor.ofVoid(paramLayouts);191return abi.upcallStub(mh, func, scope);192}193194static Object passAndSave(Object[] o, AtomicReference<Object[]> ref) {195for (int i = 0; i < o.length; i++) {196if (o[i] instanceof MemorySegment) {197MemorySegment ms = (MemorySegment) o[i];198MemorySegment copy = MemorySegment.allocateNative(ms.byteSize(), ResourceScope.newImplicitScope());199copy.copyFrom(ms);200o[i] = copy;201}202}203ref.set(o);204return o[0];205}206207static void dummy() {208//do nothing209}210}211212213