<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>wasm</title>
  <link href="https://edadma.github.io/wasm/feed.xml" rel="self"/>
  <link href="https://edadma.github.io/wasm/"/>
  <id>https://edadma.github.io/wasm/feed.xml</id>
  <updated>2026-05-17T00:33:47.893097391Z</updated>
  <author><name>Ed Maxedon</name></author>
  <entry>
    <title>Validation</title>
    <link href="https://edadma.github.io/wasm/concepts/validation/"/>
    <id>https://edadma.github.io/wasm/concepts/validation/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>Every module passes a separate validator before any code runs. Bad binaries fail at instantiate time with a precise error.</summary>
    <content type="html">&lt;p&gt;&lt;code&gt;Runtime.instantiate(bytes, …)&lt;/code&gt; is two passes: a parser that turns the byte stream into a typed &lt;code&gt;Module&lt;/code&gt; representation, and a validator that walks the resulting module section-by-section before any byte of guest code is interpreted. Either step can return &lt;code&gt;Left(InvalidModule(message))&lt;/code&gt; — and when it does, the message is precise enough to point at the offending opcode by byte offset.&lt;/p&gt;
&lt;h2 id=&quot;what-the-validator-checks&quot;&gt;What the validator checks&lt;/h2&gt;
&lt;p&gt;For every function body, the validator runs an abstract interpreter that tracks the operand stack as a list of value types (&lt;code&gt;i32&lt;/code&gt; / &lt;code&gt;i64&lt;/code&gt; / &lt;code&gt;f32&lt;/code&gt; / &lt;code&gt;f64&lt;/code&gt; / Polymorphic), plus a control-frame stack tracking &lt;code&gt;block&lt;/code&gt; / &lt;code&gt;loop&lt;/code&gt; / &lt;code&gt;if&lt;/code&gt; scopes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Every opcode pops the right number of operands and the right value types from the abstract stack.&lt;/li&gt;
&lt;li&gt;Every opcode pushes the result types it claims to.&lt;/li&gt;
&lt;li&gt;Every &lt;code&gt;br&lt;/code&gt; / &lt;code&gt;br_if&lt;/code&gt; / &lt;code&gt;br_table&lt;/code&gt; target index is in range, and the branch arguments (operand-stack top values) match the target’s parameter types.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;call&lt;/code&gt; and &lt;code&gt;call_indirect&lt;/code&gt; arguments match the callee’s signature.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;local.get&lt;/code&gt; / &lt;code&gt;local.set&lt;/code&gt; / &lt;code&gt;local.tee&lt;/code&gt; indices are in range; &lt;code&gt;global.set&lt;/code&gt; only targets mutable globals.&lt;/li&gt;
&lt;li&gt;Memory load/store accesses use a memory index that exists in section 5.&lt;/li&gt;
&lt;li&gt;Table accesses use a table index that exists in section 4, with the right element type.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unreachable code after &lt;code&gt;br&lt;/code&gt;, &lt;code&gt;return&lt;/code&gt;, or &lt;code&gt;unreachable&lt;/code&gt; is type-checked too — the spec requires it.&lt;/p&gt;
&lt;h2 id=&quot;what-fail-at-instantiate-looks-like&quot;&gt;What “fail at instantiate” looks like&lt;/h2&gt;
&lt;p&gt;Bad binaries surface as &lt;code&gt;Left(InvalidModule(message))&lt;/code&gt; from &lt;code&gt;Runtime.instantiate&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;function 7: byte offset 0x1a3: i32.add expected i32 i32 on stack, found i32 f64
function 7: byte offset 0x205: br target depth 3 out of range (max 2)
function 2: byte offset 0x40: call_indirect signature mismatch — table 0 element 5 has type (i32) -&amp;gt; (i32), expected () -&amp;gt; (i32)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The byte offset is from the start of the function body, not the whole module, so it lines up directly with what &lt;code&gt;wasm-objdump -d&lt;/code&gt; shows.&lt;/p&gt;
&lt;h2 id=&quot;binary-sections-recognised&quot;&gt;Binary sections recognised&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Section&lt;/th&gt;&lt;th&gt;Number&lt;/th&gt;&lt;th&gt;What it carries&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Type&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Function signatures (&lt;code&gt;(param i32 i32) (result i32)&lt;/code&gt; etc.)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Import&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Functions, memories, globals, tables imported from the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Function&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Function-index → type-index mapping (signature for each body)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Table&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;Funcref tables (used by &lt;code&gt;call_indirect&lt;/code&gt;)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;Linear-memory definitions (initial + optional max pages)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Global&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;Module-level globals (mutable and immutable)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Export&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;Names exposed to the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Start&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;A function index to run at instantiate time, before any export call&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Element&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;Funcref table initializers (active, passive, and declarative kinds)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Code&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;Function bodies&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Data&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;Linear-memory initializers (active and passive kinds)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;DataCount&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;A &lt;code&gt;u32&lt;/code&gt; = the number of data segments. Required when a function uses &lt;code&gt;memory.init&lt;/code&gt; or &lt;code&gt;data.drop&lt;/code&gt;. The validator cross-checks this against section 11; a mismatch is &lt;code&gt;InvalidModule&lt;/code&gt;, and a function that references a &lt;code&gt;dataidx&lt;/code&gt; without section 12 present is also &lt;code&gt;InvalidModule&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Custom sections are skipped harmlessly. The &lt;code&gt;name&lt;/code&gt; custom section is not yet used (functions appear as &lt;code&gt;function &amp;lt;N&amp;gt;&lt;/code&gt; in error messages, not by their debug name).&lt;/p&gt;
&lt;h2 id=&quot;leb128&quot;&gt;LEB128&lt;/h2&gt;
&lt;p&gt;Unsigned and signed LEB128 (up to 64 bits) are implemented from scratch — no &lt;code&gt;BigInteger&lt;/code&gt; allocation, no platform-specific decoder. The format dominates wasm’s wire encoding: every type index, function index, local index, memory immediate, and integer constant goes through it.&lt;/p&gt;
&lt;h2 id=&quot;what-validation-does-not-catch&quot;&gt;What validation does &lt;em&gt;not&lt;/em&gt; catch&lt;/h2&gt;
&lt;p&gt;By design, a validator only catches violations of wasm’s static type system. It does not catch:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Trap-at-runtime errors&lt;/strong&gt; — divide-by-zero, integer overflow on signed &lt;code&gt;div&lt;/code&gt;, out-of-bounds memory access, indirect-call signature mismatch, branch index out of range. These surface as &lt;a href=&quot;/wasm/concepts/traps-and-errors/&quot;&gt;&lt;code&gt;InvalidModule(msg)&lt;/code&gt; from a running function&lt;/a&gt;, not from &lt;code&gt;instantiate&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Behavioural bugs in the guest program&lt;/strong&gt; — infinite loops, deadlocks, wrong answers. Those are for the guest to debug, not the host.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resource-exhaustion at instantiate&lt;/strong&gt; — if a module claims a min-memory of 50,000 pages (≈ 3.2 GiB), the interpreter tries to allocate it. Validate the bytes against a size policy before handing them to &lt;code&gt;instantiate&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Traps and errors</title>
    <link href="https://edadma.github.io/wasm/concepts/traps-and-errors/"/>
    <id>https://edadma.github.io/wasm/concepts/traps-and-errors/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>The `WasmError` ADT, what causes each variant, and how runtime traps surface.</summary>
    <content type="html">&lt;p&gt;Every public API in &lt;code&gt;interp&lt;/code&gt; and &lt;code&gt;wasi&lt;/code&gt; returns &lt;code&gt;Either[WasmError, T]&lt;/code&gt;. No thrown exceptions cross the boundary. The ADT is small and exhaustive:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;InvalidMagic&lt;/span&gt;                                          &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;TypeMismatch&lt;/span&gt;                                          &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;UnreachableExecuted&lt;/span&gt;                                   &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;MemoryOutOfBounds&lt;/span&gt;                                     &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;UnknownOpcode&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;byte&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                         &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;UnknownImport&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;module&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;      &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ExportNotFound&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                     &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;InvalidModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                   &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Eight variants total. The first four are simple value objects with no payload (the variant &lt;em&gt;is&lt;/em&gt; the diagnostic); the next four carry a payload that says &lt;em&gt;where&lt;/em&gt; or &lt;em&gt;what&lt;/em&gt;.&lt;/p&gt;
&lt;h2 id=&quot;when-each-variant-fires&quot;&gt;When each variant fires&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Variant&lt;/th&gt;&lt;th&gt;Origin&lt;/th&gt;&lt;th&gt;Typical cause&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;InvalidMagic&lt;/code&gt;&lt;/td&gt;&lt;td&gt;parser&lt;/td&gt;&lt;td&gt;First four bytes aren’t &lt;code&gt;\0asm&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;TypeMismatch&lt;/code&gt;&lt;/td&gt;&lt;td&gt;runtime&lt;/td&gt;&lt;td&gt;A typed slot received a &lt;code&gt;Value&lt;/code&gt; of the wrong tag — almost always an interpreter or host-binding bug, not a guest bug.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;UnreachableExecuted&lt;/code&gt;&lt;/td&gt;&lt;td&gt;runtime&lt;/td&gt;&lt;td&gt;Guest executed an &lt;code&gt;unreachable&lt;/code&gt; opcode. Often rustc’s panic landing pad.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;MemoryOutOfBounds&lt;/code&gt;&lt;/td&gt;&lt;td&gt;runtime&lt;/td&gt;&lt;td&gt;Load, store, &lt;code&gt;memory.copy&lt;/code&gt;, &lt;code&gt;memory.fill&lt;/code&gt;, or &lt;code&gt;memory.init&lt;/code&gt; accessed past the end of linear memory.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;UnknownOpcode(b)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;parser&lt;/td&gt;&lt;td&gt;The byte stream contained an opcode the interpreter doesn’t recognise.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;UnknownImport(m, n)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;instantiate&lt;/td&gt;&lt;td&gt;The module imports &lt;code&gt;m.n&lt;/code&gt; but no &lt;code&gt;HostModule&lt;/code&gt; provides it.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;ExportNotFound(name)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;runtime&lt;/td&gt;&lt;td&gt;&lt;code&gt;inst.invoke(name, …)&lt;/code&gt; or &lt;code&gt;Wasi.run(inst, name)&lt;/code&gt; looked up an export the module doesn’t have.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;InvalidModule(msg)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;validator + runtime traps&lt;/td&gt;&lt;td&gt;See below.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;invalidmodule-is-a-dual-purpose-variant&quot;&gt;InvalidModule is a dual-purpose variant&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;InvalidModule(message)&lt;/code&gt; carries an ad-hoc string and shows up in two situations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Validator failures.&lt;/strong&gt; Any abstract-type-check failure during the validator’s pre-instantiate pass. The message is prefixed &lt;code&gt;function &amp;lt;N&amp;gt;: byte offset 0x&amp;lt;hex&amp;gt;: &amp;lt;details&amp;gt;&lt;/code&gt; so it lines up with &lt;code&gt;wasm-objdump -d&lt;/code&gt; output.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Runtime traps that aren’t memory bounds.&lt;/strong&gt; Divide-by-zero, integer overflow on signed &lt;code&gt;div_s&lt;/code&gt; / &lt;code&gt;rem_s&lt;/code&gt;, &lt;code&gt;i32.trunc_f32_s&lt;/code&gt; of NaN or out-of-range, &lt;code&gt;call_indirect&lt;/code&gt; signature mismatch, undefined table element, &lt;code&gt;br_table&lt;/code&gt; index past the table length.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The message names the operation that trapped. You don’t have to pattern-match it to recover, but you can grep it.&lt;/p&gt;
&lt;h2 id=&quot;worked-example&quot;&gt;Worked example&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;hl-type&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;instantiate&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bytes&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;EnvModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;inst&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
    inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;divide&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;= $v&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;other&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;       &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;unexpected: $other&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;InvalidModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;        &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;trap: $msg&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;MemoryOutOfBounds&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;         &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;memory bounds&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;UnreachableExecuted&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;       &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;unreachable&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ExportNotFound&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;      &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;no export: $name&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                       &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;other: $err&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;InvalidMagic&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                  &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;not a wasm binary&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;UnknownImport&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; n&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;           &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;missing import: $m.$n&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;InvalidModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;            &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;validator: $msg&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                           &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;other: $err&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;inst.invoke(&amp;quot;divide&amp;quot;, Seq(I32(10), I32(0)))&lt;/code&gt; returns &lt;code&gt;Left(InvalidModule(&amp;quot;i32.div_s: divide by zero&amp;quot;))&lt;/code&gt;. The &lt;code&gt;Right&lt;/code&gt;-side &lt;code&gt;Seq[Value]&lt;/code&gt; is whatever the export’s signature says — a 0-result export returns &lt;code&gt;Right(Seq.empty)&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;wasi-exit-codes&quot;&gt;WASI exit codes&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Wasi.run(inst, &amp;quot;_start&amp;quot;)&lt;/code&gt; adds one shape on top of the &lt;code&gt;Either&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Right(0)&lt;/code&gt; — guest’s &lt;code&gt;_start&lt;/code&gt; returned normally, no &lt;code&gt;proc_exit&lt;/code&gt; call.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Right(N)&lt;/code&gt; — guest called &lt;code&gt;proc_exit(N)&lt;/code&gt;. The host treats this as a clean exit, not a trap; whatever &lt;code&gt;N&lt;/code&gt; is, it surfaces as &lt;code&gt;Right(N)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Left(err)&lt;/code&gt; — anything that was a trap during execution of &lt;code&gt;_start&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So &lt;code&gt;Wasi.run&lt;/code&gt; lets a guest distinguish “I crashed” (Left) from “I exited cleanly with status N” (Right(N)) without you having to special-case &lt;code&gt;proc_exit&lt;/code&gt;-the-syscall in your own host shim.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Tracer</title>
    <link href="https://edadma.github.io/wasm/concepts/tracer/"/>
    <id>https://edadma.github.io/wasm/concepts/tracer/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>Instrumentation hooks for opcode counts, function transitions, throws, and traps — wire one in via the optional `tracer` parameter on `invoke` / `Wasi.run`.</summary>
    <content type="html">&lt;p&gt;&lt;code&gt;Tracer&lt;/code&gt; is the interpreter’s instrumentation hook. Every &lt;code&gt;ModuleInstance.invoke&lt;/code&gt; and &lt;code&gt;Wasi.run&lt;/code&gt; takes an optional &lt;code&gt;tracer: Tracer = Tracer.NoOp&lt;/code&gt; parameter; pass anything else to receive callbacks at well-defined points during the invocation.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Tracer&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; onOp&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;op&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt;           &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// every opcode dispatch&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; onCall&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;funcIdx&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt;    &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// wasm frame pushed&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; onHostCall&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;funcIdx&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;  &lt;span class=&quot;hl-comment&quot;&gt;// host fn dispatched (no frame)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; onReturn&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;funcIdx&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt;  &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// wasm frame popped&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; onThrow&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;tagIdx&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt;    &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// exception raised&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; onTrap&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt;  &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// non-throw runtime error&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Default methods are no-ops. The bundled &lt;code&gt;Tracer.NoOp&lt;/code&gt; extends the trait without overriding anything; the JIT inlines its empty methods, so untraced invocations pay no per-opcode cost.&lt;/p&gt;
&lt;h2 id=&quot;tracer-counting&quot;&gt;&lt;code&gt;Tracer.Counting&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;The library ships a drop-in counting tracer:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; tracer &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Tracer&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;counting
inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;_start&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; tracer&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
    println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;ops:       ${tracer.ops}&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
    println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;calls:     ${tracer.calls}&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;      &lt;span class=&quot;hl-comment&quot;&gt;// wasm frames pushed&lt;/span&gt;
    println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;hostCalls: ${tracer.hostCalls}&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;hl-comment&quot;&gt;// host fns dispatched&lt;/span&gt;
    println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;maxDepth:  ${tracer.maxDepth}&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// peak wasm-frame depth&lt;/span&gt;
    println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;throws:    ${tracer.throws}&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
    println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;traps:     ${tracer.traps}&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;failed: $err&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;maxDepth&lt;/code&gt; counts wasm frames only (host calls don’t push a frame, so they don’t grow depth). A tail-recursive loop runs at constant depth regardless of iteration count — useful for confirming that a compiler emits proper &lt;code&gt;return_call&lt;/code&gt; for recursive code.&lt;/p&gt;
&lt;h2 id=&quot;custom-tracer&quot;&gt;Custom Tracer&lt;/h2&gt;
&lt;p&gt;For finer-grained tracing — opcode histograms, branch-direction profiles, per-function timing, IR-level matching against execution — implement the trait directly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; scala&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;collection&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;mutable&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ArrayBuffer&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ops &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ArrayBuffer&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; tracer &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Tracer&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; onOp&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;op&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; ops &lt;span class=&quot;hl-keyword&quot;&gt;+=&lt;/span&gt; op

inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;compute&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt; tracer&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;opcodes executed: ${ops.size}&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;distinct opcodes: ${ops.toSet}&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The hooks fire in execution order. &lt;code&gt;onOp&lt;/code&gt; runs just before the opcode dispatches; &lt;code&gt;onCall&lt;/code&gt; runs after the new frame is pushed; &lt;code&gt;onReturn&lt;/code&gt; runs after the frame is popped. Tail calls fire &lt;code&gt;onReturn(caller)&lt;/code&gt; followed by &lt;code&gt;onCall(callee)&lt;/code&gt; — the depth net change is zero, which is what &lt;code&gt;Tracer.Counting.maxDepth&lt;/code&gt; observes.&lt;/p&gt;
&lt;h2 id=&quot;from-the-cli&quot;&gt;From the CLI&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;wasm --trace &amp;lt;file&amp;gt;&lt;/code&gt; installs &lt;code&gt;Tracer.counting&lt;/code&gt; and prints the totals to stderr after the run. See &lt;a href=&quot;/wasm/cli/flags/#--trace&quot;&gt;CLI → Flags&lt;/a&gt; for the exact output shape. Useful for quick profiling without writing any Scala.&lt;/p&gt;
&lt;h2 id=&quot;what-the-tracer-does-not-see&quot;&gt;What the tracer does NOT see&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Sub-opcodes of &lt;code&gt;0xFC&lt;/code&gt; / &lt;code&gt;0xFD&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;onOp&lt;/code&gt; surfaces the prefix byte (&lt;code&gt;0xFC&lt;/code&gt; / &lt;code&gt;0xFD&lt;/code&gt;), not the sub-opcode. If you want per-sub-op visibility, read the next byte off the function body yourself or maintain a side counter.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Block / loop / if entries&lt;/strong&gt; — these are control-flow opcodes (&lt;code&gt;0x02&lt;/code&gt; / &lt;code&gt;0x03&lt;/code&gt; / &lt;code&gt;0x04&lt;/code&gt;) and surface through &lt;code&gt;onOp&lt;/code&gt; like everything else, but the tracer doesn’t fire a dedicated “block entered” callback. Same for &lt;code&gt;br&lt;/code&gt; / &lt;code&gt;br_if&lt;/code&gt; / &lt;code&gt;br_table&lt;/code&gt; — they’re just opcodes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Memory accesses&lt;/strong&gt; — load/store ops fire &lt;code&gt;onOp&lt;/code&gt; like everything else. The tracer doesn’t surface the resolved address, the touched memory, or the byte payload.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Validator-time events&lt;/strong&gt; — the tracer is bound to &lt;em&gt;runtime&lt;/em&gt; execution. Validation runs at &lt;code&gt;Runtime.instantiate&lt;/code&gt; and emits errors via &lt;code&gt;Left(WasmError)&lt;/code&gt; directly.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For all four of these cases the tracer is a hook point you’d build on top of — combine &lt;code&gt;onOp&lt;/code&gt; with the module’s BlockInfo / function body to derive the higher-level events you need.&lt;/p&gt;
&lt;h2 id=&quot;lifetime-reuse&quot;&gt;Lifetime + reuse&lt;/h2&gt;
&lt;p&gt;A Tracer is bound to a single invocation. Reuse across invocations is allowed if your Tracer’s internal state is reset by the caller — the runtime never touches Tracer fields between calls. &lt;code&gt;Tracer.Counting&lt;/code&gt; instances are safe to construct once and inspect after each invoke; reset the counters yourself if you want fresh totals per call.&lt;/p&gt;
&lt;p&gt;The runtime is single-threaded (one in-flight invoke at a time); a Tracer never sees concurrent callbacks.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Testing</title>
    <link href="https://edadma.github.io/wasm/development/testing/"/>
    <id>https://edadma.github.io/wasm/development/testing/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>Unit suites on every backend, the W3C testsuite runner, and how to refresh `.wasm` test fixtures from their `.wat` sources.</summary>
    <content type="html">&lt;h2 id=&quot;unit-suites&quot;&gt;Unit suites&lt;/h2&gt;
&lt;p&gt;The interpreter has no test framework dependency — tests are &lt;code&gt;@main&lt;/code&gt;-style objects with a hand-rolled PASS/FAIL runner. The same code runs on all three backends:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;interpJVM/Test/run&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;    # 653 interpreter tests
sbt &lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;interpJS/Test/run&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;
sbt &apos;interpNative/Test/run&apos;

sbt &apos;wasiJVM/Test/run&apos;      # 209 WASI tests on JVM/Native (193 on JS,
sbt &apos;wasiJS/Test/run&apos;       #                   16 socket tests skipped)
sbt &apos;wasiNative/Test/run&apos;

