Evaluating JavaScript in Java with GraalVM

javascript in java graalvm

If you’re evaluating JavaScript in Java projects, you may have started to worry after the latest announcement. According to JEP-335 the Nashorn JavaScript engine might soon be deprecated. One of the motivations for this move is creation of GraalJS that provides the same functionality and better performance.

What is GraalVM?

I’m just going to cite the official GraalVM website:

GraalVM is a universal virtual machine for running applications written in JavaScript, Python 3, Ruby, R, JVM-based languages like Java, Scala, Kotlin, and LLVM-based languages such as C and C++.

GraalVM removes the isolation between programming languages and enables interoperability in a shared runtime. It can run either standalone or in the context of OpenJDK, Node.js, Oracle Database, or MySQL.

I think it explains the concept pretty well.

If you want to use it, you have to download GraalVM on your computer. For now, it’s available only for Linux and macOS. The Windows version is under development. There are two options available: Community Edition and Enterprise Edition. The first one is open-source and free but it can’t be used on macOS, the latter is free for non-production use – otherwise sales team should be contacted. The official GraalVM download page.

Before we move to the implementation, I just wanted to note that you have nothing to worry about – the code looks similar to the one built on Nashorn engine.

Running JS code in command line

If you’ve succesfully downloaded and extracted the archive, change your environment variable globally or just export path to the VM in your terminal instance.

After that, you can just type jsin the command line and write your usual JavaScript code:

Evaluating JavaScript in Java

Now, this is where the fun begins. By the way, at the end of this post you can find a link to the GitHub repository containing the complete codebase.

Let’s setup an ordinary Maven project and put the needed dependency in pom.xml:

<dependencies>
    <dependency>
        <groupId>org.graalvm</groupId>
        <artifactId>graal-sdk</artifactId>
        <version>1.0.0-rc2</version>
    </dependency>
</dependencies>

And create a new class called “Application” with the following code:

class Application {

    public static void main(String[] args) {
        Context jsContext = Context.create("js");
        runScript("console.log('Hello from the project')", jsContext);
    }

    private static Value runScript(String script, Context context) {
        return context.eval("js", script);
    }
}

In your IDE set the runtime environment to GraalVM and start the main method. If you prefer CLI, then temporarily change JAVA_HOME and execute Maven commands:

export JAVA_HOME="/home/adrian_marszalek/graalvm"
mvn install -q && mvn exec:java -Dexec.mainClass="net.amarszalek.javapolyglot.Application" -q

maven javascript java graalvm

As you can see, it works!

Exchanging data with the script

You can pass the data to GraalJS by putting members in a Bindings object:

String script = "console.log('I will welcome you ' + welcomeCount + ' times.');" +
               "for(var i=0; i<welcomeCount; i++){ console.log('Welcome') }";
Value bindings = jsContext.getBindings("js");
bindings.putMember("welcomeCount", 3);
runScript(script, polyglot);

The code above will print “I will welcome you 3 times. Welcome Welcome Welcome”

You can do it the other way around too. Accessing scipts’ values from Java is pretty easy:

runScript("var x = 'variable';", jsContext);
String jsVariable = jsContext.getBindings("js").getMember("x").asString();

Using Java objects inside scripts

GraalVM allows accessing host’s objects from inside the container. Every method and field you want to access has to be public.

Let’s create a new Phone class:

public class Phone {

    public final int number;

    public Phone(int number) {
        this.number = number;
    }

    public void call(String name) {
        System.out.println("Calling...: " + name);
    }

}

And slightly modify the main method:

String printNumberScript = "console.log(phone.number)";
String callingScript = "phone.call('Someone')";
Phone phone = new Phone(123456);

Context context = Context.create("js");
context.getBindings("js").putMember("phone", phone);
runScript(printNumberScript, context);
runScript(callingScript, context);

java objects from javascript graal

 

 

Nothing surprising here 🙂

Summary

As you can see, evaluating snippets of JavaScript in Java is fairly easy nowadays. In all likelihood, the polyglot feature included in GraalVM will be the successor to the Project Nashorn. Right now running Java projects on the specific runtime environment may feel like too big limitation to use in production but maybe some time in the future we’ll get dependencies that will work independently.

The GitHub repository is available here.
If you feel like it, you can stay in touch with me on Twitter: @a_mrszlk.

3 thoughts on “Evaluating JavaScript in Java with GraalVM

  1. Jeff Friesen Reply

    Do you know if you can compile a GraalVM app and then run it from a browser?

  2. Shridhar V Reply

    How can we ensure that the javascript scripts are not being recompiled each time they are invoked ? Is there a way to ensure that a script is compiled only once ?

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.