glib_macros/object_impl_attributes/
subclass.rs

1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use heck::ToShoutySnakeCase;
4use proc_macro2::TokenStream;
5use quote::{format_ident, quote, ToTokens};
6
7pub fn impl_object_subclass(input: super::Input) -> TokenStream {
8    let crate_ident = crate::utils::crate_ident_new();
9    let super::Input {
10        attrs,
11        generics,
12        trait_path,
13        self_ty,
14        unsafety,
15        items,
16        meta_dynamic,
17    } = input;
18
19    let register_object_subclass = if let Some(dynamic) = meta_dynamic {
20        let plugin_ty = dynamic
21            .plugin_type
22            .map(|p| p.into_token_stream())
23            .unwrap_or(quote!(#crate_ident::TypeModule));
24        register_object_subclass_as_dynamic(
25            &crate_ident,
26            &self_ty,
27            &plugin_ty,
28            dynamic.lazy_registration,
29        )
30    } else {
31        register_object_subclass_as_static(&crate_ident, &self_ty)
32    };
33
34    let mut has_new = false;
35    let mut has_parent_type = false;
36    let mut has_interfaces = false;
37    let mut has_instance = false;
38    let mut has_class = false;
39    for item in items.iter() {
40        match item {
41            syn::ImplItem::Fn(method) => {
42                let name = &method.sig.ident;
43                if name == "new" || name == "with_class" {
44                    has_new = true;
45                }
46            }
47            syn::ImplItem::Type(type_) => {
48                let name = &type_.ident;
49                if name == "ParentType" {
50                    has_parent_type = true;
51                } else if name == "Interfaces" {
52                    has_interfaces = true;
53                } else if name == "Instance" {
54                    has_instance = true;
55                } else if name == "Class" {
56                    has_class = true;
57                }
58            }
59            _ => {}
60        }
61    }
62
63    let parent_type_opt = (!has_parent_type).then(|| {
64        quote!(
65            type ParentType = #crate_ident::Object;
66        )
67    });
68
69    let interfaces_opt = (!has_interfaces).then(|| {
70        quote!(
71            type Interfaces = ();
72        )
73    });
74
75    let new_opt = (!has_new).then(|| {
76        quote! {
77            #[inline]
78            fn new() -> Self {
79                ::std::default::Default::default()
80            }
81        }
82    });
83
84    let class_opt = (!has_class)
85        .then(|| quote!(type Class = #crate_ident::subclass::basic::ClassStruct<Self>;));
86
87    let instance_opt = (!has_instance)
88        .then(|| quote!(type Instance = #crate_ident::subclass::basic::InstanceStruct<Self>;));
89
90    quote! {
91        #(#attrs)*
92        #unsafety impl #generics #trait_path for #self_ty {
93            #parent_type_opt
94            #interfaces_opt
95            #class_opt
96            #instance_opt
97            #new_opt
98            #(#items)*
99        }
100
101        unsafe impl #crate_ident::subclass::types::ObjectSubclassType for #self_ty {
102            #[inline]
103            fn type_data() -> ::std::ptr::NonNull<#crate_ident::subclass::TypeData> {
104                static mut DATA: #crate_ident::subclass::TypeData =
105                    #crate_ident::subclass::types::TypeData::new();
106                unsafe { ::std::ptr::NonNull::new_unchecked(::std::ptr::addr_of_mut!(DATA)) }
107            }
108
109            #[inline]
110            fn type_() -> #crate_ident::Type {
111                Self::register_type();
112
113                unsafe {
114                    let data = Self::type_data();
115                    let type_ = data.as_ref().type_();
116
117                    type_
118                }
119            }
120        }
121
122        #register_object_subclass
123
124        #[doc(hidden)]
125        impl #crate_ident::subclass::types::FromObject for #self_ty {
126            type FromObjectType = <Self as #crate_ident::subclass::types::ObjectSubclass>::Type;
127            #[inline]
128            fn from_object(obj: &Self::FromObjectType) -> &Self {
129                <Self as #crate_ident::subclass::types::ObjectSubclassExt>::from_obj(obj)
130            }
131        }
132
133        #[doc(hidden)]
134        impl #crate_ident::clone::Downgrade for #self_ty {
135            type Weak = #crate_ident::subclass::ObjectImplWeakRef<#self_ty>;
136
137            #[inline]
138            fn downgrade(&self) -> Self::Weak {
139                let ref_counted = #crate_ident::subclass::prelude::ObjectSubclassExt::ref_counted(self);
140                #crate_ident::clone::Downgrade::downgrade(&ref_counted)
141            }
142        }
143
144        impl #self_ty {
145            #[inline]
146            pub fn downgrade(&self) -> <Self as #crate_ident::clone::Downgrade>::Weak {
147                #crate_ident::clone::Downgrade::downgrade(self)
148            }
149        }
150
151        #[doc(hidden)]
152        impl ::std::borrow::ToOwned for #self_ty {
153            type Owned = #crate_ident::subclass::ObjectImplRef<#self_ty>;
154
155            #[inline]
156            fn to_owned(&self) -> Self::Owned {
157                #crate_ident::subclass::prelude::ObjectSubclassExt::ref_counted(self)
158            }
159        }
160
161        #[doc(hidden)]
162        impl ::std::borrow::Borrow<#self_ty> for #crate_ident::subclass::ObjectImplRef<#self_ty> {
163            #[inline]
164            fn borrow(&self) -> &#self_ty {
165                self
166            }
167        }
168    }
169}
170
171// Registers the object subclass as a static type.
172fn register_object_subclass_as_static(
173    crate_ident: &TokenStream,
174    self_ty: &syn::Ident,
175) -> TokenStream {
176    // registers the object subclass on first use (lazy registration).
177    quote! {
178        impl #self_ty {
179            /// Registers the type only once.
180            #[inline]
181            fn register_type() {
182                static ONCE: ::std::sync::Once = ::std::sync::Once::new();
183
184                ONCE.call_once(|| {
185                    #crate_ident::subclass::register_type::<Self>();
186                })
187            }
188        }
189    }
190}
191
192// The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]).
193// An object subclass can be reregistered as a dynamic type.
194fn register_object_subclass_as_dynamic(
195    crate_ident: &TokenStream,
196    self_ty: &syn::Ident,
197    plugin_ty: &TokenStream,
198    lazy_registration: bool,
199) -> TokenStream {
200    // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]).
201    // An object subclass can be reregistered as a dynamic type.
202    if lazy_registration {
203        // registers the object subclass as a dynamic type on the first use (lazy registration).
204        // a weak reference on the plugin is stored and will be used later on the first use of the object subclass.
205        // this implementation relies on a static storage of a weak reference on the plugin and of the GLib type to know if the object subclass has been registered.
206
207        // the registration status type.
208        let registration_status_type = format_ident!("{}RegistrationStatus", self_ty);
209        // name of the static variable to store the registration status.
210        let registration_status = format_ident!(
211            "{}",
212            registration_status_type.to_string().to_shouty_snake_case()
213        );
214
215        quote! {
216            /// The registration status type: a tuple of the weak reference on the plugin and of the GLib type.
217            struct #registration_status_type(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type);
218            unsafe impl Send for #registration_status_type {}
219
220            /// The registration status protected by a mutex guarantees so that no other threads are concurrently accessing the data.
221            static #registration_status: ::std::sync::Mutex<Option<#registration_status_type>> = ::std::sync::Mutex::new(None);
222
223            impl #self_ty {
224                /// Registers the object subclass as a dynamic type within the plugin only once.
225                /// Plugin must have been used at least once.
226                /// Do nothing if plugin has never been used or if the object subclass is already registered as a dynamic type.
227                #[inline]
228                fn register_type() {
229                    let mut registration_status = #registration_status.lock().unwrap();
230                    match ::std::ops::DerefMut::deref_mut(&mut registration_status) {
231                        // plugin has never been used, so the object subclass cannot be registered as a dynamic type.
232                        None => (),
233                        // plugin has been used and the object subclass has not been registered yet, so registers it as a dynamic type.
234                        Some(#registration_status_type(type_plugin, type_)) if !type_.is_valid() => {
235                            *type_ = #crate_ident::subclass::register_dynamic_type::<#plugin_ty, Self>(&(type_plugin.upgrade().unwrap()));
236                        },
237                        // plugin has been used and the object subclass has already been registered as a dynamic type.
238                        Some(_) => ()
239                    }
240                }
241
242                /// Depending on the plugin lifecycle state and on the registration status of the object subclass:
243                /// If plugin is used (and has loaded the implementation) for the first time, postpones the registration and stores a weak reference on the plugin.
244                /// If plugin is reused (and has reloaded the implementation) and the object subclass has been already registered as a dynamic type, reregisters it.
245                /// An object subclass can be reregistered several times as a dynamic type.
246                /// If plugin is reused (and has reloaded the implementation) and the object subclass has not been registered yet as a dynamic type, do nothing.
247                #[inline]
248                pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool {
249                    let mut registration_status = #registration_status.lock().unwrap();
250                    match ::std::ops::DerefMut::deref_mut(&mut registration_status) {
251                        // plugin has never been used (this is the first time), so postpones registration of the object subclass as a dynamic type on the first use.
252                        None => {
253                            *registration_status = Some(#registration_status_type(#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID));
254                            true
255                        },
256                        // plugin has been used at least one time and the object subclass has been registered as a dynamic type at least one time, so re-registers it.
257                        Some(#registration_status_type(_, type_)) if type_.is_valid() => {
258                            *type_ = #crate_ident::subclass::register_dynamic_type::<#plugin_ty, Self>(type_plugin);
259                            type_.is_valid()
260                        },
261                        // plugin has been used at least one time but the object subclass has not been registered yet as a dynamic type, so keeps postponed registration.
262                        Some(_) => {
263                            true
264                        }
265                    }
266                }
267
268                /// Depending on the plugin lifecycle state and on the registration status of the object subclass:
269                /// If plugin has been used (or reused) but the object subclass has not been registered yet as a dynamic type, cancels the postponed registration by deleting the weak reference on the plugin.
270                /// Else do nothing.
271                #[inline]
272                pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool {
273                    let mut registration_status = #registration_status.lock().unwrap();
274                    match ::std::ops::DerefMut::deref_mut(&mut registration_status) {
275                        // plugin has never been used, so unload implementation is unexpected.
276                        None => false,
277                        // plugin has been used at least one time and the object subclass has been registered as a dynamic type at least one time.
278                        Some(#registration_status_type(_, type_)) if type_.is_valid() => true,
279                        // plugin has been used at least one time but the object subclass has not been registered yet as a dynamic type, so cancels the postponed registration.
280                        Some(_) => {
281                            *registration_status = None;
282                            true
283                        }
284                    }
285                }
286            }
287        }
288    } else {
289        // registers immediately the object subclass as a dynamic type.
290
291        quote! {
292            impl #self_ty {
293                /// Do nothing as the object subclass has been registered on implementation load.
294                #[inline]
295                fn register_type() { }
296
297                /// Registers the object subclass as a dynamic type within the plugin.
298                /// The object subclass can be registered several times as a dynamic type.
299                #[inline]
300                pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool {
301                    let type_ = #crate_ident::subclass::register_dynamic_type::<#plugin_ty, Self>(type_plugin);
302                    type_ != #crate_ident::Type::INVALID
303                }
304
305                /// Do nothing as object subclasses registered as dynamic types are never unregistered.
306                #[inline]
307                pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool {
308                    true
309                }
310            }
311        }
312    }
313}