Skip to main content

wasm_bindgen_shared/
lib.rs

1#![doc(html_root_url = "https://docs.rs/wasm-bindgen-shared/0.2")]
2#![no_std]
3
4extern crate alloc;
5
6use alloc::string::{String, ToString};
7
8pub mod identifier;
9#[cfg(test)]
10mod schema_hash_approval;
11pub mod tys;
12
13// This gets changed whenever our schema changes.
14// At this time versions of wasm-bindgen and wasm-bindgen-cli are required to have the exact same
15// SCHEMA_VERSION in order to work together.
16pub const SCHEMA_VERSION: &str = "0.2.130";
17
18#[macro_export]
19macro_rules! shared_api {
20    ($mac:ident) => {
21        $mac! {
22        struct Program<'a> {
23            exports: Vec<Export<'a>>,
24            enums: Vec<Enum<'a>>,
25            imports: Vec<Import<'a>>,
26            structs: Vec<Struct<'a>>,
27            // NOTE: Originally typescript_custom_sections are just some strings
28            // But the expression type can only be parsed into a string during compilation
29            // So when encoding, LitOrExpr contains two types, one is that expressions are parsed into strings during compilation, and the other is can be parsed directly.
30            // When decoding, LitOrExpr can be decoded as a string.
31            typescript_custom_sections: Vec<LitOrExpr<'a>>,
32            local_modules: Vec<LocalModule<'a>>,
33            inline_js: Vec<&'a str>,
34            unique_crate_identifier: &'a str,
35            // `module_path!()` of the macro invocation, so same-named items
36            // in different modules of one crate stay distinct.
37            module_path: LitOrExpr<'a>,
38            package_json: Option<&'a str>,
39            linked_modules: Vec<LinkedModule<'a>>,
40        }
41
42        struct Import<'a> {
43            module: Option<ImportModule<'a>>,
44            js_namespace: Option<Vec<String>>,
45            reexport: Option<String>,
46            generate_typescript: bool,
47            kind: ImportKind<'a>,
48        }
49
50        struct LinkedModule<'a> {
51            module: ImportModule<'a>,
52            link_function_name: &'a str,
53        }
54
55        enum ImportModule<'a> {
56            Named(&'a str),
57            RawNamed(&'a str),
58            Inline(u32),
59        }
60
61        enum ImportKind<'a> {
62            Function(ImportFunction<'a>),
63            Static(ImportStatic<'a>),
64            String(ImportString<'a>),
65            Type(ImportType<'a>),
66            Enum(StringEnum<'a>),
67            DynamicUnion(DynamicUnion<'a>),
68        }
69
70        struct ImportFunction<'a> {
71            shim: &'a str,
72            catch: bool,
73            variadic: bool,
74            assert_no_shim: bool,
75            suspending: bool,
76            method: Option<MethodData<'a>>,
77            structural: bool,
78            function: Function<'a>,
79            // Whether this import uses the per-monomorphisation generic path
80            // (`#[wasm_bindgen(experimental_generic_mono)]`) and is therefore bound per
81            // concrete instantiation via the `__wbindgen_describe_generic_import`
82            // marker rather than a single named descriptor shim. (Type-erasure
83            // generic imports do not set this; they bind like normal imports.)
84            generic_per_mono: bool,
85        }
86
87        struct MethodData<'a> {
88            class: &'a str,
89            kind: MethodKind<'a>,
90        }
91
92        enum MethodKind<'a> {
93            Constructor,
94            Operation(Operation<'a>),
95        }
96
97        struct Operation<'a> {
98            is_static: bool,
99            kind: OperationKind<'a>,
100        }
101
102        enum OperationKind<'a> {
103            Regular,
104            RegularThis,
105            Getter(&'a str),
106            Setter(&'a str),
107            IndexingGetter,
108            IndexingSetter,
109            IndexingDeleter,
110        }
111
112        struct ImportStatic<'a> {
113            name: &'a str,
114            shim: &'a str,
115        }
116
117        struct ImportString<'a> {
118            shim: &'a str,
119            string: &'a str,
120        }
121
122        struct ImportType<'a> {
123            name: &'a str,
124            instanceof_shim: &'a str,
125            vendor_prefixes: Vec<&'a str>,
126        }
127
128        struct StringEnum<'a> {
129            name: &'a str,
130            variant_values: Vec<&'a str>,
131            comments: Vec<&'a str>,
132            generate_typescript: bool,
133            private: bool,
134            js_namespace: Option<Vec<&'a str>>,
135        }
136
137        enum StartKind {
138            None,
139            Public,
140            Private,
141        }
142
143        struct DynamicUnion<'a> {
144            name: &'a str,
145            variant_strings: Vec<&'a str>,
146            variant_type_cnt: u32,
147            comments: Vec<&'a str>,
148            generate_typescript: bool,
149            private: bool,
150            fallback: bool,
151        }
152
153
154        struct Export<'a> {
155            class: Option<&'a str>,
156            comments: Vec<&'a str>,
157            consumed: bool,
158            function: Function<'a>,
159            js_namespace: Option<Vec<&'a str>>,
160            method_kind: MethodKind<'a>,
161            start: StartKind,
162        }
163
164        struct Enum<'a> {
165            name: &'a str,
166            signed: bool,
167            variants: Vec<EnumVariant<'a>>,
168            comments: Vec<&'a str>,
169            generate_typescript: bool,
170            js_namespace: Option<Vec<&'a str>>,
171            private: bool,
172        }
173
174        struct EnumVariant<'a> {
175            name: &'a str,
176            value: u32,
177            comments: Vec<&'a str>,
178        }
179
180        struct Function<'a> {
181            args: Vec<FunctionArgumentData<'a>>,
182            asyncness: bool,
183            jspi: bool,
184            name: &'a str,
185            generate_typescript: bool,
186            generate_jsdoc: bool,
187            variadic: bool,
188            ret_ty_override: Option<&'a str>,
189            ret_desc: Option<&'a str>,
190        }
191
192        struct FunctionArgumentData<'a> {
193            name: String,
194            ty_override: Option<&'a str>,
195            optional: bool,
196            desc: Option<&'a str>,
197        }
198
199        struct Struct<'a> {
200            name: &'a str,
201            fields: Vec<StructField<'a>>,
202            comments: Vec<&'a str>,
203            is_inspectable: bool,
204            generate_typescript: bool,
205            js_namespace: Option<Vec<&'a str>>,
206            private: bool,
207            extends: Option<&'a str>,
208            extends_js_class: Option<&'a str>,
209            extends_js_namespace: Option<Vec<&'a str>>,
210        }
211
212        struct StructField<'a> {
213            name: &'a str,
214            readonly: bool,
215            comments: Vec<&'a str>,
216            generate_typescript: bool,
217            generate_jsdoc: bool,
218        }
219
220        struct LocalModule<'a> {
221            identifier: &'a str,
222            contents: &'a str,
223            linked_module: bool,
224        }
225        }
226    }; // end of mac case
227} // end of mac definition
228
229/// Compute a "qualified name" by prepending the namespace (joined with `__`) to the js_name.
230/// When there is no namespace, this returns the js_name unchanged.
231/// This is used to disambiguate internal wasm symbols when the same js_name
232/// appears in different namespaces. `__` is used as the separator because
233/// double underscores are unlikely to appear in user-defined names.
234pub fn qualified_name(js_namespace: Option<&[impl AsRef<str>]>, js_name: &str) -> String {
235    match js_namespace {
236        Some(ns) if !ns.is_empty() => {
237            let mut name = ns
238                .iter()
239                .map(|s| s.as_ref())
240                .collect::<alloc::vec::Vec<_>>()
241                .join("__");
242            name.push_str("__");
243            name.push_str(js_name);
244            name
245        }
246        _ => js_name.to_string(),
247    }
248}
249
250/// Append the per-crate hash to a wasm shim symbol so that identically named
251/// exports from different crates (or different versions of one crate) cannot
252/// collide at link time. The macro emits every export shim under this mangled
253/// name; cli-support recomputes it from the hash carried in
254/// `Program::unique_crate_identifier` and renames the export back to its
255/// canonical name once matched.
256pub fn mangled_symbol(base: &str, crate_hash: &str) -> String {
257    let mut name = base.to_string();
258    name.push('_');
259    name.push_str(crate_hash);
260    name
261}
262
263/// [`mangled_symbol`] further qualified by the defining module, for wasm
264/// symbols of items that may share a name across modules of one crate. The
265/// macro only learns the module path from rustc, so it emits this as
266/// `concat!(module_mangled_symbol(base, hash, ""), module_path!())`.
267pub fn module_mangled_symbol(base: &str, crate_hash: &str, module_path: &str) -> String {
268    let mut name = mangled_symbol(base, crate_hash);
269    name.push('_');
270    name.push_str(module_path);
271    name
272}
273
274/// FNV-1a hash of a `module_path!()`, carried by type descriptors to identify
275/// the defining module. A `const fn` so the macro can fold it into a constant:
276/// the descriptor interpreter cannot read static data, so the string itself
277/// cannot be informed at runtime. The macro (through `wasm_bindgen::__rt`)
278/// and the CLI must both use this one function.
279pub const fn module_path_hash(module_path: &str) -> u32 {
280    let bytes = module_path.as_bytes();
281    let mut hash = 0x811c_9dc5u32;
282    let mut i = 0;
283    while i < bytes.len() {
284        hash ^= bytes[i] as u32;
285        hash = hash.wrapping_mul(0x0100_0193);
286        i += 1;
287    }
288    hash
289}
290
291/// Extract the crate name from a `unique_crate_identifier`, which is encoded
292/// as `"{crate_name}-{hash}"` (the hash contains no `-`).
293pub fn crate_name(unique_crate_identifier: &str) -> &str {
294    unique_crate_identifier
295        .rsplit_once('-')
296        .map_or(unique_crate_identifier, |(name, _)| name)
297}
298
299/// Extract the crate hash from a `unique_crate_identifier` (see
300/// [`crate_name`]).
301pub fn crate_hash(unique_crate_identifier: &str) -> &str {
302    unique_crate_identifier
303        .rsplit_once('-')
304        .map(|(_, hash)| hash)
305        .unwrap_or(unique_crate_identifier)
306}
307
308pub fn new_function(struct_name: &str) -> String {
309    let mut name = "__wbg_".to_string();
310    name.extend(struct_name.chars().flat_map(|s| s.to_lowercase()));
311    name.push_str("_new");
312    name
313}
314
315pub fn free_function(struct_name: &str) -> String {
316    let mut name = "__wbg_".to_string();
317    name.extend(struct_name.chars().flat_map(|s| s.to_lowercase()));
318    name.push_str("_free");
319    name
320}
321
322pub fn unwrap_function(struct_name: &str) -> String {
323    let mut name = "__wbg_".to_string();
324    name.extend(struct_name.chars().flat_map(|s| s.to_lowercase()));
325    name.push_str("_unwrap");
326    name
327}
328
329/// Convert a JS-side name into a form suitable as a wasm-side export
330/// symbol suffix. Plain identifier names pass through unchanged. The
331/// bracket form `"[Symbol.<ident>]"` collapses to `Symbol_<ident>`. Any
332/// other non-alphanumeric characters are replaced with `_` so that the
333/// result is always a valid C identifier suffix.
334fn export_name_suffix(name: &str) -> alloc::borrow::Cow<'_, str> {
335    if name.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_') {
336        return alloc::borrow::Cow::Borrowed(name);
337    }
338    let mut out = String::with_capacity(name.len());
339    for c in name.chars() {
340        if c.is_ascii_alphanumeric() || c == '_' {
341            out.push(c);
342        } else if c == '.' || c == '[' || c == ']' {
343            // Bracket / dotted forms collapse cleanly; we represent
344            // `[Symbol.iterator]` as `Symbol_iterator` (the `[` and `]`
345            // drop, the `.` becomes `_`).
346            if c == '.' {
347                out.push('_');
348            }
349        } else {
350            out.push('_');
351        }
352    }
353    alloc::borrow::Cow::Owned(out)
354}
355
356/// Symbol name of the wasm export that upcasts a `*const WasmRefCell<Child>`
357/// pointer to a cloned `Rc<WasmRefCell<Parent>>` raw pointer. Used by the
358/// macro codegen and by cli-support's JS emission to call each other.
359pub fn upcast_function(child_struct: &str, parent_struct: &str) -> String {
360    let mut name = "__wbg_upcast_".to_string();
361    name.extend(child_struct.chars().flat_map(|s| s.to_lowercase()));
362    name.push_str("_to_");
363    name.extend(parent_struct.chars().flat_map(|s| s.to_lowercase()));
364    name
365}
366
367pub fn free_function_export_name(function_name: &str) -> String {
368    export_name_suffix(function_name).into_owned()
369}
370
371pub fn struct_function_export_name(struct_: &str, f: &str) -> String {
372    let mut name = struct_
373        .chars()
374        .flat_map(|s| s.to_lowercase())
375        .collect::<String>();
376    name.push('_');
377    name.push_str(&export_name_suffix(f));
378    name
379}
380
381pub fn struct_field_get(struct_: &str, f: &str) -> String {
382    let mut name = String::from("__wbg_get_");
383    name.extend(struct_.chars().flat_map(|s| s.to_lowercase()));
384    name.push('_');
385    name.push_str(&export_name_suffix(f));
386    name
387}
388
389pub fn struct_field_set(struct_: &str, f: &str) -> String {
390    let mut name = String::from("__wbg_set_");
391    name.extend(struct_.chars().flat_map(|s| s.to_lowercase()));
392    name.push('_');
393    name.push_str(&export_name_suffix(f));
394    name
395}
396
397pub fn dynamic_union_variant(union_name: &str, variant_idx: u32) -> String {
398    let mut name = String::from("__wbg_dynamic_union_");
399    name.extend(union_name.chars().flat_map(|s| s.to_lowercase()));
400    name.push('_');
401    name.push_str(&variant_idx.to_string());
402    name
403}
404
405pub fn version() -> String {
406    let mut v = env!("CARGO_PKG_VERSION").to_string();
407    if let Some(s) = option_env!("WBG_VERSION") {
408        v.push_str(" (");
409        v.push_str(s);
410        v.push(')');
411    }
412    v
413}
414
415pub fn escape_string(s: &str) -> String {
416    let mut result = String::with_capacity(s.len());
417    for c in s.chars() {
418        match c {
419            '\\' => result.push_str("\\\\"),
420            '\n' => result.push_str("\\n"),
421            '\r' => result.push_str("\\r"),
422            '\'' => result.push_str("\\'"),
423            '"' => result.push_str("\\\""),
424            _ => result.push(c),
425        }
426    }
427    result
428}