| 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 | |