sbt &apos;cliJVM/Test/run&apos;       # 19 CLI tests (JVM-only)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Total: &lt;strong&gt;881 tests&lt;/strong&gt; on JVM (653 interp + 209 wasi + 19 cli) — all three backends green for &lt;code&gt;interp&lt;/code&gt; and &lt;code&gt;wasi&lt;/code&gt;. Three of those are end-to-end integration tests against real rustc-built &lt;code&gt;wasm32-wasip1&lt;/code&gt; binaries.&lt;/p&gt;
&lt;p&gt;Each &lt;code&gt;Test/run&lt;/code&gt; target prints one &lt;code&gt;OK &amp;lt;name&amp;gt;&lt;/code&gt; or &lt;code&gt;FAIL &amp;lt;name&amp;gt; — &amp;lt;reason&amp;gt;&lt;/code&gt; line per assertion plus a final summary; the process exits non-zero if any assertion failed. Add new tests by appending &lt;code&gt;test(&amp;quot;name&amp;quot;) { ... }&lt;/code&gt; calls inside the appropriate category file under &lt;code&gt;interp/shared/src/test/scala/io/github/edadma/wasm/&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;w3c-testsuite&quot;&gt;W3C testsuite&lt;/h2&gt;
&lt;p&gt;The official &lt;a href=&quot;https://github.com/WebAssembly/testsuite&quot;&gt;WebAssembly testsuite&lt;/a&gt; runs through an integrated runner — &lt;strong&gt;~53,000 assertions&lt;/strong&gt; across &lt;strong&gt;142 manifests&lt;/strong&gt; covering numerics, control flow, memory addressing, function pointers, the complete SIMD proposal, bulk memory + tables + element segments, EH and tail-call proposals, plus binary-format and UTF-8 edge cases.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;./scripts/build-spec-tests.sh&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;/path/to/wasm-testsuite&lt;/span&gt;     &lt;span class=&quot;hl-comment&quot;&gt;# one-time, regenerates JSON fixtures&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;#&lt;/span&gt; one-time, regenerates JSON fixtures
&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;interpJVM/Test/runMain io.github.edadma.wasm.spec.SpecComplianceTests&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The runner consumes &lt;code&gt;wast2json&lt;/code&gt; JSON command manifests + per-module &lt;code&gt;.wasm&lt;/code&gt; binaries; it never parses &lt;code&gt;.wast&lt;/code&gt; text. The script’s curated slice is the &lt;code&gt;FILES=(...)&lt;/code&gt; array — append a manifest name and re-run to expand coverage.&lt;/p&gt;
&lt;p&gt;See &lt;a href=&quot;/wasm/reference/spec-compliance/&quot;&gt;Reference → Spec compliance&lt;/a&gt; for current pass / fail / known-failure totals and the manifests pinned in &lt;code&gt;KnownFailures&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;regenerating-fixtures&quot;&gt;Regenerating fixtures&lt;/h2&gt;
&lt;p&gt;The &lt;code&gt;.wasm&lt;/code&gt; test fixtures are produced from hand-written &lt;code&gt;.wat&lt;/code&gt; files with &lt;code&gt;wat2wasm&lt;/code&gt; (from &lt;a href=&quot;https://github.com/WebAssembly/wabt&quot;&gt;WABT&lt;/a&gt;). The committed &lt;code&gt;.wasm&lt;/code&gt; binaries let anyone run the tests without WABT installed; &lt;code&gt;Fixtures.scala&lt;/code&gt; is regenerated automatically on every &lt;code&gt;sbt compile&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After editing any &lt;code&gt;.wat&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;interp/shared/src/test/resources/fixtures&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;f&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;*.wat&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;wat2wasm&lt;/span&gt; &amp;quot;&lt;span class=&quot;hl-punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt; -o &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt;.&lt;span class=&quot;hl-variable&quot;&gt;wat&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;.wasm&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt;; done

cd wasi/shared/src/test/resources/fixtures
for f in *.wat; do wat2wasm &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt; -o &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;$&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt;.&lt;span class=&quot;hl-variable&quot;&gt;wat&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;.wasm&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt;; done&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The three rustc-built fixtures are committed with their &lt;code&gt;.rs&lt;/code&gt; source alongside as documentation; they’re rebuilt by hand when their semantics change (Cargo + the &lt;code&gt;wasm32-wasip1&lt;/code&gt; target).&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Syscalls</title>
    <link href="https://edadma.github.io/wasm/wasi/syscalls/"/>
    <id>https://edadma.github.io/wasm/wasi/syscalls/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>The 29 wasi_snapshot_preview1 host functions implemented, grouped by purpose.</summary>
    <content type="html">&lt;p&gt;Twenty-nine host functions are exposed under the module name &lt;code&gt;wasi_snapshot_preview1&lt;/code&gt;. That covers everything &lt;code&gt;wasm32-wasip1&lt;/code&gt; programs typically reach for: stdin/stdout, command-line args, environment variables, the clock, randomness, the filesystem (read, write, create, unlink, stat, readdir, rename, hard- and soft-link), poll-style readiness waits, and the wasi sockets surface (host-provided listening fds, accept / recv / send / shutdown).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Return shape:&lt;/strong&gt; every wasi-preview1 syscall returns a single &lt;code&gt;i32&lt;/code&gt; errno (&lt;code&gt;0&lt;/code&gt; for success; nonzero values from the wasi-preview1 errno list — &lt;code&gt;Wasi.ENOENT&lt;/code&gt;, &lt;code&gt;Wasi.EBADF&lt;/code&gt;, &lt;code&gt;Wasi.EFAULT&lt;/code&gt;, …). Output data is delivered through pointers passed by the guest into its own linear memory; the host writes the bytes there, the guest reads them back. The “Seq(I32(errno))” shape you’d see from &lt;code&gt;inst.invoke&lt;/code&gt; reflects that single-result calling convention — the data isn’t &lt;em&gt;in&lt;/em&gt; that Seq, it’s in memory.&lt;/p&gt;
&lt;h2 id=&quot;process&quot;&gt;Process&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Syscall&lt;/th&gt;&lt;th&gt;Behaviour&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;proc_exit(code)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Signals the host to terminate with &lt;code&gt;code&lt;/code&gt;. &lt;code&gt;Wasi.run&lt;/code&gt; catches it and returns &lt;code&gt;Right(code)&lt;/code&gt; instead of &lt;code&gt;Left(...)&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;args_get(argv, buf)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Writes &lt;code&gt;WasiContext.args&lt;/code&gt; into guest memory, NUL-terminated.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;args_sizes_get(*c, *sz)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Returns argc and total byte size.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;environ_get(envp, buf)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Same shape as &lt;code&gt;args_get&lt;/code&gt;, but &lt;code&gt;KEY=VALUE&lt;/code&gt; pairs from &lt;code&gt;WasiContext.envs&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;environ_sizes_get(*c, *sz)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Returns environ count and total byte size.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;clock-entropy&quot;&gt;Clock + entropy&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Syscall&lt;/th&gt;&lt;th&gt;Behaviour&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;clock_time_get(id, prec, *out)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;id=0&lt;/code&gt; → realtime nanos, &lt;code&gt;id=1&lt;/code&gt; → monotonic nanos. Backed by &lt;code&gt;WasiContext.clock&lt;/code&gt; (override the trait for deterministic tests). Other clock ids return &lt;code&gt;ENOTSUP&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;random_get(buf, len)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Writes &lt;code&gt;len&lt;/code&gt; bytes of randomness to &lt;code&gt;buf&lt;/code&gt;. Backed by &lt;code&gt;WasiContext.random: Int =&amp;gt; Array[Byte]&lt;/code&gt; — &lt;code&gt;SecureRandom&lt;/code&gt; by default, injectable for tests.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;stdio-preopens&quot;&gt;Stdio + preopens&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Syscall&lt;/th&gt;&lt;th&gt;Behaviour&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_write(fd, iovs, iovs_len, *n)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Writes one or more iovecs. fd 1 routes to &lt;code&gt;WasiContext.stdout&lt;/code&gt; (&lt;code&gt;System.out&lt;/code&gt; by default), fd 2 to &lt;code&gt;WasiContext.stderr&lt;/code&gt;, fd 3+ to a preopen-backed file handle.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_prestat_get(fd, *out)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;wasi-libc’s startup walk asks for each preopen at fd 3, 4, … until the host returns &lt;code&gt;EBADF&lt;/code&gt;. The host fills in the directory-name length here.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_prestat_dir_name(fd, buf, len)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Writes the preopen name (as UTF-8 bytes) into guest memory.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;file-i-o&quot;&gt;File I/O&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Syscall&lt;/th&gt;&lt;th&gt;Behaviour&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;path_open(dirfd, …, path_ptr, path_len, oflags, …, *out_fd)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Resolves &lt;code&gt;path&lt;/code&gt; against the preopen at &lt;code&gt;dirfd&lt;/code&gt;, opens it according to &lt;code&gt;oflags&lt;/code&gt; (&lt;code&gt;CREAT&lt;/code&gt; / &lt;code&gt;EXCL&lt;/code&gt; / &lt;code&gt;TRUNC&lt;/code&gt; / &lt;code&gt;DIRECTORY&lt;/code&gt;), and returns a new fd. Sandboxed against &lt;code&gt;..&lt;/code&gt;-escape and absolute paths.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_read(fd, iovs, iovs_len, *n)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Reads into iovecs, advancing the file position. &lt;code&gt;fd 0&lt;/code&gt; (stdin) dispatches through &lt;code&gt;WasiContext.stdin&lt;/code&gt; — the default returns 0 bytes (EOF). &lt;code&gt;fd 1&lt;/code&gt; / &lt;code&gt;fd 2&lt;/code&gt; / preopen dirs / unopened fds all reject with &lt;code&gt;EBADF&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_seek(fd, offset, whence, *new_pos)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;SEEK_SET=0&lt;/code&gt;, &lt;code&gt;SEEK_CUR=1&lt;/code&gt;, &lt;code&gt;SEEK_END=2&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_close(fd)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Releases the slot in the fd table.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_filestat_get(fd, *stat)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Fills the wasi-preview1 &lt;code&gt;filestat&lt;/code&gt; struct (filetype, inode placeholder, size, atim/mtim/ctim placeholders).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_fdstat_get(fd, *stat)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Filetype + fd-flags + rights.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_fdstat_set_flags(fd, fdflags)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Adjusts &lt;code&gt;APPEND&lt;/code&gt;, &lt;code&gt;NONBLOCK&lt;/code&gt;, &lt;code&gt;SYNC&lt;/code&gt;, etc. on the fd.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_sync(fd)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Flushes any buffered writes to the host filesystem (no-op for in-memory preopens).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_datasync(fd)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Like &lt;code&gt;fd_sync&lt;/code&gt; but data-only, for callers that don’t care about metadata.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_advise(fd, offset, len, advice)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Advisory POSIX &lt;code&gt;posix_fadvise&lt;/code&gt;. Valid advice values 0..5 (NORMAL / SEQUENTIAL / RANDOM / WILLNEED / DONTNEED / NOREUSE) — the shim honours none of them but returns ESUCCESS so callers can ship the hint without branching. EINVAL on out-of-range advice.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_allocate(fd, offset, len)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;POSIX &lt;code&gt;posix_fallocate&lt;/code&gt; — ensure &lt;code&gt;[offset, offset+len)&lt;/code&gt; is usable for writes. If the range extends past EOF, the file is grown with zero bytes in the gap. EBADF on stdio / preopen / unknown fd; EINVAL on negative arguments or arithmetic overflow.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;filesystem&quot;&gt;Filesystem&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Syscall&lt;/th&gt;&lt;th&gt;Behaviour&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;path_filestat_get(dirfd, …, path_ptr, path_len, *stat)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Stats &lt;code&gt;path&lt;/code&gt; relative to the preopen at &lt;code&gt;dirfd&lt;/code&gt; without opening it.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;path_unlink_file(dirfd, path_ptr, path_len)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Removes a file or symlink. Already-open handles keep their cell reference (POSIX unlink-while-open). &lt;code&gt;EISDIR&lt;/code&gt; for directories — use &lt;code&gt;path_remove_directory&lt;/code&gt; for those (not implemented).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;path_create_directory(dirfd, path_ptr, path_len)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Creates a directory entry. Returns &lt;code&gt;EEXIST&lt;/code&gt; if anything is there already.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;path_rename(fd, old_path, new_fd, new_path)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Moves an entry within a preopen. Cross-preopen renames return &lt;code&gt;ENOTCAPABLE&lt;/code&gt;. &lt;code&gt;ENOENT&lt;/code&gt; if source is missing, &lt;code&gt;EEXIST&lt;/code&gt; if destination is taken.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;path_link(old_fd, …, old_path, new_fd, new_path)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Hard link. Both paths refer to the same entry; writes through either are visible to both. Cross-preopen returns &lt;code&gt;ENOTCAPABLE&lt;/code&gt;; hardlinking directories returns &lt;code&gt;EPERM&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;path_symlink(old_path, fd, new_path)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Creates a symlink at &lt;code&gt;new_path&lt;/code&gt; whose stored target is the opaque string &lt;code&gt;old_path&lt;/code&gt;. The shim does not follow symlinks during path resolution; they’re visible via &lt;code&gt;path_readlink&lt;/code&gt; and surface as filetype &lt;code&gt;SYMBOLIC_LINK&lt;/code&gt; (7).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;path_readlink(fd, path, buf, buf_len, *bufused)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Reads a symlink’s target into &lt;code&gt;buf&lt;/code&gt;, truncating to &lt;code&gt;buf_len&lt;/code&gt; bytes. &lt;code&gt;bufused&lt;/code&gt; reports the actual byte count. &lt;code&gt;EINVAL&lt;/code&gt; on non-symlinks.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;fd_readdir(fd, buf, buf_len, cookie, *bytes_written)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Enumerates directory entries. Resumable via the &lt;code&gt;cookie&lt;/code&gt; for buffers smaller than the listing.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;polling&quot;&gt;Polling&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Syscall&lt;/th&gt;&lt;th&gt;Behaviour&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;poll_oneoff(in, out, nsubs, *nevents)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Wait for one of &lt;code&gt;nsubs&lt;/code&gt; subscriptions to become ready. Subscriptions are 48-byte records (decoded fields &lt;code&gt;userdata&lt;/code&gt;, &lt;code&gt;eventtype&lt;/code&gt;, &lt;code&gt;clockid&lt;/code&gt;/&lt;code&gt;fd&lt;/code&gt;, &lt;code&gt;timeout&lt;/code&gt;, &lt;code&gt;precision&lt;/code&gt;, &lt;code&gt;flags&lt;/code&gt;); events are 32-byte records (&lt;code&gt;userdata&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt;, &lt;code&gt;eventtype&lt;/code&gt;, &lt;code&gt;nbytes&lt;/code&gt;). FD-read/FD-write subs on any valid fd report &lt;code&gt;ready&lt;/code&gt; immediately (the InMemoryFs never blocks). CLOCK subs with &lt;code&gt;timeout = 0&lt;/code&gt; or an ABSTIME target already in the past fire immediately; otherwise the host actually sleeps until the earliest deadline (&lt;code&gt;Thread.sleep&lt;/code&gt; on JVM/Native, busy-spin fallback on Scala.js). EINVAL for invalid clock ids or unknown event types (per-event), EBADF for unknown fds (per-event); EFAULT only for bounds-violating pointer args.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;sockets&quot;&gt;Sockets&lt;/h2&gt;
&lt;p&gt;WASI Preview 1 sockets follow the BSD-inetd model: there is no &lt;code&gt;sock_open&lt;/code&gt; / &lt;code&gt;sock_bind&lt;/code&gt; / &lt;code&gt;sock_listen&lt;/code&gt;. The host pre-binds listening sockets and hands them to the wasi program through &lt;code&gt;WasiContext.sockets&lt;/code&gt;. The i-th listening socket is exposed at fd &lt;code&gt;3 + preopens.length + i&lt;/code&gt;; &lt;code&gt;sock_accept&lt;/code&gt; resolves a listening fd and returns a fresh accepted-socket fd from the per-instance fd table. Accepted sockets also work through &lt;code&gt;fd_read&lt;/code&gt; / &lt;code&gt;fd_write&lt;/code&gt; (they implement &lt;code&gt;FsFile&lt;/code&gt; under the hood).&lt;/p&gt;
&lt;p&gt;JVM / Scala Native: &lt;code&gt;HostServerSocket.bind(port)&lt;/code&gt; factory wraps &lt;code&gt;java.net.ServerSocket&lt;/code&gt; (&lt;code&gt;port = 0&lt;/code&gt; for an OS-chosen ephemeral). Scala.js: synchronous accept can’t be expressed on the event loop, so the factory throws — a Node-backed program needs to supply its own &lt;code&gt;WasiContext.ServerSocket&lt;/code&gt; implementation.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Syscall&lt;/th&gt;&lt;th&gt;Behaviour&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;sock_accept(fd, fdflags, *out_fd)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Block until a client connects to the listening fd; install the connection in the fd table and write its new fd to &lt;code&gt;*out_fd&lt;/code&gt;. EBADF if the fd isn’t a listening socket.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;sock_recv(fd, ri_data, ri_data_len, ri_flags, *ro_datalen, *ro_flags)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Walk the iovec table reading from an accepted socket. &lt;code&gt;ri_flags&lt;/code&gt; accepts &lt;code&gt;RECV_PEEK (1)&lt;/code&gt; and &lt;code&gt;RECV_WAITALL (2)&lt;/code&gt; (the shim ignores both today). Returns ENOTSOCK on non-socket fds, EINVAL on unknown ri_flags bits, EFAULT on bounds-violating pointers.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;sock_send(fd, si_data, si_data_len, si_flags, *so_datalen)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Walk the iovec table writing to an accepted socket. &lt;code&gt;si_flags&lt;/code&gt; is reserved in Preview 1 — non-zero values return EINVAL.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;sock_shutdown(fd, how)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Half-close the read side (&lt;code&gt;SD_RD = 1&lt;/code&gt;), write side (&lt;code&gt;SD_WR = 2&lt;/code&gt;), or both (&lt;code&gt;SD_BOTH = 3&lt;/code&gt;). EINVAL on any other &lt;code&gt;how&lt;/code&gt; value. Idempotent: repeated shutdowns of the same side are not an error.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Programs that issue an unimplemented syscall get back &lt;code&gt;Wasi.ENOTSUP&lt;/code&gt; (52), which is the spec-conformant “host doesn’t support this”.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Spec compliance</title>
    <link href="https://edadma.github.io/wasm/reference/spec-compliance/"/>
    <id>https://edadma.github.io/wasm/reference/spec-compliance/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>Running the W3C WebAssembly testsuite against the interpreter — what&apos;s wired in, what&apos;s pinned as known-failing, and how to extend the slice.</summary>
    <content type="html">&lt;p&gt;The interpreter has an integrated runner for the official &lt;a href=&quot;https://github.com/WebAssembly/testsuite&quot;&gt;WebAssembly testsuite&lt;/a&gt;. It consumes the &lt;code&gt;.wast&lt;/code&gt; files, dispatches each &lt;code&gt;assert_return&lt;/code&gt; / &lt;code&gt;assert_trap&lt;/code&gt; / &lt;code&gt;assert_invalid&lt;/code&gt; / &lt;code&gt;assert_malformed&lt;/code&gt; command against &lt;code&gt;Runtime.instantiate&lt;/code&gt; + &lt;code&gt;inst.invoke&lt;/code&gt;, and tracks per-file pass / fail / skip totals.&lt;/p&gt;
&lt;p&gt;The slice that’s wired in covers &lt;strong&gt;142 manifests&lt;/strong&gt; — numerics, conversions, control flow, memory addressing, function pointers, the complete SIMD proposal, bulk memory + tables + element segments, the EH and tail-call proposals, plus binary-format and UTF-8 edge-case manifests — over &lt;strong&gt;~53,000 assertions&lt;/strong&gt;. The parser also handles the wasm-3.0 &lt;strong&gt;compact-imports&lt;/strong&gt; wire format (groups of imports sharing a module name, signaled by an empty field name plus kind byte &lt;code&gt;0x7E&lt;/code&gt; / &lt;code&gt;0x7F&lt;/code&gt;), so manifests that emit the compact form parse without special-casing.&lt;/p&gt;
&lt;h2 id=&quot;running-it&quot;&gt;Running it&lt;/h2&gt;
&lt;p&gt;The runner is JVM-only (it needs filesystem access for the manifest tree):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;interpJVM/Test/runMain io.github.edadma.wasm.spec.SpecComplianceTests&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Output is one line per manifest plus a totals line. Each file is tagged &lt;code&gt;OK&lt;/code&gt;, &lt;code&gt;KNOWN&lt;/code&gt; (failures expected and tolerated — see below), or &lt;code&gt;FAIL&lt;/code&gt; (failures that mean a regression):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;== W3C spec compliance ==
  OK    address                  259 pass,    0 fail,    1 skip
  OK    align                    119 pass,    0 fail,   46 skip
  OK    block                    208 pass,    0 fail,   15 skip
  ...
  KNOWN br_table                  24 pass,  162 fail,    0 skip
  ...
  OK    simd_i16x8_q15mulr_sat_s    30 pass,    0 fail,    0 skip
  ...
  OK    unwind                    50 pass,    0 fail,    0 skip

== Spec totals: 51714 passed, 223 failed, 1273 skipped (of 53210) ==
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exit code is non-zero iff at least one manifest is not &lt;code&gt;OK&lt;/code&gt; or &lt;code&gt;KNOWN&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;how-the-pipeline-works&quot;&gt;How the pipeline works&lt;/h2&gt;
&lt;p&gt;The testsuite is in &lt;code&gt;.wast&lt;/code&gt; source form. &lt;code&gt;wast2json&lt;/code&gt; (from &lt;a href=&quot;https://github.com/WebAssembly/wabt&quot;&gt;wabt&lt;/a&gt;) pre-processes each &lt;code&gt;.wast&lt;/code&gt; into a JSON command manifest plus a directory of per-module &lt;code&gt;.wasm&lt;/code&gt; binaries. The Scala runner consumes those — it never parses &lt;code&gt;.wast&lt;/code&gt; text itself, so the WAT parsing burden lives entirely outside the project.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;scripts/build-spec-tests.sh&lt;/code&gt; regenerates the manifest tree from a local checkout:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;clone&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;https://github.com/WebAssembly/testsuite&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;/tmp/wasm-testsuite&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;./scripts/build-spec-tests.sh&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;/tmp/wasm-testsuite&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script names the curated slice explicitly (a &lt;code&gt;FILES=(...)&lt;/code&gt; array). To add a manifest, append its name (without &lt;code&gt;.wast&lt;/code&gt;), re-run the script, and run the spec suite to see what surfaces.&lt;/p&gt;
&lt;h2 id=&quot;skipped-commands&quot;&gt;Skipped commands&lt;/h2&gt;
&lt;p&gt;Two categories of command are skipped, not run:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;assert_malformed&lt;/code&gt; / &lt;code&gt;assert_invalid&lt;/code&gt; with &lt;code&gt;module_type: &amp;quot;text&amp;quot;&lt;/code&gt;.&lt;/strong&gt; wast2json couldn’t binary-encode the module (intentionally malformed text). Our interpreter only accepts binary input, so there’s nothing to test. ~285 commands across the current slice fall here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Anything outside the dispatch ADT.&lt;/strong&gt; &lt;code&gt;register&lt;/code&gt; for cross-module imports, &lt;code&gt;assert_unlinkable&lt;/code&gt;, &lt;code&gt;assert_uninstantiable&lt;/code&gt;, etc. The current slice doesn’t emit those, but the runner skips rather than crashes if a future addition does.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Skipped commands count toward the totals line but don’t affect pass / fail status.&lt;/p&gt;
&lt;h2 id=&quot;known-failures&quot;&gt;Known failures&lt;/h2&gt;
&lt;p&gt;9 manifests are pinned in &lt;code&gt;SpecComplianceTests.KnownFailures&lt;/code&gt; — each one needs non-trivial implementation work beyond the surgical bug-fix pattern. They’re grouped by the feature gap they represent:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Function-references / GC proposals&lt;/strong&gt; (not on the roadmap):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Manifest&lt;/th&gt;&lt;th&gt;Why&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;br_table&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Module 0 uses &lt;code&gt;(ref null func)&lt;/code&gt; short form (wire byte &lt;code&gt;0x63&lt;/code&gt;).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;table-sub&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Same reftype short form.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;local_init&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;(ref func)&lt;/code&gt; non-null short form (&lt;code&gt;0x64&lt;/code&gt;).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;unreached-valid&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Function-references + typed reftype lookup.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;Residual gaps after compact-imports, imported memories/tables, and cross-module register all landed&lt;/strong&gt; (5 manifests):&lt;/p&gt;
&lt;p&gt;&lt;code&gt;names&lt;/code&gt;, &lt;code&gt;exports&lt;/code&gt;, &lt;code&gt;memory_grow&lt;/code&gt;, &lt;code&gt;table_copy&lt;/code&gt;, and &lt;code&gt;table_grow&lt;/code&gt; were unlocked by these features. The five remaining manifests each have a niche residual cause:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Manifest&lt;/th&gt;&lt;th&gt;Residual cause&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;data&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Two &lt;code&gt;assert_invalid&lt;/code&gt; corners the validator should catch but currently lets through&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;elem&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Mostly wasm-3.0 GC reftype short form &lt;code&gt;0x40&lt;/code&gt; in table sections (15 fails); 6 result-value mismatches on elem-segment edge cases&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;global&lt;/code&gt;&lt;/td&gt;&lt;td&gt;One wasm-3.0 GC reftype short form in a table section, plus 5 cascading “no current module”&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;imports&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Niche import-shape mismatches&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;linking&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;(ref heaptype)&lt;/code&gt; short forms &lt;code&gt;0x63&lt;/code&gt;/&lt;code&gt;0x64&lt;/code&gt; in element segments; cascade from earlier-failed modules (function-references proposal needed)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Fixing any of these will trip an “UNEXPECTED PASSES” warning until the manifest is removed from &lt;code&gt;KnownFailures.names&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;what-the-runner-caught&quot;&gt;What the runner caught&lt;/h2&gt;
&lt;p&gt;Triage across the initial run-up and eight coverage-expansion passes surfaced thirteen real interpreter bugs plus four feature gaps the runner unblocked:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;i32.trunc_f64_s&lt;/code&gt; over-rejected values strictly between &lt;code&gt;-2^31&lt;/code&gt; and &lt;code&gt;-2^31 - 1&lt;/code&gt;&lt;/strong&gt; (e.g. &lt;code&gt;-2147483648.9&lt;/code&gt;, which truncates to &lt;code&gt;INT_MIN&lt;/code&gt; and is in range). The range check was &lt;code&gt;v &amp;lt; -2^31&lt;/code&gt; where it should have been &lt;code&gt;v &amp;lt;= -2^31 - 1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;MemArg.offset&lt;/code&gt; was an &lt;code&gt;Int&lt;/code&gt;&lt;/strong&gt;, so a wasm u32 offset like &lt;code&gt;0xFFFFFFFF&lt;/code&gt; was stored as Java &lt;code&gt;-1&lt;/code&gt;. The Long sum &lt;code&gt;addr + offset&lt;/code&gt; then sign-extended, turning a guaranteed-OOB load into a wrap-to-low-memory load. Widening the field to &lt;code&gt;Long&lt;/code&gt; and masking on construction restores the trap.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;align&lt;/code&gt; immediate validation was missing for plain load / store.&lt;/strong&gt; The atomic path enforced &lt;code&gt;align == log2(natural-width)&lt;/code&gt; but &lt;code&gt;skipMemArg&lt;/code&gt; (the plain path) read the field and dropped it. A module with &lt;code&gt;i32.load8_s align=2&lt;/code&gt; (natural width 1, log2 = 0) instantiated successfully. Now enforced — every load/store opcode and every SIMD load/store carries an &lt;code&gt;accessWidth&lt;/code&gt; to &lt;code&gt;skipMemArg&lt;/code&gt;, which rejects &lt;code&gt;align &amp;gt; log2(width)&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;if&lt;/code&gt; without &lt;code&gt;else&lt;/code&gt; accepted mismatched params/results.&lt;/strong&gt; The form &lt;code&gt;if bt e* end&lt;/code&gt; (no else) has an implicit empty else-branch with type &lt;code&gt;[t1*] → [t1*]&lt;/code&gt;, so it only validates iff &lt;code&gt;startTypes == endTypes&lt;/code&gt;. We were silently accepting cases like &lt;code&gt;(if (result i32) (then (i32.const 0)))&lt;/code&gt; where the implicit else can’t satisfy the result. Now rejected at instantiation.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;v128.const&lt;/code&gt; (SIMD prefix 0xFD + sub 0x0C) wasn’t accepted in const expressions&lt;/strong&gt; — only scalar/ref const forms were recognised. &lt;code&gt;(global v128 (v128.const ...))&lt;/code&gt; and v128 data-segment offsets failed to parse. Const-expr reader now decodes the 16 raw bytes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Untyped &lt;code&gt;select&lt;/code&gt; (0x1B) rejected v128 operands.&lt;/strong&gt; The SIMD proposal treats v128 as a numtype for the purpose of &lt;code&gt;select&lt;/code&gt;; only the typed &lt;code&gt;select t*&lt;/code&gt; form (0x1C) is reserved for reftypes. Numeric predicate now includes v128.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;i8x16.popcnt&lt;/code&gt; (SIMD sub-opcode 0x62) was completely unimplemented&lt;/strong&gt; — we had abs (0x60) and neg (0x61) but jumped to 0x63 (all_true).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;i16x8.q15mulr_sat_s&lt;/code&gt; (SIMD sub-opcode 0x82) was completely unimplemented&lt;/strong&gt; — only the relaxed-SIMD variant (0x111) was present. Spec semantics: &lt;code&gt;(a*b + 0x4000) &amp;gt;&amp;gt; 15&lt;/code&gt;, saturated to i16 range.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;try_table&lt;/code&gt; catch labels counted with the try_table on the label stack.&lt;/strong&gt; Per the EH proposal, catch label indices count from the OUTER scope — the try_table is not yet on the label stack from the catch clause’s perspective. Both the validator (pushed the try_table frame before validating catches) and the runtime (didn’t pop the try_table label before &lt;code&gt;branchTo&lt;/code&gt;) had matching off-by-one errors. Fix moves the &lt;code&gt;pushCtrl&lt;/code&gt; after the catch-vector validation and adds a label-pop in the runtime’s throw-dispatch path.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Export-section validation was missing entirely.&lt;/strong&gt; Two spec rules went unenforced: (a) export names must be unique within a module (duplicate &lt;code&gt;(export &amp;quot;foo&amp;quot; ...)&lt;/code&gt; declarations were silently accepted, with the second shadowing the first); (b) each export’s index must be in range for its kind (a &lt;code&gt;funcidx&lt;/code&gt; past the imports+defs count, a &lt;code&gt;globalidx&lt;/code&gt; past the global section, etc. all instantiated). Added a single pass over &lt;code&gt;module.exports&lt;/code&gt; at the top of &lt;code&gt;Validator.validate&lt;/code&gt; that checks both invariants.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Name-field UTF-8 validation was missing.&lt;/strong&gt; Every &lt;code&gt;name&lt;/code&gt; byte sequence in the wire format (import module/field names, export names, custom-section ids, name-section subsections) must be valid UTF-8 per the spec. We were decoding via &lt;code&gt;new String(bytes, &amp;quot;UTF-8&amp;quot;)&lt;/code&gt; which silently maps malformed bytes to U+FFFD instead of rejecting. Added an RFC 3629 strict walker called from &lt;code&gt;Cursor.readName&lt;/code&gt; that rejects stray continuations, overlong forms (&lt;code&gt;0xC0&lt;/code&gt;/&lt;code&gt;0xC1&lt;/code&gt; and the overlong 3/4-byte variants), surrogate codepoints (U+D800..U+DFFF), values past U+10FFFF, lead bytes &lt;code&gt;0xF5&lt;/code&gt;..&lt;code&gt;0xFF&lt;/code&gt;, and truncated multi-byte sequences. Also let the diagnostic propagate from &lt;code&gt;parseCustomSection&lt;/code&gt; so the section name itself is enforced (was silently caught).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Binary-format strictness gaps&lt;/strong&gt; — seven small rules our parser was lax about, surfaced together by the &lt;code&gt;binary&lt;/code&gt;, &lt;code&gt;binary-leb128&lt;/code&gt;, and &lt;code&gt;custom&lt;/code&gt; manifests:
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;LEB128 range checks.&lt;/strong&gt; &lt;code&gt;readU32&lt;/code&gt; / &lt;code&gt;readS32&lt;/code&gt; / &lt;code&gt;readS64&lt;/code&gt; accepted oversize encodings: a 5-byte ULEB whose final byte had data bits past position 32 silently overflowed; signed forms were equally permissive on the sign-extension bits. Final byte now enforces the u32 / s32 / s64 width, with the spec’s distinct diagnostics for “integer too large” (value outside type range) and “integer representation too long” (more bytes than the type’s max).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Section ID range.&lt;/strong&gt; IDs outside 0..13 silently fell through &lt;code&gt;case _ =&amp;gt; ()&lt;/code&gt;. Now rejected as “malformed section id”.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Section order + uniqueness.&lt;/strong&gt; Non-custom sections must appear at most once and in canonical &lt;em&gt;logical&lt;/em&gt; order. The IDs aren’t monotonically ascending — Tag (13) is logically between Memory (5) and Global (6), and DataCount (12) is between Element (9) and Code (10). A small id → position table enforces both rules.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Section size mismatch.&lt;/strong&gt; &lt;code&gt;c.pos = secEnd&lt;/code&gt; after each section silently absorbed under- or over-consumed bytes; now each non-custom section must land exactly at &lt;code&gt;secEnd&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Custom section name overruns size.&lt;/strong&gt; A custom section with declared size 0 has no bytes for even the name-length prefix. We were reading past the section into the next one. &lt;code&gt;parseCustomSection&lt;/code&gt; now verifies the name read didn’t overshoot &lt;code&gt;secEnd&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Too many locals.&lt;/strong&gt; A function’s local-count groups summed past &lt;code&gt;2^32 - 1&lt;/code&gt; were accepted; even a single huge group (e.g. &lt;code&gt;count = 0x40000000&lt;/code&gt;) OOM-ed the allocation loop before the sum check could fire. Now the code reads all (count, type) groups first, sums in &lt;code&gt;Long&lt;/code&gt; arithmetic with overflow check, &lt;em&gt;then&lt;/em&gt; expands.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Imported globals + extended-const + relaxed const-expr&lt;/strong&gt; were not surfaced. The parser silently skipped import kind &lt;code&gt;0x03&lt;/code&gt;, every const-expr accepted only a single literal opcode, and &lt;code&gt;global.get&lt;/code&gt; in a const-expr was rejected outright. Three spec features that travel together now land as one drop: (a) &lt;code&gt;GlobalImport&lt;/code&gt; joins &lt;code&gt;FuncImport&lt;/code&gt; / &lt;code&gt;TagImport&lt;/code&gt; in the module model, surfaces at instantiation via a new &lt;code&gt;HostGlobal&lt;/code&gt; resolved against &lt;code&gt;HostModule.globals&lt;/code&gt;, and occupies the leading slots of the unified globalidx space; (b) the const-expr reader is now a small stack-machine parser that flattens &lt;code&gt;iN.add&lt;/code&gt; / &lt;code&gt;iN.sub&lt;/code&gt; / &lt;code&gt;iN.mul&lt;/code&gt; (extended-const proposal) into an expression tree the validator and runtime evaluate recursively; (c) the validator allows &lt;code&gt;global.get N&lt;/code&gt; over any earlier-defined immutable global (wasm-3.0 relaxation), with forward-reference and mutability rejection. Active data / element segment offsets accept the same const-expr forms. The wasm-3.0 testsuite’s “compact-imports” wire format extension is a separate proposal and remains pinned.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All thirteen ship with regression tests in &lt;code&gt;NumericTests&lt;/code&gt;, &lt;code&gt;MemoryTests&lt;/code&gt;, &lt;code&gt;MultiValueAndStartTests&lt;/code&gt;, &lt;code&gt;SimdIntArithTests&lt;/code&gt;, &lt;code&gt;SimdConstTests&lt;/code&gt;, &lt;code&gt;TryTableTests&lt;/code&gt;, and &lt;code&gt;ParserAndRuntimeTests&lt;/code&gt;.&lt;/p&gt;
&lt;ol start=&quot;14&quot;&gt;
&lt;li&gt;&lt;strong&gt;Compact-imports wire format.&lt;/strong&gt; The wasm-3.0 testsuite emits a compact import-section encoding where a regular-looking import with &lt;code&gt;field_name == &amp;quot;&amp;quot;&lt;/code&gt; and a kind byte of &lt;code&gt;0x7E&lt;/code&gt; (shared-kind) or &lt;code&gt;0x7F&lt;/code&gt; (per-import-kind) signals that the just-read &lt;code&gt;mod_name&lt;/code&gt; is shared across a group of sub-imports. The 0x7E form is &lt;code&gt;kind sub_count (field_name desc)*&lt;/code&gt;; the 0x7F form is &lt;code&gt;sub_count (field_name kind desc)*&lt;/code&gt;. The outer &lt;code&gt;count&lt;/code&gt; field in the import-section header is the TOTAL number of imports across all groups, not the count of groups — so the parser advances &lt;code&gt;i&lt;/code&gt; by &lt;code&gt;sub_count&lt;/code&gt; per compact group. Without this, modules using the compact form failed at parse time with &lt;code&gt;unknown import kind 0x7F&lt;/code&gt;. The &lt;code&gt;names&lt;/code&gt; manifest was fully unlocked by this fix; eight other manifests gated on compact-imports now decode but still hit secondary residual gaps (imported memories / tables and cross-module register).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Imported memories + tables.&lt;/strong&gt; Parser silently skipped import kinds &lt;code&gt;0x01&lt;/code&gt; (table) and &lt;code&gt;0x02&lt;/code&gt; (memory); the module then failed when an instruction referenced a memidx or tableidx with “no memory / no table”. &lt;code&gt;MemoryImport&lt;/code&gt; and &lt;code&gt;TableImport&lt;/code&gt; now surface in the model alongside &lt;code&gt;GlobalImport&lt;/code&gt;; the runtime resolves them from &lt;code&gt;HostModule.memories: Map[String, Memory]&lt;/code&gt; and &lt;code&gt;HostModule.tables: Map[String, RuntimeTable]&lt;/code&gt; and prepends them to the live memories/tables arrays. Type checks: host’s current size ≥ module’s declared min, host’s max (if any) ≤ module’s declared max (if any), reftype match for tables, shared-vs-unshared match for memories. Four manifests fully unlocked: &lt;code&gt;exports&lt;/code&gt;, &lt;code&gt;memory_grow&lt;/code&gt;, &lt;code&gt;table_copy&lt;/code&gt;, &lt;code&gt;table_grow&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Cross-module &lt;code&gt;register&lt;/code&gt; in the spec runner.&lt;/strong&gt; wast2json emits &lt;code&gt;(register &amp;quot;Mf&amp;quot; $Mf)&lt;/code&gt; commands that bind a previously-loaded module to a host name so later modules can import from it. The runner now tracks two registries (&lt;code&gt;namedModules&lt;/code&gt; for action-targeted invokes, &lt;code&gt;registered&lt;/code&gt; for import resolution); the &lt;code&gt;Module&lt;/code&gt; command optionally binds a &lt;code&gt;$name&lt;/code&gt;; the &lt;code&gt;Register&lt;/code&gt; command picks a target by &lt;code&gt;$name&lt;/code&gt; (or current) and adds it to the import registry. &lt;code&gt;wrapAsHostModule&lt;/code&gt; builds a &lt;code&gt;HostModule&lt;/code&gt; from a &lt;code&gt;ModuleInstance&lt;/code&gt;‘s exports — exported functions forward through &lt;code&gt;inst.invoke&lt;/code&gt; with traps re-thrown as &lt;code&gt;ExecFail&lt;/code&gt; so the calling interpreter resurfaces them as &lt;code&gt;Left&lt;/code&gt;, and memories / tables / globals forward by reference.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shared mutable globals across module boundaries.&lt;/strong&gt; Module storage for globals is now &lt;code&gt;Array[GlobalCell]&lt;/code&gt; instead of &lt;code&gt;Array[Value]&lt;/code&gt;. A &lt;code&gt;GlobalCell&lt;/code&gt; is a tiny mutable holder; an imported mutable global installs the &lt;em&gt;exporter’s&lt;/em&gt; cell directly into the importing module’s slot, so &lt;code&gt;global.set&lt;/code&gt; from either side writes through the same storage — matching the wasm-3.0 spec’s “imported mutable globals are aliases for the exporter’s storage” rule. The host-import surface gains &lt;code&gt;HostGlobal.live(vt, mut, cell)&lt;/code&gt; for sharing externally-owned cells; the existing &lt;code&gt;HostGlobal(vt, mut, value)&lt;/code&gt; factory still works for the snapshot case (immutable globals + mutable globals the host doesn’t need to observe). &lt;code&gt;ModuleInstance.exportedGlobalCell(name)&lt;/code&gt; is the public accessor the spec runner uses when wrapping one module’s exports as another module’s imports. The &lt;code&gt;linking&lt;/code&gt; manifest’s mutable-global tests now exercise the shared-storage path correctly; residual &lt;code&gt;linking&lt;/code&gt; failures are wasm-3.0 GC reftype short forms, unrelated.&lt;/li&gt;
&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>Recipes</title>
    <link href="https://edadma.github.io/wasm/cli/recipes/"/>
    <id>https://edadma.github.io/wasm/cli/recipes/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>Hand-written WAT, rustc-built WASI binaries, native CLI builds.</summary>
    <content type="html">&lt;h2 id=&quot;hand-written-wat-no-wasi&quot;&gt;Hand-written WAT, no WASI&lt;/h2&gt;
&lt;p&gt;The committed &lt;code&gt;examples/hello.wasm&lt;/code&gt; is a hand-written WAT module that calls &lt;code&gt;env.putchar&lt;/code&gt; 13 times. No WASI imports, no preopens needed:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;cliJVM/run examples/hello.wasm&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;
# Hello, world!&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The matching &lt;code&gt;.wat&lt;/code&gt; source is at &lt;code&gt;examples/hello.wat&lt;/code&gt;. After editing, regenerate the binary with &lt;code&gt;wat2wasm&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;wat2wasm&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;examples/hello.wat&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;examples/hello.wasm&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;freestanding-c-no-libc&quot;&gt;Freestanding C, no libc&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;examples/c/hello.wasm&lt;/code&gt; is a freestanding C program — no libc, no wasi-sdk. The source declares &lt;code&gt;fd_write&lt;/code&gt; directly as a wasi import and defines &lt;code&gt;_start&lt;/code&gt; as the entry point; the build is just &lt;code&gt;clang --target=wasm32 -nostdlib&lt;/code&gt; plus &lt;code&gt;wasm-ld&lt;/code&gt;. Useful for understanding what a wasi binary actually is once you strip the libc convenience layer off.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;cliJVM/run examples/c/hello.wasm&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;
# Hello from freestanding C!&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The matching &lt;code&gt;examples/c/hello.c&lt;/code&gt; source and &lt;code&gt;Makefile&lt;/code&gt; are committed alongside; see &lt;a href=&quot;https://github.com/edadma/wasm/tree/dev/examples/c&quot;&gt;&lt;code&gt;examples/c/README.md&lt;/code&gt;&lt;/a&gt; for the build invocation.&lt;/p&gt;
&lt;h2 id=&quot;c-with-wasi-libc-stdio-h-argv-exit-codes&quot;&gt;C with wasi-libc (&lt;code&gt;&amp;lt;stdio.h&amp;gt;&lt;/code&gt;, argv, exit codes)&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;examples/c-libc/hexdump.c&lt;/code&gt; is the smallest “real C program” target: it uses &lt;code&gt;&amp;lt;stdio.h&amp;gt;&lt;/code&gt; (&lt;code&gt;fopen&lt;/code&gt;/&lt;code&gt;fread&lt;/code&gt;/&lt;code&gt;printf&lt;/code&gt;/&lt;code&gt;fprintf(stderr, ...)&lt;/code&gt;), reads its path argument from &lt;code&gt;argv[1]&lt;/code&gt;, and returns an exit code from &lt;code&gt;main&lt;/code&gt;. The build requires &lt;a href=&quot;https://github.com/WebAssembly/wasi-sdk&quot;&gt;wasi-sdk&lt;/a&gt; — the freestanding example above doesn’t, but anything that touches libc does.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-comment&quot;&gt;# Install wasi-sdk first, then:&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;#&lt;/span&gt; Install wasi-sdk first, then:
&lt;span class=&quot;hl-function&quot;&gt;make&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;-C&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;examples/c-libc&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;cliJVM/run --preopen /sandbox:./fixtures -- \
                examples/c-libc/hexdump.wasm /sandbox/data.bin&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;
# 00000000: 48 65 6c 6c 6f 0a                              |Hello.|&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;.wasm&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; committed (wasi-sdk isn’t free to install everywhere); see &lt;a href=&quot;https://github.com/edadma/wasm/tree/dev/examples/c-libc&quot;&gt;&lt;code&gt;examples/c-libc/README.md&lt;/code&gt;&lt;/a&gt; for the build invocation.&lt;/p&gt;
&lt;h2 id=&quot;real-wasi-binary-with-a-host-backed-preopen&quot;&gt;Real WASI binary with a host-backed preopen&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;examples/rust/word_count.wasm&lt;/code&gt; is a rustc-built &lt;code&gt;wasm32-wasip1&lt;/code&gt; binary that reads a path passed as &lt;code&gt;argv[1]&lt;/code&gt; from a wasi preopen and prints &lt;code&gt;wc -lwc&lt;/code&gt;-style counts:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;./data&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;echo&lt;/span&gt; &amp;quot;The quick brown fox jumps over the lazy dog.&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt; &amp;gt; ./data/input.txt
echo &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt;Pack my box with five dozen liquor jugs.&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt;    &amp;gt;&amp;gt; ./data/input.txt

sbt &apos;cliJVM/run --preopen ./data:/data examples/rust/word_count.wasm /data/input.txt&apos;
#        2       17       86 /data/input.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;--preopen&lt;/code&gt; flag points the wasi-libc startup walk at the host’s &lt;code&gt;./data&lt;/code&gt; directory and tells the guest the visible name is &lt;code&gt;/data&lt;/code&gt;. The trailing &lt;code&gt;/data/input.txt&lt;/code&gt; is forwarded to the program as &lt;code&gt;argv[1]&lt;/code&gt; — wasm-cli treats any positional args after the wasm file as WASI argv. From the rust binary’s perspective, &lt;code&gt;/data/input.txt&lt;/code&gt; resolves; absolute paths outside &lt;code&gt;/data&lt;/code&gt; don’t.&lt;/p&gt;
&lt;p&gt;The matching &lt;code&gt;examples/rust/src/main.rs&lt;/code&gt; source and &lt;code&gt;Cargo.toml&lt;/code&gt; are committed; rebuild with &lt;code&gt;cargo build --release --target=wasm32-wasip1 --manifest-path=examples/rust/Cargo.toml&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;real-wasi-binary-with-file-writes&quot;&gt;Real WASI binary with file writes&lt;/h2&gt;
&lt;p&gt;The test-suite fixture &lt;code&gt;real_rust_filewrite.wasm&lt;/code&gt; writes a small file under its preopen — useful for confirming write capability against a host directory:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;rm&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;-rf&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;/tmp/wasm-write&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;mkdir&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;-p&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;/tmp/wasm-write&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;cliJVM/run --preopen /tmp/wasm-write:/sandbox \
                wasi/shared/src/test/resources/fixtures/real_rust_filewrite.wasm&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;
cat /tmp/wasm-write/output.txt&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The path-sandbox rules apply: the rust binary asking for &lt;code&gt;/sandbox/output.txt&lt;/code&gt; writes to &lt;code&gt;/tmp/wasm-write/output.txt&lt;/code&gt;; the same binary asking for &lt;code&gt;../escape&lt;/code&gt; returns &lt;code&gt;ENOTCAPABLE&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;native-cli-binary&quot;&gt;Native CLI binary&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;cliNative&lt;/code&gt; builds a standalone Scala Native binary — no JVM startup overhead, useful when you want to run &lt;code&gt;wasm&lt;/code&gt; in a shell pipeline:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;cliNative/nativeLink&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;./cli/native/target/scala-3.8.3/wasm-cli-out&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;hl-function&quot;&gt;--preopen&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;/tmp/sandbox:/sandbox&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;\&lt;/span&gt;
    &lt;span class=&quot;hl-function&quot;&gt;real_rust_fileread.wasm&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Scala Native build uses the same &lt;code&gt;HostPreopen.fromDir&lt;/code&gt; shape as the JVM build but backs it with &lt;code&gt;java.nio.file&lt;/code&gt; (via Scala Native’s JVM-compatibility layer).&lt;/p&gt;
&lt;h2 id=&quot;scala-js-cli-node-js&quot;&gt;Scala.js CLI (Node.js)&lt;/h2&gt;
&lt;p&gt;sbt’s command-line parser doesn’t forward positional args to &lt;code&gt;scalajs&lt;/code&gt;‘s &lt;code&gt;run&lt;/code&gt; task, so the JS CLI runs through Node directly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;cliJS/fastLinkJS&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;node&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;cli/js/target/scala-3.8.3/wasm-cli-fastopt/main.js&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;hl-function&quot;&gt;--preopen&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;/tmp/sandbox:/sandbox&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;\&lt;/span&gt;
     &lt;span class=&quot;hl-function&quot;&gt;real_rust_fileread.wasm&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The Scala.js build backs &lt;code&gt;HostPreopen.fromDir&lt;/code&gt; with Node’s &lt;code&gt;fs.*Sync&lt;/code&gt; APIs. Same trait surface, different syscalls underneath.&lt;/p&gt;
&lt;h2 id=&quot;inspecting-a-module-without-running-it&quot;&gt;Inspecting a module without running it&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;--list-exports&lt;/code&gt; prints every exported function without instantiating:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;cliJVM/run --list-exports my-module.wasm&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Useful when picking an &lt;code&gt;--invoke&lt;/code&gt; target, or when sanity-checking that a build pipeline produced the symbols you expect.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Quickstart</title>
    <link href="https://edadma.github.io/wasm/getting-started/quickstart/"/>
    <id>https://edadma.github.io/wasm/getting-started/quickstart/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>Instantiate a module, invoke an export, run a WASI command-mode binary — the three things every caller does.</summary>
    <content type="html">&lt;p&gt;This walkthrough shows the three things you’ll do with the library 95 % of the time: instantiate a non-WASI module and invoke a function, run a WASI command-mode binary, and customize the host environment.&lt;/p&gt;
&lt;h2 id=&quot;1-instantiate-and-invoke&quot;&gt;1. Instantiate and invoke&lt;/h2&gt;
&lt;p&gt;The minimal case — a module with no WASI imports, exporting one function you’d like to call:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; bytes&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Byte&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-comment&quot;&gt;/* contents of a .wasm file */&lt;/span&gt;

&lt;span class=&quot;hl-type&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;instantiate&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bytes&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;EnvModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;inst&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
    inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;fact&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;fact(5) = $v&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;other&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;       &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;unexpected result: $other&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;          &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;trap: $err&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
    println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;failed to instantiate: $err&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;EnvModule.default&lt;/code&gt; ships a single &lt;code&gt;env.putchar(i32) → ()&lt;/code&gt; import that writes the low byte to &lt;code&gt;System.out&lt;/code&gt;. Modules compiled by lightweight toolchains (hand-written &lt;code&gt;.wat&lt;/code&gt;, Emscripten with &lt;code&gt;-s STANDALONE_WASM=0&lt;/code&gt;) often expect it; modules that don’t import &lt;code&gt;env.putchar&lt;/code&gt; are unaffected.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Runtime.instantiate&lt;/code&gt; validates the binary before any code runs. Bad modules return &lt;code&gt;Left(InvalidModule(&amp;quot;function &amp;lt;N&amp;gt;: byte offset 0x&amp;lt;hex&amp;gt;: &amp;lt;details&amp;gt;&amp;quot;))&lt;/code&gt; — see &lt;a href=&quot;/wasm/concepts/validation/&quot;&gt;Concepts → Validation&lt;/a&gt; for what the validator checks.&lt;/p&gt;
&lt;h2 id=&quot;2-run-a-wasi-command-mode-binary&quot;&gt;2. Run a WASI command-mode binary&lt;/h2&gt;
&lt;p&gt;For real &lt;code&gt;wasm32-wasip1&lt;/code&gt; binaries — anything compiled by rustc, zig, or Clang’s WASI sysroot — you want &lt;code&gt;Wasi.run&lt;/code&gt;, which handles &lt;code&gt;_start&lt;/code&gt;, command-line args, environment variables, preopens, and &lt;code&gt;proc_exit&lt;/code&gt; for you:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasi&lt;span class=&quot;hl-punctuation&quot;&gt;.{&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Wasi&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostPreopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; bytes&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Byte&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-comment&quot;&gt;/* a rustc-built wasm32-wasip1 binary */&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;copy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  args     &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;myprog&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;--flag&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;value&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  envs     &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;HOME&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/root&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;LANG&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;C.UTF-8&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  preopens &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;HostPreopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;fromDir&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/var/data&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/data&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-type&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;instantiate&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bytes&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;EnvModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Wasi&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;preview1&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;inst&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Wasi&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;run&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;inst&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;_start&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; sys&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;exit&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;runtime error: $err&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;);&lt;/span&gt; sys&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;exit&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;instantiate failed: $err&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;);&lt;/span&gt; sys&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;exit&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Clean exit returns &lt;code&gt;Right(0)&lt;/code&gt;. A &lt;code&gt;proc_exit(N)&lt;/code&gt; call returns &lt;code&gt;Right(N)&lt;/code&gt;. A trap returns &lt;code&gt;Left(WasmError)&lt;/code&gt;. No thrown exceptions cross the API boundary.&lt;/p&gt;
&lt;h2 id=&quot;3-customize-the-host-environment&quot;&gt;3. Customize the host environment&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;WasiContext&lt;/code&gt; is a plain case class — copy-and-modify the bits you care about:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasi&lt;span class=&quot;hl-punctuation&quot;&gt;.{&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostPreopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Preopen&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;copy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  args     &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;prog&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;input.txt&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  envs     &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;PATH&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/usr/bin&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-comment&quot;&gt;// Three preopen flavours, mix and match:&lt;/span&gt;
  preopens &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;HostPreopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;fromDir&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/tmp/sandbox&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/sandbox&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;                    &lt;span class=&quot;hl-comment&quot;&gt;// real host directory&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Preopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;inMemory&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/cfg&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;                                            &lt;span class=&quot;hl-comment&quot;&gt;// in-memory map&lt;/span&gt;
      &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;settings.toml&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;verbose = true&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;getBytes&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;hl-type&quot;&gt;Preopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;named&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/devices&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;                                          &lt;span class=&quot;hl-comment&quot;&gt;// probe-only stub&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  &lt;span class=&quot;hl-comment&quot;&gt;// Inject your own clock or randomness for deterministic tests:&lt;/span&gt;
  clock    &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;systemClock&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;                                   &lt;span class=&quot;hl-comment&quot;&gt;// or your own `Clock` impl&lt;/span&gt;
  random   &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;fill&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0x42&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;toByte&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;                    &lt;span class=&quot;hl-comment&quot;&gt;// not random at all — fine for tests&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;HostPreopen.fromDir&lt;/code&gt; is sandboxed by construction: absolute paths from the guest, NUL bytes, and &lt;code&gt;..&lt;/code&gt; segments are all rejected at the &lt;code&gt;path_open&lt;/code&gt; boundary. See &lt;a href=&quot;/wasm/wasi/preopens/&quot;&gt;WASI → Preopens&lt;/a&gt; for the full sandboxing model.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/concepts/module-instance/&quot;&gt;Concepts → ModuleInstance&lt;/a&gt; — the full public API on what &lt;code&gt;Runtime.instantiate&lt;/code&gt; returns.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/concepts/validation/&quot;&gt;Concepts → Validation&lt;/a&gt; — what the validator catches and what it doesn’t.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/concepts/host-imports/&quot;&gt;Concepts → Host imports&lt;/a&gt; — wire your own functions into a module.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/wasi/context/&quot;&gt;WASI → WasiContext&lt;/a&gt; — the configuration record &lt;code&gt;Wasi.preview1&lt;/code&gt; takes.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/wasi/syscalls/&quot;&gt;WASI → Syscalls&lt;/a&gt; — the 29 syscalls implemented and their semantics.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/cli/&quot;&gt;CLI&lt;/a&gt; — when you want a runner more than a library.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/reference/errors/&quot;&gt;Reference → Errors&lt;/a&gt; — every &lt;code&gt;WasmError&lt;/code&gt; variant and what causes it.&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Preopens</title>
    <link href="https://edadma.github.io/wasm/wasi/preopens/"/>
    <id>https://edadma.github.io/wasm/wasi/preopens/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>The `WasiContext.Preopen` trait, its three factories, and the path-sandboxing model.</summary>
    <content type="html">&lt;p&gt;A WASI program can’t open files by absolute path. wasi-libc walks the fds 3, 4, … at program startup, asks the host for the name of each preopened directory, and builds an internal &lt;code&gt;name → fd&lt;/code&gt; map; everything later that calls &lt;code&gt;open(&amp;quot;/sandbox/hello.txt&amp;quot;, …)&lt;/code&gt; is rewritten by libc into &lt;code&gt;path_open(dirfd=3, &amp;quot;hello.txt&amp;quot;, …)&lt;/code&gt;. The host never sees an absolute path from the guest.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;WasiContext.preopens: Seq[Preopen]&lt;/code&gt; is the list the host advertises. The i-th entry shows up at fd &lt;code&gt;3 + i&lt;/code&gt;. Three factories:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Factory&lt;/th&gt;&lt;th&gt;Backing&lt;/th&gt;&lt;th&gt;What it does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;Preopen.named(name)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;none&lt;/td&gt;&lt;td&gt;Advertises the name; every &lt;code&gt;path_open&lt;/code&gt; against it returns &lt;code&gt;ENOTCAPABLE&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;Preopen.inMemory(name, files)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;Map[String, Array[Byte]]&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Full read/write semantics — &lt;code&gt;CREAT&lt;/code&gt;, &lt;code&gt;EXCL&lt;/code&gt;, &lt;code&gt;TRUNC&lt;/code&gt;, unlink, mkdir, readdir. What the test suite uses.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;HostPreopen.fromDir(hostPath, virtualName)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;real on-disk directory&lt;/td&gt;&lt;td&gt;Sandboxed access to an actual host directory. JVM and Scala Native use &lt;code&gt;java.nio.file&lt;/code&gt; + &lt;code&gt;FileChannel&lt;/code&gt;; Scala.js uses Node’s &lt;code&gt;fs.*Sync&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Programs see the same wasi-preview1 surface no matter which kind is in play — only the host distinguishes them.&lt;/p&gt;
&lt;h2 id=&quot;named-preopens&quot;&gt;Named preopens&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;copy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  preopens &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Preopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;named&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/etc&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Preopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;named&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/var&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;Preopen.named&lt;/code&gt; is the minimum surface: it makes &lt;code&gt;fd_prestat_get(3)&lt;/code&gt; succeed and report the name, and that’s it. &lt;code&gt;path_open&lt;/code&gt; against fd 3 returns &lt;code&gt;ENOTCAPABLE&lt;/code&gt; (not &lt;code&gt;ENOENT&lt;/code&gt; — the distinction matters: ENOENT says “no such path”, ENOTCAPABLE says “you can’t even ask through this preopen”). It’s the right choice when you want to expose a directory’s &lt;em&gt;existence&lt;/em&gt; to libc startup probing without giving the guest the capability to read it.&lt;/p&gt;
&lt;h2 id=&quot;in-memory-preopens&quot;&gt;In-memory preopens&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; files &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;hello.txt&amp;quot;&lt;/span&gt;        &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Hello from memory&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;getBytes&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;config/site.toml&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;title = &lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;\&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;test&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;\&amp;quot;\n&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;getBytes&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; sandbox &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Preopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;inMemory&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/sandbox&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; files&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;copy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;preopens &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;sandbox&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The backing &lt;code&gt;Map[String, Array[Byte]]&lt;/code&gt; is mutable behind the scenes — &lt;code&gt;path_open(..., OFLAGS_CREAT, ...)&lt;/code&gt; adds new entries, &lt;code&gt;path_unlink_file&lt;/code&gt; drops them, &lt;code&gt;OFLAGS_TRUNC&lt;/code&gt; zeroes the bytes. After the guest runs, the concrete &lt;code&gt;InMemoryPreopen&lt;/code&gt; returned by &lt;code&gt;Preopen.inMemory&lt;/code&gt; exposes &lt;code&gt;bytesOf(path)&lt;/code&gt; so tests can inspect what was written:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; sandbox &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Preopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;inMemory&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/sandbox&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;              &lt;span class=&quot;hl-comment&quot;&gt;// empty to start&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;// ... run the guest, which writes &amp;quot;output.bin&amp;quot; ...&lt;/span&gt;
sandbox&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;bytesOf&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;output.bin&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;hl-comment&quot;&gt;// → Some(Array[Byte](...))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In-memory preopens never touch the host filesystem and never block on I/O — they’re the right choice for unit tests, fuzzing, and any scenario where guest writes shouldn’t escape the process.&lt;/p&gt;
&lt;h2 id=&quot;host-directory-preopens&quot;&gt;Host-directory preopens&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasi&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;HostPreopen&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;copy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  preopens &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;HostPreopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;fromDir&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/var/data&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/data&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;HostPreopen.fromDir(hostPath, virtualName)&lt;/code&gt; resolves &lt;code&gt;hostPath&lt;/code&gt; to a real &lt;code&gt;Path&lt;/code&gt; and uses it as the root. Every guest path is resolved against it through the wasi-preview1 path-sandbox rules:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Absolute paths from the guest are rejected outright (would-be roots like &lt;code&gt;/etc/passwd&lt;/code&gt; never reach the host filesystem).&lt;/li&gt;
&lt;li&gt;NUL bytes in the guest-supplied path are rejected (defense against malformed strings).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;..&lt;/code&gt; segments that would escape the root after normalization are rejected with &lt;code&gt;ENOTCAPABLE&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Symlinks inside the directory are followed by the host filesystem; following one &lt;em&gt;out&lt;/em&gt; of the root re-triggers the path-escape check on the resolved target.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The three backends (JVM, Native, JS) differ only in how they implement &lt;code&gt;path_open&lt;/code&gt; and the read/write/stat/readdir syscalls under the hood. The trait surface is identical; the path-sandbox rules are enforced before any platform-specific code runs.&lt;/p&gt;
&lt;h2 id=&quot;picking-a-preopen-kind&quot;&gt;Picking a preopen kind&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;HostPreopen.fromDir&lt;/code&gt;&lt;/strong&gt; — production use. The guest gets real persistent storage in a controlled subtree of the host filesystem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Preopen.inMemory&lt;/code&gt;&lt;/strong&gt; — tests and any case where the guest’s writes need to be inspectable from Scala without crossing the filesystem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Preopen.named&lt;/code&gt;&lt;/strong&gt; — when a guest probes preopens for capability discovery and you want to expose the &lt;em&gt;name&lt;/em&gt; but not the contents.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can mix them freely. The order of &lt;code&gt;preopens&lt;/code&gt; in &lt;code&gt;WasiContext&lt;/code&gt; determines the fd ordering — preopen &lt;code&gt;[0]&lt;/code&gt; is fd 3, preopen &lt;code&gt;[1]&lt;/code&gt; is fd 4, and so on.&lt;/p&gt;
&lt;h2 id=&quot;the-preopen-trait-surface&quot;&gt;The &lt;code&gt;Preopen&lt;/code&gt; trait surface&lt;/h2&gt;
&lt;p&gt;If the three factories don’t fit and you want to implement a custom preopen — say, an S3-backed read-only mount or an HTTP-fetching probe — you can subclass &lt;code&gt;WasiContext.Preopen&lt;/code&gt; directly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Preopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-comment&quot;&gt;/** wasi-visible directory name (what userspace sees through `fd_prestat_dir_name`). */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Open a path relative to this preopen. Called from `path_open`.&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * Default returns Left(ENOTCAPABLE). */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; open&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; oflags&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; fdflags&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Either&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Wasi&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;FsFile&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Stat a path WITHOUT opening it. Called from `path_filestat_get`.&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * Right(size) for an existing regular file; Left(errno) otherwise.&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * Default returns Left(ENOTCAPABLE). */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; statPath&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Either&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Long&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Three things to know:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;Wasi.FsFile&lt;/code&gt; is the file-handle trait&lt;/strong&gt; — &lt;code&gt;read&lt;/code&gt; / &lt;code&gt;write&lt;/code&gt; / &lt;code&gt;seek&lt;/code&gt; / &lt;code&gt;size&lt;/code&gt; / &lt;code&gt;tell&lt;/code&gt; / &lt;code&gt;close&lt;/code&gt;. The shim never sees a Java &lt;code&gt;File&lt;/code&gt; or a &lt;code&gt;Path&lt;/code&gt;; it talks to your &lt;code&gt;FsFile&lt;/code&gt;. The &lt;code&gt;InMemoryPreopen&lt;/code&gt; and &lt;code&gt;HostBackedPreopen&lt;/code&gt; impls are reference implementations you can read in the source.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Several methods are &lt;code&gt;private[wasi]&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;unlinkPath&lt;/code&gt;, &lt;code&gt;mkdir&lt;/code&gt;, &lt;code&gt;readdir&lt;/code&gt;, &lt;code&gt;filetypeOf&lt;/code&gt;). External impls inherit their &lt;code&gt;ENOTCAPABLE&lt;/code&gt; / empty defaults but can’t override them — write/enumerate semantics for custom preopens are intentionally limited to what the trait surface above allows. If you genuinely need to override one of those, the customization belongs upstream as a new factory rather than as a downstream subclass.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;oflags&lt;/code&gt; / &lt;code&gt;fdflags&lt;/code&gt; are passed through verbatim.&lt;/strong&gt; The &lt;code&gt;OFLAGS_CREAT&lt;/code&gt; (1), &lt;code&gt;OFLAGS_DIRECTORY&lt;/code&gt; (2), &lt;code&gt;OFLAGS_EXCL&lt;/code&gt; (4), &lt;code&gt;OFLAGS_TRUNC&lt;/code&gt; (8) bits and the &lt;code&gt;FDFLAGS_*&lt;/code&gt; set are documented in &lt;code&gt;wasi-preview1&lt;/code&gt;. Read-only impls ignore them.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the common case — read/write semantics over an in-memory map, or sandboxed access to a real directory — reach for &lt;code&gt;Preopen.inMemory&lt;/code&gt; and &lt;code&gt;HostPreopen.fromDir&lt;/code&gt; instead. They cover what most callers need.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Opcodes</title>
    <link href="https://edadma.github.io/wasm/reference/opcodes/"/>
    <id>https://edadma.github.io/wasm/reference/opcodes/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>Every WebAssembly opcode group the interpreter handles, plus what&apos;s intentionally out of scope.</summary>
    <content type="html">&lt;p&gt;The interpreter implements every WebAssembly Core opcode plus the sign-extension proposal, the full bulk-memory proposal, non-trapping float-to-int (&lt;code&gt;trunc_sat_*&lt;/code&gt;), the reference-types proposal (funcref, externref, &lt;code&gt;ref.null&lt;/code&gt; / &lt;code&gt;ref.is_null&lt;/code&gt; / &lt;code&gt;ref.func&lt;/code&gt;, &lt;code&gt;table.get&lt;/code&gt; / &lt;code&gt;table.set&lt;/code&gt; / &lt;code&gt;table.size&lt;/code&gt; / &lt;code&gt;table.grow&lt;/code&gt; / &lt;code&gt;table.fill&lt;/code&gt;, typed &lt;code&gt;select t*&lt;/code&gt;), the multi-memory proposal (every memory opcode now carries a memidx; modules may declare more than one linear memory, with a parallel &lt;code&gt;HostFuncMulti&lt;/code&gt; surface for host functions that need to reach beyond memidx 0), and &lt;strong&gt;the full SIMD proposal&lt;/strong&gt; (&lt;code&gt;V128&lt;/code&gt; value type plumbed end-to-end; all ~236 opcodes under the &lt;code&gt;0xFD&lt;/code&gt; prefix — &lt;code&gt;v128.const&lt;/code&gt;, the 14 loads + 8 stores including &lt;code&gt;load*_lane&lt;/code&gt; / &lt;code&gt;store*_lane&lt;/code&gt;, lane access, integer + float arithmetic, shifts, min/max, bitwise + reductions, comparisons, narrow / extend / extadd_pairwise / extmul, float ↔ int conv, demote / promote, and &lt;code&gt;i32x4.dot_i16x8_s&lt;/code&gt;). That’s enough to run real &lt;code&gt;wasm32-wasip1&lt;/code&gt; binaries produced by rustc end-to-end, and to host the full sysl standard-library test suite end-to-end as sysl’s &lt;code&gt;wasm32-WASI&lt;/code&gt; backend.&lt;/p&gt;
&lt;h2 id=&quot;numeric-all-four-scalar-types&quot;&gt;Numeric (all four scalar types)&lt;/h2&gt;
&lt;p&gt;Every &lt;code&gt;i32&lt;/code&gt; / &lt;code&gt;i64&lt;/code&gt; / &lt;code&gt;f32&lt;/code&gt; / &lt;code&gt;f64&lt;/code&gt; opcode:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Constants&lt;/strong&gt; — &lt;code&gt;const&lt;/code&gt; for each type.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Comparisons&lt;/strong&gt; — signed and unsigned for ints (&lt;code&gt;lt_s&lt;/code&gt;, &lt;code&gt;lt_u&lt;/code&gt;, &lt;code&gt;le_s&lt;/code&gt;, …); ordered for floats (&lt;code&gt;lt&lt;/code&gt;, &lt;code&gt;le&lt;/code&gt;, &lt;code&gt;gt&lt;/code&gt;, &lt;code&gt;ge&lt;/code&gt;, &lt;code&gt;eq&lt;/code&gt;, &lt;code&gt;ne&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Arithmetic&lt;/strong&gt; — &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;sub&lt;/code&gt;, &lt;code&gt;mul&lt;/code&gt;, &lt;code&gt;div_s&lt;/code&gt; / &lt;code&gt;div_u&lt;/code&gt; (ints), &lt;code&gt;div&lt;/code&gt; (floats), &lt;code&gt;rem_s&lt;/code&gt; / &lt;code&gt;rem_u&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Bitwise&lt;/strong&gt; — &lt;code&gt;and&lt;/code&gt;, &lt;code&gt;or&lt;/code&gt;, &lt;code&gt;xor&lt;/code&gt; (ints).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Shifts&lt;/strong&gt; — &lt;code&gt;shl&lt;/code&gt;, &lt;code&gt;shr_s&lt;/code&gt;, &lt;code&gt;shr_u&lt;/code&gt;, &lt;code&gt;rotl&lt;/code&gt;, &lt;code&gt;rotr&lt;/code&gt; (ints).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Conversion&lt;/strong&gt; — every cross-type cast in the Core spec (&lt;code&gt;i32.wrap_i64&lt;/code&gt;, &lt;code&gt;i64.extend_i32_s&lt;/code&gt;/&lt;code&gt;_u&lt;/code&gt;, &lt;code&gt;i32.trunc_f32_s&lt;/code&gt;/&lt;code&gt;_u&lt;/code&gt;/…, &lt;code&gt;f32.convert_i32_s&lt;/code&gt;/&lt;code&gt;_u&lt;/code&gt;/…, &lt;code&gt;f32.demote_f64&lt;/code&gt;, &lt;code&gt;f64.promote_f32&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reinterpretation&lt;/strong&gt; — &lt;code&gt;i32.reinterpret_f32&lt;/code&gt;, &lt;code&gt;f64.reinterpret_i64&lt;/code&gt;, etc. (bit-level recasts that don’t change the value’s bits).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;IEEE-754 results are deterministic across JVM, Scala.js, and Scala Native — including NaN bit patterns, signed-zero, and subnormal edges.&lt;/p&gt;
&lt;h2 id=&quot;sign-extension-proposal&quot;&gt;Sign-extension proposal&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;i32.extend8_s&lt;/code&gt;, &lt;code&gt;i32.extend16_s&lt;/code&gt;, &lt;code&gt;i64.extend8_s&lt;/code&gt;, &lt;code&gt;i64.extend16_s&lt;/code&gt;, &lt;code&gt;i64.extend32_s&lt;/code&gt;. Lifts a narrow signed value into the full operand-stack width. Required by rustc-built binaries.&lt;/p&gt;
&lt;h2 id=&quot;non-trapping-float-to-int&quot;&gt;Non-trapping float-to-int&lt;/h2&gt;
&lt;p&gt;The eight &lt;code&gt;trunc_sat_*&lt;/code&gt; sub-opcodes under the &lt;code&gt;0xFC&lt;/code&gt; prefix (sub-opcodes 0..7):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;i32.trunc_sat_f32_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;, &lt;code&gt;i32.trunc_sat_f64_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;i64.trunc_sat_f32_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;, &lt;code&gt;i64.trunc_sat_f64_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Where &lt;code&gt;trunc_f32_s&lt;/code&gt; of NaN or out-of-range traps, &lt;code&gt;trunc_sat_f32_s&lt;/code&gt; returns 0 for NaN and saturates at the type’s &lt;code&gt;MIN_VALUE&lt;/code&gt; / &lt;code&gt;MAX_VALUE&lt;/code&gt; for out-of-range. Required by rustc binaries built with &lt;code&gt;-C target-feature=+nontrapping-fptoint&lt;/code&gt; (now the default on stable).&lt;/p&gt;
&lt;h2 id=&quot;variables&quot;&gt;Variables&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;local.get&lt;/code&gt;, &lt;code&gt;local.set&lt;/code&gt;, &lt;code&gt;local.tee&lt;/code&gt;, &lt;code&gt;global.get&lt;/code&gt;, &lt;code&gt;global.set&lt;/code&gt;. Mutable and immutable globals are both supported; &lt;code&gt;global.set&lt;/code&gt; on an immutable global is caught by the validator.&lt;/p&gt;
&lt;h2 id=&quot;control-flow&quot;&gt;Control flow&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;block&lt;/code&gt;, &lt;code&gt;loop&lt;/code&gt;, &lt;code&gt;if&lt;/code&gt; / &lt;code&gt;else&lt;/code&gt; / &lt;code&gt;end&lt;/code&gt;, &lt;code&gt;br&lt;/code&gt;, &lt;code&gt;br_if&lt;/code&gt;, &lt;code&gt;br_table&lt;/code&gt;, &lt;code&gt;return&lt;/code&gt;, &lt;code&gt;call&lt;/code&gt;, &lt;code&gt;call_indirect&lt;/code&gt;, &lt;code&gt;unreachable&lt;/code&gt;, &lt;code&gt;nop&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Multi-value blocks, loops, and ifs are supported — block parameters get re-fed on &lt;code&gt;br&lt;/code&gt; to a loop, &lt;code&gt;br_if&lt;/code&gt; carries multi-result values, etc.&lt;/p&gt;
&lt;h2 id=&quot;tail-calls&quot;&gt;Tail calls&lt;/h2&gt;
&lt;p&gt;The tail-call proposal is supported: &lt;code&gt;return_call funcidx&lt;/code&gt; (&lt;code&gt;0x12&lt;/code&gt;) and &lt;code&gt;return_call_indirect typeidx tableidx&lt;/code&gt; (&lt;code&gt;0x13&lt;/code&gt;) replace the current call frame instead of growing the call stack. The callee’s &lt;code&gt;results&lt;/code&gt; must equal the current function’s &lt;code&gt;results&lt;/code&gt; — the validator enforces this. Frame-reuse is observable: deeply recursive tail calls (the test suite exercises 100k iterations) run in constant &lt;code&gt;frames&lt;/code&gt; memory.&lt;/p&gt;
&lt;h2 id=&quot;exception-handling&quot;&gt;Exception handling&lt;/h2&gt;
&lt;p&gt;Both forms of the exception-handling proposal are supported end-to-end — the &lt;strong&gt;legacy “phase 3”&lt;/strong&gt; form (&lt;code&gt;try&lt;/code&gt; / &lt;code&gt;catch&lt;/code&gt; / &lt;code&gt;catch_all&lt;/code&gt; / &lt;code&gt;delegate&lt;/code&gt; / &lt;code&gt;rethrow&lt;/code&gt;, what wasmtime + V8 + SpiderMonkey + &lt;code&gt;wat2wasm&lt;/code&gt;‘s &lt;code&gt;--enable-exceptions&lt;/code&gt; emit today) &lt;strong&gt;and&lt;/strong&gt; the &lt;strong&gt;modern &lt;code&gt;try_table&lt;/code&gt;&lt;/strong&gt; form (&lt;code&gt;0x1F&lt;/code&gt; plus an &lt;code&gt;exnref&lt;/code&gt; value type and &lt;code&gt;throw_ref&lt;/code&gt;, the phase-4 redesign that’s standardising now).&lt;/p&gt;
&lt;h3 id=&quot;legacy-form&quot;&gt;Legacy form&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;try blocktype&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0x06&lt;/code&gt;) — open a block-shaped region whose body can be guarded by one or more &lt;code&gt;catch&lt;/code&gt; clauses, a single &lt;code&gt;catch_all&lt;/code&gt;, or a single &lt;code&gt;delegate&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;catch tagidx&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0x07&lt;/code&gt;) — handle an exception whose tag matches &lt;code&gt;tagidx&lt;/code&gt;; the tag’s payload params are pushed onto the operand stack at handler entry.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;catch_all&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0x19&lt;/code&gt;) — handle any exception regardless of tag; no payload is pushed.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;delegate labelidx&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0x18&lt;/code&gt;) — terminator that replaces &lt;code&gt;end&lt;/code&gt;; on a throw out of the try body, re-fire the exception at the named outer label (must be a try or the function frame).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;throw tagidx&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0x08&lt;/code&gt;) — pop the tag’s payload params off the operand stack and raise the matching exception.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;rethrow labelidx&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0x09&lt;/code&gt;) — re-raise the exception caught by the named outer catch handler. Only valid inside a &lt;code&gt;catch&lt;/code&gt; / &lt;code&gt;catch_all&lt;/code&gt; clause.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A &lt;code&gt;catch&lt;/code&gt; / &lt;code&gt;catch_all&lt;/code&gt; arrived at by normal fall-through (i.e. the try body completed without throwing) is dead code; control jumps past the entire try/catch chain.&lt;/p&gt;
&lt;h3 id=&quot;modern-try-table-form&quot;&gt;Modern &lt;code&gt;try_table&lt;/code&gt; form&lt;/h3&gt;
&lt;p&gt;A single new opcode replaces the &lt;code&gt;try&lt;/code&gt; / &lt;code&gt;catch&lt;/code&gt; / &lt;code&gt;delegate&lt;/code&gt; / &lt;code&gt;rethrow&lt;/code&gt; cluster. The handler vector is parsed up front as an immediate, then the body runs as a regular block.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;try_table blocktype vec(catch-clause)&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0x1F&lt;/code&gt;) — open a block-shaped region. Each catch clause selects a tagidx (or wildcard) and a labelidx to branch to when a matching throw escapes the body.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;throw_ref&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0x0A&lt;/code&gt;) — pop an &lt;code&gt;exnref&lt;/code&gt; and re-raise the carried exception. Replaces &lt;code&gt;rethrow&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;exnref&lt;/code&gt;&lt;/strong&gt; valtype (wire byte &lt;code&gt;0x69&lt;/code&gt;) — carries a caught exception. Bound by &lt;code&gt;catch_ref&lt;/code&gt; / &lt;code&gt;catch_all_ref&lt;/code&gt; handler clauses; consumed by &lt;code&gt;throw_ref&lt;/code&gt;. Locals, params, results, and blocktypes may all be &lt;code&gt;exnref&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The four catch-clause shapes (encoded as a byte before each clause’s immediates):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Byte&lt;/th&gt;&lt;th&gt;Clause&lt;/th&gt;&lt;th&gt;Branch arity at target&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x00&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;catch tagidx labelidx&lt;/code&gt;&lt;/td&gt;&lt;td&gt;tag’s payload params&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x01&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;catch_ref tagidx labelidx&lt;/code&gt;&lt;/td&gt;&lt;td&gt;tag’s payload params + &lt;code&gt;exnref&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x02&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;catch_all labelidx&lt;/code&gt;&lt;/td&gt;&lt;td&gt;(empty)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x03&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;catch_all_ref labelidx&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;exnref&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;labelidx&lt;/code&gt; is counted with the &lt;code&gt;try_table&lt;/code&gt; frame on the control stack — &lt;code&gt;labelidx 0&lt;/code&gt; names the &lt;code&gt;try_table&lt;/code&gt; itself, &lt;code&gt;1&lt;/code&gt; the next outer block, etc. On a matching throw delivery, the runtime trims the operand stack to the &lt;code&gt;try_table&lt;/code&gt;‘s entry height, pushes the handler’s declared payload, then performs the equivalent of &lt;code&gt;br labelidx&lt;/code&gt;. Clauses are scanned in declared order; the first match wins.&lt;/p&gt;
&lt;h3 id=&quot;tags&quot;&gt;Tags&lt;/h3&gt;
&lt;p&gt;Tags are declared in &lt;a href=&quot;/wasm/#binary-sections&quot;&gt;Section 13&lt;/a&gt; or imported from a host module under import-kind &lt;code&gt;0x04&lt;/code&gt;. Each tag references a functype in section 1 whose &lt;code&gt;results&lt;/code&gt; must be empty — the params are the tag’s payload shape. A &lt;code&gt;throw&lt;/code&gt; whose tag has params &lt;code&gt;(i32, i64)&lt;/code&gt; pops two values (top of stack = last param). The validator rejects tagidxs out of range, rethrows outside any catch frame, delegates whose target is not an enclosing try or the function frame, tag functypes with non-empty results, and &lt;code&gt;try_table&lt;/code&gt; catch clauses whose payload doesn’t match the target label’s branch arity.&lt;/p&gt;
&lt;p&gt;An uncaught exception that propagates past the outermost &lt;code&gt;_start&lt;/code&gt;/&lt;code&gt;invoke&lt;/code&gt; call surfaces through the public API as &lt;code&gt;Left(WasmError.UncaughtException(tagIdx, args))&lt;/code&gt;. The host can pattern-match against the tagidx and re-throw as a native exception.&lt;/p&gt;
&lt;h2 id=&quot;memory&quot;&gt;Memory&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Load/store&lt;/strong&gt; — every width variant: &lt;code&gt;i32.load&lt;/code&gt;, &lt;code&gt;i32.load8_s&lt;/code&gt;/&lt;code&gt;_u&lt;/code&gt;, &lt;code&gt;i32.load16_s&lt;/code&gt;/&lt;code&gt;_u&lt;/code&gt;, &lt;code&gt;i64.load&lt;/code&gt;, &lt;code&gt;i64.load8_s&lt;/code&gt;/…/&lt;code&gt;load32_s&lt;/code&gt;/&lt;code&gt;_u&lt;/code&gt;, &lt;code&gt;f32.load&lt;/code&gt;, &lt;code&gt;f64.load&lt;/code&gt;, plus all matching stores.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Sizing&lt;/strong&gt; — &lt;code&gt;memory.size&lt;/code&gt; and &lt;code&gt;memory.grow&lt;/code&gt;. The optional &lt;code&gt;max&lt;/code&gt; from section 5 is honoured: &lt;code&gt;grow&lt;/code&gt; past it returns &lt;code&gt;-1&lt;/code&gt; rather than expanding.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Bulk-memory&lt;/strong&gt; — the full proposal, all seven ops under the &lt;code&gt;0xFC&lt;/code&gt; prefix:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;memory.copy&lt;/code&gt; (sub &lt;code&gt;0x0A&lt;/code&gt;), &lt;code&gt;memory.fill&lt;/code&gt; (sub &lt;code&gt;0x0B&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;memory.init&lt;/code&gt; (sub &lt;code&gt;0x08&lt;/code&gt;), &lt;code&gt;data.drop&lt;/code&gt; (sub &lt;code&gt;0x09&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;table.init&lt;/code&gt; (sub &lt;code&gt;0x0C&lt;/code&gt;), &lt;code&gt;elem.drop&lt;/code&gt; (sub &lt;code&gt;0x0D&lt;/code&gt;), &lt;code&gt;table.copy&lt;/code&gt; (sub &lt;code&gt;0x0E&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;memory.init&lt;/code&gt; / &lt;code&gt;table.init&lt;/code&gt; copy from passive data / element segments;
&lt;code&gt;data.drop&lt;/code&gt; / &lt;code&gt;elem.drop&lt;/code&gt; mark a segment as zero-length (idempotent).
Active segments are still initialised at instantiation and then marked
dropped automatically — subsequent &lt;code&gt;*.init&lt;/code&gt; with &lt;code&gt;n &amp;gt; 0&lt;/code&gt; traps, matching
wasmtime / V8 / wabt semantics.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;passive-vs-active-data-element-segments&quot;&gt;Passive vs active data + element segments&lt;/h3&gt;
&lt;p&gt;Section 11 (data) and section 9 (element) carry sealed-trait segment kinds. Active segments behave as before (copied at instantiation). Passive segments stay addressable by &lt;code&gt;dataidx&lt;/code&gt; / &lt;code&gt;elemidx&lt;/code&gt; until the matching &lt;code&gt;.drop&lt;/code&gt;. Declarative element segments pre-declare funcrefs for &lt;code&gt;ref.func&lt;/code&gt;. Element-expression-bearing forms (flags 4..7) parse &lt;code&gt;ref.null&lt;/code&gt; and &lt;code&gt;ref.func&lt;/code&gt; as their constant expressions; segments may carry either funcref or externref payloads.&lt;/p&gt;
&lt;h2 id=&quot;reference-types&quot;&gt;Reference types&lt;/h2&gt;
&lt;p&gt;Funcref + externref ride on a small set of new opcodes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ref.null&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0xD0&lt;/code&gt; + reftype byte) — typed null reference. &lt;code&gt;ref.null func&lt;/code&gt; produces a null funcref; &lt;code&gt;ref.null extern&lt;/code&gt; a null externref.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ref.is_null&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0xD1&lt;/code&gt;) — pop a reference, push &lt;code&gt;1&lt;/code&gt; if it’s a &lt;code&gt;ref.null&lt;/code&gt;, else &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ref.func&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0xD2&lt;/code&gt; + funcidx LEB) — produce a non-null funcref pointing at the named function. The validator enforces that the funcidx is &lt;em&gt;declared&lt;/em&gt; — i.e. it appears in an export, in &lt;code&gt;start&lt;/code&gt;, or in any element segment. Body-only references would be circular and so don’t count.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;table.get&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0x25&lt;/code&gt; + tableidx LEB), &lt;strong&gt;&lt;code&gt;table.set&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0x26&lt;/code&gt; + tableidx LEB) — read / write a table slot. Operand type is the table’s reftype.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;table.size&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0xFC&lt;/code&gt; sub &lt;code&gt;16&lt;/code&gt; + tableidx LEB), &lt;strong&gt;&lt;code&gt;table.grow&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0xFC&lt;/code&gt; sub &lt;code&gt;15&lt;/code&gt; + tableidx LEB), &lt;strong&gt;&lt;code&gt;table.fill&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0xFC&lt;/code&gt; sub &lt;code&gt;17&lt;/code&gt; + tableidx LEB) — runtime-side table resize + range fill, with a typed fill value.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Externref slots carry an opaque host &lt;code&gt;AnyRef&lt;/code&gt;. Wasm code can only move them around (&lt;code&gt;table.{get,set}&lt;/code&gt;, &lt;code&gt;local.{get,set}&lt;/code&gt;, &lt;code&gt;global.{get,set}&lt;/code&gt;, &lt;code&gt;ref.is_null&lt;/code&gt;); inspection happens host-side via the public API. The host hands them in as &lt;code&gt;Value.RefExtern(yourObject)&lt;/code&gt; and pulls them back as the same identity.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;call_indirect&lt;/code&gt; is now spec-restricted to funcref tables (an externref table can’t carry callable funcrefs); the validator rejects mismatches before code runs.&lt;/p&gt;
&lt;h2 id=&quot;tables-functions&quot;&gt;Tables + functions&lt;/h2&gt;
&lt;p&gt;Section 4 funcref + externref tables. &lt;code&gt;call_indirect&lt;/code&gt; does a signature check at the call site against the operand-stack types and the target function’s declared type; a mismatch traps with &lt;code&gt;Left(InvalidModule(&amp;quot;call_indirect type mismatch&amp;quot;))&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;stack&quot;&gt;Stack&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;drop&lt;/code&gt;, &lt;code&gt;select&lt;/code&gt;. Two &lt;code&gt;select&lt;/code&gt; forms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Untyped &lt;code&gt;select&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0x1B&lt;/code&gt;) — operand types are inferred. Spec-restricted to numeric value types when reference types are present; a reftype operand is rejected at validation with a “use select t*” diagnostic.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typed &lt;code&gt;select t*&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0x1C&lt;/code&gt;) — explicit operand type, encoded as &lt;code&gt;0x1C u32:count valtype[count]&lt;/code&gt; with &lt;code&gt;count == 1&lt;/code&gt; (multi-value &lt;code&gt;select&lt;/code&gt; isn’t enabled by any shipped proposal). Required for funcref / externref operands; also accepts the four numeric scalars.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;simd&quot;&gt;SIMD&lt;/h2&gt;
&lt;p&gt;The WebAssembly SIMD proposal adds ~236 opcodes under the &lt;code&gt;0xFD&lt;/code&gt; prefix and a new &lt;code&gt;V128&lt;/code&gt; value type (16 raw bytes, lane interpretation chosen per-opcode). The full surface is implemented.&lt;/p&gt;
&lt;h3 id=&quot;the-v128-value-type-host-side&quot;&gt;The &lt;code&gt;V128&lt;/code&gt; value type (host side)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;V128&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bits&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Byte&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A SIMD value is a raw 16-byte buffer; the lane shape (&lt;code&gt;i8x16&lt;/code&gt;, &lt;code&gt;i16x8&lt;/code&gt;, &lt;code&gt;i32x4&lt;/code&gt;, &lt;code&gt;i64x2&lt;/code&gt;, &lt;code&gt;f32x4&lt;/code&gt;, &lt;code&gt;f64x2&lt;/code&gt;) is &lt;em&gt;not&lt;/em&gt; carried on the value — it’s chosen per-opcode at use time. Same 16 bytes, six possible interpretations. The interpreter enforces &lt;code&gt;bits.length == 16&lt;/code&gt; on every constructed value.&lt;/p&gt;
&lt;p&gt;Byte order is &lt;strong&gt;little-endian&lt;/strong&gt; per the spec: lane 0 of any shape starts at byte 0, the low byte of each lane comes first, and &lt;code&gt;v128.load&lt;/code&gt; reads bytes in memory order into the same positions. So to build a &lt;code&gt;V128&lt;/code&gt; from four &lt;code&gt;i32&lt;/code&gt; lanes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; i32x4&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; c&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; d&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;V128&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; buf &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Byte&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;](&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; bb  &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; java&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;nio&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ByteBuffer&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wrap&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;buf&lt;span class=&quot;hl-punctuation&quot;&gt;).&lt;/span&gt;order&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;java&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;nio&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ByteOrder&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;LITTLE_ENDIAN&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  bb&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;putInt&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;hl-punctuation&quot;&gt;).&lt;/span&gt;putInt&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;hl-punctuation&quot;&gt;).&lt;/span&gt;putInt&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;hl-punctuation&quot;&gt;).&lt;/span&gt;putInt&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;d&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;V128&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;buf&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;dot&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;i32x4&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; i32x4&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;6&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;7&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To inspect a &lt;code&gt;V128&lt;/code&gt; result from &lt;code&gt;invoke&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;compute&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;V128&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bs&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; bb    &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; java&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;nio&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ByteBuffer&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wrap&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bs&lt;span class=&quot;hl-punctuation&quot;&gt;).&lt;/span&gt;order&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;java&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;nio&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ByteOrder&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;LITTLE_ENDIAN&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; lanes &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bb&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;getInt&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; bb&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;getInt&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; bb&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;getInt&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; bb&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;getInt&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
    println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;lanes&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;mkString&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;[&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;, &amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;]&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; _ &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;???&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The test suite’s &lt;code&gt;TestSupport.simd&lt;/code&gt; object has helpers (&lt;code&gt;fromI8&lt;/code&gt;, &lt;code&gt;fromI16&lt;/code&gt;, &lt;code&gt;fromI32&lt;/code&gt;, &lt;code&gt;fromI64&lt;/code&gt;, &lt;code&gt;fromF32&lt;/code&gt;, &lt;code&gt;fromF64&lt;/code&gt;) for each lane shape — they’re test-scope but easy to copy if your host code needs the same builders.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;V128&lt;/code&gt; equality:&lt;/strong&gt; the case class derives &lt;code&gt;equals&lt;/code&gt; from &lt;code&gt;Array[Byte]&lt;/code&gt; reference equality (Scala’s &lt;code&gt;Array&lt;/code&gt; doesn’t define structural &lt;code&gt;equals&lt;/code&gt;). So &lt;code&gt;V128(a) == V128(b)&lt;/code&gt; is true only if &lt;code&gt;a eq b&lt;/code&gt;. Compare the bytes directly if you want value equality.&lt;/p&gt;
