< prev index next >

test/jdk/java/foreign/TestDowncall.java

Print this page
*** 33,109 ***
   *   TestDowncall
   */
  
  import jdk.incubator.foreign.CLinker;
  import jdk.incubator.foreign.FunctionDescriptor;
  import jdk.incubator.foreign.SymbolLookup;
- import jdk.incubator.foreign.MemoryAddress;
  import jdk.incubator.foreign.MemoryLayout;
  
  import java.lang.invoke.MethodHandle;
- import java.lang.invoke.MethodType;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.function.Consumer;
  
  import jdk.incubator.foreign.MemorySegment;
  import jdk.incubator.foreign.SegmentAllocator;
  import org.testng.annotations.*;
  import static org.testng.Assert.*;
  
  public class TestDowncall extends CallGeneratorHelper {
  
!     static CLinker abi = CLinker.getInstance();
      static {
          System.loadLibrary("TestDowncall");
      }
  
      static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup();
  
      @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
      public void testDowncall(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
          List<Consumer<Object>> checks = new ArrayList<>();
!         MemoryAddress addr = LOOKUP.lookup(fName).get();
-         MethodType mt = methodType(ret, paramTypes, fields);
          FunctionDescriptor descriptor = function(ret, paramTypes, fields);
          Object[] args = makeArgs(paramTypes, fields, checks);
!         try (NativeScope scope = new NativeScope()) {
!             boolean needsScope = mt.returnType().equals(MemorySegment.class);
!             Object res = doCall(addr, scope, mt, descriptor, args);
              if (ret == Ret.NON_VOID) {
                  checks.forEach(c -> c.accept(res));
                  if (needsScope) {
                      // check that return struct has indeed been allocated in the native scope
!                     assertEquals(((MemorySegment) res).scope(), scope.scope());
-                     assertEquals(scope.allocatedBytes(), descriptor.returnLayout().get().byteSize());
-                 } else {
-                     // if here, there should be no allocation through the scope!
-                     assertEquals(scope.allocatedBytes(), 0L);
                  }
-             } else {
-                 // if here, there should be no allocation through the scope!
-                 assertEquals(scope.allocatedBytes(), 0L);
              }
          }
      }
  
      @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
!     public void testDowncallNoScope(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
          List<Consumer<Object>> checks = new ArrayList<>();
!         MemoryAddress addr = LOOKUP.lookup(fName).get();
!         MethodType mt = methodType(ret, paramTypes, fields);
!         FunctionDescriptor descriptor = function(ret, paramTypes, fields);
!         Object[] args = makeArgs(paramTypes, fields, checks);
!         boolean needsScope = mt.returnType().equals(MemorySegment.class);
!         Object res = doCall(addr, IMPLICIT_ALLOCATOR, mt, descriptor, args);
!         if (ret == Ret.NON_VOID) {
!             checks.forEach(c -> c.accept(res));
!             if (needsScope) {
!                 // check that return struct has indeed been allocated in the default scope
!                 try {
!                     ((MemorySegment)res).scope().close(); // should throw
!                     fail("Expected exception!");
!                 } catch (UnsupportedOperationException ex) {
-                     // ok
                  }
              }
          }
      }
  
!     Object doCall(MemoryAddress addr, SegmentAllocator allocator, MethodType type, FunctionDescriptor descriptor, Object[] args) throws Throwable {
!         MethodHandle mh = abi.downcallHandle(addr, allocator, type, descriptor);
          Object res = mh.invokeWithArguments(args);
          return res;
      }
  
!     static MethodType methodType(Ret ret, List<ParamType> params, List<StructFieldType> fields) {
!         MethodType mt = ret == Ret.VOID ?
-                 MethodType.methodType(void.class) : MethodType.methodType(paramCarrier(params.get(0).layout(fields)));
-         for (ParamType p : params) {
-             mt = mt.appendParameterTypes(paramCarrier(p.layout(fields)));
-         }
-         return mt;
      }
  
      static FunctionDescriptor function(Ret ret, List<ParamType> params, List<StructFieldType> fields) {
!         MemoryLayout[] paramLayouts = params.stream().map(p -> p.layout(fields)).toArray(MemoryLayout[]::new);
          return ret == Ret.VOID ?
                  FunctionDescriptor.ofVoid(paramLayouts) :
!                 FunctionDescriptor.of(paramLayouts[0], paramLayouts);
      }
  
      static Object[] makeArgs(List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks) throws ReflectiveOperationException {
!         Object[] args = new Object[params.size()];
          for (int i = 0 ; i < params.size() ; i++) {
!             args[i] = makeArg(params.get(i).layout(fields), checks, i == 0);
          }
          return args;
      }
  }
--- 33,118 ---
   *   TestDowncall
   */
  
  import jdk.incubator.foreign.CLinker;
  import jdk.incubator.foreign.FunctionDescriptor;
+ import jdk.incubator.foreign.GroupLayout;
+ import jdk.incubator.foreign.NativeSymbol;
+ import jdk.incubator.foreign.ResourceScope;
  import jdk.incubator.foreign.SymbolLookup;
  import jdk.incubator.foreign.MemoryLayout;
  
  import java.lang.invoke.MethodHandle;
  import java.util.ArrayList;
  import java.util.List;
  import java.util.function.Consumer;
