Contributing
View SourceHow to change erlang-wasmtime and keep it green. Read docs/design.md first for the rules a change must keep; this page is the mechanics.
Checks
make check # erlfmt, clang-format, shellcheck, elvis, xref, dialyzer, all suites
make fmt # rewrite Erlang and C in place
rebar3 ct --suite test/wasmtime_ref_SUITE --case ref_struct
CI runs the same list on Ubuntu (OTP 27 and 28), macOS and FreeBSD, plus AddressSanitizer and the runtime-only build. Run the two extra ones yourself when you touch the NIF:
# AddressSanitizer, Linux
WASMTIME_NIF_SANITIZE=address rebar3 compile
LD_PRELOAD="$(gcc -print-file-name=libasan.so)" \
ASAN_OPTIONS="detect_leaks=0:abort_on_error=1:alloc_dealloc_mismatch=0" rebar3 ct
# AddressSanitizer, macOS
WASMTIME_NIF_SANITIZE=address rebar3 compile
ASAN_RT="$(ls "$(dirname "$(xcrun -f clang)")"/../lib/clang/*/lib/darwin/libclang_rt.asan_osx_dynamic.dylib | head -1)"
DYLD_INSERT_LIBRARIES="$ASAN_RT" \
ASAN_OPTIONS="detect_leaks=0:abort_on_error=1:alloc_dealloc_mismatch=0" rebar3 ct
# Runtime-only library (no compiler): fixtures come from a full build
escript scripts/precompile-fixtures.escript test/wasmtime_runtime_only_SUITE_data /tmp/cwasm
rm -f _build/wasmtime_nif.stamp
WASMTIME_RUNTIME_ONLY=1 WASMTIME_CWASM_DIR=/tmp/cwasm rebar3 ct --suite test/wasmtime_runtime_only_SUITE
rm -f _build/wasmtime_nif.stamp && rebar3 compile # back to the full build
Removing the stamp forces the NIF to rebuild against a different library.
Add a NIF function
The order the code enforces, with one file per step:
C entry point, in the file that owns the mechanism (see the table in
docs/design.md):static ERL_NIF_TERM nif_thing(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]). Take the instance mutex throughwith_export,with_reforwith_memorywhen you touch the store; never from a thread that does not hold it. Returnmk_error_s(env, Class, Kind, Message)for every failure; raisebadargonly for terms of the wrong shape.The table in
c_src/nif_api.c:{"thing", Arity, nif_thing, 0};ERL_NIF_DIRTY_JOB_CPU_BOUNDonly for work that takes milliseconds (compiling, serializing) and never for anything that waits.The stub in
src/wasmtime_nif.erl: add to-export, to-nifs, and athing(_A, _B) -> erlang:nif_error(not_loaded).clause.The wrapper in
src/wasmtime.erl:-doc,-spec, defaults and validation live here, the NIF only checks shapes. Export it.Docs: a row in
docs/features.md(implemented, or refused with itskind), the guide the feature belongs to,CHANGELOG.md.Tests: a case in the suite for the mechanism, named after the behaviour (
stream_blocked_recv_caller_dies), asserting the error kinds, not only the happy path. Helpers (compiling WAT, the hand-assembled binaries,needs/2for capability skips) are intest/wasmtime_test.erl.Suite Covers wasmtime_SUITEbehaviour that spans mechanisms: interruption, caller death, timeouts, error shapes wasmtime_module_SUITEcompile, validate, inspect, precompiled modules, engine options wasmtime_call_SUITEvalues, traps and traces, fuel, async calls, globals and tables wasmtime_memory_SUITElinear memory access, store limits, instance lifetime wasmtime_host_SUITEhost functions, the hostprocesswasmtime_wasi_SUITEWASI arguments, environment, directories, stdio wasmtime_stream_SUITEthe inbox, streamed stdio, the erlangimports, the shim fileswasmtime_ref_SUITEreferences: funcref, externref, GC values wasmtime_runtime_only_SUITEthe runtime-only build against precompiled fixtures make check, then ASan if the C changed.
Add a value kind or a reference kind
c_src/nif_values.c: extend vtype_of (the family), term_to_val and
val_to_term; never use wasm_valtype_kind. Reference kinds also need a
ref_t kind and its unroot in ref_dtor (c_src/nif_refs.c). Add a
round trip test through a call, a host function, a global and a table.
Add an instantiate or WASI option
src/wasmtime.erl nif_options/3 or wasi_options/1 puts it in the
options map with its default; parse_options or configure_wasi in
c_src/nif_instantiate.c reads it by key. Document it in the
options() or wasi_options() type and in the guide.
Conventions
- Guest failures never raise.
{error, #{class, kind, message}}and atracefor traps. Newkinds are listed indocs/features.md. - One OS thread per instance, requests queued, results as messages. Do not add a code path where a scheduler thread waits on the worker.
- Comments say why, not what. A rule that a later edit could break gets a
comment where it is relied on and a line in
docs/design.md. - Docs are task-oriented: what it is, when you need it, the code, short notes. No hype, no "comprehensive".
- Commits and pull requests are short and have no generated-by lines.
- Anything not implemented is refused with an error and listed under
"Deferred" in
docs/features.mdwith the reason. - Numbers (limits, timeouts, sizes) are
#defines or-defines with their reason indocs/design.md.