Skip to content

Commit 4934194

Browse files
authored
js: Take a component as input, not interfaces (#373)
* js: Take a component as input, not interfaces This commit is the implementation of #314 for the JS host generator. The commit here covers basically everything in that issue for JS except it generates a slightly different structure of the JS output. Otherwise the main highlights are: * The JS host generator no longer implements the core `Generator` trait since it doesn't really fit in the component-as-input world. For now I created an `InterfaceGenerator` trait since similar functionality is used just not precisely the same. I expect that this will get iterated on over time as worlds and other host generators take shape. * The `wasmtime-environ` crate from Wasmtime itself, typically a "private" dependency of Wasmtime, is used to parse the input component and generate an `instantiate` function in JS. Wasmtime does all the heavy lifting of creating a linear list of initializers for the component and this empowers the generator to simply generate JS that is the list of initializers. * The `wit-component` crate is used to "extract" the `Interface` descriptions from the input component. This is used to generate type information for TypeScript as well as types for lifting/lowering. Internally a correlation is done from a lowering/lifting to an `Interface` function to connect the dots when instantiating a component. Lots of pieces needed updating here such as the runtime tests, the `wit-bindgen` CLI tool, and the demo web page. These are all updated for the new structure of the JS host generator. Overall this surprisingly went much more smoothly than I expected. With `wit-bindgen` being able to extract `Interface` representations from a component most of the prior JS host code generation was able to be reused. Along the way I also felt that the addition of "worlds" would have relatively obvious insertion points and would be relatively easily handled in all the places. The demo and runtime tests are proof enough to me at least that this all works internally, and this feels like a solid foundation to iterate from with the addition of worlds and continued support for JS hosts. * Rebase and fix tests * Pull in latest `wasmtime` where `main` now works * Add a `testwasi` implementation for JS and use it in all tests * Add a dummy `printf` to a C test to ensure it imports `testwasi` like the other languages. * Add a `fd_fdstat_get` stub to make C happy * Update `fd_write` to only work for fd 1 * Review comments
1 parent 0dd45bf commit 4934194

File tree

37 files changed

+1922
-1650
lines changed

37 files changed

+1922
-1650
lines changed

Cargo.lock

+42-30
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ env_logger = "0.9.1"
2828

2929
wasmtime = { git = "https://github.com/bytecodealliance/wasmtime", branch = "main" , features = ["component-model"] }
3030
wasmtime-wasi = { git = "https://github.com/bytecodealliance/wasmtime", branch = "main" }
31+
wasmtime-environ = { git = "https://github.com/bytecodealliance/wasmtime", branch = "main" }
3132
wasmprinter = "0.2.41"
3233
wasmparser = "0.92.0"
3334
wasm-encoder = "0.18.0"
@@ -62,3 +63,4 @@ wit-bindgen-gen-host-js = { path = 'crates/gen-host-js', features = ['clap'] }
6263
wit-bindgen-gen-guest-c = { path = 'crates/gen-guest-c', features = ['clap'] }
6364
wit-bindgen-gen-markdown = { path = 'crates/gen-markdown', features = ['clap'] }
6465
wit-bindgen-gen-guest-teavm-java = { path = 'crates/gen-guest-teavm-java', features = ['clap'] }
66+
wat = { workspace = true }

crates/bindgen-core/src/lib.rs

+52-1
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,11 @@ impl Source {
397397
self.indent += 1;
398398
}
399399
if trimmed.starts_with('}') {
400-
self.indent -= 1;
400+
// Note that a `saturating_sub` is used here to prevent a panic
401+
// here in the case of invalid code being generated in debug
402+
// mode. It's typically easier to debug those issues through
403+
// looking at the source code rather than getting a panic.
404+
self.indent = self.indent.saturating_sub(1);
401405
}
402406
if i != lines.len() - 1 || src.ends_with("\n") {
403407
self.newline();
@@ -526,3 +530,50 @@ mod tests {
526530
fn _assert(_: &dyn Generator) {}
527531
}
528532
}
533+
534+
/// This is a possible replacement for the `Generator` trait above, currently
535+
/// only used by the JS bindings for generating bindings for a component.
536+
///
537+
/// The current plan is to see how things shake out with worlds and various
538+
/// other generators to see if everything can be updated to a less
539+
/// per-`*.wit`-file centric interface in the future. Even this will probably
540+
/// change for JS though. In any case it's something that was useful for JS and
541+
/// is suitable to replace otherwise at any time.
542+
pub trait InterfaceGenerator<'a> {
543+
fn iface(&self) -> &'a Interface;
544+
545+
fn type_record(&mut self, id: TypeId, name: &str, record: &Record, docs: &Docs);
546+
fn type_flags(&mut self, id: TypeId, name: &str, flags: &Flags, docs: &Docs);
547+
fn type_tuple(&mut self, id: TypeId, name: &str, flags: &Tuple, docs: &Docs);
548+
fn type_variant(&mut self, id: TypeId, name: &str, variant: &Variant, docs: &Docs);
549+
fn type_option(&mut self, id: TypeId, name: &str, payload: &Type, docs: &Docs);
550+
fn type_result(&mut self, id: TypeId, name: &str, result: &Result_, docs: &Docs);
551+
fn type_union(&mut self, id: TypeId, name: &str, union: &Union, docs: &Docs);
552+
fn type_enum(&mut self, id: TypeId, name: &str, enum_: &Enum, docs: &Docs);
553+
fn type_alias(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs);
554+
fn type_list(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs);
555+
fn type_builtin(&mut self, id: TypeId, name: &str, ty: &Type, docs: &Docs);
556+
557+
fn types(&mut self) {
558+
for (id, ty) in self.iface().types.iter() {
559+
let name = match &ty.name {
560+
Some(name) => name,
561+
None => continue,
562+
};
563+
match &ty.kind {
564+
TypeDefKind::Record(record) => self.type_record(id, name, record, &ty.docs),
565+
TypeDefKind::Flags(flags) => self.type_flags(id, name, flags, &ty.docs),
566+
TypeDefKind::Tuple(tuple) => self.type_tuple(id, name, tuple, &ty.docs),
567+
TypeDefKind::Enum(enum_) => self.type_enum(id, name, enum_, &ty.docs),
568+
TypeDefKind::Variant(variant) => self.type_variant(id, name, variant, &ty.docs),
569+
TypeDefKind::Option(t) => self.type_option(id, name, t, &ty.docs),
570+
TypeDefKind::Result(r) => self.type_result(id, name, r, &ty.docs),
571+
TypeDefKind::Union(u) => self.type_union(id, name, u, &ty.docs),
572+
TypeDefKind::List(t) => self.type_list(id, name, t, &ty.docs),
573+
TypeDefKind::Type(t) => self.type_alias(id, name, t, &ty.docs),
574+
TypeDefKind::Future(_) => todo!("generate for future"),
575+
TypeDefKind::Stream(_) => todo!("generate for stream"),
576+
}
577+
}
578+
}
579+
}

0 commit comments

Comments
 (0)