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