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
18pub trait TryToTokens {
21 fn try_to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostic>;
23
24 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 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 let Some(nss) = &i.js_namespace {
60 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 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 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 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 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 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 let ptr = ptr.into_ptr();
324 #wasm_bindgen::__rt::assert_not_null(ptr);
325 drop(Rc::from_raw(ptr));
326 } else {
327 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 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 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 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 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 #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 #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 #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 #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 #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 #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 #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 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 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 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 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 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 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 #[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 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 if !self.no_upcast {
1483 (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 let type_params: Vec<_> = self.generics.type_params().collect();
1497 if type_params.is_empty() {
1498 (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 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 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 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 let target_lifetime_params = generics::lifetime_params(&self.generics);
1561 let target_ty_generics =
1562 quote! { <#(#target_lifetime_params,)* #(#target_param_names),*> };
1563
1564 (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 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
1606impl 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 let variant_paths: Vec<TokenStream> = self
1647 .variants
1648 .iter()
1649 .map(|v| quote!(#enum_name::#v).into_token_stream())
1650 .collect();
1651
1652 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 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 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 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 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 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 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 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 #[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 #[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 #[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 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 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 = 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 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 if is_mut {
2189 abi_ty = quote! { &mut dyn #fn_bounds };
2190 } else {
2191 abi_ty = quote! { &dyn #fn_bounds };
2192 }
2193
2194 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 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 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 class_impl_def = Some(quote! { impl #class });
2429 } else {
2430 let class_lifetime_params = &fn_class_generics.class_lifetime_params;
2432 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 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 #[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
2503struct DescribeImport<'a> {
2505 kind: &'a ast::ImportKind,
2506 wasm_bindgen: &'a syn::Path,
2507}
2508
2509struct FnClassGenerics<'a> {
2511 class_generic_params: BTreeSet<syn::Ident>,
2513 class_generic_exprs: Vec<&'a syn::Type>,
2515 class_bounds: Vec<Cow<'a, syn::WherePredicate>>,
2517 fn_generic_params: Vec<&'a syn::Ident>,
2519 fn_bounds: Vec<Cow<'a, syn::WherePredicate>>,
2521 concrete_defaults: BTreeMap<&'a syn::Ident, Option<Cow<'a, syn::Type>>>,
2524 class_lifetime_params: Vec<&'a syn::Lifetime>,
2526 class_bound_lifetime_params: Vec<syn::Lifetime>,
2528 fn_lifetime_params: Vec<&'a syn::Lifetime>,
2530}
2531
2532impl<'a> FnClassGenerics<'a> {
2533 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 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),
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!(); };
2592 class = Some(path);
2593 }
2594
2595 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 for gen_arg in gen_args.args.iter() {
2610 if let syn::GenericArgument::Lifetime(lt) = gen_arg {
2612 if all_lifetime_params.contains(<) {
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 class_generic_params =
2626 generics::used_generic_params(ty, &fn_generic_params, class_generic_params);
2627
2628 let used_lifetimes = generics::used_lifetimes_in_type(ty, &all_lifetime_params);
2630 class_lifetime_params_set.extend(used_lifetimes);
2631 }
2632
2633 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 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 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 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 = fn_generic_params
2705 .iter()
2706 .copied()
2707 .filter(|&p| !class_generic_params.contains(p))
2708 .collect();
2709
2710 fn_lifetime_params.retain(|<| {
2712 !class_lifetime_params_set.contains(lt)
2713 && !class_bound_lifetime_params_set.contains(lt)
2714 });
2715
2716 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 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 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 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 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 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 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
3173struct 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 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 #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
3253fn 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
3279fn 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
3313fn detect_slice_or_option_slice(ty: &syn::Type) -> Option<(syn::Type, bool)> {
3325 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 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 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}