Has anyone tried using QuickJS from Scala?

QuickJS is a small and embeddable JavaScript engine which supports the ES2023 specification.

I want to use it in Scala, for example, to create an interpreter and to execute some JavaScript code with it. I’m curious if anyone has tried this before? If not, how can I create bindings and make it into a Scala package?

1 Like

Question number one: from where? I suspect (not knowing QuickJS aside from a quick look at the repo) that embedding it in ScalaJVM is a somewhat different project from doing so in ScalaNative, and that sort of depends on what you want to do with it.

2 Likes

Thank you for your reply. It is a program written purely in C with no external dependencies. I would like to call it in Scala. For example,

def run(script: String): String =
  val runtime = new JSRuntime()
  val context = new JSContext(runtime)
  val result = context.eval(script)
  ...

Those JSRuntime and JSContext are external symbols from QuickJS.

Right, but the question remains, what kind of Scala you want to call it?
JVM Scala? If so, you need to look into JVM interop with native packages, something like JNI.

Or are you thinking in making your own native wrapper on top of it using ScalaNative? If so, I guess it should not differ from calling any other native library there.

1 Like

If you want to target Scala Native, you can look into Scala Native binding generator. Then you can call them after linking the bindings. Probably best to ask in Discord on Scala Native channel.

2 Likes

Could be of interest. Disclaimer : I haven’t used it in anger.

2 Likes

Just to clarify this further, since it sometimes isn’t obvious to folks coming from other languages:

Scala is, intentionally, Just A Language. It specifically isn’t a full platform the way that something like JavaScript or Python (basically) are.

That’s why the question is ambiguous: Scala runs in a bunch of different places (in the JVM, natively on machines, or in the browser), and the answer is different depending on how you want to run it.

4 Likes

Thanks everyone for the information. I was single-minded and had thought there’s only one Scala which JVM based.

I want to use QuickJS in Scala with JVM target So, it’s actually a matter of interfacing C libraries with JVM. I should look into JNI, right?

A quick check - you want to embed a JavaScript engine in a Scala application; so you want to run bits of JavaScript in it. Are you sure you want to run the application on the JVM?

I ask because if this is a new application with no constraints on it, perhaps you could use the JavaScript target toolchain for Scala and then call your bits of JavaScript directly.

If you really want to (or have to ) run your application on the JVM, then yes, JNI is for you….

…. however might want to try using SWIG (https://www.swig.org) to generate a Java API (it uses JNI under the hood). I’ve done this before to access C++ from Python. If it’s just a couple of C functions taking char * and int, then straight JNI is probably good enough, though.

Oh, I forgot - what about using Nashorn as an embedded JavaScript engine directly called from Scala? I’ll leave it to the JavaScript folks to opine on its suitability (my JavaScript experience is rudimentary).

2 Likes

It’s probable that someone already created a java wrapper around QuickJS with JNI. If that works you could just use that from scala instead of creating your own.

2 Likes

Doesn’t GraalVM support multiple languages?

1 Like

Yes. I want to do that. We’re developing a compiler which transpiles our language to JavaScript. We need to ensure that the generated code is syntactically correct and can run to achieve the expected results.

Our current approach is to spawn a Node.js process with REPL and send JavaScript code to its stdin and read results from its stdout. This solution can work properly at the moment, but our code interacting with the REPL is a bit dirty. The reason why we need REPL is that our generated code is executed block by block, so there’s no way to generate a complete JS file at once and then execute it with Node.js. Calling the JavaScript runtime directly from API is easier.

What’s more, Node.js is relatively cumbersome (it has too many built-in modules and the test requires specific versions). So I’m exploring using a relatively smaller JavaScript interpreter (and runtime) like QuickJS into our tests.

Thank you for your suggestion. I’m looking into SWIG and Nashorn.

Thanks. I’m checking some packages from Maven.

Cool. I found this documentation: GraalVM JavaScript and Node.js Runtime One of its examples looks like what I need.

import org.graalvm.polyglot.*;
import org.graalvm.polyglot.proxy.*;

public class HelloPolyglot {

    static String JS_CODE = "(function myFun(param){console.log('hello '+param);})";

    public static void main(String[] args) {
        System.out.println("Hello Java!");
        try (Context context = Context.create()) {
            Value value = context.eval("js", JS_CODE);
            value.execute(args[0]);
        }
    }
}

But the downside is that we need to require everyone to use GraalVM.

Uhm I have zero experience with this, so this my be dumb, a bad idea, or plain wrong.

But can’t you generate a native executable on Graal that contains Graal itself.
Making your app self contained and not needing your users to install even Java.

I also have recently been looking to do the same thing as the original poster, and after much exploration have settled on the GraalVM approach. I am in early days here though, but the support seems substantive.

If your target is tooling then Graal Native Image is a good approach and is the path I am taking with a similar need.
We also have use case that requires embedding in JVM services and I’ve asked on the Graal slack channel and seen in their docs that they Graal polyglot support is a normal JVM library. In order to get the most out of this workload you want to have the Graal compiler performing JIT, but if you don’t, it should still work but be less performant.
Run GraalVM JavaScript on a Stock JDK

You can see a thread where I ask about this here: Slack

1 Like