Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
1460a6e
wip: flattened test structure
ten3roberts Aug 30, 2023
16eed47
feat: flatten io redirect test
ten3roberts Aug 30, 2023
d0ccb39
chore: move guest examples into dedicated directory
ten3roberts Aug 30, 2023
5078f52
fix: module hierarchy mismatch wasm-bridge-js and wasmtime
ten3roberts Aug 31, 2023
c1bd3b1
fix: negative head not supported on MacOS
ten3roberts Aug 31, 2023
cfbd855
feat: wasi:cli-base/environment
ten3roberts Aug 31, 2023
1a3882d
feat: stdio
ten3roberts Sep 5, 2023
8a310b8
feat: implement filesystem stream support
ten3roberts Aug 31, 2023
1edf077
fix: update streams to newest wit
ten3roberts Aug 31, 2023
069b308
wip: sync streams
ten3roberts Sep 1, 2023
38fba21
feat: expose sync feature
ten3roberts Sep 1, 2023
6b598fb
chore: use git dependency
ten3roberts Aug 22, 2023
8069226
chore: sync WasiCtxBuilder with upstream
ten3roberts Aug 23, 2023
3cf0f45
chore: expose sync feature
ten3roberts Aug 23, 2023
04bf82e
fix: wasm-bridge-js build
ten3roberts Aug 25, 2023
11e12f7
feat: terminal
ten3roberts Sep 1, 2023
ed437e1
fix: terminal world
ten3roberts Sep 1, 2023
19de05e
feat: more terminal
ten3roberts Sep 1, 2023
58f1dda
feat: tracing
ten3roberts Sep 1, 2023
68e35a4
fix: wasi shims
ten3roberts Sep 4, 2023
547d525
chore! remove wasi js shim
ten3roberts Sep 5, 2023
af7d1fb
fix: cli-base aliased as cli
ten3roberts Sep 5, 2023
d8cd04d
fix: cargo component shenanigans
ten3roberts Sep 5, 2023
5955345
fix: remove no longer needed instance aliasing
ten3roberts Sep 5, 2023
831157a
chore: update wasmtime
ten3roberts Sep 7, 2023
526bb14
Squashed commit of the following:
ten3roberts Sep 7, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"rust-analyzer.showUnlinkedFileNotification": false
"rust-analyzer.showUnlinkedFileNotification": false,
"rust-analyzer.cargo.features": "all"
}
8 changes: 6 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ members = [
"crates/wasm-bridge",
"crates/wasm-bridge-js",
"crates/wasm-bridge-macros",
"examples/wit_components/*/*",
"examples/wasi_components/*/*",

"examples/wit_components/",
"examples/wit_components/guest/*/",

"examples/wasi_components/",
"examples/wasi_components/guest/*",
]

