Java Foreign Linker API 2D arrays

Viewed 54

I trying to downcall C code from Java, using Foreign Linker API. The target is to print 2D array (matrix) from C. But i can't understand how can i send int[][] to C from Java.

C code:

#include "library.h"

#include <iostream>

extern "C"
{
void printMatrix(int **matrix, int width, int height) {
    for (int i = 0; i < width; ++i) {
        printArray(matrix[i], height);
        std::cout << std::endl;
    }
}

void printArray(int *array, int width) {
    for (int j = 0; j < width; ++j) {
        std::cout << array[j] << " ";
    }
}
};

I understand that int[][] in memory is "array of links to arrays", so i try to send this as MemoryAddress of long[] where i put addresses of int[]'s

   public static void main(String[] args) throws Throwable {
      System.loadLibrary("test_matrix");
      MethodHandle handle = CLinker.getInstance().downcallHandle(
              SymbolLookup.loaderLookup().lookup("printMatrix").orElseThrow(),
              MethodType.methodType(void.class, MemoryAddress.class, int.class, int.class),
              FunctionDescriptor.ofVoid(CLinker.C_POINTER, CLinker.C_INT)
      );
      int[][] arr = {{1, 2}, {2, 3}, {3, 4}};
      try (ResourceScope rs = ResourceScope.newConfinedScope()) {
         long[] memories = new long[arr.length];
         for (int i = 0; i < arr.length; ++i) {
            MemorySegment ms = allocateArray(arr[i], rs);
            memories[i] = ms.address().toRawLongValue();
         }
         MemorySegment ms = MemorySegment.allocateNative(8L * memories.length, rs);
         ms.copyFrom(MemorySegment.ofArray(memories));
         handle.invokeExact(ms.address(), arr.length, 2);
      }
   }

   private static MemorySegment allocateArray(int[] array, ResourceScope rs) {
      MemorySegment ms = MemorySegment.allocateNative(4L * array.length, rs);
      ms.copyFrom(MemorySegment.ofArray(array));
      return ms;
   }

I been able to call printArray function, but when i try to call printMatrix i got exception:

Exception in thread "main" java.lang.IllegalArgumentException: Arity mismatch: (MemoryAddress,int,int)void != (b64[abi/kind=POINTER]b32[abi/kind=INT])v
    at jdk.incubator.foreign/jdk.internal.foreign.abi.SharedUtils.checkFunctionTypes(SharedUtils.java:247)
    at jdk.incubator.foreign/jdk.internal.foreign.abi.x64.sysv.CallArranger.getBindings(CallArranger.java:89)
    at jdk.incubator.foreign/jdk.internal.foreign.abi.x64.sysv.CallArranger.arrangeDowncall(CallArranger.java:125)
    at jdk.incubator.foreign/jdk.internal.foreign.abi.x64.sysv.SysVx64Linker.downcallHandle(SysVx64Linker.java:90)
    at jdk.incubator.foreign/jdk.internal.foreign.AbstractCLinker.downcallHandle(AbstractCLinker.java:44)
    at ru.semperante.tests.MainClass.main(MainClass.java:68)
1 Answers

I recognize that i don't pass second C_INT to FunctionDescriptor.ofVoid Working example:

   public static void main(String[] args) throws Throwable {
      System.loadLibrary("test_matrix");
      MethodHandle handle = CLinker.getInstance().downcallHandle(
              SymbolLookup.loaderLookup().lookup("printMatrix").orElseThrow(),
              MethodType.methodType(void.class, MemoryAddress.class, int.class, int.class),
              FunctionDescriptor.ofVoid(CLinker.C_POINTER, CLinker.C_INT, CLinker.C_INT)
      );
      int[][] arr = {{1, 2}, {2, 3}, {3, 4}};
      try (ResourceScope rs = ResourceScope.newConfinedScope()) {
         long[] memories = new long[arr.length];
         for (int i = 0; i < arr.length; ++i) {
            MemorySegment ms = allocateArray(arr[i], rs);
            memories[i] = ms.address().toRawLongValue();
         }
         MemorySegment ms = MemorySegment.allocateNative(8L * memories.length, rs);
         ms.copyFrom(MemorySegment.ofArray(memories));
         handle.invokeExact(ms.address(), arr.length, 2);
      }
   }

   private static MemorySegment allocateArray(int[] array, ResourceScope rs) {
      MemorySegment ms = MemorySegment.allocateNative(4L * array.length, rs);
      ms.copyFrom(MemorySegment.ofArray(array));
      return ms;
   }

prints

WARNING: Using incubator modules: jdk.incubator.foreign
1 2 
2 3 
3 4 

Process finished with exit code 0
Related