&lt;h3 id=&quot;foundations&quot;&gt;Foundations&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;V128&lt;/code&gt; value type&lt;/strong&gt; (wire byte &lt;code&gt;0x7B&lt;/code&gt;). First-class in function params, results, locals, globals, and blocktypes. Locals zero-init to 16 zero bytes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;v128.const&lt;/code&gt;&lt;/strong&gt; (&lt;code&gt;0xFD 0x0C&lt;/code&gt; + 16 raw little-endian bytes). The wat-side annotations (&lt;code&gt;i32x4 1 2 3 4&lt;/code&gt;, &lt;code&gt;i16x8 ...&lt;/code&gt;, etc.) are text-form only; the binary just sees 16 opaque bytes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;0xFD prefix dispatch&lt;/strong&gt; — the SIMD sub-opcode is LEB-encoded.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tests cover raw byte round-trips, parameter / local / block-result plumbing, zero-init, and the wat-form lane-annotation equivalence (&lt;code&gt;v128.const i8x16&lt;/code&gt; and &lt;code&gt;v128.const i16x8&lt;/code&gt; of the same byte payload produce identical V128 values).&lt;/p&gt;
&lt;h3 id=&quot;loads-stores&quot;&gt;Loads + stores&lt;/h3&gt;
&lt;p&gt;Every load is &lt;code&gt;[i32 addr] → [v128]&lt;/code&gt;; the store is &lt;code&gt;[i32 addr, v128 value] → []&lt;/code&gt;. All ops carry a multi-memory-shaped memarg (the alignment LEB’s bit 6 flags an optional memidx LEB; offset follows).&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Opcode&lt;/th&gt;&lt;th&gt;Sub&lt;/th&gt;&lt;th&gt;What it does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x00&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Full 16-byte little-endian load.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load8x8_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x01&lt;/code&gt; / &lt;code&gt;0x02&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 8 source bytes, widen each (sign / zero) into 8 i16 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load16x4_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x03&lt;/code&gt; / &lt;code&gt;0x04&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 4 source i16s, widen each into 4 i32 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load32x2_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x05&lt;/code&gt; / &lt;code&gt;0x06&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 2 source i32s, widen each into 2 i64 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load8_splat&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x07&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 1 byte, broadcast across all 16 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load16_splat&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x08&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 2 bytes, broadcast across all 8 i16 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load32_splat&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x09&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 4 bytes, broadcast across all 4 i32 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load64_splat&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x0A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 8 bytes, broadcast across all 2 i64 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.store&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x0B&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Full 16-byte little-endian store.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load32_zero&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x5C&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 4 bytes into lane 0; zero the remaining 12.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load64_zero&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x5D&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 8 bytes into lane 0; zero the remaining 8.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Out-of-bounds (addr + offset + width past memory end) traps with &lt;code&gt;MemoryOutOfBounds&lt;/code&gt;, same shape as the scalar memory ops.&lt;/p&gt;
&lt;h3 id=&quot;lane-access&quot;&gt;Lane access&lt;/h3&gt;
&lt;p&gt;Every “build / inspect / rearrange a v128 lane-by-lane” surface lives here. Lane shapes — &lt;code&gt;i8x16&lt;/code&gt;, &lt;code&gt;i16x8&lt;/code&gt;, &lt;code&gt;i32x4&lt;/code&gt;, &lt;code&gt;i64x2&lt;/code&gt;, &lt;code&gt;f32x4&lt;/code&gt;, &lt;code&gt;f64x2&lt;/code&gt; — pick the lane width (1/2/4/8 bytes) and the count (16/8/4/2 lanes). Lane immediates are validated &lt;code&gt;&amp;lt; lane_count&lt;/code&gt; at compile time.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Opcode&lt;/th&gt;&lt;th&gt;Sub&lt;/th&gt;&lt;th&gt;What it does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.shuffle&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x0D&lt;/code&gt;&lt;/td&gt;&lt;td&gt;16-byte laneidx immediate (each &lt;code&gt;&amp;lt; 32&lt;/code&gt;); each result lane is &lt;code&gt;a[c]&lt;/code&gt; if &lt;code&gt;c&amp;lt;16&lt;/code&gt; else &lt;code&gt;b[c-16]&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.swizzle&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x0E&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Dynamic shuffle. &lt;code&gt;s&lt;/code&gt; (top) is the index vector, &lt;code&gt;v&lt;/code&gt; (below) is the source. Result lane &lt;code&gt;i&lt;/code&gt; = &lt;code&gt;v[s[i]]&lt;/code&gt; if &lt;code&gt;s[i] &amp;lt; 16&lt;/code&gt; else &lt;code&gt;0&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.splat&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x0F&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Broadcast the low 8 bits of an i32 to 16 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.splat&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x10&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Broadcast the low 16 bits LE to 8 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.splat&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x11&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Broadcast 4 LE bytes to 4 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.splat&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x12&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Broadcast 8 LE bytes to 2 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.splat&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x13&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Broadcast the IEEE-754 bit pattern of an f32 to 4 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.splat&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x14&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Broadcast the IEEE-754 bit pattern of an f64 to 2 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.extract_lane_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x15&lt;/code&gt; / &lt;code&gt;0x16&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 1 byte at lane (signed / zero extended to i32).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.replace_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x17&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Write the low byte of an i32 at the lane.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.extract_lane_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x18&lt;/code&gt; / &lt;code&gt;0x19&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 2 LE bytes at lane (signed / zero extended to i32).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.replace_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x1A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Write the low 16 bits LE at the lane.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.extract_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x1B&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 4 LE bytes at lane → i32.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.replace_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x1C&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Write 4 LE bytes at lane.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.extract_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x1D&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 8 LE bytes at lane → i64.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.replace_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x1E&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Write 8 LE bytes at lane.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.extract_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x1F&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 4 LE bytes at lane → f32 (raw IEEE-754 bits, no NaN canonicalisation).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.replace_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x20&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Write 4 LE bytes at lane.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.extract_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x21&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 8 LE bytes at lane → f64.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.replace_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x22&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Write 8 LE bytes at lane.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;extract_lane&lt;/code&gt; / &lt;code&gt;replace_lane&lt;/code&gt; carry a 1-byte lane immediate after the sub-opcode; &lt;code&gt;i8x16.shuffle&lt;/code&gt; carries a 16-byte laneidx vector. &lt;code&gt;splat&lt;/code&gt; and &lt;code&gt;swizzle&lt;/code&gt; have no immediate beyond the sub-opcode.&lt;/p&gt;
&lt;h3 id=&quot;integer-arithmetic&quot;&gt;Integer arithmetic&lt;/h3&gt;
&lt;p&gt;Lane-wise integer arithmetic across every integer shape. Plain &lt;code&gt;add&lt;/code&gt; / &lt;code&gt;sub&lt;/code&gt; / &lt;code&gt;mul&lt;/code&gt; wrap modulo 2^lane_width; &lt;code&gt;_sat_s&lt;/code&gt; / &lt;code&gt;_sat_u&lt;/code&gt; clamp at the signed / unsigned bounds; &lt;code&gt;avgr_u&lt;/code&gt; is the rounding unsigned average &lt;code&gt;(a + b + 1) / 2&lt;/code&gt;. No &lt;code&gt;i8x16.mul&lt;/code&gt; in the spec, no saturating variants past &lt;code&gt;i16x8&lt;/code&gt;, no &lt;code&gt;avgr_u&lt;/code&gt; past &lt;code&gt;i16x8&lt;/code&gt;. All ops have no immediate.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Opcode&lt;/th&gt;&lt;th&gt;Sub&lt;/th&gt;&lt;th&gt;What it does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.abs&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x60&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;abs(MinValue)&lt;/code&gt; wraps to &lt;code&gt;MinValue&lt;/code&gt; (overflow mod 2^8).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.neg&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x61&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;neg(MinValue)&lt;/code&gt; wraps likewise.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.add&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x6E&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Wraps mod 256 per lane.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.add_sat_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x6F&lt;/code&gt; / &lt;code&gt;0x70&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Clamps to &lt;code&gt;[-128, 127]&lt;/code&gt; / &lt;code&gt;[0, 255]&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.sub&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x71&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Wraps mod 256.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.sub_sat_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x72&lt;/code&gt; / &lt;code&gt;0x73&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Clamps to the signed / unsigned lane bounds.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.avgr_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x7B&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;(a + b + 1) / 2&lt;/code&gt; per lane (rounds up).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.abs&lt;/code&gt; / &lt;code&gt;neg&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x80&lt;/code&gt; / &lt;code&gt;0x81&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Same shape as &lt;code&gt;i8x16&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.add&lt;/code&gt; / &lt;code&gt;add_sat_s&lt;/code&gt; / &lt;code&gt;add_sat_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x8E&lt;/code&gt; / &lt;code&gt;0x8F&lt;/code&gt; / &lt;code&gt;0x90&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Wrap / clamp to &lt;code&gt;[-32768, 32767]&lt;/code&gt; / &lt;code&gt;[0, 65535]&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.sub&lt;/code&gt; / &lt;code&gt;sub_sat_s&lt;/code&gt; / &lt;code&gt;sub_sat_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x91&lt;/code&gt; / &lt;code&gt;0x92&lt;/code&gt; / &lt;code&gt;0x93&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Wrap / clamp.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.mul&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x95&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Low 16 bits of the full-width product.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.avgr_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x9B&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;(a + b + 1) / 2&lt;/code&gt; per lane unsigned.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.abs&lt;/code&gt; / &lt;code&gt;neg&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xA0&lt;/code&gt; / &lt;code&gt;0xA1&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;abs(Int.MinValue)&lt;/code&gt; wraps.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.add&lt;/code&gt; / &lt;code&gt;sub&lt;/code&gt; / &lt;code&gt;mul&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xAE&lt;/code&gt; / &lt;code&gt;0xB1&lt;/code&gt; / &lt;code&gt;0xB5&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Wrap mod 2^32; &lt;code&gt;mul&lt;/code&gt; is the low 32 bits.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.abs&lt;/code&gt; / &lt;code&gt;neg&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xC0&lt;/code&gt; / &lt;code&gt;0xC1&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;abs(Long.MinValue)&lt;/code&gt; wraps.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.add&lt;/code&gt; / &lt;code&gt;sub&lt;/code&gt; / &lt;code&gt;mul&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xCE&lt;/code&gt; / &lt;code&gt;0xD1&lt;/code&gt; / &lt;code&gt;0xD5&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Wrap mod 2^64.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Sub-opcodes ≥ &lt;code&gt;0x80&lt;/code&gt; encode as 2-byte LEBs in the binary; &lt;code&gt;wat2wasm&lt;/code&gt; emits the right shape, and the dispatch’s LEB decoder handles either width transparently.&lt;/p&gt;
&lt;h3 id=&quot;shifts-min-max&quot;&gt;Shifts + min/max&lt;/h3&gt;
&lt;p&gt;Lane-wise shifts (&lt;code&gt;shl&lt;/code&gt;, &lt;code&gt;shr_s&lt;/code&gt;, &lt;code&gt;shr_u&lt;/code&gt;) on all four integer shapes, plus per-lane signed and unsigned min/max on &lt;code&gt;i8x16&lt;/code&gt;, &lt;code&gt;i16x8&lt;/code&gt;, &lt;code&gt;i32x4&lt;/code&gt; (the spec excludes &lt;code&gt;i64x2.min/max&lt;/code&gt;). Shifts pop the i32 count from the operand stack — it’s &lt;em&gt;not&lt;/em&gt; an immediate — and the spec takes &lt;code&gt;count mod lane_width&lt;/code&gt;, so e.g. &lt;code&gt;i8x16.shl(_, 8)&lt;/code&gt; is the identity.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Opcode&lt;/th&gt;&lt;th&gt;Sub&lt;/th&gt;&lt;th&gt;What it does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.shl&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x6B&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Shift left; count mod 8.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.shr_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x6C&lt;/code&gt; / &lt;code&gt;0x6D&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Arithmetic / logical right shift; count mod 8.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.min_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x76&lt;/code&gt; / &lt;code&gt;0x77&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Per-lane signed / unsigned minimum.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.max_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x78&lt;/code&gt; / &lt;code&gt;0x79&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Per-lane signed / unsigned maximum.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.shl&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x8B&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Shift left; count mod 16.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.shr_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x8C&lt;/code&gt; / &lt;code&gt;0x8D&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Arithmetic / logical right shift; count mod 16.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.min_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x96&lt;/code&gt; / &lt;code&gt;0x97&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Per-lane signed / unsigned minimum.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.max_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x98&lt;/code&gt; / &lt;code&gt;0x99&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Per-lane signed / unsigned maximum.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.shl&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xAB&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Shift left; count mod 32.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.shr_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xAC&lt;/code&gt; / &lt;code&gt;0xAD&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Arithmetic / logical right shift; count mod 32.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.min_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xB6&lt;/code&gt; / &lt;code&gt;0xB7&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Per-lane signed / unsigned minimum.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.max_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xB8&lt;/code&gt; / &lt;code&gt;0xB9&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Per-lane signed / unsigned maximum.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.shl&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xCB&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Shift left; count mod 64.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.shr_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xCC&lt;/code&gt; / &lt;code&gt;0xCD&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Arithmetic / logical right shift; count mod 64.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The signed vs unsigned distinction matters at the lane width: byte &lt;code&gt;0xFF&lt;/code&gt; is &lt;code&gt;-1&lt;/code&gt; signed but &lt;code&gt;255&lt;/code&gt; unsigned, so &lt;code&gt;i8x16.shr_s&lt;/code&gt; of it stays &lt;code&gt;-1&lt;/code&gt; while &lt;code&gt;i8x16.shr_u&lt;/code&gt; of it becomes &lt;code&gt;0x7F&lt;/code&gt;; &lt;code&gt;i8x16.min_s&lt;/code&gt; picks &lt;code&gt;-1&lt;/code&gt; as the minimum but &lt;code&gt;i8x16.min_u&lt;/code&gt; picks &lt;code&gt;0&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;float-arithmetic&quot;&gt;Float arithmetic&lt;/h3&gt;
&lt;p&gt;Lane-wise IEEE-754 arithmetic across &lt;code&gt;f32x4&lt;/code&gt; and &lt;code&gt;f64x2&lt;/code&gt;. Rounding (&lt;code&gt;ceil&lt;/code&gt;, &lt;code&gt;floor&lt;/code&gt;, &lt;code&gt;trunc&lt;/code&gt;, &lt;code&gt;nearest&lt;/code&gt;) and &lt;code&gt;abs&lt;/code&gt; / &lt;code&gt;neg&lt;/code&gt; / &lt;code&gt;sqrt&lt;/code&gt; are unary; &lt;code&gt;add&lt;/code&gt; / &lt;code&gt;sub&lt;/code&gt; / &lt;code&gt;mul&lt;/code&gt; / &lt;code&gt;div&lt;/code&gt; and &lt;code&gt;min&lt;/code&gt; / &lt;code&gt;max&lt;/code&gt; / &lt;code&gt;pmin&lt;/code&gt; / &lt;code&gt;pmax&lt;/code&gt; are binary. All ops have no immediate.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;abs&lt;/code&gt; / &lt;code&gt;neg&lt;/code&gt; are bit-level (clear / flip the sign bit) and preserve NaN payloads exactly — useful for round-tripping signaling NaNs. Arithmetic ops produce &lt;em&gt;an&lt;/em&gt; NaN when any operand is NaN; the bit pattern follows the same implementation-defined rule as scalar &lt;code&gt;f32.add&lt;/code&gt; / &lt;code&gt;f64.add&lt;/code&gt; (in practice the JVM’s canonical &lt;code&gt;0x7FC00000&lt;/code&gt; / &lt;code&gt;0x7FF8000000000000&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;&lt;code&gt;min&lt;/code&gt; and &lt;code&gt;max&lt;/code&gt; use IEEE-754 semantics: NaN-involving inputs produce NaN, and &lt;code&gt;-0&lt;/code&gt; orders below &lt;code&gt;+0&lt;/code&gt;. &lt;code&gt;pmin(a,b)&lt;/code&gt; and &lt;code&gt;pmax(a,b)&lt;/code&gt; follow the spec’s compare-then-pick formula — &lt;code&gt;pmin = if b&amp;lt;a then b else a&lt;/code&gt;, &lt;code&gt;pmax = if a&amp;lt;b then b else a&lt;/code&gt; — which means a NaN-involving compare always returns &lt;code&gt;false&lt;/code&gt;, so &lt;code&gt;a&lt;/code&gt; is picked. That gives &lt;code&gt;pmin&lt;/code&gt; / &lt;code&gt;pmax&lt;/code&gt; a &lt;em&gt;different&lt;/em&gt; NaN behaviour from &lt;code&gt;min&lt;/code&gt; / &lt;code&gt;max&lt;/code&gt;: a non-NaN &lt;code&gt;a&lt;/code&gt; paired with a NaN &lt;code&gt;b&lt;/code&gt; yields &lt;code&gt;a&lt;/code&gt; (no NaN propagation), but a NaN &lt;code&gt;a&lt;/code&gt; always propagates.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Opcode&lt;/th&gt;&lt;th&gt;Sub&lt;/th&gt;&lt;th&gt;What it does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.ceil&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x67&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Round each lane toward +Inf.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.floor&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x68&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Round each lane toward -Inf.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.trunc&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x69&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Round each lane toward zero (preserves signed zero).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.nearest&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x6A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Round half to even per lane.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.ceil&lt;/code&gt; / &lt;code&gt;floor&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x74&lt;/code&gt; / &lt;code&gt;0x75&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Same shape as &lt;code&gt;f32x4&lt;/code&gt;, double precision.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.trunc&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x7A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.nearest&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x94&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.abs&lt;/code&gt; / &lt;code&gt;neg&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xE0&lt;/code&gt; / &lt;code&gt;0xE1&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Bit-twiddle the sign bit; NaN payloads preserved.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.sqrt&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xE3&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;sqrt(-x&amp;gt;0) = NaN&lt;/code&gt;; &lt;code&gt;sqrt(-0) = -0&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.add&lt;/code&gt; / &lt;code&gt;sub&lt;/code&gt; / &lt;code&gt;mul&lt;/code&gt; / &lt;code&gt;div&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xE4&lt;/code&gt; / &lt;code&gt;0xE5&lt;/code&gt; / &lt;code&gt;0xE6&lt;/code&gt; / &lt;code&gt;0xE7&lt;/code&gt;&lt;/td&gt;&lt;td&gt;IEEE-754 per lane. &lt;code&gt;0/0 = NaN&lt;/code&gt;, &lt;code&gt;1/0 = +Inf&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.min&lt;/code&gt; / &lt;code&gt;max&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xE8&lt;/code&gt; / &lt;code&gt;0xE9&lt;/code&gt;&lt;/td&gt;&lt;td&gt;NaN → NaN; &lt;code&gt;min(-0,+0) = -0&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.pmin&lt;/code&gt; / &lt;code&gt;pmax&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xEA&lt;/code&gt; / &lt;code&gt;0xEB&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;if b&amp;lt;a then b else a&lt;/code&gt; / &lt;code&gt;if a&amp;lt;b then b else a&lt;/code&gt;; NaN-compare picks &lt;code&gt;a&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.abs&lt;/code&gt; / &lt;code&gt;neg&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xEC&lt;/code&gt; / &lt;code&gt;0xED&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Bit-level sign manipulation.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.sqrt&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xEF&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.add&lt;/code&gt; / &lt;code&gt;sub&lt;/code&gt; / &lt;code&gt;mul&lt;/code&gt; / &lt;code&gt;div&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xF0&lt;/code&gt; / &lt;code&gt;0xF1&lt;/code&gt; / &lt;code&gt;0xF2&lt;/code&gt; / &lt;code&gt;0xF3&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.min&lt;/code&gt; / &lt;code&gt;max&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xF4&lt;/code&gt; / &lt;code&gt;0xF5&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.pmin&lt;/code&gt; / &lt;code&gt;pmax&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xF6&lt;/code&gt; / &lt;code&gt;0xF7&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;bitwise-reductions&quot;&gt;Bitwise + reductions&lt;/h3&gt;
&lt;p&gt;Six bitwise ops that ignore lane shape (the v128 is just 16 raw bytes), plus nine v128 → i32 reductions. &lt;code&gt;v128.bitselect&lt;/code&gt; is the only SIMD ternary op — it takes three v128 operands &lt;code&gt;(a, b, c)&lt;/code&gt; and returns &lt;code&gt;(a AND c) OR (b AND NOT c)&lt;/code&gt;, where &lt;code&gt;c&lt;/code&gt; is the selector mask. All 15 ops have no immediate past the sub-opcode.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;any_true&lt;/code&gt; is shape-agnostic — any byte non-zero returns &lt;code&gt;1&lt;/code&gt;, otherwise &lt;code&gt;0&lt;/code&gt;. &lt;code&gt;*.all_true&lt;/code&gt; is lane-shape-aware: a v128 whose bytes are &lt;code&gt;[0, 1, 0, 1, 0, 1, ...]&lt;/code&gt; is &lt;code&gt;i8x16.all_true = 0&lt;/code&gt; (every other byte is zero) but &lt;code&gt;i16x8.all_true = 1&lt;/code&gt; (every 16-bit lane is non-zero). &lt;code&gt;*.bitmask&lt;/code&gt; packs the MSB of each lane into the i32 result at the lane-indexed bit position (lane 0 → bit 0).&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Opcode&lt;/th&gt;&lt;th&gt;Sub&lt;/th&gt;&lt;th&gt;What it does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.not&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x4D&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Bitwise complement of all 16 bytes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.and&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x4E&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Bitwise AND.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.andnot&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x4F&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;a AND (NOT b)&lt;/code&gt; — note the asymmetry.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.or&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x50&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Bitwise OR.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.xor&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x51&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Bitwise XOR.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.bitselect&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x52&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;(a AND c) OR (b AND NOT c)&lt;/code&gt;; ternary.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.any_true&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x53&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;1&lt;/code&gt; if any bit is set, else &lt;code&gt;0&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.all_true&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x63&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;1&lt;/code&gt; iff every byte is non-zero.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.all_true&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x83&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;1&lt;/code&gt; iff every 16-bit lane is non-zero.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.all_true&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xA3&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;1&lt;/code&gt; iff every i32 lane is non-zero.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.all_true&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xC3&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;1&lt;/code&gt; iff both i64 lanes are non-zero.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.bitmask&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x64&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Top bit of each byte → 16-bit mask in i32.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.bitmask&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x84&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Top bit of each i16 lane → 8-bit mask.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.bitmask&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xA4&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Top bit of each i32 lane → 4-bit mask.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.bitmask&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xC4&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Top bit of each i64 lane → 2-bit mask.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;comparisons&quot;&gt;Comparisons&lt;/h3&gt;
&lt;p&gt;Lane-wise compare ops produce a result lane that is all-1s on true (&lt;code&gt;0xFF…&lt;/code&gt; — the bitmask shape &lt;code&gt;v128.bitselect&lt;/code&gt; consumes natively) and all-0s on false, in the same lane width as the inputs. Every op is &lt;code&gt;v128 × v128 → v128&lt;/code&gt;, no immediate past the sub-opcode. 48 ops in total — three full integer shapes (i8x16 / i16x8 / i32x4) get the full &lt;code&gt;eq, ne, lt_s, lt_u, gt_s, gt_u, le_s, le_u, ge_s, ge_u&lt;/code&gt; set; i64x2 gets the six signed forms only (the spec defines no &lt;code&gt;_u&lt;/code&gt; variants for i64); f32x4 and f64x2 each get &lt;code&gt;eq, ne, lt, gt, le, ge&lt;/code&gt; (no signedness — floats are inherently signed).&lt;/p&gt;
&lt;p&gt;IEEE-754 NaN: every f32/f64 compare returns false when either operand is NaN, except &lt;code&gt;ne&lt;/code&gt; which returns true. IEEE-754 signed zero: &lt;code&gt;-0.0 == +0.0&lt;/code&gt; is true and &lt;code&gt;-0.0 &amp;lt; +0.0&lt;/code&gt; is false.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Opcode&lt;/th&gt;&lt;th&gt;Sub&lt;/th&gt;&lt;th&gt;Notes&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.eq&lt;/code&gt; / &lt;code&gt;ne&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x23&lt;/code&gt; / &lt;code&gt;0x24&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Bit-pattern equality — sign-form doesn’t matter.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.lt_s&lt;/code&gt; / &lt;code&gt;lt_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x25&lt;/code&gt; / &lt;code&gt;0x26&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;-1 &amp;lt; 0&lt;/code&gt; is true signed, false unsigned (&lt;code&gt;0xFF &amp;gt; 0&lt;/code&gt;).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.gt_s&lt;/code&gt; / &lt;code&gt;gt_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x27&lt;/code&gt; / &lt;code&gt;0x28&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.le_s&lt;/code&gt; / &lt;code&gt;le_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x29&lt;/code&gt; / &lt;code&gt;0x2A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.ge_s&lt;/code&gt; / &lt;code&gt;ge_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x2B&lt;/code&gt; / &lt;code&gt;0x2C&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.eq&lt;/code&gt; / &lt;code&gt;ne&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x2D&lt;/code&gt; / &lt;code&gt;0x2E&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.lt_s&lt;/code&gt; / &lt;code&gt;lt_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x2F&lt;/code&gt; / &lt;code&gt;0x30&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Sign-form chooses between two reads of the 16-bit lane.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.gt_s&lt;/code&gt; / &lt;code&gt;gt_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x31&lt;/code&gt; / &lt;code&gt;0x32&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.le_s&lt;/code&gt; / &lt;code&gt;le_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x33&lt;/code&gt; / &lt;code&gt;0x34&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.ge_s&lt;/code&gt; / &lt;code&gt;ge_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x35&lt;/code&gt; / &lt;code&gt;0x36&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.eq&lt;/code&gt; / &lt;code&gt;ne&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x37&lt;/code&gt; / &lt;code&gt;0x38&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.lt_s&lt;/code&gt; / &lt;code&gt;lt_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x39&lt;/code&gt; / &lt;code&gt;0x3A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;_u&lt;/code&gt; uses &lt;code&gt;Integer.compareUnsigned&lt;/code&gt; per lane.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.gt_s&lt;/code&gt; / &lt;code&gt;gt_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x3B&lt;/code&gt; / &lt;code&gt;0x3C&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.le_s&lt;/code&gt; / &lt;code&gt;le_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x3D&lt;/code&gt; / &lt;code&gt;0x3E&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.ge_s&lt;/code&gt; / &lt;code&gt;ge_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x3F&lt;/code&gt; / &lt;code&gt;0x40&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.eq&lt;/code&gt; / &lt;code&gt;ne&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xD6&lt;/code&gt; / &lt;code&gt;0xD7&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.lt_s&lt;/code&gt; / &lt;code&gt;gt_s&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xD8&lt;/code&gt; / &lt;code&gt;0xD9&lt;/code&gt;&lt;/td&gt;&lt;td&gt;i64x2 has signed-only compares per spec.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.le_s&lt;/code&gt; / &lt;code&gt;ge_s&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xDA&lt;/code&gt; / &lt;code&gt;0xDB&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.eq&lt;/code&gt; / &lt;code&gt;ne&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x41&lt;/code&gt; / &lt;code&gt;0x42&lt;/code&gt;&lt;/td&gt;&lt;td&gt;NaN-involving: every op false except &lt;code&gt;ne&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.lt&lt;/code&gt; / &lt;code&gt;gt&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x43&lt;/code&gt; / &lt;code&gt;0x44&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.le&lt;/code&gt; / &lt;code&gt;ge&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x45&lt;/code&gt; / &lt;code&gt;0x46&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.eq&lt;/code&gt; / &lt;code&gt;ne&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x47&lt;/code&gt; / &lt;code&gt;0x48&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.lt&lt;/code&gt; / &lt;code&gt;gt&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x49&lt;/code&gt; / &lt;code&gt;0x4A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.le&lt;/code&gt; / &lt;code&gt;ge&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x4B&lt;/code&gt; / &lt;code&gt;0x4C&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;narrow-extend-extadd-pairwise-extmul-float-int-conv-demote-promote&quot;&gt;Narrow / extend / extadd_pairwise / extmul + float-int conv + demote / promote&lt;/h3&gt;
&lt;p&gt;42 ops covering everything that changes lane width or moves between integer and float lanes. Mechanically: narrow takes two source v128s and packs them into one with saturating clamps; extend (the spec’s name for “widen”) pulls half the source lanes and sign- or zero-extends each into the wider lane width; extadd_pairwise pairs adjacent narrower lanes and sums each pair (with extension) into one wider lane; extmul fuses extend + multiply at the wider lane width so the product fits exactly. The 8 float↔int conversions follow the scalar &lt;code&gt;trunc_sat_*&lt;/code&gt; / &lt;code&gt;convert_*&lt;/code&gt; rules per lane (NaN → 0, ±overflow saturates). The &lt;code&gt;_zero&lt;/code&gt; suffix on the f64x2 / i32x4 form means “result has 4 lanes but only the first 2 carry data, the rest are 0”; &lt;code&gt;_low&lt;/code&gt; on the inverse direction means “read only lanes 0..1 of the source”.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Opcode&lt;/th&gt;&lt;th&gt;Sub&lt;/th&gt;&lt;th&gt;What it does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i8x16.narrow_i16x8_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x65&lt;/code&gt; / &lt;code&gt;0x66&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Pack 16 Short lanes into 16 saturated Byte lanes (signed &lt;code&gt;[-128,127]&lt;/code&gt; / unsigned &lt;code&gt;[0,255]&lt;/code&gt;).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.narrow_i32x4_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x85&lt;/code&gt; / &lt;code&gt;0x86&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Pack 8 Int lanes into 8 saturated Short lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.extend_low/high_i8x16_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x87&lt;/code&gt;–&lt;code&gt;0x8A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 8 bytes (low or high half), sign- or zero-extend each to i16.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.extend_low/high_i16x8_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xA7&lt;/code&gt;–&lt;code&gt;0xAA&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 4 i16 lanes (half), extend to i32.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.extend_low/high_i32x4_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xC7&lt;/code&gt;–&lt;code&gt;0xCA&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 2 i32 lanes (half), extend to i64.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.extadd_pairwise_i8x16_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x7C&lt;/code&gt; / &lt;code&gt;0x7D&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Pair adjacent bytes, sum with extension into 8 i16 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.extadd_pairwise_i16x8_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x7E&lt;/code&gt; / &lt;code&gt;0x7F&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Pair adjacent i16 lanes, sum with extension into 4 i32 lanes.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i16x8.extmul_low/high_i8x16_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x9C&lt;/code&gt;–&lt;code&gt;0x9F&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Multiply extended low/high bytes at i16 precision (full product).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.extmul_low/high_i16x8_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xBC&lt;/code&gt;–&lt;code&gt;0xBF&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Multiply extended i16 lanes at i32 precision.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i64x2.extmul_low/high_i32x4_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xDC&lt;/code&gt;–&lt;code&gt;0xDF&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Multiply extended i32 lanes at i64 precision.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.demote_f64x2_zero&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x5E&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Round 2 f64 lanes to f32 lanes 0..1; lanes 2 + 3 zero-filled.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.promote_low_f32x4&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x5F&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Widen f32 lanes 0..1 to f64.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.trunc_sat_f32x4_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xF8&lt;/code&gt; / &lt;code&gt;0xF9&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Per-lane scalar &lt;code&gt;trunc_sat&lt;/code&gt;: NaN → 0, ±overflow → INT_MIN/MAX (&lt;code&gt;_s&lt;/code&gt;) or 0/0xFFFFFFFF (&lt;code&gt;_u&lt;/code&gt;).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f32x4.convert_i32x4_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xFA&lt;/code&gt; / &lt;code&gt;0xFB&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Per-lane int → f32; &lt;code&gt;_u&lt;/code&gt; treats the signed lane as UInt32 first.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.trunc_sat_f64x2_s_zero&lt;/code&gt; / &lt;code&gt;_u_zero&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xFC&lt;/code&gt; / &lt;code&gt;0xFD&lt;/code&gt;&lt;/td&gt;&lt;td&gt;2 f64 → i32 lanes 0..1; lanes 2 + 3 zero-filled.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;f64x2.convert_low_i32x4_s&lt;/code&gt; / &lt;code&gt;_u&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xFE&lt;/code&gt; / &lt;code&gt;0xFF&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read i32 lanes 0..1 of source, widen to f64.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&quot;dot-product-load-lane-store-lane&quot;&gt;Dot product + load_lane / store_lane&lt;/h3&gt;
&lt;p&gt;Nine ops: one pairwise multiply-add at i32 precision, and eight partial memory accesses that touch a single lane.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;i32x4.dot_i16x8_s&lt;/code&gt; (the only “wider lane multiply-add” op in the spec) reads two i16x8 vectors, pairs up adjacent lanes (&lt;code&gt;a[2k] * b[2k] + a[2k+1] * b[2k+1]&lt;/code&gt;), and produces an i32x4. The i16 lanes are sign-extended to i32 before multiplying, so each product fits exact in i32; the pair-sum can overflow only at &lt;code&gt;-32768² + -32768² = 2³¹&lt;/code&gt;, which wraps to &lt;code&gt;Int.MinValue&lt;/code&gt; per the spec’s two’s-complement rule. No immediate past the sub-opcode.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;v128.load*_lane&lt;/code&gt; / &lt;code&gt;v128.store*_lane&lt;/code&gt; are the only SIMD ops that carry BOTH a memarg AND a 1-byte lane immediate (after the sub-opcode: memarg LEBs, then a single byte for the lane index). Each load_lane reads N bytes from memory and places them at the named lane of the v128 operand (preserving every other lane); each store_lane writes N bytes from the named lane to memory. Operand stack: &lt;code&gt;[i32 addr, v128 src]&lt;/code&gt; → &lt;code&gt;[v128]&lt;/code&gt; for load, &lt;code&gt;[i32 addr, v128 src]&lt;/code&gt; → &lt;code&gt;[]&lt;/code&gt; for store. Lane index is validated &lt;code&gt;&amp;lt; 16/8/4/2&lt;/code&gt; depending on access width. Out-of-bounds (&lt;code&gt;addr + offset + width &amp;gt; mem.size&lt;/code&gt;) traps with &lt;code&gt;MemoryOutOfBounds&lt;/code&gt;.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Opcode&lt;/th&gt;&lt;th&gt;Sub&lt;/th&gt;&lt;th&gt;What it does&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;i32x4.dot_i16x8_s&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0xBA&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Pairwise multiply-then-add: lane k = &lt;code&gt;a[2k]*b[2k] + a[2k+1]*b[2k+1]&lt;/code&gt; with i16 sign-extension to i32. Pair-sum wraps two’s-complement on overflow.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load8_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x54&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 1 byte at addr, write into lane (lane idx &amp;lt; 16). Other lanes preserved.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load16_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x55&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 2 LE bytes, write into i16 lane (&amp;lt; 8).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load32_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x56&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 4 LE bytes, write into i32 lane (&amp;lt; 4).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.load64_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x57&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Read 8 LE bytes, write into i64 lane (&amp;lt; 2).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.store8_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x58&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Write 1 byte of lane (idx &amp;lt; 16) to memory.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.store16_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x59&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Write 2 LE bytes of lane (&amp;lt; 8) to memory.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.store32_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x5A&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Write 4 LE bytes of lane (&amp;lt; 4) to memory.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;v128.store64_lane&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;0x5B&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Write 8 LE bytes of lane (&amp;lt; 2) to memory.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;relaxed-simd&quot;&gt;Relaxed SIMD&lt;/h2&gt;
&lt;p&gt;The relaxed-SIMD proposal adds 20 sub-opcodes (&lt;code&gt;0x100..0x113&lt;/code&gt;) under the existing &lt;code&gt;0xFD&lt;/code&gt; SIMD prefix. The “relaxed” name reflects that the spec lets each op pick between two or more valid implementations per edge case (NaN handling, out-of-range conversion, sign-extension of partly-used operands); this interpreter pins one deterministic choice each, documented in &lt;code&gt;simd_dispatch.scala&lt;/code&gt; and the SimdRelaxedTests fixtures.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Sub-opcode&lt;/th&gt;&lt;th&gt;Op&lt;/th&gt;&lt;th&gt;Shape&lt;/th&gt;&lt;th&gt;Pinned semantics&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x100&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i8x16.relaxed_swizzle&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;v128, v128 → v128&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Identical to non-relaxed &lt;code&gt;i8x16.swizzle&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x101..0x104&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i32x4.relaxed_trunc_*&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;v128 → v128&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Identical to &lt;code&gt;i32x4.trunc_sat_*&lt;/code&gt; (NaN → 0, overflow saturates).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x105..0x108&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;f*x*.relaxed_(n)madd&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;v128, v128, v128 → v128&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Unfused: &lt;code&gt;(±a*b) + c&lt;/code&gt; per lane. Portable across JVM / Scala.js / Native.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x109..0x10C&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;*.relaxed_laneselect&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;v128, v128, v128 → v128&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Per lane: high bit of &lt;code&gt;mask&lt;/code&gt;‘s lane picks &lt;code&gt;a&lt;/code&gt; (set) or &lt;code&gt;b&lt;/code&gt; (clear).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x10D..0x110&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;f*x*.relaxed_min&lt;/code&gt; / &lt;code&gt;_max&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;v128, v128 → v128&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;java.lang.Math.min&lt;/code&gt; / &lt;code&gt;max&lt;/code&gt; per lane — NaN propagates either way.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x111&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i16x8.relaxed_q15mulr_s&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;v128, v128 → v128&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Saturating signed Q15 multiply: &lt;code&gt;sat_i16((a*b + 0x4000) &amp;gt;&amp;gt; 15)&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x112&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i16x8.relaxed_dot_i8x16_i7x16_s&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;v128, v128 → v128&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Pair-sum of (signed-&lt;code&gt;a&lt;/code&gt; × unsigned-&lt;code&gt;b&lt;/code&gt;) byte products per i16 lane.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x113&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i32x4.relaxed_dot_i8x16_i7x16_add_s&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;v128, v128, v128 → v128&lt;/code&gt;&lt;/td&gt;&lt;td&gt;4-byte (signed × unsigned) sums per i32 lane plus an i32 accumulator.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;threads-atomics&quot;&gt;Threads + atomics&lt;/h2&gt;
&lt;p&gt;The threads proposal adds the &lt;code&gt;0xFE&lt;/code&gt; opcode prefix for atomic memory operations and a &lt;code&gt;shared&lt;/code&gt; flag on memory limits. This interpreter is single-threaded, but the structural guarantees the proposal makes — alignment-checked load/store, single-step read-modify-write, compare-and-swap — all hold by construction. What’s covered:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Sub-opcode&lt;/th&gt;&lt;th&gt;Op&lt;/th&gt;&lt;th&gt;Shape&lt;/th&gt;&lt;th&gt;Notes&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x00&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;memory.atomic.notify&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i32 addr, i32 count → i32&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Always returns 0 (no peer threads).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x01&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;memory.atomic.wait32&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i32 addr, i32 expected, i64 timeout → i32&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Trap if memory unshared; trap “would-block” if value matches expected; else return 1 (not-equal).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x02&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;memory.atomic.wait64&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i32 addr, i64 expected, i64 timeout → i32&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Same as wait32 at 64-bit.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x03&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;atomic.fence&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;→&lt;/code&gt;&lt;/td&gt;&lt;td&gt;No-op (single-threaded host).&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x10..0x16&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i32/i64.atomic.load[8_u/16_u/32_u]&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i32 → i32&lt;/code&gt; or &lt;code&gt;i32 → i64&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Plain load + alignment check.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x17..0x1D&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i32/i64.atomic.store[8/16/32]&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;… → &lt;/code&gt;&lt;/td&gt;&lt;td&gt;Plain store + alignment check.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x1E..0x47&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i32/i64.atomic.rmw{,8,16,32}.{add,sub,and,or,xor,xchg}[_u]&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i32 addr, T v → T old&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Returns OLD value, leaves &lt;code&gt;op(old, v)&lt;/code&gt; in memory.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;0x48..0x4E&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i32/i64.atomic.rmw{,8,16,32}.cmpxchg[_u]&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;i32 addr, T expected, T replacement → T old&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Writes replacement iff old == expected.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Limits-flag encoding:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;flag &amp;amp; 0x01&lt;/code&gt; — has-max (unchanged).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;flag &amp;amp; 0x02&lt;/code&gt; — shared memory. Validator enforces &lt;code&gt;shared ⇒ has-max&lt;/code&gt;. Other bits are rejected with a clear diagnostic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Validator rules unique to atomics:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The memarg’s &lt;code&gt;align&lt;/code&gt; immediate must equal &lt;code&gt;log2(accessWidth)&lt;/code&gt;. Unlike regular load/store (where alignment is advisory), atomic ops require strict natural alignment — and the validator surfaces a mismatch as &lt;code&gt;InvalidModule&lt;/code&gt; at instantiation, not at run time.&lt;/li&gt;
&lt;li&gt;Unknown 0xFE sub-opcodes surface as &lt;code&gt;UnknownOpcode(0xFE)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Runtime traps unique to atomics:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;UnalignedAtomicAccess&lt;/code&gt; — the effective address (base + offset) was not naturally aligned to the access width.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ExpectedSharedMemory&lt;/code&gt; — &lt;code&gt;memory.atomic.wait{32,64}&lt;/code&gt; ran against a non-shared memory.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The “would-block” case for &lt;code&gt;wait*&lt;/code&gt; (the operand value matches expected, so a real implementation would suspend the thread) traps with &lt;code&gt;InvalidModule(&amp;quot;…would block forever on a single-threaded host&amp;quot;)&lt;/code&gt; rather than spinning. The not-equal early-return path is the only observable non-trap result on this interpreter.&lt;/p&gt;
&lt;h2 id=&quot;multi-memory&quot;&gt;Multi-memory&lt;/h2&gt;
&lt;p&gt;Modules may declare any number of linear memories. Each memory opcode threads a &lt;code&gt;memidx&lt;/code&gt; through its immediate:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Load/store memarg&lt;/strong&gt; — the multi-memory encoding repurposes bit 6 of the alignment LEB as a “memidx-present” flag. When set, a memidx LEB follows; alignment is the LEB with that bit cleared. Single-memory modules emit the original shape (no flag, memidx = 0 implicit).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;memory.size&lt;/code&gt; / &lt;code&gt;memory.grow&lt;/code&gt; / &lt;code&gt;memory.fill&lt;/code&gt;&lt;/strong&gt; — the byte that was a must-be-zero reserved slot becomes a memidx LEB.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;memory.copy&lt;/code&gt;&lt;/strong&gt; — two memidx LEBs (dst, src), allowing memory-to-memory copies between distinct memories.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;memory.init&lt;/code&gt;&lt;/strong&gt; — second immediate is a memidx LEB (was reserved).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;ModuleInstance.memories: Array[Memory]&lt;/code&gt; exposes the full vector; &lt;code&gt;.memory&lt;/code&gt; keeps backwards compat returning memory 0. &lt;code&gt;.exportedMemory(name)&lt;/code&gt; resolves an exported memory by name.&lt;/p&gt;
&lt;h2 id=&quot;what-isn-t-implemented-yet&quot;&gt;What isn’t implemented yet&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Group&lt;/th&gt;&lt;th&gt;Sub-opcodes&lt;/th&gt;&lt;th&gt;Status&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;GC proposal&lt;/td&gt;&lt;td&gt;&lt;code&gt;struct.*&lt;/code&gt;, &lt;code&gt;array.*&lt;/code&gt;, &lt;code&gt;ref.cast&lt;/code&gt;, etc.&lt;/td&gt;&lt;td&gt;not planned&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Component model&lt;/td&gt;&lt;td&gt;the packaging proposal&lt;/td&gt;&lt;td&gt;out of scope&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Each missing group is independently scoped — adding any one of them is a self-contained piece of work that doesn’t touch the others. See the project &lt;a href=&quot;https://github.com/edadma/wasm&quot;&gt;roadmap on GitHub&lt;/a&gt; for the active Phase-8 plan.&lt;/p&gt;
&lt;h2 id=&quot;validation-pass&quot;&gt;Validation pass&lt;/h2&gt;
&lt;p&gt;Every imported module runs through a separate validator before any code executes. See &lt;a href=&quot;/wasm/concepts/validation/&quot;&gt;Concepts → Validation&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id=&quot;binary-sections&quot;&gt;Binary sections&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Section&lt;/th&gt;&lt;th&gt;ID&lt;/th&gt;&lt;th&gt;What it carries&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Type&lt;/td&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;Function signatures&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Import&lt;/td&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;Functions, memories, globals, tables imported from the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Function&lt;/td&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;Function-index → type-index mapping&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Table&lt;/td&gt;&lt;td&gt;4&lt;/td&gt;&lt;td&gt;Funcref + externref tables&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Memory&lt;/td&gt;&lt;td&gt;5&lt;/td&gt;&lt;td&gt;Linear-memory definitions&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Global&lt;/td&gt;&lt;td&gt;6&lt;/td&gt;&lt;td&gt;Module-level globals (scalar + reftype)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Export&lt;/td&gt;&lt;td&gt;7&lt;/td&gt;&lt;td&gt;Names exposed to the host&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Start&lt;/td&gt;&lt;td&gt;8&lt;/td&gt;&lt;td&gt;Function index run at instantiate time&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Element&lt;/td&gt;&lt;td&gt;9&lt;/td&gt;&lt;td&gt;Table initializers (funcidx + elemexpr forms, funcref + externref)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Code&lt;/td&gt;&lt;td&gt;10&lt;/td&gt;&lt;td&gt;Function bodies&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Data&lt;/td&gt;&lt;td&gt;11&lt;/td&gt;&lt;td&gt;Linear-memory initializers (active + passive)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;DataCount&lt;/td&gt;&lt;td&gt;12&lt;/td&gt;&lt;td&gt;u32 = number of data segments; required when a function uses &lt;code&gt;memory.init&lt;/code&gt; or &lt;code&gt;data.drop&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Tag&lt;/td&gt;&lt;td&gt;13&lt;/td&gt;&lt;td&gt;Exception tag declarations (attribute byte + typeidx)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Custom sections are skipped harmlessly.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>ModuleInstance</title>
    <link href="https://edadma.github.io/wasm/concepts/module-instance/"/>
    <id>https://edadma.github.io/wasm/concepts/module-instance/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>What `Runtime.instantiate` returns and the public surface for invoking exports, reading globals, and touching linear memory.</summary>
    <content type="html">&lt;p&gt;&lt;code&gt;Runtime.instantiate(bytes, hostModules)&lt;/code&gt; returns &lt;code&gt;Either[WasmError, ModuleInstance]&lt;/code&gt;. The &lt;code&gt;Right&lt;/code&gt; side is a &lt;code&gt;ModuleInstance&lt;/code&gt; — a single, fully-resolved module: imports bound to host functions, data + element segments copied into their target memories / tables, the start function (if any) already invoked. Everything you can do with the module after that goes through this one object.&lt;/p&gt;
&lt;p&gt;This page covers the full public surface — what you can read, what you can call, and how host code talks to wasm memory.&lt;/p&gt;
&lt;h2 id=&quot;the-surface&quot;&gt;The surface&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ModuleInstance&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-comment&quot;&gt;/** All linear memories the module declared, indexed by memidx.&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * Always at least length 1 (zero-memory modules get a synthetic placeholder&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * at index 0). Multi-memory modules carry one entry per declared memory. */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; memories&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Memory&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Convenience accessor for `memories(0)`. Most modules have exactly one&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * memory; this stays the friendly name. */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; memory&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Memory&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Invoke an exported function. Returns `Right(results)` on a normal&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * return, `Left(WasmError)` on any trap or signature mismatch. */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Either&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]]&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Total number of functions the module owns (imported + defined). */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; functionCount&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Sorted list of every exported function name. */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; exportedFunctionNames&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Read an exported global by name. Returns the current value&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * (mutable globals reflect prior `global.set` writes). */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; globalValue&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Either&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Resolve an exported memory by name. For single-memory modules&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * that export memory 0, this is equivalent to `Right(memory)`. */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; exportedMemory&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Either&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Memory&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Resolve an exported table by name. */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; exportedTable&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Either&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;RuntimeTable&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Live cell backing an exported global. Sharing this cell with&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * another module&apos;s import preserves the wasm-spec rule that&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * imported mutable globals alias the exporter&apos;s storage. */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; exportedGlobalCell&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Either&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;GlobalCell&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Mutability flag of an exported global — needed by hosts&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * forwarding one module&apos;s globals as another module&apos;s imports&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * (the wasm mutability-matching rule rejects the import if the&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * advertised flavour is wrong). */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; exportedGlobalMutability&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Either&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Boolean&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Declared signature of an exported function. Needed when&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * re-exporting a function as another module&apos;s host import — the&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * importer needs the callee&apos;s actual `FuncType` to type-check the&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * call site. */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; exportedFunctionType&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Either&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;FuncType&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Enumerated export-name lists, sorted. Sibling to&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * `exportedFunctionNames`; used by hosts that want to walk every&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * exposed binding. */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; exportedMemoryNames&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; exportedTableNames&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; exportedGlobalNames&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Everything else on the class is &lt;code&gt;private[wasm]&lt;/code&gt; and exists for the interpreter’s eval loop.&lt;/p&gt;
&lt;h2 id=&quot;invoking-exports&quot;&gt;Invoking exports&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;invoke&lt;/code&gt; is the workhorse. Arguments are wrapped &lt;code&gt;Value&lt;/code&gt;s (&lt;code&gt;I32&lt;/code&gt;, &lt;code&gt;I64&lt;/code&gt;, &lt;code&gt;F32&lt;/code&gt;, &lt;code&gt;F64&lt;/code&gt;, &lt;code&gt;V128&lt;/code&gt;, &lt;code&gt;RefFunc&lt;/code&gt;, &lt;code&gt;RefExtern&lt;/code&gt;, &lt;code&gt;RefNull&lt;/code&gt;); results come back as a &lt;code&gt;Seq[Value]&lt;/code&gt; in the function’s declared order:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;add&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;sum&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt;     &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;add = $sum&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;other&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;             &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;unexpected shape: $other&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ExportNotFound&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;no export named &apos;$n&apos;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;trap or signature mismatch: $err&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Failure modes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;ExportNotFound(name)&lt;/code&gt;&lt;/strong&gt; — the export doesn’t exist (or names a non-function export).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;TypeMismatch&lt;/code&gt;&lt;/strong&gt; — the args don’t match the function’s declared signature.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Any other &lt;code&gt;WasmError&lt;/code&gt;&lt;/strong&gt; — a runtime trap inside the function (out-of-bounds memory, division by zero, indirect call type mismatch, etc.). See &lt;a href=&quot;/wasm/concepts/traps-and-errors/&quot;&gt;Traps and errors&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;exportedFunctionNames&lt;/code&gt; gives you the full export list, sorted, when you don’t know what’s available up front:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;exportedFunctionNames&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;foreach&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;println&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what &lt;code&gt;wasm --list-exports&lt;/code&gt; uses internally.&lt;/p&gt;
&lt;h2 id=&quot;reading-globals&quot;&gt;Reading globals&lt;/h2&gt;
&lt;p&gt;Mutable globals are live state — &lt;code&gt;invoke&lt;/code&gt; calls can modify them, and &lt;code&gt;globalValue&lt;/code&gt; reads them at whatever value they happen to hold right now:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;globalValue&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;counter&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;counter = $n&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;other&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;  &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;unexpected type: $other&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;     &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;no such global: $err&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;bump&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;               &lt;span class=&quot;hl-comment&quot;&gt;// module mutates `counter`&lt;/span&gt;
inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;globalValue&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;counter&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;       &lt;span class=&quot;hl-comment&quot;&gt;// new value visible here&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Only globals listed in the module’s export section are reachable through &lt;code&gt;globalValue&lt;/code&gt;. Module-internal globals stay private.&lt;/p&gt;
&lt;h2 id=&quot;reading-writing-memory&quot;&gt;Reading + writing memory&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;memory&lt;/code&gt; exposes the underlying &lt;code&gt;Memory&lt;/code&gt; for memory 0. Multi-memory modules can reach the rest via &lt;code&gt;memories(idx)&lt;/code&gt; or &lt;code&gt;exportedMemory(name)&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Memory&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-comment&quot;&gt;/** The full byte buffer. Mutable — host code may read or write directly.&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * Resized in place when wasm code runs `memory.grow`. */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;var&lt;/span&gt; data&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Byte&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Bytes currently allocated (= currentPages * 65536). */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; size&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Current and maximum page counts. A page is 64 KiB. */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;var&lt;/span&gt; currentPages&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; maxPages&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;/** Grow by `delta` 64-KiB pages. Returns the previous page count on&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * success, or -1 if the grow would exceed `maxPages` or the platform&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;    * cap. `delta == 0` is a permitted no-op. */&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; grow&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;delta&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Memory is byte-addressable and little-endian, matching the WebAssembly spec. Host code can read or write &lt;code&gt;data&lt;/code&gt; directly — the interpreter holds no separate “live view” of memory; what you see is what wasm sees:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-comment&quot;&gt;// Plant a UTF-8 string at offset 0&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; msg &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;Hello, host!&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;getBytes&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;UTF-8&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;arraycopy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;memory&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; msg&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-comment&quot;&gt;// Now wasm can read from address 0 with i32.load8_u + a length argument&lt;/span&gt;
inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;print_at&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;length&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Conversely, after a module writes through &lt;code&gt;memory.store&lt;/code&gt;, the host reads the same bytes:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;compute_into&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt;     &lt;span class=&quot;hl-comment&quot;&gt;// writes 16 bytes at offset 64&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; out &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; java&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;util&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Arrays&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;copyOfRange&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;memory&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;80&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;memories&lt;/code&gt; (the full vector) and &lt;code&gt;exportedMemory(name)&lt;/code&gt; matter only for multi-memory modules — single-memory programs can stay on &lt;code&gt;.memory&lt;/code&gt; forever.&lt;/p&gt;
&lt;h2 id=&quot;concurrency&quot;&gt;Concurrency&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;ModuleInstance&lt;/code&gt; is &lt;strong&gt;not&lt;/strong&gt; thread-safe. &lt;code&gt;invoke&lt;/code&gt; mutates the interpreter’s per-instance value stack, frame stack, and memory; running two &lt;code&gt;invoke&lt;/code&gt; calls against the same instance from two threads will corrupt that state. If you need concurrency, instantiate the same &lt;code&gt;bytes&lt;/code&gt; multiple times — instantiation is cheap and produces independent state.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;bytes&lt;/code&gt; array passed to &lt;code&gt;Runtime.instantiate&lt;/code&gt; is read-only after parsing; sharing it across multiple &lt;code&gt;instantiate&lt;/code&gt; calls is safe and is the recommended pattern for “fork an isolated copy of this module per request”.&lt;/p&gt;
&lt;h2 id=&quot;lifecycle&quot;&gt;Lifecycle&lt;/h2&gt;
&lt;p&gt;There is no explicit &lt;code&gt;close&lt;/code&gt; or &lt;code&gt;dispose&lt;/code&gt; on &lt;code&gt;ModuleInstance&lt;/code&gt;. Drop the reference and the GC reclaims it — the underlying &lt;code&gt;Memory.data&lt;/code&gt; arrays are plain &lt;code&gt;Array[Byte]&lt;/code&gt;, no off-heap allocation. Host functions registered through &lt;code&gt;HostModule&lt;/code&gt; are also pure Scala values; they’re collected when no instance references them.&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/concepts/host-imports/&quot;&gt;Host imports&lt;/a&gt; — wire your own functions into a module.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/concepts/traps-and-errors/&quot;&gt;Traps and errors&lt;/a&gt; — the &lt;code&gt;WasmError&lt;/code&gt; model that surfaces through &lt;code&gt;invoke&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/reference/errors/&quot;&gt;Reference → Errors&lt;/a&gt; — every error variant in one table.&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Installation</title>
    <link href="https://edadma.github.io/wasm/getting-started/installation/"/>
    <id>https://edadma.github.io/wasm/getting-started/installation/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>Add `io.github.edadma:wasm:0.4.0` (and optionally `wasm-wasi`) to your sbt build, or work from a local checkout for development.</summary>
    <content type="html">&lt;h2 id=&quot;maven-central&quot;&gt;Maven Central&lt;/h2&gt;
