Skip to main content

GraalWasm Integration

This integration enables the execution of WebAssembly binaries using the Emscripten and WASI Preview 1 APIs on the JVM through GraalWasm.

Requirements

  • JVM JDK 22+ (it may also work on JDK21+)
  • GraalVM SDK Polyglot API 24.0.X

The current implementation heavily relies on internal GraalWasm APIs, making it compatible only with the GraalVM SDK Polyglot API 24.0.X for JDK22.

Installation

Add the required dependencies:

dependencies {
implementation("at.released.weh:bindings-graalvm240:0.1-alpha01")
implementation("org.graalvm.polyglot:polyglot:24.0.2")
implementation("org.graalvm.polyglot:wasm:24.0.2")
}

Usage

Below is an example demonstrating the execution of helloworld.wasm, prepared in the "Emscripten Example".

import at.released.weh.bindings.graalvm240.GraalvmHostFunctionInstaller
import org.graalvm.polyglot.Context
import org.graalvm.polyglot.Source

private object App

const val HELLO_WORLD_MODULE_NAME: String = "helloworld"

fun main() {
// Prepare source
val source = Source.newBuilder("wasm", App::class.java.getResource("helloworld.wasm"))
.name(HELLO_WORLD_MODULE_NAME)
.build()

// Setup Polyglot Context
val context: Context = Context.newBuilder().build()
context.use {
// Context must be initialized before installing modules
context.initialize("wasm")

// Setup modules
val installer = GraalvmHostFunctionInstaller(context)
installer.setupWasiPreview1Module()
val emscriptenInstaller = installer.setupEmscriptenFunctions()

// Evaluate the WebAssembly module
context.eval(source)

// Finish initialization after module instantiation
emscriptenInstaller.finalize(HELLO_WORLD_MODULE_NAME).use { emscriptenEnv ->
// Initialize Emscripten runtime environment
emscriptenEnv.emscriptenRuntime.initMainThread()

// Execute code
run(context)
}
}
}

private fun run(
context: Context,
) {
val main = context.getBindings("wasm").getMember(HELLO_WORLD_MODULE_NAME).getMember("main")
main.execute(/* argc */ 0, /* argv */ 0).asInt()
}

You can also check out the example in the repository:

GraalVM's Built-in Emscripten Functions

It is worth noting that GraalWasm provides its own implementation of the Emscripten JS and WASI Preview 1 interfaces.
This implementation varies in the set of implemented functions and the version of Emscripten. In many cases, it may be a more suitable choice rather than this library.

To use it, you should add the wasm.Builtins option with the value emscripten,wasi_snapshot_preview1.

Below is an example of running helloworld.wasm using the built-in implementation:

import org.graalvm.polyglot.Context
import org.graalvm.polyglot.Source

const val HELLO_WORLD_MODULE_NAME: String = "helloworld"

fun main() {
val source = Source.newBuilder("wasm", App::class.java.getResource("helloworld.wasm"))
.name(HELLO_WORLD_MODULE_NAME)
.build()
val context: Context = Context.newBuilder()
.option("wasm.Builtins", "emscripten,wasi_snapshot_preview1")
.build()
context.use {
context.eval(source)
run(context)
}
}

See also example in documentation: GraalVM: Running WebAssembly Embedded in Java

Runtime optimizations

By default, GraalVM executes code in interpreter mode, which can be slow, However, it offers runtime optimizations to improve performance. For more details, check this link: GraalVM: Enable Optimization on OpenJDK and Oracle JDK.

You can use Gradle toolchains to run your application on the GraalVM JVM with optimizations enabled:

java {
toolchain {
languageVersion = JavaLanguageVersion.of(22)
vendor = JvmVendorSpec.GRAAL_VM
}
}

If you need to run optimized code on OpenJDK or other non-GraalVM JDKs, you'll need to activate JVM Compiler Interface (JVMCI) using the -XX:+EnableJVMCI option and add the GraalVM compiler to the --upgrade-module-path classpath.

This can be tricky to set up with Gradle. Additionally, the GraalVM version used in the project requires JDK 22 or later to run GraalVM Compiler. For an example of how to enable the GraalVM compiler, take a look at this gist.

Other optimizations

To speed up initialization, you can reuse a single instance of the GraalVM Engine across multiple instances of Context. Check this link for more information: GraalVM: Managing the Code Cache