Skip to main content

wasm_bindgen_macro_support/
codegen.rs

1use crate::ast;
2use crate::encode;
3use crate::encode::EncodeChunk;
4use crate::generics::{self, generic_to_concrete};
5use crate::Diagnostic;
6use proc_macro2::{Ident, Span, TokenStream};
7use quote::format_ident;
8use quote::quote_spanned;
9use quote::{quote, ToTokens};
10use std::borrow::Cow;
11use std::cell::RefCell;
12use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
13use syn::parse_quote;
14use syn::spanned::Spanned;
15use syn::{Attribute, Meta, MetaList};
16use wasm_bindgen_shared as shared;
17
18/// A trait for converting AST structs into Tokens and adding them to a TokenStream,
19/// or providing a diagnostic if conversion fails.
20pub trait TryToTokens {
21    /// Attempt to convert a `Self` into tokens and add it to the `TokenStream`
22    fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic>;
23
24    /// Attempt to convert a `Self` into a new `TokenStream`
25    fn try_to_token_stream(&self) -> Result<TokenStream, Diagnostic> {
26        let mut tokens = TokenStream::new();
27        self.try_to_tokens(&mut tokens)?;
28        Ok(tokens)
29    }
30}
31
32impl TryToTokens for ast::Program {
33    // Generate wrappers for all the items that we've found
34    fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
35        let mut errors = Vec::new();
36        for export in self.exports.iter() {
37            if let Err(e) = export.try_to_tokens(tokens) {
38                errors.push(e);
39            }
40        }
41        for s in self.structs.iter() {
42            s.to_tokens(tokens);
43        }
44        let mut types = HashMap::new();
45        for i in self.imports.iter() {
46            if let ast::ImportKind::Type(t) = &i.kind {
47                types.insert(t.rust_name.to_string(), t.rust_name.clone());
48            }
49        }
50        for i in self.imports.iter() {
51            DescribeImport {
52                kind: &i.kind,
53                wasm_bindgen: &self.wasm_bindgen,
54            }
55            .try_to_tokens(tokens)?;
56
57            // If there is a js namespace, check that name isn't a type. If it is,
58            // this import might be a method on that type.
59            if let Some(nss) = &i.js_namespace {
60                // When the namespace is `A.B`, the type name should be `B`.
61                if let Some(ns) = nss.last().and_then(|t| types.get(t)) {
62                    if i.kind.fits_on_impl() {
63                        let kind = match i.kind.try_to_token_stream() {
64                            Ok(kind) => kind,
65                            Err(e) => {
66                                errors.push(e);
67                                continue;
68                            }
69                        };
70                        (quote! {
71                            #[automatically_derived]
72                            impl #ns { #kind }
73                        })
74                        .to_tokens(tokens);
75                        continue;
76                    }
77                }
78            }
79
80            if let Err(e) = i.kind.try_to_tokens(tokens) {
81                errors.push(e);
82            }
83        }
84        for e in self.enums.iter() {
85            e.to_tokens(tokens);
86        }
87
88        Diagnostic::from_vec(errors)?;
89
90        // Generate a static which will eventually be what lives in a custom section
91        // of the Wasm executable. For now it's just a plain old static, but we'll
92        // eventually have it actually in its own section.
93
94        // See comments in `crates/cli-support/src/lib.rs` about what this
95        // `schema_version` is.
96        let prefix_json = format!(
97            r#"{{"schema_version":"{}","version":"{}"}}"#,
98            shared::SCHEMA_VERSION,
99            shared::version()
100        );
101
102        let wasm_bindgen = &self.wasm_bindgen;
103
104        let encoded = encode::encode(self)?;
105
106        let encoded_chunks: Vec<_> = encoded
107            .custom_section
108            .iter()
109            .map(|chunk| match chunk {
110                EncodeChunk::EncodedBuf(buf) => {
111                    let buf = syn::LitByteStr::new(buf.as_slice(), Span::call_site());
112                    quote!(#buf)
113                }
114                EncodeChunk::StrExpr(expr) => {
115                    // encode expr as str
116                    quote!({
117                        use #wasm_bindgen::__rt::{encode_u32_to_fixed_len_bytes};
118                        const _STR_EXPR: &str = #expr;
119                        const _STR_EXPR_BYTES: &[u8] = _STR_EXPR.as_bytes();
120                        const _STR_EXPR_BYTES_LEN: usize = _STR_EXPR_BYTES.len() + 5;
121                        const _ENCODED_BYTES: [u8; _STR_EXPR_BYTES_LEN] = flat_byte_slices([
122                            &encode_u32_to_fixed_len_bytes(_STR_EXPR_BYTES.len() as u32),
123                            _STR_EXPR_BYTES,
124                        ]);
125                        &_ENCODED_BYTES
126                    })
127                }
128            })
129            .collect();
130
131        let chunk_len = encoded_chunks.len();
132
133        // concatenate all encoded chunks and write the length in front of the chunk;
134        let encode_bytes = quote!({
135            const _CHUNK_SLICES: [&[u8]; #chunk_len] = [
136                #(#encoded_chunks,)*
137            ];
138            #[allow(long_running_const_eval)]
139            const _CHUNK_LEN: usize = flat_len(_CHUNK_SLICES);
140            #[allow(long_running_const_eval)]
141            const _CHUNKS: [u8; _CHUNK_LEN] = flat_byte_slices(_CHUNK_SLICES);
142
143            const _LEN_BYTES: [u8; 4] = (_CHUNK_LEN as u32).to_le_bytes();
144            const _ENCODED_BYTES_LEN: usize = _CHUNK_LEN + 4;
145            #[allow(long_running_const_eval)]
146            const _ENCODED_BYTES: [u8; _ENCODED_BYTES_LEN] = flat_byte_slices([&_LEN_BYTES, &_CHUNKS]);
147            &_ENCODED_BYTES
148        });
149
150        // We already consumed the contents of included files when generating
151        // the custom section, but we want to make sure that updates to the
152        // generated files will cause this macro to rerun incrementally. To do
153        // that we use `include_str!` to force rustc to think it has a
154        // dependency on these files. That way when the file changes Cargo will
155        // automatically rerun rustc which will rerun this macro. Other than
156        // this we don't actually need the results of the `include_str!`, so
157        // it's just shoved into an anonymous static.
158        let file_dependencies = encoded.included_files.iter().map(|file| {
159            let file = file.to_str().unwrap();
160            quote! { include_str!(#file) }
161        });
162
163        let len = prefix_json.len() as u32;
164        let prefix_json_bytes = [&len.to_le_bytes()[..], prefix_json.as_bytes()].concat();
165        let prefix_json_bytes = syn::LitByteStr::new(&prefix_json_bytes, Span::call_site());
166
167        (quote! {
168            #[cfg(all(target_family = "wasm", not(target_os = "wasi")))]
169            #[automatically_derived]
170            const _: () = {
171                use #wasm_bindgen::__rt::{flat_len, flat_byte_slices};
172
173                static _INCLUDED_FILES: &[&str] = &[#(#file_dependencies),*];
174
175                const _ENCODED_BYTES: &[u8] = #encode_bytes;
176                const _PREFIX_JSON_BYTES: &[u8] = #prefix_json_bytes;
177                const _ENCODED_BYTES_LEN: usize  = _ENCODED_BYTES.len();
178                const _PREFIX_JSON_BYTES_LEN: usize =  _PREFIX_JSON_BYTES.len();
179                const _LEN: usize = _PREFIX_JSON_BYTES_LEN + _ENCODED_BYTES_LEN;
180
181                #[link_section = "__wasm_bindgen_unstable"]
182                #[allow(long_running_const_eval)]
183                static _GENERATED: [u8; _LEN] = flat_byte_slices([_PREFIX_JSON_BYTES, _ENCODED_BYTES]);
184            };
185        })
186        .to_tokens(tokens);
187
188        Ok(())
189    }
190}
191
192impl TryToTokens for ast::LinkToModule {
193    fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
194        let mut program_tokens = TokenStream::new();
195        self.0.try_to_tokens(&mut program_tokens)?;
196        let link_function_name = self.0.link_function_name(0);
197        let name = Ident::new(&link_function_name, Span::call_site());
198        let wasm_bindgen = &self.0.wasm_bindgen;
199        let abi_ret = quote! { #wasm_bindgen::convert::WasmRet<<#wasm_bindgen::__rt::alloc::string::String as #wasm_bindgen::convert::FromWasmAbi>::Abi> };
200        let extern_fn = extern_fn(&name, &[], &[], &[], abi_ret);
201        (quote! {
202            {
203                #program_tokens
204                #extern_fn
205
206                static __VAL: #wasm_bindgen::__rt::LazyLock<#wasm_bindgen::__rt::alloc::string::String> =
207                    #wasm_bindgen::__rt::LazyLock::new(|| unsafe {
208                        <#wasm_bindgen::__rt::alloc::string::String as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#name().join())
209                    });
210
211                #wasm_bindgen::__rt::alloc::string::String::clone(&__VAL)
212            }
213        })
214        .to_tokens(tokens);
215        Ok(())
216    }
217}
218
219impl ToTokens for ast::Struct {
220    fn to_tokens(&self, tokens: &mut TokenStream) {
221        let name = &self.rust_name;
222        let name_str = self.qualified_name.to_string();
223        let name_len = name_str.len() as u32;
224        let name_chars: Vec<u32> = name_str.chars().map(|c| c as u32).collect();
225        let new_fn = Ident::new(&shared::new_function(&name_str), Span::call_site());
226        let free_fn = Ident::new(&shared::free_function(&name_str), Span::call_site());
227        let unwrap_fn = Ident::new(&shared::unwrap_function(&name_str), Span::call_site());
228        let wasm_bindgen = &self.wasm_bindgen;
229        let class_abi = quote! {
230            #wasm_bindgen::__rt::WasmPtr<#wasm_bindgen::__rt::WasmRefCell<#name>>
231        };
232        (quote! {
233            #[automatically_derived]
234            impl #wasm_bindgen::__rt::marker::SupportsConstructor for #name {}
235            #[automatically_derived]
236            impl #wasm_bindgen::__rt::marker::SupportsInstanceProperty for #name {}
237            #[automatically_derived]
238            impl #wasm_bindgen::__rt::marker::SupportsStaticProperty for #name {}
239
240            #[automatically_derived]
241            impl #wasm_bindgen::describe::WasmDescribe for #name {
242                fn describe() {
243                    use #wasm_bindgen::describe::*;
244                    inform(RUST_STRUCT);
245                    inform(#name_len);
246                    #(inform(#name_chars);)*
247                }
248            }
249
250            #[automatically_derived]
251            impl #wasm_bindgen::convert::IntoWasmAbi for #name {
252                type Abi = #class_abi;
253
254                fn into_abi(self) -> Self::Abi {
255                    use #wasm_bindgen::__rt::alloc::rc::Rc;
256                    use #wasm_bindgen::__rt::{WasmPtr, WasmRefCell};
257                    WasmPtr::from_ptr(Rc::into_raw(Rc::new(WasmRefCell::new(self))) as *mut WasmRefCell<#name>)
258                }
259            }
260
261            #[automatically_derived]
262            impl #wasm_bindgen::convert::FromWasmAbi for #name {
263                type Abi = #class_abi;
264
265                unsafe fn from_abi(js: Self::Abi) -> Self {
266                    use #wasm_bindgen::__rt::alloc::rc::Rc;
267                    use #wasm_bindgen::__rt::core::result::Result::{Ok, Err};
268                    use #wasm_bindgen::__rt::{assert_not_null, WasmRefCell};
269
270                    let ptr = js.into_ptr();
271                    assert_not_null(ptr);
272                    let rc = Rc::from_raw(ptr);
273                    match Rc::try_unwrap(rc) {
274                        Ok(cell) => cell.into_inner(),
275                        Err(_) => #wasm_bindgen::throw_str(
276                            "attempted to take ownership of Rust value while it was borrowed"
277                        ),
278                    }
279                }
280            }
281
282            #[automatically_derived]
283            impl #wasm_bindgen::__rt::core::convert::From<#name> for
284                #wasm_bindgen::JsValue
285            {
286                fn from(value: #name) -> Self {
287                    let ptr = #wasm_bindgen::convert::IntoWasmAbi::into_abi(value);
288
289                    #[link(wasm_import_module = "__wbindgen_placeholder__")]
290                    #[cfg(all(target_family = "wasm", not(target_os = "wasi")))]
291                    extern "C" {
292                        fn #new_fn(ptr: #class_abi) -> u32;
293                    }
294
295                    #[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))]
296                    unsafe fn #new_fn(_: #class_abi) -> u32 {
297                        panic!("cannot convert to JsValue outside of the Wasm target")
298                    }
299
300                    unsafe {
301                        <#wasm_bindgen::JsValue as #wasm_bindgen::convert::FromWasmAbi>
302                            ::from_abi(#new_fn(ptr))
303                    }
304                }
305            }
306
307
308
309            #[cfg(all(target_family = "wasm", not(target_os = "wasi")))]
310            #[automatically_derived]
311            const _: () = {
312                #wasm_bindgen::__wbindgen_coverage! {
313                #[no_mangle]
314                #[doc(hidden)]
315                // `allow_delayed` is whether it's ok to not actually free the `ptr` immediately
316                // if it's still borrowed.
317                pub unsafe extern "C-unwind" fn #free_fn(ptr: #class_abi, allow_delayed: u32) {
318                    use #wasm_bindgen::__rt::alloc::rc::Rc;
319
320                    if allow_delayed != 0 {
321                        // Just drop the implicit `Rc` owned by JS, and then if the value is still
322                        // referenced it'll be kept alive by its other `Rc`s.
323                        let ptr = ptr.into_ptr();
324                        #wasm_bindgen::__rt::assert_not_null(ptr);
325                        drop(Rc::from_raw(ptr));
326                    } else {
327                        // Claim ownership of the value, which will panic if it's borrowed.
328                        let _ = <#name as #wasm_bindgen::convert::FromWasmAbi>::from_abi(ptr);
329                    }
330                }
331                }
332            };
333
334            #[automatically_derived]
335            impl #wasm_bindgen::convert::RefFromWasmAbi for #name {
336                type Abi = #class_abi;
337                type Anchor = #wasm_bindgen::__rt::RcRef<#name>;
338
339                unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor {
340                    use #wasm_bindgen::__rt::alloc::rc::Rc;
341
342                    let js = js.into_ptr();
343                    #wasm_bindgen::__rt::assert_not_null(js);
344
345                    Rc::increment_strong_count(js);
346                    let rc = Rc::from_raw(js);
347                    #wasm_bindgen::__rt::RcRef::new(rc)
348                }
349            }
350
351            #[automatically_derived]
352            impl #wasm_bindgen::convert::RefMutFromWasmAbi for #name {
353                type Abi = #class_abi;
354                type Anchor = #wasm_bindgen::__rt::RcRefMut<#name>;
355
356                unsafe fn ref_mut_from_abi(js: Self::Abi) -> Self::Anchor {
357                    use #wasm_bindgen::__rt::alloc::rc::Rc;
358
359                    let js = js.into_ptr();
360                    #wasm_bindgen::__rt::assert_not_null(js);
361
362                    Rc::increment_strong_count(js);
363                    let rc = Rc::from_raw(js);
364                    #wasm_bindgen::__rt::RcRefMut::new(rc)
365                }
366            }
367
368            #[automatically_derived]
369            impl #wasm_bindgen::convert::LongRefFromWasmAbi for #name {
370                type Abi = #class_abi;
371                type Anchor = #wasm_bindgen::__rt::RcRef<#name>;
372
373                unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor {
374                    <Self as #wasm_bindgen::convert::RefFromWasmAbi>::ref_from_abi(js)
375                }
376            }
377
378            #[automatically_derived]
379            impl #wasm_bindgen::convert::OptionIntoWasmAbi for #name {
380                #[inline]
381                fn none() -> Self::Abi { <#class_abi>::null() }
382            }
383
384            #[automatically_derived]
385            impl #wasm_bindgen::convert::OptionFromWasmAbi for #name {
386                #[inline]
387                fn is_none(abi: &Self::Abi) -> bool { abi.is_null() }
388            }
389
390            #[automatically_derived]
391            impl #wasm_bindgen::convert::TryFromJsValue for #name {
392                fn try_from_js_value(value: #wasm_bindgen::JsValue) -> #wasm_bindgen::__rt::core::result::Result<Self, #wasm_bindgen::JsValue> {
393                    Self::try_from_js_value_ref(&value).ok_or(value)
394                }
395                fn try_from_js_value_ref(value: &#wasm_bindgen::JsValue) -> #wasm_bindgen::__rt::core::option::Option<Self> {
396                    let idx = #wasm_bindgen::convert::IntoWasmAbi::into_abi(value);
397
398                    #[link(wasm_import_module = "__wbindgen_placeholder__")]
399                    #[cfg(all(target_family = "wasm", not(target_os = "wasi")))]
400                    extern "C" {
401                        fn #unwrap_fn(ptr: u32) -> #class_abi;
402                    }
403
404                    #[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))]
405                    unsafe fn #unwrap_fn(_: u32) -> #class_abi {
406                        panic!("cannot convert from JsValue outside of the Wasm target")
407                    }
408
409                    let ptr = unsafe { #unwrap_fn(idx) };
410                    if ptr.is_null() {
411                        #wasm_bindgen::__rt::core::option::Option::None
412                    } else {
413                        unsafe {
414                            #wasm_bindgen::__rt::core::option::Option::Some(
415                                <Self as #wasm_bindgen::convert::FromWasmAbi>::from_abi(ptr)
416                            )
417                        }
418                    }
419                }
420            }
421
422            #[automatically_derived]
423            impl #wasm_bindgen::describe::WasmDescribeVector for #name {
424                fn describe_vector() {
425                    use #wasm_bindgen::describe::*;
426                    inform(VECTOR);
427                    inform(NAMED_EXTERNREF);
428                    inform(#name_len);
429                    #(inform(#name_chars);)*
430                }
431            }
432
433            #[automatically_derived]
434            impl #wasm_bindgen::convert::VectorIntoWasmAbi for #name {
435                type Abi = <
436                    #wasm_bindgen::__rt::alloc::boxed::Box<[#wasm_bindgen::JsValue]>
437                    as #wasm_bindgen::convert::IntoWasmAbi
438                >::Abi;
439
440                fn vector_into_abi(
441                    vector: #wasm_bindgen::__rt::alloc::boxed::Box<[#name]>
442                ) -> Self::Abi {
443                    #wasm_bindgen::convert::js_value_vector_into_abi(vector)
444                }
445            }
446
447            #[automatically_derived]
448            impl #wasm_bindgen::convert::VectorFromWasmAbi for #name {
449                type Abi = <
450                    #wasm_bindgen::__rt::alloc::boxed::Box<[#wasm_bindgen::JsValue]>
451                    as #wasm_bindgen::convert::FromWasmAbi
452                >::Abi;
453
454                unsafe fn vector_from_abi(
455                    js: Self::Abi
456                ) -> #wasm_bindgen::__rt::alloc::boxed::Box<[#name]> {
457                    #wasm_bindgen::convert::js_value_vector_from_abi(js)
458                }
459            }
460        })
461        .to_tokens(tokens);
462
463        // If this struct `extends` another exported Rust struct, emit:
464        //
465        //   - `AsRef<Parent<ParentType>>` projecting to the wrapper field
466        //     so generic code can accept any direct child where it expects
467        //     a borrowed `Parent<ParentType>`. This impl is direct-parent
468        //     only: `AsRef` returns `&Parent<P>` borrowed from `&self`,
469        //     and ancestors at depth ≥ 2 live inside an `Rc<WasmRefCell>`
470        //     reachable only via a transient `borrow()` guard whose
471        //     lifetime would not satisfy the `AsRef` contract.
472        //   - The upcast wasm export used by the JS side to produce a
473        //     separately-refcounted parent pointer when a child instance is
474        //     constructed (or when wasm returns a child back to JS). The
475        //     upcast clones the `Rc<WasmRefCell<Parent>>` held by the
476        //     child's `Parent<ParentType>` field.
477        //
478        // The JS-side of the extends relationship (class Child extends
479        // Parent, instanceof, prototype-chain dispatch) is wired up by
480        // cli-support using this export and the matching `extends` schema
481        // entry.
482        if let Some(parent_path) = &self.extends {
483            let parent_field = self.fields.iter().find(|f| f.is_parent);
484            if let Some(parent_field) = parent_field {
485                let field_name = &parent_field.rust_name;
486                let field_ty = &parent_field.ty;
487                // The upcast shim symbol must encode the parent's JS-side
488                // identity (`extends_js_class` / `extends_js_namespace`),
489                // not its Rust path, so that cli-support (which keys
490                // `exported_classes` by qualified_name) and the macro
491                // agree on the wasm symbol name. Defaults to the last
492                // segment of the `extends` path (matching the no-rename
493                // case).
494                let parent_bare_name = self
495                    .extends_js_class
496                    .clone()
497                    .or_else(|| parent_path.segments.last().map(|s| s.ident.to_string()))
498                    .unwrap_or_default();
499                let parent_qualified =
500                    shared::qualified_name(self.extends_js_namespace.as_deref(), &parent_bare_name);
501                let upcast_fn = Ident::new(
502                    &shared::upcast_function(&name_str, &parent_qualified),
503                    Span::call_site(),
504                );
505                (quote! {
506                    #[automatically_derived]
507                    impl #wasm_bindgen::__rt::core::convert::AsRef<#field_ty> for #name {
508                        #[inline]
509                        fn as_ref(&self) -> &#field_ty {
510                            &self.#field_name
511                        }
512                    }
513
514                    #[cfg(all(target_family = "wasm", not(target_os = "wasi")))]
515                    #[automatically_derived]
516                    const _: () = {
517                        #[no_mangle]
518                        #[doc(hidden)]
519                        pub unsafe extern "C-unwind" fn #upcast_fn(ptr: u32) -> u32 {
520                            use #wasm_bindgen::__rt::alloc::rc::Rc;
521                            use #wasm_bindgen::__rt::{assert_not_null, WasmRefCell};
522
523                            let ptr = ptr as *mut WasmRefCell<#name>;
524                            assert_not_null(ptr);
525                            let cell = &*ptr;
526                            let rc_clone = cell.borrow().#field_name.__wbg_clone_rc();
527                            Rc::into_raw(rc_clone) as u32
528                        }
529                    };
530                })
531                .to_tokens(tokens);
532            }
533        }
534
535        for field in self.fields.iter() {
536            field.to_tokens(tokens);
537        }
538    }
539}
540
541impl ToTokens for ast::StructField {
542    fn to_tokens(&self, tokens: &mut TokenStream) {
543        // Parent fields exist solely to back the `extends` relationship
544        // (used by `AsRef`/`Deref` codegen above). They are not exposed to
545        // JS as a getter/setter.
546        if self.is_parent {
547            return;
548        }
549
550        let rust_name = &self.rust_name;
551        let struct_name = &self.struct_name;
552        let ty = &self.ty;
553        let getter = &self.getter;
554        let setter = &self.setter;
555
556        let maybe_assert_copy = if self.getter_with_clone.is_some() {
557            quote! {}
558        } else {
559            quote! { assert_copy::<#ty>() }
560        };
561        let maybe_assert_copy = respan(maybe_assert_copy, ty);
562
563        // Split this out so that it isn't affected by `quote_spanned!`.
564        //
565        // If we don't do this, it might end up being unable to reference `js`
566        // properly because it doesn't have the same span.
567        //
568        // See https://github.com/wasm-bindgen/wasm-bindgen/pull/3725.
569        let js_token = quote! { js };
570        let mut val = quote_spanned!(self.rust_name.span()=> (*#js_token).borrow().#rust_name);
571        if let Some(span) = self.getter_with_clone {
572            val = quote_spanned!(span=> <#ty as Clone>::clone(&#val) );
573        }
574
575        let wasm_bindgen = &self.wasm_bindgen;
576        let struct_abi = quote! {
577            #wasm_bindgen::__rt::WasmPtr<#wasm_bindgen::__rt::WasmRefCell<#struct_name>>
578        };
579
580        (quote! {
581            #[automatically_derived]
582            const _: () = {
583                #wasm_bindgen::__wbindgen_coverage! {
584                #[cfg_attr(all(target_family = "wasm", not(target_os = "wasi")), no_mangle)]
585                #[doc(hidden)]
586                pub unsafe extern "C-unwind" fn #getter(js: #struct_abi)
587                    -> #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::IntoWasmAbi>::Abi>
588                {
589                    use #wasm_bindgen::__rt::{WasmRefCell, assert_not_null};
590                    use #wasm_bindgen::convert::IntoWasmAbi;
591
592                    fn assert_copy<T: Copy>(){}
593                    #maybe_assert_copy;
594
595                    let js = js.into_ptr();
596                    assert_not_null(js);
597                    let val = #val;
598                    <#ty as IntoWasmAbi>::into_abi(val).into()
599                }
600                }
601            };
602        })
603        .to_tokens(tokens);
604
605        Descriptor {
606            ident: getter,
607            inner: quote! {
608                <#ty as WasmDescribe>::describe();
609            },
610            attrs: vec![],
611            wasm_bindgen: &self.wasm_bindgen,
612        }
613        .to_tokens(tokens);
614
615        if self.readonly {
616            return;
617        }
618
619        let abi = quote! { <#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi };
620        let (args, names) = splat(wasm_bindgen, &Ident::new("val", rust_name.span()), &abi);
621
622        (quote! {
623            #[cfg(all(target_family = "wasm", not(target_os = "wasi")))]
624            #[automatically_derived]
625            const _: () = {
626                #wasm_bindgen::__wbindgen_coverage! {
627                #[no_mangle]
628                #[doc(hidden)]
629                pub unsafe extern "C-unwind" fn #setter(
630                    js: #struct_abi,
631                    #(#args,)*
632                ) {
633                    use #wasm_bindgen::__rt::{WasmRefCell, assert_not_null};
634                    use #wasm_bindgen::convert::FromWasmAbi;
635
636                    let js = js.into_ptr();
637                    assert_not_null(js);
638                    let val = <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#names),*);
639                    let val = <#ty as FromWasmAbi>::from_abi(val);
640                    (*js).borrow_mut().#rust_name = val;
641                }
642                }
643            };
644        })
645        .to_tokens(tokens);
646    }
647}
648
649impl TryToTokens for ast::Export {
650    fn try_to_tokens(self: &ast::Export, into: &mut TokenStream) -> Result<(), Diagnostic> {
651        let generated_name = self.rust_symbol();
652        let export_name = self.export_name();
653        let mut args = vec![];
654        let mut arg_conversions = vec![];
655        let mut converted_arguments = vec![];
656        let ret = Ident::new("_ret", Span::call_site());
657
658        let name = &self.rust_name;
659        let wasm_bindgen = &self.wasm_bindgen;
660
661        let offset = if self.method_self.is_some() {
662            if matches!(self.method_self, Some(ast::MethodSelf::ByValue)) {
663                let class = self.rust_class.as_ref().unwrap();
664                args.push(quote! { me: <#class as #wasm_bindgen::convert::FromWasmAbi>::Abi });
665            } else {
666                let class = self.rust_class.as_ref().unwrap();
667                let abi = match self.method_self {
668                    Some(ast::MethodSelf::RefMutable) => {
669                        quote! { <#class as #wasm_bindgen::convert::RefMutFromWasmAbi>::Abi }
670                    }
671                    Some(ast::MethodSelf::RefShared) => {
672                        if self.function.r#async {
673                            quote! { <#class as #wasm_bindgen::convert::LongRefFromWasmAbi>::Abi }
674                        } else {
675                            quote! { <#class as #wasm_bindgen::convert::RefFromWasmAbi>::Abi }
676                        }
677                    }
678                    _ => unreachable!(),
679                };
680                args.push(quote! { me: #abi });
681            }
682            1
683        } else {
684            0
685        };
686        let wasm_bindgen_futures = &self.wasm_bindgen_futures;
687        let js_sys = &self.js_sys;
688        let futures = if ast::use_js_sys_futures() {
689            quote! { #js_sys::futures }
690        } else {
691            quote! { #wasm_bindgen_futures }
692        };
693        let receiver = match self.method_self {
694            Some(ast::MethodSelf::ByValue) => {
695                let class = self.rust_class.as_ref().unwrap();
696                arg_conversions.push(quote! {
697                    // Owned `self` is consumed inside the catch-unwind closure;
698                    // assert it's `UnwindSafe` so a panic mid-method doesn't
699                    // surface a half-modified observable value to the caller.
700                    #wasm_bindgen::__rt::ensure_unwind_safe::<#class>();
701                    let me = unsafe {
702                        <#class as #wasm_bindgen::convert::FromWasmAbi>::from_abi(me)
703                    };
704                });
705                quote! { me.#name }
706            }
707            Some(ast::MethodSelf::RefMutable) => {
708                let class = self.rust_class.as_ref().unwrap();
709                arg_conversions.push(quote! {
710                    // `&mut self` requires `Self: RefUnwindSafe` (logical
711                    // unwind-safety): if the method panics partway through
712                    // mutation, the caller may observe the struct again, so
713                    // any interior mutability whose invariants could be
714                    // broken must be opt-in via `AssertUnwindSafe` or a
715                    // manual `impl RefUnwindSafe`. Stdlib's `&mut T:
716                    // !UnwindSafe` blanket would otherwise reject every
717                    // `&mut self` method, so we use a separate type-level
718                    // assertion rather than relying on closure capture
719                    // inference.
720                    #wasm_bindgen::__rt::ensure_ref_unwind_safe::<#class>();
721                    let mut me = unsafe {
722                        <#class as #wasm_bindgen::convert::RefMutFromWasmAbi>
723                            ::ref_mut_from_abi(me)
724                    };
725                    let me = &mut *me;
726                });
727                quote! { me.#name }
728            }
729            Some(ast::MethodSelf::RefShared) => {
730                let class = self.rust_class.as_ref().unwrap();
731                let (trait_, func, borrow) = if self.function.r#async {
732                    (
733                        quote!(LongRefFromWasmAbi),
734                        quote!(long_ref_from_abi),
735                        quote!(
736                            <<#class as #wasm_bindgen::convert::LongRefFromWasmAbi>
737                                ::Anchor as #wasm_bindgen::__rt::core::borrow::Borrow<#class>>
738                                ::borrow(&me)
739                        ),
740                    )
741                } else {
742                    (quote!(RefFromWasmAbi), quote!(ref_from_abi), quote!(&*me))
743                };
744                arg_conversions.push(quote! {
745                    // `&self` requires `Self: RefUnwindSafe` for the same
746                    // reason as `&mut self` — a panic mid-method can leave
747                    // interior-mutable state in a torn condition observable
748                    // by subsequent calls.
749                    #wasm_bindgen::__rt::ensure_ref_unwind_safe::<#class>();
750                    let me = unsafe {
751                        <#class as #wasm_bindgen::convert::#trait_>::#func(me)
752                    };
753                    let me = #borrow;
754                });
755                quote! { me.#name }
756            }
757            None => match &self.rust_class {
758                Some(class) => quote! { #class::#name },
759                None => quote! { #name },
760            },
761        };
762
763        let mut argtys = Vec::new();
764        for (i, arg) in self.function.arguments.iter().enumerate() {
765            argtys.push(&*arg.pat_type.ty);
766            let i = i + offset;
767            let ident = Ident::new(&format!("arg{i}"), Span::call_site());
768            fn unwrap_nested_types(ty: &syn::Type) -> &syn::Type {
769                match &ty {
770                    syn::Type::Group(syn::TypeGroup { ref elem, .. }) => unwrap_nested_types(elem),
771                    syn::Type::Paren(syn::TypeParen { ref elem, .. }) => unwrap_nested_types(elem),
772                    _ => ty,
773                }
774            }
775            let ty = unwrap_nested_types(&arg.pat_type.ty);
776
777            match &ty {
778                syn::Type::Reference(syn::TypeReference {
779                    mutability: Some(_),
780                    elem,
781                    ..
782                }) => {
783                    let abi = quote! { <#elem as #wasm_bindgen::convert::RefMutFromWasmAbi>::Abi };
784                    let (prim_args, prim_names) = splat(wasm_bindgen, &ident, &abi);
785                    args.extend(prim_args);
786                    arg_conversions.push(quote! {
787                        // `&mut T` arg: same logical-unwind-safety check as
788                        // `&mut self` — `T` must be `RefUnwindSafe` so any
789                        // panic mid-call cannot leave torn interior state.
790                        #wasm_bindgen::__rt::ensure_ref_unwind_safe::<#elem>();
791                        let mut #ident = unsafe {
792                            <#elem as #wasm_bindgen::convert::RefMutFromWasmAbi>
793                                ::ref_mut_from_abi(
794                                    <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#prim_names),*)
795                                )
796                        };
797                        let #ident = &mut *#ident;
798                    });
799                }
800                syn::Type::Reference(syn::TypeReference { elem, .. }) => {
801                    if self.function.r#async {
802                        let abi =
803                            quote! { <#elem as #wasm_bindgen::convert::LongRefFromWasmAbi>::Abi };
804                        let (prim_args, prim_names) = splat(wasm_bindgen, &ident, &abi);
805                        args.extend(prim_args);
806                        arg_conversions.push(quote! {
807                            // `&T` arg in async export: enforce
808                            // `T: RefUnwindSafe` for the same reason.
809                            #wasm_bindgen::__rt::ensure_ref_unwind_safe::<#elem>();
810                            let #ident = unsafe {
811                                <#elem as #wasm_bindgen::convert::LongRefFromWasmAbi>
812                                    ::long_ref_from_abi(
813                                        <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#prim_names),*)
814                                    )
815                            };
816                            let #ident = <<#elem as #wasm_bindgen::convert::LongRefFromWasmAbi>
817                                ::Anchor as core::borrow::Borrow<#elem>>
818                                ::borrow(&#ident);
819                        });
820                    } else {
821                        let abi = quote! { <#elem as #wasm_bindgen::convert::RefFromWasmAbi>::Abi };
822                        let (prim_args, prim_names) = splat(wasm_bindgen, &ident, &abi);
823                        args.extend(prim_args);
824                        arg_conversions.push(quote! {
825                            // `&T` arg: enforce `T: RefUnwindSafe`.
826                            #wasm_bindgen::__rt::ensure_ref_unwind_safe::<#elem>();
827                            let #ident = unsafe {
828                                <#elem as #wasm_bindgen::convert::RefFromWasmAbi>
829                                    ::ref_from_abi(
830                                        <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#prim_names),*)
831                                    )
832                            };
833                            let #ident = &*#ident;
834                        });
835                    }
836                }
837                _ => {
838                    let abi = quote! { <#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi };
839                    let (prim_args, prim_names) = splat(wasm_bindgen, &ident, &abi);
840                    args.extend(prim_args);
841                    arg_conversions.push(quote! {
842                        // Owned arg: consumed locally inside the catch-unwind
843                        // closure, so `UnwindSafe` (not `RefUnwindSafe`) is
844                        // the relevant property.
845                        #wasm_bindgen::__rt::ensure_unwind_safe::<#ty>();
846                        let #ident = unsafe {
847                            <#ty as #wasm_bindgen::convert::FromWasmAbi>
848                                ::from_abi(
849                                    <#abi as #wasm_bindgen::convert::WasmAbi>::join(#(#prim_names),*)
850                                )
851                        };
852                    });
853                }
854            }
855            converted_arguments.push(quote! { #ident });
856        }
857        let syn_unit = syn::Type::Tuple(syn::TypeTuple {
858            elems: Default::default(),
859            paren_token: Default::default(),
860        });
861        let syn_ret = self
862            .function
863            .ret
864            .as_ref()
865            .map(|ret| &ret.r#type)
866            .unwrap_or(&syn_unit);
867        if let syn::Type::Reference(_) = syn_ret {
868            bail_span!(syn_ret, "cannot return a borrowed ref with #[wasm_bindgen]",)
869        }
870
871        // For an `async` function we always run it through `future_to_promise`
872        // since we're returning a promise to JS, and this will implicitly
873        // require that the function returns a `Future<Output = Result<...>>`
874        let (ret_ty, inner_ret_ty, ret_expr) = if self.function.r#async {
875            if self.start.is_start() {
876                (
877                    quote! { () },
878                    quote! { () },
879                    quote! {
880                        <#syn_ret as #wasm_bindgen::__rt::Start>::start(#ret.await)
881                    },
882                )
883            } else {
884                (
885                    quote! { #wasm_bindgen::JsValue },
886                    quote! { #syn_ret },
887                    quote! {
888                        <#syn_ret as #wasm_bindgen::__rt::IntoJsResult>::into_js_result(#ret.await)
889                    },
890                )
891            }
892        } else if self.start.is_start() {
893            (
894                quote! { () },
895                quote! { () },
896                quote! { <#syn_ret as #wasm_bindgen::__rt::Start>::start(#ret) },
897            )
898        } else {
899            (quote! { #syn_ret }, quote! { #syn_ret }, quote! { #ret })
900        };
901
902        let mut call = quote! {
903            {
904                #(#arg_conversions)*
905                let #ret = #receiver(#(#converted_arguments),*);
906                #ret_expr
907            }
908        };
909
910        if self.function.r#async {
911            if self.start.is_start() {
912                call = quote! {
913                    #futures::spawn_local(async move {
914                        #call
915                    })
916                }
917            } else {
918                call = quote! {
919                    #futures::future_to_promise(async move {
920                        #call
921                    }).into()
922                }
923            }
924        } else {
925            call = quote! {
926                #wasm_bindgen::__rt::maybe_catch_unwind(|| {
927                    #call
928                })
929            };
930        }
931
932        let projection = quote! { <#ret_ty as #wasm_bindgen::convert::ReturnWasmAbi> };
933        let convert_ret = quote! { #projection::return_abi(#ret).into() };
934        let describe_ret = quote! {
935            <#ret_ty as WasmDescribe>::describe();
936            <#inner_ret_ty as WasmDescribe>::describe();
937        };
938        let nargs = self.function.arguments.len() as u32;
939        let attrs = self
940            .function
941            .rust_attrs
942            .iter()
943            .map(|attr| match &attr.meta {
944                Meta::List(list @ MetaList { path, .. }) if path.is_ident("expect") => {
945                    let list = MetaList {
946                        path: parse_quote!(allow),
947                        ..list.clone()
948                    };
949                    Attribute {
950                        meta: Meta::List(list),
951                        ..*attr
952                    }
953                }
954                _ => attr.clone(),
955            })
956            .collect::<Vec<_>>();
957
958        let mut checks = Vec::new();
959        if self.start.is_start() {
960            checks.push(quote! { const _ASSERT: fn() = || -> #projection::Abi { loop {} }; });
961        };
962
963        if let Some(class) = self.rust_class.as_ref() {
964            // little helper function to make sure the check points to the
965            // location of the function causing the assert to fail
966            let mut add_check = |token_stream| {
967                checks.push(respan(token_stream, &self.rust_name));
968            };
969
970            match &self.method_kind {
971                ast::MethodKind::Constructor => {
972                    add_check(quote! {
973                        let _: #wasm_bindgen::__rt::marker::CheckSupportsConstructor<#class>;
974                    });
975
976                    if self.function.r#async {
977                        (quote_spanned! {
978                            self.function.name_span =>
979                            const _: () = {
980                                #[deprecated(note = "async constructors produce invalid TS code and support will be removed in the future")]
981                                const fn constructor() {}
982                                constructor();
983                            };
984                        })
985                        .to_tokens(into);
986                    }
987                }
988                ast::MethodKind::Operation(operation) => match operation.kind {
989                    ast::OperationKind::Getter(_) | ast::OperationKind::Setter(_) => {
990                        if operation.is_static {
991                            add_check(quote! {
992                                let _: #wasm_bindgen::__rt::marker::CheckSupportsStaticProperty<#class>;
993                            });
994                        } else {
995                            add_check(quote! {
996                                let _: #wasm_bindgen::__rt::marker::CheckSupportsInstanceProperty<#class>;
997                            });
998                        }
999                    }
1000                    _ => {}
1001                },
1002            }
1003        }
1004
1005        (quote! {
1006            #[automatically_derived]
1007            const _: () = {
1008                #wasm_bindgen::__wbindgen_coverage! {
1009                #(#attrs)*
1010                #[cfg_attr(
1011                    all(target_family = "wasm", not(target_os = "wasi")),
1012                    export_name = #export_name,
1013                )]
1014                pub unsafe extern "C-unwind" fn #generated_name(#(#args),*) -> #wasm_bindgen::convert::WasmRet<#projection::Abi> {
1015                    const _: () = {
1016                        #(#checks)*
1017                    };
1018
1019                    let #ret = #call;
1020                    #convert_ret
1021                }
1022                }
1023            };
1024        })
1025        .to_tokens(into);
1026
1027        let describe_args: TokenStream = argtys
1028            .iter()
1029            .map(|ty| match ty {
1030                syn::Type::Reference(reference)
1031                    if self.function.r#async && reference.mutability.is_none() =>
1032                {
1033                    let inner = &reference.elem;
1034                    quote! {
1035                        inform(LONGREF);
1036                        <#inner as WasmDescribe>::describe();
1037                    }
1038                }
1039                _ => quote! { <#ty as WasmDescribe>::describe(); },
1040            })
1041            .collect();
1042
1043        // In addition to generating the shim function above which is what
1044        // our generated JS will invoke, we *also* generate a "descriptor"
1045        // shim. This descriptor shim uses the `WasmDescribe` trait to
1046        // programmatically describe the type signature of the generated
1047        // shim above. This in turn is then used to inform the
1048        // `wasm-bindgen` CLI tool exactly what types and such it should be
1049        // using in JS.
1050        //
1051        // Note that this descriptor function is a purely an internal detail
1052        // of `#[wasm_bindgen]` and isn't intended to be exported to anyone
1053        // or actually part of the final was binary. Additionally, this is
1054        // literally executed when the `wasm-bindgen` tool executes.
1055        //
1056        // In any case, there's complications in `wasm-bindgen` to handle
1057        // this, but the tl;dr; is that this is stripped from the final wasm
1058        // binary along with anything it references.
1059        let export = Ident::new(&export_name, Span::call_site());
1060        Descriptor {
1061            ident: &export,
1062            inner: quote! {
1063                inform(FUNCTION);
1064                inform(0);
1065                inform(#nargs);
1066                #describe_args
1067                #describe_ret
1068            },
1069            attrs,
1070            wasm_bindgen: &self.wasm_bindgen,
1071        }
1072        .to_tokens(into);
1073
1074        Ok(())
1075    }
1076}
1077
1078impl TryToTokens for ast::ImportKind {
1079    fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
1080        match *self {
1081            ast::ImportKind::Function(ref f) => f.try_to_tokens(tokens)?,
1082            ast::ImportKind::Static(ref s) => s.to_tokens(tokens),
1083            ast::ImportKind::String(ref s) => s.to_tokens(tokens),
1084            ast::ImportKind::Type(ref t) => t.try_to_tokens(tokens)?,
1085            ast::ImportKind::Enum(ref e) => e.to_tokens(tokens),
1086            ast::ImportKind::DynamicUnion(ref e) => e.to_tokens(tokens),
1087        }
1088
1089        Ok(())
1090    }
1091}
1092
1093impl TryToTokens for ast::ImportType {
1094    fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
1095        let vis = &self.vis;
1096        let rust_name = &self.rust_name;
1097        let attrs = &self.attrs;
1098        let doc_comment = match &self.doc_comment {
1099            None => "",
1100            Some(comment) => comment,
1101        };
1102        let instanceof_shim = Ident::new(&self.instanceof_shim, Span::call_site());
1103
1104        let wasm_bindgen = &self.wasm_bindgen;
1105        let internal_obj = match self.extends.first() {
1106            Some(target) => {
1107                quote! { #target }
1108            }
1109            None => {
1110                quote! { #wasm_bindgen::JsValue }
1111            }
1112        };
1113
1114        let description = if let Some(typescript_type) = &self.typescript_type {
1115            let typescript_type_len = typescript_type.len() as u32;
1116            let typescript_type_chars = typescript_type.chars().map(|c| c as u32);
1117            quote! {
1118                use #wasm_bindgen::describe::*;
1119                inform(NAMED_EXTERNREF);
1120                inform(#typescript_type_len);
1121                #(inform(#typescript_type_chars);)*
1122            }
1123        } else {
1124            quote! {
1125                JsValue::describe()
1126            }
1127        };
1128
1129        let is_type_of = self.is_type_of.as_ref().map(|is_type_of| {
1130            quote! {
1131                #[inline]
1132                fn is_type_of(val: &JsValue) -> bool {
1133                    let is_type_of: fn(&JsValue) -> bool = #is_type_of;
1134                    is_type_of(val)
1135                }
1136            }
1137        });
1138
1139        let no_deref = self.no_deref;
1140        let no_promising = self.no_promising;
1141        let no_into_js_generic = self.no_into_js_generic;
1142
1143        let doc = if doc_comment.is_empty() {
1144            quote! {}
1145        } else {
1146            quote! {
1147                #[doc = #doc_comment]
1148            }
1149        };
1150
1151        let class_generic_params = generics::generic_params(&self.generics);
1152        let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
1153
1154        let type_params_with_bounds = generics::type_params_with_bounds(&self.generics);
1155        let impl_generics_with_lifetime_a = if type_params_with_bounds.is_empty() {
1156            quote! { <'a> }
1157        } else {
1158            quote! { <'a, #(#type_params_with_bounds),*> }
1159        };
1160
1161        // For struct definitions, we need generics with defaults, so use params directly
1162        let struct_generics = if self.generics.params.is_empty() {
1163            quote! {}
1164        } else {
1165            let params = &self.generics.params;
1166            quote! { <#params> }
1167        };
1168
1169        let phantom;
1170        let phantom_init;
1171        let lifetime_params = generics::lifetime_params(&self.generics);
1172
1173        // For `From<JsValue>`, only include lifetime params so type params
1174        // fall back to their defaults and callers don't need turbofish.
1175        let from_jsvalue_generics = if lifetime_params.is_empty() {
1176            quote! {}
1177        } else {
1178            quote! { <#(#lifetime_params),*> }
1179        };
1180
1181        if !class_generic_params.is_empty() || !lifetime_params.is_empty() {
1182            let generic_param_names: Vec<_> = class_generic_params.iter().map(|p| p.0).collect();
1183            let lifetime_refs = lifetime_params.iter().map(|lt| quote! { &#lt () });
1184            phantom = quote! {
1185                generics: ::core::marker::PhantomData<(#(#generic_param_names,)* #(#lifetime_refs),*)>
1186            };
1187            phantom_init = quote! { generics: ::core::marker::PhantomData };
1188        } else {
1189            phantom = quote! {};
1190            phantom_init = quote! {};
1191        }
1192
1193        // Identity implementation of `IntoJsGeneric`. Declaring this per-type,
1194        // rather than via a blanket over `T: JsGeneric`, preserves the option
1195        // for future wrapper types to pick a non-identity `JsCanon`.
1196        //
1197        // The body takes `self` by value and reinterprets the transparent JS
1198        // handle wrapper into its canonical type. This lets the impl apply
1199        // uniformly to types that do not implement Rust-level `Clone` (e.g.
1200        // generic types whose parameters aren't `Clone`, or plain handle
1201        // wrappers that simply don't derive `Clone`).
1202        //
1203        // Types whose Rust wrapper enforces owned-once destruction semantics
1204        // (currently just `JsClosure`) opt out via the
1205        // `#[wasm_bindgen(no_into_js_generic)]` attribute — producing a
1206        // duplicate wrapper over the same handle would violate those semantics.
1207        //
1208        // The extra `Self: JsGeneric` predicate propagates any generic
1209        // type-parameter requirements the `JsGeneric` blanket imposes
1210        // through `ErasableGeneric<Repr = JsValue>` etc.
1211        let into_js_generic_impl = if no_into_js_generic {
1212            quote! {}
1213        } else {
1214            let mut clause =
1215                self.generics
1216                    .where_clause
1217                    .clone()
1218                    .unwrap_or_else(|| syn::WhereClause {
1219                        where_token: Default::default(),
1220                        predicates: Default::default(),
1221                    });
1222            let self_ty_generics = &ty_generics;
1223            let self_ty: syn::Type = syn::parse_quote!(#rust_name #self_ty_generics);
1224            let wasm_bindgen_path: syn::Path = syn::parse_quote!(#wasm_bindgen);
1225            clause.predicates.push(syn::parse_quote!(
1226                #self_ty: #wasm_bindgen_path::JsGeneric
1227            ));
1228            quote! {
1229                #[automatically_derived]
1230                impl #impl_generics #wasm_bindgen::IntoJsGeneric
1231                    for #rust_name #ty_generics
1232                #clause
1233                {
1234                    type JsCanon = #rust_name #ty_generics;
1235                    #[inline]
1236                    fn to_js(self) -> #rust_name #ty_generics {
1237            unsafe { core::mem::transmute_copy(&core::mem::ManuallyDrop::new(self)) }
1238                    }
1239                }
1240            }
1241        };
1242
1243        (quote! {
1244            #(#attrs)*
1245            #doc
1246            #[repr(transparent)]
1247            #vis struct #rust_name #struct_generics #where_clause {
1248                obj: #internal_obj,
1249                #phantom
1250            }
1251
1252            #[automatically_derived]
1253            const _: () = {
1254                use #wasm_bindgen::convert::TryFromJsValue;
1255                use #wasm_bindgen::convert::{IntoWasmAbi, FromWasmAbi};
1256                use #wasm_bindgen::convert::{OptionIntoWasmAbi, OptionFromWasmAbi};
1257                use #wasm_bindgen::convert::{RefFromWasmAbi, LongRefFromWasmAbi};
1258                use #wasm_bindgen::describe::WasmDescribe;
1259                use #wasm_bindgen::{JsValue, JsCast};
1260                use #wasm_bindgen::__rt::{core, marker::ErasableGeneric};
1261
1262                #[automatically_derived]
1263                impl #impl_generics WasmDescribe for #rust_name #ty_generics #where_clause {
1264                    fn describe() {
1265                        #description
1266                    }
1267                }
1268
1269                #[automatically_derived]
1270                impl #impl_generics IntoWasmAbi for #rust_name #ty_generics #where_clause {
1271                    type Abi = <JsValue as IntoWasmAbi>::Abi;
1272
1273                    #[inline]
1274                    fn into_abi(self) -> Self::Abi {
1275                        self.obj.into_abi()
1276                    }
1277                }
1278
1279                #[automatically_derived]
1280                impl #impl_generics OptionIntoWasmAbi for #rust_name #ty_generics #where_clause {
1281                    #[inline]
1282                    fn none() -> Self::Abi {
1283                        0
1284                    }
1285                }
1286
1287                #[automatically_derived]
1288                impl #impl_generics_with_lifetime_a OptionIntoWasmAbi for &'a #rust_name #ty_generics #where_clause {
1289                    #[inline]
1290                    fn none() -> Self::Abi {
1291                        0
1292                    }
1293                }
1294
1295                #[automatically_derived]
1296                impl #impl_generics FromWasmAbi for #rust_name #ty_generics #where_clause {
1297                    type Abi = <JsValue as FromWasmAbi>::Abi;
1298
1299                    #[inline]
1300                    unsafe fn from_abi(js: Self::Abi) -> Self {
1301                        #rust_name {
1302                            obj: JsValue::from_abi(js).into(),
1303                            #phantom_init
1304                        }
1305                    }
1306                }
1307
1308                #[automatically_derived]
1309                impl #impl_generics OptionFromWasmAbi for #rust_name #ty_generics #where_clause {
1310                    #[inline]
1311                    fn is_none(abi: &Self::Abi) -> bool { *abi == 0 }
1312                }
1313
1314                #[automatically_derived]
1315                impl #impl_generics_with_lifetime_a IntoWasmAbi for &'a #rust_name #ty_generics #where_clause {
1316                    type Abi = <&'a JsValue as IntoWasmAbi>::Abi;
1317
1318                    #[inline]
1319                    fn into_abi(self) -> Self::Abi {
1320                        (&self.obj).into_abi()
1321                    }
1322                }
1323
1324                #[automatically_derived]
1325                impl #impl_generics RefFromWasmAbi for #rust_name #ty_generics #where_clause {
1326                    type Abi = <JsValue as RefFromWasmAbi>::Abi;
1327                    type Anchor = core::mem::ManuallyDrop<#rust_name #ty_generics>;
1328
1329                    #[inline]
1330                    unsafe fn ref_from_abi(js: Self::Abi) -> Self::Anchor {
1331                        let tmp = <JsValue as RefFromWasmAbi>::ref_from_abi(js);
1332                        core::mem::ManuallyDrop::new(#rust_name {
1333                            obj: core::mem::ManuallyDrop::into_inner(tmp).into(),
1334                            #phantom_init
1335                        })
1336                    }
1337                }
1338
1339                #[automatically_derived]
1340                impl #impl_generics LongRefFromWasmAbi for #rust_name #ty_generics #where_clause {
1341                    type Abi = <JsValue as LongRefFromWasmAbi>::Abi;
1342                    type Anchor = #rust_name #ty_generics;
1343
1344                    #[inline]
1345                    unsafe fn long_ref_from_abi(js: Self::Abi) -> Self::Anchor {
1346                        let tmp = <JsValue as LongRefFromWasmAbi>::long_ref_from_abi(js);
1347                        #rust_name {
1348                            obj: tmp.into(),
1349                            #phantom_init
1350                        }
1351                    }
1352                }
1353
1354                #[automatically_derived]
1355                impl #impl_generics AsRef<JsValue> for #rust_name #ty_generics #where_clause {
1356                    #[inline]
1357                    fn as_ref(&self) -> &JsValue { self.obj.as_ref() }
1358                }
1359
1360                #[automatically_derived]
1361                impl #impl_generics AsRef<#rust_name #ty_generics> for #rust_name #ty_generics #where_clause {
1362                    #[inline]
1363                    fn as_ref(&self) -> &#rust_name #ty_generics { self }
1364                }
1365
1366                #into_js_generic_impl
1367
1368                // TODO: remove this on the next major version
1369                // Only include lifetime params here; type params use their
1370                // defaults so callers don't need turbofish annotations.
1371                #[automatically_derived]
1372                impl #from_jsvalue_generics From<JsValue> for #rust_name #from_jsvalue_generics {
1373                    #[inline]
1374                    fn from(obj: JsValue) -> Self {
1375                        #rust_name {
1376                            obj: obj.into(),
1377                            #phantom_init
1378                        }
1379                    }
1380                }
1381
1382                #[automatically_derived]
1383                impl #impl_generics From<#rust_name #ty_generics> for JsValue #where_clause {
1384                    #[inline]
1385                    fn from(obj: #rust_name #ty_generics) -> JsValue {
1386                        obj.obj.into()
1387                    }
1388                }
1389
1390                #[automatically_derived]
1391                impl #impl_generics JsCast for #rust_name #ty_generics #where_clause {
1392                    fn instanceof(val: &JsValue) -> bool {
1393                        #[link(wasm_import_module = "__wbindgen_placeholder__")]
1394                        #[cfg(all(target_family = "wasm", not(target_os = "wasi")))]
1395                        extern "C" {
1396                            fn #instanceof_shim(val: u32) -> u32;
1397                        }
1398                        #[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))]
1399                        unsafe fn #instanceof_shim(_: u32) -> u32 {
1400                            panic!("cannot check instanceof on non-wasm targets");
1401                        }
1402                        unsafe {
1403                            let idx = val.into_abi();
1404                            #instanceof_shim(idx) != 0
1405                        }
1406                    }
1407
1408                    #is_type_of
1409
1410                    #[inline]
1411                    fn unchecked_from_js(val: JsValue) -> Self {
1412                        #rust_name {
1413                            obj: val.into(),
1414                            #phantom_init
1415                        }
1416                    }
1417
1418                    #[inline]
1419                    fn unchecked_from_js_ref(val: &JsValue) -> &Self {
1420                        // Should be safe because `#rust_name` is a transparent
1421                        // wrapper around `val`
1422                        unsafe { &*(val as *const JsValue as *const Self) }
1423                    }
1424                }
1425
1426                unsafe impl #impl_generics ErasableGeneric for #rust_name #ty_generics #where_clause {
1427                    type Repr = JsValue;
1428                }
1429            };
1430        })
1431        .to_tokens(tokens);
1432
1433        if !no_promising {
1434            (quote! {
1435                #[automatically_derived]
1436                impl #impl_generics #wasm_bindgen::sys::Promising for #rust_name #ty_generics #where_clause {
1437                    type Resolution = #rust_name #ty_generics;
1438                }
1439            })
1440            .to_tokens(tokens);
1441        }
1442
1443        if !no_deref {
1444            (quote! {
1445                #[automatically_derived]
1446                impl #impl_generics #wasm_bindgen::__rt::core::ops::Deref for #rust_name #ty_generics #where_clause {
1447                    type Target = #internal_obj;
1448
1449                    #[inline]
1450                    fn deref(&self) -> &#internal_obj {
1451                        &self.obj
1452                    }
1453                }
1454            })
1455            .to_tokens(tokens);
1456        }
1457
1458        for superclass in self.extends.iter() {
1459            (quote! {
1460                #[automatically_derived]
1461                impl #impl_generics From<#rust_name #ty_generics> for #superclass #where_clause {
1462                    #[inline]
1463                    fn from(obj: #rust_name #ty_generics) -> #superclass {
1464                        use #wasm_bindgen::JsCast;
1465                        #superclass::unchecked_from_js(obj.into())
1466                    }
1467                }
1468
1469                #[automatically_derived]
1470                impl #impl_generics AsRef<#superclass> for #rust_name #ty_generics #where_clause {
1471                    #[inline]
1472                    fn as_ref(&self) -> &#superclass {
1473                        use #wasm_bindgen::JsCast;
1474                        #superclass::unchecked_from_js_ref(self.as_ref())
1475                    }
1476                }
1477            })
1478            .to_tokens(tokens);
1479        }
1480
1481        // Generate UpcastFrom implementations (unless no_upcast is set)
1482        if !self.no_upcast {
1483            // 1. Always generate UpcastFrom<Self> for JsValue
1484            (quote! {
1485                #[automatically_derived]
1486                impl #impl_generics #wasm_bindgen::convert::UpcastFrom<#rust_name #ty_generics>
1487                    for #wasm_bindgen::JsValue
1488                #where_clause
1489                {
1490                }
1491            })
1492            .to_tokens(tokens);
1493
1494            // 2. For non-generic types: generate identity upcast (UpcastFrom<Self> for Self, UpcastFrom<Self> for JsOption<Self>)
1495            // 3. For generic types: generate structural covariance
1496            let type_params: Vec<_> = self.generics.type_params().collect();
1497            if type_params.is_empty() {
1498                // Identity impls for non-generic (or lifetime-only) types.
1499                // Always use #ty_generics so that lifetime params are included.
1500                (quote! {
1501                    #[automatically_derived]
1502                    impl #impl_generics #wasm_bindgen::convert::UpcastFrom<#rust_name #ty_generics>
1503                        for #rust_name #ty_generics
1504                    #where_clause
1505                    {
1506                    }
1507                    #[automatically_derived]
1508                    impl #impl_generics #wasm_bindgen::convert::UpcastFrom<#rust_name #ty_generics>
1509                        for #wasm_bindgen::sys::JsOption<#rust_name #ty_generics>
1510                    #where_clause
1511                    {
1512                    }
1513                })
1514                .to_tokens(tokens);
1515            } else {
1516                // Structural covariance impl for generic types
1517                // Build impl generics: all original params plus a Target param for each
1518                let mut impl_generics_extended = self.generics.clone();
1519                let target_param_names: Vec<syn::Ident> = type_params
1520                    .iter()
1521                    .enumerate()
1522                    .map(|(i, tp)| {
1523                        let target_name = quote::format_ident!("__UpcastTarget{}", i);
1524                        // Copy bounds from the original type param to the target param
1525                        // If no bounds, just add the type param without colon
1526                        if tp.bounds.is_empty() {
1527                            impl_generics_extended
1528                                .params
1529                                .push(syn::parse_quote!(#target_name));
1530                        } else {
1531                            let bounds = &tp.bounds;
1532                            impl_generics_extended
1533                                .params
1534                                .push(syn::parse_quote!(#target_name: #bounds));
1535                        }
1536                        target_name
1537                    })
1538                    .collect();
1539
1540                // Build where clause: Target: UpcastFrom<T>
1541                let mut where_clause_extended =
1542                    self.generics
1543                        .where_clause
1544                        .clone()
1545                        .unwrap_or_else(|| syn::WhereClause {
1546                            where_token: Default::default(),
1547                            predicates: Default::default(),
1548                        });
1549
1550                for (type_param, target_name) in type_params.iter().zip(&target_param_names) {
1551                    let param_ident = &type_param.ident;
1552                    where_clause_extended.predicates.push(syn::parse_quote!(
1553                        #target_name: #wasm_bindgen::convert::UpcastFrom<#param_ident>
1554                    ));
1555                }
1556
1557                let (impl_generics_split, _, _) = impl_generics_extended.split_for_impl();
1558
1559                // Build target ty_generics: lifetime params forwarded, type params replaced
1560                let target_lifetime_params = generics::lifetime_params(&self.generics);
1561                let target_ty_generics =
1562                    quote! { <#(#target_lifetime_params,)* #(#target_param_names),*> };
1563
1564                // Structural covariance - Type<Target0, Target1, ...> can be upcast from Type<T1, T2, ...>
1565                (quote! {
1566                    #[automatically_derived]
1567                    impl #impl_generics_split #wasm_bindgen::convert::UpcastFrom<#rust_name #ty_generics>
1568                        for #rust_name #target_ty_generics
1569                    #where_clause_extended
1570                    {
1571                    }
1572                    #[automatically_derived]
1573                    impl #impl_generics_split #wasm_bindgen::convert::UpcastFrom<#rust_name #ty_generics>
1574                        for #wasm_bindgen::sys::JsOption<#rust_name #target_ty_generics>
1575                    #where_clause_extended
1576                    {
1577                    }
1578                })
1579                .to_tokens(tokens);
1580            }
1581
1582            // 4. For each superclass in extends, generate UpcastFrom<Self> for superclass
1583            for superclass in self.extends.iter() {
1584                (quote! {
1585                    #[automatically_derived]
1586                    impl #impl_generics #wasm_bindgen::convert::UpcastFrom<#rust_name #ty_generics>
1587                        for #superclass
1588                    #where_clause
1589                    {
1590                    }
1591                    #[automatically_derived]
1592                    impl #impl_generics #wasm_bindgen::convert::UpcastFrom<#rust_name #ty_generics>
1593                        for #wasm_bindgen::sys::JsOption<#superclass>
1594                    #where_clause
1595                    {
1596                    }
1597                })
1598                .to_tokens(tokens);
1599            }
1600        }
1601
1602        Ok(())
1603    }
1604}
1605
1606// String enums predate dynamic unions and overlap structurally: a string
1607// enum is equivalent to a dynamic union with only string-literal variants,
1608// minus a few details. Future cleanup (separate PR) could subsume string
1609// enums into the dynamic-union codegen. Differences to reconcile first:
1610//
1611// * `__Invalid`: string enums silently accept unknown JS strings as a hidden
1612//   `__Invalid` variant. Dynamic unions throw, or accept an explicit
1613//   `#[wasm_bindgen(fallback)]` catch-all variant. Migrating means dropping
1614//   `__Invalid` (telling users to switch to `fallback`).
1615// * Inherent helpers: `from_str` / `to_str` / `from_js_value` are emitted
1616//   here as inherent methods. Dynamic unions don't generate equivalents.
1617//   Either preserve them or document removal as breaking.
1618// * `TryFromJsValue`: string enums currently lack this impl, so they
1619//   can't be `dyn_into` targets or dynamic-union variant payloads.
1620//   Dynamic unions have it. Unification would gain this on the string
1621//   enum path for free.
1622// * ABI: string enums use a u32 discriminant; dynamic unions use an
1623//   externref. Benchmarks (see `benches/enum_roundtrip.rs`) show the
1624//   round-trip cost is within ~1% on Node, so the perf argument for
1625//   keeping the discriminant ABI is weak.
1626impl ToTokens for ast::StringEnum {
1627    fn to_tokens(&self, tokens: &mut TokenStream) {
1628        let vis = &self.vis;
1629        let enum_name = &self.name;
1630        let name_str = &self.export_name;
1631        let name_len = name_str.len() as u32;
1632        let name_chars = name_str.chars().map(u32::from);
1633        let variants = &self.variants;
1634        let variant_count = self.variant_values.len() as u32;
1635        let variant_values = &self.variant_values;
1636        let variant_indices = (0..variant_count).collect::<Vec<_>>();
1637        let invalid = variant_count;
1638        let hole = variant_count + 1;
1639        let attrs = &self.rust_attrs;
1640
1641        let invalid_to_str_msg = format!(
1642            "Converting an invalid string enum ({enum_name}) back to a string is currently not supported"
1643        );
1644
1645        // A vector of EnumName::VariantName tokens for this enum
1646        let variant_paths: Vec<TokenStream> = self
1647            .variants
1648            .iter()
1649            .map(|v| quote!(#enum_name::#v).into_token_stream())
1650            .collect();
1651
1652        // Borrow variant_paths because we need to use it multiple times inside the quote! macro
1653        let variant_paths_ref = &variant_paths;
1654
1655        let wasm_bindgen = &self.wasm_bindgen;
1656
1657        (quote! {
1658            #(#attrs)*
1659            #[non_exhaustive]
1660            #[repr(u32)]
1661            #vis enum #enum_name {
1662                #(#variants = #variant_indices,)*
1663                #[automatically_derived]
1664                #[doc(hidden)]
1665                __Invalid
1666            }
1667
1668            #[automatically_derived]
1669            impl #enum_name {
1670                fn from_str(s: &str) -> Option<#enum_name> {
1671                    match s {
1672                        #(#variant_values => Some(#variant_paths_ref),)*
1673                        _ => None,
1674                    }
1675                }
1676
1677                fn to_str(&self) -> &'static str {
1678                    match self {
1679                        #(#variant_paths_ref => #variant_values,)*
1680                        #enum_name::__Invalid => panic!(#invalid_to_str_msg),
1681                    }
1682                }
1683
1684                #vis fn from_js_value(obj: &#wasm_bindgen::JsValue) -> Option<#enum_name> {
1685                    obj.as_string().and_then(|obj_str| Self::from_str(obj_str.as_str()))
1686                }
1687            }
1688
1689            #[automatically_derived]
1690            impl #wasm_bindgen::convert::IntoWasmAbi for #enum_name {
1691                type Abi = u32;
1692
1693                #[inline]
1694                fn into_abi(self) -> u32 {
1695                    self as u32
1696                }
1697            }
1698
1699            #[automatically_derived]
1700            impl #wasm_bindgen::convert::FromWasmAbi for #enum_name {
1701                type Abi = u32;
1702
1703                unsafe fn from_abi(val: u32) -> Self {
1704                    match val {
1705                        #(#variant_indices => #variant_paths_ref,)*
1706                        #invalid => #enum_name::__Invalid,
1707                        _ => unreachable!("The JS binding should only ever produce a valid value or the specific 'invalid' value"),
1708                    }
1709                }
1710            }
1711
1712            #[automatically_derived]
1713            impl #wasm_bindgen::convert::OptionFromWasmAbi for #enum_name {
1714                #[inline]
1715                fn is_none(val: &u32) -> bool { *val == #hole }
1716            }
1717
1718            #[automatically_derived]
1719            impl #wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name {
1720                #[inline]
1721                fn none() -> Self::Abi { #hole }
1722            }
1723
1724            #[automatically_derived]
1725            impl #wasm_bindgen::describe::WasmDescribe for #enum_name {
1726                fn describe() {
1727                    use #wasm_bindgen::describe::*;
1728                    inform(STRING_ENUM);
1729                    inform(#name_len);
1730                    #(inform(#name_chars);)*
1731                    inform(#variant_count);
1732                }
1733            }
1734
1735            #[automatically_derived]
1736            impl #wasm_bindgen::__rt::core::convert::From<#enum_name> for
1737                #wasm_bindgen::JsValue
1738            {
1739                fn from(val: #enum_name) -> Self {
1740                    #wasm_bindgen::JsValue::from_str(val.to_str())
1741                }
1742            }
1743        })
1744        .to_tokens(tokens);
1745    }
1746}
1747
1748impl ToTokens for ast::DynamicUnion {
1749    fn to_tokens(&self, tokens: &mut TokenStream) {
1750        let vis = &self.vis;
1751        let enum_name = &self.name;
1752        let wasm_bindgen = &self.wasm_bindgen;
1753        let attrs = &self.rust_attrs;
1754
1755        // Separate string-literal variants from tuple (typed payload) variants
1756        let (known_variants, fallback_variants): (Vec<_>, Vec<_>) = self
1757            .variants
1758            .iter()
1759            .zip(&self.variant_fields)
1760            .partition(|(_, fields)| fields.is_empty());
1761
1762        let known_variant_names: Vec<_> = known_variants.iter().map(|(v, _)| v).collect();
1763        let known_variant_values: Vec<_> = known_variants
1764            .iter()
1765            .map(|(v, _)| {
1766                let idx = self.variants.iter().position(|x| x == *v).unwrap();
1767                &self.variant_values[idx]
1768            })
1769            .collect();
1770
1771        // Build enum definition with all variants
1772        let fallback_variant_defs = fallback_variants.iter().map(|(name, fields)| {
1773            let ty = &fields[0];
1774            quote! { #name(#ty) }
1775        });
1776
1777        let enum_def = quote! {
1778            #(#known_variant_names,)*
1779            #(#fallback_variant_defs,)*
1780        };
1781
1782        // IntoWasmAbi - convert everything to JsValue
1783        let known_into_arms: Vec<_> = known_variant_names
1784            .iter()
1785            .zip(&known_variant_values)
1786            .map(|(vname, value)| {
1787                quote! {
1788                    #enum_name::#vname => #wasm_bindgen::JsValue::from_str(#value)
1789                }
1790            })
1791            .collect();
1792
1793        let fallback_into_arms: Vec<_> = fallback_variants
1794            .iter()
1795            .map(|(name, _)| {
1796                quote! {
1797                    #enum_name::#name(value) => #wasm_bindgen::JsValue::from(value)
1798                }
1799            })
1800            .collect();
1801
1802        // FromWasmAbi - try to match JsValue to each variant. All string
1803        // literal variants share a single `as_string` call coalesced into one
1804        // `match`, so the worst-case dispatch cost is a single string read
1805        // regardless of how many literal variants exist.
1806        let known_from_block = if known_variant_names.is_empty() {
1807            quote! {}
1808        } else {
1809            let arms =
1810                known_variant_names
1811                    .iter()
1812                    .zip(&known_variant_values)
1813                    .map(|(vname, value)| {
1814                        quote! { #value => return #enum_name::#vname, }
1815                    });
1816            quote! {
1817                if let Some(s) = js_value.as_string() {
1818                    match s.as_str() {
1819                        #(#arms)*
1820                        _ => {}
1821                    }
1822                }
1823            }
1824        };
1825
1826        // When `#[wasm_bindgen(fallback)]` is set on the enum and there is
1827        // at least one tuple variant, the *last* tuple variant becomes an
1828        // unconditional catch-all: anything that didn't match an earlier
1829        // variant is unconditionally accepted as that variant's payload via
1830        // an unchecked cast. This lets unions terminate in a type whose
1831        // `instanceof` check is meaningless (e.g., interface-only imports).
1832        let last_fallback_idx = if self.fallback && !fallback_variants.is_empty() {
1833            Some(fallback_variants.len() - 1)
1834        } else {
1835            None
1836        };
1837
1838        let fallback_from_arms: Vec<_> = fallback_variants
1839            .iter()
1840            .enumerate()
1841            .map(|(idx, (name, fields))| {
1842                let ty = &fields[0];
1843                if Some(idx) == last_fallback_idx {
1844                    quote! {
1845                        return #enum_name::#name(
1846                            <#wasm_bindgen::JsValue as #wasm_bindgen::JsCast>::unchecked_into::<#ty>(js_value)
1847                        );
1848                    }
1849                } else {
1850                    quote! {
1851                        if let Ok(value) = <#ty as #wasm_bindgen::convert::TryFromJsValue>::try_from_js_value(js_value.clone()) {
1852                            return #enum_name::#name(value);
1853                        }
1854                    }
1855                }
1856            })
1857            .collect();
1858
1859        // Same dispatch as `fallback_from_arms` but for `TryFromJsValue`,
1860        // which returns `Err(value)` on full failure rather than throwing.
1861        // The same fallback rule applies.
1862        let fallback_try_from_arms: Vec<_> = fallback_variants
1863            .iter()
1864            .enumerate()
1865            .map(|(idx, (name, fields))| {
1866                let ty = &fields[0];
1867                if Some(idx) == last_fallback_idx {
1868                    quote! {
1869                        return #wasm_bindgen::__rt::core::result::Result::Ok(
1870                            #enum_name::#name(
1871                                <#wasm_bindgen::JsValue as #wasm_bindgen::JsCast>::unchecked_into::<#ty>(value)
1872                            )
1873                        );
1874                    }
1875                } else {
1876                    quote! {
1877                        if let Ok(inner) = <#ty as #wasm_bindgen::convert::TryFromJsValue>::try_from_js_value(value.clone()) {
1878                            return #wasm_bindgen::__rt::core::result::Result::Ok(#enum_name::#name(inner));
1879                        }
1880                    }
1881                }
1882            })
1883            .collect();
1884
1885        // The dispatch chain ends with a throw / `Err` only when the enum
1886        // does *not* have a fallback variant. With a fallback, the last
1887        // tuple-variant arm always `return`s unconditionally, so any
1888        // trailing expression would be unreachable.
1889        let from_abi_tail = if last_fallback_idx.is_some() {
1890            quote! {}
1891        } else {
1892            quote! { #wasm_bindgen::throw_str("invalid dynamic union value") }
1893        };
1894        let try_from_tail = if last_fallback_idx.is_some() {
1895            quote! {}
1896        } else {
1897            quote! { #wasm_bindgen::__rt::core::result::Result::Err(value) }
1898        };
1899
1900        let name_str = &self.js_name;
1901        let name_len = name_str.len() as u32;
1902        let name_chars = name_str.chars().map(u32::from);
1903
1904        let mut string_variants = Vec::new();
1905        let mut type_variants = Vec::new();
1906        for (idx, fields) in self.variant_fields.iter().enumerate() {
1907            if fields.is_empty() {
1908                string_variants.push(&self.variant_values[idx]);
1909            } else {
1910                type_variants.push(&fields[0]);
1911            }
1912        }
1913        let type_count = type_variants.len() as u32;
1914
1915        (quote! {
1916            #(#attrs)*
1917            #vis enum #enum_name {
1918                #enum_def
1919            }
1920
1921            #[automatically_derived]
1922            impl #wasm_bindgen::convert::IntoWasmAbi for #enum_name {
1923                type Abi = u32;
1924
1925                #[inline]
1926                fn into_abi(self) -> u32 {
1927                    let js_value: #wasm_bindgen::JsValue = match self {
1928                        #(#known_into_arms,)*
1929                        #(#fallback_into_arms,)*
1930                    };
1931                    #wasm_bindgen::convert::IntoWasmAbi::into_abi(js_value)
1932                }
1933            }
1934
1935            #[automatically_derived]
1936            impl #wasm_bindgen::convert::FromWasmAbi for #enum_name {
1937                type Abi = u32;
1938
1939                #[inline]
1940                unsafe fn from_abi(js: u32) -> Self {
1941                    let js_value = <#wasm_bindgen::JsValue as #wasm_bindgen::convert::FromWasmAbi>::from_abi(js);
1942                    #known_from_block
1943                    #(#fallback_from_arms)*
1944                    #from_abi_tail
1945                }
1946            }
1947
1948            // Despite the generic implementation, we still encode the type information for TypeScript output
1949            #[automatically_derived]
1950            impl #wasm_bindgen::describe::WasmDescribe for #enum_name {
1951                fn describe() {
1952                    use #wasm_bindgen::describe::*;
1953                    inform(DYNAMIC_UNION);
1954                    inform(#name_len);
1955                    #(inform(#name_chars);)*
1956                    inform(#type_count);
1957                    #(<#type_variants as WasmDescribe>::describe();)*
1958                }
1959            }
1960
1961            #[automatically_derived]
1962            impl #wasm_bindgen::__rt::core::convert::From<#enum_name> for #wasm_bindgen::JsValue {
1963                fn from(value: #enum_name) -> Self {
1964                    match value {
1965                        #(#known_into_arms,)*
1966                        #(#fallback_into_arms,)*
1967                    }
1968                }
1969            }
1970
1971            // Allows this union to appear inside `Option<...>`. Reuses
1972            // `JsValue`'s `undefined` sentinel since the union ABI is a
1973            // single externref slot. This is sound only because dynamic
1974            // unions cannot match `undefined` as a variant.
1975            #[automatically_derived]
1976            impl #wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name {
1977                #[inline]
1978                fn none() -> u32 {
1979                    <#wasm_bindgen::JsValue as #wasm_bindgen::convert::OptionIntoWasmAbi>::none()
1980                }
1981            }
1982
1983            #[automatically_derived]
1984            impl #wasm_bindgen::convert::OptionFromWasmAbi for #enum_name {
1985                #[inline]
1986                fn is_none(js: &u32) -> bool {
1987                    <#wasm_bindgen::JsValue as #wasm_bindgen::convert::OptionFromWasmAbi>::is_none(js)
1988                }
1989            }
1990
1991            // Allows this union to appear as a variant payload of another
1992            // dynamic union (nested unions) and anywhere else the macro
1993            // dispatches through `TryFromJsValue`.
1994            #[automatically_derived]
1995            impl #wasm_bindgen::convert::TryFromJsValue for #enum_name {
1996                fn try_from_js_value(
1997                    value: #wasm_bindgen::JsValue,
1998                ) -> #wasm_bindgen::__rt::core::result::Result<Self, #wasm_bindgen::JsValue> {
1999                    if let Some(s) = value.as_string() {
2000                        #(
2001                            if s == #known_variant_values {
2002                                return #wasm_bindgen::__rt::core::result::Result::Ok(
2003                                    #enum_name::#known_variant_names
2004                                );
2005                            }
2006                        )*
2007                    }
2008                    #(#fallback_try_from_arms)*
2009                    #try_from_tail
2010                }
2011
2012                fn try_from_js_value_ref(
2013                    value: &#wasm_bindgen::JsValue,
2014                ) -> #wasm_bindgen::__rt::core::option::Option<Self> {
2015                    Self::try_from_js_value(value.clone()).ok()
2016                }
2017            }
2018        })
2019        .to_tokens(tokens);
2020
2021        // Generate descriptor exports for each type variant so cli-support can look them up
2022        for (idx, ty) in type_variants.iter().enumerate() {
2023            let descriptor_name = Ident::new(
2024                &shared::dynamic_union_variant(name_str, idx as u32),
2025                Span::call_site(),
2026            );
2027            Descriptor {
2028                ident: &descriptor_name,
2029                inner: quote! {
2030                    <#ty as WasmDescribe>::describe();
2031                },
2032                attrs: vec![],
2033                wasm_bindgen: &self.wasm_bindgen,
2034            }
2035            .to_tokens(tokens);
2036        }
2037    }
2038}
2039
2040impl TryToTokens for ast::ImportFunction {
2041    fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
2042        let mut class = None;
2043        let mut is_constructor = false;
2044        let mut is_method = false;
2045        let mut is_self_returning_static = false;
2046        if let ast::ImportFunctionKind::Method {
2047            class: class_name,
2048            ty,
2049            kind,
2050            ..
2051        } = &self.kind
2052        {
2053            class = Some((class_name, get_ty(ty)));
2054            match kind {
2055                ast::MethodKind::Constructor => is_constructor = true,
2056                ast::MethodKind::Operation(ast::Operation {
2057                    is_static: false, ..
2058                }) => is_method = true,
2059                _ => {}
2060            };
2061            // For constructors and static methods whose return type matches the
2062            // class (e.g. `Array::of<T>() -> Array<T>`), override the class type
2063            // to use the return type so class-level generics get hoisted.
2064            if self.class_return_path().is_some() {
2065                class = Some((class_name, get_ty(self.js_ret.as_ref().unwrap())));
2066                if !is_constructor {
2067                    is_self_returning_static = true;
2068                }
2069            }
2070        }
2071
2072        let vis = &self.function.rust_vis;
2073        let ret = match self.function.ret.as_ref().map(|ret| &ret.r#type) {
2074            Some(ty) => quote! { -> #ty },
2075            None => quote!(),
2076        };
2077
2078        let mut abi_argument_names = Vec::new();
2079        let mut abi_arguments = Vec::new();
2080        let mut arg_conversions = Vec::new();
2081        let mut arguments = Vec::new();
2082
2083        let mut fn_class_generics = self.get_fn_generics()?;
2084        let (fn_lifetime_param_names, fn_generic_param_names) =
2085            generics::all_param_names(&self.generics);
2086
2087        let ret_ident = Ident::new("_ret", Span::call_site());
2088        let wasm_bindgen = &self.wasm_bindgen;
2089        let wasm_bindgen_futures = &self.wasm_bindgen_futures;
2090        let js_sys = &self.js_sys;
2091        let futures = if ast::use_js_sys_futures() {
2092            quote! { #js_sys::futures }
2093        } else {
2094            quote! { #wasm_bindgen_futures }
2095        };
2096        let promise = if ast::use_js_sys_futures() {
2097            quote! { #js_sys::Promise }
2098        } else {
2099            quote! { #wasm_bindgen_futures::js_sys::Promise }
2100        };
2101
2102        for (i, arg) in self.function.arguments.iter().enumerate() {
2103            let ty = &*arg.pat_type.ty;
2104            let name = match &*arg.pat_type.pat {
2105                syn::Pat::Ident(syn::PatIdent {
2106                    by_ref: None,
2107                    ident,
2108                    subpat: None,
2109                    ..
2110                }) => ident.clone(),
2111                syn::Pat::Wild(_) => syn::Ident::new(&format!("__genarg_{i}"), Span::call_site()),
2112                _ => bail_span!(
2113                    arg.pat_type.pat,
2114                    "unsupported pattern in #[wasm_bindgen] imported function",
2115                ),
2116            };
2117
2118            let var = if i == 0 && is_method {
2119                quote! { self }
2120            } else {
2121                quote! { #name }
2122            };
2123
2124            let abi_ty;
2125            let convert_arg;
2126
2127            if generics::uses_generic_params(ty, &fn_generic_param_names)
2128                || generics::uses_lifetime_params(ty, &fn_lifetime_param_names)
2129            {
2130                let (inner_ty, ref_mut, ref_lifetime) =
2131                    if let syn::Type::Reference(syn::TypeReference {
2132                        elem,
2133                        mutability: mut_,
2134                        lifetime,
2135                        ..
2136                    }) = ty
2137                    {
2138                        ((**elem).clone(), Some(mut_), lifetime.clone())
2139                    } else {
2140                        (ty.clone(), None, None)
2141                    };
2142                let concrete_ty = generic_to_concrete(
2143                    inner_ty.clone(),
2144                    &fn_class_generics.concrete_defaults,
2145                    &fn_lifetime_param_names,
2146                )?;
2147                if i > 0 || !is_method {
2148                    fn_class_generics.add_fn_bound(if let Some(mut_) = ref_mut {
2149                        arguments.push(quote! { #name: & #ref_lifetime #mut_ #inner_ty });
2150                        if mut_.is_some() {
2151                            parse_quote! { #inner_ty: #wasm_bindgen::__rt::marker::ErasableGenericBorrowMut<#concrete_ty> }
2152                        } else {
2153                            parse_quote! { #inner_ty: #wasm_bindgen::__rt::marker::ErasableGenericBorrow<#concrete_ty> }
2154                        }
2155                    } else {
2156                        arguments.push(quote! { #name: #ty });
2157                        parse_quote! { #inner_ty: #wasm_bindgen::__rt::marker::ErasableGenericOwn<#concrete_ty> }
2158                    });
2159                }
2160                // abi_ty is fully concrete with 'static lifetimes (used for both extern block and transmute)
2161                abi_ty = if let Some(mut_) = ref_mut {
2162                    quote! { &'static #mut_ #concrete_ty }
2163                } else {
2164                    quote! { #concrete_ty }
2165                };
2166
2167                convert_arg = quote! { unsafe { core::mem::transmute_copy(&core::mem::ManuallyDrop::new(#var)) } };
2168            } else if let Some((is_mut, fn_bounds)) = detect_raw_fn_trait_obj(ty) {
2169                // Raw `&dyn Fn(...)` or `&mut dyn FnMut(...)` argument.
2170                //
2171                // Emit as `&mut (impl FnMut(...) + MaybeUnwindSafe)` / `&(impl Fn(...) + MaybeUnwindSafe)`
2172                // so that callers must satisfy UnwindSafe when `panic = "unwind"`, while remaining
2173                // backward-compatible when `panic != "unwind"` (MaybeUnwindSafe is blanket-impl'd).
2174                // Using `impl Trait` keeps the signature clean — no hidden generic param or where-clause.
2175                if i > 0 || !is_method {
2176                    if is_mut {
2177                        arguments.push(quote! {
2178                            #name: &mut (impl #fn_bounds + #wasm_bindgen::__rt::marker::MaybeUnwindSafe)
2179                        });
2180                    } else {
2181                        arguments.push(quote! {
2182                            #name: &(impl #fn_bounds + #wasm_bindgen::__rt::marker::MaybeUnwindSafe)
2183                        });
2184                    }
2185                }
2186
2187                // The ABI type is still the erased dyn type — same wire format.
2188                if is_mut {
2189                    abi_ty = quote! { &mut dyn #fn_bounds };
2190                } else {
2191                    abi_ty = quote! { &dyn #fn_bounds };
2192                }
2193
2194                // Coerce the concrete impl Trait type to the dyn trait object for into_abi.
2195                if is_mut {
2196                    convert_arg = quote! { #var as &mut dyn #fn_bounds };
2197                } else {
2198                    convert_arg = quote! { #var as &dyn #fn_bounds };
2199                }
2200            } else {
2201                if i > 0 || !is_method {
2202                    arguments.push(quote! { #name: #ty });
2203                }
2204                abi_ty = quote! { #ty };
2205
2206                convert_arg = quote! { #var };
2207            }
2208
2209            // `slice_to_array`: re-route an `&[T]` (or `Option<&[T]>`)
2210            // outgoing argument through `<T as VectorRefIntoWasmAbi>`
2211            // instead of the default `&[T]: IntoWasmAbi`. The user-facing
2212            // parameter is unchanged; only the ABI / describe path
2213            // changes. `VectorRefIntoWasmAbi`'s impls cover the two
2214            // genuine ABI shapes (zero-copy primitive borrow,
2215            // fresh-`Box<[u32]>` for handle-shaped element types) — no
2216            // `T: Clone` bound is introduced.
2217            //
2218            // Wire format is `WasmSlice` either way; the cli-support
2219            // side picks the right JS shim based on the element
2220            // `VectorKind` recovered from the descriptor.
2221            // `slice_to_array` is set per-fn or per-`extern "C"` block
2222            // and applies to every `&[T]` / `Option<&[T]>` argument of
2223            // every fn it covers. Args that aren't slice-shaped (e.g.
2224            // the `this: &Foo` of a method, or any other non-slice
2225            // argument of a slice_to_array fn) silently fall through to
2226            // the default ABI path — there's no per-arg opt-out form
2227            // in Rust attribute syntax to require, so silent no-op is
2228            // the only sensible behaviour.
2229            if arg.slice_to_array && detect_slice_or_option_slice(ty).is_some() {
2230                let (elem_ty, is_option) = detect_slice_or_option_slice(ty).unwrap();
2231
2232                let abi = quote! { #wasm_bindgen::convert::WasmSlice };
2233                let (prim_args, prim_names) = splat(wasm_bindgen, &name, &abi);
2234                abi_arguments.extend(prim_args);
2235                abi_argument_names.extend(prim_names.iter().cloned());
2236
2237                let body = if is_option {
2238                    quote! {
2239                        match #var {
2240                            ::core::option::Option::Some(s) =>
2241                                <#elem_ty as #wasm_bindgen::convert::VectorRefIntoWasmAbi>
2242                                    ::slice_into_abi(s),
2243                            ::core::option::Option::None =>
2244                                <#elem_ty as #wasm_bindgen::convert::VectorRefIntoWasmAbi>
2245                                    ::slice_none(),
2246                        }
2247                    }
2248                } else {
2249                    quote! {
2250                        <#elem_ty as #wasm_bindgen::convert::VectorRefIntoWasmAbi>
2251                            ::slice_into_abi(#var)
2252                    }
2253                };
2254
2255                arg_conversions.push(quote! {
2256                    let #name: #wasm_bindgen::convert::WasmSlice = #body;
2257                    let (#(#prim_names),*) =
2258                        <#wasm_bindgen::convert::WasmSlice as #wasm_bindgen::convert::WasmAbi>
2259                            ::split(#name);
2260                });
2261                continue;
2262            }
2263
2264            let abi = quote! { <#abi_ty as #wasm_bindgen::convert::IntoWasmAbi>::Abi };
2265            let (prim_args, prim_names) = splat(wasm_bindgen, &name, &abi);
2266            abi_arguments.extend(prim_args);
2267            abi_argument_names.extend(prim_names.iter().cloned());
2268
2269            arg_conversions.push(quote! {
2270                let #name = <#abi_ty as #wasm_bindgen::convert::IntoWasmAbi>
2271                    ::into_abi(#convert_arg);
2272                let (#(#prim_names),*) = <#abi as #wasm_bindgen::convert::WasmAbi>::split(#name);
2273            });
2274        }
2275        let abi_ret;
2276        let mut convert_ret;
2277        match &self.js_ret {
2278            Some(syn::Type::Reference(_)) => {
2279                bail_span!(
2280                    self.js_ret,
2281                    "cannot return references in #[wasm_bindgen] imports yet"
2282                );
2283            }
2284            Some(ref original_ty) => {
2285                let maybe_async_wrapped;
2286                let ty = if self.function.r#async {
2287                    maybe_async_wrapped = parse_quote!(#promise<#original_ty>);
2288                    &maybe_async_wrapped
2289                } else {
2290                    original_ty
2291                };
2292                if generics::uses_generic_params(ty, &fn_generic_param_names)
2293                    || generics::uses_lifetime_params(ty, &fn_lifetime_param_names)
2294                {
2295                    let concrete_ty = generic_to_concrete(
2296                        ty.clone(),
2297                        &fn_class_generics.concrete_defaults,
2298                        &fn_lifetime_param_names,
2299                    )?;
2300                    fn_class_generics.add_fn_bound(
2301                        parse_quote! { #ty: #wasm_bindgen::__rt::marker::ErasableGenericOwn<#concrete_ty> },
2302                    );
2303                    convert_ret = quote! { unsafe { core::mem::transmute_copy(&core::mem::ManuallyDrop::new(<#concrete_ty as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#ret_ident.join()))) } };
2304                    abi_ret = quote! { #wasm_bindgen::convert::WasmRet<<#concrete_ty as #wasm_bindgen::convert::FromWasmAbi>::Abi> };
2305                } else {
2306                    convert_ret = quote! { <#ty as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#ret_ident.join()) };
2307                    abi_ret = quote! { #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi> };
2308                }
2309                if self.function.r#async {
2310                    convert_ret = quote! {
2311                        #futures::JsFuture::from(
2312                            <#promise<#original_ty> as #wasm_bindgen::convert::FromWasmAbi>
2313                                ::from_abi(#ret_ident.join())
2314                        ).await
2315                    };
2316                    if self.catch {
2317                        convert_ret = quote! { Ok(#convert_ret?) };
2318                    } else {
2319                        convert_ret = quote! { #convert_ret.expect("uncaught exception") };
2320                    };
2321                }
2322            }
2323            None => {
2324                if self.function.r#async {
2325                    abi_ret = quote! {
2326                        #wasm_bindgen::convert::WasmRet<<#promise as #wasm_bindgen::convert::FromWasmAbi>::Abi>
2327                    };
2328                    let future = quote! {
2329                        #futures::JsFuture::from(
2330                            <#promise as #wasm_bindgen::convert::FromWasmAbi>
2331                                ::from_abi(#ret_ident.join())
2332                        ).await
2333                    };
2334                    convert_ret = if self.catch {
2335                        quote! { #future?; Ok(()) }
2336                    } else {
2337                        quote! { #future.expect("uncaught exception"); }
2338                    };
2339                } else {
2340                    abi_ret = quote! { () };
2341                    convert_ret = quote! { () };
2342                }
2343            }
2344        }
2345
2346        let mut exceptional_ret = quote!();
2347        if self.catch && !self.function.r#async {
2348            convert_ret = quote! { Ok(#convert_ret) };
2349            exceptional_ret = quote! {
2350                #wasm_bindgen::__rt::take_last_exception()?;
2351            };
2352        }
2353
2354        let rust_name = &self.rust_name;
2355        let import_name = &self.shim;
2356        let attrs = &self.function.rust_attrs;
2357        let arguments = &arguments;
2358        let abi_arguments = &abi_arguments[..];
2359        let abi_argument_names = &abi_argument_names[..];
2360
2361        let doc = if self.doc_comment.is_empty() {
2362            quote! {}
2363        } else {
2364            let doc_comment = &self.doc_comment;
2365            quote! { #[doc = #doc_comment] }
2366        };
2367
2368        let me = if is_method {
2369            quote! { &self, }
2370        } else {
2371            quote!()
2372        };
2373
2374        // Route any errors pointing to this imported function to the identifier
2375        // of the function we're imported from so we at least know what function
2376        // is causing issues.
2377        //
2378        // Note that this is where type errors like "doesn't implement
2379        // FromWasmAbi" or "doesn't implement IntoWasmAbi" currently get routed.
2380        // I suspect that's because they show up in the signature via trait
2381        // projections as types of arguments, and all that needs to typecheck
2382        // before the body can be typechecked. Due to rust-lang/rust#60980 (and
2383        // probably related issues) we can't really get a precise span.
2384        //
2385        // Ideally what we want is to point errors for particular types back to
2386        // the specific argument/type that generated the error, but it looks
2387        // like rustc itself doesn't do great in that regard so let's just do
2388        // the best we can in the meantime.
2389        let extern_fn = respan(
2390            extern_fn(
2391                import_name,
2392                attrs,
2393                abi_arguments,
2394                abi_argument_names,
2395                abi_ret,
2396            ),
2397            &self.rust_name,
2398        );
2399
2400        let maybe_unsafe = if self.function.r#unsafe {
2401            Some(quote! { unsafe })
2402        } else {
2403            None
2404        };
2405        let maybe_async = if self.function.r#async {
2406            Some(quote! { async })
2407        } else {
2408            None
2409        };
2410
2411        let mut class_impl_def = None;
2412        if let Some((_, class)) = class {
2413            let mut class = class.clone();
2414            if let syn::Type::Path(syn::TypePath {
2415                qself: None,
2416                ref mut path,
2417            }) = class
2418            {
2419                if let Some(segment) = path.segments.last_mut() {
2420                    segment.arguments = syn::PathArguments::None;
2421                }
2422            }
2423            let has_class_generics = !fn_class_generics.class_generic_params.is_empty()
2424                || !fn_class_generics.class_lifetime_params.is_empty()
2425                || !fn_class_generics.class_bound_lifetime_params.is_empty();
2426            if (!is_method && !is_constructor && !is_self_returning_static) || !has_class_generics {
2427                // For static functions not the constructor/self-returning, we impl on generic default
2428                class_impl_def = Some(quote! { impl #class });
2429            } else {
2430                // Type lifetimes: appear on impl AND passed to type
2431                let class_lifetime_params = &fn_class_generics.class_lifetime_params;
2432                // Bound-only lifetimes: appear on impl but NOT passed to type
2433                let class_bound_lifetime_params = &fn_class_generics.class_bound_lifetime_params;
2434                let class_generic_params = &fn_class_generics.class_generic_params;
2435                let class_generic_exprs = &fn_class_generics.class_generic_exprs;
2436                let impl_where_clause = if !fn_class_generics.class_bounds.is_empty() {
2437                    let class_bounds = fn_class_generics.class_bounds.iter();
2438                    quote! { where #(#class_bounds),* }
2439                } else {
2440                    quote! {}
2441                };
2442                class_impl_def = Some(
2443                    quote! { impl<#(#class_lifetime_params,)* #(#class_bound_lifetime_params,)* #(#class_generic_params),*> #class <#(#class_lifetime_params,)* #(#class_generic_exprs),*> #impl_where_clause },
2444                );
2445            }
2446        };
2447
2448        // Function-level lifetime params
2449        let fn_lifetime_params = &fn_class_generics.fn_lifetime_params;
2450        let has_generics =
2451            !fn_class_generics.fn_generic_params.is_empty() || !fn_lifetime_params.is_empty();
2452        let impl_generics = if !has_generics {
2453            quote! {}
2454        } else {
2455            let fn_generic_params = fn_class_generics.fn_generic_params;
2456            quote! { <#(#fn_lifetime_params,)* #(#fn_generic_params),*> }
2457        };
2458        let has_bounds = !fn_class_generics.fn_bounds.is_empty();
2459        let where_clause = if !has_bounds {
2460            quote! {}
2461        } else {
2462            let fn_bounds = fn_class_generics.fn_bounds;
2463            quote! { where #(#fn_bounds),* }
2464        };
2465
2466        let invocation = quote! {
2467            // This is due to `#[automatically_derived]` attribute cannot be
2468            // placed onto bare functions.
2469            #[allow(nonstandard_style)]
2470            #[allow(clippy::all, clippy::nursery, clippy::pedantic, clippy::restriction)]
2471            #(#attrs)*
2472            #doc
2473            #vis #maybe_async #maybe_unsafe fn #rust_name #impl_generics (#me #(#arguments),*) #ret #where_clause {
2474                #extern_fn
2475
2476                unsafe {
2477                    let #ret_ident = {
2478                        #(#arg_conversions)*
2479                        #import_name(#(#abi_argument_names),*)
2480                    };
2481                    #exceptional_ret
2482                    #convert_ret
2483                }
2484            }
2485        };
2486
2487        if let Some(class_impl_def) = class_impl_def {
2488            quote! {
2489                #[automatically_derived]
2490                #class_impl_def {
2491                    #invocation
2492                }
2493            }
2494            .to_tokens(tokens);
2495        } else {
2496            invocation.to_tokens(tokens);
2497        }
2498
2499        Ok(())
2500    }
2501}
2502
2503// See comment above in ast::Export for what's going on here.
2504struct DescribeImport<'a> {
2505    kind: &'a ast::ImportKind,
2506    wasm_bindgen: &'a syn::Path,
2507}
2508
2509// Extracted impl block info given class generics and function-level method generics
2510struct FnClassGenerics<'a> {
2511    // the hoisted class-level param idents used, with identifiers renamed to use function generic identifier names
2512    class_generic_params: BTreeSet<syn::Ident>,
2513    // the struct generic expressions on those params
2514    class_generic_exprs: Vec<&'a syn::Type>,
2515    // class where bounds including hoisted function bounds
2516    class_bounds: Vec<Cow<'a, syn::WherePredicate>>,
2517    // the remaining non-hoisted function-level param idents
2518    fn_generic_params: Vec<&'a syn::Ident>,
2519    // function bounds on params which are only specific to the function not hoisted as class bounds
2520    fn_bounds: Vec<Cow<'a, syn::WherePredicate>>,
2521    // the union of class-level defaults (for identifier generics) and function defaults
2522    // this is used to form the concrete type via replacement (using JsValue otherwise)
2523    concrete_defaults: BTreeMap<&'a syn::Ident, Option<Cow<'a, syn::Type>>>,
2524    // hoisted class-level lifetime params passed to the type
2525    class_lifetime_params: Vec<&'a syn::Lifetime>,
2526    // hoisted class-level lifetime params only used in bounds (not passed to type)
2527    class_bound_lifetime_params: Vec<syn::Lifetime>,
2528    // the remaining non-hoisted function-level lifetime params
2529    fn_lifetime_params: Vec<&'a syn::Lifetime>,
2530}
2531
2532impl<'a> FnClassGenerics<'a> {
2533    /// Adds a new function bound, checking it is not already a bound
2534    fn add_fn_bound(&mut self, bound: syn::WherePredicate) {
2535        if !self.fn_bounds.iter().any(|existing| **existing == bound) {
2536            self.fn_bounds.push(Cow::Owned(bound));
2537        }
2538    }
2539}
2540
2541impl ast::ImportFunction {
2542    fn get_fn_generics<'a>(&'a self) -> Result<FnClassGenerics<'a>, Diagnostic> {
2543        let original_fn_generics = generics::generic_params(&self.generics);
2544        let mut fn_generic_params: Vec<&syn::Ident> =
2545            original_fn_generics.iter().map(|p| p.0).collect();
2546        let concrete_defaults: BTreeMap<_, _> = original_fn_generics
2547            .into_iter()
2548            .map(|(i, d)| (i, d.map(Cow::Borrowed)))
2549            .collect();
2550
2551        // Extract lifetime parameters
2552        let all_lifetime_params = generics::lifetime_params(&self.generics);
2553        let mut fn_lifetime_params: Vec<&syn::Lifetime> = all_lifetime_params.clone();
2554
2555        let mut where_predicates: Vec<Cow<syn::WherePredicate>> = Vec::new();
2556        for param in &self.generics.params {
2557            if let syn::GenericParam::Type(type_param) = param {
2558                if !type_param.bounds.is_empty() {
2559                    let ident = &type_param.ident;
2560                    let bounds = type_param.bounds.clone();
2561                    let predicate = syn::WherePredicate::Type(syn::PredicateType {
2562                        lifetimes: None,
2563                        bounded_ty: syn::parse_quote!(#ident),
2564                        colon_token: syn::Token![:](proc_macro2::Span::call_site()),
2565                        bounds,
2566                    });
2567                    where_predicates.push(Cow::Owned(predicate));
2568                }
2569            }
2570        }
2571
2572        let mut class_bounds = Vec::new();
2573        let mut fn_bounds = generics::generic_bounds(&self.generics);
2574        let mut class_generic_params = BTreeSet::new();
2575        let mut class_lifetime_params_set = BTreeSet::new();
2576        let mut class_bound_lifetime_params_set: BTreeSet<syn::Lifetime> = BTreeSet::new();
2577        let mut class_generic_exprs = Vec::new();
2578
2579        let mut class = None;
2580        if let ast::ImportFunctionKind::Method {
2581            ty,
2582            kind:
2583                ast::MethodKind::Operation(ast::Operation {
2584                    is_static: false, ..
2585                }),
2586            ..
2587        } = &self.kind
2588        {
2589            let syn::Type::Path(syn::TypePath { path, .. }) = ty else {
2590                unreachable!(); // validated at parse time
2591            };
2592            class = Some(path);
2593        }
2594
2595        // For constructors and static methods whose return type matches the class
2596        // (e.g. `Array::of<T>() -> Array<T>`), use the return type path for hoisting
2597        // since it carries the generic arguments.
2598        if class.is_none() {
2599            class = self.class_return_path();
2600        }
2601
2602        if let Some(cls_path) = class {
2603            if let Some(syn::PathSegment {
2604                arguments: syn::PathArguments::AngleBracketed(gen_args),
2605                ..
2606            }) = cls_path.segments.last()
2607            {
2608                // Iterate the &self<expr1, expr2, ...> gen args, as the class_generic_exprs Vec
2609                for gen_arg in gen_args.args.iter() {
2610                    // Handle lifetime arguments for hoisting
2611                    if let syn::GenericArgument::Lifetime(lt) = gen_arg {
2612                        if all_lifetime_params.contains(&lt) {
2613                            class_lifetime_params_set.insert(lt.clone());
2614                        }
2615                        continue;
2616                    }
2617
2618                    let syn::GenericArgument::Type(ty) = gen_arg else {
2619                        bail_span!(gen_arg, "Functions must provide generic arguments");
2620                    };
2621
2622                    class_generic_exprs.push(ty);
2623
2624                    // Visit the generic expression, adding all used function generics to the hoisted class generic params
2625                    class_generic_params =
2626                        generics::used_generic_params(ty, &fn_generic_params, class_generic_params);
2627
2628                    // Also find lifetimes used in class generic expressions
2629                    let used_lifetimes = generics::used_lifetimes_in_type(ty, &all_lifetime_params);
2630                    class_lifetime_params_set.extend(used_lifetimes);
2631                }
2632
2633                // Transitively hoist generic params and lifetimes that are used in bounds OF already-hoisted params.
2634                // For example, if F is hoisted and has bound `F: JsFunction<Ret = Ret>`, then Ret
2635                // must also be hoisted since it appears in a bound on F. Same for lifetimes.
2636                // We only hoist from bounds where the bounded_ty IS the class param (not just mentions it).
2637                loop {
2638                    let remaining_fn_params: Vec<&Ident> = fn_generic_params
2639                        .iter()
2640                        .filter(|p| !class_generic_params.contains(*p))
2641                        .copied()
2642                        .collect();
2643
2644                    let remaining_fn_lifetimes: Vec<&syn::Lifetime> = fn_lifetime_params
2645                        .iter()
2646                        .filter(|lt| {
2647                            !class_lifetime_params_set.contains(*lt)
2648                                && !class_bound_lifetime_params_set.contains(*lt)
2649                        })
2650                        .copied()
2651                        .collect();
2652
2653                    let mut params_to_add = Vec::new();
2654                    let mut lifetimes_to_add = BTreeSet::new();
2655
2656                    for bound in &fn_bounds {
2657                        // Only process bounds where the bounded type IS a class param
2658                        // e.g., for `F: JsFunction<Ret = Ret>`, bounded_ty is `F`
2659                        if let syn::WherePredicate::Type(pred_type) = bound.as_ref() {
2660                            if let syn::Type::Path(type_path) = &pred_type.bounded_ty {
2661                                if type_path.qself.is_none() && type_path.path.segments.len() == 1 {
2662                                    let bounded_ident = &type_path.path.segments[0].ident;
2663                                    if class_generic_params.contains(bounded_ident) {
2664                                        // This bound is ON a class param, check for fn params and lifetimes used in the bounds
2665                                        let mut found_set = BTreeSet::new();
2666                                        let mut visitor = generics::GenericNameVisitor::new(
2667                                            &remaining_fn_params,
2668                                            &mut found_set,
2669                                        );
2670                                        for type_bound in &pred_type.bounds {
2671                                            syn::visit::Visit::visit_type_param_bound(
2672                                                &mut visitor,
2673                                                type_bound,
2674                                            );
2675                                        }
2676                                        params_to_add.extend(found_set);
2677
2678                                        // Also hoist lifetimes from the same bounds
2679                                        let used = generics::used_lifetimes_in_bounds(
2680                                            &pred_type.bounds,
2681                                            &remaining_fn_lifetimes,
2682                                        );
2683                                        lifetimes_to_add.extend(used);
2684                                    }
2685                                }
2686                            }
2687                        }
2688                    }
2689
2690                    if params_to_add.is_empty() && lifetimes_to_add.is_empty() {
2691                        break;
2692                    }
2693                    for param in params_to_add {
2694                        class_generic_params.insert(param);
2695                    }
2696                    for lt in lifetimes_to_add {
2697                        class_bound_lifetime_params_set.insert(lt);
2698                    }
2699                }
2700
2701                let class_generic_params_refs: Vec<&Ident> = class_generic_params.iter().collect();
2702
2703                // fn generic params are all params not hoisted as class params
2704                fn_generic_params = fn_generic_params
2705                    .iter()
2706                    .copied()
2707                    .filter(|&p| !class_generic_params.contains(p))
2708                    .collect();
2709
2710                // fn lifetime params are all lifetime params not hoisted as class lifetime params
2711                fn_lifetime_params.retain(|&lt| {
2712                    !class_lifetime_params_set.contains(lt)
2713                        && !class_bound_lifetime_params_set.contains(lt)
2714                });
2715
2716                // hoist function where bounds on class generic params
2717                fn_bounds.retain(|bound| {
2718                    if generics::generics_predicate_uses(bound, &class_generic_params_refs)
2719                        && !generics::generics_predicate_uses(bound, &fn_generic_params)
2720                    {
2721                        class_bounds.push(bound.clone());
2722                        false
2723                    } else {
2724                        true
2725                    }
2726                });
2727            }
2728        }
2729
2730        // Convert class_lifetime_params_set to Vec, maintaining order from original params
2731        let class_lifetime_params: Vec<&syn::Lifetime> = all_lifetime_params
2732            .iter()
2733            .copied()
2734            .filter(|lt| class_lifetime_params_set.contains(*lt))
2735            .collect();
2736
2737        // Convert class_bound_lifetime_params_set to Vec, maintaining order from original params
2738        let class_bound_lifetime_params: Vec<syn::Lifetime> = all_lifetime_params
2739            .iter()
2740            .copied()
2741            .filter(|lt| class_bound_lifetime_params_set.contains(*lt))
2742            .cloned()
2743            .collect();
2744
2745        Ok(FnClassGenerics {
2746            class_generic_params,
2747            class_generic_exprs,
2748            class_bounds,
2749            fn_generic_params,
2750            fn_bounds,
2751            concrete_defaults,
2752            class_lifetime_params,
2753            class_bound_lifetime_params,
2754            fn_lifetime_params,
2755        })
2756    }
2757
2758    /// For constructors and static methods (via `static_method_of`), checks whether
2759    /// the return type matches the class name. If so, returns the path from `js_ret`
2760    /// which carries any generic arguments (e.g. `Array<T>`).
2761    ///
2762    /// This is used to determine when class-level generic hoisting should apply:
2763    ///  - Constructors always return their own class, so this always matches.
2764    ///  - Static methods like `#[wasm_bindgen(static_method_of = Array, js_name = of)]`
2765    ///    returning `Array<T>` also match, and need the same hoisting treatment.
2766    ///
2767    /// For static methods, since we are *inferring* that hoisting should happen (the
2768    /// user didn't explicitly opt in like with `constructor`), we only match when all
2769    /// type generic arguments are bare type parameter idents (e.g. `Array<T>`). Cases
2770    /// like `Array<I::Item>` or `Promise<U::Resolution>` are left as plain static
2771    /// methods — the associated type is a function-level concern, not a class property.
2772    fn class_return_path(&self) -> Option<&syn::Path> {
2773        let ast::ImportFunctionKind::Method {
2774            class: class_name,
2775            kind,
2776            ..
2777        } = &self.kind
2778        else {
2779            return None;
2780        };
2781
2782        let is_constructor = matches!(kind, ast::MethodKind::Constructor);
2783        let is_static = matches!(
2784            kind,
2785            ast::MethodKind::Operation(ast::Operation {
2786                is_static: true,
2787                ..
2788            })
2789        );
2790
2791        if !is_constructor && !is_static {
2792            return None;
2793        }
2794
2795        let ret_ty = self.js_ret.as_ref()?;
2796        let syn::Type::Path(syn::TypePath {
2797            qself: None,
2798            ref path,
2799        }) = get_ty(ret_ty)
2800        else {
2801            return None;
2802        };
2803
2804        let seg = path.segments.last()?;
2805        if seg.ident != class_name.as_str() {
2806            return None;
2807        }
2808
2809        // Only hoist fn generics onto the class impl header when every fn
2810        // generic mentioned in the return type's args appears in a
2811        // *structurally constraining* position (per E0207 / RFC 0447).
2812        //
2813        // Non-constraining positions — projections (`<T as Trait>::Assoc`,
2814        // `T::Item`), fn-ptr slots (`fn(T)` / `Fn(T)` sugar), associated-type
2815        // binding RHS, etc. — would produce an `impl<T> Ret<...>` whose `T`
2816        // is not determinable from `Self`, yielding a borrow-check-level
2817        // compilation error. When we detect such a shape, bail so the
2818        // parameter stays function-level.
2819        //
2820        // This replaces the earlier "static methods must have only bare
2821        // idents" heuristic, which was both too strict (rejected valid
2822        // shapes like `Array<Option<T>>`) and too narrow (didn't apply to
2823        // constructors, leading to E0207 for `Promise<<T as Promising>::Resolution>`).
2824        if let syn::PathArguments::AngleBracketed(ref gen_args) = seg.arguments {
2825            let fn_params: Vec<&Ident> = generics::generic_params(&self.generics)
2826                .iter()
2827                .map(|p| p.0)
2828                .collect();
2829            if !generics::args_are_constraining_for(&gen_args.args, &fn_params) {
2830                return None;
2831            }
2832        }
2833
2834        Some(path)
2835    }
2836}
2837
2838impl TryToTokens for DescribeImport<'_> {
2839    fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic> {
2840        let f = match *self.kind {
2841            ast::ImportKind::Function(ref f) => f,
2842            ast::ImportKind::Static(_) => return Ok(()),
2843            ast::ImportKind::String(_) => return Ok(()),
2844            ast::ImportKind::Type(_) => return Ok(()),
2845            ast::ImportKind::Enum(_) => return Ok(()),
2846            ast::ImportKind::DynamicUnion(_) => return Ok(()),
2847        };
2848        let fn_class_generics = f.get_fn_generics()?;
2849        let fn_lifetime_params = generics::lifetime_params(&f.generics);
2850        let argtys = f
2851            .function
2852            .arguments
2853            .iter()
2854            .map(|arg| {
2855                let ty = generics::generic_to_concrete(
2856                    (*arg.pat_type.ty).clone(),
2857                    &fn_class_generics.concrete_defaults,
2858                    &fn_lifetime_params,
2859                )?;
2860                // For `slice_to_array` args, describe through `&Vec<T>` (or
2861                // `Option<&Vec<T>>`) to match the ABI rewrite in
2862                // `ImportFunction::try_to_tokens` — the descriptor shape is
2863                // `Ref(Vector(T))`, which the cli-support side recognises.
2864                // Non-slice args (e.g. `this: &Foo` of a method) under a
2865                // fn- or block-level `slice_to_array` silently fall through
2866                // to their default describe — slice_to_array is a mode that
2867                // only acts on slice-shaped args.
2868                if arg.slice_to_array {
2869                    if let Some((elem_ty, is_option)) = detect_slice_or_option_slice(&ty) {
2870                        if is_option {
2871                            return Ok(parse_quote! {
2872                                ::core::option::Option<&::std::vec::Vec<#elem_ty>>
2873                            });
2874                        } else {
2875                            return Ok(parse_quote! { &::std::vec::Vec<#elem_ty> });
2876                        }
2877                    }
2878                }
2879                Ok(ty)
2880            })
2881            .collect::<Result<Vec<syn::Type>, Diagnostic>>()?;
2882        let nargs = f.function.arguments.len() as u32;
2883        let inform_ret = match &f.js_ret {
2884            Some(ref t) => {
2885                let t = generics::generic_to_concrete(
2886                    t.clone(),
2887                    &fn_class_generics.concrete_defaults,
2888                    &fn_lifetime_params,
2889                )?;
2890                quote! { <#t as WasmDescribe>::describe(); }
2891            }
2892            // async functions always return a JsValue, even if they say to return ()
2893            None if f.function.r#async => quote! { <JsValue as WasmDescribe>::describe(); },
2894            None => quote! { <() as WasmDescribe>::describe(); },
2895        };
2896
2897        Descriptor {
2898            ident: &f.shim,
2899            inner: quote! {
2900                inform(FUNCTION);
2901                inform(0);
2902                inform(#nargs);
2903                #(<#argtys as WasmDescribe>::describe();)*
2904                #inform_ret
2905                #inform_ret
2906            },
2907            attrs: f.function.rust_attrs.clone(),
2908            wasm_bindgen: self.wasm_bindgen,
2909        }
2910        .to_tokens(tokens);
2911        Ok(())
2912    }
2913}
2914
2915impl ToTokens for ast::Enum {
2916    fn to_tokens(&self, into: &mut TokenStream) {
2917        let enum_name = &self.rust_name;
2918        let name_str = shared::qualified_name(self.js_namespace.as_deref(), &self.js_name);
2919        let name_len = name_str.len() as u32;
2920        let name_chars = name_str.chars().map(|c| c as u32);
2921        let hole = &self.hole;
2922        let underlying = if self.signed {
2923            quote! { i32 }
2924        } else {
2925            quote! { u32 }
2926        };
2927        let cast_clauses = self.variants.iter().map(|variant| {
2928            let variant_name = &variant.rust_name;
2929            quote! {
2930                if js == #enum_name::#variant_name as #underlying {
2931                    #enum_name::#variant_name
2932                }
2933            }
2934        });
2935        let try_from_cast_clauses = cast_clauses.clone();
2936        let wasm_bindgen = &self.wasm_bindgen;
2937        (quote! {
2938            #[automatically_derived]
2939            impl #wasm_bindgen::convert::IntoWasmAbi for #enum_name {
2940                type Abi = #underlying;
2941
2942                #[inline]
2943                fn into_abi(self) -> #underlying {
2944                    self as #underlying
2945                }
2946            }
2947
2948            #[automatically_derived]
2949            impl #wasm_bindgen::convert::FromWasmAbi for #enum_name {
2950                type Abi = #underlying;
2951
2952                #[inline]
2953                unsafe fn from_abi(js: #underlying) -> Self {
2954                    #(#cast_clauses else)* {
2955                        #wasm_bindgen::throw_str("invalid enum value passed")
2956                    }
2957                }
2958            }
2959
2960            #[automatically_derived]
2961            impl #wasm_bindgen::convert::OptionFromWasmAbi for #enum_name {
2962                #[inline]
2963                fn is_none(val: &Self::Abi) -> bool { *val == #hole as #underlying }
2964            }
2965
2966            #[automatically_derived]
2967            impl #wasm_bindgen::convert::OptionIntoWasmAbi for #enum_name {
2968                #[inline]
2969                fn none() -> Self::Abi { #hole as #underlying }
2970            }
2971
2972            #[automatically_derived]
2973            impl #wasm_bindgen::describe::WasmDescribe for #enum_name {
2974                fn describe() {
2975                    use #wasm_bindgen::describe::*;
2976                    inform(ENUM);
2977                    inform(#name_len);
2978                    #(inform(#name_chars);)*
2979                    inform(#hole);
2980                }
2981            }
2982
2983            #[automatically_derived]
2984            impl #wasm_bindgen::__rt::core::convert::From<#enum_name> for
2985                #wasm_bindgen::JsValue
2986            {
2987                fn from(value: #enum_name) -> Self {
2988                    #wasm_bindgen::JsValue::from_f64((value as #underlying).into())
2989                }
2990            }
2991
2992            #[automatically_derived]
2993            impl #wasm_bindgen::convert::TryFromJsValue for #enum_name {
2994                fn try_from_js_value_ref(value: &#wasm_bindgen::JsValue) -> #wasm_bindgen::__rt::core::option::Option<Self> {
2995                    let js = value.as_f64()? as #underlying;
2996
2997                    #wasm_bindgen::__rt::core::option::Option::Some(
2998                        #(#try_from_cast_clauses else)* {
2999                            return #wasm_bindgen::__rt::core::option::Option::None;
3000                        }
3001                    )
3002                }
3003            }
3004
3005            #[automatically_derived]
3006            impl #wasm_bindgen::describe::WasmDescribeVector for #enum_name {
3007                fn describe_vector() {
3008                    use #wasm_bindgen::describe::*;
3009                    inform(VECTOR);
3010                    <#wasm_bindgen::JsValue as #wasm_bindgen::describe::WasmDescribe>::describe();
3011                }
3012            }
3013
3014            #[automatically_derived]
3015            impl #wasm_bindgen::convert::VectorIntoWasmAbi for #enum_name {
3016                type Abi = <
3017                    #wasm_bindgen::__rt::alloc::boxed::Box<[#wasm_bindgen::JsValue]>
3018                    as #wasm_bindgen::convert::IntoWasmAbi
3019                >::Abi;
3020
3021                fn vector_into_abi(
3022                    vector: #wasm_bindgen::__rt::alloc::boxed::Box<[#enum_name]>
3023                ) -> Self::Abi {
3024                    #wasm_bindgen::convert::js_value_vector_into_abi(vector)
3025                }
3026            }
3027
3028            #[automatically_derived]
3029            impl #wasm_bindgen::convert::VectorFromWasmAbi for #enum_name {
3030                type Abi = <
3031                    #wasm_bindgen::__rt::alloc::boxed::Box<[#wasm_bindgen::JsValue]>
3032                    as #wasm_bindgen::convert::FromWasmAbi
3033                >::Abi;
3034
3035                unsafe fn vector_from_abi(
3036                    js: Self::Abi
3037                ) -> #wasm_bindgen::__rt::alloc::boxed::Box<[#enum_name]> {
3038                    #wasm_bindgen::convert::js_value_vector_from_abi(js)
3039                }
3040            }
3041        })
3042        .to_tokens(into);
3043    }
3044}
3045
3046impl ToTokens for ast::ImportStatic {
3047    fn to_tokens(&self, into: &mut TokenStream) {
3048        let ty = &self.ty;
3049
3050        if let Some(thread_local) = self.thread_local {
3051            thread_local_import(
3052                &self.vis,
3053                &self.rust_name,
3054                &self.wasm_bindgen,
3055                ty,
3056                ty,
3057                &self.shim,
3058                thread_local,
3059            )
3060            .to_tokens(into)
3061        } else {
3062            let vis = &self.vis;
3063            let name = &self.rust_name;
3064            let wasm_bindgen = &self.wasm_bindgen;
3065            let ty = &self.ty;
3066            let shim_name = &self.shim;
3067            let init = static_init(wasm_bindgen, ty, shim_name);
3068
3069            into.extend(quote! {
3070                #[automatically_derived]
3071                #[deprecated = "use with `#[wasm_bindgen(thread_local_v2)]` instead"]
3072            });
3073            into.extend(
3074                quote_spanned! { name.span() => #vis static #name: #wasm_bindgen::JsStatic<#ty> = {
3075                        fn init() -> #ty {
3076                            #init
3077                        }
3078                        #wasm_bindgen::__rt::std::thread_local!(static _VAL: #ty = init(););
3079                        #wasm_bindgen::JsStatic {
3080                            __inner: &_VAL,
3081                        }
3082                    };
3083                },
3084            );
3085        }
3086
3087        Descriptor {
3088            ident: &self.shim,
3089            inner: quote! {
3090                <#ty as WasmDescribe>::describe();
3091            },
3092            attrs: vec![],
3093            wasm_bindgen: &self.wasm_bindgen,
3094        }
3095        .to_tokens(into);
3096    }
3097}
3098
3099impl ToTokens for ast::ImportString {
3100    fn to_tokens(&self, into: &mut TokenStream) {
3101        let js_sys = &self.js_sys;
3102        let actual_ty: syn::Type = parse_quote!(#js_sys::JsString);
3103
3104        thread_local_import(
3105            &self.vis,
3106            &self.rust_name,
3107            &self.wasm_bindgen,
3108            &actual_ty,
3109            &self.ty,
3110            &self.shim,
3111            self.thread_local,
3112        )
3113        .to_tokens(into);
3114    }
3115}
3116
3117fn thread_local_import(
3118    vis: &syn::Visibility,
3119    name: &Ident,
3120    wasm_bindgen: &syn::Path,
3121    actual_ty: &syn::Type,
3122    ty: &syn::Type,
3123    shim_name: &Ident,
3124    thread_local: ast::ThreadLocal,
3125) -> TokenStream {
3126    let init = static_init(wasm_bindgen, ty, shim_name);
3127
3128    match thread_local {
3129        ast::ThreadLocal::V1 => quote! {
3130            #wasm_bindgen::__rt::std::thread_local! {
3131                #[automatically_derived]
3132                #[deprecated = "use with `#[wasm_bindgen(thread_local_v2)]` instead"]
3133                #vis static #name: #actual_ty = {
3134                    #init
3135                };
3136            }
3137        },
3138        ast::ThreadLocal::V2 => {
3139            quote! {
3140                #vis static #name: #wasm_bindgen::JsThreadLocal<#actual_ty> = {
3141                    fn init() -> #actual_ty {
3142                        #init
3143                    }
3144                    #wasm_bindgen::__wbindgen_thread_local!(#wasm_bindgen, #actual_ty)
3145                };
3146            }
3147        }
3148    }
3149}
3150
3151fn static_init(wasm_bindgen: &syn::Path, ty: &syn::Type, shim_name: &Ident) -> TokenStream {
3152    let abi_ret = quote! {
3153        #wasm_bindgen::convert::WasmRet<<#ty as #wasm_bindgen::convert::FromWasmAbi>::Abi>
3154    };
3155    quote! {
3156        #[link(wasm_import_module = "__wbindgen_placeholder__")]
3157        #[cfg(all(target_family = "wasm", not(target_os = "wasi")))]
3158        extern "C" {
3159            fn #shim_name() -> #abi_ret;
3160        }
3161
3162        #[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))]
3163        unsafe fn #shim_name() -> #abi_ret {
3164            panic!("cannot access imported statics on non-wasm targets")
3165        }
3166
3167        unsafe {
3168            <#ty as #wasm_bindgen::convert::FromWasmAbi>::from_abi(#shim_name().join())
3169        }
3170    }
3171}
3172
3173/// Emits the necessary glue tokens for "descriptor", generating an appropriate
3174/// symbol name as well as attributes around the descriptor function itself.
3175struct Descriptor<'a, T> {
3176    ident: &'a Ident,
3177    inner: T,
3178    attrs: Vec<syn::Attribute>,
3179    wasm_bindgen: &'a syn::Path,
3180}
3181
3182impl<T: ToTokens> ToTokens for Descriptor<'_, T> {
3183    fn to_tokens(&self, tokens: &mut TokenStream) {
3184        // It's possible for the same descriptor to be emitted in two different
3185        // modules (aka a value imported twice in a crate, each in a separate
3186        // module). In this case no need to emit duplicate descriptors (which
3187        // leads to duplicate symbol errors), instead just emit one.
3188        //
3189        // It's up to the descriptors themselves to ensure they have unique
3190        // names for unique items imported, currently done via `ShortHash` and
3191        // hashing appropriate data into the symbol name.
3192        thread_local! {
3193            static DESCRIPTORS_EMITTED: RefCell<HashSet<String>> = RefCell::default();
3194        }
3195
3196        let ident = self.ident;
3197
3198        if !DESCRIPTORS_EMITTED.with(|list| list.borrow_mut().insert(ident.to_string())) {
3199            return;
3200        }
3201
3202        let name = Ident::new(&format!("__wbindgen_describe_{ident}"), ident.span());
3203        let inner = &self.inner;
3204        let attrs = &self.attrs;
3205        let wasm_bindgen = &self.wasm_bindgen;
3206        (quote! {
3207            #[cfg(all(target_family = "wasm", not(target_os = "wasi")))]
3208            #[automatically_derived]
3209            const _: () = {
3210                #wasm_bindgen::__wbindgen_coverage! {
3211                #(#attrs)*
3212                #[no_mangle]
3213                #[doc(hidden)]
3214                pub extern "C-unwind" fn #name() {
3215                    use #wasm_bindgen::describe::*;
3216                    // See definition of `link_mem_intrinsics` for what this is doing
3217                    #wasm_bindgen::__rt::link_mem_intrinsics();
3218                    #inner
3219                }
3220                }
3221            };
3222        })
3223        .to_tokens(tokens);
3224    }
3225}
3226
3227fn extern_fn(
3228    import_name: &Ident,
3229    attrs: &[syn::Attribute],
3230    abi_arguments: &[TokenStream],
3231    abi_argument_names: &[Ident],
3232    abi_ret: TokenStream,
3233) -> TokenStream {
3234    quote! {
3235        #[cfg(all(target_family = "wasm", not(target_os = "wasi")))]
3236        #(#attrs)*
3237        #[link(wasm_import_module = "__wbindgen_placeholder__")]
3238        extern "C" {
3239            fn #import_name(#(#abi_arguments),*) -> #abi_ret;
3240        }
3241
3242        #[cfg(not(all(target_family = "wasm", not(target_os = "wasi"))))]
3243        unsafe fn #import_name(#(#abi_arguments),*) -> #abi_ret {
3244            #(
3245                drop(#abi_argument_names);
3246            )*
3247            panic!("cannot call wasm-bindgen imported functions on \
3248                    non-wasm targets");
3249        }
3250    }
3251}
3252
3253/// Splats an argument with the given name and ABI type into 4 arguments, one
3254/// for each primitive that the ABI type splits into.
3255///
3256/// Returns an `(args, names)` pair, where `args` is the list of arguments to
3257/// be inserted into the function signature, and `names` is a list of the names
3258/// of those arguments.
3259fn splat(
3260    wasm_bindgen: &syn::Path,
3261    name: &Ident,
3262    abi: &TokenStream,
3263) -> (Vec<TokenStream>, Vec<Ident>) {
3264    let mut args = Vec::new();
3265    let mut names = Vec::new();
3266
3267    for n in 1_u32..=4 {
3268        let arg_name = format_ident!("{}_{}", name, n);
3269        let prim_name = format_ident!("Prim{}", n);
3270        args.push(quote! {
3271            #arg_name: <#abi as #wasm_bindgen::convert::WasmAbi>::#prim_name
3272        });
3273        names.push(arg_name);
3274    }
3275
3276    (args, names)
3277}
3278
3279/// Converts `span` into a stream of tokens, and attempts to ensure that `input`
3280/// has all the appropriate span information so errors in it point to `span`.
3281fn respan(input: TokenStream, span: &dyn ToTokens) -> TokenStream {
3282    let mut first_span = Span::call_site();
3283    let mut last_span = Span::call_site();
3284    let mut spans = TokenStream::new();
3285    span.to_tokens(&mut spans);
3286
3287    for (i, token) in spans.into_iter().enumerate() {
3288        if i == 0 {
3289            first_span = Span::call_site().located_at(token.span());
3290        }
3291        last_span = Span::call_site().located_at(token.span());
3292    }
3293
3294    let mut new_tokens = Vec::new();
3295    for (i, mut token) in input.into_iter().enumerate() {
3296        if i == 0 {
3297            token.set_span(first_span);
3298        } else {
3299            token.set_span(last_span);
3300        }
3301        new_tokens.push(token);
3302    }
3303    new_tokens.into_iter().collect()
3304}
3305
3306fn get_ty(mut ty: &syn::Type) -> &syn::Type {
3307    while let syn::Type::Group(g) = ty {
3308        ty = &g.elem;
3309    }
3310    ty
3311}
3312
3313/// Detects whether a type is a raw `&dyn Fn(...)` or `&mut dyn FnMut(...)` argument.
3314///
3315/// Returns `Some((is_mut, fn_trait_bounds))` where:
3316/// - `is_mut` is `true` for `&mut dyn FnMut`, `false` for `&dyn Fn`
3317/// - `fn_trait_bounds` are the `TypeParamBound`s from the `dyn` trait object (e.g. `FnMut(A)->R`)
3318///
3319/// This is used by the import function codegen to auto-inject `MaybeUnwindSafe`
3320/// bounds for closure arguments, ensuring unwind safety when `panic = "unwind"`.
3321/// Recognise `&[T]` and `Option<&[T]>` argument types. Returns the element
3322/// type plus a flag indicating whether the outer `Option` was present. Used
3323/// by the `slice_to_array` codegen to rewrite the ABI path.
3324fn detect_slice_or_option_slice(ty: &syn::Type) -> Option<(syn::Type, bool)> {
3325    // Direct `&[T]` (mutability ignored — `&mut [T]` is intentionally
3326    // accepted too; the ABI layer treats it the same as `&[T]`).
3327    if let syn::Type::Reference(syn::TypeReference { elem, .. }) = ty {
3328        if let syn::Type::Slice(syn::TypeSlice { elem: inner, .. }) = &**elem {
3329            return Some(((**inner).clone(), false));
3330        }
3331    }
3332    // `Option<&[T]>` — match shape `Option<...>` and recurse once.
3333    if let syn::Type::Path(syn::TypePath { qself: None, path }) = ty {
3334        if let Some(seg) = path.segments.last() {
3335            if seg.ident == "Option" {
3336                if let syn::PathArguments::AngleBracketed(args) = &seg.arguments {
3337                    if args.args.len() == 1 {
3338                        if let syn::GenericArgument::Type(inner) = &args.args[0] {
3339                            if let Some((elem, false)) = detect_slice_or_option_slice(inner) {
3340                                return Some((elem, true));
3341                            }
3342                        }
3343                    }
3344                }
3345            }
3346        }
3347    }
3348    None
3349}
3350
3351fn detect_raw_fn_trait_obj(
3352    ty: &syn::Type,
3353) -> Option<(
3354    bool,
3355    &syn::punctuated::Punctuated<syn::TypeParamBound, syn::token::Plus>,
3356)> {
3357    let syn::Type::Reference(syn::TypeReference {
3358        mutability, elem, ..
3359    }) = ty
3360    else {
3361        return None;
3362    };
3363    let inner = get_ty(elem);
3364    let syn::Type::TraitObject(trait_obj) = inner else {
3365        return None;
3366    };
3367    let is_mut = mutability.is_some();
3368    // Check that the primary bound is Fn or FnMut (matching mutability)
3369    for bound in &trait_obj.bounds {
3370        if let syn::TypeParamBound::Trait(tb) = bound {
3371            if let Some(last_seg) = tb.path.segments.last() {
3372                let name = last_seg.ident.to_string();
3373                if is_mut && name == "FnMut" {
3374                    return Some((true, &trait_obj.bounds));
3375                }
3376                if !is_mut && name == "Fn" {
3377                    return Some((false, &trait_obj.bounds));
3378                }
3379            }
3380        }
3381    }
3382    None
3383}