1 /*
  2  *  Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
  3  *  DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4  *
  5  *  This code is free software; you can redistribute it and/or modify it
  6  *  under the terms of the GNU General Public License version 2 only, as
  7  *  published by the Free Software Foundation.
  8  *
  9  *  This code is distributed in the hope that it will be useful, but WITHOUT
 10  *  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 11  *  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 12  *  version 2 for more details (a copy is included in the LICENSE file that
 13  *  accompanied this code).
 14  *
 15  *  You should have received a copy of the GNU General Public License version
 16  *  2 along with this work; if not, write to the Free Software Foundation,
 17  *  Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 18  *
 19  *   Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 20  *  or visit www.oracle.com if you need additional information or have any
 21  *  questions.
 22  *
 23  */
 24 
 25 /*
 26  * @test
 27  * @requires ((os.arch == "amd64" | os.arch == "x86_64") & sun.arch.data.model == "64") | os.arch == "aarch64"
 28  * @modules jdk.incubator.foreign/jdk.internal.foreign
 29  * @build NativeTestHelper CallGeneratorHelper TestDowncall
 30  *
 31  * @run testng/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies
 32  *   --enable-native-access=ALL-UNNAMED -Dgenerator.sample.factor=17
 33  *   TestDowncall
 34  *
 35  * @run testng/othervm -Xint -XX:+IgnoreUnrecognizedVMOptions -XX:-VerifyDependencies
 36  *   --enable-native-access=ALL-UNNAMED -Dgenerator.sample.factor=100000
 37  *   TestDowncall
 38  */
 39 
 40 import jdk.incubator.foreign.Addressable;
 41 import jdk.incubator.foreign.CLinker;
 42 import jdk.incubator.foreign.FunctionDescriptor;
 43 import jdk.incubator.foreign.NativeSymbol;
 44 import jdk.incubator.foreign.ResourceScope;
 45 import jdk.incubator.foreign.SymbolLookup;
 46 import jdk.incubator.foreign.MemoryAddress;
 47 import jdk.incubator.foreign.MemoryLayout;
 48 
 49 import java.lang.invoke.MethodHandle;
 50 import java.lang.invoke.MethodType;
 51 import java.util.ArrayList;
 52 import java.util.List;
 53 import java.util.function.Consumer;
 54 
 55 import jdk.incubator.foreign.MemorySegment;
 56 import jdk.incubator.foreign.SegmentAllocator;
 57 import org.testng.annotations.*;
 58 import static org.testng.Assert.*;
 59 
 60 public class TestDowncall extends CallGeneratorHelper {
 61 
 62     static CLinker abi = CLinker.systemCLinker();
 63     static {
 64         System.loadLibrary("TestDowncall");
 65     }
 66 
 67     static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup();
 68 
 69     @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
 70     public void testDowncall(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
 71         List<Consumer<Object>> checks = new ArrayList<>();
 72         NativeSymbol addr = LOOKUP.lookup(fName).get();
 73         MethodType mt = methodType(ret, paramTypes, fields);
 74         FunctionDescriptor descriptor = function(ret, paramTypes, fields);
 75         Object[] args = makeArgs(paramTypes, fields, checks);
 76         try (ResourceScope scope = ResourceScope.newSharedScope()) {
 77             boolean needsScope = mt.returnType().equals(MemorySegment.class);
 78             SegmentAllocator allocator = needsScope ?
 79                     SegmentAllocator.newNativeArena(scope) :
 80                     THROWING_ALLOCATOR;
 81             Object res = doCall(addr, allocator, descriptor, args);
 82             if (ret == Ret.NON_VOID) {
 83                 checks.forEach(c -> c.accept(res));
 84                 if (needsScope) {
 85                     // check that return struct has indeed been allocated in the native scope
 86                     assertEquals(((MemorySegment) res).scope(), scope);
 87                 }
 88             }
 89         }
 90     }
 91 
 92     Object doCall(NativeSymbol symbol, SegmentAllocator allocator, FunctionDescriptor descriptor, Object[] args) throws Throwable {
 93         MethodHandle mh = downcallHandle(abi, symbol, allocator, descriptor);
 94         Object res = mh.invokeWithArguments(args);
 95         return res;
 96     }
 97 
 98     static MethodType methodType(Ret ret, List<ParamType> params, List<StructFieldType> fields) {
 99         MethodType mt = ret == Ret.VOID ?
100                 MethodType.methodType(void.class) : MethodType.methodType(carrier(params.get(0).layout(fields), false));
101         for (ParamType p : params) {
102             mt = mt.appendParameterTypes(carrier(p.layout(fields), true));
103         }
104         return mt;
105     }
106 
107     static FunctionDescriptor function(Ret ret, List<ParamType> params, List<StructFieldType> fields) {
108         MemoryLayout[] paramLayouts = params.stream().map(p -> p.layout(fields)).toArray(MemoryLayout[]::new);
109         return ret == Ret.VOID ?
110                 FunctionDescriptor.ofVoid(paramLayouts) :
111                 FunctionDescriptor.of(paramLayouts[0], paramLayouts);
112     }
113 
114     static Object[] makeArgs(List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks) throws ReflectiveOperationException {
115         Object[] args = new Object[params.size()];
116         for (int i = 0 ; i < params.size() ; i++) {
117             args[i] = makeArg(params.get(i).layout(fields), checks, i == 0);
118         }
119         return args;
120     }
121 }