When you install GraalVM, one of the things you get is a Node runtime environment (GraalVM 19.2.1 is based on Node 10.16.3 – with support for the core Node libraries and un understanding of NPM modules – and has a JavaScript engine that is ECAMScript 2019 compliant). Instead of V8, the usual JavaScript execution engine, this GraalVM environment leverages GraalJS and the JVM as execution platform. GraalJS runs Java Byte code on JVM. This GraalJS engine is a Java application that works on any Java 8+ implementation. Compared to V8, it can run faster and is better scalable to big memory structures. However: it may need some warmup time to reach peak performance: run time optimizations that can make Java applications run faster after some time now also apply to JavaScript execution.
GraalVM is among other things a polyglot language runtime. That means for JavaScript applications running on GraalVM that they can embed and call out to code written in JVM languages like Java, Scala, Groovy and Kotlin as well as non-JVM languages such as Python, R, Ruby and LLVM.
In this article, I will take a brief look at running Node applications on GraalVM and specifically their polyglot interaction on GraalVM with other languages, specifically Java, Python and R (only scratching the surface).
Polyglot Node Application calling out to Java
The sample application under scrutiny is a Node application that makes use of a Java Class provided in a JAR file. The Java Class – Joker – returns jokes from a getJoke() method or as String[] from a getJokes() method.
The Java Class can be tried out:
java -cp application-bundle.jar nl.amis.js2java.Joker
The Node application is implemented in Joker2.js. It is very straightforward. The Java Class is loaded with the Java.type call. Subsequently, a single Joker object is instantiated with the new statement. In function getJoke(), a call is made to the getJoke() method on javaJoker1 – the Java method that returns a random element from the jokes collection.
The application can be run with this command line:
node –jvm –vm.cp application-bundle.jar joker2.js
This is visualized as follows:
The JVM with GraalVM is the runtime environment where the Java Byte code is executed and optimized. The JavaScript code is interpreted by GraalJS and turned into an AST (abstract syntax tree) representation in Java Byte code. Because the JVM is the runtime engine, and the –jvm switch is passed, all Java 8 APIs are available at runtime and can be engaged from the JavaScript code. With the –vm.cp switch, the classpath is defined to indicate where at runtime additional (custom and 3rd party) Java Classes should be looked for. In this case, JAR file application-bundle.jar is passed in. Finally the Node application’s entry file is provided as well (joker2.js).
The program flow at runtime can be visualized as follows:
Polyglot Node Application embedding Python, Ruby and R
A Node application running on GraalVM can evaluate embedded, inline code snippets and also load sources from file. These code snippets and source can be defined in all languages that through Truffle parsing can run on GraalVM; these include Ruby, R, Python and LLVM languages (such as C, C++, Fortran, Rest and Swift). The node application should be run with the –polyglot command line switch, to engage the polyglot interoperability features.
The next file is a JavaScript application that can be run with the node or the js runtime. It does not use anything Node specific. The application – polyglot.js – evaluates code snippets in three languages, using the Polyglot object’s eval function. It also loads and evaluates a separate Python library library.py.
The most interesting parts to me are the definition of function Fibonacci: the function is defined in the library.py (def Fibonacci…) – loaded in line 11 – but it takes the evaluation and assignment to fibFunc – line 12 -to make the function executable from JavaScript. It is executed in line 13. That is JavaScript calling a recursive function written in Python and loaded from an external file. In line 16, another Python function object is created using a lambda expression. In line 18, this function is executed. In line 20 – a function is defined in R. In line 23, JavaScript invokes a function that is actually an R based function. Pretty cool if you ask me.
The file is executed with this command
js –polyglot –jvm polyglot.js
The output:
Polyglot Node Application calling out to Java that calls out to JavaScript
In this earlier article I discussed how a Java application running on GraalVM can leverage JavaScript and even NPM JavaScript modules. I was then wondering: can I call a Java Object from JavaScript when that Java Object itself calls out to JavaScript? Does that take anything special? The answer: yes, I can. And no, it does not take anything special at all.
The Java Class that calls out to JavaScript is ValidateThroughNPMValidator.java:
The class is bundled in the same application-bundle.jar file we saw before, including the validator-bundled.js file that it loads at runtime.
The salient part of the Node application that leverages the Postal Code capabilities of the Java Class is shown below:
Of course here we do not see that real validation goodness is not actually coming from Java but from JavaScript. The GraalVM engine turns both Java and JavaScript to a byte code that is just executed, regardless of their origin.
Resources
GitHub Repository with sources for this article: https://github.com/AMIS-Services/jfall2019-graalvm
GraalVM Docs on Node / JavaScript https://www.graalvm.org/docs/reference-manual/languages/js/
GraalVM Docs – interoperability from JavaScript – https://www.graalvm.org/docs/reference-manual/languages/js/#interoperability
GraalVM Docs – Polyglot including JavaScript / Node as target and as starting language: https://www.graalvm.org/docs/reference-manual/polyglot/
GitHub Home of GraalJS – https://github.com/graalvm/graaljs
JavaDocs for GraalVM Polyglot – https://www.graalvm.org/truffle/javadoc/org/graalvm/polyglot/package-summary.html