Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

WebAssembly Support

rilua compiles to wasm32-unknown-unknown for running Lua 5.1.1 code in the browser or other WebAssembly runtimes.

How It Works

When targeting wasm32, src/platform.rs swaps every extern "C" function for a pure-Rust stub. Only the platform layer changes; the VM, compiler, and core standard libraries are unmodified.

platform.rs
  |
  +-- #[cfg(not(target_arch = "wasm32"))]  -> extern "C" { ... }
  |
  +-- #[cfg(target_arch = "wasm32")]       -> wasm_stubs module

Stubs and Replacements

C FunctionWASM Replacement
strtodf64::from_str() (ASCII decimal only)
localeconvStatic LConv with decimal_point = '.'
strcollByte-wise comparison (no locale)
setlocaleNo-op (returns "C")
strftimeReturns 0 (no formatting)
localtime_r / gmtime_rReturns false (no time conversion)
mktimeReturns -1
clockReturns -1
time(NULL)Returns 0
isalpha, tolower, etc.ASCII-only Rust equivalents
FILE* operationsReturn null/error values
popen / pcloseReturn null/-1
signalNo-op (SIGINT handling disabled)

Locale Differences

On native platforms, rilua uses strtod and localeconv for locale-aware number parsing (matching PUC-Rio behavior where 3,14 parses as a number in locales using comma as decimal separator).

On WASM, number parsing is ASCII-only with . as the decimal point. This matches the "C" locale and is correct for all standard Lua programs.

Standard Library Availability

Libraries that need filesystem or process access are still loadable on WASM but their functions return errors. Libraries that are pure computation work without restrictions.

LibraryWASM StatusNotes
baseFullAll 29 functions work
stringFullAll 14 functions work, pattern matching included
tableFullAll 9 functions work
mathFullAll 28 functions work
coroutineFullAll 6 functions work
debugFullAll 14 functions work (no filesystem dependency)
ioErrorsFile operations return nil, "not supported"
osPartialos.clock, os.date, os.time return defaults; os.execute, os.remove, os.rename, os.tmpname error
packageLimitedrequire works for preloaded modules; file-based loading fails

Sandboxed Loading

For WASM builds, load only the libraries that work:

use rilua::{Lua, StdLib};

let libs = StdLib::BASE | StdLib::STRING | StdLib::TABLE
         | StdLib::MATH | StdLib::COROUTINE;
let mut lua = Lua::new_with(libs)?;

Building for WASM

Prerequisites

  • Rust 1.92+ with the wasm32-unknown-unknown target
  • wasm-pack (for browser builds)
rustup target add wasm32-unknown-unknown

As a Library Dependency

Add rilua to your WASM crate’s Cargo.toml:

[dependencies]
rilua = "0.1"
wasm-bindgen = "0.2"

[lib]
crate-type = ["cdylib"]

Example lib.rs:

use wasm_bindgen::prelude::*;
use rilua::{Lua, StdLib};

#[wasm_bindgen]
pub fn eval_lua(code: &str) -> String {
    let libs = StdLib::BASE | StdLib::STRING | StdLib::TABLE
             | StdLib::MATH | StdLib::COROUTINE;

    let mut lua = match Lua::new_with(libs) {
        Ok(l) => l,
        Err(e) => return format!("init error: {e}"),
    };

    match lua.exec(code) {
        Ok(()) => String::new(),
        Err(e) => format!("{e}"),
    }
}

Build with wasm-pack:

wasm-pack build --target web

Browser Demo

A working browser demo is in examples/wasm-demo/. It provides a textarea for Lua code and renders output in the page. See examples/wasm-demo/README.md for build and serve instructions.

The demo replaces print with a version that writes to a thread-local String buffer (stdout does not exist in WASM), then returns the captured output after execution.

Feature Interactions

FeatureWASM Behavior
dynmodDisabled (no shared library loading on WASM)
sendWorks (GcRef indices are just u32 values)
SIGINTNo-op (no signal handling on WASM)

Limitations

See the Standard Library Availability table and Locale Differences section above for specifics. In summary: no filesystem, no process control, no locale, no clock, ASCII-only number parsing.

print writes to stdout, which does not exist in WASM. Override it to capture output (as the browser demo does).