### WASM Example: If with I32 Constant Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/15_extra_execute_fibonacci.md A WebAssembly text format example demonstrating an `if` statement that pushes an `i32` constant onto the stack. ```wat (if (i32.const 1) (i32.const 3) ) ``` -------------------------------- ### Install wabt using Homebrew Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/03_intro_wasm.md Installs the WebAssembly Binary Toolkit (wabt) on macOS using Homebrew. This toolset includes `wat2wasm` for compiling WAT to Wasm. ```sh brew install wabt ``` -------------------------------- ### Install Wasmtime Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/03_intro_wasm.md Installs Wasmtime, a runtime for executing Wasm binaries, using a script for macOS and Linux. Refer to official documentation for Windows installation. ```sh curl https://wasmtime.dev/install.sh -sSf | bash ``` -------------------------------- ### WAT Example: i32.add function Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/07_decode_function_2.md This WAT code defines a function that takes two i32 arguments, adds them using i32.add, and returns the result. ```wat (module (func (param i32 i32) (result i32) (local.get 0) (local.get 1) i32.add ) ) ``` -------------------------------- ### Example WAT for Import Section Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/11_build_runtime_external_func_call.md This WebAssembly Text Format (WAT) demonstrates a module importing an 'add' function from the 'env' module and exporting a function that calls it. ```wat (module (func $add (import "env" "add") (param i32) (result i32)) (func (export "call_add") (param i32) (result i32) (local.get 0) (call $add) ) ) ``` -------------------------------- ### WAT Example for Hello World Output Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/14_build_runtime_wasi.md This WAT function demonstrates how to use fd_write to print 'Hello, World!' by preparing memory buffers and calling the imported fd_write function. ```wat (func $hello_world (result i32) (local $iovs i32) (i32.store (i32.const 16) (i32.const 0)) ;; 1 (i32.store (i32.const 20) (i32.const 14)) ;; 2 (local.set $iovs (i32.const 16)) ;; 3 (call $fd_write ;; 4 (i32.const 1) (local.get $iovs) (i32.const 1) (i32.const 24) ) ) ``` -------------------------------- ### WAT Example for Hello World with fd_write Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/14_build_runtime_wasi.md This WebAssembly Text (WAT) snippet demonstrates how to import and use the WASI fd_write function to print "Hello, World!\n" to standard output. It sets up the necessary memory and iovecs for the write operation. ```wat (module (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)) ) (memory 1) (data (i32.const 0) "Hello, World!\n") (func $hello_world (result i32) (local $iovs i32) (i32.store (i32.const 16) (i32.const 0)) (i32.store (i32.const 20) (i32.const 14)) (local.set $iovs (i32.const 16)) (call $fd_write (i32.const 1) (local.get $iovs) (i32.const 1) (i32.const 24) ) ) (export "_start" (func $hello_world)) ) ``` -------------------------------- ### Check Wasmtime version Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/03_intro_wasm.md Displays the installed version of the Wasmtime CLI. ```sh wasmtime --version ``` -------------------------------- ### Check wabt version Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/03_intro_wasm.md Displays the installed version of the `wat2wasm` tool, part of the wabt package. ```sh wat2wasm --version ``` -------------------------------- ### Wasm Binary Structure: Import Section Example Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/04_wasm_binary_structure.md Demonstrates the binary representation of the Import Section, including type, import module name, import field name, import kind, and import signature index. ```wasm ; section "Type" (1) 0000008: 01 ; section code 0000009: 07 ; section size 000000a: 01 ; num types ; func type 0 000000b: 60 ; func 000000c: 02 ; num params 000000d: 7f ; i32 000000e: 7f ; i32 000000f: 01 ; num results 0000010: 7f ; i32 ; section "Import" (2) 0000011: 02 ; section code 0000012: 0d ; section size 0000013: 01 ; num imports ; import header 0 0000014: 05 ; string length 0000015: 6164 6465 72 ; import module name (adder) 000001a: 03 ; string length 000001b: 6164 64 ; import field name (add) 000001e: 00 ; import kind 000001f: 00 ; import signature index ``` -------------------------------- ### Wasm Import Function Declaration Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/04_wasm_binary_structure.md Example of importing a function named 'add' from a module named 'adder' with specific parameter and result types (i32, i32 -> i32). ```wat (module (import "adder" "add" (func (param i32 i32) (result i32))) ) ``` -------------------------------- ### Example WASM Section Header Decoding Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/06_decode_function_1.md Illustrates the byte-level representation of a WASM function section header and its subsequent decoding. Shows the consumption of bytes for section code and size, and the remaining data for further processing. ```text 000000c: 00 ; num params 000000d: 00 ; num results ; section "Function" (3) 000000e: 03 ; section code 000000f: 02 ; section size 0000010: 01 ; num functions 0000011: 00 ; function 0 signature index ... ``` -------------------------------- ### Memory Declaration in WAT Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/04_wasm_binary_structure.md Example of declaring memory in WebAssembly Text Format (WAT), specifying initial and maximum memory pages. The maximum is optional. ```Wast (module (memory 2 3) ) ``` -------------------------------- ### Rust Example of Calling an External C Function Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/11_build_runtime_external_func_call.md Illustrates how to call an external C function, `double`, from Rust using `extern "C"` and `unsafe` blocks. This demonstrates the concept of external function calls from the perspective of the calling code. ```rust extern "C" { fn double(x: i32) -> i32; } fn main() { unsafe { double(10) }; } ``` -------------------------------- ### Minimal Wasm Module Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/06_decode_function_1.md This is the smallest possible WebAssembly module that contains a single, empty function. It serves as a starting point for implementing decoding logic. ```wat (module (func) ) ``` -------------------------------- ### Rust Compiler Version Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/05_how_decode_wasm_binary.md Displays the installed Rust compiler version. Ensure you are using a compatible version for the project. ```sh rustc --version rustc 1.77.2 (25ef9e3d8 2024-04-09) ``` -------------------------------- ### Wasm Function Call Example Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/10_build_runtime_func_call.md This snippet demonstrates a Wasm module where one function (`call_doubler`) calls another (`double`). It takes an i32 argument, passes it to `double`, and returns the result. This illustrates basic function invocation within Wasm. ```wat (module (func (export "call_doubler") (param i32) (result i32) (local.get 0) (call $double) ) (func $double (param i32) (result i32) (local.get 0) (local.get 0) i32.add ) ) ``` -------------------------------- ### nom Parsing Error Example Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/05_how_decode_wasm_binary.md Illustrates the error output when the `tag` parser fails to match the input byte sequence. This helps in debugging parsing issues. ```sh Error: failed to parse wasm: Parsing Error: Error { input: [0, 97, 115, 109, 1, 0, 0, 0], code: Tag } ``` -------------------------------- ### Compile WAT to Wasm and Execute Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/03_intro_wasm.md Demonstrates the process of compiling a WAT file (`add.wat`) into a Wasm binary (`add.wasm`) using `wat2wasm`, and then executing the compiled Wasm binary with Wasmtime, invoking the 'add' function with arguments 1 and 2. ```sh # Compile $ wat2wasm add.wat $ ls  add.wasm  add.wat # Execute function $ wasmtime add.wasm --invoke add 1 2 warning: using `--invoke` with a function that takes arguments is experimental and may break in the future warning: using `--invoke` with a function that returns values is experimental and may break in the future 3 ``` -------------------------------- ### Rust Enum for WASM Instructions Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/06_decode_function_1.md Defines the basic WASM instructions, starting with End. ```rust pub enum Instruction { End, } ``` -------------------------------- ### Execute WASM with WASI in Rust Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/14_build_runtime_wasi.md This code demonstrates how to compile a WAT file to WASM, then use a Rust runtime to execute a WASM module that utilizes WASI for system calls. It includes setting up the WASI environment and calling the `_start` function. ```rust use anyhow::Result; use tinywasm::execution::{runtime::Runtime, wasi::WasiSnapshotPreview1}; fn main() -> Result<()> { let wasi = WasiSnapshotPreview1::new(); let wasm = include_bytes!("./fixtures/hello_world.wasm"); let mut runtime = Runtime::instantiate_with_wasi(wasm, wasi)?; runtime.call("_start", vec![]).unwrap(); Ok(()) } ``` -------------------------------- ### Instantiate WASI Runtime with Module and Store Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/14_build_runtime_wasi.md This snippet shows how to create a new runtime instance, initializing it with a WASM module and a store, and optionally integrating WASI. ```rust let module = Module::new(wasm.as_ref())?; let store = Store::new(module)?; Ok(Self { store, wasi: Some(wasi), ..Default::default() }) ``` -------------------------------- ### Create New Rust Project Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/05_how_decode_wasm_binary.md Initializes a new Rust project named 'tiny-wasm-runtime' with the binary name 'tinywasm'. This command sets up the basic project structure. ```sh cargo new tiny-wasm-runtime --name tinywasm ``` -------------------------------- ### WebAssembly Module with Function Signatures Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/04_wasm_binary_structure.md Defines three functions with different parameter orders and return values to illustrate signature variations. ```wast (module (func $a (param i32 i64)) (func $b (param i64 i32) (result i32 i64) (local.get 1) (local.get 0) ) (func $c (param i32 i64)) ) ``` -------------------------------- ### WebAssembly Text Format for If Instruction Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/15_extra_execute_fibonacci.md The WebAssembly text representation of an 'if' block, demonstrating conditional logic based on a comparison. ```wat (if (i32.lt_s (local.get $n) (i32.const 2)) (return (i32.const 1)) ) ``` -------------------------------- ### Initialize Wasm Runtime Store from Module Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/09_build_runtime_func_execute.md Implements the `Store::new` constructor to create a `Store` instance by processing the type, function, and code sections of a given Wasm `Module`. Handles potential errors like missing sections or type indices. ```rust use crate::binary::{ instruction::Instruction, module::Module, types::{FuncType, ValueType}, }; use anyhow::{bail, Result}; #[derive(Clone)] pub struct Func { pub locals: Vec, pub body: Vec, } #[derive(Clone)] pub struct InternalFuncInst { pub func_type: FuncType, pub code: Func, } #[derive(Clone)] pub enum FuncInst { Internal(InternalFuncInst), } #[derive(Default)] pub struct Store { pub funcs: Vec, } impl Store { pub fn new(module: Module) -> Result { let func_type_idxs = match module.function_section { Some(ref idxs) => idxs.clone(), _ => vec![], }; let mut funcs = vec![]; if let Some(ref code_section) = module.code_section { for (func_body, type_idx) in code_section.iter().zip(func_type_idxs.into_iter()) { let Some(ref func_types) = module.type_section else { bail!("not found type_section") }; let Some(func_type) = func_types.get(type_idx as usize) else { bail!("not found func type in type_section") }; let mut locals = Vec::with_capacity(func_body.locals.len()); for local in func_body.locals.iter() { for _ in 0..local.type_count { locals.push(local.value_type.clone()); } } let func = FuncInst::Internal(InternalFuncInst { func_type: func_type.clone(), code: Func { locals, body: func_body.code.clone(), }, }); funcs.push(func); } } Ok(Self { funcs }) } } ``` -------------------------------- ### WebAssembly Text Format for func_add Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/07_decode_function_2.md Defines a WebAssembly module with a single function that takes two i32 parameters and returns their sum. This WAT file is used as input for the Rust test. ```wat (module (func (param i32 i32) (result i32) (local.get 0) (local.get 1) i32.add ) ) ``` -------------------------------- ### Test Execution Summary Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/15_extra_execute_fibonacci.md Shows the output of running all tests, confirming that the i32_lts test passed. ```sh running 10 tests test execution::runtime::tests::i32_const ... ok test execution::runtime::tests::not_found_imported_func ... ok test execution::runtime::tests::func_call ... ok test execution::runtime::tests::i32_sub ... ok test execution::runtime::tests::local_set ... ok test execution::runtime::tests::call_imported_func ... ok test execution::runtime::tests::i32_store ... ok test execution::runtime::tests::execute_i32_add ... ok test execution::runtime::tests::not_found_export_function ... ok test execution::runtime::tests::i32_lts ... ok ``` -------------------------------- ### Define String in Memory (Wasm Text Format) Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/04_wasm_binary_structure.md Defines a memory space and initializes it with a string literal. The string 'Hello, World!\n' is placed at the beginning of the memory. ```wast (module (memory 1) (data 0 (i32.const 0) "Hello, World!\n") ) ``` -------------------------------- ### Instantiating Memory in Wasm Runtime Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/13_build_runtime_initialize_memory.md This snippet shows how to instantiate memory for a Wasm module. It calculates the minimum memory size based on pages and initializes the memory data with zeros. The `memory.limits.max` is also considered for potential future use with `memory.grow`. ```rust if let Some(ref sections) = module.memory_section { for memory in sections { let min = memory.limits.min * PAGE_SIZE; // 1 let memory = MemoryInst { data: vec![0; min as usize], // 2 max: memory.limits.max, }; memories.push(memory); } } ``` -------------------------------- ### WebAssembly Text Format for local.set with i32.const Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/12_build_runtime_additional_instruction.md A WebAssembly module demonstrating the use of local.set with i32.const to store and retrieve a value. ```wat (module (func $local_set (result i32) (local $x i32) (local.set $x (i32.const 42)) (local.get 0) ) (export "local_set" (func $local_set)) ) ``` -------------------------------- ### WebAssembly Text Format for Return and Arithmetic Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/15_extra_execute_fibonacci.md The WebAssembly text representation for the recursive part of a function, involving subtraction, function calls, and addition. ```wat (return (i32.add (call $fib (i32.sub (local.get $n) (i32.const 2))) (call $fib (i32.sub (local.get $n) (i32.const 1))) ) ) ``` -------------------------------- ### Initialize Wasm Memory with Data Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/13_build_runtime_initialize_memory.md Defines a Wasm module with a memory segment and initializes it with two data segments. Use this to set initial string data in Wasm memory. ```WAT (module (memory 1) (data (i32.const 0) "hello") (data (i32.const 5) "world") ) ``` -------------------------------- ### Placeholder for Instruction Processing Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/12_build_runtime_additional_instruction.md Adds a placeholder `todo!()` for the `I32Store` instruction in the `Runtime`'s instruction processing loop in `src/execution/runtime.rs` to prevent compilation errors. ```rust _ => todo!(), // Instruction processing is set to TODO to avoid compilation errors ``` -------------------------------- ### WASI fd_write Import in WAT Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/14_build_runtime_wasi.md This WAT snippet shows the import of the fd_write function from wasi_snapshot_preview1, including its signature. ```wat (import "wasi_snapshot_preview1" "fd_write" (func $fd_write (param i32 i32 i32 i32) (result i32)) ) ``` -------------------------------- ### Wasm Stack Machine Pseudo-code Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/03_intro_wasm.md Illustrates the fundamental operation of a Wasm stack machine using pseudo-Rust code. It shows how instructions like `local.get` push values onto a stack and `i32.add` pops operands, performs addition, and pushes the result back. ```rust // Stack to store values to process let mut stack: Vec = vec![]; // Area to hold function local variables let mut locals: Vec = vec![]; // A loop that processes instructions loop { let instruction = fetch_inst(); match instruction { inst::LocalGet => { let value = locals.pop(); stack.push(value); } inst::I32Add => { let right = stack.pop(); let left = stack.pop(); stack.push(left + right); } ... } } return stack.pop(); ``` -------------------------------- ### WAT for i32.store Test Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/13_build_runtime_initialize_memory.md This WebAssembly WAT (WebAssembly Text Format) defines a module with memory and a function that stores the value 42 at memory address 0. It's used to test the `i32.store` implementation. ```wat (module (memory 1) (func $i32_store (i32.const 0) (i32.const 42) (i32.store) ) (export "i32_store" (func $i32_store)) ) ``` -------------------------------- ### Code Section Binary Structure Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/04_wasm_binary_structure.md Illustrates the byte-level structure of the WebAssembly Code Section, including section code, size, number of functions, and individual function body definitions with their sizes, local variable counts, and instructions. ```Assembly ; section "Code" (10) 000001d: 0a ; section code 000001e: 0e ; section size 000001f: 03 ; num functions ; function body 0 0000020: 02 ; func body size 0000021: 00 ; local decl count 0000022: 0b ; end ; function body 1 0000023: 06 ; func body size 0000024: 00 ; local decl count 0000025: 20 ; local.get 0000026: 01 ; local index 0000027: 20 ; local.get 0000028: 00 ; local index 0000029: 0b ; end ; function body 2 000002a: 02 ; func body size 000002b: 00 ; local decl count 000002c: 0b ; end ``` -------------------------------- ### Test i32.store Implementation Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/13_build_runtime_initialize_memory.md This Rust test function verifies the `i32.store` instruction. It parses the WAT file, instantiates the runtime, calls the `i32_store` function, and asserts that memory address 0 now contains the value 42. ```rust #[test] fn i32_store() -> Result<()> { let wasm = wat::parse_file("src/fixtures/i32_store.wat")?; let mut runtime = Runtime::instantiate(wasm)?; runtime.call("i32_store", vec![])?; let memory = &runtime.store.memories[0].data; assert_eq!(memory[0], 42); Ok(()) } ``` -------------------------------- ### Add Opcode Module to Binary Module Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/07_decode_function_2.md This diff shows how to include the new opcode module in the binary module's public API. ```diff pub mod instruction; pub mod module; +pub mod opcode; pub mod section; pub mod types; ``` -------------------------------- ### WAT for Local Variables Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/07_decode_function_2.md This is the WebAssembly Text (WAT) format used to define a function with local variables of types i32 and i64. ```wat (module (func (local i32) (local i64 i64) ) ) ``` -------------------------------- ### Wasm Instruction Execution Loop Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/08_how_function_execute.md Illustrates the basic loop for fetching and executing Wasm instructions using a program counter, stack, and local variables. This pseudocode demonstrates the core logic before specific instruction handling. ```rust let instructions = vec![...]; // Instruction sequence let mut stack: Vec = vec![]; // Stack let mut locals: Vec = vec![]; // Local variables let mut pc: usize = 0; // Program counter loop { if let Some(instruction) = instructions.get(pc) else { break; }; match instruction { inst::LocalGet => { ... } inst::I32Add => { ... } ... } pc += 1; } ``` -------------------------------- ### Registering and Calling an External Function Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/11_build_runtime_external_func_call.md Demonstrates how to register an external function 'add' with the Wasm runtime and then call it. The 'add' function takes an i32 argument and returns its double. This snippet shows the import registration and subsequent function call with assertion. ```rust runtime.add_import("env", "add", |_, args| { let arg = args[0]; Ok(Some(arg + arg)) })?; let args = vec![Value::I32(arg)]; let result = runtime.call("call_add", args)?; assert_eq!(result, Some(Value::I32(want))); ``` -------------------------------- ### Test Runtime Memory Initialization in Rust Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/13_build_runtime_initialize_memory.md Tests the initialization of runtime memory with data from a WebAssembly module. It asserts that the memory is correctly sized and that the initial data is placed at the expected offsets. ```rust fn init_memory() -> Result<()> { let wasm = wat::parse_file("src/fixtures/memory.wat")?; let module = Module::new(&wasm)?; let store = Store::new(module)?; assert_eq!(store.memories.len(), 1); assert_eq!(store.memories[0].data.len(), 65536); assert_eq!(&store.memories[0].data[0..5], b"hello"); } ``` -------------------------------- ### Implement I32Sub Instruction in Runtime Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/15_extra_execute_fibonacci.md Handles the I32Sub instruction by popping two i32 values from the stack, subtracting them, and pushing the result. ```rust Instruction::I32Sub => { let (Some(right), Some(left)) = (self.stack.pop(), self.stack.pop()) else { bail!("not found any value in the stack"); }; let result = left - right; self.stack.push(result); } ``` -------------------------------- ### WebAssembly Text Format for i32.const Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/12_build_runtime_additional_instruction.md A simple WebAssembly module defining a function that returns the i32 constant 42. ```wat (module (func $i32_const (result i32) (i32.const 42) ) (export "i32_const" (func $i32_const)) ) ``` -------------------------------- ### Add Section Module to Binary Module Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/06_decode_function_1.md Includes the newly created `section` module into the `binary` module. ```diff pub mod module; +pub mod section; ``` -------------------------------- ### Alternative Section Header Parsing Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/06_decode_function_1.md Shows how to parse section code and size individually without using `nom::sequence::pair`. ```rust let (input, code) = le_u8(input); let (input, size) = leb128_u32(input); ``` -------------------------------- ### Test i32 Addition Function Execution Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/09_build_runtime_func_execute.md Verifies the correct execution of an i32 addition function within the Wasm runtime. It instantiates the runtime with a WAT file, calls the function with various inputs, and asserts that the returned value matches the expected sum. ```rust #[test] fn execute_i32_add() -> Result<()> { let wasm = wat::parse_file("src/fixtures/func_add.wat")?; let mut runtime = Runtime::instantiate(wasm)?; let tests = vec![(2, 3, 5), (10, 5, 15), (1, 1, 2)]; for (left, right, want) in tests { let args = vec![Value::I32(left), Value::I32(right)]; let result = runtime.call(0, args)?; assert_eq!(result, Some(Value::I32(want))); } Ok(()) } ``` -------------------------------- ### Test Decoding of i32.store Instruction Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/12_build_runtime_additional_instruction.md Adds a test case in `src/binary/module.rs` to verify the correct decoding of the `i32.store` instruction, including its alignment and offset operands, using `wat::parse_str`. ```rust #[test] fn decode_i32_store() -> Result<()> { let wasm = wat::parse_str( "(module (func (i32.store offset=4 (i32.const 4))))", )?; let module = Module::new(&wasm)?; assert_eq!( module, Module { type_section: Some(vec![FuncType { params: vec![], results: vec![], }]), function_section: Some(vec![0]), code_section: Some(vec![Function { locals: vec![], code: vec![ Instruction::I32Const(4), Instruction::I32Store { align: 2, offset: 4 }, Instruction::End ], }]), ..Default::default() } ); Ok(()) } ``` -------------------------------- ### WebAssembly Type Section Header Breakdown Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/04_wasm_binary_structure.md Highlights the section code and section size bytes within the Type Section, explaining their roles in identifying and sizing the section. ```wasm ; section "Type" (1) 0000008: 01 ; section code 0000009: 0d ; section size 000000a: 02 ; num types ``` -------------------------------- ### Initialize Memory Vector in Store Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/13_build_runtime_initialize_memory.md Initializes the `memories` vector as empty when a new `Store` is created. ```rust let mut memories = vec![]; ``` -------------------------------- ### Handle Unknown Opcode in Runtime Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/10_build_runtime_func_call.md Adds a `todo!()` placeholder for unhandled opcodes within the `Runtime::execute` method in `src/execution/runtime.rs`. This ensures the runtime doesn't panic on unknown instructions. ```diff diff --git a/src/execution/runtime.rs b/src/execution/runtime.rs index acc48cf..bc2a20b 100644 --- a/src/execution/runtime.rs +++ b/src/execution/runtime.rs @@ -127,6 +127,7 @@ impl Runtime { let result = left + right; self.stack.push(result); } + _ => todo!(), } } Ok(()) ``` -------------------------------- ### Implement From for Value and PartialOrd for Value Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/15_extra_execute_fibonacci.md Enables comparison of i32 values and conversion of boolean results to Value. This is necessary for the i32.lt_s instruction. ```diff diff --git a/src/execution/value.rs b/src/execution/value.rs index 6a7820f..eee47ac 100644 --- a/src/execution/value.rs +++ b/src/execution/value.rs @@ -1,3 +1,5 @@ +use std::cmp::Ordering; + #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Value { I32(i32), @@ -19,6 +21,12 @@ impl From for i32 { } } +impl From for Value { + fn from(value: bool) -> Self { + Value::I32(if value { 1 } else { 0 }) + } +} + impl From for Value { fn from(value: i64) -> Self { Value::I64(value) @@ -46,3 +54,13 @@ impl std::ops::Sub for Value { } } } + +impl PartialOrd for Value { + fn partial_cmp(&self, other: &Self) -> Option { + match (self, other) { + (Value::I32(a), Value::I32(b)) => a.partial_cmp(b), + (Value::I64(a), Value::I64(b)) => a.partial_cmp(b), + _ => panic!("type mismatch"), + } + } +} ``` -------------------------------- ### Test for Decoding Simplest WASM Module Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/05_how_decode_wasm_binary.md Tests the `Module::new` function by compiling a minimal WAT module to Wasm binary and verifying that the decoded preamble matches the expected default `Module` structure. ```rust #[cfg(test)] mod tests { use crate::binary::module::Module; use anyhow::Result; #[test] fn decode_simplest_module() -> Result<()> { // Generate wasm binary with only preamble present let wasm = wat::parse_str("(module)")?; // Decode binary and generate Module structure let module = Module::new(&wasm)?; // Compare whether the generated Module structure is as expected assert_eq!(module, Module::default()); Ok(()) } } ``` -------------------------------- ### Add Dependencies to Cargo.toml Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/05_how_decode_wasm_binary.md Configures the project's dependencies in Cargo.toml, including crates for error handling, parsing, LEB128 decoding, and numeric conversions. Also includes development dependencies for Wasm compilation and testing assertions. ```toml [dependencies] anyhow = "1.0.71" # Crate for easy error handling nom = "7.1.3" # Parser combinator nom-leb128 = "0.2.0" # For decoding LEB128 variable length code compressed numbers Crate num-derive = "0.4.0" # Crate that makes converting numeric types convenient num-traits = "0.2.15" # Crate that makes converting numeric types convenient [dev-dependencies] wat = "=1.0.67" # Crate for compiling Wasm binaries from WAT pretty_assertions = "1.4.0" # Crate that makes it easier to see differences during testing ``` -------------------------------- ### Wasm Binary Preamble Structure Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/04_wasm_binary_structure.md Illustrates the initial 8-byte preamble of a Wasm binary, consisting of the magic number '\0asm' and the version number. ```text \0asm ┌───┴───┐ 0000000: 0061 736d ; WASM_BINARY_MAGIC ~~~~~~~ ~~ ~~~~~~~~~~~~~~~~~~~~ │ │ │ │ │ └ Comment │ └ Hexadecimal notation, 2 digits = 1 byte └ Offset of address 0000004: 0100 0000 ; WASM_BINARY_VERSION ``` -------------------------------- ### Define ModuleInst and ExportInst Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/10_build_runtime_func_call.md Defines `ModuleInst` to hold `ExportInst` and `ExportInst` for export information. `ModuleInst::exports` is a `HashMap` for easy lookup by function name. ```rust use std::collections::HashMap; use crate::binary::{ instruction::Instruction, module::Module, types::{ExportDesc, FuncType, ValueType}, }; use anyhow::{bail, Result}; pub enum FuncInst { Internal(InternalFuncInst), } pub struct ExportInst { pub name: String, pub desc: ExportDesc, } #[derive(Default)] pub struct ModuleInst { pub exports: HashMap, } #[derive(Default)] pub struct Store { pub funcs: Vec, pub module: ModuleInst, } impl Store { pub fn new(module: Module) -> Result { let mut funcs = Vec::new(); for _ in module.func_section.iter().flat_map(|s| s.entries.iter()) { funcs.push(FuncInst::Internal(InternalFuncInst::default())); } let mut exports = HashMap::default(); if let Some(ref sections) = module.export_section { for export in sections { let name = export.name.clone(); let export_inst = ExportInst { name: name.clone(), desc: export.desc.clone(), }; exports.insert(name, export_inst); } }; let module_inst = ModuleInst { exports }; Ok(Self { funcs, module: module_inst, }) } } ``` -------------------------------- ### Match Section Codes Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/06_decode_function_1.md Illustrates a `match` statement to handle different section codes during decoding. ```rust match code { SectionCode::Type => { ... } SectionCode::Function => { ... } ... } ``` -------------------------------- ### Implement Runtime::execute for WASM Instruction Processing Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/09_build_runtime_func_execute.md This snippet shows the core implementation of the `execute` method for a WASM runtime. It iterates through instructions, handling `I32Add` by popping operands, performing addition, and pushing the result. It includes logic for stack underflow checks. ```rust fn execute(&mut self) -> Result<()> { loop { let Some(frame) = self.call_stack.last_mut() else { // 1 break; }; frame.pc += 1; let Some(inst) = frame.insts.get(frame.pc as usize) else { // 2 break; }; match inst { // 3 Instruction::I32Add => { let (Some(right), Some(left)) = (self.stack.pop(), self.stack.pop()) else { bail!("not found any value in the stack"); }; let result = left + right; self.stack.push(result); } } } Ok(()) } ``` -------------------------------- ### WebAssembly Type Section Binary Structure Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/04_wasm_binary_structure.md Shows the raw byte representation of the Type Section, including section code, size, number of types, and details of two function types. ```wasm ; section "Type" (1) 0000008: 01 ; section code 0000009: 0d ; section size 000000a: 02 ; num types ; func type 0 000000b: 60 ; func 000000c: 02 ; num params 000000d: 7f ; i32 000000e: 7e ; i64 000000f: 00 ; num results ; func type 1 0000010: 60 ; func 0000011: 02 ; num params 0000012: 7e ; i64 0000013: 7f ; i32 0000014: 02 ; num results 0000015: 7f ; i32 0000016: 7e ; i64 ``` -------------------------------- ### Rust: Initialize WasiSnapshotPreview1 struct Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/14_build_runtime_wasi.md This Rust code defines the WasiSnapshotPreview1 struct, which holds a file table. It initializes the file table with standard input, output, and error file descriptors. ```rust use std::{fs::File, os::fd::FromRawFd}; #[derive(Default)] pub struct WasiSnapshotPreview1 { pub file_table: Vec>, } impl WasiSnapshotPreview1 { pub fn new() -> Self { unsafe { Self { file_table: vec![ Box::new(File::from_raw_fd(0)), Box::new(File::from_raw_fd(1)), Box::new(File::from_raw_fd(2)), ], } } } } ``` -------------------------------- ### Binary Structure of Minimal Wasm Module Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/06_decode_function_1.md The binary representation of the minimal Wasm module, showing the magic number, version, and the structure of the Type, Function, and Code sections. ```text 0000000: 0061 736d ; WASM_BINARY_MAGIC 0000004: 0100 0000 ; WASM_BINARY_VERSION ; section "Type" (1) 0000008: 01 ; section code 0000009: 04 ; section size 000000a: 01 ; num types ; func type 0 000000b: 60 ; func 000000c: 00 ; num params 000000d: 00 ; num results ; section "Function" (3) 000000e: 03 ; section code 000000f: 02 ; section size 0000010: 01 ; num functions 0000011: 00 ; function 0 signature index ; section "Code" (10) 0000012: 0a ; section code 0000013: 04 ; section size 0000014: 01 ; num functions ; function body 0 0000015: 02 ; func body size 0000016: 00 ; local decl count 0000017: 0b ; end ``` -------------------------------- ### Next Iteration Input Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/06_decode_function_1.md Demonstrates how the `rest` of the byte array from the previous step is assigned to `remaining`, serving as the input for decoding the subsequent WASM section. ```text | remaining | |-------------------------------| | [0x03, 0x02, 0x01, 0x00, ...] | ``` -------------------------------- ### Execute LocalSet Instruction in Runtime Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/12_build_runtime_additional_instruction.md This snippet implements the runtime execution of the `LocalSet` instruction in `src/execution/runtime.rs`. It pops a value from the stack and assigns it to the specified local variable index in the current frame. ```diff diff --git a/src/execution/runtime.rs b/src/execution/runtime.rs index 3c492bc..c52de7b 100644 --- a/src/execution/runtime.rs +++ b/src/execution/runtime.rs @@ -154,6 +154,13 @@ impl Runtime { }; self.stack.push(*value); } + Instruction::LocalSet(idx) => { + let Some(value) = self.stack.pop() else { + bail!("not found value in the stack"); + }; + let idx = *idx as usize; + frame.locals[idx] = value; + } Instruction::I32Add => { let (Some(right), Some(left)) = (self.stack.pop(), self.stack.pop()) else { bail!("not found any value in the stack"); ``` -------------------------------- ### Wasm Binary Structure for Return and Arithmetic Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/15_extra_execute_fibonacci.md Hexadecimal dump corresponding to the 'return' and arithmetic operations in WebAssembly text format, illustrating function calls and result aggregation. ```wasm 000002d: 20 ; local.get 000002e: 00 ; local index 000002f: 41 ; i32.const 0000030: 02 ; i32 literal 0000031: 6b ; i32.sub 0000032: 10 ; call 0000033: 00 ; function index 0000034: 20 ; local.get 0000035: 00 ; local index 0000036: 41 ; i32.const 0000037: 01 ; i32 literal 0000038: 6b ; i32.sub 0000039: 10 ; call 000003a: 00 ; function index 000003b: 6a ; i32.add 000003c: 0f ; return 000003d: 0b ; end ``` -------------------------------- ### Add Execution Module to Lib.rs Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/09_build_runtime_func_execute.md Modifies the main library file (`src/lib.rs`) to include the newly created `execution` module. ```diff diff --git a/src/lib.rs b/src/lib.rs index 96eab66..ec63376 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,2 @@ pub mod binary; +pub mod execution; ``` -------------------------------- ### Define Memory and Limits Structures Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/13_build_runtime_initialize_memory.md Defines the `Limits` and `Memory` structs to hold memory configuration, including minimum and optional maximum page counts. ```rust #[derive(Debug, Clone, PartialEq, Eq)] pub struct Limits { pub min: u32, pub max: Option, } #[derive(Debug, Clone, PartialEq, Eq)] pub struct Memory { pub limits: Limits, } ``` -------------------------------- ### Decoded Section Code and Size Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/06_decode_function_1.md Illustrates the state after decoding the section code and size, showing the extracted values and the remaining input for further parsing. ```text | section code | section size | input | |--------------|--------------|-----------------------------------------------------| | 0x01 | 0x04 | [0x01, 0x60, 0x0, 0x0, 0x03, 0x02, 0x01, 0x00, ...] | ``` -------------------------------- ### Add Test for Function Call Instruction Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/10_build_runtime_func_call.md Includes a new unit test `func_call` to verify the correct execution of the `call` instruction. This test uses a WAT file to define a function that doubles its input and asserts the output for several test cases. ```rust diff --git a/src/execution/runtime.rs b/src/execution/runtime.rs index f5d61e..509ec05 100644 --- a/src/execution/runtime.rs +++ b/src/execution/runtime.rs @@ -193,4 +193,18 @@ mod tests { assert!(result.is_err()); Ok(()) } + + #[test] + fn func_call() -> Result<()> { + let wasm = wat::parse_file("src/fixtures/func_call.wat")?; + let mut runtime = Runtime::instantiate(wasm)?; + let tests = vec![(2, 4), (10, 20), (1, 2)]; + + for (arg, want) in tests { + let args = vec![Value::I32(arg)]; + let result = runtime.call("call_doubler", args)?; + assert_eq!(result, Some(Value::I32(want))); + } + Ok(()) + } } ``` -------------------------------- ### Manual SectionCode Conversion Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/06_decode_function_1.md Demonstrates a manual implementation for converting a `u8` byte to a `SectionCode` enum variant. ```rust impl From for SectionCode { fn from(code: u8) -> Self { match code { 0x00 => Self::Custom, 0x01 => Self::Type, ... } } } ``` -------------------------------- ### Function Section Binary Structure Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/04_wasm_binary_structure.md Shows the binary format of the WebAssembly Function Section, detailing the section code, size, number of functions, and the signature index for each function, which links to the Type Section. ```Assembly ; section "Function" (3) 0000017: 03 ; section code 0000018: 04 ; section size 0000019: 03 ; num functions 000001a: 00 ; function 0 signature index 000001b: 01 ; function 1 signature index 000001c: 00 ; function 2 signature index ``` -------------------------------- ### Define Page Size Constant Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/13_build_runtime_initialize_memory.md Defines a constant `PAGE_SIZE` for WebAssembly memory pages, set to 64KiB. ```rust pub const PAGE_SIZE: u32 = 65536; // 64KiB ``` -------------------------------- ### Test Results Summary Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/07_decode_function_2.md Shows the output of running the tests, indicating that all 3 tests passed successfully. ```sh running 3 tests test binary::module::tests::decode_simplest_module ... ok test binary::module::tests::decode_func_param ... ok test binary::module::tests::decode_simplest_func ... ok test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s ``` -------------------------------- ### Call WASM Function by Index Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/10_build_runtime_func_call.md This snippet demonstrates calling a WebAssembly function using its index and providing arguments. It's used to verify the function's return value against an expected result. ```rust let args = vec![Value::I32(left), Value::I32(right)]; let result = runtime.call(0, args)?; assert_eq!(result, Some(Value::I32(want))); ``` -------------------------------- ### Wasm Text Format (WAT) for Addition Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/03_intro_wasm.md Defines a Wasm module with an exported function named 'add' that takes two 32-bit integers and returns their sum. This WAT code is compiled into a Wasm binary. ```wabt (module (func (export "add") (param $a i32) (param $b i32) (result i32) (local.get $a) (local.get $b) i32.add ) ) ``` -------------------------------- ### WASM Function Definition Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/07_decode_function_2.md Defines a WebAssembly function with i32 and i64 parameters. ```wat (module (func (param i32 i64) ) ) ``` -------------------------------- ### Rust: Implement WasiSnapshotPreview1::invoke and fd_write stub Source: https://github.com/skanehira/writing-a-wasm-runtime-in-rust/blob/main/src/14_build_runtime_wasi.md This Rust code adds the `invoke` method to WasiSnapshotPreview1 for dispatching WASI function calls and includes a stub implementation for `fd_write`. ```rust use anyhow::Result; use std::{fs::File, os::fd::FromRawFd}; use super::{store::Store, value::Value}; #[derive(Default)] pub struct WasiSnapshotPreview1 { pub file_table: Vec>, } impl WasiSnapshotPreview1 { pub fn new() -> Self { unsafe { Self { file_table: vec![ Box::new(File::from_raw_fd(0)), Box::new(File::from_raw_fd(1)), Box::new(File::from_raw_fd(2)), ], } } } pub fn invoke( &mut self, store: &mut Store, func: &str, args: Vec, ) -> Result> { match func { "fd_write" => self.fd_write(store, args), _ => unimplemented!("{}", func), } } pub fn fd_write(&mut self, store: &mut Store, args: Vec) -> Result> { // TODO Ok(Some(0.into())) } } ```