Search code examples
javaintellij-ideajargraalvmgraaljs

GraalJS - Cannot build ScriptEngine from jar


I'm stuck with a problem involving GraalJS. I'm trying to use it as my JavaScript backend. It works flawlessly while I run in from IntelliJ, but as soon as I export a runnable .jar, it stops working. I've been debugging this issue for a few days now, below are my findings.

Should be mentioned that I generate my runnable .jar by making use of a gradle command that comes packaged with libGdx called desktop:dist.

First, these are the dependencies that I use

// GraalVM
    compile group: 'org.graalvm.js', name: 'js', version: '20.2.0'
    compile group: 'org.graalvm.js', name: 'js-scriptengine', version: '20.2.0'
    compile group: 'org.graalvm.sdk', name: 'graal-sdk', version: '20.2.0'
    compile group: 'org.graalvm.truffle', name: 'truffle-api', version: '20.2.0'
    implementation 'com.ibm.icu:icu4j:51.1'

This is how I build my ScriptEngine

private fun getGraalEngine() = GraalJSScriptEngine.create(
    Engine.newBuilder()
        .allowExperimentalOptions(false)
        .useSystemProperties(false)
        .build(),
    Context.newBuilder("js")
        .allowHostAccess(HostAccess.ALL)
        .allowHostClassLookup { true }
        .allowAllAccess(true))

Now, when I run the program from the .jar, I get this exception:

java.lang.IllegalArgumentException: Could not find option with name js.script-engine-global-scope-import.
    at com.oracle.truffle.polyglot.PolyglotEngineException.illegalArgument(PolyglotEngineException.java:128)
    at com.oracle.truffle.polyglot.OptionValuesImpl.failNotFound(OptionValuesImpl.java:283)
    at com.oracle.truffle.polyglot.PolyglotContextConfig.findLanguageForOption(PolyglotContextConfig.java:239)
    at com.oracle.truffle.polyglot.PolyglotContextConfig.<init>(PolyglotContextConfig.java:129)
    at com.oracle.truffle.polyglot.PolyglotEngineImpl.createContext(PolyglotEngineImpl.java:1434)
    at org.graalvm.polyglot.Context$Builder.build(Context.java:1598)

After some debugging, I came to the conlusion that this happens when PolyglotContextConfig tries to find a PolyglotLanguage for the optionName js. Turns out that this is the difference between IntelliJ and running from a runnable .jar. IntelliJ contains the support for JavaScript PolyglotLanguage, while the .jar does not. Here's what I found

By looking at Engine -> impl -> idToLanguage (I accessed these package private fields via reflection) I get the following results

Intellij :

id to lang: {regex=PolyglotLanguage [id=regex, name=REGEX, host=false], js=PolyglotLanguage [id=js, name=JavaScript, host=false]}

Runnable .jar:

id to lang: {regex=PolyglotLanguage [id=regex, name=REGEX, host=false]}

Turns out the support for JavaScript just... misses? Because of this, when PolyglotContextConfig.findLanguageForOption() tries to match the js optionName to the JavaScript PolyglotLanguage, the exception occurs.

I'm not sure how to proceed or why this could be happening. Perhaps something regarding classloaders? Either way, any help would be greately appreciated


Solution

  • The following link answered my question: https://github.com/oracle/graaljs/issues/125

    Turns out that during the .jar packaging process, the PolyglotLanguage entries for REGEX and Javascript get overwritten somehow. This means that you have to create a file inside your .jar at META-INF/truffle/language and add the contents from the answer. Note, for me the contents from the github link didn't quite work. I needed to change them to

    #https://github.com/graalvm/graaljs/issues/125
    
    language2.characterMimeType.0=application/javascript
    language2.characterMimeType.1=application/javascript+module
    language2.characterMimeType.2=text/javascript
    language2.className=com.oracle.truffle.js.lang.JavaScriptLanguage
    language2.defaultMimeType=application/javascript
    language2.dependentLanguage.0=regex
    language2.fileTypeDetector0=com.oracle.truffle.js.lang.JSFileTypeDetector
    language2.id=js
    language2.implementationName=GraalVM JavaScript
    language2.interactive=true
    language2.internal=false
    language2.name=JavaScript
    language2.version=inherit