如何使用 GraalVM 从 C++ 调用非原始类型作为参数的 Java 入口点方法

How to call a Java entrypoint method with non-primitive types as parameters from C++ using GraalVM

我正在尝试使用 graalvm 创建 java 代码的共享库(带有头文件和 lib 文件的 dll)。

将有一个 java 方法带有 2 个字符串类型的参数,我将从 c++ 中调用它。

我正在使用 Maven 项目,我无法从我的 java 代码创建 dll,我正在使用 graalvm 将我的 java 代码转换为 dll。

我的 java 代码如下所示:

package demo;

import org.graalvm.nativeimage.IsolateThread;
import org.graalvm.nativeimage.c.function.CEntryPoint;

public class MyClass{
    
    @CEntryPoint (name = "myFunc")
    public static byte[] myfunc(IsolateThread thread, String x, String y) {

        // logic goes here

        byte[] arr = "byte array will contain actual bytes".getBytes();
        
        return arr;
    }

但是当我尝试将代码构建到 dll 中时,出现了这个错误

Error: Entry point method parameter types are restricted to primitive types, word types and enumerations (@CEnum): demo.MyClass.myFunc(IsolateThread, String, String)

我进行了搜索,但没有找到解决此问题的合适方法。 这里的任何人都可以告诉我如何使用非原始数据类型从 C++ 调用 java 方法 任何类型的建议都会有很大帮助,在此先感谢

为了在 GraalVM 中从 C 或 C++ 成功 运行,java 方法需要满足特定的先决条件。 在设计将用 @CEntryPoint 注释的 Java 入口点方法时,应考虑这些先决条件。提到以下先决条件 in the CEntryPoint documentation.

  1. Java 入口点方法只允许包含原始 Java 类型、字值和枚举。此外,为了实际使用 Enumenum class 必须具有 CEnum 注释。In the CEntryPoint documentation.
  2. Java 入口点方法应该是静态的。
  3. Java 入口点方法应该捕获所有异常,因为它不应该抛出任何异常。如果未捕获到异常,则会打印异常,然后终止进程。但是 @CEntryPoint 文档明确提到要捕获 java 入口点方法中的所有异常。
  4. 需要一个 IsolateThread 参数。更准确地说

An execution context must be passed as a parameter and can be either an IsolateThread that is specific to the current thread, or an Isolate for an isolate in which the current thread is attached. These pointers can be obtained via the methods of CurrentIsolate. When there is more than one parameter of these types, exactly one of the parameters must be annotated with CEntryPoint.IsolateThreadContext for IsolateThread, or CEntryPoint.IsolateContext for Isolate.

您问题中的示例会引发此错误,因为 myFunc 方法签名包含对象,例如 String 参数。即 xy。根据上面的先决条件1,这是不允许的。这就是错误描述试图表达的内容。

解决方案是使用提供的功能在 Java 类型和 C 类型之间进行转换。在这种情况下,为了在 CJava 之间传递文本,我们可以使用 CCharPointer。由于文本在 CJava 中的建模方式不同,因此必须将 Java String 转换为 C *char,反之亦然。

创建并返回一个 Java 字符串

下面有一个例子,当byte[]表示文本时可以使用。

//These are the imports needed
import org.graalvm.nativeimage.c.type.CCharPointer;
import org.graalvm.nativeimage.c.type.CTypeConversion;

@CEntryPoint(name = "myFunc")
  public static CCharPointer myFunc(IsolateThread thread, CCharPointer x, CCharPointer y) {
        //Convert C *char to Java String
        final String xString= CTypeConversion.toJavaString(x);
        final String yString= CTypeConversion.toJavaString(y);

        //logic goes here

        //Convert Java String to C *char
        try(final CTypeConversion.CCharPointerHolder holder=CTypeConversion.toCString("Hello from Java")){
        
                final CCharPointer result=holder.get();
                return result;
         }
        
  }

使用并返回在 C

中分配的数组

您也可以遵循 C 风格,将 C 中的数组作为参数传递,然后使用此数组将结果字节值写入 Java。 CCharPointer.write(int,byte) 方法可以将 Java byte 值写入 *charchar[] 中的特定数组索引。如果需要,也可以返回字节数组。

@CEntryPoint(name = "myFunc2")
  public static CCharPointer myFunc2(IsolateThread thread
         , CCharPointer x, CCharPointer y                                                                                  
         , CCharPointer resultArray, int resultArrayLength) {

        //Convert C *char to Java String
        final String xString= CTypeConversion.toJavaString(x);
        final String yString= CTypeConversion.toJavaString(y);

        //logic goes here

        //Fill in the result array
        final byte sampleByteValue=7;
        for(int index =0; index<resultArrayLength; index++){
        resultArray.write(index, sampleByteValue);
        }
        return resultArray;
  }

使用 Java NIO ByteBuffer

对于较大的字节数组,您可以检查 CTypeConversion that can create a Java NIO ByteBuffer 具有引用本机内存的特定容量。注意

the caller is responsible for ensuring that the memory can be safely accessed while the ByteBuffer is used, and for freeing the memory afterwards.