&lt;p&gt;Released artifacts (Scala 3, cross-built on JVM / Scala.js / Scala Native):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;libraryDependencies &lt;span class=&quot;hl-keyword&quot;&gt;++=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;io.github.edadma&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%%%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;wasm&amp;quot;&lt;/span&gt;      &lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;0.4.0&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;io.github.edadma&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%%%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;wasm-wasi&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;%&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;0.4.0&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;hl-comment&quot;&gt;// optional — only if you want the WASI shim&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;%%%&lt;/code&gt; (triple &lt;code&gt;%&lt;/code&gt;) is the &lt;a href=&quot;https://github.com/portable-scala/sbt-crossproject&quot;&gt;sbt-crossproject&lt;/a&gt; form that picks the right artifact for each platform — drop one &lt;code&gt;%&lt;/code&gt; if you’re on a non-cross build. The CLI (&lt;code&gt;wasm-cli&lt;/code&gt;) is built from this repo but is not published; it’s a runnable example, not a library.&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Coordinate&lt;/th&gt;&lt;th&gt;What you get&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;io.github.edadma:wasm:0.4.0&lt;/code&gt;&lt;/td&gt;&lt;td&gt;The interpreter — &lt;code&gt;Runtime.instantiate&lt;/code&gt;, &lt;code&gt;ModuleInstance&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;io.github.edadma:wasm-wasi:0.4.0&lt;/code&gt;&lt;/td&gt;&lt;td&gt;The WASI Preview 1 host shim — depends on &lt;code&gt;wasm&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The &lt;code&gt;wasm&lt;/code&gt; artifact has zero external runtime dependencies; the &lt;code&gt;wasm-wasi&lt;/code&gt; artifact depends only on &lt;code&gt;wasm&lt;/code&gt;.&lt;/p&gt;
&lt;h2 id=&quot;from-source&quot;&gt;From source&lt;/h2&gt;
&lt;p&gt;If you want to build the repo locally — to run the test suite, hack on the interpreter, or use the CLI runner — clone and run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;git&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;clone&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;https://github.com/edadma/wasm.git&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;cd&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;wasm&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;test&lt;/span&gt;                                              &lt;span class=&quot;hl-comment&quot;&gt;# whole tree, all backends&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;#&lt;/span&gt; whole tree, all backends
&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;interpJVM/Test/run&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;                              # 653 interpreter tests, JVM
sbt &lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;wasiJVM/Test/run&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;                                # 209 WASI tests, JVM
sbt &lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;cliJVM/Test/run&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;                                 # 19 CLI tests&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The aggregate &lt;code&gt;sbt test&lt;/code&gt; runs the interpreter and WASI suites on JVM, Scala.js (Node 20+), and Scala Native, plus the CLI suite on the JVM. &lt;strong&gt;881 tests&lt;/strong&gt; total on JVM; the interpreter and WASI also pass on JS and Native.&lt;/p&gt;
&lt;h2 id=&quot;linking-against-a-local-checkout&quot;&gt;Linking against a local checkout&lt;/h2&gt;
&lt;p&gt;If you’re hacking on the interpreter and want a downstream project to pick up your changes without a &lt;code&gt;publishSigned&lt;/code&gt; round-trip, point the downstream sbt build at the local source tree:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;lazy&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; wasm &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ProjectRef&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;../wasm&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;interpJVM&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;lazy&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; wasi &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ProjectRef&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;../wasm&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;wasiJVM&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;lazy&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; myProject &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; project
  &lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;dependsOn&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; wasi&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The same trick works for &lt;code&gt;cliJVM&lt;/code&gt;, &lt;code&gt;interpJS&lt;/code&gt;, &lt;code&gt;interpNative&lt;/code&gt;, etc.&lt;/p&gt;
