Skip to main content

Chicory Integration

Chicory is a zero-dependency, pure Java WebAssembly runtime.

Key Features:

  • Compatible with Android API 33+ and JVM JDK 17+.
  • Simple JVM-only runtime with minimal dependencies.
  • Supports single-threaded execution only.

This integration allows you to run WebAssembly binaries using the Emscripten and WASI Preview 1 APIs on the JVM with Chicory.

The runtime is actively developed, and its public interfaces are subject to frequent changes.
Our integration is compatible with version 0.0.12 of Chicory.

Installation

Add the required dependencies:

dependencies {
implementation("at.released.weh:bindings-chicory:0.1-alpha01")
implementation("com.dylibso.chicory:runtime:0.0.12")
}

Usage

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

import at.released.weh.bindings.chicory.ChicoryHostFunctionInstaller
import at.released.weh.bindings.chicory.ChicoryHostFunctionInstaller.ChicoryEmscriptenInstaller
import com.dylibso.chicory.runtime.HostFunction
import com.dylibso.chicory.runtime.HostGlobal
import com.dylibso.chicory.runtime.HostImports
import com.dylibso.chicory.runtime.HostMemory
import com.dylibso.chicory.runtime.HostTable
import com.dylibso.chicory.runtime.Memory
import com.dylibso.chicory.runtime.Module
import com.dylibso.chicory.wasm.types.MemoryLimits
import com.dylibso.chicory.wasm.types.Value

// You can use `wasm-objdump -x helloworld.wasm -j Memory` to get the memory limits
// declared in the WebAssembly binary.
const val INITIAL_MEMORY_SIZE_PAGES = 258

fun main() {
// Prepare Host memory
val memory = HostMemory(
/* moduleName = */ "env",
/* fieldName = */ "memory",
/* memory = */
Memory(
MemoryLimits(INITIAL_MEMORY_SIZE_PAGES),
),
)

// Prepare WASI and Emscripten host imports
val installer = ChicoryHostFunctionInstaller(
memory = memory.memory(),
)
val wasiFunctions: List<HostFunction> = installer.setupWasiPreview1HostFunctions()
val emscriptenInstaller: ChicoryEmscriptenInstaller = installer.setupEmscriptenFunctions()
val hostImports = HostImports(
/* functions = */ (emscriptenInstaller.emscriptenFunctions + wasiFunctions).toTypedArray(),
/* globals = */ arrayOf<HostGlobal>(),
/* memory = */ memory,
/* tables = */ arrayOf<HostTable>(),
)

// Setup Chicory Module
val module = Module
.builder("helloworld.wasm")
.withHostImports(hostImports)
.withInitialize(true)
.withStart(false)
.build()

// Instantiate the WebAssembly module
val instance = module.instantiate()

// Finalize initialization after module instantiation
val emscriptenRuntime = emscriptenInstaller.finalize(instance)

// Initialize Emscripten runtime environment
emscriptenRuntime.initMainThread()

// Execute code
instance.export("main").apply(
/* argc */ Value.i32(0),
/* argv */ Value.i32(0),
)[0].asInt()
}

You can also check out the example in the repository:

Internal WASI Preview 1 implementation

Chicory includes its own implementation of the WASI Preview 1 interfaces.
You can find documentation on how to use it here: chicory/wasi.

Runtime Optimizations

Chicory is also working on an Ahead-of-Time (AOT) compiler that translates WebAssembly into JVM code. To experiment with this feature, you can add the following dependency:

implementation("com.dylibso.chicory:aot:0.0.12")

And add Engine when building Module:

import com.dylibso.chicory.aot.AotMachine

val module = Module
.builder("helloworld.wasm")
.withHostImports(hostImports)
.withInitialize(true)
.withStart(false)
.withMachineFactory(::AotMachine)
.build()

For the latest updates, check this link: chicory/aot.