4

We're using GraalVM.js as a ScriptEngine in our application. The goal is to have a Java JRE that can run user-defined javascript scripts to extend functionality.

MVCE:

        ScriptEngine engine = GraalJSScriptEngine.create(null, Context.newBuilder("js"));

        String script = "6 * 7";
        Object eval = ((Compilable) engine).compile(script).eval();
        System.out.println(eval);

The code neatly prints "42" but not before this lengthy warning:

[To redirect Truffle log output to a file use one of the following options:
* '--log.file=<path>' if the option is passed using a guest language launcher.
* '-Dpolyglot.log.file=<path>' if the option is passed using the host Java launcher.
* Configure logging using the polyglot embedding API.]
[engine] WARNING: The polyglot context is using an implementation that does not support runtime compilation.
The guest application code will therefore be executed in interpreted mode only.
Execution only in interpreted mode will strongly impact the guest application performance.
For more information on using GraalVM see https://www.graalvm.org/java/quickstart/.
To disable this warning the '--engine.WarnInterpreterOnly=false' option or use the '-Dpolyglot.engine.WarnInterpreterOnly=false' system property.

I have tried to follow the printed suggestion:

    ScriptEngine engine = GraalJSScriptEngine.create(null, Context.newBuilder("js")
        .option("engine.WarnInterpreterOnly","false")
    );

Unfortunately this fails with an exception:

Exception in thread "main" java.lang.IllegalArgumentException: Option engine.WarnInterpreterOnly is an engine option. Engine level options can only be configured for contexts without a shared engine set. To resolve this, configure the option when creating the Engine or create a context without a shared engine.
    at com.oracle.truffle.polyglot.PolyglotEngineException.illegalArgument(PolyglotEngineException.java:131)
    at com.oracle.truffle.polyglot.PolyglotContextConfig.findObjectForContextOption(PolyglotContextConfig.java:433)
    at com.oracle.truffle.polyglot.PolyglotContextConfig.<init>(PolyglotContextConfig.java:260)
    at com.oracle.truffle.polyglot.PolyglotEngineImpl.createContext(PolyglotEngineImpl.java:1697)
    at com.oracle.truffle.polyglot.PolyglotEngineDispatch.createContext(PolyglotEngineDispatch.java:159)
    at org.graalvm.polyglot.Context$Builder.build(Context.java:1837)
    at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.createDefaultContext(GraalJSScriptEngine.java:353)
    at com.oracle.truffle.js.scriptengine.GraalJSBindings.initContext(GraalJSBindings.java:91)
    at com.oracle.truffle.js.scriptengine.GraalJSBindings.requireContext(GraalJSBindings.java:86)
    at com.oracle.truffle.js.scriptengine.GraalJSBindings.entrySet(GraalJSBindings.java:172)
    at java.base/java.util.AbstractMap.containsKey(AbstractMap.java:144)
    at java.scripting/javax.script.SimpleScriptContext.getAttribute(SimpleScriptContext.java:158)
    at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.createSource(GraalJSScriptEngine.java:450)
    at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.compile(GraalJSScriptEngine.java:628)
    at Scratch.main(scratch_1.java:17)

Process finished with exit code 1

I'm unsure what this exception means and how to solve it. It would be impractical to add a VM option, and the documentation promises it should be possible to pass on options like this: https://www.graalvm.org/22.0/reference-manual/polyglot-programming/#passing-options-programmatically

The log option doesn't work at all:

    ScriptEngine engine = GraalJSScriptEngine.create(null, Context.newBuilder("js")
        .option("log.file","log\\engine.log")
    );

results in