&lt;h2 id=&quot;cross-build-matrix&quot;&gt;Cross-build matrix&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Target&lt;/th&gt;&lt;th&gt;Version&lt;/th&gt;&lt;th&gt;Runtime requirement&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Scala&lt;/td&gt;&lt;td&gt;3.8.3&lt;/td&gt;&lt;td&gt;—&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;JVM&lt;/td&gt;&lt;td&gt;—&lt;/td&gt;&lt;td&gt;Java 17 or newer&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Scala.js&lt;/td&gt;&lt;td&gt;1.21.0&lt;/td&gt;&lt;td&gt;Node.js 20+&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Scala Native&lt;/td&gt;&lt;td&gt;0.5.11&lt;/td&gt;&lt;td&gt;Clang&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;verifying-the-install&quot;&gt;Verifying the install&lt;/h2&gt;
&lt;p&gt;Drop this in any &lt;code&gt;@main&lt;/code&gt; source file inside the cloned repo, run with &lt;code&gt;sbt &apos;interpJVM/run&apos;&lt;/code&gt;, and you should see &lt;code&gt;120&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

@main &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; smoke&lt;span class=&quot;hl-punctuation&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;hl-comment&quot;&gt;// The committed `fact.wasm` fixture is a factorial export — fact(5) = 120.&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; bytes &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; java&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;nio&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;file&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Files&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;readAllBytes&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    java&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;nio&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;file&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Paths&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;get&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;interp/shared/src/test/resources/fixtures/fact.wasm&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-type&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;instantiate&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bytes&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;EnvModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
    &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;inst&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
      inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;fact&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
        &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;hl-punctuation&quot;&gt;)))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;v&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;           &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;no result&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;          &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;trap: $err&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;instantiate failed: $err&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If you see &lt;code&gt;120&lt;/code&gt;, you’re done. Move on to the &lt;a href=&quot;/wasm/getting-started/quickstart/&quot;&gt;Quickstart&lt;/a&gt; for a tour of the API surface most callers actually use.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Host imports</title>
    <link href="https://edadma.github.io/wasm/concepts/host-imports/"/>
    <id>https://edadma.github.io/wasm/concepts/host-imports/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>How guest modules reach into Scala — the `HostModule` surface for functions, globals, memories, and tables.</summary>
    <content type="html">&lt;p&gt;WebAssembly modules can declare imports of four kinds: functions, globals, memories, and tables. The &lt;code&gt;interp&lt;/code&gt; library exposes all four through the &lt;code&gt;HostModule&lt;/code&gt; trait — guest declarations like &lt;code&gt;(import &amp;quot;env&amp;quot; &amp;quot;foo&amp;quot; (func ...))&lt;/code&gt;, &lt;code&gt;(import &amp;quot;env&amp;quot; &amp;quot;bar&amp;quot; (global ...))&lt;/code&gt;, &lt;code&gt;(import &amp;quot;env&amp;quot; &amp;quot;mem&amp;quot; (memory ...))&lt;/code&gt;, and &lt;code&gt;(import &amp;quot;env&amp;quot; &amp;quot;tab&amp;quot; (table ...))&lt;/code&gt; all resolve against the host modules you hand to &lt;code&gt;Runtime.instantiate&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; functions&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;      &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostFunc&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;        &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; functionsMulti&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostFuncMulti&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;   &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; globals&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;        &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostGlobal&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;      &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; memories&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;       &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Memory&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;          &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; tables&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;         &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;RuntimeTable&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;    &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty

&lt;span class=&quot;hl-keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostFunc&lt;/span&gt;      &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Memory&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;             &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostFuncMulti&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;IndexedSeq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Memory&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;

&lt;span class=&quot;hl-comment&quot;&gt;// HostGlobal is internally backed by a GlobalCell. Two factories:&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;//   - HostGlobal(vt, mut, value)        — wraps the value in a fresh cell&lt;/span&gt;
&lt;span class=&quot;hl-comment&quot;&gt;//   - HostGlobal.live(vt, mut, cell)    — shares an externally-owned cell&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;GlobalCell&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;var&lt;/span&gt; value&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A &lt;code&gt;HostFunc&lt;/code&gt; takes the guest’s &lt;code&gt;Memory&lt;/code&gt; instance (memidx 0) plus a sequence of &lt;code&gt;Value&lt;/code&gt; arguments matching the import’s declared signature, and returns a sequence of &lt;code&gt;Value&lt;/code&gt; results matching the import’s declared results. Pure functions, no &lt;code&gt;Future&lt;/code&gt; / &lt;code&gt;IO&lt;/code&gt; wrapping.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;HostFuncMulti&lt;/code&gt; takes the guest’s full vector of memories (length ≥ 1) instead of just memidx 0 — useful only for multi-memory modules. Single-memory programs should stay on &lt;code&gt;HostFunc&lt;/code&gt;; multi-memory hosts that need to inspect or write a non-zero memidx use &lt;code&gt;HostFuncMulti&lt;/code&gt;. A name registered in &lt;em&gt;both&lt;/em&gt; maps resolves to the multi-memory form.&lt;/p&gt;
&lt;p&gt;A &lt;code&gt;HostGlobal&lt;/code&gt; is a typed global the host exposes for guest modules to import as &lt;code&gt;(import &amp;quot;...&amp;quot; &amp;quot;...&amp;quot; (global &amp;lt;type&amp;gt;))&lt;/code&gt;. The declared &lt;code&gt;valueType&lt;/code&gt; and &lt;code&gt;mutable&lt;/code&gt; flag must match the import declaration exactly. Internally, every &lt;code&gt;HostGlobal&lt;/code&gt; is backed by a &lt;code&gt;GlobalCell&lt;/code&gt; — a tiny mutable holder for one &lt;code&gt;Value&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;HostGlobal(vt, mut, value)&lt;/code&gt; wraps the literal in a &lt;em&gt;fresh&lt;/em&gt; cell that nobody else holds a reference to. Guest &lt;code&gt;global.set&lt;/code&gt; updates this private cell. Use this for immutable globals (where there’s nothing to share anyway) and for mutable globals the host doesn’t need to read back.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;HostGlobal.live(vt, mut, cell)&lt;/code&gt; shares an externally-owned cell. The host keeps a reference; guest writes flow through the same storage; the host can read and write the cell directly. This is how to satisfy the wasm-3.0 spec’s “imported mutable globals alias the exporter’s storage” rule when forwarding one module’s exports as another module’s imports.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A &lt;code&gt;Memory&lt;/code&gt; or &lt;code&gt;RuntimeTable&lt;/code&gt; exposed via &lt;code&gt;memories&lt;/code&gt; / &lt;code&gt;tables&lt;/code&gt; forwards by reference — guest reads and writes hit the same backing array the host can inspect. Limits checking happens at instantiation: the host’s current size must be at least the importing module’s declared min, and the host’s max (if any) must be at most the module’s declared max (if any). Reftype must match for tables; shared-vs-unshared must match for memories.&lt;/p&gt;
&lt;h2 id=&quot;envmodule-default&quot;&gt;EnvModule.default&lt;/h2&gt;
&lt;p&gt;The interpreter ships one host module out of the box:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;hl-type&quot;&gt;EnvModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default     &lt;span class=&quot;hl-comment&quot;&gt;// module name: &amp;quot;env&amp;quot;, one function: &amp;quot;putchar&amp;quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;env.putchar(i32) → ()&lt;/code&gt; writes the low byte of its argument to &lt;code&gt;System.out&lt;/code&gt;. It’s the minimal “I can print” surface — modules compiled by lightweight toolchains use it when no WASI runtime is available.&lt;/p&gt;
&lt;p&gt;To redirect the output (tests, in-process capture, custom encoding):&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; buf &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;new&lt;/span&gt; java&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;lang&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;StringBuilder&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;EnvModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;withWriter&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;c &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; buf&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;append&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;c&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;toChar&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// returns a HostModule&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;defining-your-own-hostmodule&quot;&gt;Defining your own HostModule&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Logger&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;logger&amp;quot;&lt;/span&gt;

  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; functions&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostFunc&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;log_i32&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Memory&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;level&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;code&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; _ &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; args&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; @unchecked
      println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;log level=$level code=$code&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty
    &lt;span class=&quot;hl-punctuation&quot;&gt;},&lt;/span&gt;

    &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;log_str&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;mem&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Memory&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;hl-comment&quot;&gt;// Convention: caller pushes (ptr: i32, len: i32) onto the stack.&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;ptr&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;len&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; _ &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; args&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; @unchecked
      &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; bytes &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Byte&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;](&lt;/span&gt;len&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;hl-type&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;arraycopy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;mem&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; ptr&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; bytes&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; len&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bytes&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;UTF-8&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;
      &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty
    &lt;span class=&quot;hl-punctuation&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;hl-comment&quot;&gt;// Pass it alongside whatever else the module needs:&lt;/span&gt;
&lt;span class=&quot;hl-type&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;instantiate&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;bytes&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;EnvModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Logger&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;inst&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; inst&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;invoke&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;main&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;err&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The guest module’s import section declares the imports by &lt;code&gt;(module, name)&lt;/code&gt; pair:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-wat&quot;&gt;(module
  (import &amp;quot;logger&amp;quot; &amp;quot;log_str&amp;quot; (func $log (param i32 i32)))
  ...
  (call $log (i32.const 100) (i32.const 13)))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The interpreter looks up &lt;code&gt;&amp;quot;logger&amp;quot;&lt;/code&gt; in the supplied list of host modules, then &lt;code&gt;&amp;quot;log_str&amp;quot;&lt;/code&gt; in that module’s &lt;code&gt;functions&lt;/code&gt; map. Missing module: &lt;code&gt;Left(UnknownImport(&amp;quot;logger&amp;quot;, &amp;quot;log_str&amp;quot;))&lt;/code&gt; at instantiate time. Missing function in a found module: same error.&lt;/p&gt;
&lt;h2 id=&quot;memory-access-from-host-code&quot;&gt;Memory access from host code&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Memory.data&lt;/code&gt; is a public &lt;code&gt;Array[Byte]&lt;/code&gt; — host functions read and write the guest’s linear memory directly through it. Bounds-checking is the host function’s responsibility (the array’s own &lt;code&gt;IndexOutOfBoundsException&lt;/code&gt; is what fires on an out-of-range access, and the interpreter doesn’t catch that on the host side). Use &lt;code&gt;mem.size&lt;/code&gt; to find the current byte length; use &lt;code&gt;mem.currentPages&lt;/code&gt; and &lt;code&gt;mem.maxPages&lt;/code&gt; for the page-count view.&lt;/p&gt;
&lt;p&gt;For multi-byte loads, wasm is little-endian:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; readI32&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;mem&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Memory&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; addr&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;mem&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;addr&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;     &amp;amp; &lt;span class=&quot;hl-number&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;        |
  &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;mem&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;addr &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &amp;amp; &lt;span class=&quot;hl-number&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt;  &lt;span class=&quot;hl-number&quot;&gt;8&lt;/span&gt;  |
  &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;mem&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;addr &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &amp;amp; &lt;span class=&quot;hl-number&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;16&lt;/span&gt;  |
  &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;mem&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;addr &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &amp;amp; &lt;span class=&quot;hl-number&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;24&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; writeI32&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;mem&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Memory&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; addr&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt;
  mem&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;addr&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;     &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;v        &amp;amp; &lt;span class=&quot;hl-number&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;).&lt;/span&gt;toByte
  mem&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;addr &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;((&lt;/span&gt;v &lt;span class=&quot;hl-keyword&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt;  &lt;span class=&quot;hl-number&quot;&gt;8&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &amp;amp; &lt;span class=&quot;hl-number&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;).&lt;/span&gt;toByte
  mem&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;addr &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;((&lt;/span&gt;v &lt;span class=&quot;hl-keyword&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &amp;amp; &lt;span class=&quot;hl-number&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;).&lt;/span&gt;toByte
  mem&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;addr &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;((&lt;/span&gt;v &lt;span class=&quot;hl-keyword&quot;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;24&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &amp;amp; &lt;span class=&quot;hl-number&quot;&gt;0xff&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;).&lt;/span&gt;toByte&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The WASI shim wraps these patterns; if you find yourself writing them repeatedly, see &lt;a href=&quot;https://github.com/edadma/wasm/blob/stable/wasi/shared/src/main/scala/io/github/edadma/wasm/wasi/Wasi.scala&quot;&gt;&lt;code&gt;wasi/Wasi.scala&lt;/code&gt;&lt;/a&gt; for production-quality versions.&lt;/p&gt;
&lt;h2 id=&quot;why-not-a-typed-binding-generator&quot;&gt;Why not a typed binding generator?&lt;/h2&gt;
&lt;p&gt;The host-import surface is deliberately a &lt;code&gt;Map[String, HostFunc]&lt;/code&gt; of dynamically-typed functions rather than a typed binding generated from &lt;code&gt;.wit&lt;/code&gt; or &lt;code&gt;.wasm&lt;/code&gt;. Two reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Zero external dependencies.&lt;/strong&gt; No code-gen step at compile time means no Scala-version constraint and no annotation-processor wiring. Drop the jar on the classpath and go.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Multi-platform shared code.&lt;/strong&gt; The same &lt;code&gt;HostModule&lt;/code&gt; definition compiles for JVM, Scala.js, and Scala Native without &lt;code&gt;@JSExport&lt;/code&gt; / &lt;code&gt;@ExportTopLevel&lt;/code&gt; annotations — the &lt;code&gt;Value&lt;/code&gt; types are platform-agnostic.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For a stronger-typed wrapper, a separate &lt;code&gt;wasm-bindgen&lt;/code&gt;-style library could be layered on top without touching the &lt;code&gt;interp&lt;/code&gt; core.&lt;/p&gt;
&lt;h2 id=&quot;multi-memory-host-functions&quot;&gt;Multi-memory host functions&lt;/h2&gt;
&lt;p&gt;When a guest module declares more than one linear memory (a multi-memory proposal feature; see &lt;a href=&quot;/wasm/reference/opcodes/&quot;&gt;Opcodes → Multi-memory&lt;/a&gt;), &lt;code&gt;HostFunc&lt;/code&gt; only gets a handle to memidx 0. Register through &lt;code&gt;functionsMulti&lt;/code&gt; instead to receive the whole &lt;code&gt;IndexedSeq[Memory]&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;DualBuffer&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;dual&amp;quot;&lt;/span&gt;

  &lt;span class=&quot;hl-keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; functionsMulti&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostFuncMulti&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Map&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;hl-comment&quot;&gt;// Copies one byte from mem0 into mem1 at the same offset.&lt;/span&gt;
    &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;mirror&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;mems&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;IndexedSeq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Memory&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;],&lt;/span&gt; args&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;I32&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;addr&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; _ &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; args&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; @unchecked
      mems&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;).&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;addr&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; mems&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;).&lt;/span&gt;data&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;addr&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty
    &lt;span class=&quot;hl-punctuation&quot;&gt;},&lt;/span&gt;
  &lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The vector is always length ≥ 1 — zero-memory modules get a synthetic placeholder at index 0 so &lt;code&gt;mems.head&lt;/code&gt; is safe to dereference. Single-memory &lt;code&gt;HostFunc&lt;/code&gt; registrations continue to work unchanged against single-memory modules; the runtime wraps them at import-resolution time so they always see &lt;code&gt;mems.head&lt;/code&gt;.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Flags</title>
    <link href="https://edadma.github.io/wasm/cli/flags/"/>
    <id>https://edadma.github.io/wasm/cli/flags/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>Every CLI option, what it does, and the dispatch rules between them.</summary>
    <content type="html">&lt;h2 id=&quot;file-positional&quot;&gt;&lt;code&gt;&amp;lt;file&amp;gt;&lt;/code&gt; (positional)&lt;/h2&gt;
