Skip to content

Commit 79cf4f6

Browse files
boringcactusalexcrichton
authored andcommitted
Add first-class support for binary crates (#1843)
* autodiscover an exported `main` if possible this allows for first-class support of binary crates * wrap `main` to zero out arguments and suppress return value * add test for bin crate support * process only the export of the generated main wrapper * skip most of `export` since only one line of that is needed
1 parent b29c110 commit 79cf4f6

File tree

2 files changed

+88
-0
lines changed

2 files changed

+88
-0
lines changed

crates/cli-support/src/webidl/mod.rs

+42
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,10 @@ pub fn process(
512512
cx.program(program)?;
513513
}
514514

515+
if !cx.start_found {
516+
cx.discover_main()?;
517+
}
518+
515519
if let Some(standard) = cx.module.customs.delete_typed::<ast::WebidlBindings>() {
516520
cx.standard(&standard)?;
517521
}
@@ -623,6 +627,44 @@ impl<'a> Context<'a> {
623627
Ok(())
624628
}
625629

630+
// Discover a function `main(i32, i32) -> i32` and, if it exists, make that function run at module start.
631+
fn discover_main(&mut self) -> Result<(), Error> {
632+
// find a `main(i32, i32) -> i32`
633+
let main_id = self
634+
.module
635+
.functions()
636+
.find(|x| {
637+
use walrus::ValType::I32;
638+
// name has to be `main`
639+
let name_matches = x.name.as_ref().map_or(false, |x| x == "main");
640+
// type has to be `(i32, i32) -> i32`
641+
let ty = self.module.types.get(x.ty());
642+
let type_matches = ty.params() == [I32, I32] && ty.results() == [I32];
643+
name_matches && type_matches
644+
})
645+
.map(|x| x.id());
646+
let main_id = match main_id {
647+
Some(x) => x,
648+
None => return Ok(()),
649+
};
650+
651+
// build a wrapper to zero out the arguments and ignore the return value
652+
let mut wrapper = walrus::FunctionBuilder::new(&mut self.module.types, &[], &[]);
653+
wrapper
654+
.func_body()
655+
.i32_const(0)
656+
.i32_const(0)
657+
.call(main_id)
658+
.drop()
659+
.return_();
660+
let wrapper = wrapper.finish(vec![], &mut self.module.funcs);
661+
662+
// call that wrapper when the module starts
663+
self.add_start_function(wrapper)?;
664+
665+
Ok(())
666+
}
667+
626668
// Ensure that the `start` function for this module calls the
627669
// `__wbindgen_init_anyref_table` function. This'll ensure that all
628670
// instances of this module have the initial slots of the anyref table

crates/cli/tests/wasm-bindgen/main.rs

+46
Original file line numberDiff line numberDiff line change
@@ -151,3 +151,49 @@ fn one_export_works() {
151151
.wasm_bindgen("");
152152
cmd.assert().success();
153153
}
154+
155+
#[test]
156+
fn bin_crate_works() {
157+
let (mut cmd, out_dir) = Project::new("bin_crate_works")
158+
.file(
159+
"src/main.rs",
160+
r#"
161+
use wasm_bindgen::prelude::*;
162+
#[wasm_bindgen]
163+
extern "C" {
164+
#[wasm_bindgen(js_namespace = console)]
165+
fn log(data: &str);
166+
}
167+
168+
fn main() {
169+
log("hello, world");
170+
}
171+
"#,
172+
)
173+
.file(
174+
"Cargo.toml",
175+
&format!(
176+
"
177+
[package]
178+
name = \"bin_crate_works\"
179+
authors = []
180+
version = \"1.0.0\"
181+
edition = '2018'
182+
183+
[dependencies]
184+
wasm-bindgen = {{ path = '{}' }}
185+
186+
[workspace]
187+
",
188+
repo_root().display(),
189+
),
190+
)
191+
.wasm_bindgen("--target nodejs");
192+
cmd.assert().success();
193+
Command::new("node")
194+
.arg("bin_crate_works.js")
195+
.current_dir(out_dir)
196+
.assert()
197+
.success()
198+
.stdout("hello, world\n");
199+
}

0 commit comments

Comments
 (0)