Search code examples
javagraalvm

Running JS script from Java code in GraalVM native-image


I am experimenting with running JS scripts from Java code within a GraalVM native-image.

The Java code looks like this:

try (Context context = Context.create("js")) {
    Value bindings = context.getBindings("js");
    bindings.putMember("response", response);
    bindings.putMember("UTF8", StandardCharsets.UTF_8);
    context.eval("js", script);
} catch (PolyglotException e) {
    error("Error: " + e, 10);
}

The JS code just tries to use the response object by calling a method on it, for example:

 print("Status code: " + response.getStatusCode());

This works when running in GraalVM, but when creating a native-image, it fails with this error:

INVOKE on JavaObject[...] failed due to: Message not supported: INVOKE

If I just print the object as in print("Response: " + response);, it does not fail. But if I attempt to call any method on response, I get this error (even toString() or hashCode()).

Is there something else I need to do or is this just a bug in SubstractVM native-image, currently?

My GraalVM version:

java version "1.8.0_172"
Java(TM) SE Runtime Environment (build 1.8.0_172-b11)
GraalVM 1.0.0-rc4 (build 25.71-b01-internal-jvmci-0.45, mixed mode)

native-image command I am using:

native-image --language:js --report-unsupported-elements-at-runtime -jar my.jar

Solution

  • Update: as of RC 13 reflective access with native image is now supported. You need to provide a reflection config to native image.

    Unfortunately GraalVM, as of RC5, doesn't yet support reflective access of Java objects when compiled using native-image. We plan to support this in one of the next release candidates.

    As a temporary workaround you may use the Proxy API like this:

    try (Context context = Context.create("js")) {
        Map<String, Object> myObject = new HashMap<>();
        myObject.put("foo", "bar");
        context.getBindings("js").putMember("hostObject", ProxyObject.fromMap(myObject));
        assert "bar".equals(context.eval("js", "hostObject.foo").asString());
        myObject.put("foo", "baz");
        assert "baz".equals(context.eval("js", "hostObject.foo").asString());
    }
    

    The Proxy API allows to mimic guest language values.

    Here is another Proxy Example: http://www.graalvm.org/docs/graalvm-as-a-platform/embed/#computed-arrays-using-polyglot-proxies

    Proxy Javadoc: http://www.graalvm.org/sdk/javadoc/org/graalvm/polyglot/proxy/package-summary.html