API Reference

Core Compilation

WasmTarget.compileFunction
compile(f, arg_types; optimize=false) -> Vector{UInt8}

Compile a Julia function f with the given argument types to WebAssembly bytes. Returns a valid WebAssembly binary that can be instantiated and executed.

Set optimize=true for size-optimized output (default -Os like dart2wasm), optimize=:speed for -O3, or optimize=:debug for -O1 without --traps-never-happen.

source
WasmTarget.compile_multiFunction
compile_multi(functions; optimize=false) -> Vector{UInt8}

Compile multiple Julia functions into a single WebAssembly module.

Each element should be (function, argtypes) or (function, argtypes, name).

Set optimize=true for size-optimized output, optimize=:speed for -O3, or optimize=:debug for -O1 without --traps-never-happen.

Example

wasm_bytes = compile_multi([
    (add, (Int32, Int32)),
    (sub, (Int32, Int32)),
    (helper, (Int32,), "internal_helper"),
])

Functions can call each other within the module.

source
WasmTarget.compile_from_codeinfoFunction
compile_from_codeinfo(code_info, return_type, func_name, arg_types; optimize=false) -> Vector{UInt8}

Compile a pre-computed typed CodeInfo to WebAssembly bytes, bypassing Base.codetyped(). This is the entry point for the evaljulia pipeline where type inference has already been run.

Arguments

  • code_info::Core.CodeInfo: Typed CodeInfo (from Base.code_typed or equivalent)
  • return_type::Type: The inferred return type
  • func_name::String: Export name for the WASM function
  • arg_types::Tuple: Argument types for the function
  • optimize: Same as compile() — false, true, :speed, or :debug
source
WasmTarget.compile_with_baseFunction
compile_with_base(functions; base_wasm_path, optimize=false) -> Vector{UInt8}

Compile user functions and merge with a pre-compiled base.wasm module. User functions that would normally be compiled standalone are instead compiled into a separate user.wasm module, then merged with base.wasm via wasm-merge.

The merged module contains all base exports + user exports, and user code can call base functions directly.

Arguments

  • functions: Same format as compile_multi[(f, arg_types, name), ...]
  • base_wasm_path: Path to pre-compiled base.wasm (default: base.wasm in project root)
  • optimize: Same as compile() — false, true, :speed, or :debug

Returns

Merged Vector{UInt8} containing both base and user functions.

source
WasmTarget.optimizeFunction
optimize(bytes::Vector{UInt8}; level=:size, validate=true) -> Vector{UInt8}

Run Binaryen wasm-opt on compiled WebAssembly bytes for size and performance optimization. Uses dart2wasm's production WasmGC flags by default.

Keywords

  • level: Optimization level — :size (default, -Os like dart2wasm), :speed (-O3), or :debug (-O1, no --traps-never-happen)
  • validate: Run wasm-tools validate after optimization (default true)

Returns

Optimized Vector{UInt8}.

Throws

  • Error if wasm-opt is not found (with install instructions)
  • Error if optimization or validation fails
source

Types

WasmTarget.JSValueType
JSValue

A Julia type representing a JavaScript value held as an externref. Used for DOM elements, JS objects, and other JS values.

This is a primitive type to prevent Julia from optimizing it away.

source
WasmTarget.WasmGlobalType
WasmGlobal{T, IDX}

A handle to a WebAssembly global variable at index IDX. When compiled to Wasm:

  • global[] (getindex) → global.get IDX
  • global[] = x (setindex!) → global.set IDX, x

The index is a type parameter so it's known at compile time, which is required because Wasm's global.get and global.set instructions take immediate indices.

This is a general-purpose abstraction for any Julia code that needs to interact with Wasm global variables. Use cases include:

  • Stateful applications
  • Game engines
  • Reactive frameworks
  • Any code needing mutable Wasm state

Type Parameters

  • T: The type of value stored in the global (Int32, Float64, etc.)
  • IDX: The Wasm global index (0-based), must be an Int literal

Example

# Define types for specific globals (index is compile-time constant)
const Counter = WasmGlobal{Int32, 0}   # Global index 0
const Flag = WasmGlobal{Int32, 1}      # Global index 1

# Functions that use globals - index is known from the type
function increment(g::Counter)::Int32
    g[] = g[] + Int32(1)
    return g[]
end

function toggle(g::Flag)::Int32
    g[] = g[] == Int32(0) ? Int32(1) : Int32(0)
    return g[]
end

# Create instances (value is for Julia-side testing)
counter = Counter(0)
flag = Flag(1)

