Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to instantiate WASM module without calling the start function #3062

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
65 changes: 58 additions & 7 deletions crates/cli-support/src/js/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ pub struct Context<'a> {

/// A flag to track if the stack pointer setter shim has been injected.
stack_pointer_shim_injected: bool,

start_found: bool,
}

#[derive(Default)]
Expand Down Expand Up @@ -93,6 +95,7 @@ impl<'a> Context<'a> {
config: &'a Bindgen,
wit: &'a NonstandardWitSection,
aux: &'a WasmBindgenAux,
start_found: bool,
) -> Result<Context<'a>, Error> {
Ok(Context {
globals: String::new(),
Expand All @@ -113,6 +116,7 @@ impl<'a> Context<'a> {
memories: Default::default(),
table_indices: Default::default(),
stack_pointer_shim_injected: false,
start_found,
})
}

Expand Down Expand Up @@ -381,7 +385,7 @@ impl<'a> Context<'a> {
js.push_str("let wasm;\n");
init = self.gen_init(needs_manual_start, None)?;
footer.push_str(&format!(
"{} = Object.assign(init, {{ initSync }}, __exports);\n",
"{} = Object.assign(init, {{ initWithoutStart }}, {{ initSync }}, __exports);\n",
global
));
}
Expand Down Expand Up @@ -414,6 +418,10 @@ impl<'a> Context<'a> {
if needs_manual_start {
footer.push_str("\nwasm.__wbindgen_start();\n");
}

if self.start_found {
footer.push_str("\nwasm.__wbindgen_main();\n");
}
}

