Skip to content

Commit c463cc9

Browse files
committed
Clean up the super long finalize function
1 parent f3935cc commit c463cc9

File tree

2 files changed

+192
-133
lines changed

2 files changed

+192
-133
lines changed

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

Lines changed: 187 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,170 @@ impl<'a> Context<'a> {
222222
}
223223

224224
pub fn finalize(&mut self, module_name: &str) -> Result<(String, String), Error> {
225+
// Wire up all default intrinsics, those which don't have any sort of
226+
// dependency on the clsoure/anyref/etc passes. This is where almost all
227+
// intrinsics are wired up.
228+
self.wire_up_initial_intrinsics()?;
229+
230+
// Next up, perform our closure rewriting pass. This is where we'll
231+
// update invocations of the closure intrinsics we have to instead call
232+
// appropriate JS functions which actually create closures.
233+
closures::rewrite(self).with_context(|_| "failed to generate internal closure shims")?;
234+
235+
// Finalize all bindings for JS classes. This is where we'll generate JS
236+
// glue for all classes as well as finish up a few final imports like
237+
// `__wrap` and such.
238+
self.write_classes()?;
239+
240+
// And now that we're almost ready, run the final "anyref" pass. This is
241+
// where we transform a wasm module which doesn't actually use `anyref`
242+
// anywhere to using the type internally. The transformation here is
243+
// based off all the previous data we've collected so far for each
244+
// import/export listed.
245+
self.anyref.run(self.module)?;
246+
247+
// With our transforms finished, we can now wire up the final list of
248+
// intrinsics which may depend on the passes run above.
249+
self.wire_up_late_intrinsics()?;
250+
251+
// We're almost done here, so we can delete any internal exports (like
252+
// `__wbindgen_malloc`) if none of our JS glue actually needed it.
253+
self.unexport_unused_internal_exports();
254+
255+
// Handle the `start` function, if one was specified. If we're in a
256+
// --test mode (such as wasm-bindgen-test-runner) then we skip this
257+
// entirely. Otherwise we want to first add a start function to the
258+
// `start` section if one is specified.
259+
//
260+
// Note that once a start function is added, if any, we immediately
261+
// un-start it. This is done because we require that the JS glue
262+
// initializes first, so we execute wasm startup manually once the JS
263+
// glue is all in place.
264+
let mut needs_manual_start = false;
265+
if self.config.emit_start {
266+
self.add_start_function()?;
267+
needs_manual_start = self.unstart_start_function();
268+
}
269+
270+
// If our JS glue needs to access the function table, then do so here.
271+
// JS closure shim generation may access the function table as an
272+
// example, but if there's no closures in the module there's no need to
273+
// export it!
274+
self.export_table()?;
275+
276+
// After all we've done, especially
277+
// `unexport_unused_internal_exports()`, we probably have a bunch of
278+
// garbage in the module that's no longer necessary, so delete
279+
// everything that we don't actually need.
280+
walrus::passes::gc::run(self.module);
281+
282+
// Almost there, but before we're done make sure to rewrite the `module`
283+
// field of all imports in the wasm module. The field is currently
284+
// always `__wbindgen_placeholder__` coming out of rustc, but we need to
285+
// update that here to the shim file or an actual ES module.
286+
self.rewrite_imports(module_name);
287+
288+
// We likely made a ton of modifications, so add ourselves to the
289+
// producers section!
290+
self.update_producers_section();
291+
292+
// Cause any future calls to `should_write_global` to panic, making sure
293+
// we don't ask for items which we can no longer emit.
294+
drop(self.exposed_globals.take().unwrap());
295+
296+
Ok(self.finalize_js(module_name, needs_manual_start))
297+
}
298+
299+
/// Performs the task of actually generating the final JS module, be it
300+
/// `--no-modules`, `--browser`, or for bundlers. This is the very last step
301+
/// performed in `finalize`.
302+
fn finalize_js(&mut self, module_name: &str, needs_manual_start: bool) -> (String, String) {
303+
let mut js = String::new();
304+
if self.config.mode.no_modules() {
305+
js.push_str("(function() {\n");
306+
}
307+
308+
// Depending on the output mode, generate necessary glue to actually
309+
// import the wasm file in one way or another.
310+
let mut init = String::new();
311+
match &self.config.mode {
312+
// In `--no-modules` mode we need to both expose a name on the
313+
// global object as well as generate our own custom start function.
314+
OutputMode::NoModules { global } => {
315+
js.push_str("const __exports = {};\n");
316+
js.push_str("let wasm;\n");
317+
init = self.gen_init(&module_name, needs_manual_start);
318+
self.footer.push_str(&format!(
319+
"self.{} = Object.assign(init, __exports);\n",
320+
global
321+
));
322+
}
323+
324+
// With normal CommonJS node we need to defer requiring the wasm
325+
// until the end so most of our own exports are hooked up
326+
OutputMode::Node {
327+
experimental_modules: false,
328+
} => {
329+
self.footer
330+
.push_str(&format!("wasm = require('./{}_bg');\n", module_name));
331+
if needs_manual_start {
332+
self.footer.push_str("wasm.__wbindgen_start();\n");
333+
}
334+
js.push_str("var wasm;\n");
335+
}
336+
337+
// With Bundlers and modern ES6 support in Node we can simply import
338+
// the wasm file as if it were an ES module and let the
339+
// bundler/runtime take care of it.
340+
OutputMode::Bundler
341+
| OutputMode::Node {
342+
experimental_modules: true,
343+
} => {
344+
js.push_str(&format!("import * as wasm from './{}_bg';\n", module_name));
345+
if needs_manual_start {
346+
self.footer.push_str("wasm.__wbindgen_start();\n");
347+
}
348+
}
349+
350+
// With a browser-native output we're generating an ES module, but
351+
// browsers don't support natively importing wasm right now so we
352+
// expose the same initialization function as `--no-modules` as the
353+
// default export of the module.
354+
OutputMode::Browser => {
355+
js.push_str("const __exports = {};\n");
356+
self.imports_post.push_str("let wasm;\n");
357+
init = self.gen_init(&module_name, needs_manual_start);
358+
self.footer.push_str("export default init;\n");
359+
}
360+
}
361+
362+
// Emit all the JS for importing all our functionality
363+
js.push_str(&self.imports);
364+
js.push_str("\n");
365+
js.push_str(&self.imports_post);
366+
js.push_str("\n");
367+
368+
// Emit all our exports from this module
369+
js.push_str(&self.globals);
370+
js.push_str("\n");
371+
372+
// Generate the initialization glue, if there was any
373+
js.push_str(&init);
374+
js.push_str("\n");
375+
js.push_str(&self.footer);
376+
js.push_str("\n");
377+
if self.config.mode.no_modules() {
378+
js.push_str("})();\n");
379+
}
380+
381+
while js.contains("\n\n\n") {
382+
js = js.replace("\n\n\n", "\n\n");
383+
}
384+
385+
(js, self.typescript.clone())
386+
}
387+
388+
fn wire_up_initial_intrinsics(&mut self) -> Result<(), Error> {
225389
self.bind("__wbindgen_string_new", &|me| {
226390
me.anyref.import_xform(
227391
"__wbindgen_placeholder__",
@@ -604,10 +768,28 @@ impl<'a> Context<'a> {
604768
))
605769
})?;
606770

607-
closures::rewrite(self).with_context(|_| "failed to generate internal closure shims")?;
608-
self.write_classes()?;
609-
self.anyref.run(self.module)?;
771+
self.bind("__wbindgen_throw", &|me| {
772+
me.expose_get_string_from_wasm();
773+
Ok(String::from(
774+
"
775+
function(ptr, len) {
776+
throw new Error(getStringFromWasm(ptr, len));
777+
}
778+
",
779+
))
780+
})?;
610781

782+
Ok(())
783+
}
784+
785+
/// Provide implementations of remaining intrinsics after initial passes
786+
/// have been run on the wasm module.
787+
///
788+
/// The intrinsics implemented here are added very late in the process or
789+
/// otherwise may be overwritten by passes (such as the anyref pass). As a
790+
/// result they don't go into the initial list of intrinsics but go just at
791+
/// the end.
792+
fn wire_up_late_intrinsics(&mut self) -> Result<(), Error> {
611793
// After the anyref pass has executed, if this intrinsic is needed then
612794
// we expose a function which initializes it
613795
self.bind("__wbindgen_init_anyref_table", &|me| {
@@ -645,132 +827,7 @@ impl<'a> Context<'a> {
645827
Ok(String::from("function(i) { dropObject(i); }"))
646828
})?;
647829

648-
self.unexport_unused_internal_exports();
649-
650-
// Handle the `start` function, if one was specified. If we're in a
651-
// --test mode (such as wasm-bindgen-test-runner) then we skip this
652-
// entirely. Otherwise we want to first add a start function to the
653-
// `start` section if one is specified.
654-
//
655-
// Note that once a start function is added, if any, we immediately
656-
// un-start it. This is done because we require that the JS glue
657-
// initializes first, so we execute wasm startup manually once the JS
658-
// glue is all in place.
659-
let mut needs_manual_start = false;
660-
if self.config.emit_start {
661-
self.add_start_function()?;
662-
needs_manual_start = self.unstart_start_function();
663-
}
664-
665-
self.export_table()?;
666-
667-
walrus::passes::gc::run(self.module);
668-
669-
// Note that it's important `throw` comes last *after* we gc. The
670-
// `__wbindgen_malloc` function may call this but we only want to
671-
// generate code for this if it's actually live (and __wbindgen_malloc
672-
// isn't gc'd).
673-
self.bind("__wbindgen_throw", &|me| {
674-
me.expose_get_string_from_wasm();
675-
Ok(String::from(
676-
"
677-
function(ptr, len) {
678-
throw new Error(getStringFromWasm(ptr, len));
679-
}
680-
",
681-
))
682-
})?;
683-
684-
self.rewrite_imports(module_name);
685-
self.update_producers_section();
686-
687-
// Cause any future calls to `should_write_global` to panic, making sure
688-
// we don't ask for items which we can no longer emit.
689-
drop(self.exposed_globals.take().unwrap());
690-
691-
let mut js = String::new();
692-
if self.config.mode.no_modules() {
693-
js.push_str("(function() {\n");
694-
}
695-
696-
// Depending on the output mode, generate necessary glue to actually
697-
// import the wasm file in one way or another.
698-
let mut init = String::new();
699-
match &self.config.mode {
700-
// In `--no-modules` mode we need to both expose a name on the
701-
// global object as well as generate our own custom start function.
702-
OutputMode::NoModules { global } => {
703-
js.push_str("const __exports = {};\n");
704-
js.push_str("let wasm;\n");
705-
init = self.gen_init(&module_name, needs_manual_start);
706-
self.footer.push_str(&format!(
707-
"self.{} = Object.assign(init, __exports);\n",
708-
global
709-
));
710-
}
711-
712-
// With normal CommonJS node we need to defer requiring the wasm
713-
// until the end so most of our own exports are hooked up
714-
OutputMode::Node {
715-
experimental_modules: false,
716-
} => {
717-
self.footer
718-
.push_str(&format!("wasm = require('./{}_bg');\n", module_name));
719-
if needs_manual_start {
720-
self.footer.push_str("wasm.__wbindgen_start();\n");
721-
}
722-
js.push_str("var wasm;\n");
723-
}
724-
725-
// With Bundlers and modern ES6 support in Node we can simply import
726-
// the wasm file as if it were an ES module and let the
727-
// bundler/runtime take care of it.
728-
OutputMode::Bundler
729-
| OutputMode::Node {
730-
experimental_modules: true,
731-
} => {
732-
js.push_str(&format!("import * as wasm from './{}_bg';\n", module_name));
733-
if needs_manual_start {
734-
self.footer.push_str("wasm.__wbindgen_start();\n");
735-
}
736-
}
737-
738-
// With a browser-native output we're generating an ES module, but
739-
// browsers don't support natively importing wasm right now so we
740-
// expose the same initialization function as `--no-modules` as the
741-
// default export of the module.
742-
OutputMode::Browser => {
743-
js.push_str("const __exports = {};\n");
744-
self.imports_post.push_str("let wasm;\n");
745-
init = self.gen_init(&module_name, needs_manual_start);
746-
self.footer.push_str("export default init;\n");
747-
}
748-
}
749-
750-
// Emit all the JS for importing all our functionality
751-
js.push_str(&self.imports);
752-
js.push_str("\n");
753-
js.push_str(&self.imports_post);
754-
js.push_str("\n");
755-
756-
// Emit all our exports from this module
757-
js.push_str(&self.globals);
758-
js.push_str("\n");
759-
760-
// Generate the initialization glue, if there was any
761-
js.push_str(&init);
762-
js.push_str("\n");
763-
js.push_str(&self.footer);
764-
js.push_str("\n");
765-
if self.config.mode.no_modules() {
766-
js.push_str("})();\n");
767-
}
768-
769-
while js.contains("\n\n\n") {
770-
js = js.replace("\n\n\n", "\n\n");
771-
}
772-
773-
Ok((js, self.typescript.clone()))
830+
Ok(())
774831
}
775832

776833
fn gen_init(&mut self, module_name: &str, needs_manual_start: bool) -> String {
@@ -1250,8 +1307,7 @@ impl<'a> Context<'a> {
12501307
passStringToWasm = function(arg) {{ {} }};
12511308
}}
12521309
",
1253-
use_encode_into,
1254-
use_encode,
1310+
use_encode_into, use_encode,
12551311
));
12561312
}
12571313
_ => {

src/anyref.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,11 @@ impl Slab {
110110
}
111111

112112
fn internal_error(msg: &str) -> ! {
113-
let msg = if cfg!(debug_assertions) { msg } else { "" };
114-
super::throw_str(msg)
113+
if cfg!(debug_assertions) {
114+
super::throw_str(msg)
115+
} else {
116+
std::process::abort()
117+
}
115118
}
116119

117120
// Whoa, there's two `tl` modules here! That's currently intention, but for sort

0 commit comments

Comments
 (0)