Skip to content

Commit d08bf24

Browse files
authored
Merge pull request #1271 from fbrouille/flags_dynamic
add support of flags registered as dynamic types
2 parents acb7f0d + 8d3ac91 commit d08bf24

File tree

9 files changed

+1216
-90
lines changed

9 files changed

+1216
-90
lines changed

glib-macros/src/enum_derive.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ fn register_enum_as_dynamic(
284284

285285
let enum_values = quote! {
286286
#crate_ident::enums::EnumValuesStorage<#nb_enum_values> = unsafe {
287-
#crate_ident::enums::EnumValuesStorage::<#nb_enum_values>::new::<{#nb_enum_values - 1}>([
287+
#crate_ident::enums::EnumValuesStorage::<#nb_enum_values>::new([
288288
#(#enum_values_iter)*
289289
])
290290
}

glib-macros/src/flags_attribute.rs

Lines changed: 241 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
// Take a look at the license at the top of the repository in the LICENSE file.
22

3-
use heck::{ToKebabCase, ToUpperCamelCase};
3+
use heck::{ToKebabCase, ToShoutySnakeCase, ToUpperCamelCase};
44
use proc_macro2::TokenStream;
5-
use proc_macro_error::abort_call_site;
6-
use quote::{quote, quote_spanned};
5+
use quote::{format_ident, quote, quote_spanned, ToTokens};
76
use syn::{
8-
punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Data, DeriveInput, Ident,
9-
Variant, Visibility,
7+
punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, Ident, Variant, Visibility,
108
};
119

12-
use crate::utils::{crate_ident_new, parse_nested_meta_items, NestedMetaItem};
10+
use crate::utils::{
11+
crate_ident_new, parse_nested_meta_items, parse_optional_nested_meta_items, NestedMetaItem,
12+
};
13+
14+
pub const WRONG_PLACE_MSG: &str = "#[glib::flags] only supports enums";
1315

1416
pub struct AttrInput {
1517
pub enum_name: syn::LitStr,
@@ -114,20 +116,61 @@ fn gen_bitflags(
114116
}
115117
}
116118