+ import java.util.stream.Stream;
  
  import jdk.incubator.foreign.MemorySegment;
  import jdk.incubator.foreign.SegmentAllocator;
  import org.testng.annotations.*;
  import static org.testng.Assert.*;
  
  public class TestDowncall extends CallGeneratorHelper {
  
!     static CLinker abi = CLinker.systemCLinker();
      static {
          System.loadLibrary("TestDowncall");
+         System.loadLibrary("TestDowncallStack");
      }
  
      static final SymbolLookup LOOKUP = SymbolLookup.loaderLookup();
  
      @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
      public void testDowncall(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
          List<Consumer<Object>> checks = new ArrayList<>();
!         NativeSymbol addr = LOOKUP.lookup(fName).get();
          FunctionDescriptor descriptor = function(ret, paramTypes, fields);
          Object[] args = makeArgs(paramTypes, fields, checks);
!         try (ResourceScope scope = ResourceScope.newSharedScope()) {
!             boolean needsScope = descriptor.returnLayout().map(GroupLayout.class::isInstance).orElse(false);
!             SegmentAllocator allocator = needsScope ?
+                     SegmentAllocator.newNativeArena(scope) :
+                     THROWING_ALLOCATOR;
+             Object res = doCall(addr, allocator, descriptor, args);
              if (ret == Ret.NON_VOID) {
                  checks.forEach(c -> c.accept(res));
                  if (needsScope) {
                      // check that return struct has indeed been allocated in the native scope
!                     assertEquals(((MemorySegment) res).scope(), scope);
                  }
              }
          }
      }
  
      @Test(dataProvider="functions", dataProviderClass=CallGeneratorHelper.class)
!     public void testDowncallStack(int count, String fName, Ret ret, List<ParamType> paramTypes, List<StructFieldType> fields) throws Throwable {
          List<Consumer<Object>> checks = new ArrayList<>();
!         NativeSymbol addr = LOOKUP.lookup("s" + fName).get();
!         FunctionDescriptor descriptor = functionStack(ret, paramTypes, fields);
!         Object[] args = makeArgsStack(paramTypes, fields, checks);
!         try (ResourceScope scope = ResourceScope.newSharedScope()) {
!             boolean needsScope = descriptor.returnLayout().map(GroupLayout.class::isInstance).orElse(false);
!             SegmentAllocator allocator = needsScope ?
!                     SegmentAllocator.newNativeArena(scope) :
!                     THROWING_ALLOCATOR;
!             Object res = doCall(addr, allocator, descriptor, args);
!             if (ret == Ret.NON_VOID) {
!                 checks.forEach(c -> c.accept(res));
!                 if (needsScope) {
!                     // check that return struct has indeed been allocated in the native scope
!                     assertEquals(((MemorySegment) res).scope(), scope);
                  }
              }
          }
      }
  
!     Object doCall(NativeSymbol symbol, SegmentAllocator allocator, FunctionDescriptor descriptor, Object[] args) throws Throwable {
!         MethodHandle mh = downcallHandle(abi, symbol, allocator, descriptor);
          Object res = mh.invokeWithArguments(args);
          return res;
      }
  
!     static FunctionDescriptor functionStack(Ret ret, List<ParamType> params, List<StructFieldType> fields) {
!         return function(ret, params, fields, STACK_PREFIX_LAYOUTS);
      }
  
      static FunctionDescriptor function(Ret ret, List<ParamType> params, List<StructFieldType> fields) {
!         return function(ret, params, fields, List.of());
+     }
+ 
+     static FunctionDescriptor function(Ret ret, List<ParamType> params, List<StructFieldType> fields, List<MemoryLayout> prefix) {
+         List<MemoryLayout> pLayouts = params.stream().map(p -> p.layout(fields)).toList();
+         MemoryLayout[] paramLayouts = Stream.concat(prefix.stream(), pLayouts.stream()).toArray(MemoryLayout[]::new);
          return ret == Ret.VOID ?
                  FunctionDescriptor.ofVoid(paramLayouts) :
!                 FunctionDescriptor.of(paramLayouts[prefix.size()], paramLayouts);
+     }
+ 
+     static Object[] makeArgsStack(List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks) throws ReflectiveOperationException {
+         return makeArgs(params, fields, checks, STACK_PREFIX_LAYOUTS);
      }
  
      static Object[] makeArgs(List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks) throws ReflectiveOperationException {
!         return makeArgs(params, fields, checks, List.of());
+     }
+ 
+     static Object[] makeArgs(List<ParamType> params, List<StructFieldType> fields, List<Consumer<Object>> checks, List<MemoryLayout> prefix) throws ReflectiveOperationException {
+         Object[] args = new Object[prefix.size() + params.size()];
+         int argNum = 0;
+         for (MemoryLayout layout : prefix) {
+             args[argNum++] = makeArg(layout, null, false);
+         }
          for (int i = 0 ; i < params.size() ; i++) {
!             args[argNum++] = makeArg(params.get(i).layout(fields), checks, i == 0);
          }
          return args;
      }
  }
< prev index next >