# Compile to Wasm - global index extracted from type
wasm_bytes = compile(increment, (Counter,))
source

Module Building

WasmTarget.add_import!Function
add_import!(mod, module_name, field_name, params, results) -> func_idx

Add an imported function to the module and return its function index. Imported functions come before local functions in the function index space.

source
WasmTarget.add_function!Function
add_function!(mod, params, results, locals, body) -> func_idx

Add a function to the module and return its index. Note: Local function indices start after imported functions. Params and results can be NumType or WasmValType vectors.

source
WasmTarget.add_export!Function
add_export!(mod, name, kind, idx)

Add an export entry to the module.

  • kind: 0=func, 1=table, 2=memory, 3=global
source
WasmTarget.add_global!Function
add_global!(mod, valtype, mutable, init_value) -> global_idx

Add a global variable to the module and return its index. The init_value should be a constant of the appropriate type.

source

Package Extensions

WasmTarget.register_package!Function
register_package!(name::Symbol, functions::Vector)

Register a package's functions for pre-compilation. Each entry is (function, argtypes) or (function, argtypes, export_name).

source
WasmTarget.compile_with_packagesFunction
compile_with_packages(user_functions::Vector, packages::Vector{Symbol}; optimize=false)
    -> Vector{UInt8}

Compile user functions alongside pre-registered package functions in a single module. Package functions are included alongside user functions so cross-calls resolve correctly.

source

Caching

WasmTarget.compile_cachedFunction
compile_cached(f, arg_types::Tuple; optimize=false) -> Vector{UInt8}

Like compile() but uses the global cache. Enable with enable_cache!().

source
WasmTarget.enable_cache!Function
enable_cache!(; max_entries=256)

Enable the global compilation cache. Subsequent calls to compile() and compile_multi() will use the cache.

source

Source Maps

WasmTarget.compile_with_sourcemapFunction
compile_with_sourcemap(f, arg_types; optimize=false, sourcemap_url="module.wasm.map")
    -> (wasm_bytes, sourcemap_json)

Compile a function and generate both the Wasm binary and a Source Map V3 JSON. The Wasm binary includes a sourceMappingURL custom section.

source
WasmTarget.compile_multi_with_sourcemapFunction
compile_multi_with_sourcemap(functions; optimize=false, sourcemap_url="module.wasm.map")
    -> (wasm_bytes, sourcemap_json)

Compile multiple functions and generate both Wasm binary and Source Map V3 JSON.

source

Low-Level / Advanced

WasmTarget.FrozenCompilationStateType
FrozenCompilationState

Snapshot of WasmModule + TypeRegistry after all setup (type registration, hierarchy, box types, etc.) but BEFORE function body compilation. This allows Phase 1-mini to pre-compute the Dict-heavy setup at build time and ship only the pure codegen to WASM.

The frozen state captures everything needed to compile function bodies without re-running any Dict-based setup code.

source
WasmTarget.build_frozen_stateFunction
build_frozen_state(ir_entries::Vector) -> FrozenCompilationState

Run the SETUP portion of compilemodulefromir for representative functions, capturing the resulting WasmModule and TypeRegistry state. The frozen state can then be used by compilemodulefromir_frozen to skip all Dict-heavy setup.

Each entry is (codeinfo::CodeInfo, returntype::Type, arg_types::Tuple, name::String).

source
WasmTarget.compile_handlerFunction
compile_handler(closure, signal_fields, export_name; globals, imports, dom_bindings) -> WasmModule

Compile a Therapy.jl event handler closure to WebAssembly with signal substitution.

The signal_fields dict maps captured closure field names to their signal info:

  • Key: field name (Symbol), e.g., :count, :set_count
  • Value: tuple (isgetter::Bool, globalidx::UInt32, value_type::Type)

The handler closure should take no arguments. Signal getters/setters are captured in the closure and compiled to Wasm global.get/global.set operations.

When dom_bindings is provided, DOM update calls are automatically injected after each signal write. This is used by Therapy.jl for reactive DOM updates.

Example

count, set_count = create_signal(0)
handler = () -> set_count(count() + 1)

signal_fields = Dict(
    :count => (true, UInt32(0), Int64),      # getter for global 0
    :set_count => (false, UInt32(0), Int64)  # setter for global 0
)

mod = compile_handler(handler, signal_fields, "onclick")
source
WasmTarget.DOMBindingSpecType

Specification for a DOM update call after signal write. Used by compile_handler to inject DOM update calls after signal writes.

source