OutputMode::Deno => {
Expand All @@ -428,6 +436,10 @@ impl<'a> Context<'a> {
if needs_manual_start {
footer.push_str("\nwasm.__wbindgen_start();\n");
}

if self.start_found {
footer.push_str("\nwasm.__wbindgen_main();\n");
}
}

// With Bundlers and modern ES6 support in Node we can simply import
Expand Down Expand Up @@ -462,6 +474,12 @@ impl<'a> Context<'a> {
if needs_manual_start {
start = Some("\nwasm.__wbindgen_start();\n".to_string());
}

if self.start_found {
start
.get_or_insert_with(String::default)
.push_str("\nwasm.__wbindgen_main();\n");
}
}

// With a browser-native output we're generating an ES module, but
Expand All @@ -471,7 +489,7 @@ impl<'a> Context<'a> {
OutputMode::Web => {
self.imports_post.push_str("let wasm;\n");
init = self.gen_init(needs_manual_start, Some(&mut imports))?;
footer.push_str("export { initSync }\n");
footer.push_str("export { initSync, initWithoutStart }\n");
footer.push_str("export default init;");
}
}
Expand Down Expand Up @@ -610,6 +628,7 @@ impl<'a> Context<'a> {
// Also in (at least) the NoModules, the `init()` method is renamed to `wasm_bindgen()`.
let setup_function_declaration;
let mut sync_init_function = String::new();
let mut without_start_init_function = String::new();
let declare_or_export;
if self.config.mode.no_modules() {
declare_or_export = "declare";
Expand All @@ -635,6 +654,23 @@ impl<'a> Context<'a> {
memory_param = memory_param
));

without_start_init_function.push_str(&format!(
"\
/**\n\
* If `module_or_path` is {{RequestInfo}} or {{URL}}, makes a request and\n\
* for everything else, calls `WebAssembly.instantiate` directly.\n\
*\n\
* @param {{InitInput | Promise<InitInput>}} module_or_path\n\
{}\
*\n\
* @returns {{Promise<InitOutput>}}\n\
*/\n\
export function initWithoutStart \
(module_or_path{}: InitInput | Promise<InitInput>{}): Promise<InitOutput>;\n\n\
",
memory_doc, arg_optional, memory_param,
));

setup_function_declaration = "export default function init";
}
Ok(format!(
Expand All @@ -647,7 +683,8 @@ impl<'a> Context<'a> {
{sync_init_function}\
/**\n\
* If `module_or_path` is {{RequestInfo}} or {{URL}}, makes a request and\n\
* for everything else, calls `WebAssembly.instantiate` directly.\n\
* for everything else, calls `WebAssembly.instantiate` directly and runs\n\
* the start function.\n\
*\n\
* @param {{InitInput | Promise<InitInput>}} module_or_path\n\
{}\
Expand Down Expand Up @@ -834,11 +871,12 @@ impl<'a> Context<'a> {
{init_memory}
}}

function finalizeInit(instance, module) {{
function finalizeInit(instance, module, start) {{
wasm = instance.exports;
init.__wbindgen_wasm_module = module;
{init_memviews}
{start}
{main}
return wasm;
}}

Expand All @@ -853,10 +891,10 @@ impl<'a> Context<'a> {

const instance = new WebAssembly.Instance(module, imports);

return finalizeInit(instance, module);
return finalizeInit(instance, module, true);
}}

async function init(input{init_memory_arg}) {{
async function initInternal(input{init_memory_arg}, start) {{
{default_module_path}
const imports = getImports();

Expand All @@ -868,7 +906,15 @@ impl<'a> Context<'a> {

const {{ instance, module }} = await load(await input, imports);

return finalizeInit(instance, module);
return finalizeInit(instance, module, start);
}}

async function initWithoutStart(input{init_memory_arg}) {{
return initInternal(input{init_memory_arg}, false);
}}

async function init(input{init_memory_arg}) {{
return initInternal(input{init_memory_arg}, true);
}}
",
init_memory_arg = init_memory_arg,
Expand All @@ -880,6 +926,11 @@ impl<'a> Context<'a> {
} else {
""
},
main = if self.start_found {
"if (start == true) { wasm.__wbindgen_main(); }"
} else {
""
},
imports_init = imports_init,
);

Expand Down
4 changes: 2 additions & 2 deletions crates/cli-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -359,7 +359,7 @@ impl Bindgen {
// auxiliary section for all sorts of miscellaneous information and
// features #[wasm_bindgen] supports that aren't covered by wasm
// interface types.
wit::process(
let wit::ProcessResult { start_found, .. } = wit::process(
&mut module,
self.externref,
self.wasm_interface_types,
Expand Down Expand Up @@ -441,7 +441,7 @@ impl Bindgen {
.customs
.delete_typed::<wit::NonstandardWitSection>()
.unwrap();
let mut cx = js::Context::new(&mut module, self, &adapters, &aux)?;
let mut cx = js::Context::new(&mut module, self, &adapters, &aux, start_found)?;
cx.generate()?;
let (js, ts, start) = cx.finalize(stem)?;
Generated::Js(JsGenerated {
Expand Down
33 changes: 14 additions & 19 deletions crates/cli-support/src/wit/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,18 @@ struct InstructionBuilder<'a, 'b> {
return_position: bool,
}

pub struct ProcessResult {
pub adapters: NonstandardWitSectionId,
pub aux: WasmBindgenAuxId,
pub start_found: bool,
}

pub fn process(
module: &mut Module,
externref_enabled: bool,
wasm_interface_types: bool,
support_start: bool,
) -> Result<(NonstandardWitSectionId, WasmBindgenAuxId), Error> {
) -> Result<ProcessResult, Error> {
let mut storage = Vec::new();
let programs = extract_programs(module, &mut storage)?;

Expand Down Expand Up @@ -88,7 +94,11 @@ pub fn process(

let adapters = cx.module.customs.add(cx.adapters);
let aux = cx.module.customs.add(cx.aux);
Ok((adapters, aux))
Ok(ProcessResult {
adapters,
aux,
start_found: cx.start_found && cx.support_start,
})
}

impl<'a> Context<'a> {
Expand Down Expand Up @@ -476,22 +486,7 @@ impl<'a> Context<'a> {
return Ok(());
}

let prev_start = match self.module.start {
Some(f) => f,
None => {
self.module.start = Some(id);
return Ok(());
}
};

// Note that we call the previous start function, if any, first. This is
// because the start function currently only shows up when it's injected
// through thread/externref transforms. These injected start functions
// need to happen before user code, so we always schedule them first.
let mut builder = walrus::FunctionBuilder::new(&mut self.module.types, &[], &[]);
builder.func_body().call(prev_start).call(id);
let new_start = builder.finish(Vec::new(), &mut self.module.funcs);
self.module.start = Some(new_start);
self.module.exports.add("__wbindgen_main", id);
daxpedda marked this conversation as resolved.
Show resolved Hide resolved
Ok(())
}

Expand Down Expand Up @@ -1464,7 +1459,7 @@ impl<'a> Context<'a> {
let mut to_remove = Vec::new();
for export in self.module.exports.iter() {
match export.name.as_str() {
n if n.starts_with("__wbindgen") => {
n if n.starts_with("__wbindgen") && n != "__wbindgen_main" => {
to_remove.push(export.id());
}
_ => {}
Expand Down
8 changes: 4 additions & 4 deletions crates/cli/tests/wasm-bindgen/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ fn default_module_path_target_web() {
let contents = fs::read_to_string(out_dir.join("default_module_path_target_web.js")).unwrap();
assert!(contents.contains(
"\
async function init(input) {
async function initInternal(input, start) {
if (typeof input === 'undefined') {
input = new URL('default_module_path_target_web_bg.wasm', import.meta.url);
}",
Expand All @@ -260,7 +260,7 @@ fn default_module_path_target_no_modules() {
fs::read_to_string(out_dir.join("default_module_path_target_no_modules.js")).unwrap();
assert!(contents.contains(
"\
async function init(input) {
async function initInternal(input, start) {
if (typeof input === 'undefined') {
let src;
if (typeof document === 'undefined') {
Expand All @@ -287,7 +287,7 @@ fn omit_default_module_path_target_web() {
fs::read_to_string(out_dir.join("omit_default_module_path_target_web.js")).unwrap();
assert!(contents.contains(
"\
async function init(input) {
async function initInternal(input, start) {

const imports = getImports();",
));
Expand All @@ -307,7 +307,7 @@ fn omit_default_module_path_target_no_modules() {
fs::read_to_string(out_dir.join("omit_default_module_path_target_no_modules.js")).unwrap();
assert!(contents.contains(
"\
async function init(input) {
async function initInternal(input, start) {

const imports = getImports();",
));
Expand Down