diff --git a/crates/cli-support/src/webidl/mod.rs b/crates/cli-support/src/webidl/mod.rs index ba7132ec60f..8e24641312c 100644 --- a/crates/cli-support/src/webidl/mod.rs +++ b/crates/cli-support/src/webidl/mod.rs @@ -512,6 +512,10 @@ pub fn process( cx.program(program)?; } + if !cx.start_found { + cx.discover_main()?; + } + if let Some(standard) = cx.module.customs.delete_typed::() { cx.standard(&standard)?; } @@ -623,6 +627,44 @@ impl<'a> Context<'a> { Ok(()) } + // Discover a function `main(i32, i32) -> i32` and, if it exists, make that function run at module start. + fn discover_main(&mut self) -> Result<(), Error> { + // find a `main(i32, i32) -> i32` + let main_id = self + .module + .functions() + .find(|x| { + use walrus::ValType::I32; + // name has to be `main` + let name_matches = x.name.as_ref().map_or(false, |x| x == "main"); + // type has to be `(i32, i32) -> i32` + let ty = self.module.types.get(x.ty()); + let type_matches = ty.params() == [I32, I32] && ty.results() == [I32]; + name_matches && type_matches + }) + .map(|x| x.id()); + let main_id = match main_id { + Some(x) => x, + None => return Ok(()), + }; + + // build a wrapper to zero out the arguments and ignore the return value + let mut wrapper = walrus::FunctionBuilder::new(&mut self.module.types, &[], &[]); + wrapper + .func_body() + .i32_const(0) + .i32_const(0) + .call(main_id) + .drop() + .return_(); + let wrapper = wrapper.finish(vec![], &mut self.module.funcs); + + // call that wrapper when the module starts + self.add_start_function(wrapper)?; + + Ok(()) + } + // Ensure that the `start` function for this module calls the // `__wbindgen_init_anyref_table` function. This'll ensure that all // instances of this module have the initial slots of the anyref table diff --git a/crates/cli/tests/wasm-bindgen/main.rs b/crates/cli/tests/wasm-bindgen/main.rs index acec2cce813..312e5947d96 100644 --- a/crates/cli/tests/wasm-bindgen/main.rs +++ b/crates/cli/tests/wasm-bindgen/main.rs @@ -151,3 +151,49 @@ fn one_export_works() { .wasm_bindgen(""); cmd.assert().success(); } + +#[test] +fn bin_crate_works() { + let (mut cmd, out_dir) = Project::new("bin_crate_works") + .file( + "src/main.rs", + r#" + use wasm_bindgen::prelude::*; + #[wasm_bindgen] + extern "C" { + #[wasm_bindgen(js_namespace = console)] + fn log(data: &str); + } + + fn main() { + log("hello, world"); + } + "#, + ) + .file( + "Cargo.toml", + &format!( + " + [package] + name = \"bin_crate_works\" + authors = [] + version = \"1.0.0\" + edition = '2018' + + [dependencies] + wasm-bindgen = {{ path = '{}' }} + + [workspace] + ", + repo_root().display(), + ), + ) + .wasm_bindgen("--target nodejs"); + cmd.assert().success(); + Command::new("node") + .arg("bin_crate_works.js") + .current_dir(out_dir) + .assert() + .success() + .stdout("hello, world\n"); +}