{"id":13547187,"url":"https://github.com/rhysd/wain","last_synced_at":"2025-04-08T11:15:16.571Z","repository":{"id":37949443,"uuid":"241254192","full_name":"rhysd/wain","owner":"rhysd","description":"WebAssembly implementation from scratch in Safe Rust with zero dependencies","archived":false,"fork":false,"pushed_at":"2024-07-22T09:13:39.000Z","size":969,"stargazers_count":445,"open_issues_count":2,"forks_count":22,"subscribers_count":13,"default_branch":"master","last_synced_at":"2025-04-01T10:08:31.148Z","etag":null,"topics":["interpreter","programming-language","wasm","wat","webassembly"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/rhysd.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2020-02-18T02:26:59.000Z","updated_at":"2025-03-30T16:32:13.000Z","dependencies_parsed_at":"2023-11-18T04:22:39.437Z","dependency_job_id":"ffb36804-8d07-48b3-8637-3bcaf1659e64","html_url":"https://github.com/rhysd/wain","commit_stats":{"total_commits":488,"total_committers":5,"mean_commits":97.6,"dds":0.1864754098360656,"last_synced_commit":"18e3122392564809e88dfb8c5542641ff79296c3"},"previous_names":[],"tags_count":38,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhysd%2Fwain","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhysd%2Fwain/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhysd%2Fwain/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/rhysd%2Fwain/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/rhysd","download_url":"https://codeload.github.com/rhysd/wain/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247829512,"owners_count":21002997,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["interpreter","programming-language","wasm","wat","webassembly"],"created_at":"2024-08-01T12:00:52.220Z","updated_at":"2025-04-08T11:15:16.550Z","avatar_url":"https://github.com/rhysd.png","language":"Rust","readme":"wain\n====\n[![crates.io][crates-io-badge]][crates-io]\n[![CI][ci-badge]][ci]\n\n[wain][proj] is a **W**eb**A**ssembly **IN**terpreter written in Safe Rust from scratch with zero dependencies.\nAn implementation of [WebAssembly][wasm-spec].\n\n\u003cimg width=438 height=257 src=\"https://github.com/rhysd/ss/blob/master/wain/main.gif?raw=true\" alt=\"screencast\"\u003e\n\n**Features:**\n\n- No unsafe code. Memory safety and no undefined behavior are guaranteed\n- No external runtime dependency. It means no unsafe dependency and less binary size\n- Efficiency. Avoid unnecessary allocations and run instructions as fast as possible without unsafe code\n- Modular implementation. Binary format parser, text format parser, validator, executor are\n  developed as separate libraries\n\nNote that this project is in progress. Before v1.0.0 means experimental. Not all of the features\nare implemented yet. Current status is that all the MVP implementations have been done and many\ntasks are remaining.\n\n**Roadmap to v1.0.0 (priority order):**\n\n- Add sufficient tests to all libraries and fuzz tests for parsers\n- Pass all [spec tests][wasm-test-suite]\n- Add benchmarks to track performance\n- Introduce an intermediate representation to execute instructions more efficiently\n- Add documentation for every public APIs\n\nPlease see [the task board](https://github.com/rhysd/wain/projects/1) for current progress.\n\nThis project started for fun and understanding Wasm deeply.\n\n\n## Installation\n\n`wain` crate is not published yet. Please clone this repository and build the project by `cargo build`.\n\nMinimum supported Rust version is 1.45.0.\n\n```\n$ cargo install wain\n$ wain --help\n```\n\nIf you don't want to run text format code, it can be omitted:\n\n```\n# Only run binary format files\n$ cargo install wain --no-default-features --features binary\n```\n\n\n## Usage\n\n### `wain` command\n\nRun a binary format Wasm source file:\n\n```\n$ wain examples/hello/hello.wasm\nHello, world\n```\n\nRun a text format Wasm source file:\n\n```\n$ wain examples/hello/hello.wat\nHello, world\n```\n\nWithout argument, `wain` command detects both binary format and text format from stdin and runs the\ninput.\n\n```\n$ wain \u003c examples/hello/hello.wasm\nHello, world\n$ wain \u003c examples/hello/hello.wat\nHello, world\n```\n\nPlease see [examples directory](./examples) for more examples.\n\nCurrent restrictions are as follows:\n\n- Only `int putchar(int)` and `int getchar()` are implemented as external functions by default\n- wain can run only one module at once. It means that importing things from other modules does not\n  work yet\n- Many extensions like threads, WASI support, SIMD support, ... are not implemented yet\n\n### As libraries\n\nwain consists of multiple crates.\n\n- **[wain](.):** Command line tool to execute given Wasm sources\n- **[wain-ast](./wain-ast):** Abstract syntax tree definition. Implementation of [Wasm structure spec][wasm-spec-structure].\n  This syntax tree is common for both binary format and text format\n- **[wain-syntax-binary](./wain-syntax-binary):** Parser for Wasm binary format (`.wasm` files).\n  Implementation for [Wasm binary format spec][wasm-spec-bin]. It parses `\u0026[u8]` value into\n  `wain_ast::Root` abstract syntax tree\n- **[wain-syntax-text](./wain-syntax-text):** Parser for Wasm text format (`.wat` files).\n  Implementation for [Wasm text format spec][wasm-spec-text]. It parses `\u0026str` value into\n  `wain_ast::Root` abstract syntax tree\n- **[wain-validate](./wain-validate):** Validator of a Wasm abstract syntax tree. Implementation of\n  [Wasm validation spec][wasm-spec-validation]\n- **[wain-exec](./wain-exec):** Executor which interprets a Wasm abstract syntax tree. Implementation\n  of [Wasm execution spec][wasm-spec-exec]. It directly interprets a syntax tree for now, but in the\n  it would translate the tree into an intermediate representation to execute it efficiently\n\n`wain-*` crates are libraries as modular implementation of WebAssembly. They can parse, validate,\nexecute WebAssembly code.\n\nHere is an example code to run the interpreter from Rust.\n\n```rust\nextern crate wain_syntax_binary;\nextern crate wain_validate;\nextern crate wain_exec;\n\nuse std::fs;\nuse std::process::exit;\nuse wain_syntax_binary::parse;\nuse wain_validate::validate;\nuse wain_exec::execute;\n\n// Read wasm binary\nlet source = fs::read(\"foo.wasm\").unwrap();\n\n// Parse binary into syntax tree\nlet tree = match parse(\u0026source) {\n    Ok(tree) =\u003e tree,\n    Err(err) =\u003e {\n        eprintln!(\"Could not parse: {}\", err);\n        exit(1);\n    }\n};\n\n// Validate module\nif let Err(err) = validate(\u0026tree) {\n    eprintln!(\"This .wasm file is invalid!: {}\", err);\n    exit(1);\n}\n\n// Execute module\nif let Err(trap) = execute(\u0026tree.module) {\n    eprintln!(\"Execution was trapped: {}\", trap);\n    exit(1);\n}\n```\n\nOr invoke specific exported function with arguments\n\n```rust\n// ...(snip)\n\nuse wain_exec::{Runtime, DefaultImporter, Value};\nuse std::io;\n\n// Create default importer to call external function supported by default\nlet stdin = io::stdin();\nlet stdout = io::stdout();\nlet importer = DefaultImporter::with_stdio(stdin.lock(), stdout.lock());\n\n// Make abstract machine runtime. It instantiates a module\nlet mut runtime = match Runtime::instantiate(\u0026tree.module, importer) {\n    Ok(m) =\u003e m,\n    Err(err) =\u003e {\n        eprintln!(\"could not instantiate module: {}\", err);\n        exit(1);\n    }\n};\n\n// Let's say `int add(int, int)` is exported\nmatch runtime.invoke(\"add\", \u0026[Value::I32(10), Value::I32(32)]) {\n    Ok(ret) =\u003e {\n        // `ret` is type of `Option\u003cValue\u003e` where it contains `Some` value when the invoked\n        // function returned a value. Otherwise it's `None` value.\n        if let Some(Value::I32(i)) = ret {\n            println!(\"10 + 32 = {}\", i);\n        } else {\n            unreachable!();\n        }\n    }\n    Err(trap) =\u003e eprintln!(\"Execution was trapped: {}\", trap),\n}\n```\n\nBy default, only following C functions are supported in `env` module as external functions\n\n- `int putchar(int)` (in wasm `(func (param i32) (result i32))`)\n- `int getchar(void)` (in wasm `(func (param) (result i32))`)\n- `void *memcpy(void *, void *, size_t)` (in wasm `(func (param i32 i32 i32) (result i32))`)\n- `void abort(void)` (in wasm `(func (param) (result))`)\n\nBut you can implement your own struct which implements `wain_exec::Importer` for defining external\nfunctions from Rust side.\n\n```rust\nextern crate wain_exec;\nextern crate wain_ast;\nuse wain_exec::{Runtime, Stack, Memory, Importer, ImportInvokeError, ImportInvalidError}\nuse wain_ast::ValType;\n\nstruct YourOwnImporter {\n    // ...\n}\n\nimpl Importer for YourOwnImporter {\n    fn validate(\u0026self, name: \u0026str, params: \u0026[ValType], ret: Option\u003cValType\u003e) -\u003e Option\u003cImportInvalidError\u003e {\n        // `name` is a name of function to validate. `params` and `ret` are the function's signature.\n        // Return ImportInvalidError::NotFound when the name is unknown.\n        // Return ImportInvalidError::SignatureMismatch when signature does not match.\n        // wain_exec::check_func_signature() utility is would be useful for the check.\n    }\n    fn call(\u0026mut self, name: \u0026str, stack: \u0026mut Stack, memory: \u0026mut Memory) -\u003e Result\u003c(), ImportInvokeError\u003e {\n        // Implement your own function call. `name` is a name of function and you have full access\n        // to stack and linear memory. Pop values from stack for getting arguments and push value to\n        // set return value.\n        // Note: Consistency between imported function signature and implementation of this method\n        // is your responsibility.\n        // On invocation failure, return ImportInvokeError::Fatal. It is trapped by interpreter and it\n        // stops execution immediately.\n    };\n}\n\nlet ast = ...; // Parse abstract syntax tree and validate it\n\nlet mut runtime = Runtime::instantiate(\u0026ast.module, YourOwnImporter{ /* ... */ }).unwrap();\nlet result = runtime.invoke(\"do_something\", \u0026[]);\n```\n\nTo know the usage of APIs, working examples are available at [examples/api/](./examples/api).\n\n\n## Future works\n\n- WASI support\n- Wasm features after MVP support (threads, SIMD, multiple return values, ...)\n- Compare benchmarks with other Wasm implementations\n- Self-hosting interpreter. Compile wain into Wasm and run it by itself\n- Upgrade the Wasm implementation from [Wasm v1][wasm-v1] to [Wasm v2][wasm-v2].\n\n\n## How it works\n\nHere I note some points on each phase of interpretation.\n\n### Parsing\n\n\u003cimg width=644 height=433 src=\"https://github.com/rhysd/ss/blob/master/wain/parsing-diagram.png?raw=true\" alt=\"Sequence to parse Wasm\"\u003e\n\n[wain-syntax-binary](./wain-syntax-binary) parses `.wasm` binary file into `wain_ast::Root` abstract\nsyntax tree following [binary format spec][wasm-spec-bin]. Wasm binary format is designed to be\nparsed by basic LL(1) parser. So parsing is very straightforward.\n[Parser implementation](./wain-syntax-binary/src/parser.rs) is smaller than 1000 lines.\n\nIn contrast, implementation of parsing text format is more complicated. [wain-syntax-text](./wain-syntax-text)\nparses `.wat` text file into `wain_ast::Root` abstract syntax tree following [text format spec][wasm-spec-text].\n\n1. Lex and parse `.wat` file into WAT sytnax tree which is dedicated for text format resolving many\n   syntax sugars. Since multiple modules can be put in `.wat` file, it can be parsed into multiple trees\n2. Translate the WAT syntax trees into common Wasm syntax trees (`wain_ast::Root`) resolving identifiers.\n   Identifiers may refer things not defined yet (forward references) so `.wat` file cannot be parsed\n   into common Wasm syntax trees directly\n3. Compose a single module from the multiple Wasm syntax trees following\n   [spec](https://webassembly.github.io/spec/core/text/modules.html#text-module)\n\n### Validation\n\nValidation is done by traversing a given Wasm syntax tree in [wain-validate](./wain-validate) crate.\nConforming [spec][wasm-spec-validation], following things are validated:\n\n- In Wasm, every reference is an index. It validates all indices are not out of bounds\n- Wasm is designed to check stack operations statically. It validates instructions sequences with\n  emulating stack state\n- Type check is best-effort due to polymorphic instruction `select`. Since almost all instructions\n  are not polymorphic, almost all type checks can be done in validation\n\nConforming the spec, wain validates instructions after `unreachable` instruction. For example,\n\n```wat\n(unreachable) (i64.const 0) (i32.add)\n```\n\n`i32.add` is invalid because it should take two `i32` values from stack but at least one `i64` value\nis in the stack.\n\n### Execution\n\n[wain-exec](./wain-exec) crate interprets a Wasm syntax tree conforming [spec][wasm-spec-exec]. Thanks\nto validation, checks at runtime are minimal (e.g. function signature on indirect call).\n\n1. Allocate memory, table, global variables. Initialize stack\n2. Interpret syntax tree nodes pushing/popping values to/from stack\n\nCurrently wain interprets a Wasm syntax tree directly. I'm planning to define intermediate representation\nwhich can be interpreted faster.\n\nEntrypoint is 'start function' which is defined either\n\n1. Function set in `start` section\n2. Exported function named `_start` in export section\n\nThe 1. is a standard entrypoint but Clang does not emit `start` section. Instead it handles `_start`\nfunction as entrypoint. wain implements both entrypoints (1. is prioritized).\n\n\n## License\n\n[the MIT license](./LICENSE.txt)\n\n[ci-badge]: https://github.com/rhysd/wain/workflows/CI/badge.svg?branch=master\u0026event=push\n[ci]: https://github.com/rhysd/wain/actions?query=workflow%3ACI+branch%3Amaster+event%3Apush\n[crates-io-badge]: https://img.shields.io/crates/v/wain.svg\n[crates-io]: https://crates.io/crates/wain\n[proj]: https://github.com/rhysd/wain\n[wasm-spec]: https://webassembly.github.io/spec/core/index.html\n[wasm-test-suite]: https://github.com/WebAssembly/testsuite\n[wasm-spec-structure]: https://webassembly.github.io/spec/core/syntax/index.html\n[wasm-spec-bin]: https://webassembly.github.io/spec/core/binary/index.html\n[wasm-spec-text]: https://webassembly.github.io/spec/core/text/index.html\n[wasm-spec-validation]: https://webassembly.github.io/spec/core/valid/index.html\n[wasm-spec-exec]: https://webassembly.github.io/spec/core/exec/index.html\n[wasm-v1]: https://www.w3.org/TR/2019/REC-wasm-core-1-20191205/\n[wasm-v2]: https://www.w3.org/TR/wasm-core-2/\n","funding_links":[],"categories":["Rust","Development tools","开发工具 Development tools"],"sub_categories":["FFI","FFI FFI"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frhysd%2Fwain","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frhysd%2Fwain","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frhysd%2Fwain/lists"}