Skip to content

Commit fe56633

Browse files
committed
Add support to wit-component to polyfill WASI
This commit is an addition to the `wit-component` tool to be able to polyfill WASI imports today using `wasi_snapshot_preview1` with a component-model-using interface in the future. This is a large extension to the functionality of `wit-component` internally since the generated component is much "fancier". The support in this commit is modeled as the addition of "adapter modules" into the `wit-component` tool. An adapter module is understood to translate from some core-wasm ABI into a component-model using ABI. The intention is that for any previous API prior to the component model an adapter module could be written which would translate from the prior API to the new API. For example in WASI today there is: (@interface func (export "random_get") (param $buf (@WitX pointer u8)) (param $buf_len $size) (result $error (expected (error $errno))) ) whereas a component-model-using API would look more like: random-get: func(size: u32) -> list<u8> This component-model version can be adapted with a module such as: (module $wasi_snapshot_preview1 (import "new-wasi" "random_get" (func $new_random_get (param i32 i32))) (import "env" "memory" (memory 0)) (global $last_ptr (mut i32) i32.const 0) (func (export "random_get") (param i32 i32) (result i32) ;; store buffer pointer in a saved global for `cabi_realloc` ;; later (global.set $last_ptr (local.get 0)) ;; 1st argument: the `size: u32` local.get 1 ;; 2nd argument: return pointer for `list<u8>` i32.const 8 call $new_random_get ;; return a "success" return code i32.const 0 ) ;; When the canonical ABI allocates space for the list return value ;; return the original buffer pointer to place it directly in the ;; target buffer (func (export "cabi_realloc") (param i32 i32 i32 i32) (result i32) global.get $last_ptr) ) Using this adapter module the internal structure of the generated component can be done such that everything is wired up in all the right places meaning that when the original module calls `wasi_snapshot_preview1::random_get` it actually calls this shim module which then calls the actual `new-wasi::random_get` import. There's a few details I'm glossing over here like the stack used by the shim module but this suffices to describe the general shape. My plan in the future is to use this support to generate a component from all test cases that this repository supports. That means that, specifically for `wit-bindgen` tests, a fresh new interface representing "future WASI" will be created and the WASI functions used by tests will be adapted via this adapter module. In this manner components will now be generated for all tests and then the next step is bytecodealliance#314, actually ingesting these components into hosts.
1 parent a3a1c01 commit fe56633

30 files changed

+1873
-182
lines changed

Cargo.lock

Lines changed: 1 addition & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,7 @@ wit-bindgen-gen-host-js = { path = 'crates/gen-host-js', features = ['structopt'
2727
wit-bindgen-gen-guest-c = { path = 'crates/gen-guest-c', features = ['structopt'] }
2828
wit-bindgen-gen-markdown = { path = 'crates/gen-markdown', features = ['structopt'] }
2929
wit-bindgen-gen-guest-teavm-java = { path = 'crates/gen-guest-teavm-java', features = ['structopt'] }
30+
31+
[patch.crates-io]
32+
wasmparser = { path = '../wasm-tools/crates/wasmparser' }
33+
wasm-encoder = { path = '../wasm-tools/crates/wasm-encoder' }

crates/test-helpers/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ filetime = "0.2"
3030
wit-bindgen-gen-guest-c = { path = '../gen-guest-c' }
3131
wit-bindgen-gen-guest-teavm-java = { path = '../gen-guest-teavm-java' }
3232
wit-bindgen-core = { path = '../bindgen-core' }
33+
wit-component = { path = '../wit-component' }
3334

3435
[features]
3536
default = ['guest-rust', 'guest-c', 'guest-teavm-java', 'host-js', 'host-wasmtime-py', 'host-wasmtime-rust']

crates/test-helpers/build.rs

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,16 @@ fn main() {
6161

6262
let import = Interface::parse_file(&test_dir.join("imports.wit")).unwrap();
6363
let export = Interface::parse_file(&test_dir.join("exports.wit")).unwrap();
64+
let imports = &[import];
65+
let exports = &[export];
6466
let mut files = Default::default();
6567
// TODO: should combine this into one
6668
wit_bindgen_gen_guest_c::Opts::default()
6769
.build()
68-
.generate_all(&[import], &[], &mut files);
70+
.generate_all(imports, &[], &mut files);
6971
wit_bindgen_gen_guest_c::Opts::default()
7072
.build()
71-
.generate_all(&[], &[export], &mut files);
73+
.generate_all(&[], exports, &mut files);
7274

7375
let out_dir = out_dir.join(format!(
7476
"c-{}",
@@ -120,6 +122,16 @@ fn main() {
120122
test_dir.file_stem().unwrap().to_str().unwrap().to_string(),
121123
out_wasm.to_str().unwrap().to_string(),
122124
));
125+
126+
let wasm = std::fs::read(&out_wasm).unwrap();
127+
wit_component::ComponentEncoder::default()
128+
.validate(true)
129+
.module(&wasm)
130+
.imports(imports)
131+
.interface(&exports[0])
132+
.wasi(true)
133+
.encode()
134+
.unwrap();
123135
}
124136
}
125137

crates/wit-component/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,13 @@ env_logger = { version = "0.9.1", optional = true }
3131
log = { version = "0.4.17", optional = true }
3232
bitflags = "1.3.2"
3333

34+
wasmprinter = "*"
35+
3436
[dev-dependencies]
3537
wasmprinter = "0.2.40"
3638
glob = "0.3.0"
3739
pretty_assertions = "1.3.0"
40+
env_logger = "0.9.1"
3841

3942
[features]
4043
default = ["cli"]

crates/wit-component/src/adapter.rs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
use crate::validation::ValidatedAdapter;
2+
use anyhow::{Context, Result};
3+
use indexmap::IndexMap;
4+
use wasmparser::FuncType;
5+
use wit_parser::Interface;
6+
7+
mod gc;
8+
9+
pub fn adapt<'a>(
10+
wasm: &[u8],
11+
interface: &'a Interface,
12+
required: &IndexMap<&str, FuncType>,
13+
) -> Result<(Vec<u8>, ValidatedAdapter<'a>)> {
14+
let wasm = gc::run(wasm, required)
15+
.context("failed to reduce input adapter module to its minimal size")?;
16+
let info = crate::validation::validate_adapter_module(&wasm, interface, required)
17+
.context("failed to validate the imports of the minimized adapter module")?;
18+
Ok((wasm, info))
19+
}

0 commit comments

Comments
 (0)