Skip to content

Commit 2cb10ec

Browse files
authored
Merge pull request #1169 from fbrouille/module_type
add support of module types
2 parents e3e821d + 6839fc4 commit 2cb10ec

22 files changed

+2481
-51
lines changed

glib-macros/src/lib.rs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,75 @@ pub fn object_subclass(_attr: TokenStream, item: TokenStream) -> TokenStream {
645645
}
646646
}
647647

648+
/// Macro for boilerplate of [`ObjectSubclass`] implementations that are
649+
/// registered as dynamic types.
650+
///
651+
/// An object subclass must be explicitly registered as a dynamic type when the
652+
/// system loads its implementation (see [`TypePlugin`] and [`TypeModule`].
653+
/// Therefore, whereas an object subclass can be registered only once as a
654+
/// static type, it can be registered several times as a dynamic type.
655+
///
656+
/// An object subclass registered as a dynamic type is never unregistered. The
657+
/// system calls [`TypePluginExt::unuse`] to unload its implementation. If the
658+
/// [`TypePlugin`] subclass is a [`TypeModule`], the object subclass registered
659+
/// as a dynamic type is marked as unloaded and must be registered again when
660+
/// the module is reloaded.
661+
///
662+
/// This macro provides two behaviors when registering an object subclass as a
663+
/// dynamic type:
664+
///
665+
/// By default an object subclass is registered as a dynamic type when the
666+
/// system loads its implementation (e.g. when the module is loaded):
667+
/// ```ignore
668+
/// #[glib::dynamic_object_subclass]
669+
/// impl ObjectSubclass for MyType { ... }
670+
/// ```
671+
///
672+
/// Optionally setting the macro attribute `lazy_registration` to `true`
673+
/// postpones registration on the first use (when `type_()` is called for the
674+
/// first time), similarly to the [`macro@object_subclass`]
675+
/// macro:
676+
/// ```ignore
677+
/// #[glib::dynamic_object_subclass(lazy_registration = true)]
678+
/// impl ObjectSubclass for MyType { ... }
679+
/// ```
680+
///
681+
/// By default an object subclass is considered to be registered as a dynamic
682+
/// type within a [`TypeModule`] subclass. Optionally setting the macro
683+
/// attribute `plugin_type` allows to register an object subclass as a dynamic
684+
/// type within a given [`TypePlugin`] subclass:
685+
/// ```ignore
686+
/// #[glib::dynamic_object_subclass(plugin_type = MyPlugin)]
687+
/// impl ObjectSubclass for MyType { ... }
688+
/// ```
689+
///
690+
/// [`ObjectSubclass`]: ../glib/subclass/types/trait.ObjectSubclass.html
691+
/// [`TypePlugin`]: ../glib/gobject/type_plugin/struct.TypePlugin.html
692+
/// [`TypeModule`]: ../glib/gobject/type_module/struct.TypeModule.html
693+
/// [`TypePluginExt::unuse`]: ../glib/gobject/type_plugin/trait.TypePluginExt.html#method.unuse
694+
#[proc_macro_attribute]
695+
#[proc_macro_error]
696+
pub fn dynamic_object_subclass(attr: TokenStream, item: TokenStream) -> TokenStream {
697+
use proc_macro_error::abort_call_site;
698+
let attrs = match syn::parse::Parser::parse(
699+
syn::punctuated::Punctuated::<syn::Expr, syn::Token![,]>::parse_terminated,
700+
attr,
701+
) {
702+
Ok(attrs)
703+
if attrs
704+
.iter()
705+
.all(|attr| matches!(attr, syn::Expr::Assign(_))) =>
706+
{
707+
attrs
708+
}
709+
_ => abort_call_site!(object_subclass_attribute::WRONG_EXPRESSION_MSG),
710+
};
711+
match syn::parse::<syn::ItemImpl>(item) {
712+
Ok(input) => object_subclass_attribute::impl_dynamic_object_subclass(&attrs, &input).into(),
713+
Err(_) => abort_call_site!(object_subclass_attribute::WRONG_PLACE_MSG),
714+
}
715+
}
716+
648717
/// Macro for boilerplate of [`ObjectInterface`] implementations.
649718
///
650719
/// This adds implementations for the `get_type()` method, which should probably never be defined
@@ -670,6 +739,77 @@ pub fn object_interface(_attr: TokenStream, item: TokenStream) -> TokenStream {
670739
}
671740
}
672741