117-
pub fn impl_flags(attrs: AttrInput, input: &DeriveInput) -> TokenStream {
118-
let gtype_name = attrs.enum_name.value();
119-
let name = &input.ident;
120-
let visibility = &input.vis;
119+
pub fn impl_flags(attrs: AttrInput, input: &mut syn::ItemEnum) -> TokenStream {
120+
let gtype_name = attrs.enum_name;
121121

122-
let enum_variants = match input.data {
123-
Data::Enum(ref e) => &e.variants,
124-
_ => abort_call_site!("#[glib::flags] only supports enums"),
125-
};
122+
let syn::ItemEnum {
123+
attrs,
124+
ident: name,
125+
vis: visibility,
126+
..
127+
} = input;
128+
129+
let enum_variants = &input.variants;
130+
let (g_flags_values, nb_flags_values) = gen_flags_values(name, enum_variants);
126131

127132
let crate_ident = crate_ident_new();
128133

134+
let mut plugin_type = NestedMetaItem::<syn::Path>::new("plugin_type").value_required();
135+
let mut lazy_registration =
136+
NestedMetaItem::<syn::LitBool>::new("lazy_registration").value_required();
137+
138+
let found = parse_optional_nested_meta_items(
139+
&*attrs,
140+
"flags_dynamic",
141+
&mut [&mut plugin_type, &mut lazy_registration],
142+
);
143+
144+
let register_flags = match found {
145+
Err(e) => return e.to_compile_error(),
146+
Ok(None) => register_flags_as_static(
147+
&crate_ident,
148+
name,
149+
gtype_name,
150+
g_flags_values,
151+
nb_flags_values,
152+
),
153+
Ok(Some(_)) => {
154+
// remove attribute 'flags_dynamic' from the attribute list because it is not a real proc_macro_attribute
155+
attrs.retain(|attr| !attr.path().is_ident("flags_dynamic"));
156+
let plugin_ty = plugin_type
157+
.value
158+
.map(|p| p.into_token_stream())
159+
.unwrap_or(quote!(#crate_ident::TypeModule));
160+
let lazy_registration = lazy_registration.value.map(|b| b.value).unwrap_or_default();
161+
register_flags_as_dynamic(
162+
&crate_ident,
163+
plugin_ty,
164+
lazy_registration,
165+
name,
166+
gtype_name,
167+
g_flags_values,
168+
nb_flags_values,
169+
)
170+
}
171+
};
172+
129173
let bitflags = gen_bitflags(name, visibility, enum_variants, &crate_ident);
130-
let (flags_values, nb_flags_values) = gen_flags_values(name, enum_variants);
131174

132175
quote! {
133176
#bitflags
@@ -202,12 +245,34 @@ pub fn impl_flags(attrs: AttrInput, input: &DeriveInput) -> TokenStream {
202245
impl #crate_ident::StaticType for #name {
203246
#[inline]
204247
fn static_type() -> #crate_ident::Type {
248+
Self::register_flags()
249+
}
250+
}
251+
252+
#register_flags
253+
}
254+
}
255+
256+
// Registers the flags as a static type.
257+
fn register_flags_as_static(
258+
crate_ident: &TokenStream,
259+
name: &syn::Ident,
260+
gtype_name: syn::LitStr,
261+
g_flags_values: TokenStream,
262+
nb_flags_values: usize,
263+
) -> TokenStream {
264+
// registers the flags on first use (lazy registration).
265+
quote! {
266+
impl #name {
267+
/// Registers the flags only once.
268+
#[inline]
269+
fn register_flags() -> #crate_ident::Type {
205270
static ONCE: ::std::sync::Once = ::std::sync::Once::new();
206271
static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID;
207272

208273
ONCE.call_once(|| {
209274
static mut VALUES: [#crate_ident::gobject_ffi::GFlagsValue; #nb_flags_values] = [
210-
#flags_values
275+
#g_flags_values
211276
#crate_ident::gobject_ffi::GFlagsValue {
212277
value: 0,
213278
value_name: ::std::ptr::null(),
@@ -231,3 +296,163 @@ pub fn impl_flags(attrs: AttrInput, input: &DeriveInput) -> TokenStream {
231296
}
232297
}
233298
}
299+
300+
// The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]).
301+
// Flags can be reregistered as a dynamic type.
302+
fn register_flags_as_dynamic(
303+
crate_ident: &TokenStream,
304+
plugin_ty: TokenStream,
305+
lazy_registration: bool,
306+
name: &syn::Ident,
307+
gtype_name: syn::LitStr,
308+
g_flags_values: TokenStream,
309+
nb_flags_values: usize,
310+
) -> TokenStream {
311+
// Wrap each GFlagsValue to FlagsValue
312+
let g_flags_values_expr: syn::ExprArray = syn::parse_quote! { [#g_flags_values] };
313+
let flags_values_iter = g_flags_values_expr.elems.iter().map(|v| {
314+
quote_spanned! {syn::spanned::Spanned::span(&v)=>
315+
#crate_ident::FlagsValue::unsafe_from(#v),
316+
}
317+
});
318+
319+
let flags_values = quote! {
320+
#crate_ident::enums::FlagsValuesStorage<#nb_flags_values> = unsafe {
321+
#crate_ident::enums::FlagsValuesStorage::<#nb_flags_values>::new([
322+
#(#flags_values_iter)*
323+
])
324+
}
325+
};
326+
327+
// The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]).
328+
// Flags can be reregistered as a dynamic type.
329+
if lazy_registration {
330+
// registers the flags as a dynamic type on the first use (lazy registration).
331+
// a weak reference on the plugin is stored and will be used later on the first use of the flags.
332+
// this implementation relies on a static storage of a weak reference on the plugin and of the GLib type to know if the flags have been registered.
333+
334+
// the registration status type.
335+
let registration_status_type = format_ident!("{}RegistrationStatus", name);
336+
// name of the static variable to store the registration status.
337+
let registration_status = format_ident!(
338+
"{}",
339+
registration_status_type.to_string().to_shouty_snake_case()
340+
);
341+
// name of the static array to store the flags values.
342+
let flags_values_array =
343+
format_ident!("{}_VALUES", name.to_string().to_shouty_snake_case());
344+
345+
quote! {
346+
/// The registration status type: a tuple of the weak reference on the plugin and of the GLib type.
347+
struct #registration_status_type(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type);
348+
unsafe impl Send for #registration_status_type {}
349+
350+
/// The registration status protected by a mutex guarantees so that no other threads are concurrently accessing the data.
351+
static #registration_status: ::std::sync::Mutex<Option<#registration_status_type>> = ::std::sync::Mutex::new(None);
352+
353+
/// Array of `FlagsValue` for the possible flags values.
354+
static #flags_values_array: #flags_values;
355+
356+
impl #name {
357+
/// Registers the flags as a dynamic type within the plugin only once.
358+
/// Plugin must have been used at least once.
359+
/// Do nothing if plugin has never been used or if the flags are already registered as a dynamic type.
360+
#[inline]
361+
fn register_flags() -> #crate_ident::Type {
362+
let mut registration_status = #registration_status.lock().unwrap();
363+
match ::std::ops::DerefMut::deref_mut(&mut registration_status) {
364+
// plugin has never been used, so the flags cannot be registered as a dynamic type.
365+
None => #crate_ident::Type::INVALID,
366+
// plugin has been used and the flags have not been registered yet, so registers tem as a dynamic type.
367+
Some(#registration_status_type(type_plugin, type_)) if !type_.is_valid() => {
368+
*type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_flags(type_plugin.upgrade().unwrap().as_ref(), #gtype_name, #flags_values_array.as_ref());
369+
*type_
370+
},
371+
// plugin has been used and the flags have already been registered as a dynamic type.
372+
Some(#registration_status_type(_, type_)) => *type_
373+
}
374+
}
375+
376+
/// Depending on the plugin lifecycle state and on the registration status of the flags:
377+
/// If plugin is used (and has loaded the implementation) for the first time, postpones the registration and stores a weak reference on the plugin.
378+
/// If plugin is reused (and has reloaded the implementation) and the flags have been already registered as a dynamic type, reregisters them.
379+
/// Flags can be reregistered several times as a dynamic type.
380+
/// If plugin is reused (and has reloaded the implementation) and the flags have not been registered yet as a dynamic type, do nothing.
381+
#[inline]
382+
pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool {
383+
let mut registration_status = #registration_status.lock().unwrap();
384+
match ::std::ops::DerefMut::deref_mut(&mut registration_status) {
385+
// plugin has never been used (this is the first time), so postpones registration of the flags as a dynamic type on the first use.
386+
None => {
387+
*registration_status = Some(#registration_status_type(#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID));
388+
true
389+
},
390+
// plugin has been used at least one time and the flags have been registered as a dynamic type at least one time, so re-registers them.
391+
Some(#registration_status_type(_, type_)) if type_.is_valid() => {
392+
*type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_flags(type_plugin, #gtype_name, #flags_values_array.as_ref());
393+
type_.is_valid()
394+
},
395+
// plugin has been used at least one time but the flags have not been registered yet as a dynamic type, so keeps postponed registration.
396+
Some(_) => {
397+
true
398+
}
399+
}
400+
}
401+
402+
/// Depending on the plugin lifecycle state and on the registration status of the flags:
403+
/// If plugin has been used (or reused) but the flags have not been registered yet as a dynamic type, cancels the postponed registration by deleting the weak reference on the plugin.
404+
/// Else do nothing.
405+
#[inline]
406+
pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool {
407+
let mut registration_status = #registration_status.lock().unwrap();
408+
match ::std::ops::DerefMut::deref_mut(&mut registration_status) {
409+
// plugin has never been used, so unload implementation is unexpected.
410+
None => false,
411+
// plugin has been used at least one time and the flags have been registered as a dynamic type at least one time.
412+
Some(#registration_status_type(_, type_)) if type_.is_valid() => true,
413+
// plugin has been used at least one time but the flags have not been registered yet as a dynamic type, so cancels the postponed registration.
414+
Some(_) => {
415+
*registration_status = None;
416+
true
417+
}
418+
}
419+
}
420+
}
421+
}
422+
} else {
423+
// registers immediately the flags as a dynamic type.
424+
425+
// name of the static variable to store the GLib type.
426+
let gtype_status = format_ident!("{}_G_TYPE", name.to_string().to_shouty_snake_case());
427+
428+
quote! {
429+
/// The GLib type which can be safely shared between threads.
430+
static #gtype_status: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID);
431+
432+
impl #name {
433+
/// Do nothing as the flags has been registered on implementation load.
434+
#[inline]
435+
fn register_flags() -> #crate_ident::Type {
436+
let gtype = #gtype_status.load(::std::sync::atomic::Ordering::Acquire);
437+
unsafe { <#crate_ident::Type as #crate_ident::translate::FromGlib<#crate_ident::ffi::GType>>::from_glib(gtype) }
438+
}
439+
440+
/// Registers the flags as a dynamic type within the plugin.
441+
/// The flags can be registered several times as a dynamic type.
442+
#[inline]
443+
pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool {
444+
static VALUES: #flags_values;
445+
let gtype = #crate_ident::translate::IntoGlib::into_glib(<#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_flags(type_plugin, #gtype_name, VALUES.as_ref()));
446+
#gtype_status.store(gtype, ::std::sync::atomic::Ordering::Release);
447+
gtype != #crate_ident::gobject_ffi::G_TYPE_INVALID
448+
}
449+
450+
/// Do nothing as flags registered as dynamic types are never unregistered.
451+
#[inline]
452+
pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool {
453+
true
454+
}
455+
}
456+
}
457+
}
458+
}

0 commit comments

Comments
 (0)