1use std::collections::HashSet;
24
25use proc_macro2::TokenStream;
26use quote::quote;
27
28use crate::codegen::signatures::{
29 dedupe_name, expand_signatures, generate_concrete_params, is_void_return, ExpandedSignature,
30 SignatureKind,
31};
32use crate::codegen::typemap::{to_return_type, to_syn_type, CodegenContext, TypePosition};
33use crate::ir::{
34 ClassDecl, GetterMember, InterfaceClassification, InterfaceDecl, Member, ModuleContext,
35 SetterMember, StaticGetterMember, StaticSetterMember, TypeRef,
36};
37use crate::parse::scope::ScopeId;
38use crate::util::naming::to_snake_case;
39
40struct ClassConfig<'a> {
42 rust_name: String,
44 js_name: String,
46 extends: Vec<TokenStream>,
48 module: Option<std::rc::Rc<str>>,
50 js_namespace: Option<String>,
52 is_abstract: bool,
54 members: Vec<Member>,
56 cgctx: Option<&'a CodegenContext<'a>>,
58 scope: ScopeId,
60}
61
62impl<'a> ClassConfig<'a> {
63 fn from_class(
64 decl: &ClassDecl,
65 ctx: &ModuleContext,
66 cgctx: Option<&'a CodegenContext>,
67 scope: ScopeId,
68 ) -> Self {
69 let extends = match &decl.extends {
70 Some(e) => vec![extends_tokens(e, cgctx, scope)],
71 None => vec![quote! { Object }],
72 };
73 let module = match ctx {
74 ModuleContext::Module(m) => Some(m.clone()),
75 ModuleContext::Global => None,
76 };
77
78 ClassConfig {
79 rust_name: decl.name.clone(),
80 js_name: decl.js_name.clone(),
81 extends,
82 module,
83 js_namespace: None,
84 is_abstract: decl.is_abstract,
85 members: decl.members.clone(),
86 cgctx,
87 scope,
88 }
89 }
90
91 fn from_interface(
92 decl: &InterfaceDecl,
93 ctx: &ModuleContext,
94 cgctx: Option<&'a CodegenContext>,
95 scope: ScopeId,
96 ) -> Self {
97 let extends = if decl.extends.is_empty() {
98 vec![quote! { Object }]
99 } else {
100 decl.extends
101 .iter()
102 .map(|e| extends_tokens(e, cgctx, scope))
103 .collect()
104 };
105 let module = match ctx {
106 ModuleContext::Module(m) => Some(m.clone()),
107 ModuleContext::Global => None,
108 };
109
110 ClassConfig {
111 rust_name: decl.name.clone(),
112 js_name: decl.js_name.clone(),
113 extends,
114 module,
115 js_namespace: None,
116 is_abstract: false,
117 members: decl.members.clone(),
118 cgctx,
119 scope,
120 }
121 }
122}
123
124pub fn generate_class(
126 decl: &ClassDecl,
127 ctx: &ModuleContext,
128 cgctx: Option<&CodegenContext<'_>>,
129 scope: ScopeId,
130) -> TokenStream {
131 let config = ClassConfig::from_class(decl, ctx, cgctx, scope);
132 generate_extern_block(&config)
133}
134
135pub fn generate_class_like_interface(
137 decl: &InterfaceDecl,
138 ctx: &ModuleContext,
139 cgctx: Option<&CodegenContext<'_>>,
140 js_namespace: Option<&str>,
141 scope: ScopeId,
142) -> TokenStream {
143 debug_assert!(
144 matches!(
145 decl.classification,
146 InterfaceClassification::ClassLike | InterfaceClassification::Unclassified
147 ),
148 "expected ClassLike or Unclassified, got {:?}",
149 decl.classification
150 );
151 let mut config = ClassConfig::from_interface(decl, ctx, cgctx, scope);
152 config.js_namespace = js_namespace.map(|s| s.to_string());
153 generate_extern_block(&config)
154}
155
156pub fn generate_class_with_js_namespace(
158 decl: &ClassDecl,
159 ctx: &ModuleContext,
160 js_namespace: &str,
161 cgctx: Option<&CodegenContext<'_>>,
162 scope: ScopeId,
163) -> TokenStream {
164 let mut config = ClassConfig::from_class(decl, ctx, cgctx, scope);
165 config.js_namespace = Some(js_namespace.to_string());
166 generate_extern_block(&config)
167}
168
169pub fn generate_dictionary_extern(
172 decl: &InterfaceDecl,
173 ctx: &ModuleContext,
174 cgctx: Option<&CodegenContext<'_>>,
175 js_namespace: Option<&str>,
176 scope: ScopeId,
177) -> TokenStream {
178 let mut config = ClassConfig::from_interface(decl, ctx, cgctx, scope);
179 config.js_namespace = js_namespace.map(|s| s.to_string());
180
181 let extern_block = generate_extern_block(&config);
182 let factory = generate_dictionary_factory(&config);
183
184 quote! {
185 #extern_block
186 #factory
187 }
188}
189
190fn generate_dictionary_factory(config: &ClassConfig) -> TokenStream {
213 let rust_type = super::typemap::make_ident(&config.rust_name);
214 let builder_name = super::typemap::make_ident(&format!("{}Builder", config.rust_name));
215
216 let setter_names: std::collections::HashSet<&str> = config
220 .members
221 .iter()
222 .filter_map(|m| {
223 if let Member::Setter(s) = m {
224 Some(s.js_name.as_str())
225 } else {
226 None
227 }
228 })
229 .collect();
230 let has_readonly = config.members.iter().any(|m| {
231 if let Member::Getter(g) = m {
232 !setter_names.contains(g.js_name.as_str())
233 } else {
234 false
235 }
236 });
237 if has_readonly {
238 return quote! {
239 impl #rust_type {
240 #[allow(clippy::new_without_default)]
241 pub fn new() -> Self {
242 #[allow(unused_unsafe)]
243 unsafe { JsValue::from(js_sys::Object::new()).unchecked_into() }
244 }
245 }
246 };
247 }
248
249 let getters: Vec<&crate::ir::GetterMember> = config
251 .members
252 .iter()
253 .filter_map(|m| {
254 if let Member::Getter(g) = m {
255 Some(g)
256 } else {
257 None
258 }
259 })
260 .collect();
261
262 let required_props: Vec<(usize, &str)> = getters
264 .iter()
265 .enumerate()
266 .filter(|(_, g)| !g.optional)
267 .take(64) .map(|(i, g)| (i, g.js_name.as_str()))
269 .collect();
270 let has_required = !required_props.is_empty();
271
272 let mut required_bit: Vec<Option<u64>> = vec![None; getters.len()];
274 for (bit, &(getter_idx, _)) in required_props.iter().enumerate() {
275 required_bit[getter_idx] = Some(bit as u64);
276 }
277 let full_mask: u64 = if required_props.len() >= 64 {
278 u64::MAX
279 } else {
280 (1u64 << required_props.len()) - 1
281 };
282
283 let mut builder_methods = Vec::new();
285
286 for (getter_idx, g) in getters.iter().enumerate() {
287 let setter_param = crate::ir::Param {
288 name: "val".to_string(),
289 type_ref: g.type_ref.clone(),
290 optional: false,
291 variadic: false,
292 };
293
294 let mut setter_used = HashSet::new();
295 let setter_sigs = expand_signatures(
296 &g.js_name,
297 &[&[setter_param]],
298 &crate::ir::TypeRef::Void,
299 SignatureKind::Setter,
300 &None,
301 &mut setter_used,
302 config.cgctx,
303 config.scope,
304 );
305
306 let bit_clear = required_bit[getter_idx].map(|bit| {
307 let mask = !(1u64 << bit);
308 quote! { self.required &= #mask; }
309 });
310
311 for sig in &setter_sigs {
312 let builder_method_name = sig.rust_name.strip_prefix("set_").unwrap_or(&sig.rust_name);
313 let method_ident = super::typemap::make_ident(builder_method_name);
314 let setter_ident = super::typemap::make_ident(&sig.rust_name);
315 let params = generate_concrete_params(&sig.params, config.cgctx, config.scope);
316
317 let param_idents: Vec<_> = sig
318 .params
319 .iter()
320 .map(|p| super::typemap::make_ident(&p.name))
321 .collect();
322
323 builder_methods.push(quote! {
324 pub fn #method_ident(mut self, #params) -> Self {
325 self.inner.#setter_ident(#(#param_idents),*);
326 #bit_clear
327 self
328 }
329 });
330 }
331 }
332
333 let build_method = if has_required {
335 let missing_checks: Vec<TokenStream> = required_props
336 .iter()
337 .enumerate()
338 .map(|(bit, (_, name))| {
339 let mask = 1u64 << bit;
340 let msg = format!("missing required property `{name}`");
341 quote! {
342 if self.required & #mask != 0 {
343 missing.push(#msg);
344 }
345 }
346 })
347 .collect();
348
349 quote! {
350 pub fn build(self) -> Result<#rust_type, JsValue> {
351 if self.required != 0 {
352 let mut missing = Vec::new();
353 #(#missing_checks)*
354 return Err(JsValue::from_str(&format!(
355 "{}: {}", stringify!(#rust_type), missing.join(", ")
356 )));
357 }
358 Ok(self.inner)
359 }
360 }
361 } else {
362 quote! {
363 pub fn build(self) -> #rust_type {
364 self.inner
365 }
366 }
367 };
368
369 let builder_struct = if has_required {
371 quote! {
372 pub struct #builder_name {
373 inner: #rust_type,
374 required: u64,
375 }
376 }
377 } else {
378 quote! {
379 pub struct #builder_name {
380 inner: #rust_type,
381 }
382 }
383 };
384
385 let builder_init = if has_required {
386 quote! { #builder_name { inner: Self::new(), required: #full_mask } }
387 } else {
388 quote! { #builder_name { inner: Self::new() } }
389 };
390
391 quote! {
392 impl #rust_type {
393 #[allow(clippy::new_without_default)]
394 pub fn new() -> Self {
395 #[allow(unused_imports)]
396 use wasm_bindgen::JsCast;
397 JsCast::unchecked_into(js_sys::Object::new())
398 }
399
400 pub fn builder() -> #builder_name {
401 #builder_init
402 }
403 }
404
405 #builder_struct
406
407 #[allow(unused_mut)]
408 impl #builder_name {
409 #(#builder_methods)*
410
411 #build_method
412 }
413 }
414}
415
416fn generate_extern_block(config: &ClassConfig) -> TokenStream {
427 use crate::ir::{ConstructorMember, MethodMember, Param, StaticMethodMember};
428 use std::collections::HashMap;
429
430 let mut items = Vec::new();
431 let mut used_names: HashSet<String> = HashSet::new();
432
433 let mut method_groups: HashMap<String, Vec<&MethodMember>> = HashMap::new();
437 let mut static_method_groups: HashMap<String, Vec<&StaticMethodMember>> = HashMap::new();
438 let mut constructor_overloads: Vec<&ConstructorMember> = Vec::new();
439
440 for member in &config.members {
441 match member {
442 Member::Constructor(ctor) if !config.is_abstract => {
443 constructor_overloads.push(ctor);
444 }
445 Member::Method(m) => {
446 method_groups.entry(m.js_name.clone()).or_default().push(m);
447 }
448 Member::StaticMethod(m) => {
449 static_method_groups
450 .entry(m.js_name.clone())
451 .or_default()
452 .push(m);
453 }
454 _ => {}
455 }
456 }
457
458 let mut expanded_methods: HashSet<String> = HashSet::new();
460 let mut expanded_static_methods: HashSet<String> = HashSet::new();
461 let mut expanded_constructors = false;
462
463 items.push(generate_type_decl(config));
465
466 for member in &config.members {
470 match member {
471 Member::Constructor(_) if !config.is_abstract => {
472 if expanded_constructors {
473 continue;
474 }
475 expanded_constructors = true;
476
477 let overloads: Vec<&[Param]> = constructor_overloads
478 .iter()
479 .map(|c| c.params.as_slice())
480 .collect();
481 let doc = constructor_overloads.first().and_then(|c| c.doc.clone());
482 let sigs = expand_signatures(
483 &config.js_name,
484 &overloads,
485 &TypeRef::Named(config.rust_name.clone()),
486 SignatureKind::Constructor,
487 &doc,
488 &mut used_names,
489 config.cgctx,
490 config.scope,
491 );
492 for sig in &sigs {
493 items.push(generate_expanded_constructor(config, sig));
494 }
495 }
496 Member::Method(m) => {
497 if expanded_methods.contains(&m.js_name) {
498 continue;
499 }
500 expanded_methods.insert(m.js_name.clone());
501
502 let group = &method_groups[&m.js_name];
503 let overloads: Vec<&[Param]> = group.iter().map(|m| m.params.as_slice()).collect();
504 let doc = group.first().and_then(|m| m.doc.clone());
505 let return_type = &group[0].return_type;
506 let sigs = expand_signatures(
507 &m.js_name,
508 &overloads,
509 return_type,
510 SignatureKind::Method,
511 &doc,
512 &mut used_names,
513 config.cgctx,
514 config.scope,
515 );
516 for sig in &sigs {
517 items.push(generate_expanded_method(config, sig));
518 }
519 }
520 Member::StaticMethod(m) => {
521 if expanded_static_methods.contains(&m.js_name) {
522 continue;
523 }
524 expanded_static_methods.insert(m.js_name.clone());
525
526 let group = &static_method_groups[&m.js_name];
527 let overloads: Vec<&[Param]> = group.iter().map(|m| m.params.as_slice()).collect();
528 let doc = group.first().and_then(|m| m.doc.clone());
529 let return_type = &group[0].return_type;
530 let sigs = expand_signatures(
531 &m.js_name,
532 &overloads,
533 return_type,
534 SignatureKind::StaticMethod,
535 &doc,
536 &mut used_names,
537 config.cgctx,
538 config.scope,
539 );
540 for sig in &sigs {
541 items.push(generate_expanded_static_method(config, sig));
542 }
543 }
544 Member::Getter(g) => {
545 items.push(generate_getter(config, g, &mut used_names));
546 }
547 Member::Setter(s) => {
548 items.extend(generate_setter(config, s, &mut used_names));
549 }
550 Member::StaticGetter(g) => {
551 items.push(generate_static_getter(config, g, &mut used_names));
552 }
553 Member::StaticSetter(s) => {
554 items.extend(generate_static_setter(config, s, &mut used_names));
555 }
556 Member::IndexSignature(_) | Member::Constructor(_) => {
557 }
560 }
561 }
562
563 let wb_extern_attr = match &config.module {
565 Some(m) => quote! { #[wasm_bindgen(module = #m)] },
566 None => quote! { #[wasm_bindgen] },
567 };
568
569 quote! {
570 #wb_extern_attr
571 extern "C" {
572 #(#items)*
573 }
574 }
575}
576
577fn generate_type_decl(config: &ClassConfig) -> TokenStream {
585 let rust_ident = super::typemap::make_ident(&config.rust_name);
586 let js_name = &config.js_name;
587
588 let mut wb_parts: Vec<TokenStream> = Vec::new();
590
591 let mut has_object = false;
592 for extends in &config.extends {
593 let extends_str = extends.to_string();
594 if extends_str == "JsValue" {
596 continue;
597 }
598 if let Some(cgctx) = config.cgctx {
599 let uses = cgctx.external_uses.borrow();
600 if uses.get(&extends_str).is_some_and(|v| v == "JsValue") {
601 continue;
602 }
603 }
604 if extends_str == "Object" {
605 has_object = true;
606 }
607 wb_parts.push(quote! { extends = #extends });
608 }
609 if !has_object {
611 wb_parts.push(quote! { extends = Object });
612 }
613
614 if config.js_name != config.rust_name {
616 wb_parts.push(quote! { js_name = #js_name });
617 }
618
619 if let Some(ns) = &config.js_namespace {
621 wb_parts.push(quote! { js_namespace = #ns });
622 }
623
624 let wb_attr = if wb_parts.is_empty() {
625 quote! {}
626 } else {
627 quote! { #[wasm_bindgen(#(#wb_parts),*)] }
628 };
629
630 quote! {
631 #wb_attr
632 #[derive(Debug, Clone, PartialEq, Eq)]
633 pub type #rust_ident;
634 }
635}
636
637fn generate_expanded_constructor(config: &ClassConfig, sig: &ExpandedSignature) -> TokenStream {
639 let rust_ident = super::typemap::make_ident(&sig.rust_name);
640 let rust_type = super::typemap::make_ident(&config.rust_name);
641 let params = generate_concrete_params(&sig.params, config.cgctx, config.scope);
642 let doc = super::doc_tokens(&sig.doc);
643
644 let ret = if sig.catch {
645 quote! { Result<#rust_type, JsValue> }
646 } else {
647 quote! { #rust_type }
648 };
649
650 let mut wb_parts = vec![quote! { constructor }];
651 if sig.catch {
652 wb_parts.push(quote! { catch });
653 }
654 if sig.rust_name != "new" {
657 let js_name = &config.js_name;
658 wb_parts.push(quote! { js_name = #js_name });
659 }
660
661 quote! {
662 #doc
663 #[wasm_bindgen(#(#wb_parts),*)]
664 pub fn #rust_ident(#params) -> #ret;
665 }
666}
667
668fn generate_expanded_method(config: &ClassConfig, sig: &ExpandedSignature) -> TokenStream {
670 let rust_ident = super::typemap::make_ident(&sig.rust_name);
671 let this_type = super::typemap::make_ident(&config.rust_name);
672 let params = generate_concrete_params(&sig.params, config.cgctx, config.scope);
673 let doc = super::doc_tokens(&sig.doc);
674 let has_variadic = sig.params.last().is_some_and(|p| p.variadic);
675
676 let mut wb_parts: Vec<TokenStream> = vec![quote! { method }];
677 if has_variadic {
678 wb_parts.push(quote! { variadic });
679 }
680 if sig.catch {
681 wb_parts.push(quote! { catch });
682 }
683 if sig.rust_name != sig.js_name {
685 let js_name = &sig.js_name;
686 wb_parts.push(quote! { js_name = #js_name });
687 }
688
689 let ret_ty = to_return_type(&sig.return_type, sig.catch, config.cgctx, config.scope);
690 let ret = if is_void_return(&sig.return_type) && !sig.catch {
691 quote! {}
692 } else {
693 quote! { -> #ret_ty }
694 };
695
696 quote! {
697 #doc
698 #[wasm_bindgen(#(#wb_parts),*)]
699 pub fn #rust_ident(this: &#this_type, #params) #ret;
700 }
701}
702
703fn generate_expanded_static_method(config: &ClassConfig, sig: &ExpandedSignature) -> TokenStream {
705 let rust_ident = super::typemap::make_ident(&sig.rust_name);
706 let class_ident = super::typemap::make_ident(&config.rust_name);
707 let params = generate_concrete_params(&sig.params, config.cgctx, config.scope);
708 let doc = super::doc_tokens(&sig.doc);
709 let has_variadic = sig.params.last().is_some_and(|p| p.variadic);
710
711 let mut wb_parts: Vec<TokenStream> = vec![quote! { static_method_of = #class_ident }];
712 if has_variadic {
713 wb_parts.push(quote! { variadic });
714 }
715 if sig.catch {
716 wb_parts.push(quote! { catch });
717 }
718 if sig.rust_name != sig.js_name {
719 let js_name = &sig.js_name;
720 wb_parts.push(quote! { js_name = #js_name });
721 }
722
723 let ret_ty = to_return_type(&sig.return_type, sig.catch, config.cgctx, config.scope);
724 let ret = if is_void_return(&sig.return_type) && !sig.catch {
725 quote! {}
726 } else {
727 quote! { -> #ret_ty }
728 };
729
730 quote! {
731 #doc
732 #[wasm_bindgen(#(#wb_parts),*)]
733 pub fn #rust_ident(#params) #ret;
734 }
735}
736
737fn generate_getter(
739 config: &ClassConfig,
740 getter: &GetterMember,
741 used_names: &mut HashSet<String>,
742) -> TokenStream {
743 let this_type = super::typemap::make_ident(&config.rust_name);
744 let doc = super::doc_tokens(&getter.doc);
745
746 let candidate = to_snake_case(&getter.js_name);
747 let rust_name = dedupe_name(&candidate, used_names);
748 let rust_ident = super::typemap::make_ident(&rust_name);
749
750 let getter_type = if getter.optional {
751 let unwrapped = match &getter.type_ref {
754 TypeRef::Nullable(inner) => inner.as_ref(),
755 other => other,
756 };
757 let inner = to_syn_type(unwrapped, TypePosition::RETURN, config.cgctx, config.scope);
758 quote! { Option<#inner> }
759 } else {
760 to_syn_type(
761 &getter.type_ref,
762 TypePosition::RETURN,
763 config.cgctx,
764 config.scope,
765 )
766 };
767
768 let mut wb_parts: Vec<TokenStream> = vec![quote! { method }, quote! { getter }];
769 if rust_name != getter.js_name {
770 let js_name = &getter.js_name;
771 wb_parts.push(quote! { js_name = #js_name });
772 }
773
774 quote! {
775 #doc
776 #[wasm_bindgen(#(#wb_parts),*)]
777 pub fn #rust_ident(this: &#this_type) -> #getter_type;
778 }
779}
780
781fn generate_setter(
783 config: &ClassConfig,
784 setter: &SetterMember,
785 used_names: &mut HashSet<String>,
786) -> Vec<TokenStream> {
787 let this_type = super::typemap::make_ident(&config.rust_name);
788 let doc = setter.doc.clone();
789
790 let param = crate::ir::Param {
792 name: "val".to_string(),
793 type_ref: setter.type_ref.clone(),
794 optional: false,
795 variadic: false,
796 };
797
798 let sigs = expand_signatures(
799 &setter.js_name,
800 &[&[param]],
801 &crate::ir::TypeRef::Void,
802 SignatureKind::Setter,
803 &doc,
804 used_names,
805 config.cgctx,
806 config.scope,
807 );
808
809 sigs.iter()
810 .map(|sig| {
811 let rust_ident = super::typemap::make_ident(&sig.rust_name);
812 let params = generate_concrete_params(&sig.params, config.cgctx, config.scope);
813
814 let mut wb_parts: Vec<TokenStream> = vec![quote! { method }, quote! { setter }];
815 if sig.rust_name != format!("set_{}", setter.js_name) {
816 let js_name = &setter.js_name;
817 wb_parts.push(quote! { js_name = #js_name });
818 }
819
820 let doc = super::doc_tokens(&sig.doc);
821 quote! {
822 #doc
823 #[wasm_bindgen(#(#wb_parts),*)]
824 pub fn #rust_ident(this: &#this_type, #params);
825 }
826 })
827 .collect()
828}
829
830fn generate_static_getter(
832 config: &ClassConfig,
833 getter: &StaticGetterMember,
834 used_names: &mut HashSet<String>,
835) -> TokenStream {
836 let class_ident = super::typemap::make_ident(&config.rust_name);
837 let doc = super::doc_tokens(&getter.doc);
838
839 let candidate = to_snake_case(&getter.js_name);
840 let rust_name = dedupe_name(&candidate, used_names);
841 let rust_ident = super::typemap::make_ident(&rust_name);
842
843 let getter_type = to_syn_type(
844 &getter.type_ref,
845 TypePosition::RETURN,
846 config.cgctx,
847 config.scope,
848 );
849
850 let mut wb_parts: Vec<TokenStream> = vec![
851 quote! { static_method_of = #class_ident },
852 quote! { getter },
853 ];
854 if rust_name != getter.js_name {
855 let js_name = &getter.js_name;
856 wb_parts.push(quote! { js_name = #js_name });
857 }
858
859 quote! {
860 #doc
861 #[wasm_bindgen(#(#wb_parts),*)]
862 pub fn #rust_ident() -> #getter_type;
863 }
864}
865
866fn generate_static_setter(
868 config: &ClassConfig,
869 setter: &StaticSetterMember,
870 used_names: &mut HashSet<String>,
871) -> Vec<TokenStream> {
872 let class_ident = super::typemap::make_ident(&config.rust_name);
873 let doc = setter.doc.clone();
874
875 let param = crate::ir::Param {
876 name: "val".to_string(),
877 type_ref: setter.type_ref.clone(),
878 optional: false,
879 variadic: false,
880 };
881
882 let sigs = expand_signatures(
883 &setter.js_name,
884 &[&[param]],
885 &crate::ir::TypeRef::Void,
886 SignatureKind::StaticSetter,
887 &doc,
888 used_names,
889 config.cgctx,
890 config.scope,
891 );
892
893 sigs.iter()
894 .map(|sig| {
895 let rust_ident = super::typemap::make_ident(&sig.rust_name);
896 let params = generate_concrete_params(&sig.params, config.cgctx, config.scope);
897
898 let mut wb_parts: Vec<TokenStream> = vec![
899 quote! { static_method_of = #class_ident },
900 quote! { setter },
901 ];
902 if sig.rust_name != format!("set_{}", setter.js_name) {
903 let js_name = &setter.js_name;
904 wb_parts.push(quote! { js_name = #js_name });
905 }
906
907 let doc = super::doc_tokens(&sig.doc);
908 quote! {
909 #doc
910 #[wasm_bindgen(#(#wb_parts),*)]
911 pub fn #rust_ident(#params);
912 }
913 })
914 .collect()
915}
916
917fn extends_tokens(ty: &TypeRef, cgctx: Option<&CodegenContext<'_>>, scope: ScopeId) -> TokenStream {
926 let tokens = match ty {
927 TypeRef::Named(_) | TypeRef::GenericInstantiation(_, _) => {
928 super::typemap::to_syn_type(ty, TypePosition::ARGUMENT.to_inner(), cgctx, scope)
929 }
930 _ => {
931 if let Some(ctx) = cgctx {
932 ctx.warn(format!(
933 "unsupported extends type `{ty:?}`, falling back to Object"
934 ));
935 }
936 quote! { Object }
937 }
938 };
939 if tokens.to_string() == "JsValue" {
942 quote! { Object }
943 } else {
944 tokens
945 }
946}