From 68da73d2a5a1292ac9f0c1ebfc9a11e61feb0330 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Fri, 3 Nov 2023 11:45:49 +0100 Subject: [PATCH 01/15] add support of enums as dynamic types Signed-off-by: fbrouille --- glib-macros/src/enum_derive.rs | 285 ++++++++-- glib-macros/src/lib.rs | 72 ++- glib-macros/src/object_interface_attribute.rs | 4 +- glib-macros/src/object_subclass_attribute.rs | 4 +- glib-macros/tests/dynamic_enums.rs | 494 ++++++++++++++++++ glib-macros/tests/dynamic_objects.rs | 8 + glib/Gir_GObject.toml | 1 + glib/src/enums.rs | 68 ++- glib/src/gobject/dynamic_object.rs | 26 +- glib/src/gobject/type_module.rs | 17 +- glib/src/lib.rs | 4 +- glib/src/subclass/mod.rs | 4 + glib/src/subclass/type_plugin.rs | 16 +- 13 files changed, 953 insertions(+), 50 deletions(-) create mode 100644 glib-macros/tests/dynamic_enums.rs diff --git a/glib-macros/src/enum_derive.rs b/glib-macros/src/enum_derive.rs index de61835603c7..1d1b3ffa98c7 100644 --- a/glib-macros/src/enum_derive.rs +++ b/glib-macros/src/enum_derive.rs @@ -3,24 +3,31 @@ use heck::{ToKebabCase, ToUpperCamelCase}; use proc_macro2::TokenStream; use proc_macro_error::abort_call_site; -use quote::{quote, quote_spanned}; +use quote::{quote, quote_spanned, ToTokens}; use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, Data, Ident, Variant}; use crate::utils::{crate_ident_new, gen_enum_from_glib, parse_nested_meta_items, NestedMetaItem}; -// Generate glib::gobject_ffi::GEnumValue structs mapping the enum such as: +// If `wrap` is `false`, generates glib::gobject_ffi::GEnumValue structs mapping the enum such as: // glib::gobject_ffi::GEnumValue { // value: Animal::Goat as i32, // value_name: "Goat\0" as *const _ as *const _, // value_nick: "goat\0" as *const _ as *const _, // }, +// If `wrap` is `true`, generates glib::EnumValue structs mapping the enum such as: +// glib::EnumValue::new(glib::gobject_ffi::GEnumValue { +// value: Animal::Goat as i32, +// value_name: "Goat\0" as *const _ as *const _, +// value_nick: "goat\0" as *const _ as *const _, +// }), fn gen_enum_values( + wrap: bool, enum_name: &Ident, enum_variants: &Punctuated, ) -> (TokenStream, usize) { let crate_ident = crate_ident_new(); - // start at one as GEnumValue array is null-terminated + // starts at one as GEnumValue array is null-terminated. let mut n = 1; let recurse = enum_variants.iter().map(|v| { let name = &v.ident; @@ -43,12 +50,24 @@ fn gen_enum_values( let value_nick = format!("{value_nick}\0"); n += 1; - quote_spanned! {v.span()=> - #crate_ident::gobject_ffi::GEnumValue { - value: #enum_name::#name as i32, - value_name: #value_name as *const _ as *const _, - value_nick: #value_nick as *const _ as *const _, - }, + if wrap { + // generates a glib::EnumValue. + quote_spanned! {v.span()=> + #crate_ident::EnumValue::new(#crate_ident::gobject_ffi::GEnumValue { + value: #enum_name::#name as i32, + value_name: #value_name as *const _ as *const _, + value_nick: #value_nick as *const _ as *const _, + }), + } + } else { + // generates a glib::gobject_ffi::GEnumValue. + quote_spanned! {v.span()=> + #crate_ident::gobject_ffi::GEnumValue { + value: #enum_name::#name as i32, + value_name: #value_name as *const _ as *const _, + value_nick: #value_nick as *const _ as *const _, + }, + } } }); ( @@ -81,10 +100,228 @@ pub fn impl_enum(input: &syn::DeriveInput) -> TokenStream { }; let gtype_name = gtype_name.value.unwrap(); let from_glib = gen_enum_from_glib(name, enum_variants); - let (enum_values, nb_enum_values) = gen_enum_values(name, enum_variants); + let (enum_values, nb_enum_values) = gen_enum_values(false, name, enum_variants); + + let crate_ident = crate_ident_new(); + + // registers the enum on first use (lazy registration). + let register_enum = quote! { + impl #name { + /// Registers the enum only once. + #[inline] + fn register_enum() -> #crate_ident::Type { + static ONCE: ::std::sync::Once = ::std::sync::Once::new(); + static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID; + + ONCE.call_once(|| { + static mut VALUES: [#crate_ident::gobject_ffi::GEnumValue; #nb_enum_values] = [ + #enum_values + #crate_ident::gobject_ffi::GEnumValue { + value: 0, + value_name: ::std::ptr::null(), + value_nick: ::std::ptr::null(), + }, + ]; + let name = ::std::ffi::CString::new(#gtype_name).expect("CString::new failed"); + unsafe { + let type_ = #crate_ident::gobject_ffi::g_enum_register_static(name.as_ptr(), VALUES.as_ptr()); + let type_: #crate_ident::Type = #crate_ident::translate::from_glib(type_); + assert!(type_.is_valid()); + TYPE = type_; + } + }); + + unsafe { + TYPE + } + } + } + }; + + impl_enum_(name, from_glib, register_enum) +} + +pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { + let name = &input.ident; + + let enum_variants = match input.data { + Data::Enum(ref e) => &e.variants, + _ => abort_call_site!("#[derive(glib::Enum)] only supports enums"), + }; + + let mut gtype_name = NestedMetaItem::::new("name") + .required() + .value_required(); + let mut plugin_type = NestedMetaItem::::new("plugin_type").value_required(); + let mut lazy_registration = + NestedMetaItem::::new("lazy_registration").value_required(); + let found = parse_nested_meta_items( + &input.attrs, + "enum_type", + &mut [&mut gtype_name, &mut plugin_type, &mut lazy_registration], + ); + + match found { + Ok(None) => { + abort_call_site!("#[derive(glib::DynamicEnum)] requires #[enum_type(name = \"EnumTypeName\"[, plugin_type = ][, lazy_registration = true|false])]") + } + Err(e) => return e.to_compile_error(), + Ok(attr) => attr, + }; let crate_ident = crate_ident_new(); + let gtype_name = gtype_name.value.unwrap(); + let plugin_ty = plugin_type + .value + .map(|p| p.into_token_stream()) + .unwrap_or(quote!(#crate_ident::TypeModule)); + let lazy_registration = lazy_registration.value.map(|b| b.value).unwrap_or_default(); + + let from_glib = gen_enum_from_glib(name, enum_variants); + let (enum_values, nb_enum_values) = gen_enum_values(true, name, enum_variants); + + let enum_values_expr = quote! { + [#crate_ident::EnumValue; #nb_enum_values] = [ + #enum_values + #crate_ident::EnumValue::new(#crate_ident::gobject_ffi::GEnumValue { + value: 0, + value_name: ::std::ptr::null(), + value_nick: ::std::ptr::null(), + }), + ] + }; + + // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). + // An enum can be reregistered as a dynamic type. + let register_enum_impl = if lazy_registration { + // registers the enum as a dynamic type on the first use (lazy registration). + // a weak reference on the plugin is stored and will be used later on the first use of the enum. + // this implementation relies on a static storage of a weak reference on the plugin and of the glib type to know if the enum has been registered. + quote! { + impl #name { + /// Returns a mutable reference to the registration status: a tuple of the weak reference on the plugin and of the glib type. + /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the data. + #[inline] + fn get_registration_status_ref_mut() -> &'static mut Option<(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type)> { + static mut REGISTRATION_STATUS: ::std::sync::Mutex::Weak, #crate_ident::Type)>> = ::std::sync::Mutex::new(None); + unsafe { REGISTRATION_STATUS.get_mut().unwrap() } + } + + /// Registers the enum as a dynamic type within the plugin only once. + /// Plugin must have been used at least once. + /// Do nothing if plugin has never been used or if the enum is already registered as a dynamic type. + #[inline] + fn register_enum() -> #crate_ident::Type { + let registration_status_ref_mut = Self::get_registration_status_ref_mut(); + match registration_status_ref_mut { + // plugin has never been used, so the enum cannot be registered as a dynamic type. + None => #crate_ident::Type::INVALID, + // plugin has been used and the enum has not been registered yet, so registers it as a dynamic type. + Some((type_plugin, type_)) if !type_.is_valid() => { + static mut VALUES: #enum_values_expr; + *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin.upgrade().unwrap().as_ref(), #gtype_name, unsafe { &VALUES } ); + *type_ + }, + // plugin has been used and the enum has already been registered as a dynamic type. + Some((_, type_)) => *type_ + } + } + + /// Depending on the plugin lifecycle state and on the registration status of the enum: + /// If plugin is used (and has loaded the implementation) for the first time, postpones the registration and stores a weak reference on the plugin. + /// If plugin is reused (and has reloaded the implementation) and the enum has been already registered as a dynamic type, reregisters it. + /// An enum can be reregistered several times as a dynamic type. + /// If plugin is reused (and has reloaded the implementation) and the enum has not been registered yet as a dynamic type, do nothing. + #[inline] + pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { + let registration_status_ref_mut = Self::get_registration_status_ref_mut(); + match registration_status_ref_mut { + // plugin has never been used (this is the first time), so postpones registration of the enum as a dynamic type on the first use. + None => { + *registration_status_ref_mut = Some((#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); + true + }, + // plugin has been used at least one time and the enum has been registered as a dynamic type at least one time, so re-registers it. + Some((_, type_)) if type_.is_valid() => { + static mut VALUES: #enum_values_expr; + *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, unsafe { &VALUES } ); + type_.is_valid() + }, + // plugin has been used at least one time but the enum has not been registered yet as a dynamic type, so keeps postponed registration. + Some(_) => { + true + } + } + } + + /// Depending on the plugin lifecycle state and on the registration status of the enum: + /// If plugin has been used (or reused) but the enum has not been registered yet as a dynamic type, cancels the postponed registration by deleting the weak reference on the plugin. + /// Else do nothing. + #[inline] + pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { + let registration_status_ref_mut = Self::get_registration_status_ref_mut(); + match registration_status_ref_mut { + // plugin has never been used, so unload implementation is unexpected. + None => false, + // plugin has been used at least one time and the enum has been registered as a dynamic type at least one time. + Some((_, type_)) if type_.is_valid() => true, + // plugin has been used at least one time but the enum has not been registered yet as a dynamic type, so cancels the postponed registration. + Some(_) => { + *registration_status_ref_mut = None; + true + } + } + } + } + } + } else { + // registers immediately the enum as a dynamic type. + quote! { + impl #name { + /// Returns a mutable reference to the glib type. + /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the atomic data. + #[inline] + fn get_type_mut() -> &'static mut #crate_ident::ffi::GType { + static mut TYPE: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); + unsafe { TYPE.get_mut() } + } + + /// Do nothing as the enum has been registered on implementation load. + #[inline] + fn register_enum() -> #crate_ident::Type { + unsafe { <#crate_ident::Type as #crate_ident::translate::FromGlib<#crate_ident::ffi::GType>>::from_glib(*Self::get_type_mut()) } + } + + /// Registers the enum as a dynamic type within the plugin. + /// The enum can be registered several times as a dynamic type. + #[inline] + pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { + let type_mut = Self::get_type_mut(); + static mut VALUES: #enum_values_expr; + *type_mut = #crate_ident::translate::IntoGlib::into_glib(<#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, unsafe { &VALUES } )); + *type_mut != #crate_ident::gobject_ffi::G_TYPE_INVALID + } + + /// Do nothing as enums registered as dynamic types are never unregistered. + #[inline] + pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { + true + } + } + } + }; + + impl_enum_(name, from_glib, register_enum_impl) +} + +pub fn impl_enum_( + name: &syn::Ident, + from_glib: TokenStream, + register_enum: TokenStream, +) -> TokenStream { + let crate_ident = crate_ident_new(); + quote! { impl #crate_ident::translate::IntoGlib for #name { type GlibType = i32; @@ -161,34 +398,12 @@ pub fn impl_enum(input: &syn::DeriveInput) -> TokenStream { impl #crate_ident::StaticType for #name { #[inline] fn static_type() -> #crate_ident::Type { - static ONCE: ::std::sync::Once = ::std::sync::Once::new(); - static mut TYPE: #crate_ident::Type = #crate_ident::Type::INVALID; - - ONCE.call_once(|| { - static mut VALUES: [#crate_ident::gobject_ffi::GEnumValue; #nb_enum_values] = [ - #enum_values - #crate_ident::gobject_ffi::GEnumValue { - value: 0, - value_name: ::std::ptr::null(), - value_nick: ::std::ptr::null(), - }, - ]; - - let name = ::std::ffi::CString::new(#gtype_name).expect("CString::new failed"); - unsafe { - let type_ = #crate_ident::gobject_ffi::g_enum_register_static(name.as_ptr(), VALUES.as_ptr()); - let type_: #crate_ident::Type = #crate_ident::translate::from_glib(type_); - assert!(type_.is_valid()); - TYPE = type_; - } - }); - - unsafe { - TYPE - } + Self::register_enum() } } + #register_enum + impl #crate_ident::HasParamSpec for #name { type ParamSpec = #crate_ident::ParamSpecEnum; type SetValue = Self; diff --git a/glib-macros/src/lib.rs b/glib-macros/src/lib.rs index e0e9224ca52a..9feb068c134c 100644 --- a/glib-macros/src/lib.rs +++ b/glib-macros/src/lib.rs @@ -468,6 +468,75 @@ pub fn enum_derive(input: TokenStream) -> TokenStream { gen.into() } +/// Derive macro for register a rust enum in the glib type system as a dynamic +/// type and derive the [`glib::Value`] traits. +/// +/// An enum must be explicitly registered as a dynamic type when the system +/// loads its implementation (see [`TypePlugin`] and [`TypeModule`]. +/// Therefore, whereas an enum can be registered only once as a static type, +/// it can be registered several times as a dynamic type. +/// +/// An enum registered as a dynamic type is never unregistered. The system +/// calls [`TypePluginExt::unuse`] to unload its implementation. If the +/// [`TypePlugin`] subclass is a [`TypeModule`], the enum registered as a +/// dynamic type is marked as unloaded and must be registered again when the +/// module is reloaded. +/// +/// This macro provides two behaviors when registering an enum as a dynamic +/// type: +/// +/// By default an enum is registered as a dynamic type when the system loads +/// its implementation (e.g. when the module is loaded): +/// ```ignore +/// use glib::prelude::*; +/// use glib::subclass::prelude::*; +/// +/// #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::DynamicEnum)] +/// #[enum_type(name = "MyEnum")] +/// enum MyEnum { +/// Val, +/// #[enum_value(name = "My Val")] +/// ValWithCustomName, +/// #[enum_value(name = "My Other Val", nick = "other")] +/// ValWithCustomNameAndNick, +/// } +/// ``` +/// +/// Optionally setting the macro attribute `lazy_registration` to `true` +/// postpones registration on the first use (when `static_type()` is called for +/// the first time), similarly to the [`macro@enum_derive`] macro: +/// ```ignore +/// #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::DynamicEnum)] +/// #[enum_type(name = "MyEnum", lazy_registration = true)] +/// enum MyEnum { +/// ... +/// } +/// ``` +/// +/// By default an enum is considered to be registered as a dynamic type within +/// a [`TypeModule`] subclass. Optionally setting the macro attribute +/// `plugin_type` allows to register an enum as a dynamic type within a given +/// [`TypePlugin`] subclass: +/// ```ignore +/// #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::DynamicEnum)] +/// #[enum_type(name = "MyEnum", plugin_type = MyPlugin)] +/// enum MyEnum { +/// ... +/// } +/// ``` +/// +/// [`glib::Value`]: ../glib/value/struct.Value.html +/// [`TypePlugin`]: ../glib/gobject/type_plugin/struct.TypePlugin.html +/// [`TypeModule`]: ../glib/gobject/type_module/struct.TypeModule.html +/// [`TypePluginExt::unuse`]: ../glib/gobject/type_plugin/trait.TypePluginExt.html#method.unuse +#[proc_macro_derive(DynamicEnum, attributes(enum_type, enum_value))] +#[proc_macro_error] +pub fn dynamic_enum_derive(input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as DeriveInput); + let gen = enum_derive::impl_dynamic_enum(&input); + gen.into() +} + /// Attribute macro for defining flags using the `bitflags` crate. /// This macro will also define a `GFlags::type_` function and /// the [`glib::Value`] traits. @@ -671,8 +740,7 @@ pub fn object_subclass(_attr: TokenStream, item: TokenStream) -> TokenStream { /// /// Optionally setting the macro attribute `lazy_registration` to `true` /// postpones registration on the first use (when `type_()` is called for the -/// first time), similarly to the [`macro@object_subclass`] -/// macro: +/// first time), similarly to the [`macro@object_subclass`] macro: /// ```ignore /// #[glib::dynamic_object_subclass(lazy_registration = true)] /// impl ObjectSubclass for MyType { ... } diff --git a/glib-macros/src/object_interface_attribute.rs b/glib-macros/src/object_interface_attribute.rs index 321e6624d125..7048d1031ac5 100644 --- a/glib-macros/src/object_interface_attribute.rs +++ b/glib-macros/src/object_interface_attribute.rs @@ -86,8 +86,8 @@ pub fn impl_dynamic_object_interface( ), }; - // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt::unuse`]). - // An object interface can be reregistered as a dynamic type (see [`TypePluginExt::register_type`]). + // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). + // An object interface can be reregistered as a dynamic type. let register_interface = if lazy_registration { // registers the object interface as a dynamic type on the first use (lazy registration). // a weak reference on the plugin is stored and will be used later on the first use of the object interface. diff --git a/glib-macros/src/object_subclass_attribute.rs b/glib-macros/src/object_subclass_attribute.rs index d9ad6bf7ad19..a516baf25620 100644 --- a/glib-macros/src/object_subclass_attribute.rs +++ b/glib-macros/src/object_subclass_attribute.rs @@ -81,8 +81,8 @@ pub fn impl_dynamic_object_subclass( ), }; - // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt::unuse`]). - // An object subclass can be reregistered as a dynamic type (see [`TypePluginExt::register_type`]). + // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). + // An object subclass can be reregistered as a dynamic type. let register_type = if lazy_registration { // registers the object subclass as a dynamic type on the first use (lazy registration). // a weak reference on the plugin is stored and will be used later on the first use of the object subclass. diff --git a/glib-macros/tests/dynamic_enums.rs b/glib-macros/tests/dynamic_enums.rs new file mode 100644 index 000000000000..e9f8545c85d5 --- /dev/null +++ b/glib-macros/tests/dynamic_enums.rs @@ -0,0 +1,494 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use glib::{prelude::*, subclass::prelude::*, Cast}; + +mod module { + use super::*; + + mod imp { + use super::*; + + // impl for a type module (must extend `glib::TypeModule` and must implement `glib::TypePlugin`). + #[derive(Default)] + pub struct MyModule; + + #[glib::object_subclass] + impl ObjectSubclass for MyModule { + const NAME: &'static str = "MyModule"; + type Type = super::MyModule; + type ParentType = glib::TypeModule; + type Interfaces = (glib::TypePlugin,); + } + + impl ObjectImpl for MyModule {} + + impl TypePluginImpl for MyModule {} + + impl TypeModuleImpl for MyModule { + fn load(&self) -> bool { + // registers enums as dynamic types. + let my_module = self.obj(); + let type_module: &glib::TypeModule = my_module.upcast_ref(); + super::MyModuleEnum::on_implementation_load(type_module) + && super::MyModuleEnumLazy::on_implementation_load(type_module) + } + + fn unload(&self) { + // marks the enums as unregistered. + let my_module = self.obj(); + let type_module: &glib::TypeModule = my_module.upcast_ref(); + super::MyModuleEnumLazy::on_implementation_unload(type_module); + super::MyModuleEnum::on_implementation_unload(type_module); + } + } + } + + // an enum to register as a dynamic type. + #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::DynamicEnum)] + #[repr(u32)] + #[enum_type(name = "MyModuleEnum")] + pub enum MyModuleEnum { + #[enum_value(name = "Foo")] + Foo, + Bar, + } + + // an enum to lazy register as a dynamic type. + #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::DynamicEnum)] + #[repr(u32)] + #[enum_type(name = "MyModuleEnumLazy", lazy_registration = true)] + pub enum MyModuleEnumLazy { + #[enum_value(name = "Foo")] + Foo, + Bar, + } + + // a module (must extend `glib::TypeModule` and must implement `glib::TypePlugin`). + glib::wrapper! { + pub struct MyModule(ObjectSubclass) + @extends glib::TypeModule, @implements glib::TypePlugin; + } + + #[test] + fn dynamic_types() { + // 1st: creates a single module to test with. + let module = glib::Object::new::(); + // 1st: uses it to test lifecycle of enums registered as dynamic types. + enum_lifecycle(&module); + // 2nd: uses it to test behavior of enums registered as dynamic types. + enum_behavior(&module); + } + + // tests lifecycle of enums registered as dynamic types within a module. + fn enum_lifecycle(module: &MyModule) { + // checks types of enums to register as dynamic types are invalid (module is not loaded yet). + assert!(!MyModuleEnum::static_type().is_valid()); + assert!(!MyModuleEnumLazy::static_type().is_valid()); + + // simulates the glib type system to load/unload the module. + TypeModuleExt::use_(module); + TypeModuleExt::unuse(module); + + // checks types of enums registered as dynamic types are valid (module is unloaded). + assert!(MyModuleEnum::static_type().is_valid()); + // checks types of enums that are lazy registered as dynamic types are valid (module is unloaded). + assert!(!MyModuleEnumLazy::static_type().is_valid()); + + // simulates the glib type system to load the module. + TypeModuleExt::use_(module); + + // checks types of enums registered as dynamic types are valid (module is loaded). + let enum_type = MyModuleEnum::static_type(); + assert!(enum_type.is_valid()); + let enum_lazy_type = MyModuleEnumLazy::static_type(); + assert!(enum_lazy_type.is_valid()); + + // checks plugin of enums registered as dynamic types is `MyModule`. + assert_eq!( + enum_type.plugin().as_ref(), + Some(module.upcast_ref::()) + ); + assert_eq!( + enum_lazy_type.plugin().as_ref(), + Some(module.upcast_ref::()) + ); + + // simulates the glib type system to unload the module. + TypeModuleExt::unuse(module); + + // checks types of enums registered as dynamic types are still valid (should have been marked as unloaded by the glib type system but this cannot be checked). + assert!(MyModuleEnum::static_type().is_valid()); + assert!(MyModuleEnumLazy::static_type().is_valid()); + + // simulates the glib type system to reload the module. + TypeModuleExt::use_(module); + + // checks types of enums registered as dynamic types are still valid (should have been marked as loaded by the glib type system but this cannot be checked). + assert!(MyModuleEnum::static_type().is_valid()); + assert!(MyModuleEnumLazy::static_type().is_valid()); + + // simulates the glib type system to unload the module. + TypeModuleExt::unuse(module); + } + + // tests behavior of enums registered as dynamic types within a module. + fn enum_behavior(module: &MyModule) { + use glib::prelude::*; + use glib::translate::{FromGlib, IntoGlib}; + + // simulates the glib type system to load the module. + TypeModuleExt::use_(module); + + assert_eq!(MyModuleEnum::Foo.into_glib(), 0); + assert_eq!(MyModuleEnum::Bar.into_glib(), 1); + + assert_eq!(unsafe { MyModuleEnum::from_glib(0) }, MyModuleEnum::Foo); + assert_eq!(unsafe { MyModuleEnum::from_glib(1) }, MyModuleEnum::Bar); + + let t = MyModuleEnum::static_type(); + assert!(t.is_a(glib::Type::ENUM)); + assert_eq!(t.name(), "MyModuleEnum"); + + let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); + let v = e.value(0).expect("EnumClass::get_value(0) failed"); + assert_eq!(v.name(), "Foo"); + assert_eq!(v.nick(), "foo"); + let v = e.value(1).expect("EnumClass::get_value(1) failed"); + assert_eq!(v.name(), "Bar"); + assert_eq!(v.nick(), "bar"); + assert_eq!(e.value(2), None); + + // within enums registered as dynamic types, values are usables only if + // at least one type class ref exists (see `glib::EnumClass`). + assert_eq!( + MyModuleEnum::Foo.to_value().get::(), + Ok(MyModuleEnum::Foo) + ); + assert_eq!( + MyModuleEnum::Bar.to_value().get::(), + Ok(MyModuleEnum::Bar) + ); + + assert_eq!(MyModuleEnumLazy::Foo.into_glib(), 0); + assert_eq!(MyModuleEnumLazy::Bar.into_glib(), 1); + + assert_eq!( + unsafe { MyModuleEnumLazy::from_glib(0) }, + MyModuleEnumLazy::Foo + ); + assert_eq!( + unsafe { MyModuleEnumLazy::from_glib(1) }, + MyModuleEnumLazy::Bar + ); + + let t = MyModuleEnumLazy::static_type(); + assert!(t.is_a(glib::Type::ENUM)); + assert_eq!(t.name(), "MyModuleEnumLazy"); + + let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); + let v = e.value(0).expect("EnumClass::get_value(0) failed"); + assert_eq!(v.name(), "Foo"); + assert_eq!(v.nick(), "foo"); + let v = e.value(1).expect("EnumClass::get_value(1) failed"); + assert_eq!(v.name(), "Bar"); + assert_eq!(v.nick(), "bar"); + assert_eq!(e.value(2), None); + + // within enums registered as dynamic types, values are usables only if + // at least one type class ref exists (see `glib::EnumClass`). + assert_eq!( + MyModuleEnumLazy::Foo.to_value().get::(), + Ok(MyModuleEnumLazy::Foo) + ); + assert_eq!( + MyModuleEnumLazy::Bar.to_value().get::(), + Ok(MyModuleEnumLazy::Bar) + ); + + // simulates the glib type system to unload the module. + TypeModuleExt::unuse(module); + } +} + +pub mod plugin { + use super::*; + + pub mod imp { + use glib::EnumClass; + + use super::*; + use std::cell::Cell; + + // impl for a type plugin (must implement `glib::TypePlugin`). + #[derive(Default)] + pub struct MyPlugin { + my_enum_type_values: Cell>, + my_enum_lazy_type_values: Cell>, + } + + #[glib::object_subclass] + impl ObjectSubclass for MyPlugin { + const NAME: &'static str = "MyPlugin"; + type Type = super::MyPlugin; + type Interfaces = (glib::TypePlugin,); + } + + impl ObjectImpl for MyPlugin {} + + impl TypePluginImpl for MyPlugin { + fn use_plugin(&self) { + // register enums as dynamic types. + let my_plugin = self.obj(); + super::MyPluginEnum::on_implementation_load(my_plugin.as_ref()); + super::MyPluginEnumLazy::on_implementation_load(my_plugin.as_ref()); + } + + fn unuse_plugin(&self) { + // marks enums as unregistered. + let my_plugin = self.obj(); + super::MyPluginEnumLazy::on_implementation_unload(my_plugin.as_ref()); + super::MyPluginEnum::on_implementation_unload(my_plugin.as_ref()); + } + + fn complete_type_info( + &self, + type_: glib::Type, + ) -> (glib::TypeInfo, glib::TypeValueTable) { + let enum_type_values = match type_ { + type_ if type_ == super::MyPluginEnum::static_type() => { + self.my_enum_type_values.get() + } + type_ if type_ == super::MyPluginEnumLazy::static_type() => { + self.my_enum_lazy_type_values.get() + } + _ => panic!("unexpected type"), + } + .expect("enum type values"); + let type_info = EnumClass::type_info(type_, enum_type_values) + .expect("EnumClass::type_info failed"); + (type_info, glib::TypeValueTable::default()) + } + + fn complete_interface_info( + &self, + _instance_type: glib::Type, + _interface_type: glib::Type, + ) -> glib::InterfaceInfo { + unimplemented!() + } + } + + impl TypePluginRegisterImpl for MyPlugin { + fn add_dynamic_interface( + &self, + _instance_type: glib::Type, + _interface_type: glib::Type, + _interface_info: &glib::InterfaceInfo, + ) { + unimplemented!() + } + + fn register_dynamic_enum( + &self, + type_name: &str, + const_static_values: &'static [glib::EnumValue], + ) -> glib::Type { + let type_ = glib::Type::from_name(type_name).unwrap_or_else(|| { + glib::Type::register_dynamic( + glib::Type::ENUM, + type_name, + self.obj().upcast_ref::(), + glib::TypeFlags::NONE, + ) + }); + if type_.is_valid() { + match type_name { + "MyPluginEnum" => self.my_enum_type_values.set(Some(const_static_values)), + "MyPluginEnumLazy" => { + self.my_enum_lazy_type_values.set(Some(const_static_values)) + } + _ => panic!("unexpected"), + }; + } + type_ + } + + fn register_dynamic_type( + &self, + _parent_type: glib::Type, + _type_name: &str, + _type_info: &glib::TypeInfo, + _flags: glib::TypeFlags, + ) -> glib::Type { + unimplemented!() + } + } + } + + // an enum to register as a dynamic type. + #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::DynamicEnum)] + #[repr(u32)] + #[enum_type(name = "MyPluginEnum", plugin_type = MyPlugin)] + pub enum MyPluginEnum { + #[enum_value(name = "Foo")] + Foo, + Bar, + } + + // an enum to lazy register as a dynamic type. + #[derive(Debug, Eq, PartialEq, Clone, Copy, glib::DynamicEnum)] + #[repr(u32)] + #[enum_type(name = "MyPluginEnumLazy", plugin_type = MyPlugin, lazy_registration = true)] + pub enum MyPluginEnumLazy { + #[enum_value(name = "Foo")] + Foo, + Bar, + } + + // a plugin (must implement `glib::TypePlugin`). + glib::wrapper! { + pub struct MyPlugin(ObjectSubclass) @implements glib::TypePlugin; + } + + #[test] + fn dynamic_types() { + // 1st: creates a single plugin to test with. + let plugin = glib::Object::new::(); + // 1st: uses it to test lifecycle of enums registered as dynamic types. + enum_lifecycle(&plugin); + // 2nd: uses it to test behavior of enums registered as dynamic types. + enum_behavior(&plugin); + } + + // tests lifecycle of enums registered as dynamic types within a plugin. + fn enum_lifecycle(plugin: &MyPlugin) { + use glib::prelude::*; + + // checks types of enums to register as dynamic types are invalid (plugin is not used yet). + assert!(!MyPluginEnum::static_type().is_valid()); + assert!(!MyPluginEnumLazy::static_type().is_valid()); + + // simulates the glib type system to use/unuse the plugin. + TypePluginExt::use_(plugin); + TypePluginExt::unuse(plugin); + + // checks types of enums registered as dynamic types are valid (plugin is unused). + assert!(MyPluginEnum::static_type().is_valid()); + // checks types of enums that are lazy registered as dynamic types are still invalid (plugin is unused). + assert!(!MyPluginEnumLazy::static_type().is_valid()); + + // simulates the glib type system to use the plugin. + TypePluginExt::use_(plugin); + + // checks types of enums registered as dynamic types are valid (plugin is used). + let enum_type = MyPluginEnum::static_type(); + assert!(enum_type.is_valid()); + let enum_lazy_type = MyPluginEnumLazy::static_type(); + assert!(enum_lazy_type.is_valid()); + + // checks plugin of enums registered as dynamic types is `MyPlugin`. + assert_eq!( + enum_type.plugin().as_ref(), + Some(plugin.upcast_ref::()) + ); + assert_eq!( + enum_lazy_type.plugin().as_ref(), + Some(plugin.upcast_ref::()) + ); + + // simulates the glib type system to unuse the plugin. + TypePluginExt::unuse(plugin); + + // checks types of enums registered as dynamic types are still valid. + assert!(MyPluginEnum::static_type().is_valid()); + assert!(MyPluginEnumLazy::static_type().is_valid()); + + // simulates the glib type system to reuse the plugin. + TypePluginExt::use_(plugin); + + // checks types of enums registered as dynamic types are still valid. + assert!(MyPluginEnum::static_type().is_valid()); + assert!(MyPluginEnumLazy::static_type().is_valid()); + + // simulates the glib type system to unuse the plugin. + TypePluginExt::unuse(plugin); + } + + // tests behavior of enums registered as dynamic types within a plugin. + fn enum_behavior(plugin: &MyPlugin) { + use glib::prelude::*; + use glib::translate::{FromGlib, IntoGlib}; + + // simulates the glib type system to use the plugin. + TypePluginExt::use_(plugin); + + assert_eq!(MyPluginEnum::Foo.into_glib(), 0); + assert_eq!(MyPluginEnum::Bar.into_glib(), 1); + + assert_eq!(unsafe { MyPluginEnum::from_glib(0) }, MyPluginEnum::Foo); + assert_eq!(unsafe { MyPluginEnum::from_glib(1) }, MyPluginEnum::Bar); + + let t = MyPluginEnum::static_type(); + assert!(t.is_a(glib::Type::ENUM)); + assert_eq!(t.name(), "MyPluginEnum"); + + let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); + let v = e.value(0).expect("EnumClass::get_value(0) failed"); + assert_eq!(v.name(), "Foo"); + assert_eq!(v.nick(), "foo"); + let v = e.value(1).expect("EnumClass::get_value(1) failed"); + assert_eq!(v.name(), "Bar"); + assert_eq!(v.nick(), "bar"); + assert_eq!(e.value(2), None); + + // within enums registered as dynamic types, values are usables only if + // at least one type class ref exists (see `glib::EnumClass`). + assert_eq!( + MyPluginEnum::Foo.to_value().get::(), + Ok(MyPluginEnum::Foo) + ); + assert_eq!( + MyPluginEnum::Bar.to_value().get::(), + Ok(MyPluginEnum::Bar) + ); + + assert_eq!(MyPluginEnumLazy::Foo.into_glib(), 0); + assert_eq!(MyPluginEnumLazy::Bar.into_glib(), 1); + + assert_eq!( + unsafe { MyPluginEnumLazy::from_glib(0) }, + MyPluginEnumLazy::Foo + ); + assert_eq!( + unsafe { MyPluginEnumLazy::from_glib(1) }, + MyPluginEnumLazy::Bar + ); + + let t = MyPluginEnumLazy::static_type(); + assert!(t.is_a(glib::Type::ENUM)); + assert_eq!(t.name(), "MyPluginEnumLazy"); + + let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); + let v = e.value(0).expect("EnumClass::get_value(0) failed"); + assert_eq!(v.name(), "Foo"); + assert_eq!(v.nick(), "foo"); + let v = e.value(1).expect("EnumClass::get_value(1) failed"); + assert_eq!(v.name(), "Bar"); + assert_eq!(v.nick(), "bar"); + assert_eq!(e.value(2), None); + + // within enums registered as dynamic types, values are usables only if + // at least one type class ref exists (see `glib::EnumClass`). + assert_eq!( + MyPluginEnumLazy::Foo.to_value().get::(), + Ok(MyPluginEnumLazy::Foo) + ); + assert_eq!( + MyPluginEnumLazy::Bar.to_value().get::(), + Ok(MyPluginEnumLazy::Bar) + ); + + // simulates the glib type system to unuse the plugin. + TypePluginExt::unuse(plugin); + } +} diff --git a/glib-macros/tests/dynamic_objects.rs b/glib-macros/tests/dynamic_objects.rs index cbc432240225..a6fd6d13567d 100644 --- a/glib-macros/tests/dynamic_objects.rs +++ b/glib-macros/tests/dynamic_objects.rs @@ -465,6 +465,14 @@ pub mod plugin { }; } + fn register_dynamic_enum( + &self, + _name: &str, + _const_static_values: &'static [glib::EnumValue], + ) -> glib::Type { + unimplemented!() + } + fn register_dynamic_type( &self, parent_type: glib::Type, diff --git a/glib/Gir_GObject.toml b/glib/Gir_GObject.toml index 7de9527f73a4..250209663f63 100644 --- a/glib/Gir_GObject.toml +++ b/glib/Gir_GObject.toml @@ -24,6 +24,7 @@ manual = [ "GLib.Quark", "GObject.Object", "GObject.Value", + "GObject.EnumValue", "GObject.TypeValueTable", "GObject.ParamFlags", "GObject.ParamSpec", diff --git a/glib/src/enums.rs b/glib/src/enums.rs index 2c59236eea62..efc9822a87f9 100644 --- a/glib/src/enums.rs +++ b/glib/src/enums.rs @@ -1,11 +1,11 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use std::{cmp, ffi::CStr, fmt, ptr}; +use std::{cmp, ffi::CStr, fmt, marker::PhantomData, ptr}; use crate::{ translate::*, value::{FromValue, ValueTypeChecker}, - HasParamSpec, ParamSpecEnum, ParamSpecFlags, StaticType, Type, Value, + HasParamSpec, ParamSpecEnum, ParamSpecFlags, StaticType, Type, TypeInfo, Value, }; #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] @@ -187,6 +187,33 @@ impl EnumClass { pub fn to_value_by_nick(&self, nick: &str) -> Option { self.value_by_nick(nick).map(|v| v.to_value(self)) } + + // rustdoc-stripper-ignore-next + /// Complete `TypeInfo` for an enum with values. + /// This is an associated function. A method would result in a stack overflow due to a recurvice call: + /// callers should first create an `EnumClass` instance by calling `EnumClass::with_type()` which indirectly + /// calls `TypePluginRegisterImpl::register_dynamic_enum()` and `TypePluginImpl::complete_type_info()` + /// and one of them should call `EnumClass::with_type()` before calling this method. + #[doc(alias = "g_enum_complete_type_info")] + pub fn type_info(type_: Type, const_static_values: &'static [EnumValue]) -> Option { + unsafe { + let is_enum: bool = from_glib(gobject_ffi::g_type_is_a( + type_.into_glib(), + gobject_ffi::G_TYPE_ENUM, + )); + if !is_enum { + return None; + } + + let info = TypeInfo::default(); + gobject_ffi::g_enum_complete_type_info( + type_.into_glib(), + info.as_ptr(), + const_static_values.to_glib_none().0, + ); + Some(info) + } + } } impl Drop for EnumClass { @@ -227,6 +254,12 @@ impl fmt::Debug for EnumValue { } impl EnumValue { + // rustdoc-stripper-ignore-next + /// Creates a new `EnumValue` containing `value`. + pub const fn new(value: gobject_ffi::GEnumValue) -> Self { + Self(value) + } + // rustdoc-stripper-ignore-next /// Get integer value corresponding to the value. #[doc(alias = "get_value")] @@ -300,6 +333,37 @@ unsafe impl<'a, 'b> FromValue<'a> for &'b EnumValue { } } +#[doc(hidden)] +impl<'a> ToGlibPtr<'a, *const gobject_ffi::GEnumValue> for EnumValue { + type Storage = PhantomData<&'a Self>; + + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *const gobject_ffi::GEnumValue, Self> { + Stash(&self.0 as *const gobject_ffi::GEnumValue, PhantomData) + } +} + +impl<'a> ToGlibContainerFromSlice<'a, *const gobject_ffi::GEnumValue> for EnumValue { + type Storage = PhantomData<&'a Self>; + + fn to_glib_none_from_slice(t: &'a [Self]) -> (*const gobject_ffi::GEnumValue, Self::Storage) { + ( + t.as_ptr() as *const gobject_ffi::GEnumValue, + std::marker::PhantomData, + ) + } + + fn to_glib_container_from_slice( + _: &'a [Self], + ) -> (*const gobject_ffi::GEnumValue, Self::Storage) { + unimplemented!(); + } + + fn to_glib_full_from_slice(_: &[Self]) -> *const gobject_ffi::GEnumValue { + unimplemented!(); + } +} + pub struct EnumTypeChecker(); unsafe impl ValueTypeChecker for EnumTypeChecker { type Error = InvalidEnumError; diff --git a/glib/src/gobject/dynamic_object.rs b/glib/src/gobject/dynamic_object.rs index e8f3cc4f06c6..fb044ffaaa7c 100644 --- a/glib/src/gobject/dynamic_object.rs +++ b/glib/src/gobject/dynamic_object.rs @@ -1,8 +1,8 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::{ - prelude::*, subclass::prelude::*, InterfaceInfo, IsA, TypeFlags, TypeInfo, TypeModule, - TypePlugin, + prelude::*, subclass::prelude::*, EnumValue, InterfaceInfo, IsA, TypeFlags, TypeInfo, + TypeModule, TypePlugin, }; mod sealed { @@ -18,6 +18,12 @@ pub trait DynamicObjectRegisterExt: AsRef + sealed::Sealed + 'static interface_info: &InterfaceInfo, ); + fn register_dynamic_enum( + &self, + name: &str, + const_static_values: &'static [EnumValue], + ) -> crate::types::Type; + fn register_dynamic_type( &self, parent_type: crate::types::Type, @@ -41,6 +47,14 @@ where .add_dynamic_interface(instance_type, interface_type, interface_info); } + fn register_dynamic_enum( + &self, + name: &str, + const_static_values: &'static [EnumValue], + ) -> crate::types::Type { + self.imp().register_dynamic_enum(name, const_static_values) + } + fn register_dynamic_type( &self, parent_type: crate::types::Type, @@ -63,6 +77,14 @@ impl DynamicObjectRegisterExt for TypeModule { ::add_interface(self, instance_type, interface_type, interface_info); } + fn register_dynamic_enum( + &self, + name: &str, + const_static_values: &'static [EnumValue], + ) -> crate::types::Type { + ::register_enum(self, name, const_static_values) + } + fn register_dynamic_type( &self, parent_type: crate::types::Type, diff --git a/glib/src/gobject/type_module.rs b/glib/src/gobject/type_module.rs index 6181d33137bb..8a1ec36a24ac 100644 --- a/glib/src/gobject/type_module.rs +++ b/glib/src/gobject/type_module.rs @@ -1,6 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use crate::{prelude::*, translate::*, InterfaceInfo, TypeFlags, TypeInfo, TypePlugin}; +use crate::{prelude::*, translate::*, EnumValue, InterfaceInfo, TypeFlags, TypeInfo, TypePlugin}; crate::wrapper! { #[doc(alias = "GTypeModule")] @@ -38,6 +38,21 @@ pub trait TypeModuleExt: IsA + sealed::Sealed + 'static { } } + #[doc(alias = "g_type_module_register_enum")] + fn register_enum( + &self, + name: &str, + const_static_values: &'static [EnumValue], + ) -> crate::types::Type { + unsafe { + from_glib(gobject_ffi::g_type_module_register_enum( + self.as_ref().to_glib_none().0, + name.to_glib_none().0, + const_static_values.to_glib_none().0, + )) + } + } + #[doc(alias = "g_type_module_register_type")] fn register_type( &self, diff --git a/glib/src/lib.rs b/glib/src/lib.rs index a484a0690df0..d1aa8e12df06 100644 --- a/glib/src/lib.rs +++ b/glib/src/lib.rs @@ -10,8 +10,8 @@ pub use ffi; pub use glib_macros::cstr_bytes; pub use glib_macros::{ clone, closure, closure_local, derived_properties, dynamic_object_interface, - dynamic_object_subclass, flags, object_interface, object_subclass, Boxed, Downgrade, Enum, - ErrorDomain, Properties, SharedBoxed, ValueDelegate, Variant, + dynamic_object_subclass, flags, object_interface, object_subclass, Boxed, Downgrade, + DynamicEnum, Enum, ErrorDomain, Properties, SharedBoxed, ValueDelegate, Variant, }; pub use gobject_ffi; pub use once_cell; diff --git a/glib/src/subclass/mod.rs b/glib/src/subclass/mod.rs index 271a4a98c81e..b7eb8adc8d5a 100644 --- a/glib/src/subclass/mod.rs +++ b/glib/src/subclass/mod.rs @@ -347,6 +347,10 @@ //! unimplemented!() //! } //! +//! fn register_dynamic_enum(&self, _: &str, _: &'static [glib::EnumValue]) -> glib::Type { +//! unimplemented!() +//! } +//! //! fn register_dynamic_type(&self, parent_type: glib::Type, type_name: &str, type_info: &glib::TypeInfo, flags: glib::TypeFlags) -> glib::Type { //! let type_ = glib::Type::from_name(type_name).unwrap_or_else(|| { //! glib::Type::register_dynamic(parent_type, type_name, self.obj().upcast_ref::(), flags) diff --git a/glib/src/subclass/type_plugin.rs b/glib/src/subclass/type_plugin.rs index cda3581bb97e..a43a544b0fef 100644 --- a/glib/src/subclass/type_plugin.rs +++ b/glib/src/subclass/type_plugin.rs @@ -2,11 +2,11 @@ use crate::translate::IntoGlib; use crate::translate::{FromGlib, ToGlibPtr}; -use crate::TypeFlags; use crate::{ subclass::prelude::*, Cast, Interface, InterfaceInfo, Type, TypeInfo, TypePlugin, TypeValueTable, }; +use crate::{EnumValue, TypeFlags}; pub trait TypePluginImpl: ObjectImpl + TypePluginImplExt { fn use_plugin(&self) { @@ -184,7 +184,11 @@ pub trait TypePluginRegisterImpl: ObjectImpl + TypePluginImpl { _interface_type: Type, _interface_info: &InterfaceInfo, ); - + fn register_dynamic_enum( + &self, + _name: &str, + _const_static_values: &'static [EnumValue], + ) -> Type; fn register_dynamic_type( &self, _parent_type: Type, @@ -245,6 +249,14 @@ mod tests { unimplemented!() } + fn register_dynamic_enum( + &self, + _name: &str, + _const_static_values: &'static [EnumValue], + ) -> Type { + unimplemented!() + } + fn register_dynamic_type( &self, parent_type: Type, From fabb3abb5ee5acceae9e268111b6422319db063f Mon Sep 17 00:00:00 2001 From: fbrouille Date: Sun, 3 Dec 2023 22:03:37 +0100 Subject: [PATCH 02/15] revert gen_enum_values() and transform GEnumValue to EnumValue in dynamic_enum macro Signed-off-by: fbrouille --- glib-macros/src/enum_derive.rs | 61 +++++++++++++++------------------- 1 file changed, 27 insertions(+), 34 deletions(-) diff --git a/glib-macros/src/enum_derive.rs b/glib-macros/src/enum_derive.rs index 1d1b3ffa98c7..c29bfdb21fb6 100644 --- a/glib-macros/src/enum_derive.rs +++ b/glib-macros/src/enum_derive.rs @@ -4,24 +4,20 @@ use heck::{ToKebabCase, ToUpperCamelCase}; use proc_macro2::TokenStream; use proc_macro_error::abort_call_site; use quote::{quote, quote_spanned, ToTokens}; -use syn::{punctuated::Punctuated, spanned::Spanned, token::Comma, Data, Ident, Variant}; +use syn::{ + parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data, ExprArray, Ident, + Variant, +}; use crate::utils::{crate_ident_new, gen_enum_from_glib, parse_nested_meta_items, NestedMetaItem}; -// If `wrap` is `false`, generates glib::gobject_ffi::GEnumValue structs mapping the enum such as: +// generates glib::gobject_ffi::GEnumValue structs mapping the enum such as: // glib::gobject_ffi::GEnumValue { // value: Animal::Goat as i32, // value_name: "Goat\0" as *const _ as *const _, // value_nick: "goat\0" as *const _ as *const _, // }, -// If `wrap` is `true`, generates glib::EnumValue structs mapping the enum such as: -// glib::EnumValue::new(glib::gobject_ffi::GEnumValue { -// value: Animal::Goat as i32, -// value_name: "Goat\0" as *const _ as *const _, -// value_nick: "goat\0" as *const _ as *const _, -// }), fn gen_enum_values( - wrap: bool, enum_name: &Ident, enum_variants: &Punctuated, ) -> (TokenStream, usize) { @@ -50,24 +46,13 @@ fn gen_enum_values( let value_nick = format!("{value_nick}\0"); n += 1; - if wrap { - // generates a glib::EnumValue. - quote_spanned! {v.span()=> - #crate_ident::EnumValue::new(#crate_ident::gobject_ffi::GEnumValue { - value: #enum_name::#name as i32, - value_name: #value_name as *const _ as *const _, - value_nick: #value_nick as *const _ as *const _, - }), - } - } else { - // generates a glib::gobject_ffi::GEnumValue. - quote_spanned! {v.span()=> - #crate_ident::gobject_ffi::GEnumValue { - value: #enum_name::#name as i32, - value_name: #value_name as *const _ as *const _, - value_nick: #value_nick as *const _ as *const _, - }, - } + // generates a glib::gobject_ffi::GEnumValue. + quote_spanned! {v.span()=> + #crate_ident::gobject_ffi::GEnumValue { + value: #enum_name::#name as i32, + value_name: #value_name as *const _ as *const _, + value_nick: #value_nick as *const _ as *const _, + }, } }); ( @@ -100,7 +85,7 @@ pub fn impl_enum(input: &syn::DeriveInput) -> TokenStream { }; let gtype_name = gtype_name.value.unwrap(); let from_glib = gen_enum_from_glib(name, enum_variants); - let (enum_values, nb_enum_values) = gen_enum_values(false, name, enum_variants); + let (enum_values, nb_enum_values) = gen_enum_values(name, enum_variants); let crate_ident = crate_ident_new(); @@ -179,11 +164,19 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { let lazy_registration = lazy_registration.value.map(|b| b.value).unwrap_or_default(); let from_glib = gen_enum_from_glib(name, enum_variants); - let (enum_values, nb_enum_values) = gen_enum_values(true, name, enum_variants); + let (g_enum_values, nb_enum_values) = gen_enum_values(name, enum_variants); + + // Wrap each GEnumValue to EnumValue + let g_enum_values_expr: ExprArray = parse_quote! { [#g_enum_values] }; + let enum_values_iter = g_enum_values_expr.elems.iter().map(|v| { + quote_spanned! {v.span()=> + #crate_ident::EnumValue::new(#v), + } + }); - let enum_values_expr = quote! { + let enum_values = quote! { [#crate_ident::EnumValue; #nb_enum_values] = [ - #enum_values + #(#enum_values_iter)* #crate_ident::EnumValue::new(#crate_ident::gobject_ffi::GEnumValue { value: 0, value_name: ::std::ptr::null(), @@ -219,7 +212,7 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { None => #crate_ident::Type::INVALID, // plugin has been used and the enum has not been registered yet, so registers it as a dynamic type. Some((type_plugin, type_)) if !type_.is_valid() => { - static mut VALUES: #enum_values_expr; + static mut VALUES: #enum_values; *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin.upgrade().unwrap().as_ref(), #gtype_name, unsafe { &VALUES } ); *type_ }, @@ -244,7 +237,7 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { }, // plugin has been used at least one time and the enum has been registered as a dynamic type at least one time, so re-registers it. Some((_, type_)) if type_.is_valid() => { - static mut VALUES: #enum_values_expr; + static mut VALUES: #enum_values; *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, unsafe { &VALUES } ); type_.is_valid() }, @@ -298,7 +291,7 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { let type_mut = Self::get_type_mut(); - static mut VALUES: #enum_values_expr; + static mut VALUES: #enum_values; *type_mut = #crate_ident::translate::IntoGlib::into_glib(<#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, unsafe { &VALUES } )); *type_mut != #crate_ident::gobject_ffi::G_TYPE_INVALID } From 010ef551e23a67bb6940d7ae96de85566d55d197 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Tue, 5 Dec 2023 21:51:30 +0100 Subject: [PATCH 03/15] fix concurrency issues in dynamic_enum macro Signed-off-by: fbrouille --- glib-macros/src/enum_derive.rs | 55 +++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 24 deletions(-) diff --git a/glib-macros/src/enum_derive.rs b/glib-macros/src/enum_derive.rs index c29bfdb21fb6..3e7e8332dd12 100644 --- a/glib-macros/src/enum_derive.rs +++ b/glib-macros/src/enum_derive.rs @@ -192,13 +192,16 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { // a weak reference on the plugin is stored and will be used later on the first use of the enum. // this implementation relies on a static storage of a weak reference on the plugin and of the glib type to know if the enum has been registered. quote! { + struct RegistrationStatus(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type); + unsafe impl Send for RegistrationStatus {} + impl #name { /// Returns a mutable reference to the registration status: a tuple of the weak reference on the plugin and of the glib type. /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the data. #[inline] - fn get_registration_status_ref_mut() -> &'static mut Option<(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type)> { - static mut REGISTRATION_STATUS: ::std::sync::Mutex::Weak, #crate_ident::Type)>> = ::std::sync::Mutex::new(None); - unsafe { REGISTRATION_STATUS.get_mut().unwrap() } + fn get_registration_status_ref() -> &'static ::std::sync::Mutex> { + static REGISTRATION_STATUS: ::std::sync::Mutex> = ::std::sync::Mutex::new(None); + ®ISTRATION_STATUS } /// Registers the enum as a dynamic type within the plugin only once. @@ -206,18 +209,19 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { /// Do nothing if plugin has never been used or if the enum is already registered as a dynamic type. #[inline] fn register_enum() -> #crate_ident::Type { - let registration_status_ref_mut = Self::get_registration_status_ref_mut(); - match registration_status_ref_mut { + let registration_status_ref = Self::get_registration_status_ref(); + let mut registration_status = registration_status_ref.lock().unwrap(); + match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used, so the enum cannot be registered as a dynamic type. None => #crate_ident::Type::INVALID, // plugin has been used and the enum has not been registered yet, so registers it as a dynamic type. - Some((type_plugin, type_)) if !type_.is_valid() => { + Some(RegistrationStatus(type_plugin, type_)) if !type_.is_valid() => { static mut VALUES: #enum_values; *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin.upgrade().unwrap().as_ref(), #gtype_name, unsafe { &VALUES } ); *type_ }, // plugin has been used and the enum has already been registered as a dynamic type. - Some((_, type_)) => *type_ + Some(RegistrationStatus(_, type_)) => *type_ } } @@ -228,15 +232,16 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { /// If plugin is reused (and has reloaded the implementation) and the enum has not been registered yet as a dynamic type, do nothing. #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { - let registration_status_ref_mut = Self::get_registration_status_ref_mut(); - match registration_status_ref_mut { + let registration_status_ref = Self::get_registration_status_ref(); + let mut registration_status = registration_status_ref.lock().unwrap(); + match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used (this is the first time), so postpones registration of the enum as a dynamic type on the first use. None => { - *registration_status_ref_mut = Some((#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); + *registration_status = Some(RegistrationStatus(#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); true }, // plugin has been used at least one time and the enum has been registered as a dynamic type at least one time, so re-registers it. - Some((_, type_)) if type_.is_valid() => { + Some(RegistrationStatus(_, type_)) if type_.is_valid() => { static mut VALUES: #enum_values; *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, unsafe { &VALUES } ); type_.is_valid() @@ -253,15 +258,16 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { /// Else do nothing. #[inline] pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { - let registration_status_ref_mut = Self::get_registration_status_ref_mut(); - match registration_status_ref_mut { + let registration_status_ref = Self::get_registration_status_ref(); + let mut registration_status = registration_status_ref.lock().unwrap(); + match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used, so unload implementation is unexpected. None => false, // plugin has been used at least one time and the enum has been registered as a dynamic type at least one time. - Some((_, type_)) if type_.is_valid() => true, + Some(RegistrationStatus(_, type_)) if type_.is_valid() => true, // plugin has been used at least one time but the enum has not been registered yet as a dynamic type, so cancels the postponed registration. Some(_) => { - *registration_status_ref_mut = None; + *registration_status = None; true } } @@ -272,28 +278,29 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { // registers immediately the enum as a dynamic type. quote! { impl #name { - /// Returns a mutable reference to the glib type. - /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the atomic data. + /// Returns a reference to the glib type which can be safely shared between threads. #[inline] - fn get_type_mut() -> &'static mut #crate_ident::ffi::GType { - static mut TYPE: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); - unsafe { TYPE.get_mut() } + fn get_gtype_ref() -> &'static ::std::sync::atomic::AtomicUsize { + static TYPE: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); + &TYPE } /// Do nothing as the enum has been registered on implementation load. #[inline] fn register_enum() -> #crate_ident::Type { - unsafe { <#crate_ident::Type as #crate_ident::translate::FromGlib<#crate_ident::ffi::GType>>::from_glib(*Self::get_type_mut()) } + let gtype = Self::get_gtype_ref().load(::std::sync::atomic::Ordering::Relaxed); + unsafe { <#crate_ident::Type as #crate_ident::translate::FromGlib<#crate_ident::ffi::GType>>::from_glib(gtype) } } /// Registers the enum as a dynamic type within the plugin. /// The enum can be registered several times as a dynamic type. #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { - let type_mut = Self::get_type_mut(); + let gtype_ref = Self::get_gtype_ref(); static mut VALUES: #enum_values; - *type_mut = #crate_ident::translate::IntoGlib::into_glib(<#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, unsafe { &VALUES } )); - *type_mut != #crate_ident::gobject_ffi::G_TYPE_INVALID + let gtype = #crate_ident::translate::IntoGlib::into_glib(<#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, unsafe { &VALUES } )); + gtype_ref.store(gtype, ::std::sync::atomic::Ordering::Relaxed); + gtype != #crate_ident::gobject_ffi::G_TYPE_INVALID } /// Do nothing as enums registered as dynamic types are never unregistered. From 72d9b06d620b9ef7701bb1d121e75115878da795 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Tue, 5 Dec 2023 22:36:58 +0100 Subject: [PATCH 04/15] replace 'rust' by 'Rust' and 'glib' by 'GLib' Signed-off-by: fbrouille --- glib-macros/src/enum_derive.rs | 6 ++-- glib-macros/src/lib.rs | 4 +-- glib-macros/src/object_interface_attribute.rs | 6 ++-- glib-macros/src/object_subclass_attribute.rs | 4 +-- glib-macros/tests/dynamic_enums.rs | 32 +++++++++---------- glib-macros/tests/dynamic_objects.rs | 24 +++++++------- glib/src/subclass/mod.rs | 4 +-- glib/src/subclass/type_module.rs | 2 +- glib/src/subclass/type_plugin.rs | 2 +- 9 files changed, 42 insertions(+), 42 deletions(-) diff --git a/glib-macros/src/enum_derive.rs b/glib-macros/src/enum_derive.rs index 3e7e8332dd12..b577d6c6f16e 100644 --- a/glib-macros/src/enum_derive.rs +++ b/glib-macros/src/enum_derive.rs @@ -190,13 +190,13 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { let register_enum_impl = if lazy_registration { // registers the enum as a dynamic type on the first use (lazy registration). // a weak reference on the plugin is stored and will be used later on the first use of the enum. - // this implementation relies on a static storage of a weak reference on the plugin and of the glib type to know if the enum has been registered. + // this implementation relies on a static storage of a weak reference on the plugin and of the GLib type to know if the enum has been registered. quote! { struct RegistrationStatus(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type); unsafe impl Send for RegistrationStatus {} impl #name { - /// Returns a mutable reference to the registration status: a tuple of the weak reference on the plugin and of the glib type. + /// Returns a mutable reference to the registration status: a tuple of the weak reference on the plugin and of the GLib type. /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the data. #[inline] fn get_registration_status_ref() -> &'static ::std::sync::Mutex> { @@ -278,7 +278,7 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { // registers immediately the enum as a dynamic type. quote! { impl #name { - /// Returns a reference to the glib type which can be safely shared between threads. + /// Returns a reference to the GLib type which can be safely shared between threads. #[inline] fn get_gtype_ref() -> &'static ::std::sync::atomic::AtomicUsize { static TYPE: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); diff --git a/glib-macros/src/lib.rs b/glib-macros/src/lib.rs index 9feb068c134c..fdb02d3d3d96 100644 --- a/glib-macros/src/lib.rs +++ b/glib-macros/src/lib.rs @@ -439,7 +439,7 @@ pub fn closure_local(item: TokenStream) -> TokenStream { closure::closure_inner(item, "new_local") } -/// Derive macro for register a rust enum in the glib type system and derive the +/// Derive macro for register a Rust enum in the GLib type system and derive the /// the [`glib::Value`] traits. /// /// # Example @@ -468,7 +468,7 @@ pub fn enum_derive(input: TokenStream) -> TokenStream { gen.into() } -/// Derive macro for register a rust enum in the glib type system as a dynamic +/// Derive macro for register a Rust enum in the GLib type system as a dynamic /// type and derive the [`glib::Value`] traits. /// /// An enum must be explicitly registered as a dynamic type when the system diff --git a/glib-macros/src/object_interface_attribute.rs b/glib-macros/src/object_interface_attribute.rs index 7048d1031ac5..d18659ae5ce3 100644 --- a/glib-macros/src/object_interface_attribute.rs +++ b/glib-macros/src/object_interface_attribute.rs @@ -91,10 +91,10 @@ pub fn impl_dynamic_object_interface( let register_interface = if lazy_registration { // registers the object interface as a dynamic type on the first use (lazy registration). // a weak reference on the plugin is stored and will be used later on the first use of the object interface. - // 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. + // 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. quote! { impl #self_ty { - /// Returns a mutable reference to the registration status: a tuple of the weak reference on the plugin and of the glib type. + /// Returns a mutable reference to the registration status: a tuple of the weak reference on the plugin and of the GLib type. /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the data. #[inline] fn get_registration_status_ref_mut() -> &'static mut Option<(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type)> { @@ -171,7 +171,7 @@ pub fn impl_dynamic_object_interface( // registers immediately the object interface as a dynamic type. quote! { impl #self_ty { - /// Returns a mutable reference to the glib type. + /// Returns a mutable reference to the GLib type. /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the atomic data. #[inline] fn get_type_mut() -> &'static mut #crate_ident::ffi::GType { diff --git a/glib-macros/src/object_subclass_attribute.rs b/glib-macros/src/object_subclass_attribute.rs index a516baf25620..2331570e13a7 100644 --- a/glib-macros/src/object_subclass_attribute.rs +++ b/glib-macros/src/object_subclass_attribute.rs @@ -86,10 +86,10 @@ pub fn impl_dynamic_object_subclass( let register_type = if lazy_registration { // registers the object subclass as a dynamic type on the first use (lazy registration). // a weak reference on the plugin is stored and will be used later on the first use of the object subclass. - // this implementation relies on a static storage of a weak reference on the plugin and of the glib type to know if the object subclass has been registered. + // this implementation relies on a static storage of a weak reference on the plugin and of the GLib type to know if the object subclass has been registered. quote! { impl #self_ty { - /// Returns a mutable reference to the registration status: a tuple of the weak reference on the plugin and of the glib type. + /// Returns a mutable reference to the registration status: a tuple of the weak reference on the plugin and of the GLib type. /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the data. #[inline] fn get_registration_status_ref_mut() -> &'static mut Option<(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type)> { diff --git a/glib-macros/tests/dynamic_enums.rs b/glib-macros/tests/dynamic_enums.rs index e9f8545c85d5..7f857a71baae 100644 --- a/glib-macros/tests/dynamic_enums.rs +++ b/glib-macros/tests/dynamic_enums.rs @@ -85,7 +85,7 @@ mod module { assert!(!MyModuleEnum::static_type().is_valid()); assert!(!MyModuleEnumLazy::static_type().is_valid()); - // simulates the glib type system to load/unload the module. + // simulates the GLib type system to load/unload the module. TypeModuleExt::use_(module); TypeModuleExt::unuse(module); @@ -94,7 +94,7 @@ mod module { // checks types of enums that are lazy registered as dynamic types are valid (module is unloaded). assert!(!MyModuleEnumLazy::static_type().is_valid()); - // simulates the glib type system to load the module. + // simulates the GLib type system to load the module. TypeModuleExt::use_(module); // checks types of enums registered as dynamic types are valid (module is loaded). @@ -113,21 +113,21 @@ mod module { Some(module.upcast_ref::()) ); - // simulates the glib type system to unload the module. + // simulates the GLib type system to unload the module. TypeModuleExt::unuse(module); - // checks types of enums registered as dynamic types are still valid (should have been marked as unloaded by the glib type system but this cannot be checked). + // checks types of enums registered as dynamic types are still valid (should have been marked as unloaded by the GLib type system but this cannot be checked). assert!(MyModuleEnum::static_type().is_valid()); assert!(MyModuleEnumLazy::static_type().is_valid()); - // simulates the glib type system to reload the module. + // simulates the GLib type system to reload the module. TypeModuleExt::use_(module); - // checks types of enums registered as dynamic types are still valid (should have been marked as loaded by the glib type system but this cannot be checked). + // checks types of enums registered as dynamic types are still valid (should have been marked as loaded by the GLib type system but this cannot be checked). assert!(MyModuleEnum::static_type().is_valid()); assert!(MyModuleEnumLazy::static_type().is_valid()); - // simulates the glib type system to unload the module. + // simulates the GLib type system to unload the module. TypeModuleExt::unuse(module); } @@ -136,7 +136,7 @@ mod module { use glib::prelude::*; use glib::translate::{FromGlib, IntoGlib}; - // simulates the glib type system to load the module. + // simulates the GLib type system to load the module. TypeModuleExt::use_(module); assert_eq!(MyModuleEnum::Foo.into_glib(), 0); @@ -205,7 +205,7 @@ mod module { Ok(MyModuleEnumLazy::Bar) ); - // simulates the glib type system to unload the module. + // simulates the GLib type system to unload the module. TypeModuleExt::unuse(module); } } @@ -368,7 +368,7 @@ pub mod plugin { assert!(!MyPluginEnum::static_type().is_valid()); assert!(!MyPluginEnumLazy::static_type().is_valid()); - // simulates the glib type system to use/unuse the plugin. + // simulates the GLib type system to use/unuse the plugin. TypePluginExt::use_(plugin); TypePluginExt::unuse(plugin); @@ -377,7 +377,7 @@ pub mod plugin { // checks types of enums that are lazy registered as dynamic types are still invalid (plugin is unused). assert!(!MyPluginEnumLazy::static_type().is_valid()); - // simulates the glib type system to use the plugin. + // simulates the GLib type system to use the plugin. TypePluginExt::use_(plugin); // checks types of enums registered as dynamic types are valid (plugin is used). @@ -396,21 +396,21 @@ pub mod plugin { Some(plugin.upcast_ref::()) ); - // simulates the glib type system to unuse the plugin. + // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(plugin); // checks types of enums registered as dynamic types are still valid. assert!(MyPluginEnum::static_type().is_valid()); assert!(MyPluginEnumLazy::static_type().is_valid()); - // simulates the glib type system to reuse the plugin. + // simulates the GLib type system to reuse the plugin. TypePluginExt::use_(plugin); // checks types of enums registered as dynamic types are still valid. assert!(MyPluginEnum::static_type().is_valid()); assert!(MyPluginEnumLazy::static_type().is_valid()); - // simulates the glib type system to unuse the plugin. + // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(plugin); } @@ -419,7 +419,7 @@ pub mod plugin { use glib::prelude::*; use glib::translate::{FromGlib, IntoGlib}; - // simulates the glib type system to use the plugin. + // simulates the GLib type system to use the plugin. TypePluginExt::use_(plugin); assert_eq!(MyPluginEnum::Foo.into_glib(), 0); @@ -488,7 +488,7 @@ pub mod plugin { Ok(MyPluginEnumLazy::Bar) ); - // simulates the glib type system to unuse the plugin. + // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(plugin); } } diff --git a/glib-macros/tests/dynamic_objects.rs b/glib-macros/tests/dynamic_objects.rs index a6fd6d13567d..5e9c429e41fa 100644 --- a/glib-macros/tests/dynamic_objects.rs +++ b/glib-macros/tests/dynamic_objects.rs @@ -215,7 +215,7 @@ mod module { assert!(!imp::MyModuleInterfaceLazy::type_().is_valid()); assert!(!imp::MyModuleTypeLazy::type_().is_valid()); - // simulates the glib type system to load/unload the module. + // simulates the GLib type system to load/unload the module. let module = glib::Object::new::(); TypeModuleExt::use_(&module); TypeModuleExt::unuse(&module); @@ -227,7 +227,7 @@ mod module { assert!(!imp::MyModuleInterfaceLazy::type_().is_valid()); assert!(!imp::MyModuleTypeLazy::type_().is_valid()); - // simulates the glib type system to load the module. + // simulates the GLib type system to load the module. TypeModuleExt::use_(&module); // checks types of object subclasses and of object interfaces registered as dynamic types are valid (module is loaded). @@ -258,25 +258,25 @@ mod module { Some(module.upcast_ref::()) ); - // simulates the glib type system to unload the module. + // simulates the GLib type system to unload the module. TypeModuleExt::unuse(&module); - // checks types of object subclasses and of object interfaces registered as dynamic types are still valid (should have been marked as unloaded by the glib type system but this cannot be checked). + // checks types of object subclasses and of object interfaces registered as dynamic types are still valid (should have been marked as unloaded by the GLib type system but this cannot be checked). assert!(imp::MyModuleInterface::type_().is_valid()); assert!(imp::MyModuleType::type_().is_valid()); assert!(imp::MyModuleInterfaceLazy::type_().is_valid()); assert!(imp::MyModuleTypeLazy::type_().is_valid()); - // simulates the glib type system to reload the module. + // simulates the GLib type system to reload the module. TypeModuleExt::use_(&module); - // checks types of object subclasses and of object interfaces registered as dynamic types are still valid (should have been marked as unloaded by the glib type system but this cannot be checked). + // checks types of object subclasses and of object interfaces registered as dynamic types are still valid (should have been marked as unloaded by the GLib type system but this cannot be checked). assert!(imp::MyModuleInterface::type_().is_valid()); assert!(imp::MyModuleType::type_().is_valid()); assert!(imp::MyModuleInterfaceLazy::type_().is_valid()); assert!(imp::MyModuleTypeLazy::type_().is_valid()); - // simulates the glib type system to unload the module. + // simulates the GLib type system to unload the module. TypeModuleExt::unuse(&module); } } @@ -547,7 +547,7 @@ pub mod plugin { assert!(!imp::MyPluginInterfaceLazy::type_().is_valid()); assert!(!imp::MyPluginTypeLazy::type_().is_valid()); - // simulates the glib type system to use/unuse the plugin. + // simulates the GLib type system to use/unuse the plugin. let plugin = glib::Object::new::(); TypePluginExt::use_(&plugin); TypePluginExt::unuse(&plugin); @@ -559,7 +559,7 @@ pub mod plugin { assert!(!imp::MyPluginInterfaceLazy::type_().is_valid()); assert!(!imp::MyPluginTypeLazy::type_().is_valid()); - // simulates the glib type system to use the plugin. + // simulates the GLib type system to use the plugin. TypePluginExt::use_(&plugin); // checks types of object subclasses and of object interfaces registered as dynamic types are valid (plugin is used). @@ -590,7 +590,7 @@ pub mod plugin { Some(plugin.upcast_ref::()) ); - // simulates the glib type system to unuse the plugin. + // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(&plugin); // checks types of object subclasses and of object interfaces registered as dynamic types are still valid. @@ -599,7 +599,7 @@ pub mod plugin { assert!(imp::MyPluginInterfaceLazy::type_().is_valid()); assert!(imp::MyPluginTypeLazy::type_().is_valid()); - // simulates the glib type system to reuse the plugin. + // simulates the GLib type system to reuse the plugin. TypePluginExt::use_(&plugin); // checks types of object subclasses and of object interfaces registered as dynamic types are still valid. @@ -608,7 +608,7 @@ pub mod plugin { assert!(imp::MyPluginInterfaceLazy::type_().is_valid()); assert!(imp::MyPluginTypeLazy::type_().is_valid()); - // simulates the glib type system to unuse the plugin. + // simulates the GLib type system to unuse the plugin. TypePluginExt::unuse(&plugin); } } diff --git a/glib/src/subclass/mod.rs b/glib/src/subclass/mod.rs index b7eb8adc8d5a..a800a3ac7c0c 100644 --- a/glib/src/subclass/mod.rs +++ b/glib/src/subclass/mod.rs @@ -275,7 +275,7 @@ //! let simple_module_object_type = imp::SimpleModuleObject::type_(); //! assert!(!simple_module_object_type.is_valid()); //! -//! // simulates the glib type system to load the module. +//! // simulates the GLib type system to load the module. //! TypeModuleExt::use_(&simple_type_module); //! //! // at this step, SimpleModuleObject must have been registered. @@ -389,7 +389,7 @@ //! let simple_plugin_object_type = imp::SimplePluginObject::type_(); //! assert!(!simple_plugin_object_type.is_valid()); //! -//! // simulates the glib type system to use the plugin. +//! // simulates the GLib type system to use the plugin. //! TypePluginExt::use_(&simple_type_plugin); //! //! // at this step, SimplePluginObject must have been registered. diff --git a/glib/src/subclass/type_module.rs b/glib/src/subclass/type_module.rs index bef50c6ad7a4..b8009758c716 100644 --- a/glib/src/subclass/type_module.rs +++ b/glib/src/subclass/type_module.rs @@ -156,7 +156,7 @@ mod tests { fn test_module() { assert!(!imp::SimpleModuleType::type_().is_valid()); let simple_module = glib::Object::new::(); - // simulates the glib type system to load the module. + // simulates the GLib type system to load the module. assert!(simple_module.use_()); assert!(imp::SimpleModuleType::type_().is_valid()); simple_module.unuse(); diff --git a/glib/src/subclass/type_plugin.rs b/glib/src/subclass/type_plugin.rs index a43a544b0fef..98b5d50742b0 100644 --- a/glib/src/subclass/type_plugin.rs +++ b/glib/src/subclass/type_plugin.rs @@ -305,7 +305,7 @@ mod tests { fn test_plugin() { assert!(!imp::SimplePluginType::type_().is_valid()); let simple_plugin = crate::Object::new::(); - // simulates the glib type system to use the plugin. + // simulates the GLib type system to use the plugin. TypePluginExt::use_(&simple_plugin); assert!(imp::SimplePluginType::type_().is_valid()); TypePluginExt::unuse(&simple_plugin); From 9e1a3b791053625f63f516b42fea9fa0ebe96f23 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Thu, 7 Dec 2023 19:26:37 +0100 Subject: [PATCH 05/15] review documentation of macro dynamic_enum_derive Signed-off-by: fbrouille --- glib-macros/src/lib.rs | 45 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/glib-macros/src/lib.rs b/glib-macros/src/lib.rs index fdb02d3d3d96..4f555a67fafd 100644 --- a/glib-macros/src/lib.rs +++ b/glib-macros/src/lib.rs @@ -513,14 +513,47 @@ pub fn enum_derive(input: TokenStream) -> TokenStream { /// } /// ``` /// -/// By default an enum is considered to be registered as a dynamic type within -/// a [`TypeModule`] subclass. Optionally setting the macro attribute -/// `plugin_type` allows to register an enum as a dynamic type within a given -/// [`TypePlugin`] subclass: +/// An enum is usually registered as a dynamic type within a [`TypeModule`] +/// subclass: /// ```ignore /// #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::DynamicEnum)] -/// #[enum_type(name = "MyEnum", plugin_type = MyPlugin)] -/// enum MyEnum { +/// #[enum_type(name = "MyModuleEnum")] +/// enum MyModuleEnum { +/// ... +/// } +/// ... +/// #[derive(Default)] +/// pub struct MyModule; +/// ... +/// impl TypeModuleImpl for MyModule { +/// fn load(&self) -> bool { +/// // registers enums as dynamic types. +/// let my_module = self.obj(); +/// let type_module: &glib::TypeModule = my_module.upcast_ref(); +/// MyModuleEnum::on_implementation_load(type_module) +/// } +/// ... +/// } +/// ``` +/// +/// Optionally setting the macro attribute `plugin_type` allows to register an +/// enum as a dynamic type within a given [`TypePlugin`] subclass: +/// ```ignore +/// #[derive(Debug, Copy, Clone, PartialEq, Eq, glib::DynamicEnum)] +/// #[enum_type(name = "MyPluginEnum", plugin_type = MyPlugin)] +/// enum MyPluginEnum { +/// ... +/// } +/// ... +/// #[derive(Default)] +/// pub struct MyPlugin; +/// ... +/// impl TypePluginImpl for MyPlugin { +/// fn use_plugin(&self) { +/// // register enums as dynamic types. +/// let my_plugin = self.obj(); +/// MyPluginEnum::on_implementation_load(my_plugin.as_ref()); +/// } /// ... /// } /// ``` From cd7ec879d3b83c1eb5ffba20cfe8dc1b1968f2a3 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Thu, 7 Dec 2023 20:30:09 +0100 Subject: [PATCH 06/15] fix IntoGlib/TryFromGlib for Enum (must fail if enum is not registered) Signed-off-by: fbrouille --- glib-macros/src/enum_derive.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/glib-macros/src/enum_derive.rs b/glib-macros/src/enum_derive.rs index b577d6c6f16e..838f076ad162 100644 --- a/glib-macros/src/enum_derive.rs +++ b/glib-macros/src/enum_derive.rs @@ -328,6 +328,7 @@ pub fn impl_enum_( #[inline] fn into_glib(self) -> i32 { + assert!(#name::static_type().is_valid()); self as i32 } } @@ -337,6 +338,7 @@ pub fn impl_enum_( #[inline] unsafe fn try_from_glib(value: i32) -> ::core::result::Result { + assert!(#name::static_type().is_valid()); let from_glib = || { #from_glib }; From 0effac68bdafef961c46ca3cdc9244442e42bd92 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Thu, 7 Dec 2023 21:18:45 +0100 Subject: [PATCH 07/15] rename EnumClass::type_info to EnumClass::complete_type_info Signed-off-by: fbrouille --- glib-macros/tests/dynamic_enums.rs | 4 ++-- glib/src/enums.rs | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/glib-macros/tests/dynamic_enums.rs b/glib-macros/tests/dynamic_enums.rs index 7f857a71baae..c2ac8d1fe5a1 100644 --- a/glib-macros/tests/dynamic_enums.rs +++ b/glib-macros/tests/dynamic_enums.rs @@ -264,8 +264,8 @@ pub mod plugin { _ => panic!("unexpected type"), } .expect("enum type values"); - let type_info = EnumClass::type_info(type_, enum_type_values) - .expect("EnumClass::type_info failed"); + let type_info = EnumClass::complete_type_info(type_, enum_type_values) + .expect("EnumClass::complete_type_info failed"); (type_info, glib::TypeValueTable::default()) } diff --git a/glib/src/enums.rs b/glib/src/enums.rs index efc9822a87f9..9279fb4b437c 100644 --- a/glib/src/enums.rs +++ b/glib/src/enums.rs @@ -195,7 +195,10 @@ impl EnumClass { /// calls `TypePluginRegisterImpl::register_dynamic_enum()` and `TypePluginImpl::complete_type_info()` /// and one of them should call `EnumClass::with_type()` before calling this method. #[doc(alias = "g_enum_complete_type_info")] - pub fn type_info(type_: Type, const_static_values: &'static [EnumValue]) -> Option { + pub fn complete_type_info( + type_: Type, + const_static_values: &'static [EnumValue], + ) -> Option { unsafe { let is_enum: bool = from_glib(gobject_ffi::g_type_is_a( type_.into_glib(), From 0c9be600e16e0571b2ca93e8d0174efe3c4c79c6 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Thu, 7 Dec 2023 22:29:57 +0100 Subject: [PATCH 08/15] replace EnumValue::new() by EnumValue::unsafe_from() Signed-off-by: fbrouille --- glib-macros/src/enum_derive.rs | 8 ++++---- glib/src/enums.rs | 15 ++++++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/glib-macros/src/enum_derive.rs b/glib-macros/src/enum_derive.rs index 838f076ad162..e629c62ecd95 100644 --- a/glib-macros/src/enum_derive.rs +++ b/glib-macros/src/enum_derive.rs @@ -170,19 +170,19 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { let g_enum_values_expr: ExprArray = parse_quote! { [#g_enum_values] }; let enum_values_iter = g_enum_values_expr.elems.iter().map(|v| { quote_spanned! {v.span()=> - #crate_ident::EnumValue::new(#v), + #crate_ident::EnumValue::unsafe_from(#v), } }); let enum_values = quote! { - [#crate_ident::EnumValue; #nb_enum_values] = [ + [#crate_ident::EnumValue; #nb_enum_values] = unsafe {[ #(#enum_values_iter)* - #crate_ident::EnumValue::new(#crate_ident::gobject_ffi::GEnumValue { + #crate_ident::EnumValue::unsafe_from(#crate_ident::gobject_ffi::GEnumValue { value: 0, value_name: ::std::ptr::null(), value_nick: ::std::ptr::null(), }), - ] + ]} }; // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). diff --git a/glib/src/enums.rs b/glib/src/enums.rs index 9279fb4b437c..1da0fbf6fefe 100644 --- a/glib/src/enums.rs +++ b/glib/src/enums.rs @@ -258,9 +258,12 @@ impl fmt::Debug for EnumValue { impl EnumValue { // rustdoc-stripper-ignore-next - /// Creates a new `EnumValue` containing `value`. - pub const fn new(value: gobject_ffi::GEnumValue) -> Self { - Self(value) + /// # Safety + /// + /// It is the responsibility of the caller to ensure `GEnumValue` is + /// valid. + pub const unsafe fn unsafe_from(g_value: gobject_ffi::GEnumValue) -> Self { + Self(g_value) } // rustdoc-stripper-ignore-next @@ -326,6 +329,12 @@ impl Ord for EnumValue { } } +impl UnsafeFrom for EnumValue { + unsafe fn unsafe_from(g_value: gobject_ffi::GEnumValue) -> Self { + Self::unsafe_from(g_value) + } +} + unsafe impl<'a, 'b> FromValue<'a> for &'b EnumValue { type Checker = EnumTypeChecker; From ccdb0b2296089a1177de5184f9c371dfdb778559 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Sun, 10 Dec 2023 16:41:52 +0100 Subject: [PATCH 09/15] Revert "fix IntoGlib/TryFromGlib for Enum (must fail if enum is not registered)" This reverts commit cd7ec879d3b83c1eb5ffba20cfe8dc1b1968f2a3. Signed-off-by: fbrouille --- glib-macros/src/enum_derive.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/glib-macros/src/enum_derive.rs b/glib-macros/src/enum_derive.rs index e629c62ecd95..b13ce382ec5f 100644 --- a/glib-macros/src/enum_derive.rs +++ b/glib-macros/src/enum_derive.rs @@ -328,7 +328,6 @@ pub fn impl_enum_( #[inline] fn into_glib(self) -> i32 { - assert!(#name::static_type().is_valid()); self as i32 } } @@ -338,7 +337,6 @@ pub fn impl_enum_( #[inline] unsafe fn try_from_glib(value: i32) -> ::core::result::Result { - assert!(#name::static_type().is_valid()); let from_glib = || { #from_glib }; From e3840791182aef714aa59036363b261301bc3e34 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Sun, 10 Dec 2023 18:34:54 +0100 Subject: [PATCH 10/15] check array of EnumValue is zero terminated Signed-off-by: fbrouille --- glib/src/enums.rs | 14 ++++++++++++++ glib/src/gobject/type_module.rs | 11 +++++++++++ 2 files changed, 25 insertions(+) diff --git a/glib/src/enums.rs b/glib/src/enums.rs index 1da0fbf6fefe..4e9395f36398 100644 --- a/glib/src/enums.rs +++ b/glib/src/enums.rs @@ -194,11 +194,25 @@ impl EnumClass { /// callers should first create an `EnumClass` instance by calling `EnumClass::with_type()` which indirectly /// calls `TypePluginRegisterImpl::register_dynamic_enum()` and `TypePluginImpl::complete_type_info()` /// and one of them should call `EnumClass::with_type()` before calling this method. + /// `const_static_values` is an array of `EnumValue` for the possible enumeration values. The array must be + /// static to ensure enumeration values are never dropped, and must be terminated by an `EnumValue` with + /// all members being 0, as expected by GLib. #[doc(alias = "g_enum_complete_type_info")] pub fn complete_type_info( type_: Type, const_static_values: &'static [EnumValue], ) -> Option { + assert!( + !const_static_values.is_empty() + && const_static_values[const_static_values.len() - 1] + == unsafe { + EnumValue::unsafe_from(gobject_ffi::GEnumValue { + value: 0, + value_name: ::std::ptr::null(), + value_nick: ::std::ptr::null(), + }) + } + ); unsafe { let is_enum: bool = from_glib(gobject_ffi::g_type_is_a( type_.into_glib(), diff --git a/glib/src/gobject/type_module.rs b/glib/src/gobject/type_module.rs index 8a1ec36a24ac..6bc2443b47d9 100644 --- a/glib/src/gobject/type_module.rs +++ b/glib/src/gobject/type_module.rs @@ -44,6 +44,17 @@ pub trait TypeModuleExt: IsA + sealed::Sealed + 'static { name: &str, const_static_values: &'static [EnumValue], ) -> crate::types::Type { + assert!( + !const_static_values.is_empty() + && const_static_values[const_static_values.len() - 1] + == unsafe { + EnumValue::unsafe_from(gobject_ffi::GEnumValue { + value: 0, + value_name: ::std::ptr::null(), + value_nick: ::std::ptr::null(), + }) + } + ); unsafe { from_glib(gobject_ffi::g_type_module_register_enum( self.as_ref().to_glib_none().0, From 5c5644baf732d203a61bee71ef42d91bf1ae1391 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Sun, 10 Dec 2023 18:51:22 +0100 Subject: [PATCH 11/15] add default implementation to trait TypePluginRegisterImpl Signed-off-by: fbrouille --- glib-macros/tests/dynamic_enums.rs | 27 -------------------------- glib-macros/tests/dynamic_objects.rs | 8 -------- glib/src/subclass/mod.rs | 8 -------- glib/src/subclass/type_plugin.rs | 29 +++++++++------------------- 4 files changed, 9 insertions(+), 63 deletions(-) diff --git a/glib-macros/tests/dynamic_enums.rs b/glib-macros/tests/dynamic_enums.rs index c2ac8d1fe5a1..b025adc59011 100644 --- a/glib-macros/tests/dynamic_enums.rs +++ b/glib-macros/tests/dynamic_enums.rs @@ -268,26 +268,9 @@ pub mod plugin { .expect("EnumClass::complete_type_info failed"); (type_info, glib::TypeValueTable::default()) } - - fn complete_interface_info( - &self, - _instance_type: glib::Type, - _interface_type: glib::Type, - ) -> glib::InterfaceInfo { - unimplemented!() - } } impl TypePluginRegisterImpl for MyPlugin { - fn add_dynamic_interface( - &self, - _instance_type: glib::Type, - _interface_type: glib::Type, - _interface_info: &glib::InterfaceInfo, - ) { - unimplemented!() - } - fn register_dynamic_enum( &self, type_name: &str, @@ -312,16 +295,6 @@ pub mod plugin { } type_ } - - fn register_dynamic_type( - &self, - _parent_type: glib::Type, - _type_name: &str, - _type_info: &glib::TypeInfo, - _flags: glib::TypeFlags, - ) -> glib::Type { - unimplemented!() - } } } diff --git a/glib-macros/tests/dynamic_objects.rs b/glib-macros/tests/dynamic_objects.rs index 5e9c429e41fa..3bec08f47c6f 100644 --- a/glib-macros/tests/dynamic_objects.rs +++ b/glib-macros/tests/dynamic_objects.rs @@ -465,14 +465,6 @@ pub mod plugin { }; } - fn register_dynamic_enum( - &self, - _name: &str, - _const_static_values: &'static [glib::EnumValue], - ) -> glib::Type { - unimplemented!() - } - fn register_dynamic_type( &self, parent_type: glib::Type, diff --git a/glib/src/subclass/mod.rs b/glib/src/subclass/mod.rs index a800a3ac7c0c..6ada58559ce7 100644 --- a/glib/src/subclass/mod.rs +++ b/glib/src/subclass/mod.rs @@ -343,14 +343,6 @@ //! } //! //! impl TypePluginRegisterImpl for SimpleTypePlugin { -//! fn add_dynamic_interface(&self, _: glib::Type, _: glib::Type, _: &glib::InterfaceInfo) { -//! unimplemented!() -//! } -//! -//! fn register_dynamic_enum(&self, _: &str, _: &'static [glib::EnumValue]) -> glib::Type { -//! unimplemented!() -//! } -//! //! fn register_dynamic_type(&self, parent_type: glib::Type, type_name: &str, type_info: &glib::TypeInfo, flags: glib::TypeFlags) -> glib::Type { //! let type_ = glib::Type::from_name(type_name).unwrap_or_else(|| { //! glib::Type::register_dynamic(parent_type, type_name, self.obj().upcast_ref::(), flags) diff --git a/glib/src/subclass/type_plugin.rs b/glib/src/subclass/type_plugin.rs index 98b5d50742b0..be0e8c2c3ede 100644 --- a/glib/src/subclass/type_plugin.rs +++ b/glib/src/subclass/type_plugin.rs @@ -183,19 +183,25 @@ pub trait TypePluginRegisterImpl: ObjectImpl + TypePluginImpl { _instance_type: Type, _interface_type: Type, _interface_info: &InterfaceInfo, - ); + ) { + unimplemented!() + } fn register_dynamic_enum( &self, _name: &str, _const_static_values: &'static [EnumValue], - ) -> Type; + ) -> Type { + unimplemented!() + } fn register_dynamic_type( &self, _parent_type: Type, _type_name: &str, _type_info: &TypeInfo, _flags: TypeFlags, - ) -> Type; + ) -> Type { + unimplemented!() + } } #[cfg(test)] @@ -240,23 +246,6 @@ mod tests { } impl TypePluginRegisterImpl for SimplePlugin { - fn add_dynamic_interface( - &self, - _instance_type: Type, - _interface_type: Type, - _interface_info: &InterfaceInfo, - ) { - unimplemented!() - } - - fn register_dynamic_enum( - &self, - _name: &str, - _const_static_values: &'static [EnumValue], - ) -> Type { - unimplemented!() - } - fn register_dynamic_type( &self, parent_type: Type, From b436773d13e7c18579b6b8cfeb90a90831ee600e Mon Sep 17 00:00:00 2001 From: fbrouille Date: Sun, 10 Dec 2023 19:26:10 +0100 Subject: [PATCH 12/15] add more tests on EnumValues Signed-off-by: fbrouille --- glib-macros/tests/dynamic_enums.rs | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/glib-macros/tests/dynamic_enums.rs b/glib-macros/tests/dynamic_enums.rs index b025adc59011..0f74ccd83cad 100644 --- a/glib-macros/tests/dynamic_enums.rs +++ b/glib-macros/tests/dynamic_enums.rs @@ -150,6 +150,14 @@ mod module { assert_eq!(t.name(), "MyModuleEnum"); let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); + + let values = e.values(); + assert_eq!(values.len(), 2); + assert_eq!(values[0].name(), "Foo"); + assert_eq!(values[0].nick(), "foo"); + assert_eq!(values[1].name(), "Bar"); + assert_eq!(values[1].nick(), "bar"); + let v = e.value(0).expect("EnumClass::get_value(0) failed"); assert_eq!(v.name(), "Foo"); assert_eq!(v.nick(), "foo"); @@ -186,6 +194,14 @@ mod module { assert_eq!(t.name(), "MyModuleEnumLazy"); let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); + + let values = e.values(); + assert_eq!(values.len(), 2); + assert_eq!(values[0].name(), "Foo"); + assert_eq!(values[0].nick(), "foo"); + assert_eq!(values[1].name(), "Bar"); + assert_eq!(values[1].nick(), "bar"); + let v = e.value(0).expect("EnumClass::get_value(0) failed"); assert_eq!(v.name(), "Foo"); assert_eq!(v.nick(), "foo"); @@ -406,6 +422,14 @@ pub mod plugin { assert_eq!(t.name(), "MyPluginEnum"); let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); + + let values = e.values(); + assert_eq!(values.len(), 2); + assert_eq!(values[0].name(), "Foo"); + assert_eq!(values[0].nick(), "foo"); + assert_eq!(values[1].name(), "Bar"); + assert_eq!(values[1].nick(), "bar"); + let v = e.value(0).expect("EnumClass::get_value(0) failed"); assert_eq!(v.name(), "Foo"); assert_eq!(v.nick(), "foo"); @@ -442,6 +466,14 @@ pub mod plugin { assert_eq!(t.name(), "MyPluginEnumLazy"); let e = glib::EnumClass::with_type(t).expect("EnumClass::new failed"); + + let values = e.values(); + assert_eq!(values.len(), 2); + assert_eq!(values[0].name(), "Foo"); + assert_eq!(values[0].nick(), "foo"); + assert_eq!(values[1].name(), "Bar"); + assert_eq!(values[1].nick(), "bar"); + let v = e.value(0).expect("EnumClass::get_value(0) failed"); assert_eq!(v.name(), "Foo"); assert_eq!(v.nick(), "foo"); From a994cd10383651ebd30b5059cc3e704d0c6c9b90 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Wed, 13 Dec 2023 23:34:54 +0100 Subject: [PATCH 13/15] safe methods to create a zero terminated [EnumValue] Signed-off-by: fbrouille --- glib-macros/src/enum_derive.rs | 25 ++++----- glib-macros/tests/dynamic_enums.rs | 6 +- glib/src/enums.rs | 88 +++++++++++++++++------------- glib/src/gobject/dynamic_object.rs | 8 +-- glib/src/gobject/type_module.rs | 17 ++---- glib/src/lib.rs | 5 +- glib/src/subclass/type_plugin.rs | 6 +- 7 files changed, 80 insertions(+), 75 deletions(-) diff --git a/glib-macros/src/enum_derive.rs b/glib-macros/src/enum_derive.rs index b13ce382ec5f..bb2f772d00ed 100644 --- a/glib-macros/src/enum_derive.rs +++ b/glib-macros/src/enum_derive.rs @@ -175,14 +175,11 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { }); let enum_values = quote! { - [#crate_ident::EnumValue; #nb_enum_values] = unsafe {[ - #(#enum_values_iter)* - #crate_ident::EnumValue::unsafe_from(#crate_ident::gobject_ffi::GEnumValue { - value: 0, - value_name: ::std::ptr::null(), - value_nick: ::std::ptr::null(), - }), - ]} + #crate_ident::EnumValuesStorage<#nb_enum_values> = unsafe { + #crate_ident::EnumValuesStorage::<#nb_enum_values>::new::<{#nb_enum_values - 1}>([ + #(#enum_values_iter)* + ]) + } }; // The following implementations follows the lifecycle of plugins and of dynamic types (see [`TypePluginExt`] and [`TypeModuleExt`]). @@ -216,8 +213,8 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { None => #crate_ident::Type::INVALID, // plugin has been used and the enum has not been registered yet, so registers it as a dynamic type. Some(RegistrationStatus(type_plugin, type_)) if !type_.is_valid() => { - static mut VALUES: #enum_values; - *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin.upgrade().unwrap().as_ref(), #gtype_name, unsafe { &VALUES } ); + static VALUES: #enum_values; + *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin.upgrade().unwrap().as_ref(), #gtype_name, VALUES.as_ref()); *type_ }, // plugin has been used and the enum has already been registered as a dynamic type. @@ -242,8 +239,8 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { }, // plugin has been used at least one time and the enum has been registered as a dynamic type at least one time, so re-registers it. Some(RegistrationStatus(_, type_)) if type_.is_valid() => { - static mut VALUES: #enum_values; - *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, unsafe { &VALUES } ); + static VALUES: #enum_values; + *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, VALUES.as_ref()); type_.is_valid() }, // plugin has been used at least one time but the enum has not been registered yet as a dynamic type, so keeps postponed registration. @@ -297,8 +294,8 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { let gtype_ref = Self::get_gtype_ref(); - static mut VALUES: #enum_values; - let gtype = #crate_ident::translate::IntoGlib::into_glib(<#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, unsafe { &VALUES } )); + static VALUES: #enum_values; + let gtype = #crate_ident::translate::IntoGlib::into_glib(<#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, VALUES.as_ref())); gtype_ref.store(gtype, ::std::sync::atomic::Ordering::Relaxed); gtype != #crate_ident::gobject_ffi::G_TYPE_INVALID } diff --git a/glib-macros/tests/dynamic_enums.rs b/glib-macros/tests/dynamic_enums.rs index 0f74ccd83cad..321d8128444d 100644 --- a/glib-macros/tests/dynamic_enums.rs +++ b/glib-macros/tests/dynamic_enums.rs @@ -238,8 +238,8 @@ pub mod plugin { // impl for a type plugin (must implement `glib::TypePlugin`). #[derive(Default)] pub struct MyPlugin { - my_enum_type_values: Cell>, - my_enum_lazy_type_values: Cell>, + my_enum_type_values: Cell>, + my_enum_lazy_type_values: Cell>, } #[glib::object_subclass] @@ -290,7 +290,7 @@ pub mod plugin { fn register_dynamic_enum( &self, type_name: &str, - const_static_values: &'static [glib::EnumValue], + const_static_values: &'static glib::EnumValues, ) -> glib::Type { let type_ = glib::Type::from_name(type_name).unwrap_or_else(|| { glib::Type::register_dynamic( diff --git a/glib/src/enums.rs b/glib/src/enums.rs index 4e9395f36398..202f78f82b8f 100644 --- a/glib/src/enums.rs +++ b/glib/src/enums.rs @@ -1,6 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use std::{cmp, ffi::CStr, fmt, marker::PhantomData, ptr}; +use std::{cmp, ffi::CStr, fmt, ptr}; use crate::{ translate::*, @@ -194,25 +194,14 @@ impl EnumClass { /// callers should first create an `EnumClass` instance by calling `EnumClass::with_type()` which indirectly /// calls `TypePluginRegisterImpl::register_dynamic_enum()` and `TypePluginImpl::complete_type_info()` /// and one of them should call `EnumClass::with_type()` before calling this method. - /// `const_static_values` is an array of `EnumValue` for the possible enumeration values. The array must be - /// static to ensure enumeration values are never dropped, and must be terminated by an `EnumValue` with - /// all members being 0, as expected by GLib. + /// `const_static_values` is a reference on a wrapper of a slice of `EnumValue`. + /// It must be static to ensure enumeration values are never dropped, and ensures that slice is terminated + /// by an `EnumValue` with all members being 0, as expected by GLib. #[doc(alias = "g_enum_complete_type_info")] pub fn complete_type_info( type_: Type, - const_static_values: &'static [EnumValue], + const_static_values: &'static EnumValues, ) -> Option { - assert!( - !const_static_values.is_empty() - && const_static_values[const_static_values.len() - 1] - == unsafe { - EnumValue::unsafe_from(gobject_ffi::GEnumValue { - value: 0, - value_name: ::std::ptr::null(), - value_nick: ::std::ptr::null(), - }) - } - ); unsafe { let is_enum: bool = from_glib(gobject_ffi::g_type_is_a( type_.into_glib(), @@ -359,34 +348,59 @@ unsafe impl<'a, 'b> FromValue<'a> for &'b EnumValue { } } -#[doc(hidden)] -impl<'a> ToGlibPtr<'a, *const gobject_ffi::GEnumValue> for EnumValue { - type Storage = PhantomData<&'a Self>; - - #[inline] - fn to_glib_none(&'a self) -> Stash<'a, *const gobject_ffi::GEnumValue, Self> { - Stash(&self.0 as *const gobject_ffi::GEnumValue, PhantomData) +// rustdoc-stripper-ignore-next +/// Storage of enumeration values terminated by an `EnumValue` with all members +/// being 0. Should be used only as a storage location for enumeration values +/// when registering an enumeration as a dynamic type. +/// see `TypePluginRegisterImpl::register_dynamic_enum()` and `TypePluginImpl::complete_type_info()`. +/// Inner is intentionally private to ensure other modules will not access the +/// enumeration values by this way. +/// Use `EnumClass::values()` or `EnumClass::value()` to get enumeration values. +#[repr(transparent)] +pub struct EnumValuesStorage([EnumValue; S]); + +impl EnumValuesStorage { + pub const fn new(values: [EnumValue; N]) -> Self { + const ZERO: EnumValue = unsafe { + EnumValue::unsafe_from(gobject_ffi::GEnumValue { + value: 0, + value_name: ptr::null(), + value_nick: ptr::null(), + }) + }; + unsafe { + let v: [EnumValue; S] = [ZERO; S]; + ptr::copy_nonoverlapping(values.as_ptr(), v.as_ptr() as _, N); + Self(v) + } } } -impl<'a> ToGlibContainerFromSlice<'a, *const gobject_ffi::GEnumValue> for EnumValue { - type Storage = PhantomData<&'a Self>; +// rustdoc-stripper-ignore-next +/// Representation of enumeration values wrapped by `EnumValuesStorage`. Easier +/// to use because don't have a size parameter to be specify. Should be used +/// only to register an enumeration as a dynamic type. +/// see `TypePluginRegisterImpl::register_dynamic_enum()` and `TypePluginImpl::complete_type_info()`. +/// Field is intentionally private to ensure other modules will not access the +/// enumeration values by this way. +/// Use `EnumClass::values()` or `EnumClass::value()` to get the enumeration values. +#[repr(transparent)] +pub struct EnumValues([EnumValue]); - fn to_glib_none_from_slice(t: &'a [Self]) -> (*const gobject_ffi::GEnumValue, Self::Storage) { - ( - t.as_ptr() as *const gobject_ffi::GEnumValue, - std::marker::PhantomData, - ) +impl AsRef for EnumValuesStorage { + fn as_ref(&self) -> &EnumValues { + // SAFETY: EnumValues is repr(transparent) over [EnumValue] so the cast is safe. + unsafe { &*(&self.0 as *const [EnumValue] as *const EnumValues) } } +} - fn to_glib_container_from_slice( - _: &'a [Self], - ) -> (*const gobject_ffi::GEnumValue, Self::Storage) { - unimplemented!(); - } +#[doc(hidden)] +impl<'a> ToGlibPtr<'a, *const gobject_ffi::GEnumValue> for EnumValues { + type Storage = &'a Self; - fn to_glib_full_from_slice(_: &[Self]) -> *const gobject_ffi::GEnumValue { - unimplemented!(); + #[inline] + fn to_glib_none(&'a self) -> Stash<'a, *const gobject_ffi::GEnumValue, Self> { + Stash(self.0.as_ptr() as *const gobject_ffi::GEnumValue, self) } } diff --git a/glib/src/gobject/dynamic_object.rs b/glib/src/gobject/dynamic_object.rs index fb044ffaaa7c..706ddd07ec7a 100644 --- a/glib/src/gobject/dynamic_object.rs +++ b/glib/src/gobject/dynamic_object.rs @@ -1,7 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::{ - prelude::*, subclass::prelude::*, EnumValue, InterfaceInfo, IsA, TypeFlags, TypeInfo, + enums::EnumValues, prelude::*, subclass::prelude::*, InterfaceInfo, IsA, TypeFlags, TypeInfo, TypeModule, TypePlugin, }; @@ -21,7 +21,7 @@ pub trait DynamicObjectRegisterExt: AsRef + sealed::Sealed + 'static fn register_dynamic_enum( &self, name: &str, - const_static_values: &'static [EnumValue], + const_static_values: &'static EnumValues, ) -> crate::types::Type; fn register_dynamic_type( @@ -50,7 +50,7 @@ where fn register_dynamic_enum( &self, name: &str, - const_static_values: &'static [EnumValue], + const_static_values: &'static EnumValues, ) -> crate::types::Type { self.imp().register_dynamic_enum(name, const_static_values) } @@ -80,7 +80,7 @@ impl DynamicObjectRegisterExt for TypeModule { fn register_dynamic_enum( &self, name: &str, - const_static_values: &'static [EnumValue], + const_static_values: &'static EnumValues, ) -> crate::types::Type { ::register_enum(self, name, const_static_values) } diff --git a/glib/src/gobject/type_module.rs b/glib/src/gobject/type_module.rs index 6bc2443b47d9..e96bf17e0198 100644 --- a/glib/src/gobject/type_module.rs +++ b/glib/src/gobject/type_module.rs @@ -1,6 +1,8 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use crate::{prelude::*, translate::*, EnumValue, InterfaceInfo, TypeFlags, TypeInfo, TypePlugin}; +use crate::{ + enums::EnumValues, prelude::*, translate::*, InterfaceInfo, TypeFlags, TypeInfo, TypePlugin, +}; crate::wrapper! { #[doc(alias = "GTypeModule")] @@ -42,19 +44,8 @@ pub trait TypeModuleExt: IsA + sealed::Sealed + 'static { fn register_enum( &self, name: &str, - const_static_values: &'static [EnumValue], + const_static_values: &'static EnumValues, ) -> crate::types::Type { - assert!( - !const_static_values.is_empty() - && const_static_values[const_static_values.len() - 1] - == unsafe { - EnumValue::unsafe_from(gobject_ffi::GEnumValue { - value: 0, - value_name: ::std::ptr::null(), - value_nick: ::std::ptr::null(), - }) - } - ); unsafe { from_glib(gobject_ffi::g_type_module_register_enum( self.as_ref().to_glib_none().0, diff --git a/glib/src/lib.rs b/glib/src/lib.rs index d1aa8e12df06..ade90a67b6d3 100644 --- a/glib/src/lib.rs +++ b/glib/src/lib.rs @@ -20,7 +20,10 @@ pub use self::{ byte_array::ByteArray, bytes::Bytes, closure::{Closure, RustClosure}, - enums::{EnumClass, EnumValue, FlagsBuilder, FlagsClass, FlagsValue, UserDirectory}, + enums::{ + EnumClass, EnumValue, EnumValues, EnumValuesStorage, FlagsBuilder, FlagsClass, FlagsValue, + UserDirectory, + }, error::{BoolError, Error}, object::{ BorrowedObject, Cast, CastNone, Class, InitiallyUnowned, Interface, IsA, Object, diff --git a/glib/src/subclass/type_plugin.rs b/glib/src/subclass/type_plugin.rs index be0e8c2c3ede..6116c9bcc9fd 100644 --- a/glib/src/subclass/type_plugin.rs +++ b/glib/src/subclass/type_plugin.rs @@ -1,12 +1,12 @@ // Take a look at the license at the top of the repository in the LICENSE file. +use crate::enums::EnumValues; use crate::translate::IntoGlib; use crate::translate::{FromGlib, ToGlibPtr}; use crate::{ - subclass::prelude::*, Cast, Interface, InterfaceInfo, Type, TypeInfo, TypePlugin, + subclass::prelude::*, Cast, Interface, InterfaceInfo, Type, TypeFlags, TypeInfo, TypePlugin, TypeValueTable, }; -use crate::{EnumValue, TypeFlags}; pub trait TypePluginImpl: ObjectImpl + TypePluginImplExt { fn use_plugin(&self) { @@ -189,7 +189,7 @@ pub trait TypePluginRegisterImpl: ObjectImpl + TypePluginImpl { fn register_dynamic_enum( &self, _name: &str, - _const_static_values: &'static [EnumValue], + _const_static_values: &'static EnumValues, ) -> Type { unimplemented!() } From e7319ec7d72e7c9e0571c6c5b882d369273b47a3 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Thu, 14 Dec 2023 14:08:06 +0100 Subject: [PATCH 14/15] implement Deref for EnumValues and make module enums public Signed-off-by: fbrouille --- glib-macros/src/enum_derive.rs | 4 +-- glib-macros/tests/dynamic_enums.rs | 6 ++-- glib/src/enums.rs | 44 ++++++++++++++++++++---------- glib/src/lib.rs | 7 ++--- 4 files changed, 37 insertions(+), 24 deletions(-) diff --git a/glib-macros/src/enum_derive.rs b/glib-macros/src/enum_derive.rs index bb2f772d00ed..2d94705425da 100644 --- a/glib-macros/src/enum_derive.rs +++ b/glib-macros/src/enum_derive.rs @@ -175,8 +175,8 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { }); let enum_values = quote! { - #crate_ident::EnumValuesStorage<#nb_enum_values> = unsafe { - #crate_ident::EnumValuesStorage::<#nb_enum_values>::new::<{#nb_enum_values - 1}>([ + #crate_ident::enums::EnumValuesStorage<#nb_enum_values> = unsafe { + #crate_ident::enums::EnumValuesStorage::<#nb_enum_values>::new::<{#nb_enum_values - 1}>([ #(#enum_values_iter)* ]) } diff --git a/glib-macros/tests/dynamic_enums.rs b/glib-macros/tests/dynamic_enums.rs index 321d8128444d..8db937085f9b 100644 --- a/glib-macros/tests/dynamic_enums.rs +++ b/glib-macros/tests/dynamic_enums.rs @@ -238,8 +238,8 @@ pub mod plugin { // impl for a type plugin (must implement `glib::TypePlugin`). #[derive(Default)] pub struct MyPlugin { - my_enum_type_values: Cell>, - my_enum_lazy_type_values: Cell>, + my_enum_type_values: Cell>, + my_enum_lazy_type_values: Cell>, } #[glib::object_subclass] @@ -290,7 +290,7 @@ pub mod plugin { fn register_dynamic_enum( &self, type_name: &str, - const_static_values: &'static glib::EnumValues, + const_static_values: &'static glib::enums::EnumValues, ) -> glib::Type { let type_ = glib::Type::from_name(type_name).unwrap_or_else(|| { glib::Type::register_dynamic( diff --git a/glib/src/enums.rs b/glib/src/enums.rs index 202f78f82b8f..dd8768ad60b1 100644 --- a/glib/src/enums.rs +++ b/glib/src/enums.rs @@ -1,6 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use std::{cmp, ffi::CStr, fmt, ptr}; +use std::{cmp, ffi::CStr, fmt, ops::Deref, ptr}; use crate::{ translate::*, @@ -348,6 +348,22 @@ unsafe impl<'a, 'b> FromValue<'a> for &'b EnumValue { } } +#[doc(hidden)] +impl<'a> ToGlibContainerFromSlice<'a, *const gobject_ffi::GEnumValue> for EnumValue { + type Storage = &'a [Self]; + fn to_glib_none_from_slice(t: &'a [Self]) -> (*const gobject_ffi::GEnumValue, Self::Storage) { + (t.as_ptr() as *const gobject_ffi::GEnumValue, t) + } + fn to_glib_container_from_slice( + _: &'a [Self], + ) -> (*const gobject_ffi::GEnumValue, Self::Storage) { + unimplemented!(); + } + fn to_glib_full_from_slice(_: &[Self]) -> *const gobject_ffi::GEnumValue { + unimplemented!(); + } +} + // rustdoc-stripper-ignore-next /// Storage of enumeration values terminated by an `EnumValue` with all members /// being 0. Should be used only as a storage location for enumeration values @@ -360,6 +376,7 @@ unsafe impl<'a, 'b> FromValue<'a> for &'b EnumValue { pub struct EnumValuesStorage([EnumValue; S]); impl EnumValuesStorage { + // rustdoc-stripper-ignore-next pub const fn new(values: [EnumValue; N]) -> Self { const ZERO: EnumValue = unsafe { EnumValue::unsafe_from(gobject_ffi::GEnumValue { @@ -376,6 +393,13 @@ impl EnumValuesStorage { } } +impl AsRef for EnumValuesStorage { + fn as_ref(&self) -> &EnumValues { + // SAFETY: EnumValues is repr(transparent) over [EnumValue] so the cast is safe. + unsafe { &*(&self.0 as *const [EnumValue] as *const EnumValues) } + } +} + // rustdoc-stripper-ignore-next /// Representation of enumeration values wrapped by `EnumValuesStorage`. Easier /// to use because don't have a size parameter to be specify. Should be used @@ -387,20 +411,12 @@ impl EnumValuesStorage { #[repr(transparent)] pub struct EnumValues([EnumValue]); -impl AsRef for EnumValuesStorage { - fn as_ref(&self) -> &EnumValues { - // SAFETY: EnumValues is repr(transparent) over [EnumValue] so the cast is safe. - unsafe { &*(&self.0 as *const [EnumValue] as *const EnumValues) } - } -} +impl Deref for EnumValues { + type Target = [EnumValue]; -#[doc(hidden)] -impl<'a> ToGlibPtr<'a, *const gobject_ffi::GEnumValue> for EnumValues { - type Storage = &'a Self; - - #[inline] - fn to_glib_none(&'a self) -> Stash<'a, *const gobject_ffi::GEnumValue, Self> { - Stash(self.0.as_ptr() as *const gobject_ffi::GEnumValue, self) + fn deref(&self) -> &Self::Target { + // SAFETY: EnumValues contains at least the zero `EnumValue` which terminates the enumeration values. + unsafe { std::slice::from_raw_parts(self.0.as_ptr(), self.0.len() - 1) } } } diff --git a/glib/src/lib.rs b/glib/src/lib.rs index ade90a67b6d3..b25866b826b7 100644 --- a/glib/src/lib.rs +++ b/glib/src/lib.rs @@ -20,10 +20,7 @@ pub use self::{ byte_array::ByteArray, bytes::Bytes, closure::{Closure, RustClosure}, - enums::{ - EnumClass, EnumValue, EnumValues, EnumValuesStorage, FlagsBuilder, FlagsClass, FlagsValue, - UserDirectory, - }, + enums::{EnumClass, EnumValue, FlagsBuilder, FlagsClass, FlagsValue, UserDirectory}, error::{BoolError, Error}, object::{ BorrowedObject, Cast, CastNone, Class, InitiallyUnowned, Interface, IsA, Object, @@ -142,7 +139,7 @@ mod checksum; pub mod closure; mod convert; pub use self::convert::*; -mod enums; +pub mod enums; mod functions; pub use self::functions::*; mod key_file; From c9674e9007a1705259cd2eb79ad2014370a5c5a6 Mon Sep 17 00:00:00 2001 From: fbrouille Date: Thu, 14 Dec 2023 16:29:30 +0100 Subject: [PATCH 15/15] replace get_registration_status_ref() and get_gtype_ref() by static variables Signed-off-by: fbrouille --- glib-macros/src/enum_derive.rs | 76 +++++++++++++++++----------------- 1 file changed, 39 insertions(+), 37 deletions(-) diff --git a/glib-macros/src/enum_derive.rs b/glib-macros/src/enum_derive.rs index 2d94705425da..6b693fa13202 100644 --- a/glib-macros/src/enum_derive.rs +++ b/glib-macros/src/enum_derive.rs @@ -1,9 +1,9 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use heck::{ToKebabCase, ToUpperCamelCase}; +use heck::{ToKebabCase, ToShoutySnakeCase, ToUpperCamelCase}; use proc_macro2::TokenStream; use proc_macro_error::abort_call_site; -use quote::{quote, quote_spanned, ToTokens}; +use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::{ parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Data, ExprArray, Ident, Variant, @@ -188,37 +188,44 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { // registers the enum as a dynamic type on the first use (lazy registration). // a weak reference on the plugin is stored and will be used later on the first use of the enum. // this implementation relies on a static storage of a weak reference on the plugin and of the GLib type to know if the enum has been registered. + + // the registration status type. + let registration_status_type = format_ident!("{}RegistrationStatus", name); + // name of the static variable to store the registration status. + let registration_status = format_ident!( + "{}", + registration_status_type.to_string().to_shouty_snake_case() + ); + // name of the static array to store the enumeration values. + let enum_values_array = format_ident!("{}_VALUES", name.to_string().to_shouty_snake_case()); quote! { - struct RegistrationStatus(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type); - unsafe impl Send for RegistrationStatus {} + /// The registration status type: a tuple of the weak reference on the plugin and of the GLib type. + struct #registration_status_type(<#plugin_ty as #crate_ident::clone::Downgrade>::Weak, #crate_ident::Type); + unsafe impl Send for #registration_status_type {} - impl #name { - /// Returns a mutable reference to the registration status: a tuple of the weak reference on the plugin and of the GLib type. - /// This is safe because the mutable reference guarantees that no other threads are concurrently accessing the data. - #[inline] - fn get_registration_status_ref() -> &'static ::std::sync::Mutex> { - static REGISTRATION_STATUS: ::std::sync::Mutex> = ::std::sync::Mutex::new(None); - ®ISTRATION_STATUS - } + /// The registration status protected by a mutex guarantees so that no other threads are concurrently accessing the data. + static #registration_status: ::std::sync::Mutex> = ::std::sync::Mutex::new(None); + + /// Array of `EnumValue` for the possible enumeration values. + static #enum_values_array: #enum_values; + impl #name { /// Registers the enum as a dynamic type within the plugin only once. /// Plugin must have been used at least once. /// Do nothing if plugin has never been used or if the enum is already registered as a dynamic type. #[inline] fn register_enum() -> #crate_ident::Type { - let registration_status_ref = Self::get_registration_status_ref(); - let mut registration_status = registration_status_ref.lock().unwrap(); + let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used, so the enum cannot be registered as a dynamic type. None => #crate_ident::Type::INVALID, // plugin has been used and the enum has not been registered yet, so registers it as a dynamic type. - Some(RegistrationStatus(type_plugin, type_)) if !type_.is_valid() => { - static VALUES: #enum_values; - *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin.upgrade().unwrap().as_ref(), #gtype_name, VALUES.as_ref()); + Some(#registration_status_type(type_plugin, type_)) if !type_.is_valid() => { + *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin.upgrade().unwrap().as_ref(), #gtype_name, #enum_values_array.as_ref()); *type_ }, // plugin has been used and the enum has already been registered as a dynamic type. - Some(RegistrationStatus(_, type_)) => *type_ + Some(#registration_status_type(_, type_)) => *type_ } } @@ -229,18 +236,16 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { /// If plugin is reused (and has reloaded the implementation) and the enum has not been registered yet as a dynamic type, do nothing. #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { - let registration_status_ref = Self::get_registration_status_ref(); - let mut registration_status = registration_status_ref.lock().unwrap(); + let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used (this is the first time), so postpones registration of the enum as a dynamic type on the first use. None => { - *registration_status = Some(RegistrationStatus(#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); + *registration_status = Some(#registration_status_type(#crate_ident::clone::Downgrade::downgrade(type_plugin), #crate_ident::Type::INVALID)); true }, // plugin has been used at least one time and the enum has been registered as a dynamic type at least one time, so re-registers it. - Some(RegistrationStatus(_, type_)) if type_.is_valid() => { - static VALUES: #enum_values; - *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, VALUES.as_ref()); + Some(#registration_status_type(_, type_)) if type_.is_valid() => { + *type_ = <#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, #enum_values_array.as_ref()); type_.is_valid() }, // plugin has been used at least one time but the enum has not been registered yet as a dynamic type, so keeps postponed registration. @@ -255,13 +260,12 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { /// Else do nothing. #[inline] pub fn on_implementation_unload(type_plugin_: &#plugin_ty) -> bool { - let registration_status_ref = Self::get_registration_status_ref(); - let mut registration_status = registration_status_ref.lock().unwrap(); + let mut registration_status = #registration_status.lock().unwrap(); match ::std::ops::DerefMut::deref_mut(&mut registration_status) { // plugin has never been used, so unload implementation is unexpected. None => false, // plugin has been used at least one time and the enum has been registered as a dynamic type at least one time. - Some(RegistrationStatus(_, type_)) if type_.is_valid() => true, + Some(#registration_status_type(_, type_)) if type_.is_valid() => true, // plugin has been used at least one time but the enum has not been registered yet as a dynamic type, so cancels the postponed registration. Some(_) => { *registration_status = None; @@ -273,19 +277,18 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { } } else { // registers immediately the enum as a dynamic type. + + // name of the static variable to store the GLib type. + let gtype_status = format_ident!("{}_G_TYPE", name.to_string().to_shouty_snake_case()); quote! { - impl #name { - /// Returns a reference to the GLib type which can be safely shared between threads. - #[inline] - fn get_gtype_ref() -> &'static ::std::sync::atomic::AtomicUsize { - static TYPE: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); - &TYPE - } + /// The GLib type which can be safely shared between threads. + static #gtype_status: ::std::sync::atomic::AtomicUsize = ::std::sync::atomic::AtomicUsize::new(#crate_ident::gobject_ffi::G_TYPE_INVALID); + impl #name { /// Do nothing as the enum has been registered on implementation load. #[inline] fn register_enum() -> #crate_ident::Type { - let gtype = Self::get_gtype_ref().load(::std::sync::atomic::Ordering::Relaxed); + let gtype = #gtype_status.load(::std::sync::atomic::Ordering::Relaxed); unsafe { <#crate_ident::Type as #crate_ident::translate::FromGlib<#crate_ident::ffi::GType>>::from_glib(gtype) } } @@ -293,10 +296,9 @@ pub fn impl_dynamic_enum(input: &syn::DeriveInput) -> TokenStream { /// The enum can be registered several times as a dynamic type. #[inline] pub fn on_implementation_load(type_plugin: &#plugin_ty) -> bool { - let gtype_ref = Self::get_gtype_ref(); static VALUES: #enum_values; let gtype = #crate_ident::translate::IntoGlib::into_glib(<#plugin_ty as glib::prelude::DynamicObjectRegisterExt>::register_dynamic_enum(type_plugin, #gtype_name, VALUES.as_ref())); - gtype_ref.store(gtype, ::std::sync::atomic::Ordering::Relaxed); + #gtype_status.store(gtype, ::std::sync::atomic::Ordering::Relaxed); gtype != #crate_ident::gobject_ffi::G_TYPE_INVALID }