exclude = [
Expand Down
8 changes: 5 additions & 3 deletions crates/wasm-bridge-js/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,20 @@ categories.workspace = true
js-sys = { version = "0.3.64" }
wasm-bindgen = "0.2.87"
wasm-bindgen-futures = { version = "0.4.37" }
wat = { version = "1.0.66", optional = true }
wat = { version = "1.0.70", git = "https://github.com/bytecodealliance/wasm-tools", optional = true }
wasm-bridge-macros = { path = "../wasm-bridge-macros", version = "0.2.2", optional = true }
js-component-bindgen = { version = "0.9.5", optional = true }
anyhow = { version = "1.0.22" }
heck = { version = "0.4.1" }
rand_core = { version = "0.6.4", optional = true }
getrandom = { version = "0.2.10", optional = true }
tracing = "0.1"
convert_case = "0.6.0"

[dev-dependencies]
wasm-bindgen-test = "0.3.37"

[features]
component-model = ["wasm-bridge-macros", "js-component-bindgen"]
async = ["wasm-bridge-macros/async"]
wasi = ["async", "rand_core"]
wasi = ["async", "rand_core", "getrandom", "getrandom/js"]
error-logging = []
3 changes: 2 additions & 1 deletion crates/wasm-bridge-js/src/component/component.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{collections::HashMap, rc::Rc};
use std::{collections::HashMap, io::Write, rc::Rc};

use js_sys::{Function, WebAssembly};
use wasm_bindgen::prelude::*;
Expand Down Expand Up @@ -50,6 +50,7 @@ impl Component {
import_object: &JsValue,
closures: Rc<[DropHandle]>,
) -> Result<Instance> {
tracing::debug!("instantiate {:#?}", import_object);
let exports = self
.instantiate
.call3(
Expand Down
4 changes: 4 additions & 0 deletions crates/wasm-bridge-js/src/component/component_loader.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::io::Write;

use anyhow::{bail, Context};
use js_component_bindgen::{transpile, TranspileOpts};
use js_sys::Function;
Expand Down Expand Up @@ -29,6 +31,8 @@ impl ComponentLoader {
if name.ends_with(".wasm") {
wasm_cores.push((name, bytes));
} else if name.ends_with(".js") {
tracing::debug!(s = %&*String::from_utf8_lossy(&bytes), "js loader");
// panic!("{}", String::from_utf8_lossy(&bytes));
// TODO: test that instantiate is not already Some?
instantiate = Some(load_instantiate_fn(&bytes)?);
}
Expand Down
9 changes: 6 additions & 3 deletions crates/wasm-bridge-js/src/component/exports.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::{collections::HashMap, marker::PhantomData, rc::Rc};

use anyhow::Context;
use heck::ToLowerCamelCase;
use convert_case::Casing;
use js_sys::{Object, Reflect};
use wasm_bindgen::JsValue;

Expand Down Expand Up @@ -61,7 +61,7 @@ impl ExportsRoot {

pub fn typed_func<Params, Return>(&self, name: &str) -> Result<TypedFunc<Params, Return>> {
// TODO: converting in the opposite direction when storing would be slightly faster
let name = name.to_lower_camel_case();
let name = name.to_case(convert_case::Case::Camel);

let func = self
.exported_fns
Expand All @@ -77,7 +77,10 @@ impl ExportsRoot {
self.exported_objects
.get(name)
// TODO: This is a workaround for https://github.com/bytecodealliance/jco/issues/110
.or_else(|| self.exported_objects.get(&name.to_lower_camel_case()))?,
.or_else(|| {
self.exported_objects
.get(&name.to_case(convert_case::Case::Camel))
})?,
))
}
}
Expand Down
33 changes: 21 additions & 12 deletions crates/wasm-bridge-js/src/component/linker.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::{collections::HashMap, rc::Rc};
use std::{collections::HashMap, iter::once, rc::Rc};

use heck::ToLowerCamelCase;
use convert_case::Casing;
use js_sys::{Object, Reflect};
use wasm_bindgen::JsValue;

Expand Down Expand Up @@ -29,9 +29,12 @@ impl<T> Linker<T> {
component: &Component,
) -> Result<Instance> {
let import_object = js_sys::Object::new();

if let Some(imports) = &self.wasi_imports {
tracing::debug!("assign wasi imports");
js_sys::Object::assign(&import_object, imports);
}

let import_object: JsValue = import_object.into();

let mut closures = Vec::with_capacity(self.fns.len());
Expand All @@ -42,15 +45,22 @@ impl<T> Linker<T> {
closures.push(drop_handle);
}

// JCO makes instance functions use camel case
for (instance_name, instance_linker) in self.instances.iter() {
let _span = tracing::debug_span!("link instance", instance_name).entered();

let instance_obj = Object::new();

for function in instance_linker.fns.iter() {
tracing::debug!(function = function.name.as_str(), "link instance func");

let drop_handle =
function.add_to_instance_imports(&instance_obj, data_handle.clone());

closures.push(drop_handle);
}

tracing::debug!(instance_name, "assign instance");
Reflect::set(&import_object, &instance_name.into(), &instance_obj).unwrap();
}

Expand All @@ -72,18 +82,18 @@ impl<T> Linker<T> {
self
}

pub fn func_wrap<Params, Results, F>(&mut self, name: &str, func: F) -> Result<()>
pub fn func_wrap<Params, Results, F>(&mut self, name: &str, func: F) -> Result<&mut Self>
where
T: 'static,
F: IntoMakeClosure<T, Params, Results>,
{
self.fns
.push(PreparedFn::new(name, func.into_make_closure()));

Ok(())
Ok(self)
}

pub fn func_wrap_async<Params, Results, F>(&mut self, name: &str, func: F) -> Result<()>
pub fn func_wrap_async<Params, Results, F>(&mut self, name: &str, func: F) -> Result<&mut Self>
where
T: 'static,
F: IntoMakeClosure<T, Params, Results>,
Expand All @@ -98,11 +108,6 @@ impl<T> Linker<T> {
.entry(name.to_owned())
.or_insert_with(|| Linker::new(&Engine {}))) // TODO: engine re-creation
}

#[cfg(feature = "wasi")]
pub(crate) fn set_wasi_imports(&mut self, imports: Object) {
self.wasi_imports = Some(imports);
}
}

struct PreparedFn<T> {
Expand All @@ -121,6 +126,7 @@ impl<T> PreparedFn<T> {

#[must_use]
fn add_to_imports(&self, imports: &JsValue, handle: DataHandle<T>) -> DropHandle {
tracing::debug!("import func {}", self.name);
let (js_val, handler) = (self.creator)(handle);

let object: JsValue = Object::new().into();
Expand All @@ -135,8 +141,11 @@ impl<T> PreparedFn<T> {
fn add_to_instance_imports(&self, imports: &JsValue, handle: DataHandle<T>) -> DropHandle {
let (js_val, handler) = (self.creator)(handle);

Reflect::set(imports, &self.name.to_lower_camel_case().into(), &js_val)
.expect("imports is object");
let name = self.name.to_case(convert_case::Case::Camel);

tracing::debug!(?name, "instance func");

Reflect::set(imports, &name.into(), &js_val).expect("imports is object");

handler
}
Expand Down
79 changes: 79 additions & 0 deletions crates/wasm-bridge-js/src/wasi/preview2/command.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
use anyhow::bail;
use js_sys::Object;

use crate::component::Linker;
use crate::wasi::preview2::{clocks, WasiView};
use crate::{Result, StoreContextMut};

use super::{environment, filesystem, preopens, stdio, streams, terminal};

pub fn add_to_linker<T: WasiView + 'static>(linker: &mut Linker<T>) -> Result<()> {
// Overrides
// linker.instance("wasi:io/streams")?.func_wrap(
// "read",
// |data: StoreContextMut<T>, (id, max_bytes): (u32, u64)| {
// if id != STDIN_IDENT {
// bail!("unexpected read stream id: {id}");
// }

// let mut bytes = vec![0u8; max_bytes as usize];

// let (bytes_written, stream_ended) = data.ctx_mut().stdin().read(&mut bytes)?;

// bytes.truncate(bytes_written as _);

// Ok((bytes, stream_ended))
// },
// )?;

// linker.instance("wasi:io/streams")?.func_wrap(
// "write",
// |data: StoreContextMut<T>, (id, buffer): (u32, Vec<u8>)| {
// let bytes_written = match id {
// STDOUT_IDENT => data.ctx_mut().stdout().write(&buffer)?,

// STDERR_IDENT => data.ctx_mut().stderr().write(&buffer)?,

// id => bail!("unexpected write stream id: {id}"),
// };
// Ok(bytes_written)
// },
// )?;

linker.instance("wasi:random/random")?.func_wrap(
"get-random-bytes",
|data: StoreContextMut<T>, (len,): (u64,)| {
let random = data.ctx_mut().random();
let mut bytes = vec![0u8; len as _];
random.fill_bytes(&mut bytes);
Ok(bytes)
},
)?;

linker
.instance("wasi:random/random")?
.func_wrap("get-random-u64", |data: StoreContextMut<T>, (): ()| {
Ok(data.ctx_mut().random().next_u64())
})?;

linker.instance("wasi:cli/exit")?.func_wrap(
"exit",
|_data: StoreContextMut<T>,
(_status,): (std::result::Result<(), ()>,)|
-> anyhow::Result<()> {
todo!("exit called");
// Ok(())
},
)?;

clocks::add_to_linker(linker)?;
stdio::add_to_linker(linker)?;

environment::add_to_linker(linker)?;
preopens::add_to_linker(linker)?;
filesystem::add_to_linker(linker)?;
terminal::add_to_linker(linker)?;
streams::add_to_linker(linker)?;

Ok(())
}
30 changes: 30 additions & 0 deletions crates/wasm-bridge-js/src/wasi/preview2/environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//! Provides environment interaction for WASI.
//!
//! See: <https://github.com/WebAssembly/wasi-cli/blob/main/wit/environment.wit>

use crate::{component::Linker, Result, StoreContextMut};

use super::WasiView;

/// Adds environment integration to the linker
pub(crate) fn add_to_linker<T: 'static + WasiView>(linker: &mut Linker<T>) -> Result<()> {
linker
.instance("wasi:cli/environment")?
.func_wrap("get-environment", |data: StoreContextMut<T>, (): ()| {
Ok(data
.ctx()
.environment()
.iter()
.map(|(k, v)| (k.to_owned(), v.to_owned()))
.collect::<Vec<_>>())
})?
.func_wrap(
"get-arguments",
|_data: StoreContextMut<T>, (): ()| -> Result<String> { unimplemented!() },
)?
.func_wrap("initial-cwd", |_data: StoreContextMut<T>, (): ()| {
Ok(String::from("."))
})?;

Ok(())
}
46 changes: 46 additions & 0 deletions crates/wasm-bridge-js/src/wasi/preview2/filesystem.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use std::io;

use crate::{component::Linker, Result, StoreContextMut};

use super::{stream, WasiView};

pub type Descriptor = u32;
pub type OutputStream = u32;
pub type InputStream = u32;

pub(crate) fn add_to_linker<T: 'static + WasiView>(linker: &mut Linker<T>) -> Result<()> {
linker
.instance("wasi:filesystem/types")?
.func_wrap(
"append-via-stream",
|_data: StoreContextMut<T>, (_this,): (Descriptor,)| -> Result<OutputStream> {
Err(io::Error::new(io::ErrorKind::Unsupported, "append-via-stream").into())
},
)?
.func_wrap(
"drop-descriptor",
|_data: StoreContextMut<T>, (_this,): (Descriptor,)| -> Result<()> {
Err(io::Error::new(io::ErrorKind::Unsupported, "drop-descriptor").into())
},
)?
.func_wrap(
"get-type",
|_data: StoreContextMut<T>, (_this,): (Descriptor,)| -> Result<u32> {
Err(io::Error::new(io::ErrorKind::Unsupported, "get-type").into())
},
)?
.func_wrap(
"read-via-stream",
|_data: StoreContextMut<T>, (_this, _off): (Descriptor, u64)| -> Result<InputStream> {
Err(io::Error::new(io::ErrorKind::Unsupported, "read-via-stream").into())
},
)?
.func_wrap(
"write-via-stream",
|_data: StoreContextMut<T>, (_this, _off): (Descriptor, u64)| -> Result<OutputStream> {
Err(io::Error::new(io::ErrorKind::Unsupported, "write-via-stream").into())
},
)?;

Ok(())
}
17 changes: 12 additions & 5 deletions crates/wasm-bridge-js/src/wasi/preview2/mod.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
mod streams;
mod wasi_ctx_builder;
pub use wasi_ctx_builder::*;
pub use wasi_ctx_builder::{IsATTY, WasiCtxBuilder};

mod wasi_ctx;
pub use wasi_ctx::*;
pub use wasi_ctx::WasiCtx;

mod table;
pub use table::*;
pub use table::Table;

mod wasi_view;
pub use wasi_view::*;
pub use wasi_view::WasiView;

pub mod wasi;
pub mod command;

pub mod stream;
pub use stream::*;
Expand All @@ -20,3 +21,9 @@ pub use clocks::*;

mod random;
pub(crate) use random::*;

pub(crate) mod environment;
pub(crate) mod filesystem;
pub(crate) mod preopens;
pub(crate) mod stdio;
pub(crate) mod terminal;
Loading