glib_macros/object_impl_attributes/interface.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_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.
83fn 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.
104fn 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}