servo_dom_struct/
domobject.rs1use quote::{TokenStreamExt, quote};
6
7pub(crate) fn expand_dom_object(
10 input: syn::ItemStruct,
11 associated_memory: bool,
12) -> proc_macro2::TokenStream {
13 let fields = input.fields.iter().collect::<Vec<&syn::Field>>();
14 let (first_field, fields) = fields
15 .split_first()
16 .expect("#[dom_struct] should not be applied on empty structs");
17
18 let first_field_name = first_field.ident.as_ref().unwrap();
19 let reflector_type = if associated_memory {
20 quote! { crate::AssociatedMemory }
21 } else {
22 quote! { () }
23 };
24 let mut field_types_and_cfgs = vec![];
25 for field in fields {
26 if field_types_and_cfgs.contains(&(&field.ty, vec![])) {
27 continue;
28 }
29 let cfgs = field
30 .attrs
31 .iter()
32 .filter(|a| a.path().is_ident("cfg"))
33 .collect::<Vec<_>>();
34 field_types_and_cfgs.push((&field.ty, cfgs));
35 }
36
37 let name = &input.ident;
38 let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
39 let items = quote! {
40 impl #impl_generics ::js::conversions::ToJSValConvertible for #name #ty_generics #where_clause {
41 #[expect(unsafe_code)]
42 unsafe fn to_jsval(&self,
43 cx: *mut js::jsapi::JSContext,
44 rval: js::rust::MutableHandleValue) {
45 let object = crate::DomObject::reflector(self).get_jsobject();
46 object.to_jsval(cx, rval)
47 }
48 }
49
50 impl #impl_generics crate::DomObject for #name #ty_generics #where_clause {
51 type ReflectorType = #reflector_type;
52
53 #[inline]
54 fn reflector(&self) -> &crate::Reflector<Self::ReflectorType> {
55 self.#first_field_name.reflector()
56 }
57 }
58
59 impl #impl_generics crate::MutDomObject for #name #ty_generics #where_clause {
60 unsafe fn init_reflector<Actual>(&self, obj: *mut js::jsapi::JSObject) {
61 self.#first_field_name.init_reflector::<Actual>(obj);
62 }
63 unsafe fn init_reflector_without_associated_memory(&self, obj: *mut js::jsapi::JSObject) {
64 self.#first_field_name.init_reflector_without_associated_memory(obj);
65 }
66 }
67
68 impl #impl_generics Eq for #name #ty_generics #where_clause {}
69
70 impl #impl_generics PartialEq for #name #ty_generics #where_clause {
71 fn eq(&self, other: &Self) -> bool {
72 crate::DomObject::reflector(self) == crate::DomObject::reflector(other)
73 }
74 }
75 };
76
77 let mut params = proc_macro2::TokenStream::new();
78 params.append_separated(
79 input.generics.type_params().map(|param| ¶m.ident),
80 quote! {,},
81 );
82
83 let mut dummy_items = quote! {
84 trait NoDomObjectInDomObject<A> {
87 fn some_item() {}
89 }
90
91 impl<T: ?Sized> NoDomObjectInDomObject<()> for T {}
92
93 #[expect(dead_code)]
95 struct Invalid;
96 impl<T> NoDomObjectInDomObject<Invalid> for T where T: ?Sized + crate::DomObject {}
98 };
99
100 dummy_items.append_all(
101 field_types_and_cfgs
102 .iter()
103 .enumerate()
104 .map(|(i, (ty, cfgs))| {
105 let s = syn::Ident::new(&format!("S{i}"), proc_macro2::Span::call_site());
106 quote! {
107 struct #s<#params>(#params);
108
109 impl #impl_generics #s<#params> #where_clause {
110 #(#cfgs)*
111 fn f() {
112 let _ = <#ty as NoDomObjectInDomObject<_>>::some_item;
116 }
117 }
118 }
119 }),
120 );
121
122 let dummy_const = syn::Ident::new(
123 &format!("_IMPL_DOMOBJECT_FOR_{}", name),
124 proc_macro2::Span::call_site(),
125 );
126 let tokens = quote! {
127 #[expect(non_upper_case_globals)]
128 const #dummy_const: () = { #dummy_items };
129 #items
130 };
131
132 tokens
133}