I am having difficulties in accessing java methods from a shared library built with GraalVM.
Imagine this code loading a GraalVM Shared library:
public class Main {
static native int onEnable(long isolateThread, int a);
static native long createIsolate();
public static void main(String[] args) throws Exception {
try {
System.loadLibrary("simplelib");
} catch (UnsatisfiedLinkError e) {
NativeHelper.loadFromJar("simplelib", Main.class);
}
System.out.println("Loaded simplelib");
System.out.println(
"onEnable returned: " + onEnable(createIsolate(), 1)
);
}
public static void printedFromGraal() {
System.out.println("Printed from GraalVM");
}
}
Then, I can easily implement onEnable()
with GraalVM as follows:
@CEntryPoint(name = "Java_com_daysling_dllloader_Main_onEnable")
public static int onEnable(Pointer env, Pointer clazz, IsolateThread thread, int a) throws Exception {
return a + 10;
}
@CEntryPoint(name = "Java_com_daysling_dllloader_Main_createIsolate", builtin=CEntryPoint.Builtin.CREATE_ISOLATE)
public static native IsolateThread createIsolate();
It's not really a big deal, but the problem comes when trying to call the printedFromGraal()
function from inside the GraalVM shared library. I've read documentation, searched on slack and much more but I still haven't found anything useful or any kind of examples.
The most concerning thing to be me is, if I were to implement a ReflectionHelper
in my main application in order to ease JNI (if this is even possible possible) from the GraalVM:
package com.daysling.simplelib.reflections;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class ReflectionHelper {
public static ReflectionPackage getPackage(String packageName) {
return new ReflectionPackage(packageName);
}
public static class ReflectionPackage {
private final String packageName;
private ReflectionPackage(String packageName) {
this.packageName = packageName;
}
public ReflectedClass getClass(String className) throws ClassNotFoundException {
String fullClassName = packageName + "." + className;
Class<?> clazz = Class.forName(fullClassName);
return new ReflectedClass(clazz);
}
public List<ReflectedClass> findAllClasses() throws ClassNotFoundException {
List<ReflectedClass> classes = new ArrayList<>();
String packagePath = packageName.replace('.', '/');
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader == null) {
classLoader = ReflectionHelper.class.getClassLoader();
}
java.net.URL resource = classLoader.getResource(packagePath);
if (resource != null) {
String filePath = resource.getFile();
if (filePath != null) {
java.io.File folder = new java.io.File(filePath);
java.io.File[] files = folder.listFiles();
if (files != null) {
for (java.io.File file : files) {
if (file.isFile() && file.getName().endsWith(".class")) {
String className = file.getName().substring(0, file.getName().lastIndexOf('.'));
classes.add(getClass(className));
}
}
}
}
}
return classes;
}
public static class ReflectedClass {
private final Class<?> clazz;
private ReflectedClass(Class<?> clazz) {
this.clazz = clazz;
}
public String getName() {
return clazz.getName();
}
public ReflectedClass getSuperclass() {
Class<?> superclass = clazz.getSuperclass();
return (superclass != null) ? new ReflectedClass(superclass) : null;
}
public List<ReflectedMethod> findAllMethods() {
List<ReflectedMethod> methods = new ArrayList<>();
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method method : declaredMethods) {
methods.add(new ReflectedMethod(method));
}
return methods;
}
public List<ReflectedField> findAllFields() {
List<ReflectedField> fields = new ArrayList<>();
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
fields.add(new ReflectedField(field));
}
return fields;
}
public ReflectedMethod findMethod(String methodName, Class<?>... parameterTypes) throws NoSuchMethodException {
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
return new ReflectedMethod(method);
}
public ReflectedField findField(String fieldName) throws NoSuchFieldException {
Field field = clazz.getDeclaredField(fieldName);
return new ReflectedField(field);
}
}
}
public static class ReflectedMethod {
private final Method method;
private ReflectedMethod(Method method) {
this.method = method;
}
public String getName() {
return method.getName();
}
public Class<?> getReturnType() {
return method.getReturnType();
}
public Class<?>[] getParameterTypes() {
return method.getParameterTypes();
}
public ReflectedObject invoke(Object object, Object... args) throws Exception {
Object result = method.invoke(object, args);
return new ReflectedObject(result);
}
}
public static class ReflectedObject {
private final Object object;
public ReflectedObject(Object object) {
this.object = object;
}
public ReflectionPackage.ReflectedClass getJavaClass() {
return new ReflectionPackage.ReflectedClass(object.getClass());
}
public ReflectedObject callMethod(String methodName, Object... args) throws Exception {
Class<?>[] parameterTypes = new Class<?>[args.length];
for (int i = 0; i < args.length; i++) {
parameterTypes[i] = args[i].getClass();
}
Method method = object.getClass().getDeclaredMethod(methodName, parameterTypes);
Object result = method.invoke(object, args);
return new ReflectedObject(result);
}
public Object getObject() {
return object;
}
public static ReflectedObject getAsReflectedObject(Object object) {
return new ReflectedObject(object);
}
}
public static class ReflectedField {
private final Field field;
private ReflectedField(Field field) {
this.field = field;
}
public String getName() {
return field.getName();
}
public Class<?> getType() {
return field.getType();
}
public Object get(Object object) throws IllegalAccessException {
return field.get(object);
}
public void set(Object object, Object value) throws IllegalAccessException {
field.set(object, value);
}
}
}
I don't think I can replicate an wrapper exact to how it'd looking in any java code.