&lt;p&gt;Path to the &lt;code&gt;.wasm&lt;/code&gt; binary to run. Required. The file is read as raw bytes; nothing about the path matters beyond that — there’s no virtual-fs lookup, no env-var substitution.&lt;/p&gt;
&lt;h2 id=&quot;wasi-args-trailing-positional&quot;&gt;&lt;code&gt;&amp;lt;wasi-args&amp;gt;...&lt;/code&gt; (trailing positional)&lt;/h2&gt;
&lt;p&gt;Anything positional after &lt;code&gt;&amp;lt;file&amp;gt;&lt;/code&gt; is captured and passed to the WASI program’s &lt;code&gt;_start&lt;/code&gt; as &lt;code&gt;argv[1..]&lt;/code&gt;. &lt;code&gt;argv[0]&lt;/code&gt; is synthesised from the file basename (stripped of any &lt;code&gt;.wasm&lt;/code&gt; suffix), matching the wasmtime / wasmer convention.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;cliJVM/run --preopen ./data:/data examples/rust/word_count.wasm /data/input.txt&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;
# argv inside word_count.wasm: [&amp;quot;word_count&amp;quot;, &amp;quot;/data/input.txt&amp;quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If any value starts with &lt;code&gt;-&lt;/code&gt; it would otherwise be parsed as a CLI flag — use the standard POSIX &lt;code&gt;--&lt;/code&gt; separator to stop option parsing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;cliJVM/run examples/rust/word_count.wasm -- -v --quiet /data/input.txt&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;
# argv: [&amp;quot;word_count&amp;quot;, &amp;quot;-v&amp;quot;, &amp;quot;--quiet&amp;quot;, &amp;quot;/data/input.txt&amp;quot;]&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Non-WASI modules (&lt;code&gt;main&lt;/code&gt; or &lt;code&gt;--invoke&lt;/code&gt;-named exports) don’t see these args — &lt;code&gt;argv&lt;/code&gt; is a WASI concept; non-WASI exports take &lt;code&gt;i32&lt;/code&gt; operands through &lt;a href=&quot;/wasm/#--a---args-n1n2&quot;&gt;&lt;code&gt;--args&lt;/code&gt;&lt;/a&gt; instead.&lt;/p&gt;
&lt;h2 id=&quot;i-invoke-export&quot;&gt;&lt;code&gt;-i&lt;/code&gt;, &lt;code&gt;--invoke &amp;lt;export&amp;gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Name of the export to invoke. Overrides the default dispatch. Use this when the module exports neither &lt;code&gt;_start&lt;/code&gt; nor &lt;code&gt;main&lt;/code&gt;, or when you want to run a specific function instead of the default entry point:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;cliJVM/run --invoke fact -a 5 examples/fact.wasm&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;
# 120&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;a-args-n1-n2&quot;&gt;&lt;code&gt;-a&lt;/code&gt;, &lt;code&gt;--args n1,n2,...&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Comma-separated decimal &lt;code&gt;i32&lt;/code&gt; values to pass as arguments to the invoked export. Each &lt;code&gt;n&lt;/code&gt; is parsed as &lt;code&gt;Int.parseInt(_)&lt;/code&gt;, so values must fit in a signed 32-bit int. Negative numbers, hex, and floats are not accepted.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--args&lt;/code&gt; only applies when the dispatch is not &lt;code&gt;_start&lt;/code&gt; (the WASI command-mode entry point takes no wasm-level arguments — its argv comes from the WASI context, populated by trailing positional args after &lt;code&gt;&amp;lt;file&amp;gt;&lt;/code&gt;; see &lt;a href=&quot;/wasm/#wasi-args-trailing-positional&quot;&gt;&lt;code&gt;&amp;lt;wasi-args&amp;gt;...&lt;/code&gt;&lt;/a&gt; above). When dispatch is &lt;code&gt;main&lt;/code&gt; or an &lt;code&gt;--invoke&lt;/code&gt;-named export, the &lt;code&gt;--args&lt;/code&gt; values are pushed onto the operand stack in order before the call.&lt;/p&gt;
&lt;h2 id=&quot;list-exports&quot;&gt;&lt;code&gt;--list-exports&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Prints every exported function name on its own line and exits with status 0. No instantiation traps run, no &lt;code&gt;_start&lt;/code&gt; fires, no preopens are needed.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;cliJVM/run --list-exports interp/shared/src/test/resources/fixtures/fact.wasm&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;
# fact
# memory&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;e-env-key-value&quot;&gt;&lt;code&gt;-e&lt;/code&gt;, &lt;code&gt;--env &amp;lt;key&amp;gt;=&amp;lt;value&amp;gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Sets an environment variable for the WASI program. Repeatable; each &lt;code&gt;--env&lt;/code&gt; adds one entry to &lt;code&gt;WasiContext.envs&lt;/code&gt; in order. Empty keys (&lt;code&gt;=value&lt;/code&gt;) are rejected; values may themselves contain &lt;code&gt;=&lt;/code&gt; signs (&lt;code&gt;PATH=/usr/bin:/bin&lt;/code&gt; is fine).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;cliJVM/run -e HOME=/root -e LANG=C.UTF-8 my-program.wasm&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From the guest’s perspective, these reach userspace through &lt;code&gt;environ_sizes_get&lt;/code&gt; + &lt;code&gt;environ_get&lt;/code&gt;. &lt;code&gt;std::env::vars()&lt;/code&gt; in Rust, &lt;code&gt;getenv&lt;/code&gt; in C through wasi-libc.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;--env&lt;/code&gt; is for WASI programs specifically — non-WASI modules don’t have an environ surface.&lt;/p&gt;
&lt;h2 id=&quot;p-preopen-host-path-virtual-name&quot;&gt;&lt;code&gt;-p&lt;/code&gt;, &lt;code&gt;--preopen &amp;lt;host-path&amp;gt;:&amp;lt;virtual-name&amp;gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Mounts a real host directory as a wasi preopen, backed by &lt;code&gt;HostPreopen.fromDir&lt;/code&gt;. Repeatable; each &lt;code&gt;--preopen&lt;/code&gt; adds one entry to the WASI context’s preopen list in order.&lt;/p&gt;
&lt;p&gt;The split is on the &lt;strong&gt;last&lt;/strong&gt; colon in the spec, so Windows-style paths like &lt;code&gt;C:\data:/data&lt;/code&gt; parse correctly (&lt;code&gt;C:\data&lt;/code&gt; host → &lt;code&gt;/data&lt;/code&gt; virtual). The virtual name should start with &lt;code&gt;/&lt;/code&gt; — that’s the convention wasi-libc expects, and it’s what guest code matches &lt;code&gt;path_open&lt;/code&gt; calls against.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;cliJVM/run -p /tmp/sandbox:/sandbox -p /var/log:/logs my-program.wasm&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If a &lt;code&gt;--preopen&lt;/code&gt;‘s host path doesn’t exist or isn’t a directory, the CLI exits with an error before instantiating the module.&lt;/p&gt;
&lt;h2 id=&quot;stdin-path&quot;&gt;&lt;code&gt;--stdin &amp;lt;path&amp;gt;&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Redirects the guest’s fd 0 (stdin) to read from a host file. The file is read once at startup and streamed byte-for-byte through &lt;code&gt;fd_read&lt;/code&gt; until exhausted (subsequent reads return 0 = EOF).&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;echo&lt;/span&gt; &amp;quot;hello&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;&lt;/span&gt; &amp;gt; /tmp/in.txt
sbt &apos;cliJVM/run --stdin /tmp/in.txt examples/wc.wasm&apos;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When omitted, fd 0 reads return 0 (EOF) immediately — wasi programs that probe stdin without writing prerequisites still terminate cleanly. A non-existent &lt;code&gt;--stdin&lt;/code&gt; path is rejected with a clear diagnostic before instantiation.&lt;/p&gt;
&lt;p&gt;Non-WASI modules never call &lt;code&gt;fd_read&lt;/code&gt;, so this flag is inert for them.&lt;/p&gt;
&lt;h2 id=&quot;trace&quot;&gt;&lt;code&gt;--trace&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Installs a counting &lt;a href=&quot;/wasm/concepts/tracer/&quot;&gt;&lt;code&gt;Tracer&lt;/code&gt;&lt;/a&gt; on the run and prints the totals to stderr after &lt;code&gt;_start&lt;/code&gt; / &lt;code&gt;main&lt;/code&gt; / &lt;code&gt;--invoke&lt;/code&gt; returns:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;sbt&lt;/span&gt; &apos;cliJVM/run --trace examples/hello.wasm&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;
Hello, world!
[trace] ops=53 calls=2 hostCalls=14 throws=0 traps=0 maxDepth=2&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Counts are: opcode dispatches (&lt;code&gt;ops&lt;/code&gt;), wasm-defined function calls (&lt;code&gt;calls&lt;/code&gt;), host-function calls (&lt;code&gt;hostCalls&lt;/code&gt;), &lt;code&gt;throw&lt;/code&gt; opcodes (&lt;code&gt;throws&lt;/code&gt;), traps surfaced as &lt;code&gt;WasmError&lt;/code&gt; (&lt;code&gt;traps&lt;/code&gt;), and the maximum wasm-frame depth observed at any point.&lt;/p&gt;
&lt;p&gt;Use this for quick performance / instrumentation triage. For finer-grained tracing (per-block, per-branch), write your own &lt;code&gt;Tracer&lt;/code&gt; implementation and wire it through the library API — the CLI’s &lt;code&gt;--trace&lt;/code&gt; deliberately picks the minimal default.&lt;/p&gt;
&lt;h2 id=&quot;validate-only&quot;&gt;&lt;code&gt;--validate-only&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Parse and validate the module, then exit. No instantiation, no start function, no host imports needed. Useful as a CI lint step on generated wasm:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;hl-function&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;wasm-cli&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;--validate-only&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;build/output.wasm&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;build/output.wasm:&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;ok&lt;/span&gt;

