I am trying to create a shared library(dll with header and lib files) of a java code using graalvm.
there will be one java method with 2 parameters of type String which I will call from c++.
I am using a maven project, I am not able to create the dll out of my java code, I am using graalvm to convert my java code to dll.
My java code looks like this:
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;
}
but when I try to build the code into dll, I got this error
Error: Entry point method parameter types are restricted to primitive types, word types and enumerations (@CEnum): demo.MyClass.myFunc(IsolateThread, String, String)
I searched but didn't find suitable solutions to this problem. can anyone here please tell me how to call java methods from c++ with non-primitive datatypes any suggestions of any kind will be a great help, thanks in advance
There are specific preconditions that a java method needs to fulfill in order to run successfully from C or C++ in GraalVM.
These preconditions should be taken into account when designing the Java entry point methods that are going to be annotated with @CEntryPoint
. The following preconditions are mentioned in the CEntryPoint documentation.
Enum
the enum class
must have a CEnum
annotation.In the CEntryPoint documentation.@CEntryPoint
documentation explicitly mention to catch all exceptions inside the java entry point methods.IsolateThread
parameter is required. More preciselyAn 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.
The sample in your question throws this error since the myFunc
method signature includes objects, for instance the String
parameters. Namely x
and y
. This is not allowed according to the precondition 1 from above. This is what the error description tries to say.
The solution is to use the functionality provided to convert between Java types and C types. In this case in order to pass text between C
and Java
we can use a CCharPointer
. Since text is modeled differently in C
and Java
, a Java String
must be converted to C *char
and vice versa.
Creating and returning a Java String
There is an example below that can be used when byte[]
represents text.
//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;
}
}
Using and returning an array allocated in C
You can also follow a C style and pass as an argument an array in C and then use this array to write the result byte values in Java. The method CCharPointer.write(int,byte) can write Java byte
values to specific array indexes in the *char
or char[]
. The byte array can also be returned, if needed.
@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;
}
Using a Java NIO ByteBuffer
For larger byte arrays you can check CTypeConversion that can create a Java NIO ByteBuffer
with a specific capacity that refers to the native memory. Note that
the caller is responsible for ensuring that the memory can be safely accessed while the ByteBuffer is used, and for freeing the memory afterwards.