Path: blob/master/test/jdk/java/foreign/TestUpcall.java
41145 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/timeout=24031* --enable-native-access=ALL-UNNAMED32* 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 jdk.incubator.foreign.SegmentAllocator;44import org.testng.annotations.BeforeClass;45import org.testng.annotations.Test;4647import java.lang.invoke.MethodHandle;48import java.lang.invoke.MethodHandles;49import java.lang.invoke.MethodType;50import java.util.ArrayList;51import java.util.List;52import java.util.concurrent.atomic.AtomicReference;53import java.util.function.Consumer;54import java.util.stream.Collectors;5556import static java.lang.invoke.MethodHandles.insertArguments;57import static jdk.incubator.foreign.CLinker.C_POINTER;58import static org.testng.Assert.assertEquals;596061public class TestUpcall extends CallGeneratorHelper {6263static {64System.loadLibrary("TestUpcall");65}66static CLinker abi = CLinker.getInstance();6768static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup();6970static MethodHandle DUMMY;71static MethodHandle PASS_AND_SAVE;7273static {74try {75DUMMY = MethodHandles.lookup().findStatic(TestUpcall.class, "dummy", MethodType.methodType(void.class));76PASS_AND_SAVE = MethodHandles.lookup().findStatic(TestUpcall.class, "passAndSave",77MethodType.methodType(Object.class, Object[].class, AtomicReference.class));78} catch (Throwable ex) {79throw new IllegalStateException(ex);80}81}8283static MemoryAddress dummyStub;8485@BeforeClass86void setup() {87dummyStub = abi.upcallStub(DUMMY, FunctionDescriptor.ofVoid(), ResourceScope.newImplicitScope());88}8990@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)91public void testUpcalls(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {92List<Consumer<Object>> returnChecks = new ArrayList<>();93List<Consumer<Object[]>> argChecks = new ArrayList<>();94MemoryAddress addr = LOOKUP.lookup(fName).get();95MethodType mtype = methodType(ret, paramTypes, fields);96try (NativeScope scope = new NativeScope()) {97MethodHandle mh = abi.downcallHandle(addr, scope, mtype, function(ret, paramTypes, fields));98Object[] args = makeArgs(scope.scope(), ret, paramTypes, fields, returnChecks, argChecks);99Object[] callArgs = args;100Object res = mh.invokeWithArguments(callArgs);101argChecks.forEach(c -> c.accept(args));102if (ret == Ret.NON_VOID) {103returnChecks.forEach(c -> c.accept(res));104}105}106}107108@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)109public void testUpcallsNoScope(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {110List<Consumer<Object>> returnChecks = new ArrayList<>();111List<Consumer<Object[]>> argChecks = new ArrayList<>();112MemoryAddress addr = LOOKUP.lookup(fName).get();113MethodType mtype = methodType(ret, paramTypes, fields);114MethodHandle mh = abi.downcallHandle(addr, IMPLICIT_ALLOCATOR, mtype, function(ret, paramTypes, fields));115Object[] args = makeArgs(ResourceScope.newImplicitScope(), ret, paramTypes, fields, returnChecks, argChecks);116Object[] callArgs = args;117if (count % 100 == 0) {118System.gc();119}120Object res = mh.invokeWithArguments(callArgs);121argChecks.forEach(c -> c.accept(args));122if (ret == Ret.NON_VOID) {123returnChecks.forEach(c -> c.accept(res));124}125}126127static MethodType methodType(Ret ret, List<ParamType> params, List<StructFieldType> fields) {128MethodType mt = ret == Ret.VOID ?129MethodType.methodType(void.class) : MethodType.methodType(paramCarrier(params.get(0).layout(fields)));130for (ParamType p : params) {131mt = mt.appendParameterTypes(paramCarrier(p.layout(fields)));132}133mt = mt.appendParameterTypes(MemoryAddress.class); //the callback134return mt;135}136137static FunctionDescriptor function(Ret ret, List<ParamType> params, List<StructFieldType> fields) {138List<MemoryLayout> paramLayouts = params.stream().map(p -> p.layout(fields)).collect(Collectors.toList());139paramLayouts.add(C_POINTER); // the callback140MemoryLayout[] layouts = paramLayouts.toArray(new MemoryLayout[0]);141return ret == Ret.VOID ?142FunctionDescriptor.ofVoid(layouts) :143FunctionDescriptor.of(layouts[0], layouts);144}145146static Object[] makeArgs(ResourceScope scope, Ret ret, List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks, List<Consumer<Object[]>> argChecks) throws ReflectiveOperationException {147Object[] args = new Object[params.size() + 1];148for (int i = 0 ; i < params.size() ; i++) {149args[i] = makeArg(params.get(i).layout(fields), checks, i == 0);150}151args[params.size()] = makeCallback(scope, ret, params, fields, checks, argChecks);152return args;153}154155@SuppressWarnings("unchecked")156static MemoryAddress makeCallback(ResourceScope scope, Ret ret, List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks, List<Consumer<Object[]>> argChecks) {157if (params.isEmpty()) {158return dummyStub.address();159}160161AtomicReference<Object[]> box = new AtomicReference<>();162MethodHandle mh = insertArguments(PASS_AND_SAVE, 1, box);163mh = mh.asCollector(Object[].class, params.size());164165for (int i = 0; i < params.size(); i++) {166ParamType pt = params.get(i);167MemoryLayout layout = pt.layout(fields);168Class<?> carrier = paramCarrier(layout);169mh = mh.asType(mh.type().changeParameterType(i, carrier));170171final int finalI = i;172if (carrier == MemorySegment.class) {173argChecks.add(o -> assertStructEquals((MemorySegment) box.get()[finalI], (MemorySegment) o[finalI], layout));174} else {175argChecks.add(o -> assertEquals(box.get()[finalI], o[finalI]));176}177}178179ParamType firstParam = params.get(0);180MemoryLayout firstlayout = firstParam.layout(fields);181Class<?> firstCarrier = paramCarrier(firstlayout);182183if (firstCarrier == MemorySegment.class) {184checks.add(o -> assertStructEquals((MemorySegment) box.get()[0], (MemorySegment) o, firstlayout));185} else {186checks.add(o -> assertEquals(o, box.get()[0]));187}188189mh = mh.asType(mh.type().changeReturnType(ret == Ret.VOID ? void.class : firstCarrier));190191MemoryLayout[] paramLayouts = params.stream().map(p -> p.layout(fields)).toArray(MemoryLayout[]::new);192FunctionDescriptor func = ret != Ret.VOID193? FunctionDescriptor.of(firstlayout, paramLayouts)194: FunctionDescriptor.ofVoid(paramLayouts);195return abi.upcallStub(mh, func, scope);196}197198static Object passAndSave(Object[] o, AtomicReference<Object[]> ref) {199for (int i = 0; i < o.length; i++) {200if (o[i] instanceof MemorySegment) {201MemorySegment ms = (MemorySegment) o[i];202MemorySegment copy = MemorySegment.allocateNative(ms.byteSize(), ResourceScope.newImplicitScope());203copy.copyFrom(ms);204o[i] = copy;205}206}207ref.set(o);208return o[0];209}210211static void dummy() {212//do nothing213}214}215216217