Path: blob/master/test/jdk/java/foreign/TestDowncall.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 TestDowncall29*30* @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies31* --enable-native-access=ALL-UNNAMED -Dgenerator.sample.factor=1732* TestDowncall33*34* @run testng/othervm -Xint -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies35* --enable-native-access=ALL-UNNAMED -Dgenerator.sample.factor=10000036* TestDowncall37*/3839import jdk.incubator.foreign.CLinker;40import jdk.incubator.foreign.FunctionDescriptor;41import jdk.incubator.foreign.SymbolLookup;42import jdk.incubator.foreign.MemoryAddress;43import jdk.incubator.foreign.MemoryLayout;4445import java.lang.invoke.MethodHandle;46import java.lang.invoke.MethodType;47import java.util.ArrayList;48import java.util.List;49import java.util.function.Consumer;5051import jdk.incubator.foreign.MemorySegment;52import jdk.incubator.foreign.SegmentAllocator;53import org.testng.annotations.*;54import static org.testng.Assert.*;5556public class TestDowncall extends CallGeneratorHelper {5758static CLinker abi = CLinker.getInstance();59static {60System.loadLibrary("TestDowncall");61}6263static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup();6465@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)66public void testDowncall(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {67List<Consumer<Object>> checks = new ArrayList<>();68MemoryAddress addr = LOOKUP.lookup(fName).get();69MethodType mt = methodType(ret, paramTypes, fields);70FunctionDescriptor descriptor = function(ret, paramTypes, fields);71Object[] args = makeArgs(paramTypes, fields, checks);72try (NativeScope scope = new NativeScope()) {73boolean needsScope = mt.returnType().equals(MemorySegment.class);74Object res = doCall(addr, scope, mt, descriptor, args);75if (ret == Ret.NON_VOID) {76checks.forEach(c -> c.accept(res));77if (needsScope) {78// check that return struct has indeed been allocated in the native scope79assertEquals(((MemorySegment) res).scope(), scope.scope());80assertEquals(scope.allocatedBytes(), descriptor.returnLayout().get().byteSize());81} else {82// if here, there should be no allocation through the scope!83assertEquals(scope.allocatedBytes(), 0L);84}85} else {86// if here, there should be no allocation through the scope!87assertEquals(scope.allocatedBytes(), 0L);88}89}90}9192@Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)93public void testDowncallNoScope(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {94List<Consumer<Object>> checks = new ArrayList<>();95MemoryAddress addr = LOOKUP.lookup(fName).get();96MethodType mt = methodType(ret, paramTypes, fields);97FunctionDescriptor descriptor = function(ret, paramTypes, fields);98Object[] args = makeArgs(paramTypes, fields, checks);99boolean needsScope = mt.returnType().equals(MemorySegment.class);100Object res = doCall(addr, IMPLICIT_ALLOCATOR, mt, descriptor, args);101if (ret == Ret.NON_VOID) {102checks.forEach(c -> c.accept(res));103if (needsScope) {104// check that return struct has indeed been allocated in the default scope105try {106((MemorySegment)res).scope().close(); // should throw107fail("Expected exception!");108} catch (UnsupportedOperationException ex) {109// ok110}111}112}113}114115Object doCall(MemoryAddress addr, SegmentAllocator allocator, MethodType type, FunctionDescriptor descriptor, Object[] args) throws Throwable {116MethodHandle mh = abi.downcallHandle(addr, allocator, type, descriptor);117Object res = mh.invokeWithArguments(args);118return res;119}120121static MethodType methodType(Ret ret, List<ParamType> params, List<StructFieldType> fields) {122MethodType mt = ret == Ret.VOID ?123MethodType.methodType(void.class) : MethodType.methodType(paramCarrier(params.get(0).layout(fields)));124for (ParamType p : params) {125mt = mt.appendParameterTypes(paramCarrier(p.layout(fields)));126}127return mt;128}129130static FunctionDescriptor function(Ret ret, List<ParamType> params, List<StructFieldType> fields) {131MemoryLayout[] paramLayouts = params.stream().map(p -> p.layout(fields)).toArray(MemoryLayout[]::new);132return ret == Ret.VOID ?133FunctionDescriptor.ofVoid(paramLayouts) :134FunctionDescriptor.of(paramLayouts[0], paramLayouts);135}136137static Object[] makeArgs(List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks) throws ReflectiveOperationException {138Object[] args = new Object[params.size()];139for (int i = 0 ; i < params.size() ; i++) {140args[i] = makeArg(params.get(i).layout(fields), checks, i == 0);141}142return args;143}144}145146147