|
2 | 2 |
|
3 | 3 | use proc_macro2::TokenStream;
|
4 | 4 | use proc_macro_error::abort_call_site;
|
5 |
| -use quote::quote; |
| 5 | +use quote::{quote, ToTokens}; |
| 6 | + |
| 7 | +pub const WRONG_EXPRESSION_MSG: &str = |
| 8 | + "This macro's attributes should be a sequence of assign expressions punctuated by comma"; |
| 9 | + |
| 10 | +pub const UNSUPPORTED_EXPRESSION_MSG: &str = |
| 11 | + "This macro's supported attributes are: `plugin_type = <subclass_of_glib::TypePlugin>, lazy_registration = true|false`"; |
6 | 12 |
|
7 | 13 | pub const WRONG_PLACE_MSG: &str =
|
8 | 14 | "This macro should be used on `impl` block for `glib::ObjectInterface` trait";
|
9 | 15 |
|
10 | 16 | pub fn impl_object_interface(input: &syn::ItemImpl) -> TokenStream {
|
| 17 | + let crate_ident = crate::utils::crate_ident_new(); |
| 18 | + let syn::ItemImpl { self_ty, .. } = &input; |
| 19 | + |
| 20 | + // registers the interface on first use (lazy registration). |
| 21 | + let register_interface = quote! { |
| 22 | + impl #self_ty { |
| 23 | + /// Registers the interface only once. |
| 24 | + #[inline] |
| 25 | + fn register_interface() -> #crate_ident::Type { |
| 26 | + static ONCE: ::std::sync::Once = ::std::sync::Once::new(); |
| 27 | + static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID; |
| 28 | + |
| 29 | + ONCE.call_once(|| unsafe { |
| 30 | + TYPE = #crate_ident::subclass::register_interface::<Self>(); |
| 31 | + }); |
| 32 | + |
| 33 | + unsafe { |
| 34 | + TYPE |
| 35 | + } |
| 36 | + } |
| 37 | + } |
| 38 | + }; |
| 39 | + |
| 40 | + impl_object_interface_(register_interface, input) |
| 41 | +} |
| 42 | + |
| 43 | +pub fn impl_dynamic_object_interface( |
| 44 | + attrs: &syn::punctuated::Punctuated<syn::Expr, syn::Token![,]>, |
| 45 | + input: &syn::ItemImpl, |
| 46 | +) -> TokenStream { |
| 47 | + let crate_ident = crate::utils::crate_ident_new(); |
| 48 | + let syn::ItemImpl { self_ty, .. } = &input; |
| 49 | + |
| 50 | + let mut plugin_type_opt: Option<syn::Path> = None; |
| 51 | + let mut lazy_registration_opt: Option<bool> = None; |
| 52 | + |
| 53 | + for attr in attrs { |
| 54 | + match attr { |
| 55 | + // attribute must be one of supported assign expressions. |
| 56 | + syn::Expr::Assign(syn::ExprAssign { left, right, .. }) => { |
| 57 | + match (*left.to_owned(), *right.to_owned()) { |
| 58 | + // `plugin_type = <subclass_of_TypePlugin>` |
| 59 | + ( |
| 60 | + syn::Expr::Path(syn::ExprPath { path: path1, .. }), |
| 61 | + syn::Expr::Path(syn::ExprPath { path: path2, .. }), |
| 62 | + ) if path1.is_ident(&"plugin_type") => plugin_type_opt = Some(path2), |
| 63 | + // `lazy_registration = true|false` |
| 64 | + ( |
| 65 | + syn::Expr::Path(syn::ExprPath { path, .. }), |
| 66 | + syn::Expr::Lit(syn::ExprLit { |
| 67 | + lit: syn::Lit::Bool(syn::LitBool { value, .. }), |
| 68 | + .. |
| 69 | + }), |
| 70 | + ) if path.is_ident(&"lazy_registration") => lazy_registration_opt = Some(value), |
| 71 | + _ => abort_call_site!(UNSUPPORTED_EXPRESSION_MSG), |
| 72 | + }; |
| 73 | + } |
| 74 | + _ => abort_call_site!(WRONG_EXPRESSION_MSG), |
| 75 | + }; |
| 76 | + } |
| 77 | + |
| 78 | + let (plugin_ty, lazy_registration) = match (plugin_type_opt, lazy_registration_opt) { |
| 79 | + (Some(type_plugin), lazy_registration_opt) => ( |
| 80 | + type_plugin.into_token_stream(), |
| 81 | + lazy_registration_opt.unwrap_or_default(), |
| 82 | + ), |
| 83 | + (None, lazy_registration_opt) => ( |
| 84 | + quote!(#crate_ident::TypeModule), |
| 85 | + lazy_registration_opt.unwrap_or_default(), |
| 86 | + ), |
| 87 | + }; |
| 88 | + |
| 89 | + // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt::unuse`]). |
| 90 | + // An object interface can be reregistered as a dynamic type (see [`TypePluginExt::register_type`]). |
| 91 | + let register_interface = if lazy_registration { |
| 92 | + // registers the object interface as a dynamic type on the first use (lazy registration). |
| 93 | + // a weak reference on the plugin is stored and will be used later on the first use of the object interface. |
| 94 | + // 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. |
| 95 | + quote! { |
| 96 | + impl #self_ty { |
| 97 | + /// Returns a mutable reference to the registration status: a tuple of the weak reference on the plugin and of the glib type. |
| 98 | + /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the data. |
| 99 | + #[inline] |
| 100 | + fn get_registration_status_ref_mut() -> &'static mut Option<(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type)> { |
| 101 | + static mut REGISTRATION_STATUS: ::std::sync::Mutex<Option<(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type)>> = ::std::sync::Mutex::new(None); |
| 102 | + unsafe { REGISTRATION_STATUS.get_mut().unwrap() } |
| 103 | + } |
| 104 | + |
| 105 | + /// Registers the object interface as a dynamic type within the plugin only once. |
| 106 | + /// Plugin must have been used at least once. |
| 107 | + /// Do nothing if plugin has never been used or if the object interface is already registered as a dynamic type. |
| 108 | + #[inline] |
| 109 | + fn register_interface() -> #crate_ident::Type { |
| 110 | + let registration_status_ref_mut = Self::get_registration_status_ref_mut(); |
| 111 | + match registration_status_ref_mut { |
| 112 | + // plugin has never been used, so the object interface cannot be registered as a dynamic type. |
| 113 | + None => #crate_ident::Type::INVALID, |
| 114 | + // plugin has been used and the object interface has not been registered yet, so registers it as a dynamic type. |
| 115 | + Some((type_plugin, type_)) if !type_.is_valid() => { |
| 116 | + *type_ = #crate_ident::subclass::register_dynamic_interface::<#plugin_ty, Self>(&(type_plugin.upgrade().unwrap())); |
| 117 | + *type_ |
| 118 | + }, |
| 119 | + // plugin has been used and the object interface has already been registered as a dynamic type. |
| 120 | + Some((_, type_)) => *type_ |
| 121 | + } |
| 122 | + } |
| 123 | + |
| 124 | + /// Depending on the plugin lifecycle state and on the registration status of the object interface: |
| 125 | + /// If plugin is used (and has loaded the implementation) for the first time, postpones the registration and stores a weak reference on the plugin. |
| 126 | + /// If plugin is reused (and has reloaded the implementation) and the object interface has been already registered as a dynamic type, reregisters it. |
| 127 | + /// An object interface can be reregistered several times as a dynamic type. |
| 128 | + /// If plugin is reused (and has reloaded the implementation) and the object interface has not been registered yet as a dynamic type, do nothing. |
| 129 | + #[inline] |
| 130 | + pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { |
| 131 | + let registration_status_ref_mut = Self::get_registration_status_ref_mut(); |
| 132 | + match registration_status_ref_mut { |
| 133 | + // 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. |
| 134 | + None => { |
| 135 | + *registration_status_ref_mut = Some((#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); |
| 136 | + true |
| 137 | + }, |
| 138 | + // 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. |
| 139 | + Some((_, type_)) if type_.is_valid() => { |
| 140 | + *type_ = #crate_ident::subclass::register_dynamic_interface::<#plugin_ty, Self>(type_plugin); |
| 141 | + type_.is_valid() |
| 142 | + }, |
| 143 | + // 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. |
| 144 | + Some(_) => { |
| 145 | + true |
| 146 | + } |
| 147 | + } |
| 148 | + } |
| 149 | + |
| 150 | + /// Depending on the plugin lifecycle state and on the registration status of the object interface: |
| 151 | + /// 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. |
| 152 | + /// Else do nothing. |
| 153 | + #[inline] |
| 154 | + pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { |
| 155 | + let registration_status_ref_mut = Self::get_registration_status_ref_mut(); |
| 156 | + match registration_status_ref_mut { |
| 157 | + // plugin has never been used, so unload implementation is unexpected. |
| 158 | + None => false, |
| 159 | + // plugin has been used at least one time and the object interface has been registered as a dynamic type at least one time. |
| 160 | + Some((_, type_)) if type_.is_valid() => true, |
| 161 | + // 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. |
| 162 | + Some(_) => { |
| 163 | + *registration_status_ref_mut = None; |
| 164 | + true |
| 165 | + } |
| 166 | + } |
| 167 | + } |
| 168 | + } |
| 169 | + } |
| 170 | + } else { |
| 171 | + // registers immediately the object interface as a dynamic type. |
| 172 | + quote! { |
| 173 | + impl #self_ty { |
| 174 | + /// Returns a mutable reference to the glib type. |
| 175 | + /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the atomic data. |
| 176 | + #[inline] |
| 177 | + fn get_type_mut() -> &'static mut #crate_ident::ffi::GType { |
| 178 | + static mut TYPE: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); |
| 179 | + unsafe { TYPE.get_mut() } |
| 180 | + } |
| 181 | + |
| 182 | + /// Do nothing as the object interface has been registered on implementation load. |
| 183 | + #[inline] |
| 184 | + fn register_interface() -> #crate_ident::Type { |
| 185 | + unsafe { <#crate_ident::Type as #crate_ident::translate::FromGlib<#crate_ident::ffi::GType>>::from_glib(*Self::get_type_mut()) } |
| 186 | + } |
| 187 | + |
| 188 | + /// Registers the object interface as a dynamic type within the plugin. |
| 189 | + /// The object interface can be registered several times as a dynamic type. |
| 190 | + #[inline] |
| 191 | + pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { |
| 192 | + let type_mut = Self::get_type_mut(); |
| 193 | + *type_mut = #crate_ident::translate::IntoGlib::into_glib(#crate_ident::subclass::register_dynamic_interface::<#plugin_ty, Self>(type_plugin)); |
| 194 | + *type_mut != #crate_ident::gobject_ffi::G_TYPE_INVALID |
| 195 | + } |
| 196 | + |
| 197 | + /// Do nothing as object interfaces registered as dynamic types are never unregistered. |
| 198 | + #[inline] |
| 199 | + pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { |
| 200 | + true |
| 201 | + } |
| 202 | + } |
| 203 | + } |
| 204 | + }; |
| 205 | + |
| 206 | + impl_object_interface_(register_interface, input) |
| 207 | +} |
| 208 | + |
| 209 | +pub fn impl_object_interface_( |
| 210 | + register_interface: TokenStream, |
| 211 | + input: &syn::ItemImpl, |
| 212 | +) -> TokenStream { |
11 | 213 | let mut has_prerequisites = false;
|
12 | 214 | for item in &input.items {
|
13 | 215 | if let syn::ImplItem::Type(type_) = item {
|
@@ -53,20 +255,10 @@ pub fn impl_object_interface(input: &syn::ItemImpl) -> TokenStream {
|
53 | 255 | unsafe impl #crate_ident::subclass::interface::ObjectInterfaceType for #self_ty {
|
54 | 256 | #[inline]
|
55 | 257 | fn type_() -> #crate_ident::Type {
|
56 |
| - static ONCE: ::std::sync::Once = ::std::sync::Once::new(); |
57 |
| - static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID; |
58 |
| - |
59 |
| - ONCE.call_once(|| { |
60 |
| - let type_ = #crate_ident::subclass::register_interface::<Self>(); |
61 |
| - unsafe { |
62 |
| - TYPE = type_; |
63 |
| - } |
64 |
| - }); |
65 |
| - |
66 |
| - unsafe { |
67 |
| - TYPE |
68 |
| - } |
| 258 | + Self::register_interface() |
69 | 259 | }
|
70 | 260 | }
|
| 261 | + |
| 262 | + #register_interface |
71 | 263 | }
|
72 | 264 | }
|
0 commit comments