1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use heck::ToShoutySnakeCase; |
4 | use proc_macro2::TokenStream; |
5 | use quote::{format_ident, quote, ToTokens}; |
6 | |
7 | pub fn impl_object_interface(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_interface = 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_interface_as_dynamic( |
25 | &crate_ident, |
26 | &self_ty, |
27 | &plugin_ty, |
28 | dynamic.lazy_registration, |
29 | ) |
30 | } else { |
31 | register_object_interface_as_static(&crate_ident, &self_ty) |
32 | }; |
33 | |
34 | let mut has_prerequisites = false; |
35 | let mut has_instance = false; |
36 | for item in items.iter() { |
37 | if let syn::ImplItem::Type(type_) = item { |
38 | let name = type_.ident.to_string(); |
39 | if name == "Prerequisites" { |
40 | has_prerequisites = true; |
41 | } else if name == "Instance" { |
42 | has_instance = true; |
43 | } |
44 | } |
45 | } |
46 | |
47 | let prerequisites_opt = if has_prerequisites { |
48 | None |
49 | } else { |
50 | Some(quote!( |
51 | type Prerequisites = (); |
52 | )) |
53 | }; |
54 | |
55 | let instance_opt = if has_instance { |
56 | None |
57 | } else { |
58 | Some(quote!( |
59 | type Instance = ::std::ffi::c_void; |
60 | )) |
61 | }; |
62 | |
63 | quote! { |
64 | #(#attrs)* |
65 | #unsafety impl #generics #trait_path for #self_ty { |
66 | #prerequisites_opt |
67 | #instance_opt |
68 | #(#items)* |
69 | } |
70 | |
71 | unsafe impl #crate_ident::subclass::interface::ObjectInterfaceType for #self_ty { |
72 | #[inline] |
73 | fn type_() -> #crate_ident::Type { |
74 | Self::register_interface() |
75 | } |
76 | } |
77 | |
78 | #register_object_interface |
79 | } |
80 | } |
81 | |
82 | // Registers the object interface as a static type. |
83 | fn register_object_interface_as_static( |
84 | crate_ident: &TokenStream, |
85 | self_ty: &syn::Ident, |
86 | ) -> TokenStream { |
87 | // registers the interface on first use (lazy registration). |
88 | quote! { |
89 | impl #self_ty { |
90 | /// Registers the interface only once. |
91 | #[inline] |
92 | fn register_interface() -> #crate_ident::Type { |
93 | static TYPE: ::std::sync::OnceLock<#crate_ident::Type> = ::std::sync::OnceLock::new(); |
94 | *TYPE.get_or_init(|| unsafe { |
95 | #crate_ident::subclass::register_interface::<Self>() |
96 | }) |
97 | } |
98 | } |
99 | } |
100 | } |
101 | |
102 | // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). |
103 | // An object interface can be reregistered as a dynamic type. |
104 | fn register_object_interface_as_dynamic( |
105 | crate_ident: &TokenStream, |
106 | self_ty: &syn::Ident, |
107 | plugin_ty: &TokenStream, |
108 | lazy_registration: bool, |
109 | ) -> TokenStream { |
110 | // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). |
111 | // An object interface can be reregistered as a dynamic type. |
112 | if lazy_registration { |
113 | // registers the object interface as a dynamic type on the first use (lazy registration). |
114 | // a weak reference on the plugin is stored and will be used later on the first use of the object interface. |
115 | // this implementation relies on a static storage of a weak reference on the plugin and of the GLib type to know if the object interface has been registered. |
116 | |
117 | // the registration status type. |
118 | let registration_status_type = format_ident!(" {}RegistrationStatus" , self_ty); |
119 | // name of the static variable to store the registration status. |
120 | let registration_status = format_ident!( |
121 | " {}" , |
122 | registration_status_type.to_string().to_shouty_snake_case() |
123 | ); |
124 | |
125 | quote! { |
126 | /// The registration status type: a tuple of the weak reference on the plugin and of the GLib type. |
127 | struct #registration_status_type(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type); |
128 | unsafe impl Send for #registration_status_type {} |
129 | |
130 | /// The registration status protected by a mutex guarantees so that no other threads are concurrently accessing the data. |
131 | static #registration_status: ::std::sync::Mutex<Option<#registration_status_type>> = ::std::sync::Mutex::new(None); |
132 | |
133 | impl #self_ty { |
134 | /// Registers the object interface as a dynamic type within the plugin only once. |
135 | /// Plugin must have been used at least once. |
136 | /// Do nothing if plugin has never been used or if the object interface is already registered as a dynamic type. |
137 | #[inline] |
138 | fn register_interface() -> #crate_ident::Type { |
139 | let mut registration_status = #registration_status.lock().unwrap(); |
140 | match ::std::ops::DerefMut::deref_mut(&mut registration_status) { |
141 | // plugin has never been used, so the object interface cannot be registered as a dynamic type. |
142 | None => #crate_ident::Type::INVALID, |
143 | // plugin has been used and the object interface has not been registered yet, so registers it as a dynamic type. |
144 | Some(#registration_status_type(type_plugin, type_)) if !type_.is_valid() => { |
145 | *type_ = #crate_ident::subclass::register_dynamic_interface::<#plugin_ty, Self>(&(type_plugin.upgrade().unwrap())); |
146 | *type_ |
147 | }, |
148 | // plugin has been used and the object interface has already been registered as a dynamic type. |
149 | Some(#registration_status_type(_, type_)) => *type_ |
150 | } |
151 | } |
152 | |
153 | /// Depending on the plugin lifecycle state and on the registration status of the object interface: |
154 | /// If plugin is used (and has loaded the implementation) for the first time, postpones the registration and stores a weak reference on the plugin. |
155 | /// If plugin is reused (and has reloaded the implementation) and the object interface has been already registered as a dynamic type, reregisters it. |
156 | /// An object interface can be reregistered several times as a dynamic type. |
157 | /// If plugin is reused (and has reloaded the implementation) and the object interface has not been registered yet as a dynamic type, do nothing. |
158 | #[inline] |
159 | pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { |
160 | let mut registration_status = #registration_status.lock().unwrap(); |
161 | match ::std::ops::DerefMut::deref_mut(&mut registration_status) { |
162 | // plugin has never been used (this is the first time), so postpones registration of the object interface as a dynamic type on the first use. |
163 | None => { |
164 | *registration_status = Some(#registration_status_type(#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); |
165 | true |
166 | }, |
167 | // plugin has been used at least one time and the object interface has been registered as a dynamic type at least one time, so re-registers it. |
168 | Some(#registration_status_type(_, type_)) if type_.is_valid() => { |
169 | *type_ = #crate_ident::subclass::register_dynamic_interface::<#plugin_ty, Self>(type_plugin); |
170 | type_.is_valid() |
171 | }, |
172 | // plugin has been used at least one time but the object interface has not been registered yet as a dynamic type, so keeps postponed registration. |
173 | Some(_) => { |
174 | true |
175 | } |
176 | } |
177 | } |
178 | |
179 | /// Depending on the plugin lifecycle state and on the registration status of the object interface: |
180 | /// If plugin has been used (or reused) but the object interface has not been registered yet as a dynamic type, cancels the postponed registration by deleting the weak reference on the plugin. |
181 | /// Else do nothing. |
182 | #[inline] |
183 | pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { |
184 | let mut registration_status = #registration_status.lock().unwrap(); |
185 | match ::std::ops::DerefMut::deref_mut(&mut registration_status) { |
186 | // plugin has never been used, so unload implementation is unexpected. |
187 | None => false, |
188 | // plugin has been used at least one time and the object interface has been registered as a dynamic type at least one time. |
189 | Some(#registration_status_type(_, type_)) if type_.is_valid() => true, |
190 | // plugin has been used at least one time but the object interface has not been registered yet as a dynamic type, so cancels the postponed registration. |
191 | Some(_) => { |
192 | *registration_status = None; |
193 | true |
194 | } |
195 | } |
196 | } |
197 | } |
198 | } |
199 | } else { |
200 | // registers immediately the object interface as a dynamic type. |
201 | |
202 | // name of the static variable to store the GLib type. |
203 | let gtype_status = format_ident!(" {}_G_TYPE" , self_ty.to_string().to_shouty_snake_case()); |
204 | |
205 | quote! { |
206 | /// The GLib type which can be safely shared between threads. |
207 | static #gtype_status: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); |
208 | |
209 | impl #self_ty { |
210 | /// Do nothing as the object interface has been registered on implementation load. |
211 | #[inline] |
212 | fn register_interface() -> #crate_ident::Type { |
213 | let gtype = #gtype_status.load(::std::sync::atomic::Ordering::Acquire); |
214 | unsafe { <#crate_ident::Type as #crate_ident::translate::FromGlib<#crate_ident::ffi::GType>>::from_glib(gtype) } |
215 | } |
216 | |
217 | /// Registers the object interface as a dynamic type within the plugin. |
218 | /// The object interface can be registered several times as a dynamic type. |
219 | #[inline] |
220 | pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { |
221 | let gtype = #crate_ident::translate::IntoGlib::into_glib(#crate_ident::subclass::register_dynamic_interface::<#plugin_ty, Self>(type_plugin)); |
222 | #gtype_status.store(gtype, ::std::sync::atomic::Ordering::Release); |
223 | gtype != #crate_ident::gobject_ffi::G_TYPE_INVALID |
224 | } |
225 | |
226 | /// Do nothing as object interfaces registered as dynamic types are never unregistered. |
227 | #[inline] |
228 | pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { |
229 | true |
230 | } |
231 | } |
232 | } |
233 | } |
234 | } |
235 | |