GraalVM is the new Rhino/Nashorn?

Bridging Java and JavaScript with GraalVM: One Language to Rule Them All

In a world where developers constantly juggle between languages, frameworks, and runtimes, GraalVM offers something refreshingly powerful: a single environment where you can seamlessly integrate Java and JavaScript — and even other languages like Python or Ruby — all in the same runtime.

Too Many Languages, Too Little Time

Every developer has faced this dilemma: you start learning JavaScript because it's accessible, but soon you're working on a project that requires Java for performance, or because of legacy code. You spend time switching mental models, rewriting logic in another syntax, and rebuilding interfaces that already exist elsewhere.

Now imagine if you could just call your Java class from JavaScript — no wrappers, no hacks. Just Java.type('com.example.MyClass'). That’s the promise of GraalVM.

Why GraalVM Changes Everything

  • Multithreading with JavaScript: JavaScript (in Node.js) isn’t naturally multi-threaded. But GraalVM allows you to call Java’s native multithreaded classes from JS. Need parallel processing? Just use java.util.concurrent.
  • Access to JavaScript libraries from Java: Want to use an npm package to process something client-side and call a Java backend from the same runtime? GraalVM makes that seamless.
  • Integrated with Node.js: You don’t need to give up your favorite tools. GraalVM comes with full Node.js support, so you can run fs, path, or http as usual — and still call Java classes whenever needed.

Real-World Example: Mixing Java Threads in JS

const Thread = Java.type('java.lang.Thread');

let t = new Thread(() => {
  console.log("Running Java thread from JavaScript!");
});
t.start();
t.join();

Yes — this is actual JavaScript calling and managing a Java thread. Your console.log still works, and you can mix both ecosystems.

Developer Tools and VSCode Integration

One of the most underrated aspects of GraalVM is how easily you can customize your toolchain. Whether you prefer working in Java or JS, you can configure your VSCode environment to give you real-time intellisense, error checking, and code navigation across both.

Don’t Choose. Combine.

GraalVM gives you the freedom to:

  • Write logic once and reuse it.
  • Blend the simplicity of JavaScript with the power of Java.
  • Build apps faster without sacrificing performance or flexibility.

Examples

Let's take a look at some tests and usage, shall we?

GraalVM JavaScript Interop Tests

1. Dynamic Class Creation

Summary

This test demonstrates how JavaScript can dynamically extend an abstract Java class using Java.extend and how Java can reflectively invoke those implementations.

Example Code

// JavaScript (executed via GraalVM)
var DynamicClass = Java.extend(BaseJavaClass, {
  getIntValue: function() { return 42; },
  getStringValue: function() { return "Hello, GraalVM!"; },
  isBooleanValue: function() { return true; },
  computeSum: function(a, b) { return a + b; }
});
var dynamicInstance = new DynamicClass();
globalDynamicInstance = dynamicInstance;

Expected Output

Dynamic class created and methods executed successfully!
Assertions passed:
- getIntValue = 42
- getStringValue = "Hello, GraalVM!"
- isBooleanValue = true
- computeSum(5, 10) = 15

2. Fetching Fields and Methods

Summary

This test shows how JavaScript can inspect a Java class (e.g., EventGenerator) to fetch fields, methods, and values using Java reflection through GraalVM.

Example Code

// JavaScript (executed via GraalVM)
let fields = javaClass.getDeclaredFields();
fields.forEach(field => {
  field.setAccessible(true);
  try {
    let value = field.get(javaInstance);
    console.log(field.getName() + " = " + value);
  } catch (error) {
    console.log(field.getName() + " = [INACCESSIBLE]");
  }
});
let methods = javaClass.getDeclaredMethods();
methods.forEach(method => console.log(method.getName()));

Expected Output

Fetching Java fields, methods, and initial values from EventGenerator...
Fields:
- serialVersionUID = ...
- UniqueInstance = ...
- abstractModel = ...
- messageCache = ...
- stackOfEnclosingEntities = ...
Methods:
- getName
- reset
- getInstance
- close
- open
...

3. Multi-threading in JavaScript with GraalVM

Summary

These tests cover how multiple GraalVM JS contexts can be evaluated concurrently, and the differences in behavior when sharing a context with and without proper synchronization.

Example Code (Two Contexts)

const jsonCode = "(function(x,y) { return JSON.stringify({x:x,y:y}); })";
let json1 = cx1.eval("js", jsonCode);
let json2 = cx2.eval("js", jsonCode);

Expected Output

Both threads encode JSON without error.
Output: {"x":42,"y":43}

Example Code (Same Context Without Enter)

json.execute(42, 43);

Expected Output

IllegalStateException occurs from concurrent access to shared context without synchronization.

Example Code (Same Context With Enter)

contextLock.lock();
cx.enter();
json.execute(42, 43);
cx.leave();
contextLock.unlock();

Expected Output

All iterations succeed. No exceptions thrown.

4. Running a JUnit Test Suite from JavaScript

Summary

This test validates that JavaScript (via GraalVM) can dynamically locate and execute a full JUnit test suite from an external compiled Java class.

Example Code

// JavaScript (executed via GraalVM)
var testSuiteClass = Java.type("cpl.test.TestCPL");
var testSuite = testSuiteClass.suite();
var result = TestRunner.run(testSuite);
console.log("Total Tests: " + result.runCount());

Expected Output

External test suite ran successfully!
Total Tests: > 1