&lt;span class=&quot;hl-function&quot;&gt;$&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;wasm-cli&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;--validate-only&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;build/broken.wasm&lt;/span&gt;
&lt;span class=&quot;hl-function&quot;&gt;build/broken.wasm:&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;InvalidModule(function&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;0:&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;byte&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;offset&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;0x12:&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;expected&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;i32,&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;got&lt;/span&gt; &lt;span class=&quot;hl-function&quot;&gt;i64&lt;/span&gt;)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Exit code is 0 if the binary parses and the validator accepts every function body; 1 with the diagnostic otherwise. Any other CLI flags (&lt;code&gt;--preopen&lt;/code&gt;, &lt;code&gt;--invoke&lt;/code&gt;, etc.) are ignored when &lt;code&gt;--validate-only&lt;/code&gt; is set.&lt;/p&gt;
&lt;h2 id=&quot;help-version&quot;&gt;&lt;code&gt;--help&lt;/code&gt; / &lt;code&gt;--version&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Standard. &lt;code&gt;--help&lt;/code&gt; prints the synopsis above; &lt;code&gt;--version&lt;/code&gt; prints &lt;code&gt;wasm 0.4.0&lt;/code&gt; and exits.&lt;/p&gt;
&lt;h2 id=&quot;dispatch-rules&quot;&gt;Dispatch rules&lt;/h2&gt;
&lt;p&gt;The CLI picks what to invoke based on what’s exported:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;If &lt;code&gt;--invoke&lt;/code&gt; is given, that export is invoked. &lt;code&gt;_start&lt;/code&gt; and WASI dispatch are bypassed.&lt;/li&gt;
&lt;li&gt;Otherwise, if the module exports &lt;code&gt;_start&lt;/code&gt;, it’s treated as a WASI command-mode binary. The CLI runs it through &lt;code&gt;Wasi.run&lt;/code&gt;, so &lt;code&gt;proc_exit(N)&lt;/code&gt; becomes the process exit code, args/envs/preopens go through &lt;code&gt;WasiContext&lt;/code&gt;, trailing positional args become &lt;code&gt;argv[1..]&lt;/code&gt;, and &lt;code&gt;--args&lt;/code&gt; is ignored.&lt;/li&gt;
&lt;li&gt;Otherwise, if the module exports &lt;code&gt;main&lt;/code&gt;, that’s invoked. &lt;code&gt;--args&lt;/code&gt; is passed to it as &lt;code&gt;i32&lt;/code&gt; operands; trailing positional args are ignored.&lt;/li&gt;
&lt;li&gt;Otherwise, the CLI exits with &lt;code&gt;ExportNotFound(&amp;quot;_start&amp;quot;)&lt;/code&gt; (the default it tried first).&lt;/li&gt;
&lt;/ol&gt;
&lt;h2 id=&quot;exit-codes&quot;&gt;Exit codes&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;0&lt;/code&gt; — clean exit (WASI &lt;code&gt;_start&lt;/code&gt; returned, or non-WASI export returned a single non-trapping result).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;N&lt;/code&gt; (1–255) — &lt;code&gt;proc_exit(N)&lt;/code&gt; from a WASI binary.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1&lt;/code&gt; — any non-&lt;code&gt;proc_exit&lt;/code&gt; failure (parse error, validator error, instantiate error, trap during execution, missing export).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Non-WASI exports that return a single &lt;code&gt;i32&lt;/code&gt; print the result to stdout as &lt;code&gt;result: &amp;lt;decimal&amp;gt;&lt;/code&gt;; multi-result exports print &lt;code&gt;result: &amp;lt;decimal&amp;gt;, &amp;lt;decimal&amp;gt;, …&lt;/code&gt;. Zero-result exports print nothing.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Errors</title>
    <link href="https://edadma.github.io/wasm/reference/errors/"/>
    <id>https://edadma.github.io/wasm/reference/errors/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>The `WasmError` ADT in one table — every variant, what causes it, where it surfaces.</summary>
    <content type="html">&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;InvalidMagic&lt;/span&gt;                                          &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;TypeMismatch&lt;/span&gt;                                          &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;UnreachableExecuted&lt;/span&gt;                                   &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;MemoryOutOfBounds&lt;/span&gt;                                     &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;UnknownOpcode&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;byte&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                         &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;UnknownImport&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;module&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;      &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ExportNotFound&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                     &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;InvalidModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;message&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                   &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;UncaughtException&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;tagIdx&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;])&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;UnalignedAtomicAccess&lt;/span&gt;                                 &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;object&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ExpectedSharedMemory&lt;/span&gt;                                  &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;lookup&quot;&gt;Lookup&lt;/h2&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Variant&lt;/th&gt;&lt;th&gt;Origin&lt;/th&gt;&lt;th&gt;Trigger&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;InvalidMagic&lt;/code&gt;&lt;/td&gt;&lt;td&gt;parser&lt;/td&gt;&lt;td&gt;First 4 bytes aren’t &lt;code&gt;\0asm&lt;/code&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;TypeMismatch&lt;/code&gt;&lt;/td&gt;&lt;td&gt;runtime&lt;/td&gt;&lt;td&gt;A typed slot received a &lt;code&gt;Value&lt;/code&gt; of the wrong tag — almost always an interpreter or host-binding bug.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;UnreachableExecuted&lt;/code&gt;&lt;/td&gt;&lt;td&gt;runtime&lt;/td&gt;&lt;td&gt;Guest executed an &lt;code&gt;unreachable&lt;/code&gt; opcode.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;MemoryOutOfBounds&lt;/code&gt;&lt;/td&gt;&lt;td&gt;runtime&lt;/td&gt;&lt;td&gt;Load/store/&lt;code&gt;memory.copy&lt;/code&gt;/&lt;code&gt;memory.fill&lt;/code&gt;/&lt;code&gt;memory.init&lt;/code&gt; accessed past linear memory’s end.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;UnknownOpcode(b)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;parser&lt;/td&gt;&lt;td&gt;Byte stream contained an opcode this interpreter doesn’t recognise.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;UnknownImport(m, n)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;instantiate&lt;/td&gt;&lt;td&gt;Module imports &lt;code&gt;m.n&lt;/code&gt;, but no &lt;code&gt;HostModule&lt;/code&gt; provides it.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;ExportNotFound(name)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;runtime&lt;/td&gt;&lt;td&gt;&lt;code&gt;inst.invoke(name, …)&lt;/code&gt; or &lt;code&gt;Wasi.run(inst, name)&lt;/code&gt; looked up a non-existent export.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;InvalidModule(message)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;validator + runtime traps&lt;/td&gt;&lt;td&gt;Static type-check failure, &lt;strong&gt;or&lt;/strong&gt; a runtime trap that isn’t a memory bound — divide-by-zero, signed-&lt;code&gt;div&lt;/code&gt; overflow, &lt;code&gt;trunc&lt;/code&gt; of NaN, &lt;code&gt;call_indirect&lt;/code&gt; signature mismatch, branch index out of range, etc. The message names the operation.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;UncaughtException(tag, args)&lt;/code&gt;&lt;/td&gt;&lt;td&gt;runtime&lt;/td&gt;&lt;td&gt;A &lt;code&gt;throw&lt;/code&gt; propagated past the outermost call without a matching &lt;code&gt;catch tagidx&lt;/code&gt; / &lt;code&gt;catch_all&lt;/code&gt;. &lt;code&gt;tag&lt;/code&gt; is the unified tagidx (imports first, then defs); &lt;code&gt;args&lt;/code&gt; is the payload, top-of-stack at throw site = last element.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;UnalignedAtomicAccess&lt;/code&gt;&lt;/td&gt;&lt;td&gt;runtime&lt;/td&gt;&lt;td&gt;Threads proposal: the effective address of an atomic memory op (base + memarg.offset) was not naturally aligned to the access width.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;ExpectedSharedMemory&lt;/code&gt;&lt;/td&gt;&lt;td&gt;runtime&lt;/td&gt;&lt;td&gt;Threads proposal: &lt;code&gt;memory.atomic.wait{32,64}&lt;/code&gt; was executed against a memory that wasn’t declared &lt;code&gt;shared&lt;/code&gt; (limits flag bit 0x02).&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;h2 id=&quot;invalidmodule-prefixes&quot;&gt;InvalidModule prefixes&lt;/h2&gt;
&lt;p&gt;Validator-stage messages always start with the function index and byte offset:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;function 7: byte offset 0x1a3: i32.add expected i32 i32 on stack, found i32 f64
function 7: byte offset 0x205: br target depth 3 out of range (max 2)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Runtime-trap messages don’t include byte offsets — they name the trapping operation:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;i32.div_s: divide by zero
i32.trunc_f32_s: value out of range
call_indirect: signature mismatch
call_indirect: undefined table element
br_table: index out of range
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can pattern-match on the prefix if you need to distinguish them programmatically — but the standard recovery is &lt;code&gt;case Left(InvalidModule(msg)) =&amp;gt; log(msg)&lt;/code&gt; without sub-parsing.&lt;/p&gt;
&lt;h2 id=&quot;pattern-match-template&quot;&gt;Pattern-match template&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;import&lt;/span&gt; io&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;github&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;edadma&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;wasm&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasmError&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-keyword&quot;&gt;*&lt;/span&gt;

result &lt;span class=&quot;hl-keyword&quot;&gt;match&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Right&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
    handle&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;// -- failure to even get to running guest code --&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;InvalidMagic&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;               &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;not a wasm binary&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;UnknownOpcode&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;           &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;f&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;parser: unknown opcode 0x$b%02x&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;UnknownImport&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; n&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;        &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;missing import: $m.$n&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;InvalidModule&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;msg&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;         &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;module rejected: $msg&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;// -- failure during guest execution --&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;UnreachableExecuted&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;        &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;guest hit unreachable&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;MemoryOutOfBounds&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;          &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;guest read/wrote out of bounds&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ExportNotFound&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;       &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;no such export: $name&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;UncaughtException&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;t&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; args&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;uncaught wasm exception: tag=$t payload=$args&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;UnalignedAtomicAccess&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;      &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;atomic op: misaligned effective address&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ExpectedSharedMemory&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;       &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;atomic wait: target memory is not shared&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;hl-comment&quot;&gt;// -- host or interpreter bug --&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Left&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;TypeMismatch&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;                &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;internal type tag mismatch (bug)&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first group (parse / instantiate failures) surfaces only from &lt;code&gt;Runtime.instantiate&lt;/code&gt;. The second group surfaces from &lt;code&gt;inst.invoke(...)&lt;/code&gt; or &lt;code&gt;Wasi.run(...)&lt;/code&gt;. The third group can surface anywhere and indicates something is wrong with the interpreter or host-import wiring, not the guest.&lt;/p&gt;
&lt;h2 id=&quot;wasi-errnos-vs-wasmerror&quot;&gt;WASI errnos vs. WasmError&lt;/h2&gt;
&lt;p&gt;WASI syscalls return their own errno space (&lt;code&gt;Wasi.ENOENT = 44&lt;/code&gt;, &lt;code&gt;Wasi.EBADF = 8&lt;/code&gt;, &lt;code&gt;Wasi.ENOTCAPABLE = 76&lt;/code&gt;, …) inside the wasm-level return value of the syscall. Those are &lt;em&gt;not&lt;/em&gt; &lt;code&gt;WasmError&lt;/code&gt; values — they’re &lt;code&gt;Int&lt;/code&gt;s the guest reads. A WASI program that tries to open &lt;code&gt;/notfound&lt;/code&gt; gets &lt;code&gt;EBADF&lt;/code&gt;-or-&lt;code&gt;ENOENT&lt;/code&gt; &lt;em&gt;from the syscall&lt;/em&gt;, and the host shim returns &lt;code&gt;Right(...)&lt;/code&gt; to the interpreter; the wasm execution continues normally. &lt;code&gt;Left(WasmError)&lt;/code&gt; is reserved for cases where the host can’t make further progress at all.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>WasiContext</title>
    <link href="https://edadma.github.io/wasm/wasi/context/"/>
    <id>https://edadma.github.io/wasm/wasi/context/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>The configuration passed to `Wasi.preview1` — args, envs, stdio sinks, clock, random source, preopens, sockets.</summary>
    <content type="html">&lt;p&gt;&lt;code&gt;WasiContext&lt;/code&gt; is the configuration record &lt;code&gt;Wasi.preview1(ctx)&lt;/code&gt; accepts. It’s a plain case class with nine fields, all of which have sensible defaults:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;final&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
    args&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;                       &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;                   &lt;span class=&quot;hl-comment&quot;&gt;// argv&lt;/span&gt;
    envs&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;     &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)]&lt;/span&gt;             &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;                   &lt;span class=&quot;hl-comment&quot;&gt;// KEY=VALUE pairs&lt;/span&gt;
    stdout&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt;                       &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;defaultStdout&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// per-byte sink for fd 1&lt;/span&gt;
    stderr&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt;                       &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;defaultStderr&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// per-byte sink for fd 2&lt;/span&gt;
    stdin&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Byte&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;    &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;defaultStdin&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;    &lt;span class=&quot;hl-comment&quot;&gt;// reader for fd 0&lt;/span&gt;
    clock&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;    &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Clock&lt;/span&gt;                 &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;systemClock&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;     &lt;span class=&quot;hl-comment&quot;&gt;// realtime + monotonic&lt;/span&gt;
    random&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;   &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Byte&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;                &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;defaultRandom&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// random_get source&lt;/span&gt;
    preopens&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Preopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;          &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;                   &lt;span class=&quot;hl-comment&quot;&gt;// fd 3 .. 3+P-1&lt;/span&gt;
    sockets&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;  &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;ServerSocket&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;     &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;empty&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;                   &lt;span class=&quot;hl-comment&quot;&gt;// fd 3+P .. 3+P+S-1&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;stdin&lt;/code&gt; reader has the POSIX &lt;code&gt;read&lt;/code&gt; shape: fill &lt;code&gt;dst[off .. off + len)&lt;/code&gt; with up to &lt;code&gt;len&lt;/code&gt; bytes from the current stream position, return the number of bytes written (0 = EOF, never negative). The default returns 0 immediately, so a program that reads stdin without one being supplied sees a clean empty input rather than &lt;code&gt;EBADF&lt;/code&gt;. &lt;code&gt;WasiContext.stdinFromBytes(bytes)&lt;/code&gt; builds a cursor-tracking reader from an &lt;code&gt;Array[Byte]&lt;/code&gt;; the CLI’s &lt;code&gt;--stdin &amp;lt;path&amp;gt;&lt;/code&gt; uses it to stream a file through fd 0.&lt;/p&gt;
&lt;p&gt;Every wasi syscall that needs host state reads from this record — &lt;code&gt;args_get&lt;/code&gt; walks &lt;code&gt;args&lt;/code&gt;, &lt;code&gt;clock_time_get&lt;/code&gt; calls &lt;code&gt;clock.realtimeNanos()&lt;/code&gt;, &lt;code&gt;random_get&lt;/code&gt; calls &lt;code&gt;random(n)&lt;/code&gt;, and so on. Copy-and-modify (&lt;code&gt;ctx.copy(args = …)&lt;/code&gt;) is the only way to construct one; there is no mutation.&lt;/p&gt;
&lt;h2 id=&quot;factories&quot;&gt;Factories&lt;/h2&gt;
&lt;h3 id=&quot;wasicontext-default&quot;&gt;&lt;code&gt;WasiContext.default&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Equivalent to &lt;code&gt;WasiContext()&lt;/code&gt; — every field at its default. stdout / stderr go to &lt;code&gt;System.out&lt;/code&gt; / &lt;code&gt;System.err&lt;/code&gt;, the clock walks &lt;code&gt;System.currentTimeMillis&lt;/code&gt; + &lt;code&gt;System.nanoTime&lt;/code&gt;, random uses &lt;code&gt;scala.util.Random&lt;/code&gt;, no args, no envs, no preopens.&lt;/p&gt;
&lt;p&gt;Use it when running a real binary outside tests. Pair it with &lt;code&gt;.copy(...)&lt;/code&gt; to override individual fields:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;copy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  args     &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;myprog&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;input.txt&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  preopens &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;HostPreopen&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;fromDir&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/var/data&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/data&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;wasicontext-collecting&quot;&gt;&lt;code&gt;WasiContext.collecting(...)&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; collecting &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;collecting&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  args     &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;myprog&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;--flag&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  envs     &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;LANG&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;C&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; collecting&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;context&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same shape as &lt;code&gt;WasiContext.default&lt;/code&gt; but redirects stdout and stderr into &lt;code&gt;ArrayBuffer[Byte]&lt;/code&gt;s you can read back after the guest runs:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;collecting&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;stdoutBytes   &lt;span class=&quot;hl-comment&quot;&gt;// Array[Byte]&lt;/span&gt;
collecting&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;stderrBytes   &lt;span class=&quot;hl-comment&quot;&gt;// Array[Byte]&lt;/span&gt;
collecting&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;stdoutString  &lt;span class=&quot;hl-comment&quot;&gt;// String (UTF-8 decoded)&lt;/span&gt;
collecting&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;stderrString  &lt;span class=&quot;hl-comment&quot;&gt;// String (UTF-8 decoded)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what the WASI test suite uses to assert against program output without touching the real stdout/stderr. The &lt;code&gt;clock&lt;/code&gt; / &lt;code&gt;random&lt;/code&gt; / &lt;code&gt;preopens&lt;/code&gt; overrides flow through identically, so the same factory works for any test that wants deterministic clock or random reads as well.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Collecting&lt;/code&gt; is single-threaded — the interpreter is single-threaded, and the underlying buffers aren’t synchronized. Don’t share a &lt;code&gt;Collecting&lt;/code&gt; across threads.&lt;/p&gt;
&lt;h3 id=&quot;constructing-from-scratch&quot;&gt;Constructing from scratch&lt;/h3&gt;
&lt;p&gt;Both factories produce a &lt;code&gt;WasiContext&lt;/code&gt;. If neither fits, build one directly:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  args     &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;prog&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  envs     &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&amp;quot;PATH&amp;quot;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;-&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&amp;quot;/usr/bin&amp;quot;&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  stdout   &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; b &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; myLogger&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;write&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  stderr   &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; b &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; myLogger&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;write&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
  clock    &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; myClock&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  random   &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; myRandom&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt;
  preopens &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;myPreopen&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-clock-trait&quot;&gt;The &lt;code&gt;Clock&lt;/code&gt; trait&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Clock&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; realtimeNanos&lt;span class=&quot;hl-punctuation&quot;&gt;():&lt;/span&gt;  &lt;span class=&quot;hl-type&quot;&gt;Long&lt;/span&gt;       &lt;span class=&quot;hl-comment&quot;&gt;// wall clock, nanoseconds since Unix epoch&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; monotonicNanos&lt;span class=&quot;hl-punctuation&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Long&lt;/span&gt;       &lt;span class=&quot;hl-comment&quot;&gt;// arbitrary anchor, nanoseconds; only deltas matter&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;clock_time_get(id, ...)&lt;/code&gt; dispatches against this. Four wasi &lt;code&gt;clockid_t&lt;/code&gt; values map onto these two methods:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;&lt;code&gt;clockid_t&lt;/code&gt;&lt;/th&gt;&lt;th&gt;Backing method&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;CLOCK_REALTIME&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;realtimeNanos()&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;CLOCK_MONOTONIC&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;monotonicNanos()&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;CLOCK_PROCESS_CPUTIME&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;monotonicNanos()&lt;/code&gt; (folded)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;CLOCK_THREAD_CPUTIME&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;monotonicNanos()&lt;/code&gt; (folded)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;WasiContext.systemClock&lt;/code&gt; is the default — &lt;code&gt;System.currentTimeMillis() * 1_000_000L&lt;/code&gt; for realtime, &lt;code&gt;System.nanoTime()&lt;/code&gt; for monotonic. Cross-platform: JVM, Scala.js (via &lt;code&gt;Date.now&lt;/code&gt; / &lt;code&gt;performance.now&lt;/code&gt;), and Scala Native all support both calls without conditional code.&lt;/p&gt;
&lt;p&gt;For deterministic tests, plug in your own:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; frozenClock &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Clock&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; realtimeNanos&lt;span class=&quot;hl-punctuation&quot;&gt;():&lt;/span&gt;  &lt;span class=&quot;hl-type&quot;&gt;Long&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;1_700_000_000_000_000_000L&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; monotonicNanos&lt;span class=&quot;hl-punctuation&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Long&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-number&quot;&gt;42L&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;copy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;clock &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; frozenClock&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-random-closure&quot;&gt;The &lt;code&gt;random&lt;/code&gt; closure&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;random&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Byte&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;random_get(buf, len)&lt;/code&gt; calls &lt;code&gt;ctx.random(len)&lt;/code&gt; and copies the result into linear memory at &lt;code&gt;buf&lt;/code&gt;. The closure must return an array of &lt;em&gt;exactly&lt;/em&gt; &lt;code&gt;len&lt;/code&gt; bytes — shorter returns are not detected and would surface as silent zero-fill in the guest. The default implementation (&lt;code&gt;WasiContext.defaultRandom&lt;/code&gt;) uses &lt;code&gt;scala.util.Random.nextBytes&lt;/code&gt;; not cryptographic, but adequate for &lt;code&gt;getrandom&lt;/code&gt;-style entropy in most cases.&lt;/p&gt;
&lt;p&gt;Override for deterministic tests:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;copy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;
  random &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;fill&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;n&lt;span class=&quot;hl-punctuation&quot;&gt;)(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0x42&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;toByte&lt;span class=&quot;hl-punctuation&quot;&gt;),&lt;/span&gt;
&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or for a real CSPRNG, wire &lt;code&gt;java.security.SecureRandom&lt;/code&gt; (JVM/Native) or &lt;code&gt;crypto.randomBytes&lt;/code&gt; (JS) in here.&lt;/p&gt;
&lt;h2 id=&quot;stdout-stderr-sinks&quot;&gt;stdout / stderr sinks&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;stdout&lt;/code&gt; and &lt;code&gt;stderr&lt;/code&gt; are per-byte callbacks. Every &lt;code&gt;fd_write(1, …)&lt;/code&gt; byte goes through &lt;code&gt;stdout&lt;/code&gt;; every &lt;code&gt;fd_write(2, …)&lt;/code&gt; byte goes through &lt;code&gt;stderr&lt;/code&gt;. They’re called exactly once per byte the guest writes, in order — they’re not aware of newlines or line buffering.&lt;/p&gt;
&lt;p&gt;If you want line-buffered output, do the buffering in your callback:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; lineBuf &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;StringBuilder&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; stdout&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; b &lt;span class=&quot;hl-keyword&quot;&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;if&lt;/span&gt; b &lt;span class=&quot;hl-keyword&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;hl-variable&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;hl-string&quot;&gt;&apos;&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;then&lt;/span&gt;
    println&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;lineBuf&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;toString&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;
    lineBuf&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;clear&lt;span class=&quot;hl-punctuation&quot;&gt;()&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;else&lt;/span&gt;
    lineBuf &lt;span class=&quot;hl-keyword&quot;&gt;+=&lt;/span&gt; b&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;toChar

&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;copy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;stdout &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; stdout&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;the-sockets-field&quot;&gt;The &lt;code&gt;sockets&lt;/code&gt; field&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;sockets: Seq[WasiContext.ServerSocket]&lt;/code&gt; — host-provided listening sockets, exposed to the guest one fd at a time starting at &lt;code&gt;3 + preopens.length&lt;/code&gt;. There is no &lt;code&gt;sock_open&lt;/code&gt; / &lt;code&gt;sock_bind&lt;/code&gt; / &lt;code&gt;sock_listen&lt;/code&gt; in Preview 1; the host pre-binds and the guest can only &lt;code&gt;sock_accept&lt;/code&gt; against the inherited fds.&lt;/p&gt;
&lt;p&gt;JVM and Scala Native get a factory:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; &lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;listener&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; port&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;HostServerSocket&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;bind&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;hl-number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;)&lt;/span&gt;   &lt;span class=&quot;hl-comment&quot;&gt;// 0 → OS-chosen ephemeral&lt;/span&gt;
&lt;span class=&quot;hl-keyword&quot;&gt;val&lt;/span&gt; ctx &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;WasiContext&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;default&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;copy&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;sockets &lt;span class=&quot;hl-keyword&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Seq&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;listener&lt;span class=&quot;hl-punctuation&quot;&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Scala.js doesn’t link &lt;code&gt;java.net.ServerSocket&lt;/code&gt;; a Node-backed program that needs sockets must supply its own &lt;code&gt;WasiContext.ServerSocket&lt;/code&gt; implementation (typically wrapping Node’s &lt;code&gt;net&lt;/code&gt; module).&lt;/p&gt;
&lt;p&gt;The trait surface is intentionally tiny:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-scala&quot;&gt;&lt;span class=&quot;hl-keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ServerSocket&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; accept&lt;span class=&quot;hl-punctuation&quot;&gt;():&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Either&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ClientSocket&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; address&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;String&lt;/span&gt;        &lt;span class=&quot;hl-comment&quot;&gt;// &amp;quot;host:port&amp;quot; for debug logs&lt;/span&gt;

&lt;span class=&quot;hl-keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;ClientSocket&lt;/span&gt; &lt;span class=&quot;hl-keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Wasi&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;FsFile&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;hl-keyword&quot;&gt;def&lt;/span&gt; shutdown&lt;span class=&quot;hl-punctuation&quot;&gt;(&lt;/span&gt;how&lt;span class=&quot;hl-punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;):&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Either&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;hl-type&quot;&gt;Int&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;hl-type&quot;&gt;Unit&lt;/span&gt;&lt;span class=&quot;hl-punctuation&quot;&gt;]&lt;/span&gt;
  &lt;span class=&quot;hl-comment&quot;&gt;// close/read/write inherited from FsFile — fd_read/fd_write&lt;/span&gt;
  &lt;span class=&quot;hl-comment&quot;&gt;// route to them through the standard FdTable lookup.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;accept&lt;/code&gt; blocks until a client connects; the returned &lt;code&gt;ClientSocket&lt;/code&gt; is installed in the per-instance fd table by &lt;code&gt;sock_accept&lt;/code&gt; and is observable through both &lt;code&gt;fd_read&lt;/code&gt; / &lt;code&gt;fd_write&lt;/code&gt; (the generic byte-stream surface) and the dedicated &lt;code&gt;sock_recv&lt;/code&gt; / &lt;code&gt;sock_send&lt;/code&gt; (the iovec-shape recv/send surface with recvflags/sendflags).&lt;/p&gt;
&lt;h2 id=&quot;where-to-go-next&quot;&gt;Where to go next&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/wasi/preopens/&quot;&gt;Preopens&lt;/a&gt; — the &lt;code&gt;preopens&lt;/code&gt; field in depth.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/wasi/syscalls/&quot;&gt;Syscalls&lt;/a&gt; — which fields each syscall reads.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;/wasm/concepts/module-instance/&quot;&gt;Concepts → ModuleInstance&lt;/a&gt; — what &lt;code&gt;Wasi.preview1(ctx)&lt;/code&gt; returns and how to drive it.&lt;/li&gt;
&lt;/ul&gt;</content>
  </entry>
  <entry>
    <title>Architecture</title>
    <link href="https://edadma.github.io/wasm/development/architecture/"/>
    <id>https://edadma.github.io/wasm/development/architecture/</id>
    <updated>2026-05-17T00:33:47.893097391Z</updated>
    <summary>Three sub-projects (`interp`, `wasi`, `cli`) so the published libraries stay dependency-free, with platform shims confined to thin per-target source roots.</summary>
    <content type="html">&lt;h2 id=&quot;three-sub-projects&quot;&gt;Three sub-projects&lt;/h2&gt;
&lt;p&gt;The repo splits into three sub-projects so the published libraries stay dependency-free:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Sub-project&lt;/th&gt;&lt;th&gt;Published as&lt;/th&gt;&lt;th&gt;Depends on&lt;/th&gt;&lt;th&gt;External runtime deps&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;interp/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;wasm&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Scala stdlib only&lt;/td&gt;&lt;td&gt;None&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;wasi/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;wasm-wasi&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;interp&lt;/code&gt;&lt;/td&gt;&lt;td&gt;None&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;cli/&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;em&gt;(not published)&lt;/em&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;interp&lt;/code&gt; + &lt;code&gt;wasi&lt;/code&gt;&lt;/td&gt;&lt;td&gt;&lt;code&gt;scopt&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;interp/&lt;/code&gt;&lt;/strong&gt; — the interpreter itself: parser, validator, runtime, interpreter loop, host-import surface. Cross-built on JVM / Scala.js / Scala Native; the same &lt;code&gt;shared/&lt;/code&gt; sources compile for every backend.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;wasi/&lt;/code&gt;&lt;/strong&gt; — the WASI Preview 1 host shim (29 syscalls). Also zero external deps. Pulls &lt;code&gt;interp&lt;/code&gt; in transitively.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;cli/&lt;/code&gt;&lt;/strong&gt; — a small command-line runner. Depends on &lt;code&gt;scopt&lt;/code&gt; for argument parsing; not published because it’s a runnable example, not a library. JVM / Scala.js (Node) / Scala Native builds are all wired up.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;source-tree-layout&quot;&gt;Source-tree layout&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;build.sbt                              three crossProjects + Fixtures.scala generator
project/
interp/                                cross-platform interpreter library (zero deps)
  shared/src/
    main/scala/io/github/edadma/wasm/  types, parser, validator, interpreter, runtime, host
    test/scala/io/github/edadma/wasm/  category-split tests + zero-dep runner
    test/resources/fixtures/           .wat sources + wat2wasm-produced .wasm
  jvm/  js/  native/                   empty platform shells
wasi/                                  cross-platform WASI Preview 1 shim (zero deps)
  shared/src/main/scala/.../wasi/      Wasi.preview1 + WasiContext + HostBackedPreopen
  jvm/    src/main/scala/.../wasi/     HostPreopen.fromDir   (java.nio.file + FileChannel)
  native/ src/main/scala/.../wasi/     HostPreopen.fromDir   (same surface as JVM)
  js/     src/main/scala/.../wasi/     HostPreopen.fromDir   (Node fs.*Sync)
  shared/src/test/                     WasiFd / WasiArgs / WasiClock / WasiFs / WasiHostFs / WasiRealRust tests
cli/                                   cross-platform CLI (depends on interp + wasi + scopt)
  shared/src/main/scala/.../cli/       Cli.scala (scopt config + dispatch)
  jvm/    src/main/scala/.../cli/      Main.scala (java.nio file read + HostPreopen plumbing)
  js/     src/main/scala/.../cli/      Main.scala (Node fs + process.argv + HostPreopen plumbing)
  native/ src/main/scala/.../cli/      Main.scala (java.nio + HostPreopen plumbing)
examples/
  hello.wat / hello.wasm               canonical Hello, world! module
&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;how-fixtures-get-into-the-test-suite&quot;&gt;How fixtures get into the test suite&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;Fixtures.scala&lt;/code&gt; is a generated Scala object exposing each committed &lt;code&gt;.wasm&lt;/code&gt; binary as an &lt;code&gt;Array[Byte]&lt;/code&gt;. It’s regenerated into each platform’s &lt;code&gt;target/.../src_managed/test/&lt;/code&gt; on every compile by a &lt;code&gt;sourceGenerator&lt;/code&gt; in &lt;code&gt;build.sbt&lt;/code&gt;, so it always derives from the committed binaries — no committed copy to drift.&lt;/p&gt;
&lt;p&gt;Fixtures are base64-encoded string constants decoded once at class init. The previous &lt;code&gt;Array[Byte](b0, b1, ...)&lt;/code&gt; literal form trips the JVM 64KB method-size limit on real-world fixtures like rustc-built wasi binaries; the base64 form sidesteps it.&lt;/p&gt;
&lt;p&gt;To refresh a fixture from its &lt;code&gt;.wat&lt;/code&gt; source, see &lt;a href=&quot;/wasm/development/testing/#regenerating-fixtures&quot;&gt;Testing → Regenerating fixtures&lt;/a&gt;.&lt;/p&gt;</content>
  </entry>
</feed>