Exception in thread "main" java.lang.IllegalArgumentException: log.file
at com.oracle.truffle.polyglot.PolyglotEngineException.illegalArgument(PolyglotEngineException.java:131)
at com.oracle.truffle.polyglot.PolyglotEngineImpl.parseLoggerName(PolyglotEngineImpl.java:776)
at com.oracle.truffle.polyglot.PolyglotContextConfig.<init>(PolyglotContextConfig.java:257)
at com.oracle.truffle.polyglot.PolyglotEngineImpl.createContext(PolyglotEngineImpl.java:1697)
at com.oracle.truffle.polyglot.PolyglotEngineDispatch.createContext(PolyglotEngineDispatch.java:159)
at org.graalvm.polyglot.Context$Builder.build(Context.java:1837)
at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.createDefaultContext(GraalJSScriptEngine.java:353)
at com.oracle.truffle.js.scriptengine.GraalJSBindings.initContext(GraalJSBindings.java:91)
at com.oracle.truffle.js.scriptengine.GraalJSBindings.requireContext(GraalJSBindings.java:86)
at com.oracle.truffle.js.scriptengine.GraalJSBindings.entrySet(GraalJSBindings.java:172)
at java.base/java.util.AbstractMap.containsKey(AbstractMap.java:144)
at java.scripting/javax.script.SimpleScriptContext.getAttribute(SimpleScriptContext.java:158)
at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.createSource(GraalJSScriptEngine.java:450)
at com.oracle.truffle.js.scriptengine.GraalJSScriptEngine.compile(GraalJSScriptEngine.java:628)
at Scratch.main(scratch_1.java:17)

Dependencies:

    <!-- https://mvnrepository.com/artifact/org.graalvm.sdk/graal-sdk -->
    <dependency>
        <groupId>org.graalvm.sdk</groupId>
        <artifactId>graal-sdk</artifactId>
        <version>22.2.0</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.graalvm.js/js -->
    <dependency>
        <groupId>org.graalvm.js</groupId>
        <artifactId>js</artifactId>
        <version>22.2.0</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.graalvm.js/js-scriptengine -->
    <dependency>
        <groupId>org.graalvm.js</groupId>
        <artifactId>js-scriptengine</artifactId>
        <version>22.2.0</version>
    </dependency>

Any suggestions?

2 Answers2

3

You can use context eval instead of ScriptEngine which makes it slower performance wise check here. Code below is working for me without the warning.

    public static void main(String[] args) {
        Engine engine = Engine.newBuilder()
                .option("engine.WarnInterpreterOnly", "false")
                .build();
        Context ctx = Context.newBuilder("js").engine(engine).build();

        String script = "6 * 7";

        Object eval = ctx.eval("js", script);
        System.out.println(eval);
    }

output looks like below in my intellij graalvm js engine output from intellij

ozkanpakdil
  • 3,199
  • 31
  • 48
  • 1
    That's interesting. At least, the error is clearer to me now. I had no idea the engine could be created before the context and then attached later. Looks like we're doing the reverse: create a context and then ask it for an engine. I'm unsure if there's a benefit to doing the one or the other. In any case it looks like we can't reuse contexts as we want all script executions to be independent from each other (no variable reuse and so on). But the error is gone now so thanks! – colouredmirrorball Aug 29 '22 at 13:23
  • 1
    Please consider editing this post as it does not actually resolve the underlying issue and simply suppresses the warning with `.option("engine.WarnInterpreterOnly", "false")` – Jordan LaPrise Jan 18 '23 at 02:27
  • Questions is "how to get rid of these warnings?" and [Bruce`s answer](https://stackoverflow.com/a/75023914/175554) tells it is same performance. – ozkanpakdil Jan 18 '23 at 07:48
2

You need to set the option on the engine, not the context (builder).

So

GraalJSScriptEngine.create(
        Engine.newBuilder()
              .option("engine.WarnInterpreterOnly", "false")
              .build(),
        Context.newBuilder("js")
               .allowIO(false)
               .option(JSContextOptions.ECMASCRIPT_VERSION_NAME, "2022")
)

The only difference between ozkanpakdil's answer is hat the above code use the API of the ScriptEngine (javax.script), while in the other answer using graalvm's Context (org.graalvm.polyglot) will give you a pure graalvm API.

There's some differences e.g in the eval method one requires a Reader as it doesn't know graalvm's Source, while the other can use this object directly. Or one allows you to pass Writers for the engine to write output, while graalvm uses OutputStreams.

That said the performance is the same as in both case the source will run in interpreted mode (hence the warning).


If the JS code needs to have good performance then it might be required to either switch to GraalVM distribution, or to modify the java command line to switch the compiler to to Graal (requires to enable Compiler Interface via -XX:+EnableJVMCI -XX:+UseJVMCICompiler and modify the module path with the jars of graal and truffle in particular).

bric3
  • 40,072
  • 9
  • 91
  • 111