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.128";
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            package_json: Option<&'a str>,
36            linked_modules: Vec<LinkedModule<'a>>,
37        }
38
39        struct Import<'a> {
40            module: Option<ImportModule<'a>>,
41            js_namespace: Option<Vec<String>>,
42            reexport: Option<String>,
43            generate_typescript: bool,
44            kind: ImportKind<'a>,
45        }
46
47        struct LinkedModule<'a> {
48            module: ImportModule<'a>,
49            link_function_name: &'a str,
50        }
51
52        enum ImportModule<'a> {
53            Named(&'a str),
54            RawNamed(&'a str),
55            Inline(u32),
56        }
57
58        enum ImportKind<'a> {
59            Function(ImportFunction<'a>),
60            Static(ImportStatic<'a>),
61            String(ImportString<'a>),
62            Type(ImportType<'a>),
63            Enum(StringEnum<'a>),
64            DynamicUnion(DynamicUnion<'a>),
65        }
66
67        struct ImportFunction<'a> {
68            shim: &'a str,
69            catch: bool,
70            variadic: bool,
71            assert_no_shim: bool,
72            suspending: bool,
73            method: Option<MethodData<'a>>,
74            structural: bool,
75            function: Function<'a>,
76            // Whether this import uses the per-monomorphisation generic path
77            // (`#[wasm_bindgen(experimental_generic_mono)]`) and is therefore bound per
78            // concrete instantiation via the `__wbindgen_describe_generic_import`
79            // marker rather than a single named descriptor shim. (Type-erasure
80            // generic imports do not set this; they bind like normal imports.)
81            generic_per_mono: bool,
82        }
83
84        struct MethodData<'a> {
85            class: &'a str,
86            kind: MethodKind<'a>,
87        }
88
89        enum MethodKind<'a> {
90            Constructor,
91            Operation(Operation<'a>),
92        }
93
94        struct Operation<'a> {
95            is_static: bool,
96            kind: OperationKind<'a>,
97        }
98
99        enum OperationKind<'a> {
100            Regular,
101            RegularThis,
102            Getter(&'a str),
103            Setter(&'a str),
104            IndexingGetter,
105            IndexingSetter,
106            IndexingDeleter,
107        }
108
109        struct ImportStatic<'a> {
110            name: &'a str,
111            shim: &'a str,
112        }
113
114        struct ImportString<'a> {
115            shim: &'a str,
116            string: &'a str,
117        }
118
119        struct ImportType<'a> {
120            name: &'a str,
121            instanceof_shim: &'a str,
122            vendor_prefixes: Vec<&'a str>,
123        }
124
125        struct StringEnum<'a> {
126            name: &'a str,
127            variant_values: Vec<&'a str>,
128            comments: Vec<&'a str>,
129            generate_typescript: bool,
130            private: bool,
131            js_namespace: Option<Vec<&'a str>>,
132        }
133
134        enum StartKind {
135            None,
136            Public,
137            Private,
138        }
139
140        struct DynamicUnion<'a> {
141            name: &'a str,
142            variant_strings: Vec<&'a str>,
143            variant_type_cnt: u32,
144            comments: Vec<&'a str>,
145            generate_typescript: bool,
146            private: bool,
147            fallback: bool,
148        }
149
150
151        struct Export<'a> {
152            class: Option<&'a str>,
153            comments: Vec<&'a str>,
154            consumed: bool,
155            function: Function<'a>,
156            js_namespace: Option<Vec<&'a str>>,
157            method_kind: MethodKind<'a>,
158            start: StartKind,
159        }
160
161        struct Enum<'a> {
162            name: &'a str,
163            signed: bool,
164            variants: Vec<EnumVariant<'a>>,
165            comments: Vec<&'a str>,
166            generate_typescript: bool,
167            js_namespace: Option<Vec<&'a str>>,
168            private: bool,
169        }
170
171        struct EnumVariant<'a> {
172            name: &'a str,
173            value: u32,
174            comments: Vec<&'a str>,
175        }
176
177        struct Function<'a> {
178            args: Vec<FunctionArgumentData<'a>>,
179            asyncness: bool,
180            jspi: bool,
181            name: &'a str,
182            generate_typescript: bool,
183            generate_jsdoc: bool,
184            variadic: bool,
185            ret_ty_override: Option<&'a str>,
186            ret_desc: Option<&'a str>,
187        }
188
189        struct FunctionArgumentData<'a> {
190            name: String,
191            ty_override: Option<&'a str>,
192            optional: bool,
193            desc: Option<&'a str>,
194        }
195
196        struct Struct<'a> {
197            name: &'a str,
198            fields: Vec<StructField<'a>>,
199            comments: Vec<&'a str>,
200            is_inspectable: bool,
201            generate_typescript: bool,
202            js_namespace: Option<Vec<&'a str>>,
203            private: bool,
204            extends: Option<&'a str>,
205            extends_js_class: Option<&'a str>,
206            extends_js_namespace: Option<Vec<&'a str>>,
207        }
208
209        struct StructField<'a> {
210            name: &'a str,
211            readonly: bool,
212            comments: Vec<&'a str>,
213            generate_typescript: bool,
214            generate_jsdoc: bool,
215        }
216
217        struct LocalModule<'a> {
218            identifier: &'a str,
219            contents: &'a str,
220            linked_module: bool,
221        }
222        }
223    }; // end of mac case
224} // end of mac definition
225
226/// Compute a "qualified name" by prepending the namespace (joined with `__`) to the js_name.
227/// When there is no namespace, this returns the js_name unchanged.
228/// This is used to disambiguate internal wasm symbols when the same js_name
229/// appears in different namespaces. `__` is used as the separator because
230/// double underscores are unlikely to appear in user-defined names.
231pub fn qualified_name(js_namespace: Option<&[impl AsRef<str>]>, js_name: &str) -> String {
232    match js_namespace {
233        Some(ns) if !ns.is_empty() => {
234            let mut name = ns
235                .iter()
236                .map(|s| s.as_ref())
237                .collect::<alloc::vec::Vec<_>>()
238                .join("__");
239            name.push_str("__");
240            name.push_str(js_name);
241            name
242        }
243        _ => js_name.to_string(),
244    }
245}
246
247/// Append the per-crate hash to a wasm shim symbol so that identically named
248/// exports from different crates (or different versions of one crate) cannot
249/// collide at link time. The macro emits every export shim under this mangled
250/// name; cli-support recomputes it from the hash carried in
251/// `Program::unique_crate_identifier` and renames the export back to its
252/// canonical name once matched.
253pub fn mangled_symbol(base: &str, crate_hash: &str) -> String {
254    let mut name = base.to_string();
255    name.push('_');
256    name.push_str(crate_hash);
257    name
258}
259
260/// Extract the crate hash from a `unique_crate_identifier`, which is encoded
261/// as `"{crate_name}-{hash}"` (the hash contains no `-`).
262pub fn crate_hash(unique_crate_identifier: &str) -> &str {
263    unique_crate_identifier
264        .rsplit_once('-')
265        .map(|(_, hash)| hash)
266        .unwrap_or(unique_crate_identifier)
267}
268
269pub fn new_function(struct_name: &str) -> String {
270    let mut name = "__wbg_".to_string();
271    name.extend(struct_name.chars().flat_map(|s| s.to_lowercase()));
272    name.push_str("_new");
273    name
274}
275
276pub fn free_function(struct_name: &str) -> String {
277    let mut name = "__wbg_".to_string();
278    name.extend(struct_name.chars().flat_map(|s| s.to_lowercase()));
279    name.push_str("_free");
280    name
281}
282
283pub fn unwrap_function(struct_name: &str) -> String {
284    let mut name = "__wbg_".to_string();
285    name.extend(struct_name.chars().flat_map(|s| s.to_lowercase()));
286    name.push_str("_unwrap");
287    name
288}
289
290/// Convert a JS-side name into a form suitable as a wasm-side export
291/// symbol suffix. Plain identifier names pass through unchanged. The
292/// bracket form `"[Symbol.<ident>]"` collapses to `Symbol_<ident>`. Any
293/// other non-alphanumeric characters are replaced with `_` so that the
294/// result is always a valid C identifier suffix.
295fn export_name_suffix(name: &str) -> alloc::borrow::Cow<'_, str> {
296    if name.bytes().all(|b| b.is_ascii_alphanumeric() || b == b'_') {
297        return alloc::borrow::Cow::Borrowed(name);
298    }
299    let mut out = String::with_capacity(name.len());
300    for c in name.chars() {
301        if c.is_ascii_alphanumeric() || c == '_' {
302            out.push(c);
303        } else if c == '.' || c == '[' || c == ']' {
304            // Bracket / dotted forms collapse cleanly; we represent
305            // `[Symbol.iterator]` as `Symbol_iterator` (the `[` and `]`
306            // drop, the `.` becomes `_`).
307            if c == '.' {
308                out.push('_');
309            }
310        } else {
311            out.push('_');
312        }
313    }
314    alloc::borrow::Cow::Owned(out)
315}
316
317/// Symbol name of the wasm export that upcasts a `*const WasmRefCell<Child>`
318/// pointer to a cloned `Rc<WasmRefCell<Parent>>` raw pointer. Used by the
319/// macro codegen and by cli-support's JS emission to call each other.
320pub fn upcast_function(child_struct: &str, parent_struct: &str) -> String {
321    let mut name = "__wbg_upcast_".to_string();
322    name.extend(child_struct.chars().flat_map(|s| s.to_lowercase()));
323    name.push_str("_to_");
324    name.extend(parent_struct.chars().flat_map(|s| s.to_lowercase()));
325    name
326}
327
328pub fn free_function_export_name(function_name: &str) -> String {
329    export_name_suffix(function_name).into_owned()
330}
331
332pub fn struct_function_export_name(struct_: &str, f: &str) -> String {
333    let mut name = struct_
334        .chars()
335        .flat_map(|s| s.to_lowercase())
336        .collect::<String>();
337    name.push('_');
338    name.push_str(&export_name_suffix(f));
339    name
340}
341
342pub fn struct_field_get(struct_: &str, f: &str) -> String {
343    let mut name = String::from("__wbg_get_");
344    name.extend(struct_.chars().flat_map(|s| s.to_lowercase()));
345    name.push('_');
346    name.push_str(&export_name_suffix(f));
347    name
348}
349
350pub fn struct_field_set(struct_: &str, f: &str) -> String {
351    let mut name = String::from("__wbg_set_");
352    name.extend(struct_.chars().flat_map(|s| s.to_lowercase()));
353    name.push('_');
354    name.push_str(&export_name_suffix(f));
355    name
356}
357
358pub fn dynamic_union_variant(union_name: &str, variant_idx: u32) -> String {
359    let mut name = String::from("__wbg_dynamic_union_");
360    name.extend(union_name.chars().flat_map(|s| s.to_lowercase()));
361    name.push('_');
362    name.push_str(&variant_idx.to_string());
363    name
364}
365
366pub fn version() -> String {
367    let mut v = env!("CARGO_PKG_VERSION").to_string();
368    if let Some(s) = option_env!("WBG_VERSION") {
369        v.push_str(" (");
370        v.push_str(s);
371        v.push(')');
372    }
373    v
374}
375
376pub fn escape_string(s: &str) -> String {
377    let mut result = String::with_capacity(s.len());
378    for c in s.chars() {
379        match c {
380            '\\' => result.push_str("\\\\"),
381            '\n' => result.push_str("\\n"),
382            '\r' => result.push_str("\\r"),
383            '\'' => result.push_str("\\'"),
384            '"' => result.push_str("\\\""),
385            _ => result.push(c),
386        }
387    }
388    result
389}