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