wasm_bindgen_backend/
codegen.rs

1use crate::ast;
2use crate::encode;
3use crate::encode::EncodeChunk;
4use crate::Diagnostic;
5use once_cell::sync::Lazy;
6use proc_macro2::{Ident, Span, TokenStream};
7use quote::format_ident;
8use quote::quote_spanned;
9use quote::{quote, ToTokens};
10use std::collections::{HashMap, HashSet};
11use std::sync::Mutex;
12use syn::parse_quote;
13use syn::spanned::Spanned;
14use wasm_bindgen_shared as shared;
15
16/// A trait for converting AST structs into Tokens and adding them to a TokenStream,
17/// or providing a diagnostic if conversion fails.
18pub trait TryToTokens {
19    /// Attempt to convert a `Self` into tokens and add it to the `TokenStream`
20    fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic>;
21
22    /// Attempt to convert a `Self` into a new `TokenStream`
23    fn try_to_token_stream(&self) -> Result<TokenStream, Diagnostic> {
24        let mut tokens = TokenStream::new();
25        self.try_to_tokens(&mut tokens)?;
26        Ok(tokens)
27    }
28}
29
30impl TryToTokens for ast::Program {
31    // Generate wrappers for all the items that we've found
32    fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
33        let mut errors = Vec::new();
34        for export in self.exports.iter() {
35            if let Err(e) = export.try_to_tokens(tokens) {
36                errors.push(e);
37            }
38        }
39        for s in self.structs.iter() {
40            s.to_tokens(tokens);
41        }
42        let mut types = HashMap::new();
43        for i in self.imports.iter() {
44            if let ast::ImportKind::Type(t) = &i.kind {
45                types.insert(t.rust_name.to_string(), t.rust_name.clone());
46            }
47        }
48        for i in self.imports.iter() {
49            DescribeImport {
50                kind: &i.kind,
51                wasm_bindgen: &self.wasm_bindgen,
52            }
53            .to_tokens(tokens);
54
55            // If there is a js namespace, check that name isn't a type. If it is,
56            // this import might be a method on that type.
57            if let Some(nss) = &i.js_namespace {
58                // When the namespace is `A.B`, the type name should be `B`.
59                if let Some(ns) = nss.last().and_then(|t| types.get(t)) {
60                    if i.kind.fits_on_impl() {
61                        let kind = match i.kind.try_to_token_stream() {
62                            Ok(kind) => kind,
63                            Err(e) => {
64                                errors.push(e);
65                                continue;
66                            }
67                        };
68                        (quote! {
69                            #[automatically_derived]
70                            impl #ns { #kind }
71                        })
72                        .to_tokens(tokens);
73                        continue;
74                    }
75                }
76            }
77
78            if let Err(e) = i.kind.try_to_tokens(tokens) {
79                errors.push(e);
80            }
81        }
82        for e in self.enums.iter() {
83            e.to_tokens(tokens);
84        }
85
86        Diagnostic::from_vec(errors)?;
87
88        // Generate a static which will eventually be what lives in a custom section
89        // of the wasm executable. For now it's just a plain old static, but we'll
90        // eventually have it actually in its own section.
91
92        // See comments in `crates/cli-support/src/lib.rs` about what this
93        // `schema_version` is.
94        let prefix_json = format!(
95            r#"{{"schema_version":"{}","version":"{}"}}"#,
96            shared::SCHEMA_VERSION,
97            shared::version()
98        );
99
100        let wasm_bindgen = &self.wasm_bindgen;
101
102        let encoded = encode::encode(self)?;
103
104        let encoded_chunks: Vec<_> = encoded
105            .custom_section
106            .iter()
107            .map(|chunk| match chunk {
108                EncodeChunk::EncodedBuf(buf) => {
109                    let buf = syn::LitByteStr::new(buf.as_slice(), Span::call_site());
110                    quote!(#buf)
111                }
112                EncodeChunk::StrExpr(expr) => {
113                    // encode expr as str
114                    quote!({
115                        use #wasm_bindgen::__rt::{encode_u32_to_fixed_len_bytes};
116                        const _STR_EXPR: &str = #expr;
117                        const _STR_EXPR_BYTES: &[u8] = _STR_EXPR.as_bytes();
118                        const _STR_EXPR_BYTES_LEN: usize = _STR_EXPR_BYTES.len() + 5;
119                        const _ENCODED_BYTES: [u8; _STR_EXPR_BYTES_LEN] = flat_byte_slices([
120                            &encode_u32_to_fixed_len_bytes(_STR_EXPR_BYTES.len() as u32),
121                            _STR_EXPR_BYTES,
122                        ]);
123                        &_ENCODED_BYTES
124                    })
125                }
126            })
127            .collect();
128
129        let chunk_len = encoded_chunks.len();
130
131        // concatenate all encoded chunks and write the length in front of the chunk;
132        let encode_bytes = quote!({
133            const _CHUNK_SLICES: [&[u8]; #chunk_len] = [
134                #(#encoded_chunks,)*
135            ];
136            const _CHUNK_LEN: usize = flat_len(_CHUNK_SLICES);
137            const _CHUNKS: [u8; _CHUNK_LEN] = flat_byte_slices(_CHUNK_SLICES);
138
139            const _LEN_BYTES: [u8; 4] = (_CHUNK_LEN as u32).to_le_bytes();
140            const _ENCODED_BYTES_LEN: usize = _CHUNK_LEN + 4;
141            const _ENCODED_BYTES: [u8; _ENCODED_BYTES_LEN] = flat_byte_slices([&_LEN_BYTES, &_CHUNKS]);
142            &_ENCODED_BYTES
143        });
144
145        // We already consumed the contents of included files when generating
146        // the custom section, but we want to make sure that updates to the
147        // generated files will cause this macro to rerun incrementally. To do
148        // that we use `include_str!` to force rustc to think it has a
149        // dependency on these files. That way when the file changes Cargo will
150        // automatically rerun rustc which will rerun this macro. Other than
151        // this we don't actually need the results of the `include_str!`, so
152        // it's just shoved into an anonymous static.
153        let file_dependencies = encoded.included_files.iter().map(|file| {
154            let file = file.to_str().unwrap();
155            quote! { include_str!(#file) }
156        });
157
158        let len = prefix_json.len() as u32;
159        let prefix_json_bytes = [&len.to_le_bytes()[..], prefix_json.as_bytes()].concat();
160        let prefix_json_bytes = syn::LitByteStr::new(&prefix_json_bytes, Span::call_site());
161
162        (quote! {
163            #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
164            #[automatically_derived]
165            const _: () = {
166                use #wasm_bindgen::__rt::{flat_len, flat_byte_slices};
167
168                static _INCLUDED_FILES: &[&str] = &[#(#file_dependencies),*];
169
170                const _ENCODED_BYTES: &[u8] = #encode_bytes;
171                const _PREFIX_JSON_BYTES: &[u8] = #prefix_json_bytes;
172                const _ENCODED_BYTES_LEN: usize  = _ENCODED_BYTES.len();
173                const _PREFIX_JSON_BYTES_LEN: usize =  _PREFIX_JSON_BYTES.len();
174                const _LEN: usize = _PREFIX_JSON_BYTES_LEN + _ENCODED_BYTES_LEN;
175
176                #[link_section = "__wasm_bindgen_unstable"]
177                static _GENERATED: [u8; _LEN] = flat_byte_slices([_PREFIX_JSON_BYTES, _ENCODED_BYTES]);
178            };
179        })
180        .to_tokens(tokens);
181
182        Ok(())
183    }
184}
185
186impl TryToTokens for ast::LinkToModule {
187    fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
188        let mut program = TokenStream::new();
189        self.0.try_to_tokens(&mut program)?;
190        let link_function_name = self.0.link_function_name(0);
191        let name = Ident::new(&link_function_name, Span::call_site());
192        let wasm_bindgen = &self.0.wasm_bindgen;
193        let abi_ret = quote! { #wasm_bindgen::convert::WasmRet<<#wasm_bindgen::__rt::alloc::string::String as #wasm_bindgen::convert::FromWasmAbi>::Abi> };
194        let extern_fn = extern_fn(&name, &[], &[], &[], abi_ret);
195        (quote! {
196            {
197                #program
198                #extern_fn
199
200                static __VAL: #wasm_bindgen::__rt::Lazy<String> = #wasm_bindgen::__rt::Lazy::new(|| unsafe {
201                    <#wasm_bindgen::__rt::alloc::string::String as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#name().join())
202                });
203
204                #wasm_bindgen::__rt::alloc::string::String::clone(&__VAL)
205            }
206        })
207        .to_tokens(tokens);
208        Ok(())
209    }
210}
211
212impl ToTokens for ast::Struct {
213    fn to_tokens(&self, tokens: &mut TokenStream) {
214        let name = &self.rust_name;
215        let name_str = self.js_name.to_string();
216        let name_len = name_str.len() as u32;
217        let name_chars: Vec<u32> = name_str.chars().map(|c| c as u32).collect();
218        let new_fn = Ident::new(&shared::new_function(&name_str), Span::call_site());
219        let free_fn = Ident::new(&shared::free_function(&name_str), Span::call_site());
220        let unwrap_fn = Ident::new(&shared::unwrap_function(&name_str), Span::call_site());
221        let wasm_bindgen = &self.wasm_bindgen;
222        (quote! {
223            #[automatically_derived]
224            impl #wasm_bindgen::describe::WasmDescribe for #name {
225                #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
226                fn describe() {
227                    use #wasm_bindgen::__wbindgen_if_not_std;
228                    use #wasm_bindgen::describe::*;
229                    inform(RUST_STRUCT);
230                    inform(#name_len);
231                    #(inform(#name_chars);)*
232                }
233            }
234
235            #[automatically_derived]
236            impl #wasm_bindgen::convert::IntoWasmAbi for #name {
237                type Abi = u32;
238
239                fn into_abi(self) -> u32 {
240                    use #wasm_bindgen::__rt::alloc::rc::Rc;
241                    use #wasm_bindgen::__rt::WasmRefCell;
242                    Rc::into_raw(Rc::new(WasmRefCell::new(self))) as u32
243                }
244            }
245
246            #[automatically_derived]
247            impl #wasm_bindgen::convert::FromWasmAbi for #name {
248                type Abi = u32;
249
250                unsafe fn from_abi(js: u32) -> Self {
251                    use #wasm_bindgen::__rt::alloc::rc::Rc;
252                    use #wasm_bindgen::__rt::core::result::Result::{Ok, Err};
253                    use #wasm_bindgen::__rt::{assert_not_null, WasmRefCell};
254
255                    let ptr = js as *mut WasmRefCell<#name>;
256                    assert_not_null(ptr);
257                    let rc = Rc::from_raw(ptr);
258                    match Rc::try_unwrap(rc) {
259                        Ok(cell) => cell.into_inner(),
260                        Err(_) => #wasm_bindgen::throw_str(
261                            "attempted to take ownership of Rust value while it was borrowed"
262                        ),
263                    }
264                }
265            }
266
267            #[automatically_derived]
268            impl #wasm_bindgen::__rt::core::convert::From<#name> for
269                #wasm_bindgen::JsValue
270            {
271                fn from(value: #name) -> Self {
272                    let ptr = #wasm_bindgen::convert::IntoWasmAbi::into_abi(value);
273
274                    #[link(wasm_import_module = "__wbindgen_placeholder__")]
275                    #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
276                    extern "C" {
277                        fn #new_fn(ptr: u32) -> u32;
278                    }
279
280                    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
281                    unsafe fn #new_fn(_: u32) -> u32 {
282                        panic!("cannot convert to JsValue outside of the wasm target")
283                    }
284
285                    unsafe {
286                        <#wasm_bindgen::JsValue as #wasm_bindgen::convert::FromWasmAbi>
287                            ::from_abi(#new_fn(ptr))
288                    }
289                }
290            }
291
292            #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
293            #[automatically_derived]
294            const _: () = {
295                #[no_mangle]
296                #[doc(hidden)]
297                // `allow_delayed` is whether it's ok to not actually free the `ptr` immediately
298                // if it's still borrowed.
299                #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
300                pub unsafe extern "C" fn #free_fn(ptr: u32, allow_delayed: u32) {
301                    use #wasm_bindgen::__rt::alloc::rc::Rc;
302
303                    if allow_delayed != 0 {
304                        // Just drop the implicit `Rc` owned by JS, and then if the value is still
305                        // referenced it'll be kept alive by its other `Rc`s.
306                        let ptr = ptr as *mut #wasm_bindgen::__rt::WasmRefCell<#name>;
307                        #wasm_bindgen::__rt::assert_not_null(ptr);
308                        drop(Rc::from_raw(ptr));
309                    } else {
310                        // Claim ownership of the value, which will panic if it's borrowed.
311                        let _ = <#name as #wasm_bindgen::convert::FromWasmAbi>::from_abi(ptr);
312                    }
313                }
314            };
315
316            #[automatically_derived]
317            impl #wasm_bindgen::convert::RefFromWasmAbi for #name {
318                type Abi = u32;
319                type Anchor = #wasm_bindgen::__rt::RcRef<#name>;
320
321                unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor {
322                    use #wasm_bindgen::__rt::alloc::rc::Rc;
323
324                    let js = js as *mut #wasm_bindgen::__rt::WasmRefCell<#name>;
325                    #wasm_bindgen::__rt::assert_not_null(js);
326
327                    Rc::increment_strong_count(js);
328                    let rc = Rc::from_raw(js);
329                    #wasm_bindgen::__rt::RcRef::new(rc)
330                }
331            }
332
333            #[automatically_derived]
334            impl #wasm_bindgen::convert::RefMutFromWasmAbi for #name {
335                type Abi = u32;
336                type Anchor = #wasm_bindgen::__rt::RcRefMut<#name>;
337
338                unsafe fn ref_mut_from_abi(js: Self::Abi) -> Self::Anchor {
339                    use #wasm_bindgen::__rt::alloc::rc::Rc;
340
341                    let js = js as *mut #wasm_bindgen::__rt::WasmRefCell<#name>;
342                    #wasm_bindgen::__rt::assert_not_null(js);
343
344                    Rc::increment_strong_count(js);
345                    let rc = Rc::from_raw(js);
346                    #wasm_bindgen::__rt::RcRefMut::new(rc)
347                }
348            }
349
350            #[automatically_derived]
351            impl #wasm_bindgen::convert::LongRefFromWasmAbi for #name {
352                type Abi = u32;
353                type Anchor = #wasm_bindgen::__rt::RcRef<#name>;
354
355                unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor {
356                    <Self as #wasm_bindgen::convert::RefFromWasmAbi>::ref_from_abi(js)
357                }
358            }
359
360            #[automatically_derived]
361            impl #wasm_bindgen::convert::OptionIntoWasmAbi for #name {
362                #[inline]
363                fn none() -> Self::Abi { 0 }
364            }
365
366            #[automatically_derived]
367            impl #wasm_bindgen::convert::OptionFromWasmAbi for #name {
368                #[inline]
369                fn is_none(abi: &Self::Abi) -> bool { *abi == 0 }
370            }
371
372            #[allow(clippy::all)]
373            impl #wasm_bindgen::convert::TryFromJsValue for #name {
374                type Error = #wasm_bindgen::JsValue;
375
376                fn try_from_js_value(value: #wasm_bindgen::JsValue)
377                    -> #wasm_bindgen::__rt::core::result::Result<Self, Self::Error> {
378                    let idx = #wasm_bindgen::convert::IntoWasmAbi::into_abi(&value);
379
380                    #[link(wasm_import_module = "__wbindgen_placeholder__")]
381                    #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
382                    extern "C" {
383                        fn #unwrap_fn(ptr: u32) -> u32;
384                    }
385
386                    #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
387                    unsafe fn #unwrap_fn(_: u32) -> u32 {
388                        panic!("cannot convert from JsValue outside of the wasm target")
389                    }
390
391                    let ptr = unsafe { #unwrap_fn(idx) };
392                    if ptr == 0 {
393                        #wasm_bindgen::__rt::core::result::Result::Err(value)
394                    } else {
395                        // Don't run `JsValue`'s destructor, `unwrap_fn` already did that for us.
396                        #[allow(clippy::mem_forget)]
397                        #wasm_bindgen::__rt::core::mem::forget(value);
398                        unsafe {
399                            #wasm_bindgen::__rt::core::result::Result::Ok(
400                                <Self as #wasm_bindgen::convert::FromWasmAbi>::from_abi(ptr)
401                            )
402                        }
403                    }
404                }
405            }
406
407            impl #wasm_bindgen::describe::WasmDescribeVector for #name {
408                #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
409                fn describe_vector() {
410                    use #wasm_bindgen::describe::*;
411                    inform(VECTOR);
412                    inform(NAMED_EXTERNREF);
413                    inform(#name_len);
414                    #(inform(#name_chars);)*
415                }
416            }
417
418            impl #wasm_bindgen::convert::VectorIntoWasmAbi for #name {
419                type Abi = <
420                    #wasm_bindgen::__rt::alloc::boxed::Box<[#wasm_bindgen::JsValue]>
421                    as #wasm_bindgen::convert::IntoWasmAbi
422                >::Abi;
423
424                fn vector_into_abi(
425                    vector: #wasm_bindgen::__rt::alloc::boxed::Box<[#name]>
426                ) -> Self::Abi {
427                    #wasm_bindgen::convert::js_value_vector_into_abi(vector)
428                }
429            }
430
431            impl #wasm_bindgen::convert::VectorFromWasmAbi for #name {
432                type Abi = <
433                    #wasm_bindgen::__rt::alloc::boxed::Box<[#wasm_bindgen::JsValue]>
434                    as #wasm_bindgen::convert::FromWasmAbi
435                >::Abi;
436
437                unsafe fn vector_from_abi(
438                    js: Self::Abi
439                ) -> #wasm_bindgen::__rt::alloc::boxed::Box<[#name]> {
440                    #wasm_bindgen::convert::js_value_vector_from_abi(js)
441                }
442            }
443
444            impl #wasm_bindgen::__rt::VectorIntoJsValue for #name {
445                fn vector_into_jsvalue(vector: #wasm_bindgen::__rt::alloc::boxed::Box<[#name]>) -> #wasm_bindgen::JsValue {
446                    #wasm_bindgen::__rt::js_value_vector_into_jsvalue(vector)
447                }
448            }
449        })
450        .to_tokens(tokens);
451
452        for field in self.fields.iter() {
453            field.to_tokens(tokens);
454        }
455    }
456}
457
458impl ToTokens for ast::StructField {
459    fn to_tokens(&self, tokens: &mut TokenStream) {
460        let rust_name = &self.rust_name;
461        let struct_name = &self.struct_name;
462        let ty = &self.ty;
463        let getter = &self.getter;
464        let setter = &self.setter;
465
466        let maybe_assert_copy = if self.getter_with_clone.is_some() {
467            quote! {}
468        } else {
469            quote! { assert_copy::<#ty>() }
470        };
471        let maybe_assert_copy = respan(maybe_assert_copy, ty);
472
473        // Split this out so that it isn't affected by `quote_spanned!`.
474        //
475        // If we don't do this, it might end up being unable to reference `js`
476        // properly because it doesn't have the same span.
477        //
478        // See https://github.com/rustwasm/wasm-bindgen/pull/3725.
479        let js_token = quote! { js };
480        let mut val = quote_spanned!(self.rust_name.span()=> (*#js_token).borrow().#rust_name);
481        if let Some(span) = self.getter_with_clone {
482            val = quote_spanned!(span=> <#ty as Clone>::clone(&#val) );
483        }
484
485        let wasm_bindgen = &self.wasm_bindgen;
486
487        (quote! {
488            #[automatically_derived]
489            const _: () = {
490                #[cfg_attr(all(target_arch = "wasm32", target_os = "unknown"), no_mangle)]
491                #[doc(hidden)]
492                #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
493                pub unsafe extern "C" fn #getter(js: u32)
494                    -> #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::IntoWasmAbi>::Abi>
495                {
496                    use #wasm_bindgen::__rt::{WasmRefCell, assert_not_null};
497                    use #wasm_bindgen::convert::IntoWasmAbi;
498
499                    fn assert_copy<T: Copy>(){}
500                    #maybe_assert_copy;
501
502                    let js = js as *mut WasmRefCell<#struct_name>;
503                    assert_not_null(js);
504                    let val = #val;
505                    <#ty as IntoWasmAbi>::into_abi(val).into()
506                }
507            };
508        })
509        .to_tokens(tokens);
510
511        Descriptor {
512            ident: getter,
513            inner: quote! {
514                <#ty as WasmDescribe>::describe();
515            },
516            attrs: vec![],
517            wasm_bindgen: &self.wasm_bindgen,
518        }
519        .to_tokens(tokens);
520
521        if self.readonly {
522            return;
523        }
524
525        let abi = quote! { <#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi };
526        let (args, names) = splat(wasm_bindgen, &Ident::new("val", rust_name.span()), &abi);
527
528        (quote! {
529            #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
530            #[automatically_derived]
531            const _: () = {
532                #[no_mangle]
533                #[doc(hidden)]
534                #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
535                pub unsafe extern "C" fn #setter(
536                    js: u32,
537                    #(#args,)*
538                ) {
539                    use #wasm_bindgen::__rt::{WasmRefCell, assert_not_null};
540                    use #wasm_bindgen::convert::FromWasmAbi;
541
542                    let js = js as *mut WasmRefCell<#struct_name>;
543                    assert_not_null(js);
544                    let val = <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#names),*);
545                    let val = <#ty as FromWasmAbi>::from_abi(val);
546                    (*js).borrow_mut().#rust_name = val;
547                }
548            };
549        })
550        .to_tokens(tokens);
551    }
552}
553
554impl TryToTokens for ast::Export {
555    fn try_to_tokens(self: &ast::Export, into: &mut TokenStream) -> Result<(), Diagnostic> {
556        let generated_name = self.rust_symbol();
557        let export_name = self.export_name();
558        let mut args = vec![];
559        let mut arg_conversions = vec![];
560        let mut converted_arguments = vec![];
561        let ret = Ident::new("_ret", Span::call_site());
562
563        let offset = if self.method_self.is_some() {
564            args.push(quote! { me: u32 });
565            1
566        } else {
567            0
568        };
569
570        let name = &self.rust_name;
571        let wasm_bindgen = &self.wasm_bindgen;
572        let wasm_bindgen_futures = &self.wasm_bindgen_futures;
573        let receiver = match self.method_self {
574            Some(ast::MethodSelf::ByValue) => {
575                let class = self.rust_class.as_ref().unwrap();
576                arg_conversions.push(quote! {
577                    let me = unsafe {
578                        <#class as #wasm_bindgen::convert::FromWasmAbi>::from_abi(me)
579                    };
580                });
581                quote! { me.#name }
582            }
583            Some(ast::MethodSelf::RefMutable) => {
584                let class = self.rust_class.as_ref().unwrap();
585                arg_conversions.push(quote! {
586                    let mut me = unsafe {
587                        <#class as #wasm_bindgen::convert::RefMutFromWasmAbi>
588                            ::ref_mut_from_abi(me)
589                    };
590                    let me = &mut *me;
591                });
592                quote! { me.#name }
593            }
594            Some(ast::MethodSelf::RefShared) => {
595                let class = self.rust_class.as_ref().unwrap();
596                let (trait_, func, borrow) = if self.function.r#async {
597                    (
598                        quote!(LongRefFromWasmAbi),
599                        quote!(long_ref_from_abi),
600                        quote!(
601                            <<#class as #wasm_bindgen::convert::LongRefFromWasmAbi>
602                                ::Anchor as #wasm_bindgen::__rt::core::borrow::Borrow<#class>>
603                                ::borrow(&me)
604                        ),
605                    )
606                } else {
607                    (quote!(RefFromWasmAbi), quote!(ref_from_abi), quote!(&*me))
608                };
609                arg_conversions.push(quote! {
610                    let me = unsafe {
611                        <#class as #wasm_bindgen::convert::#trait_>::#func(me)
612                    };
613                    let me = #borrow;
614                });
615                quote! { me.#name }
616            }
617            None => match &self.rust_class {
618                Some(class) => quote! { #class::#name },
619                None => quote! { #name },
620            },
621        };
622
623        let mut argtys = Vec::new();
624        for (i, arg) in self.function.arguments.iter().enumerate() {
625            argtys.push(&*arg.ty);
626            let i = i + offset;
627            let ident = Ident::new(&format!("arg{}", i), Span::call_site());
628            fn unwrap_nested_types(ty: &syn::Type) -> &syn::Type {
629                match &ty {
630                    syn::Type::Group(syn::TypeGroup { ref elem, .. }) => unwrap_nested_types(elem),
631                    syn::Type::Paren(syn::TypeParen { ref elem, .. }) => unwrap_nested_types(elem),
632                    _ => ty,
633                }
634            }
635            let ty = unwrap_nested_types(&arg.ty);
636
637            match &ty {
638                syn::Type::Reference(syn::TypeReference {
639                    mutability: Some(_),
640                    elem,
641                    ..
642                }) => {
643                    let abi = quote! { <#elem as #wasm_bindgen::convert::RefMutFromWasmAbi>::Abi };
644                    let (prim_args, prim_names) = splat(wasm_bindgen, &ident, &abi);
645                    args.extend(prim_args);
646                    arg_conversions.push(quote! {
647                        let mut #ident = unsafe {
648                            <#elem as #wasm_bindgen::convert::RefMutFromWasmAbi>
649                                ::ref_mut_from_abi(
650                                    <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#prim_names),*)
651                                )
652                        };
653                        let #ident = &mut *#ident;
654                    });
655                }
656                syn::Type::Reference(syn::TypeReference { elem, .. }) => {
657                    if self.function.r#async {
658                        let abi =
659                            quote! { <#elem as #wasm_bindgen::convert::LongRefFromWasmAbi>::Abi };
660                        let (prim_args, prim_names) = splat(wasm_bindgen, &ident, &abi);
661                        args.extend(prim_args);
662                        arg_conversions.push(quote! {
663                            let #ident = unsafe {
664                                <#elem as #wasm_bindgen::convert::LongRefFromWasmAbi>
665                                    ::long_ref_from_abi(
666                                        <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#prim_names),*)
667                                    )
668                            };
669                            let #ident = <<#elem as #wasm_bindgen::convert::LongRefFromWasmAbi>
670                                ::Anchor as core::borrow::Borrow<#elem>>
671                                ::borrow(&#ident);
672                        });
673                    } else {
674                        let abi = quote! { <#elem as #wasm_bindgen::convert::RefFromWasmAbi>::Abi };
675                        let (prim_args, prim_names) = splat(wasm_bindgen, &ident, &abi);
676                        args.extend(prim_args);
677                        arg_conversions.push(quote! {
678                            let #ident = unsafe {
679                                <#elem as #wasm_bindgen::convert::RefFromWasmAbi>
680                                    ::ref_from_abi(
681                                        <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#prim_names),*)
682                                    )
683                            };
684                            let #ident = &*#ident;
685                        });
686                    }
687                }
688                _ => {
689                    let abi = quote! { <#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi };
690                    let (prim_args, prim_names) = splat(wasm_bindgen, &ident, &abi);
691                    args.extend(prim_args);
692                    arg_conversions.push(quote! {
693                        let #ident = unsafe {
694                            <#ty as #wasm_bindgen::convert::FromWasmAbi>
695                                ::from_abi(
696                                    <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#prim_names),*)
697                                )
698                        };
699                    });
700                }
701            }
702            converted_arguments.push(quote! { #ident });
703        }
704        let syn_unit = syn::Type::Tuple(syn::TypeTuple {
705            elems: Default::default(),
706            paren_token: Default::default(),
707        });
708        let syn_ret = self.function.ret.as_ref().unwrap_or(&syn_unit);
709        if let syn::Type::Reference(_) = syn_ret {
710            bail_span!(syn_ret, "cannot return a borrowed ref with #[wasm_bindgen]",)
711        }
712
713        // For an `async` function we always run it through `future_to_promise`
714        // since we're returning a promise to JS, and this will implicitly
715        // require that the function returns a `Future<Output = Result<...>>`
716        let (ret_ty, inner_ret_ty, ret_expr) = if self.function.r#async {
717            if self.start {
718                (
719                    quote! { () },
720                    quote! { () },
721                    quote! {
722                        <#syn_ret as #wasm_bindgen::__rt::Start>::start(#ret.await)
723                    },
724                )
725            } else {
726                (
727                    quote! { #wasm_bindgen::JsValue },
728                    quote! { #syn_ret },
729                    quote! {
730                        <#syn_ret as #wasm_bindgen::__rt::IntoJsResult>::into_js_result(#ret.await)
731                    },
732                )
733            }
734        } else if self.start {
735            (
736                quote! { () },
737                quote! { () },
738                quote! { <#syn_ret as #wasm_bindgen::__rt::Start>::start(#ret) },
739            )
740        } else {
741            (quote! { #syn_ret }, quote! { #syn_ret }, quote! { #ret })
742        };
743
744        let mut call = quote! {
745            {
746                #(#arg_conversions)*
747                let #ret = #receiver(#(#converted_arguments),*);
748                #ret_expr
749            }
750        };
751
752        if self.function.r#async {
753            if self.start {
754                call = quote! {
755                    #wasm_bindgen_futures::spawn_local(async move {
756                        #call
757                    })
758                }
759            } else {
760                call = quote! {
761                    #wasm_bindgen_futures::future_to_promise(async move {
762                        #call
763                    }).into()
764                }
765            }
766        }
767
768        let projection = quote! { <#ret_ty as #wasm_bindgen::convert::ReturnWasmAbi> };
769        let convert_ret = quote! { #projection::return_abi(#ret).into() };
770        let describe_ret = quote! {
771            <#ret_ty as WasmDescribe>::describe();
772            <#inner_ret_ty as WasmDescribe>::describe();
773        };
774        let nargs = self.function.arguments.len() as u32;
775        let attrs = &self.function.rust_attrs;
776
777        let start_check = if self.start {
778            quote! { const _ASSERT: fn() = || -> #projection::Abi { loop {} }; }
779        } else {
780            quote! {}
781        };
782
783        (quote! {
784            #[automatically_derived]
785            const _: () = {
786                #(#attrs)*
787                #[cfg_attr(
788                    all(target_arch = "wasm32", target_os = "unknown"),
789                    export_name = #export_name,
790                )]
791                #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
792                pub unsafe extern "C" fn #generated_name(#(#args),*) -> #wasm_bindgen::convert::WasmRet<#projection::Abi> {
793                    #start_check
794
795                    let #ret = #call;
796                    #convert_ret
797                }
798            };
799        })
800        .to_tokens(into);
801
802        let describe_args: TokenStream = argtys
803            .iter()
804            .map(|ty| match ty {
805                syn::Type::Reference(reference)
806                    if self.function.r#async && reference.mutability.is_none() =>
807                {
808                    let inner = &reference.elem;
809                    quote! {
810                        inform(LONGREF);
811                        <#inner as WasmDescribe>::describe();
812                    }
813                }
814                _ => quote! { <#ty as WasmDescribe>::describe(); },
815            })
816            .collect();
817
818        // In addition to generating the shim function above which is what
819        // our generated JS will invoke, we *also* generate a "descriptor"
820        // shim. This descriptor shim uses the `WasmDescribe` trait to
821        // programmatically describe the type signature of the generated
822        // shim above. This in turn is then used to inform the
823        // `wasm-bindgen` CLI tool exactly what types and such it should be
824        // using in JS.
825        //
826        // Note that this descriptor function is a purely an internal detail
827        // of `#[wasm_bindgen]` and isn't intended to be exported to anyone
828        // or actually part of the final was binary. Additionally, this is
829        // literally executed when the `wasm-bindgen` tool executes.
830        //
831        // In any case, there's complications in `wasm-bindgen` to handle
832        // this, but the tl;dr; is that this is stripped from the final wasm
833        // binary along with anything it references.
834        let export = Ident::new(&export_name, Span::call_site());
835        Descriptor {
836            ident: &export,
837            inner: quote! {
838                inform(FUNCTION);
839                inform(0);
840                inform(#nargs);
841                #describe_args
842                #describe_ret
843            },
844            attrs: attrs.clone(),
845            wasm_bindgen: &self.wasm_bindgen,
846        }
847        .to_tokens(into);
848
849        Ok(())
850    }
851}
852
853impl TryToTokens for ast::ImportKind {
854    fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
855        match *self {
856            ast::ImportKind::Function(ref f) => f.try_to_tokens(tokens)?,
857            ast::ImportKind::Static(ref s) => s.to_tokens(tokens),
858            ast::ImportKind::String(ref s) => s.to_tokens(tokens),
859            ast::ImportKind::Type(ref t) => t.to_tokens(tokens),
860            ast::ImportKind::Enum(ref e) => e.to_tokens(tokens),
861        }
862
863        Ok(())
864    }
865}
866
867impl ToTokens for ast::ImportType {
868    fn to_tokens(&self, tokens: &mut TokenStream) {
869        let vis = &self.vis;
870        let rust_name = &self.rust_name;
871        let attrs = &self.attrs;
872        let doc_comment = match &self.doc_comment {
873            None => "",
874            Some(comment) => comment,
875        };
876        let instanceof_shim = Ident::new(&self.instanceof_shim, Span::call_site());
877
878        let wasm_bindgen = &self.wasm_bindgen;
879        let internal_obj = match self.extends.first() {
880            Some(target) => {
881                quote! { #target }
882            }
883            None => {
884                quote! { #wasm_bindgen::JsValue }
885            }
886        };
887
888        let description = if let Some(typescript_type) = &self.typescript_type {
889            let typescript_type_len = typescript_type.len() as u32;
890            let typescript_type_chars = typescript_type.chars().map(|c| c as u32);
891            quote! {
892                use #wasm_bindgen::describe::*;
893                inform(NAMED_EXTERNREF);
894                inform(#typescript_type_len);
895                #(inform(#typescript_type_chars);)*
896            }
897        } else {
898            quote! {
899                JsValue::describe()
900            }
901        };
902
903        let is_type_of = self.is_type_of.as_ref().map(|is_type_of| {
904            quote! {
905                #[inline]
906                fn is_type_of(val: &JsValue) -> bool {
907                    let is_type_of: fn(&JsValue) -> bool = #is_type_of;
908                    is_type_of(val)
909                }
910            }
911        });
912
913        let no_deref = self.no_deref;
914
915        let doc = if doc_comment.is_empty() {
916            quote! {}
917        } else {
918            quote! {
919                #[doc = #doc_comment]
920            }
921        };
922
923        (quote! {
924            #[automatically_derived]
925            #(#attrs)*
926            #doc
927            #[repr(transparent)]
928            #vis struct #rust_name {
929                obj: #internal_obj
930            }
931
932            #[automatically_derived]
933            const _: () = {
934                use #wasm_bindgen::convert::TryFromJsValue;
935                use #wasm_bindgen::convert::{IntoWasmAbi, FromWasmAbi};
936                use #wasm_bindgen::convert::{OptionIntoWasmAbi, OptionFromWasmAbi};
937                use #wasm_bindgen::convert::{RefFromWasmAbi, LongRefFromWasmAbi};
938                use #wasm_bindgen::describe::WasmDescribe;
939                use #wasm_bindgen::{JsValue, JsCast, JsObject};
940                use #wasm_bindgen::__rt::core;
941
942                impl WasmDescribe for #rust_name {
943                    #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
944                    fn describe() {
945                        #description
946                    }
947                }
948
949                impl IntoWasmAbi for #rust_name {
950                    type Abi = <JsValue as IntoWasmAbi>::Abi;
951
952                    #[inline]
953                    fn into_abi(self) -> Self::Abi {
954                        self.obj.into_abi()
955                    }
956                }
957
958                impl OptionIntoWasmAbi for #rust_name {
959                    #[inline]
960                    fn none() -> Self::Abi {
961                        0
962                    }
963                }
964
965                impl<'a> OptionIntoWasmAbi for &'a #rust_name {
966                    #[inline]
967                    fn none() -> Self::Abi {
968                        0
969                    }
970                }
971
972                impl FromWasmAbi for #rust_name {
973                    type Abi = <JsValue as FromWasmAbi>::Abi;
974
975                    #[inline]
976                    unsafe fn from_abi(js: Self::Abi) -> Self {
977                        #rust_name {
978                            obj: JsValue::from_abi(js).into(),
979                        }
980                    }
981                }
982
983                impl OptionFromWasmAbi for #rust_name {
984                    #[inline]
985                    fn is_none(abi: &Self::Abi) -> bool { *abi == 0 }
986                }
987
988                impl<'a> IntoWasmAbi for &'a #rust_name {
989                    type Abi = <&'a JsValue as IntoWasmAbi>::Abi;
990
991                    #[inline]
992                    fn into_abi(self) -> Self::Abi {
993                        (&self.obj).into_abi()
994                    }
995                }
996
997                impl RefFromWasmAbi for #rust_name {
998                    type Abi = <JsValue as RefFromWasmAbi>::Abi;
999                    type Anchor = core::mem::ManuallyDrop<#rust_name>;
1000
1001                    #[inline]
1002                    unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor {
1003                        let tmp = <JsValue as RefFromWasmAbi>::ref_from_abi(js);
1004                        core::mem::ManuallyDrop::new(#rust_name {
1005                            obj: core::mem::ManuallyDrop::into_inner(tmp).into(),
1006                        })
1007                    }
1008                }
1009
1010                impl LongRefFromWasmAbi for #rust_name {
1011                    type Abi = <JsValue as LongRefFromWasmAbi>::Abi;
1012                    type Anchor = #rust_name;
1013
1014                    #[inline]
1015                    unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor {
1016                        let tmp = <JsValue as LongRefFromWasmAbi>::long_ref_from_abi(js);
1017                        #rust_name { obj: tmp.into() }
1018                    }
1019                }
1020
1021                // TODO: remove this on the next major version
1022                impl From<JsValue> for #rust_name {
1023                    #[inline]
1024                    fn from(obj: JsValue) -> #rust_name {
1025                        #rust_name { obj: obj.into() }
1026                    }
1027                }
1028
1029                impl AsRef<JsValue> for #rust_name {
1030                    #[inline]
1031                    fn as_ref(&self) -> &JsValue { self.obj.as_ref() }
1032                }
1033
1034                impl AsRef<#rust_name> for #rust_name {
1035                    #[inline]
1036                    fn as_ref(&self) -> &#rust_name { self }
1037                }
1038
1039
1040                impl From<#rust_name> for JsValue {
1041                    #[inline]
1042                    fn from(obj: #rust_name) -> JsValue {
1043                        obj.obj.into()
1044                    }
1045                }
1046
1047                impl JsCast for #rust_name {
1048                    fn instanceof(val: &JsValue) -> bool {
1049                        #[link(wasm_import_module = "__wbindgen_placeholder__")]
1050                        #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1051                        extern "C" {
1052                            fn #instanceof_shim(val: u32) -> u32;
1053                        }
1054                        #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1055                        unsafe fn #instanceof_shim(_: u32) -> u32 {
1056                            panic!("cannot check instanceof on non-wasm targets");
1057                        }
1058                        unsafe {
1059                            let idx = val.into_abi();
1060                            #instanceof_shim(idx) != 0
1061                        }
1062                    }
1063
1064                    #is_type_of
1065
1066                    #[inline]
1067                    fn unchecked_from_js(val: JsValue) -> Self {
1068                        #rust_name { obj: val.into() }
1069                    }
1070
1071                    #[inline]
1072                    fn unchecked_from_js_ref(val: &JsValue) -> &Self {
1073                        // Should be safe because `#rust_name` is a transparent
1074                        // wrapper around `val`
1075                        unsafe { &*(val as *const JsValue as *const #rust_name) }
1076                    }
1077                }
1078
1079                impl JsObject for #rust_name {}
1080            };
1081        })
1082        .to_tokens(tokens);
1083
1084        if !no_deref {
1085            (quote! {
1086                #[automatically_derived]
1087                impl core::ops::Deref for #rust_name {
1088                    type Target = #internal_obj;
1089
1090                    #[inline]
1091                    fn deref(&self) -> &#internal_obj {
1092                        &self.obj
1093                    }
1094                }
1095            })
1096            .to_tokens(tokens);
1097        }
1098
1099        for superclass in self.extends.iter() {
1100            (quote! {
1101                #[automatically_derived]
1102                impl From<#rust_name> for #superclass {
1103                    #[inline]
1104                    fn from(obj: #rust_name) -> #superclass {
1105                        use #wasm_bindgen::JsCast;
1106                        #superclass::unchecked_from_js(obj.into())
1107                    }
1108                }
1109
1110                #[automatically_derived]
1111                impl AsRef<#superclass> for #rust_name {
1112                    #[inline]
1113                    fn as_ref(&self) -> &#superclass {
1114                        use #wasm_bindgen::JsCast;
1115                        #superclass::unchecked_from_js_ref(self.as_ref())
1116                    }
1117                }
1118            })
1119            .to_tokens(tokens);
1120        }
1121    }
1122}
1123
1124impl ToTokens for ast::StringEnum {
1125    fn to_tokens(&self, tokens: &mut TokenStream) {
1126        let vis = &self.vis;
1127        let enum_name = &self.name;
1128        let name_str = enum_name.to_string();
1129        let name_len = name_str.len() as u32;
1130        let name_chars = name_str.chars().map(u32::from);
1131        let variants = &self.variants;
1132        let variant_count = self.variant_values.len() as u32;
1133        let variant_values = &self.variant_values;
1134        let variant_indices = (0..variant_count).collect::<Vec<_>>();
1135        let invalid = variant_count;
1136        let hole = variant_count + 1;
1137        let attrs = &self.rust_attrs;
1138
1139        let invalid_to_str_msg = format!(
1140            "Converting an invalid string enum ({}) back to a string is currently not supported",
1141            enum_name
1142        );
1143
1144        // A vector of EnumName::VariantName tokens for this enum
1145        let variant_paths: Vec<TokenStream> = self
1146            .variants
1147            .iter()
1148            .map(|v| quote!(#enum_name::#v).into_token_stream())
1149            .collect();
1150
1151        // Borrow variant_paths because we need to use it multiple times inside the quote! macro
1152        let variant_paths_ref = &variant_paths;
1153
1154        let wasm_bindgen = &self.wasm_bindgen;
1155
1156        let describe_variants = self.variant_values.iter().map(|variant_value| {
1157            let length = variant_value.len() as u32;
1158            let chars = variant_value.chars().map(u32::from);
1159            quote! {
1160                inform(#length);
1161                #(inform(#chars);)*
1162            }
1163        });
1164
1165        (quote! {
1166            #(#attrs)*
1167            #[non_exhaustive]
1168            #[repr(u32)]
1169            #vis enum #enum_name {
1170                #(#variants = #variant_indices,)*
1171                #[automatically_derived]
1172                #[doc(hidden)]
1173                __Invalid
1174            }
1175
1176            #[automatically_derived]
1177            impl #enum_name {
1178                fn from_str(s: &str) -> Option<#enum_name> {
1179                    match s {
1180                        #(#variant_values => Some(#variant_paths_ref),)*
1181                        _ => None,
1182                    }
1183                }
1184
1185                fn to_str(&self) -> &'static str {
1186                    match self {
1187                        #(#variant_paths_ref => #variant_values,)*
1188                        #enum_name::__Invalid => panic!(#invalid_to_str_msg),
1189                    }
1190                }
1191
1192                #vis fn from_js_value(obj: &#wasm_bindgen::JsValue) -> Option<#enum_name> {
1193                    obj.as_string().and_then(|obj_str| Self::from_str(obj_str.as_str()))
1194                }
1195            }
1196
1197            #[automatically_derived]
1198            impl #wasm_bindgen::convert::IntoWasmAbi for #enum_name {
1199                type Abi = u32;
1200
1201                #[inline]
1202                fn into_abi(self) -> u32 {
1203                    self as u32
1204                }
1205            }
1206
1207            #[automatically_derived]
1208            impl #wasm_bindgen::convert::FromWasmAbi for #enum_name {
1209                type Abi = u32;
1210
1211                unsafe fn from_abi(val: u32) -> Self {
1212                    match val {
1213                        #(#variant_indices => #variant_paths_ref,)*
1214                        #invalid => #enum_name::__Invalid,
1215                        _ => unreachable!("The JS binding should only ever produce a valid value or the specific 'invalid' value"),
1216                    }
1217                }
1218            }
1219
1220            #[automatically_derived]
1221            impl #wasm_bindgen::convert::OptionFromWasmAbi for #enum_name {
1222                #[inline]
1223                fn is_none(val: &u32) -> bool { *val == #hole }
1224            }
1225
1226            #[automatically_derived]
1227            impl #wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name {
1228                #[inline]
1229                fn none() -> Self::Abi { #hole }
1230            }
1231
1232            #[automatically_derived]
1233            impl #wasm_bindgen::describe::WasmDescribe for #enum_name {
1234                #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
1235                fn describe() {
1236                    use #wasm_bindgen::describe::*;
1237                    inform(STRING_ENUM);
1238                    inform(#name_len);
1239                    #(inform(#name_chars);)*
1240                    inform(#variant_count);
1241                    #(#describe_variants)*
1242                }
1243            }
1244
1245            #[automatically_derived]
1246            impl #wasm_bindgen::__rt::core::convert::From<#enum_name> for
1247                #wasm_bindgen::JsValue
1248            {
1249                fn from(val: #enum_name) -> Self {
1250                    #wasm_bindgen::JsValue::from_str(val.to_str())
1251                }
1252            }
1253        })
1254        .to_tokens(tokens);
1255    }
1256}
1257
1258impl TryToTokens for ast::ImportFunction {
1259    fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
1260        let mut class_ty = None;
1261        let mut is_method = false;
1262        match self.kind {
1263            ast::ImportFunctionKind::Method {
1264                ref ty, ref kind, ..
1265            } => {
1266                if let ast::MethodKind::Operation(ast::Operation {
1267                    is_static: false, ..
1268                }) = kind
1269                {
1270                    is_method = true;
1271                }
1272                class_ty = Some(ty);
1273            }
1274            ast::ImportFunctionKind::Normal => {}
1275        }
1276        let vis = &self.function.rust_vis;
1277        let ret = match &self.function.ret {
1278            Some(ty) => quote! { -> #ty },
1279            None => quote!(),
1280        };
1281
1282        let mut abi_argument_names = Vec::new();
1283        let mut abi_arguments = Vec::new();
1284        let mut arg_conversions = Vec::new();
1285        let mut arguments = Vec::new();
1286        let ret_ident = Ident::new("_ret", Span::call_site());
1287        let wasm_bindgen = &self.wasm_bindgen;
1288        let wasm_bindgen_futures = &self.wasm_bindgen_futures;
1289
1290        for (i, arg) in self.function.arguments.iter().enumerate() {
1291            let ty = &arg.ty;
1292            let name = match &*arg.pat {
1293                syn::Pat::Ident(syn::PatIdent {
1294                    by_ref: None,
1295                    ident,
1296                    subpat: None,
1297                    ..
1298                }) => ident.clone(),
1299                syn::Pat::Wild(_) => syn::Ident::new(&format!("__genarg_{}", i), Span::call_site()),
1300                _ => bail_span!(
1301                    arg.pat,
1302                    "unsupported pattern in #[wasm_bindgen] imported function",
1303                ),
1304            };
1305
1306            let abi = quote! { <#ty as #wasm_bindgen::convert::IntoWasmAbi>::Abi };
1307            let (prim_args, prim_names) = splat(wasm_bindgen, &name, &abi);
1308            abi_arguments.extend(prim_args);
1309            abi_argument_names.extend(prim_names.iter().cloned());
1310
1311            let var = if i == 0 && is_method {
1312                quote! { self }
1313            } else {
1314                arguments.push(quote! { #name: #ty });
1315                quote! { #name }
1316            };
1317            arg_conversions.push(quote! {
1318                let #name = <#ty as #wasm_bindgen::convert::IntoWasmAbi>
1319                    ::into_abi(#var);
1320                let (#(#prim_names),*) = <#abi as #wasm_bindgen::convert::WasmAbi>::split(#name);
1321            });
1322        }
1323        let abi_ret;
1324        let mut convert_ret;
1325        match &self.js_ret {
1326            Some(syn::Type::Reference(_)) => {
1327                bail_span!(
1328                    self.js_ret,
1329                    "cannot return references in #[wasm_bindgen] imports yet"
1330                );
1331            }
1332            Some(ref ty) => {
1333                if self.function.r#async {
1334                    abi_ret = quote! {
1335                        #wasm_bindgen::convert::WasmRet<<#wasm_bindgen_futures::js_sys::Promise as #wasm_bindgen::convert::FromWasmAbi>::Abi>
1336                    };
1337                    let future = quote! {
1338                        #wasm_bindgen_futures::JsFuture::from(
1339                            <#wasm_bindgen_futures::js_sys::Promise as #wasm_bindgen::convert::FromWasmAbi>
1340                                ::from_abi(#ret_ident.join())
1341                        ).await
1342                    };
1343                    convert_ret = if self.catch {
1344                        quote! { Ok(#wasm_bindgen::JsCast::unchecked_from_js(#future?)) }
1345                    } else {
1346                        quote! { #wasm_bindgen::JsCast::unchecked_from_js(#future.expect("unexpected exception")) }
1347                    };
1348                } else {
1349                    abi_ret = quote! {
1350                        #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi>
1351                    };
1352                    convert_ret = quote! {
1353                        <#ty as #wasm_bindgen::convert::FromWasmAbi>
1354                            ::from_abi(#ret_ident.join())
1355                    };
1356                }
1357            }
1358            None => {
1359                if self.function.r#async {
1360                    abi_ret = quote! {
1361                        #wasm_bindgen::convert::WasmRet<<#wasm_bindgen_futures::js_sys::Promise as #wasm_bindgen::convert::FromWasmAbi>::Abi>
1362                    };
1363                    let future = quote! {
1364                        #wasm_bindgen_futures::JsFuture::from(
1365                            <#wasm_bindgen_futures::js_sys::Promise as #wasm_bindgen::convert::FromWasmAbi>
1366                                ::from_abi(#ret_ident.join())
1367                        ).await
1368                    };
1369                    convert_ret = if self.catch {
1370                        quote! { #future?; Ok(()) }
1371                    } else {
1372                        quote! { #future.expect("uncaught exception"); }
1373                    };
1374                } else {
1375                    abi_ret = quote! { () };
1376                    convert_ret = quote! { () };
1377                }
1378            }
1379        }
1380
1381        let mut exceptional_ret = quote!();
1382        if self.catch && !self.function.r#async {
1383            convert_ret = quote! { Ok(#convert_ret) };
1384            exceptional_ret = quote! {
1385                #wasm_bindgen::__rt::take_last_exception()?;
1386            };
1387        }
1388
1389        let rust_name = &self.rust_name;
1390        let import_name = &self.shim;
1391        let attrs = &self.function.rust_attrs;
1392        let arguments = &arguments;
1393        let abi_arguments = &abi_arguments[..];
1394        let abi_argument_names = &abi_argument_names[..];
1395
1396        let doc = if self.doc_comment.is_empty() {
1397            quote! {}
1398        } else {
1399            let doc_comment = &self.doc_comment;
1400            quote! { #[doc = #doc_comment] }
1401        };
1402        let me = if is_method {
1403            quote! { &self, }
1404        } else {
1405            quote!()
1406        };
1407
1408        // Route any errors pointing to this imported function to the identifier
1409        // of the function we're imported from so we at least know what function
1410        // is causing issues.
1411        //
1412        // Note that this is where type errors like "doesn't implement
1413        // FromWasmAbi" or "doesn't implement IntoWasmAbi" currently get routed.
1414        // I suspect that's because they show up in the signature via trait
1415        // projections as types of arguments, and all that needs to typecheck
1416        // before the body can be typechecked. Due to rust-lang/rust#60980 (and
1417        // probably related issues) we can't really get a precise span.
1418        //
1419        // Ideally what we want is to point errors for particular types back to
1420        // the specific argument/type that generated the error, but it looks
1421        // like rustc itself doesn't do great in that regard so let's just do
1422        // the best we can in the meantime.
1423        let extern_fn = respan(
1424            extern_fn(
1425                import_name,
1426                attrs,
1427                abi_arguments,
1428                abi_argument_names,
1429                abi_ret,
1430            ),
1431            &self.rust_name,
1432        );
1433
1434        let maybe_unsafe = if self.function.r#unsafe {
1435            Some(quote! {unsafe})
1436        } else {
1437            None
1438        };
1439        let maybe_async = if self.function.r#async {
1440            Some(quote! {async})
1441        } else {
1442            None
1443        };
1444        let invocation = quote! {
1445            // This is due to `#[automatically_derived]` attribute cannot be
1446            // placed onto bare functions.
1447            #[allow(nonstandard_style)]
1448            #[allow(clippy::all, clippy::nursery, clippy::pedantic, clippy::restriction)]
1449            #(#attrs)*
1450            #doc
1451            #vis #maybe_async #maybe_unsafe fn #rust_name(#me #(#arguments),*) #ret {
1452                #extern_fn
1453
1454                unsafe {
1455                    let #ret_ident = {
1456                        #(#arg_conversions)*
1457                        #import_name(#(#abi_argument_names),*)
1458                    };
1459                    #exceptional_ret
1460                    #convert_ret
1461                }
1462            }
1463        };
1464
1465        if let Some(class) = class_ty {
1466            (quote! {
1467                #[automatically_derived]
1468                impl #class {
1469                    #invocation
1470                }
1471            })
1472            .to_tokens(tokens);
1473        } else {
1474            invocation.to_tokens(tokens);
1475        }
1476
1477        Ok(())
1478    }
1479}
1480
1481// See comment above in ast::Export for what's going on here.
1482struct DescribeImport<'a> {
1483    kind: &'a ast::ImportKind,
1484    wasm_bindgen: &'a syn::Path,
1485}
1486
1487impl<'a> ToTokens for DescribeImport<'a> {
1488    fn to_tokens(&self, tokens: &mut TokenStream) {
1489        let f = match *self.kind {
1490            ast::ImportKind::Function(ref f) => f,
1491            ast::ImportKind::Static(_) => return,
1492            ast::ImportKind::String(_) => return,
1493            ast::ImportKind::Type(_) => return,
1494            ast::ImportKind::Enum(_) => return,
1495        };
1496        let argtys = f.function.arguments.iter().map(|arg| &arg.ty);
1497        let nargs = f.function.arguments.len() as u32;
1498        let inform_ret = match &f.js_ret {
1499            Some(ref t) => quote! { <#t as WasmDescribe>::describe(); },
1500            // async functions always return a JsValue, even if they say to return ()
1501            None if f.function.r#async => quote! { <JsValue as WasmDescribe>::describe(); },
1502            None => quote! { <() as WasmDescribe>::describe(); },
1503        };
1504
1505        Descriptor {
1506            ident: &f.shim,
1507            inner: quote! {
1508                inform(FUNCTION);
1509                inform(0);
1510                inform(#nargs);
1511                #(<#argtys as WasmDescribe>::describe();)*
1512                #inform_ret
1513                #inform_ret
1514            },
1515            attrs: f.function.rust_attrs.clone(),
1516            wasm_bindgen: self.wasm_bindgen,
1517        }
1518        .to_tokens(tokens);
1519    }
1520}
1521
1522impl ToTokens for ast::Enum {
1523    fn to_tokens(&self, into: &mut TokenStream) {
1524        let enum_name = &self.rust_name;
1525        let name_str = self.js_name.to_string();
1526        let name_len = name_str.len() as u32;
1527        let name_chars = name_str.chars().map(|c| c as u32);
1528        let hole = &self.hole;
1529        let cast_clauses = self.variants.iter().map(|variant| {
1530            let variant_name = &variant.name;
1531            quote! {
1532                if js == #enum_name::#variant_name as u32 {
1533                    #enum_name::#variant_name
1534                }
1535            }
1536        });
1537        let try_from_cast_clauses = cast_clauses.clone();
1538        let wasm_bindgen = &self.wasm_bindgen;
1539        (quote! {
1540            #[automatically_derived]
1541            impl #wasm_bindgen::convert::IntoWasmAbi for #enum_name {
1542                type Abi = u32;
1543
1544                #[inline]
1545                fn into_abi(self) -> u32 {
1546                    self as u32
1547                }
1548            }
1549
1550            #[automatically_derived]
1551            impl #wasm_bindgen::convert::FromWasmAbi for #enum_name {
1552                type Abi = u32;
1553
1554                #[inline]
1555                unsafe fn from_abi(js: u32) -> Self {
1556                    #(#cast_clauses else)* {
1557                        #wasm_bindgen::throw_str("invalid enum value passed")
1558                    }
1559                }
1560            }
1561
1562            #[automatically_derived]
1563            impl #wasm_bindgen::convert::OptionFromWasmAbi for #enum_name {
1564                #[inline]
1565                fn is_none(val: &u32) -> bool { *val == #hole }
1566            }
1567
1568            #[automatically_derived]
1569            impl #wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name {
1570                #[inline]
1571                fn none() -> Self::Abi { #hole }
1572            }
1573
1574            #[automatically_derived]
1575            impl #wasm_bindgen::describe::WasmDescribe for #enum_name {
1576                #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
1577                fn describe() {
1578                    use #wasm_bindgen::describe::*;
1579                    inform(ENUM);
1580                    inform(#name_len);
1581                    #(inform(#name_chars);)*
1582                    inform(#hole);
1583                }
1584            }
1585
1586            #[automatically_derived]
1587            impl #wasm_bindgen::__rt::core::convert::From<#enum_name> for
1588                #wasm_bindgen::JsValue
1589            {
1590                fn from(value: #enum_name) -> Self {
1591                    #wasm_bindgen::JsValue::from_f64((value as u32).into())
1592                }
1593            }
1594
1595            #[allow(clippy::all)]
1596            impl #wasm_bindgen::convert::TryFromJsValue for #enum_name {
1597                type Error = #wasm_bindgen::JsValue;
1598
1599                fn try_from_js_value(value: #wasm_bindgen::JsValue)
1600                    -> #wasm_bindgen::__rt::core::result::Result<Self, <#enum_name as #wasm_bindgen::convert::TryFromJsValue>::Error> {
1601                    use #wasm_bindgen::__rt::core::convert::TryFrom;
1602                    let js = f64::try_from(&value)? as u32;
1603
1604                    #wasm_bindgen::__rt::core::result::Result::Ok(
1605                        #(#try_from_cast_clauses else)* {
1606                            return #wasm_bindgen::__rt::core::result::Result::Err(value)
1607                        }
1608                    )
1609                }
1610            }
1611
1612            impl #wasm_bindgen::describe::WasmDescribeVector for #enum_name {
1613                #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
1614                fn describe_vector() {
1615                    use #wasm_bindgen::describe::*;
1616                    inform(VECTOR);
1617                    <#wasm_bindgen::JsValue as #wasm_bindgen::describe::WasmDescribe>::describe();
1618                }
1619            }
1620
1621            impl #wasm_bindgen::convert::VectorIntoWasmAbi for #enum_name {
1622                type Abi = <
1623                    #wasm_bindgen::__rt::alloc::boxed::Box<[#wasm_bindgen::JsValue]>
1624                    as #wasm_bindgen::convert::IntoWasmAbi
1625                >::Abi;
1626
1627                fn vector_into_abi(
1628                    vector: #wasm_bindgen::__rt::alloc::boxed::Box<[#enum_name]>
1629                ) -> Self::Abi {
1630                    #wasm_bindgen::convert::js_value_vector_into_abi(vector)
1631                }
1632            }
1633
1634            impl #wasm_bindgen::convert::VectorFromWasmAbi for #enum_name {
1635                type Abi = <
1636                    #wasm_bindgen::__rt::alloc::boxed::Box<[#wasm_bindgen::JsValue]>
1637                    as #wasm_bindgen::convert::FromWasmAbi
1638                >::Abi;
1639
1640                unsafe fn vector_from_abi(
1641                    js: Self::Abi
1642                ) -> #wasm_bindgen::__rt::alloc::boxed::Box<[#enum_name]> {
1643                    #wasm_bindgen::convert::js_value_vector_from_abi(js)
1644                }
1645            }
1646
1647            impl #wasm_bindgen::__rt::VectorIntoJsValue for #enum_name {
1648                fn vector_into_jsvalue(vector: #wasm_bindgen::__rt::alloc::boxed::Box<[#enum_name]>) -> #wasm_bindgen::JsValue {
1649                    #wasm_bindgen::__rt::js_value_vector_into_jsvalue(vector)
1650                }
1651            }
1652        })
1653        .to_tokens(into);
1654    }
1655}
1656
1657impl ToTokens for ast::ImportStatic {
1658    fn to_tokens(&self, into: &mut TokenStream) {
1659        let ty = &self.ty;
1660
1661        if self.thread_local {
1662            thread_local_import(
1663                &self.vis,
1664                &self.rust_name,
1665                &self.wasm_bindgen,
1666                ty,
1667                ty,
1668                &self.shim,
1669            )
1670            .to_tokens(into)
1671        } else {
1672            let vis = &self.vis;
1673            let name = &self.rust_name;
1674            let wasm_bindgen = &self.wasm_bindgen;
1675            let ty = &self.ty;
1676            let shim_name = &self.shim;
1677            let init = static_init(wasm_bindgen, ty, shim_name);
1678
1679            into.extend(quote! {
1680                #[automatically_derived]
1681                #[deprecated = "use with `#[wasm_bindgen(thread_local)]` instead"]
1682            });
1683            into.extend(
1684                quote_spanned! { name.span() => #vis static #name: #wasm_bindgen::JsStatic<#ty> = {
1685                        fn init() -> #ty {
1686                            #init
1687                        }
1688                        thread_local!(static _VAL: #ty = init(););
1689                        #wasm_bindgen::JsStatic {
1690                            __inner: &_VAL,
1691                        }
1692                    };
1693                },
1694            );
1695        }
1696
1697        Descriptor {
1698            ident: &self.shim,
1699            inner: quote! {
1700                <#ty as WasmDescribe>::describe();
1701            },
1702            attrs: vec![],
1703            wasm_bindgen: &self.wasm_bindgen,
1704        }
1705        .to_tokens(into);
1706    }
1707}
1708
1709impl ToTokens for ast::ImportString {
1710    fn to_tokens(&self, into: &mut TokenStream) {
1711        let js_sys = &self.js_sys;
1712        let actual_ty: syn::Type = parse_quote!(#js_sys::JsString);
1713
1714        thread_local_import(
1715            &self.vis,
1716            &self.rust_name,
1717            &self.wasm_bindgen,
1718            &actual_ty,
1719            &self.ty,
1720            &self.shim,
1721        )
1722        .to_tokens(into);
1723    }
1724}
1725
1726fn thread_local_import(
1727    vis: &syn::Visibility,
1728    name: &Ident,
1729    wasm_bindgen: &syn::Path,
1730    actual_ty: &syn::Type,
1731    ty: &syn::Type,
1732    shim_name: &Ident,
1733) -> TokenStream {
1734    let init = static_init(wasm_bindgen, ty, shim_name);
1735
1736    quote! {
1737        thread_local! {
1738            #[automatically_derived]
1739            #vis static #name: #actual_ty = {
1740                #init
1741            };
1742        }
1743    }
1744}
1745
1746fn static_init(wasm_bindgen: &syn::Path, ty: &syn::Type, shim_name: &Ident) -> TokenStream {
1747    let abi_ret = quote! {
1748        #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi>
1749    };
1750    quote! {
1751        #[link(wasm_import_module = "__wbindgen_placeholder__")]
1752        #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1753        extern "C" {
1754            fn #shim_name() -> #abi_ret;
1755        }
1756
1757        #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1758        unsafe fn #shim_name() -> #abi_ret {
1759            panic!("cannot access imported statics on non-wasm targets")
1760        }
1761
1762        unsafe {
1763            <#ty as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#shim_name().join())
1764        }
1765    }
1766}
1767
1768/// Emits the necessary glue tokens for "descriptor", generating an appropriate
1769/// symbol name as well as attributes around the descriptor function itself.
1770struct Descriptor<'a, T> {
1771    ident: &'a Ident,
1772    inner: T,
1773    attrs: Vec<syn::Attribute>,
1774    wasm_bindgen: &'a syn::Path,
1775}
1776
1777impl<'a, T: ToTokens> ToTokens for Descriptor<'a, T> {
1778    fn to_tokens(&self, tokens: &mut TokenStream) {
1779        // It's possible for the same descriptor to be emitted in two different
1780        // modules (aka a value imported twice in a crate, each in a separate
1781        // module). In this case no need to emit duplicate descriptors (which
1782        // leads to duplicate symbol errors), instead just emit one.
1783        //
1784        // It's up to the descriptors themselves to ensure they have unique
1785        // names for unique items imported, currently done via `ShortHash` and
1786        // hashing appropriate data into the symbol name.
1787        static DESCRIPTORS_EMITTED: Lazy<Mutex<HashSet<String>>> = Lazy::new(Default::default);
1788
1789        let ident = self.ident;
1790
1791        if !DESCRIPTORS_EMITTED
1792            .lock()
1793            .unwrap()
1794            .insert(ident.to_string())
1795        {
1796            return;
1797        }
1798
1799        let name = Ident::new(&format!("__wbindgen_describe_{}", ident), ident.span());
1800        let inner = &self.inner;
1801        let attrs = &self.attrs;
1802        let wasm_bindgen = &self.wasm_bindgen;
1803        (quote! {
1804            #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1805            #[automatically_derived]
1806            const _: () = {
1807                #(#attrs)*
1808                #[no_mangle]
1809                #[doc(hidden)]
1810                #[cfg_attr(wasm_bindgen_unstable_test_coverage, coverage(off))]
1811                pub extern "C" fn #name() {
1812                    use #wasm_bindgen::describe::*;
1813                    // See definition of `link_mem_intrinsics` for what this is doing
1814                    #wasm_bindgen::__rt::link_mem_intrinsics();
1815                    #inner
1816                }
1817            };
1818        })
1819        .to_tokens(tokens);
1820    }
1821}
1822
1823fn extern_fn(
1824    import_name: &Ident,
1825    attrs: &[syn::Attribute],
1826    abi_arguments: &[TokenStream],
1827    abi_argument_names: &[Ident],
1828    abi_ret: TokenStream,
1829) -> TokenStream {
1830    quote! {
1831        #[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
1832        #(#attrs)*
1833        #[link(wasm_import_module = "__wbindgen_placeholder__")]
1834        extern "C" {
1835            fn #import_name(#(#abi_arguments),*) -> #abi_ret;
1836        }
1837
1838        #[cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
1839        unsafe fn #import_name(#(#abi_arguments),*) -> #abi_ret {
1840            #(
1841                drop(#abi_argument_names);
1842            )*
1843            panic!("cannot call wasm-bindgen imported functions on \
1844                    non-wasm targets");
1845        }
1846    }
1847}
1848
1849/// Splats an argument with the given name and ABI type into 4 arguments, one
1850/// for each primitive that the ABI type splits into.
1851///
1852/// Returns an `(args, names)` pair, where `args` is the list of arguments to
1853/// be inserted into the function signature, and `names` is a list of the names
1854/// of those arguments.
1855fn splat(
1856    wasm_bindgen: &syn::Path,
1857    name: &Ident,
1858    abi: &TokenStream,
1859) -> (Vec<TokenStream>, Vec<Ident>) {
1860    let mut args = Vec::new();
1861    let mut names = Vec::new();
1862
1863    for n in 1_u32..=4 {
1864        let arg_name = format_ident!("{}_{}", name, n);
1865        let prim_name = format_ident!("Prim{}", n);
1866        args.push(quote! {
1867            #arg_name: <#abi as #wasm_bindgen::convert::WasmAbi>::#prim_name
1868        });
1869        names.push(arg_name);
1870    }
1871
1872    (args, names)
1873}
1874
1875/// Converts `span` into a stream of tokens, and attempts to ensure that `input`
1876/// has all the appropriate span information so errors in it point to `span`.
1877fn respan(input: TokenStream, span: &dyn ToTokens) -> TokenStream {
1878    let mut first_span = Span::call_site();
1879    let mut last_span = Span::call_site();
1880    let mut spans = TokenStream::new();
1881    span.to_tokens(&mut spans);
1882
1883    for (i, token) in spans.into_iter().enumerate() {
1884        if i == 0 {
1885            first_span = token.span();
1886        }
1887        last_span = token.span();
1888    }
1889
1890    let mut new_tokens = Vec::new();
1891    for (i, mut token) in input.into_iter().enumerate() {
1892        if i == 0 {
1893            token.set_span(first_span);
1894        } else {
1895            token.set_span(last_span);
1896        }
1897        new_tokens.push(token);
1898    }
1899    new_tokens.into_iter().collect()
1900}