wasm_init/
wasm.rs

1use js_sys::{Array, Function, JsString, Object};
2use std::sync::atomic::{AtomicBool, Ordering};
3use wasm_bindgen::prelude::*;
4
5#[doc(hidden)]
6pub mod __macrodeps {
7	pub use gensym::gensym;
8	pub use std::{concat, stringify};
9}
10
11static INIT_DONE: AtomicBool = AtomicBool::new(false);
12
13#[cfg(feature = "auto-init")]
14#[wasm_bindgen(start)]
15fn main() {
16	wasm_init();
17}
18
19/// This function will call all instances of [`crate::wasm_init!`].
20///
21/// This function can be called either:
22/// - from Rust as part of an entrypoint
23/// - from JavaScript/TypeScript by calling the exported `wasm_init` function in your module
24/// - automatically, by enabling the "auto-init" feature
25///
26/// This function is idempotent, or safe to call multiple times;
27/// your [`crate::wasm_init!`] calls will only be executed once.
28#[wasm_bindgen]
29pub fn wasm_init() {
30	if INIT_DONE.swap(true, Ordering::Relaxed) {
31		return;
32	};
33
34	let exports: Object = wasm_bindgen::exports().into();
35	let entries = Object::entries(&exports);
36	for entry in entries {
37		let entry = Array::from(&entry);
38		let name: JsString = entry.get(0).into();
39		let func: Function = entry.get(1).into();
40
41		if name.starts_with("__wasm_init", 0) {
42			func.apply(&JsValue::undefined(), &Array::new())
43				.expect("func invocation failed");
44		}
45	}
46}
47
48#[doc(hidden)]
49#[macro_export]
50macro_rules! __wasm_init_impl {
51	($gensym:ident, $($input:tt)*) => {
52		const _: () = {
53			#[export_name = $crate::__macrodeps::concat!(
54				"__wasm_init",
55				$crate::__macrodeps::stringify!($gensym)
56			)]
57			pub extern "C" fn init() {
58				$($input)*
59			}
60		};
61	};
62}
63
64/// Register code to be run on start-up.
65///
66/// Each call to this macro will generate a function that will be called exactly once,
67/// after the [wasm_init] function is called for the first time.
68///
69/// You can register code from as many different crates in your project as you'd like;
70/// [wasm_init] only needs to be called once.
71///
72/// # Examples
73/// ```
74/// trait Plugin {}
75///
76/// fn register_plugin(plugin: impl Plugin) {
77/// 	// grab data from each plugin and store them in a global somewhere
78/// }
79///
80/// struct Plugin1;
81///
82/// wasm_init::wasm_init! {
83/// 	register_plugin(Plugin1);
84/// }
85///
86/// struct Plugin2;
87///
88/// wasm_init::wasm_init! {
89/// 	register_plugin(Plugin2);
90/// }
91/// ```
92#[macro_export]
93macro_rules! wasm_init {
94	($($input:tt)*) => {
95		$crate::__macrodeps::gensym! { $crate::__wasm_init_impl! { $($input)* } }
96	};
97}