Skip to content

Commit

Permalink
Merge pull request #1451 from sdroege/allow-gtype-name-conflict
Browse files Browse the repository at this point in the history
glib: Add support for registering GTypes with name conflicts
  • Loading branch information
sdroege authored Jul 4, 2024
2 parents ffdad00 + bdb5fa9 commit 4101f56
Show file tree
Hide file tree
Showing 12 changed files with 311 additions and 36 deletions.
1 change: 1 addition & 0 deletions gio/src/read_input_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ mod imp {
#[glib::object_subclass]
impl ObjectSubclass for ReadInputStream {
const NAME: &'static str = "ReadInputStream";
const ALLOW_NAME_CONFLICT: bool = true;
type Type = super::ReadInputStream;
type ParentType = InputStream;
type Interfaces = (crate::Seekable,);
Expand Down
1 change: 1 addition & 0 deletions gio/src/write_output_stream.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ mod imp {
#[glib::object_subclass]
impl ObjectSubclass for WriteOutputStream {
const NAME: &'static str = "WriteOutputStream";
const ALLOW_NAME_CONFLICT: bool = true;
type Type = super::WriteOutputStream;
type ParentType = OutputStream;
type Interfaces = (crate::Seekable,);
Expand Down
10 changes: 9 additions & 1 deletion glib-macros/src/boxed_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,11 +97,13 @@ pub fn impl_boxed(input: &syn::DeriveInput) -> syn::Result<TokenStream> {
.required()
.value_required();
let mut nullable = NestedMetaItem::<syn::LitBool>::new("nullable").value_optional();
let mut allow_name_conflict =
NestedMetaItem::<syn::LitBool>::new("allow_name_conflict").value_optional();

let found = parse_nested_meta_items(
&input.attrs,
"boxed_type",
&mut [&mut gtype_name, &mut nullable],
&mut [&mut gtype_name, &mut nullable, &mut allow_name_conflict],
)?;

if found.is_none() {
Expand All @@ -113,6 +115,11 @@ pub fn impl_boxed(input: &syn::DeriveInput) -> syn::Result<TokenStream> {

let gtype_name = gtype_name.value.unwrap();
let nullable = nullable.found || nullable.value.map(|b| b.value()).unwrap_or(false);
let allow_name_conflict = allow_name_conflict.found
|| allow_name_conflict
.value
.map(|b| b.value())
.unwrap_or(false);

let crate_ident = crate_ident_new();

Expand All @@ -130,6 +137,7 @@ pub fn impl_boxed(input: &syn::DeriveInput) -> syn::Result<TokenStream> {
Ok(quote! {
impl #crate_ident::subclass::boxed::BoxedType for #name {
const NAME: &'static ::core::primitive::str = #gtype_name;
const ALLOW_NAME_CONFLICT: bool = #allow_name_conflict;
}

impl #crate_ident::prelude::StaticType for #name {
Expand Down
61 changes: 58 additions & 3 deletions glib-macros/src/enum_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,13 @@ pub fn impl_enum(input: &syn::DeriveInput) -> syn::Result<TokenStream> {
let mut gtype_name = NestedMetaItem::<syn::LitStr>::new("name")
.required()
.value_required();
let found = parse_nested_meta_items(&input.attrs, "enum_type", &mut [&mut gtype_name])?;
let mut allow_name_conflict =
NestedMetaItem::<syn::LitBool>::new("allow_name_conflict").value_optional();
let found = parse_nested_meta_items(
&input.attrs,
"enum_type",
&mut [&mut gtype_name, &mut allow_name_conflict],
)?;

if found.is_none() {
return Err(syn::Error::new_spanned(
Expand All @@ -87,6 +93,11 @@ pub fn impl_enum(input: &syn::DeriveInput) -> syn::Result<TokenStream> {
));
}
let gtype_name = gtype_name.value.unwrap();
let allow_name_conflict = allow_name_conflict.found
|| allow_name_conflict
.value
.map(|b| b.value())
.unwrap_or(false);

let mut plugin_type = NestedMetaItem::<syn::Path>::new("plugin_type").value_required();
let mut lazy_registration =
Expand All @@ -105,10 +116,18 @@ pub fn impl_enum(input: &syn::DeriveInput) -> syn::Result<TokenStream> {
&crate_ident,
name,
gtype_name,
allow_name_conflict,
g_enum_values,
nb_enum_values,
),
Some(_) => {
if allow_name_conflict {
return Err(syn::Error::new_spanned(
input,
"#[enum_dynamic] and #[enum_type(allow_name_conflict)] are not allowed together",
));
}

let plugin_ty = plugin_type
.value
.map(|p| p.into_token_stream())
Expand Down Expand Up @@ -227,9 +246,45 @@ fn register_enum_as_static(
crate_ident: &TokenStream,
name: &syn::Ident,
gtype_name: syn::LitStr,
allow_name_conflict: bool,
g_enum_values: TokenStream,
nb_enum_values: usize,
) -> TokenStream {
let type_name_snippet = if allow_name_conflict {
quote! {
unsafe {
let mut i = 0;
loop {
let type_name = ::std::ffi::CString::new(if i == 0 {
#gtype_name
} else {
format!("{}-{}", #gtype_name, i)
})
.unwrap();
if #crate_ident::gobject_ffi::g_type_from_name(type_name.as_ptr()) == #crate_ident::gobject_ffi::G_TYPE_INVALID
{
break type_name;
}
i += 1;
}
}
}
} else {
quote! {
unsafe {
let type_name = ::std::ffi::CString::new(#gtype_name).unwrap();
assert_eq!(
#crate_ident::gobject_ffi::g_type_from_name(type_name.as_ptr()),
#crate_ident::gobject_ffi::G_TYPE_INVALID,
"Type {} has already been registered",
type_name.to_str().unwrap()
);

type_name
}
}
};

// registers the enum on first use (lazy registration).
quote! {
impl #name {
Expand All @@ -246,9 +301,9 @@ fn register_enum_as_static(
value_nick: ::std::ptr::null(),
},
];
let name = ::std::ffi::CString::new(#gtype_name).expect("CString::new failed");
let type_name = #type_name_snippet;
unsafe {
let type_ = #crate_ident::gobject_ffi::g_enum_register_static(name.as_ptr(), VALUES.as_ptr());
let type_ = #crate_ident::gobject_ffi::g_enum_register_static(type_name.as_ptr(), VALUES.as_ptr());
let type_: #crate_ident::Type = #crate_ident::translate::from_glib(type_);
assert!(type_.is_valid());
type_
Expand Down
53 changes: 49 additions & 4 deletions glib-macros/src/flags_attribute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ pub const WRONG_PLACE_MSG: &str = "#[glib::flags] only supports enums";

pub struct AttrInput {
pub enum_name: syn::LitStr,
pub allow_name_conflict: bool,
}
struct FlagsDesc {
variant: Variant,
Expand Down Expand Up @@ -136,8 +137,8 @@ fn gen_default(
})
}

pub fn impl_flags(attrs: AttrInput, input: &mut syn::ItemEnum) -> TokenStream {
let gtype_name = attrs.enum_name;
pub fn impl_flags(attr_meta: AttrInput, input: &mut syn::ItemEnum) -> TokenStream {
let gtype_name = attr_meta.enum_name;

let syn::ItemEnum {
attrs,
Expand Down Expand Up @@ -167,10 +168,18 @@ pub fn impl_flags(attrs: AttrInput, input: &mut syn::ItemEnum) -> TokenStream {
&crate_ident,
name,
gtype_name,
attr_meta.allow_name_conflict,
g_flags_values,
nb_flags_values,
),
Ok(Some(_)) => {
if attr_meta.allow_name_conflict {
return syn::Error::new_spanned(
input,
"#[flags_dynamic] and #[glib::flags(allow_name_conflict)] are not allowed together",
).to_compile_error();
}

// remove attribute 'flags_dynamic' from the attribute list because it is not a real proc_macro_attribute
attrs.retain(|attr| !attr.path().is_ident("flags_dynamic"));
let plugin_ty = plugin_type
Expand Down Expand Up @@ -281,9 +290,45 @@ fn register_flags_as_static(
crate_ident: &TokenStream,
name: &syn::Ident,
gtype_name: syn::LitStr,
allow_name_conflict: bool,
g_flags_values: TokenStream,
nb_flags_values: usize,
) -> TokenStream {
let type_name_snippet = if allow_name_conflict {
quote! {
unsafe {
let mut i = 0;
loop {
let type_name = ::std::ffi::CString::new(if i == 0 {
#gtype_name
} else {
format!("{}-{}", #gtype_name, i)
})
.unwrap();
if #crate_ident::gobject_ffi::g_type_from_name(type_name.as_ptr()) == #crate_ident::gobject_ffi::G_TYPE_INVALID
{
break type_name;
}
i += 1;
}
}
}
} else {
quote! {
unsafe {
let type_name = ::std::ffi::CString::new(#gtype_name).unwrap();
assert_eq!(
#crate_ident::gobject_ffi::g_type_from_name(type_name.as_ptr()),
#crate_ident::gobject_ffi::G_TYPE_INVALID,
"Type {} has already been registered",
type_name.to_str().unwrap()
);

type_name
}
}
};

// registers the flags on first use (lazy registration).
quote! {
impl #name {
Expand All @@ -301,9 +346,9 @@ fn register_flags_as_static(
},
];

let name = ::std::ffi::CString::new(#gtype_name).expect("CString::new failed");
let type_name = #type_name_snippet;
unsafe {
let type_ = #crate_ident::gobject_ffi::g_flags_register_static(name.as_ptr(), VALUES.as_ptr());
let type_ = #crate_ident::gobject_ffi::g_flags_register_static(type_name.as_ptr(), VALUES.as_ptr());
let type_: #crate_ident::Type = #crate_ident::translate::from_glib(type_);
assert!(type_.is_valid());
type_
Expand Down
14 changes: 13 additions & 1 deletion glib-macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -785,13 +785,25 @@ pub fn flags(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut name = NestedMetaItem::<syn::LitStr>::new("name")
.required()
.value_required();
let mut allow_name_conflict_attr =
NestedMetaItem::<syn::LitBool>::new("allow_name_conflict").value_optional();

if let Err(e) = parse_nested_meta_items_from_stream(attr.into(), &mut [&mut name]) {
if let Err(e) = parse_nested_meta_items_from_stream(
attr.into(),
&mut [&mut name, &mut allow_name_conflict_attr],
) {
return e.to_compile_error().into();
}

let allow_name_conflict = allow_name_conflict_attr.found
|| allow_name_conflict_attr
.value
.map(|b| b.value())
.unwrap_or(false);

let attr_meta = AttrInput {
enum_name: name.value.unwrap(),
allow_name_conflict,
};

syn::parse::<syn::ItemEnum>(item)
Expand Down
10 changes: 9 additions & 1 deletion glib-macros/src/shared_boxed_derive.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,13 @@ pub fn impl_shared_boxed(input: &syn::DeriveInput) -> syn::Result<proc_macro2::T
.required()
.value_required();
let mut nullable = NestedMetaItem::<syn::LitBool>::new("nullable").value_optional();
let mut allow_name_conflict =
NestedMetaItem::<syn::LitBool>::new("allow_name_conflict").value_optional();

let found = parse_nested_meta_items(
&input.attrs,
"shared_boxed_type",
&mut [&mut gtype_name, &mut nullable],
&mut [&mut gtype_name, &mut nullable, &mut allow_name_conflict],
)?;

if found.is_none() {
Expand All @@ -121,6 +123,11 @@ pub fn impl_shared_boxed(input: &syn::DeriveInput) -> syn::Result<proc_macro2::T

let gtype_name = gtype_name.value.unwrap();
let nullable = nullable.found || nullable.value.map(|b| b.value()).unwrap_or(false);
let allow_name_conflict = allow_name_conflict.found
|| allow_name_conflict
.value
.map(|b| b.value())
.unwrap_or(false);

let crate_ident = crate_ident_new();
let refcounted_type_prefix = refcounted_type_prefix(name, &crate_ident);
Expand All @@ -140,6 +147,7 @@ pub fn impl_shared_boxed(input: &syn::DeriveInput) -> syn::Result<proc_macro2::T
Ok(quote! {
impl #crate_ident::subclass::shared::SharedType for #name {
const NAME: &'static ::core::primitive::str = #gtype_name;
const ALLOW_NAME_CONFLICT: bool = #allow_name_conflict;

type RefCountedType = #refcounted_type;

Expand Down
1 change: 1 addition & 0 deletions glib/src/boxed_any_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ mod imp {
#[glib::object_subclass]
impl ObjectSubclass for BoxedAnyObject {
const NAME: &'static str = "BoxedAnyObject";
const ALLOW_NAME_CONFLICT: bool = true;
type Type = super::BoxedAnyObject;
}
impl Default for BoxedAnyObject {
Expand Down
48 changes: 41 additions & 7 deletions glib/src/subclass/boxed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,21 @@ pub trait BoxedType: StaticType + Clone + Sized + 'static {
///
/// This must be unique in the whole process.
const NAME: &'static str;

// rustdoc-stripper-ignore-next
/// Allow name conflicts for this boxed type.
///
/// By default, trying to register a type with a name that was registered before will panic. If
/// this is set to `true` then a new name will be selected by appending a counter.
///
/// This is useful for defining new types in Rust library crates that might be linked multiple
/// times in the same process.
///
/// A consequence of setting this to `true` is that it's not guaranteed that
/// `glib::Type::from_name(Self::NAME).unwrap() == Self::static_type()`.
///
/// Optional.
const ALLOW_NAME_CONFLICT: bool = false;
}

// rustdoc-stripper-ignore-next
Expand All @@ -45,13 +60,32 @@ pub fn register_boxed_type<T: BoxedType>() -> crate::Type {
unsafe {
use std::ffi::CString;

let type_name = CString::new(T::NAME).unwrap();
assert_eq!(
gobject_ffi::g_type_from_name(type_name.as_ptr()),
gobject_ffi::G_TYPE_INVALID,
"Type {} has already been registered",
type_name.to_str().unwrap()
);
let type_name = if T::ALLOW_NAME_CONFLICT {
let mut i = 0;
loop {
let type_name = CString::new(if i == 0 {
T::NAME.to_string()
} else {
format!("{}-{}", T::NAME, i)
})
.unwrap();
if gobject_ffi::g_type_from_name(type_name.as_ptr()) == gobject_ffi::G_TYPE_INVALID
{
break type_name;
}
i += 1;
}
} else {
let type_name = CString::new(T::NAME).unwrap();
assert_eq!(
gobject_ffi::g_type_from_name(type_name.as_ptr()),
gobject_ffi::G_TYPE_INVALID,
"Type {} has already been registered",
type_name.to_str().unwrap()
);

type_name
};

let type_ = crate::Type::from_glib(gobject_ffi::g_boxed_type_register_static(
type_name.as_ptr(),
Expand Down
Loading

0 comments on commit 4101f56

Please sign in to comment.