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