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 } ;
44use 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 } ;
76use 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
1416pub 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