742+
/// Macro for boilerplate of [`ObjectInterface`] implementations that are
743+
/// registered as dynamic types.
744+
///
745+
/// An object interface must be explicitly registeredas a dynamic type when the
746+
/// system loads its implementation (see [`TypePlugin`] and [`TypeModule`].
747+
/// Therefore, whereas an object interface can be registered only once as a
748+
/// static type, it can be registered several times as a dynamic type.
749+
///
750+
/// An object interface registered as a dynamic type is never unregistered. The
751+
/// system calls [`TypePluginExt::unuse`] to unload its implementation. If the
752+
/// [`TypePlugin`] subclass is a [`TypeModule`], the object interface
753+
/// registered as a dynamic type is marked as unloaded and must be registered
754+
/// again when the module is reloaded.
755+
///
756+
/// This macro provides two behaviors when registering an object interface as a
757+
/// dynamic type:
758+
///
759+
/// By default an object interface is registered as a dynamic type when the
760+
/// system loads its implementation (e.g. when the module is loaded):
761+
/// ```ignore
762+
/// #[glib::dynamic_object_interface]
763+
/// unsafe impl ObjectInterface for MyInterface { ... }
764+
/// ```
765+
///
766+
/// Optionally setting the macro attribute `lazy_registration` to `true`
767+
/// postpones registration on the first use (when `type_()` is called for the
768+
/// first time), similarly to the [`macro@object_subclass`]
769+
/// [`macro@object_interface`] macro.
770+
/// ```ignore
771+
/// #[glib::dynamic_object_interface(lazy_registration = true)]
772+
/// unsafe impl ObjectInterface for MyInterface { ... }
773+
/// ```
774+
///
775+
/// By default an object interface is considered to be registered as a dynamic
776+
/// type within a [`TypeModule`] subclass. Optionally setting the macro
777+
/// attribute `plugin_type` allows to register an object interface as a dynamic
778+
/// type within a given [`TypePlugin`] subclass:
779+
/// ```ignore
780+
/// #[glib::dynamic_object_interface(plugin_type = MyPlugin)]
781+
/// unsafe impl ObjectInterface for MyInterface { ... }
782+
/// ```
783+
///
784+
/// [`ObjectInterface`]: ../glib/subclass/interface/trait.ObjectInterface.html
785+
/// [`TypePlugin`]: ../glib/gobject/type_plugin/struct.TypePlugin.html
786+
/// [`TypeModule`]: ../glib/gobject/type_module/struct.TypeModule.html
787+
/// [`TypePluginExt::unuse`]: ../glib/gobject/type_plugin/trait.TypePluginExt.html#method.unuse
788+
#[proc_macro_attribute]
789+
#[proc_macro_error]
790+
pub fn dynamic_object_interface(attr: TokenStream, item: TokenStream) -> TokenStream {
791+
use proc_macro_error::abort_call_site;
792+
let attrs = match syn::parse::Parser::parse(
793+
syn::punctuated::Punctuated::<syn::Expr, syn::Token![,]>::parse_terminated,
794+
attr,
795+
) {
796+
Ok(attrs)
797+
if attrs
798+
.iter()
799+
.all(|attr| matches!(attr, syn::Expr::Assign(_))) =>
800+
{
801+
attrs
802+
}
803+
_ => abort_call_site!(object_interface_attribute::WRONG_EXPRESSION_MSG),
804+
};
805+
match syn::parse::<syn::ItemImpl>(item) {
806+
Ok(input) => {
807+
object_interface_attribute::impl_dynamic_object_interface(&attrs, &input).into()
808+
}
809+
Err(_) => abort_call_site!(object_interface_attribute::WRONG_PLACE_MSG),
810+
}
811+
}
812+
673813
/// Macro for deriving implementations of [`glib::clone::Downgrade`] and
674814
/// [`glib::clone::Upgrade`] traits and a weak type.
675815
///

glib-macros/src/object_interface_attribute.rs

Lines changed: 206 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,214 @@
22

33
use proc_macro2::TokenStream;
44
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`";
612

713
pub const WRONG_PLACE_MSG: &str =
814
"This macro should be used on `impl` block for `glib::ObjectInterface` trait";
915

1016
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 {
11213
let mut has_prerequisites = false;
12214
for item in &input.items {
13215
if let syn::ImplItem::Type(type_) = item {
@@ -53,20 +255,10 @@ pub fn impl_object_interface(input: &syn::ItemImpl) -> TokenStream {
53255
unsafe impl #crate_ident::subclass::interface::ObjectInterfaceType for #self_ty {
54256
#[inline]
55257
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()
69259
}
70260
}
261+
262+
#register_interface
71263
}
72264
}

0 commit comments

Comments
 (0)