wasm_bindgen_backend/
codegen.rs

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