oracle / graal

GraalVM compiles Java applications into native executables that start instantly, scale fast, and use fewer compute resources 🚀
https://www.graalvm.org
Other
20.41k stars 1.64k forks source link

Access my own Java classes from Graal JS #4976

Open Daniel-Alievsky opened 2 years ago

Daniel-Alievsky commented 2 years ago

I have very simple test:

package net.algart.bridges.graalvm.tests;
import org.graalvm.polyglot.Context;
public class GraalVMFullAccessToJava {
    public void test() {
        System.out.println("test");
    }

    public static void testStatic() {
        System.out.println("testStatic");
    }

    public static void main(String[] args) {
        Context context = Context.newBuilder("js")
                .allowAllAccess(true)
                .build();

        context.eval("js","java.lang.System.out.println('Hello')");
        context.getBindings("js").putMember("test", new GraalVMFullAccessToJava());
        context.eval("js","test.test()");
        context.eval("js","Java.type('net.algart.bridges.graalvm.tests.GraalVMFullAccessToJava').testStatic()");
        context.eval("js","net.algart.bridges.graalvm.tests.GraalVMFullAccessToJava.testStatic()");
    }
}

GraalVM after allowAllAccess, really, allows to access to any standard Java classes and also to an instance of my class, added to bindings. Also it works via Java.type(). But the last line does not work! Results:

Hello test testStatic Exception in thread "main" ReferenceError: net is not defined at :program(Unnamed:1:0-2) at org.graalvm.polyglot.Context.eval(Context.java:425) at net.algart.bridges.graalvm.tests.GraalVMFullAccessToJava.main(GraalVMFullAccessToJava.java:47)

What is the reason? What is the difference between standard "java.lang.System" class and my own class?

Moreover, there is more serious problem. While I call context.eval("js","Java.type('net.algart.bridges.graalvm.tests.GraalVMFullAccessToJava').testStatic()"); (or access to any other my class by Java.type) from IntelliJ IDEA, it works normally. However, when I try to use the similar code from our external program, written in C++, which initializes JavaVM by JNI, it does not work!

org.graalvm.polyglot.PolyglotException: TypeError: Access to host class .... is not allowed or does not exist.

The main difference is that Thread.currentThread().getContextClassLoader() is null in this case. If I attempt to set it manually, it works:

    static {
        correctClassLoader(Thread.currentThread());
    }
    static void correctClassLoader(Thread thread) {
        ClassLoader contextClassLoader = thread.getContextClassLoader();
        if (contextClassLoader == null) {
            thread.setContextClassLoader(GraalClassLoaderCorrection.class.getClassLoader());
            // - attempt to avoid a problem: GraalVM uses getContextClassLoader, but
            // it can be null while calling from JNI
        }
    }

But only once - for this thread. When I will try to access to my class next time from other thread, the problem will occur again. It seems that I must to set class loader in every thread, where I want to work with Graal. Not too good solution...

Do you have any ideas? Can you fix this problem with null contextClassLoader ?

Daniel-Alievsky commented 2 years ago

I've found better workaround: it is enough to call builder.hostClassLoader((some_our_class).class.getClassLoader()); while building Context via Context.Builder. But it is still a workaround, it is not an obvious and convenient solution.

woess commented 2 years ago

What is the reason? What is the difference between standard "java.lang.System" class and my own class?

That's simply because do not have a net package global (only com, org, edu, java, javax, and javafx). So you would have to either use Java.type("net.x.y.Z"), Packages.net.x.y.Z, or add your own net global like e.g.: globalThis.net = Packages.net so that you can use net.x.y.Z.

Daniel-Alievsky commented 2 years ago

I thought that "net" global package is almost so popular as "com" and "org" :) Not a problem, Java.type is suitable solution. Main problem is not this, but Java VM with null contextClassLoader.