From 61d241770e6458f3bb7d0973f1363512a73adfb6 Mon Sep 17 00:00:00 2001 From: badcel <1218031+badcel@users.noreply.github.com> Date: Thu, 5 Aug 2021 19:19:10 +0200 Subject: [PATCH] Several fixes - Prefer symbol name for primitive value types over their ctype to detect the correct type as the symbol sometimes is more expressive (e.g. symbolname = "double" ctype = "gpointer") - Differentiate between Bitfields and Enumerations. Bitfields are transferred as ulong and Enumerations as long. They are represented as different types in the gir.rnc file, too. Additionally Bitfields can contain negative values (despite them being of type ulong). Those values must be filtered out as they are convenience functions of C which do not apply for us. - Bitfields/Enumerations need to respect the type references namespace name to avoid false positive matches. --- Generator/Extensions/TypeExtension.cs | 3 +- Generator/Services/Writer/WriterService.cs | 2 +- Generator/Templates/bitfield.sbntxt | 13 +++++ Generator/Templates/enum.sbntxt | 6 +-- .../{enum.member.sbntxt => member.sbntxt} | 0 GirLoader/Input/Model/Bitfield.cs | 23 +++++++++ GirLoader/Input/Model/Namespace.cs | 2 +- GirLoader/Output/Model/Bitfield.cs | 38 +++++++++++++++ GirLoader/Output/Model/BitfieldFactory.cs | 47 +++++++++++++++++++ GirLoader/Output/Model/Enumeration.cs | 15 +++--- GirLoader/Output/Model/EnumerationFactory.cs | 3 +- GirLoader/Output/Model/Namespace.cs | 6 +-- GirLoader/Output/Model/NamespaceFactory.cs | 12 +++-- GirLoader/Output/Model/PrimitiveValueType.cs | 2 +- GirLoader/Output/Model/Type.cs | 3 ++ GirLoader/Output/Module.cs | 1 + Libs/GObject-2.0/Records/Value.cs | 2 +- 17 files changed, 153 insertions(+), 25 deletions(-) create mode 100644 Generator/Templates/bitfield.sbntxt rename Generator/Templates/{enum.member.sbntxt => member.sbntxt} (100%) create mode 100644 GirLoader/Input/Model/Bitfield.cs create mode 100644 GirLoader/Output/Model/Bitfield.cs create mode 100644 GirLoader/Output/Model/BitfieldFactory.cs diff --git a/Generator/Extensions/TypeExtension.cs b/Generator/Extensions/TypeExtension.cs index 5da03dd9a..e7208e709 100644 --- a/Generator/Extensions/TypeExtension.cs +++ b/Generator/Extensions/TypeExtension.cs @@ -12,9 +12,10 @@ internal static string Write(this Type type, Target target, Namespace currentNam { PrimitiveType => type.Name, - // Enumerations do not have a native representation they + // Enumerations/Bitfields do not have a native representation they // always live in the managed namespace (see method GetName) Enumeration e => $"{e.Repository.Namespace.Name}.{e.Name}", + Bitfield e => $"{e.Repository.Namespace.Name}.{e.Name}", ComplexType c when !c.Repository.Namespace.IsForeignTo(currentNamespace) => c.Name, ComplexType c => $"{c.Repository.Namespace.GetName(target)}.{c.Name}", diff --git a/Generator/Services/Writer/WriterService.cs b/Generator/Services/Writer/WriterService.cs index 086fa8cd3..048e88c25 100644 --- a/Generator/Services/Writer/WriterService.cs +++ b/Generator/Services/Writer/WriterService.cs @@ -76,7 +76,7 @@ public void Write(Namespace ns, string outputDir, WriterOptions options) _writeTypesService.Write( projectName: ns.ToCanonicalName(), outputDir: outputDir, - templateName: "enum.sbntxt", + templateName: "bitfield.sbntxt", subfolder: Folder.Managed.Enums, types: ns.Bitfields, @namespace: ns diff --git a/Generator/Templates/bitfield.sbntxt b/Generator/Templates/bitfield.sbntxt new file mode 100644 index 000000000..547fa769f --- /dev/null +++ b/Generator/Templates/bitfield.sbntxt @@ -0,0 +1,13 @@ +{{~ #Bitfields are passed as ulong via the ffi ~}} +using System; + +namespace {{ namespace.name }} +{ + [Flags] + public enum {{ name }} : ulong + { + {{~ for member in members }} + {{ include 'member.sbntxt' member ~}} + {{ end }} + } +} \ No newline at end of file diff --git a/Generator/Templates/enum.sbntxt b/Generator/Templates/enum.sbntxt index 747bb811d..80313f6cb 100644 --- a/Generator/Templates/enum.sbntxt +++ b/Generator/Templates/enum.sbntxt @@ -1,14 +1,12 @@ +{{~ #Enumerations are passed as long via the ffi ~}} using System; namespace {{ namespace.name }} { - {{~ if has_flags ~}} - [Flags] - {{~ end ~}} public enum {{ name }} : long { {{~ for member in members }} - {{ include 'enum.member.sbntxt' member ~}} + {{ include 'member.sbntxt' member ~}} {{ end }} } } \ No newline at end of file diff --git a/Generator/Templates/enum.member.sbntxt b/Generator/Templates/member.sbntxt similarity index 100% rename from Generator/Templates/enum.member.sbntxt rename to Generator/Templates/member.sbntxt diff --git a/GirLoader/Input/Model/Bitfield.cs b/GirLoader/Input/Model/Bitfield.cs new file mode 100644 index 000000000..c0c385dae --- /dev/null +++ b/GirLoader/Input/Model/Bitfield.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Xml.Serialization; + +namespace GirLoader.Input.Model +{ + public class Bitfield + { + [XmlAttribute("name")] + public string? Name { get; set; } + + [XmlAttribute("type", Namespace = "http://www.gtk.org/introspection/c/1.0")] + public string? Type { get; set; } + + [XmlAttribute("type-name", Namespace = "http://www.gtk.org/introspection/glib/1.0")] + public string? TypeName { get; set; } + + [XmlElement("doc")] + public Doc? Doc { get; set; } + + [XmlElement("member")] + public List Members { get; set; } = default!; + } +} diff --git a/GirLoader/Input/Model/Namespace.cs b/GirLoader/Input/Model/Namespace.cs index 7ddf0a1a9..506be0d49 100644 --- a/GirLoader/Input/Model/Namespace.cs +++ b/GirLoader/Input/Model/Namespace.cs @@ -27,7 +27,7 @@ public class Namespace public List Interfaces { get; set; } = new(); [XmlElement("bitfield")] - public List Bitfields { get; set; } = new(); + public List Bitfields { get; set; } = new(); [XmlElement("enumeration")] public List Enumerations { get; set; } = new(); diff --git a/GirLoader/Output/Model/Bitfield.cs b/GirLoader/Output/Model/Bitfield.cs new file mode 100644 index 000000000..65988e0e5 --- /dev/null +++ b/GirLoader/Output/Model/Bitfield.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.Linq; +using GirLoader.Helper; + +namespace GirLoader.Output.Model +{ + public class Bitfield : ComplexType + { + public IEnumerable Members { get; } + + public Bitfield(Repository repository, CType? cType, TypeName originalName, TypeName name, IEnumerable members) : base(repository, cType, name, originalName) + { + Members = members; + } + + internal override IEnumerable GetTypeReferences() + => Members.SelectMany(x => x.GetTypeReferences()); + + internal override bool GetIsResolved() + => Members.All(x => x.GetIsResolved()); + + internal override bool Matches(TypeReference typeReference) + { + if (typeReference.SymbolNameReference is not null) + { + var namespaceOk = typeReference.SymbolNameReference.NamespaceName == Repository.Namespace.Name + || typeReference.SymbolNameReference.NamespaceName == null; + + return namespaceOk && typeReference.SymbolNameReference.SymbolName == OriginalName; + } + + if (typeReference.CTypeReference is not null) + return typeReference.CTypeReference.CType == CType; + + return false; + } + } +} diff --git a/GirLoader/Output/Model/BitfieldFactory.cs b/GirLoader/Output/Model/BitfieldFactory.cs new file mode 100644 index 000000000..8d303e4b4 --- /dev/null +++ b/GirLoader/Output/Model/BitfieldFactory.cs @@ -0,0 +1,47 @@ +using System; +using System.Linq; + +namespace GirLoader.Output.Model +{ + internal class BitfieldFactory + { + private readonly MemberFactory _memberFactory; + + public BitfieldFactory(MemberFactory memberFactory) + { + _memberFactory = memberFactory; + } + + public Bitfield Create(Input.Model.Bitfield bitfield, Repository repository) + { + if (bitfield.Name is null) + throw new Exception("Bitfield has no name"); + + if (bitfield.Type is null) + throw new Exception("Bitfield is missing a type"); + + var members = bitfield.Members.Select(_memberFactory.Create).ToList(); + + bool ValueIsULong(Member member) + { + // Bitfields are transfered as ulong and thus do not support negative values. + // Negative values can occur as C convenience members as a "mask". + // Those can safely be should be ignored. + + if (ulong.TryParse(member.Value, out var _)) + return true; + + Log.Verbose($"{nameof(Bitfield)} - {member.OriginalName} ignored because value is no ulong: {member.Value}"); + return false; + } + + return new Bitfield( + repository: repository, + originalName: new TypeName(bitfield.Name), + name: new TypeName(bitfield.Name), + members: members.Where(ValueIsULong), + cType: new CType(bitfield.Type) + ); + } + } +} diff --git a/GirLoader/Output/Model/Enumeration.cs b/GirLoader/Output/Model/Enumeration.cs index e62057783..23d53be0e 100644 --- a/GirLoader/Output/Model/Enumeration.cs +++ b/GirLoader/Output/Model/Enumeration.cs @@ -6,12 +6,10 @@ namespace GirLoader.Output.Model { public class Enumeration : ComplexType { - public bool HasFlags { get; } public IEnumerable Members { get; } - public Enumeration(Repository repository, CType? cType, TypeName originalName, TypeName name, bool hasFlags, IEnumerable members) : base(repository, cType, name, originalName) + public Enumeration(Repository repository, CType? cType, TypeName originalName, TypeName name, IEnumerable members) : base(repository, cType, name, originalName) { - HasFlags = hasFlags; Members = members; } @@ -23,12 +21,17 @@ internal override bool GetIsResolved() internal override bool Matches(TypeReference typeReference) { + if (typeReference.SymbolNameReference is not null) + { + var namespaceOk = typeReference.SymbolNameReference.NamespaceName == Repository.Namespace.Name + || typeReference.SymbolNameReference.NamespaceName == null; + + return namespaceOk && typeReference.SymbolNameReference.SymbolName == OriginalName; + } + if (typeReference.CTypeReference is not null) return typeReference.CTypeReference.CType == CType; - if (typeReference.SymbolNameReference is not null) - return typeReference.SymbolNameReference.SymbolName == OriginalName; - return false; } } diff --git a/GirLoader/Output/Model/EnumerationFactory.cs b/GirLoader/Output/Model/EnumerationFactory.cs index 7c9f69dfd..f13286f64 100644 --- a/GirLoader/Output/Model/EnumerationFactory.cs +++ b/GirLoader/Output/Model/EnumerationFactory.cs @@ -12,7 +12,7 @@ public EnumerationFactory(MemberFactory memberFactory) _memberFactory = memberFactory; } - public Enumeration Create(Input.Model.Enum @enum, Repository repository, bool hasFlags) + public Enumeration Create(Input.Model.Enum @enum, Repository repository) { if (@enum.Name is null) throw new Exception("Enum has no name"); @@ -24,7 +24,6 @@ public Enumeration Create(Input.Model.Enum @enum, Repository repository, bool ha repository: repository, originalName: new TypeName(@enum.Name), name: new TypeName(@enum.Name), - hasFlags: hasFlags, members: @enum.Members.Select(_memberFactory.Create).ToList(), cType: new CType(@enum.Type) ); diff --git a/GirLoader/Output/Model/Namespace.cs b/GirLoader/Output/Model/Namespace.cs index ad6ccb03f..2ad4e9e24 100644 --- a/GirLoader/Output/Model/Namespace.cs +++ b/GirLoader/Output/Model/Namespace.cs @@ -28,8 +28,8 @@ public class Namespace private readonly List _enumerations = new(); public IEnumerable Enumerations => _enumerations; - private readonly List _bitfields = new(); - public IEnumerable Bitfields => _bitfields; + private readonly List _bitfields = new(); + public IEnumerable Bitfields => _bitfields; private readonly List _interfaces = new(); public IEnumerable Interfaces => _interfaces; @@ -69,7 +69,7 @@ internal void AddClass(Class @class) internal void AddEnumeration(Enumeration enumeration) => _enumerations.Add(enumeration); - internal void AddBitfield(Enumeration enumeration) + internal void AddBitfield(Bitfield enumeration) => _bitfields.Add(enumeration); internal void AddInterface(Interface @interface) diff --git a/GirLoader/Output/Model/NamespaceFactory.cs b/GirLoader/Output/Model/NamespaceFactory.cs index 653ddeda8..e6a188bf0 100644 --- a/GirLoader/Output/Model/NamespaceFactory.cs +++ b/GirLoader/Output/Model/NamespaceFactory.cs @@ -9,18 +9,20 @@ internal class NamespaceFactory private readonly AliasFactory _aliasFactory; private readonly CallbackFactory _callbackFactory; private readonly EnumerationFactory _enumerationFactory; + private readonly BitfieldFactory _bitfieldFactory; private readonly InterfaceFactory _interfaceFactory; private readonly RecordFactory _recordFactory; private readonly MethodFactory _methodFactory; private readonly ConstantFactory _constantFactory; private readonly UnionFactory _unionFactory; - public NamespaceFactory(ClassFactory classFactory, AliasFactory aliasFactory, CallbackFactory callbackFactory, EnumerationFactory enumerationFactory, InterfaceFactory interfaceFactory, RecordFactory recordFactory, MethodFactory methodFactory, ConstantFactory constantFactory, UnionFactory unionFactory) + public NamespaceFactory(ClassFactory classFactory, AliasFactory aliasFactory, CallbackFactory callbackFactory, EnumerationFactory enumerationFactory, BitfieldFactory bitfieldFactory,InterfaceFactory interfaceFactory, RecordFactory recordFactory, MethodFactory methodFactory, ConstantFactory constantFactory, UnionFactory unionFactory) { _classFactory = classFactory; _aliasFactory = aliasFactory; _callbackFactory = callbackFactory; _enumerationFactory = enumerationFactory; + _bitfieldFactory = bitfieldFactory; _interfaceFactory = interfaceFactory; _recordFactory = recordFactory; _methodFactory = methodFactory; @@ -85,13 +87,13 @@ private void SetCallbacks(Repository repository, IEnumerable enumerations) { foreach (Input.Model.Enum @enum in enumerations) - repository.Namespace.AddEnumeration(_enumerationFactory.Create(@enum, repository, false)); + repository.Namespace.AddEnumeration(_enumerationFactory.Create(@enum, repository)); } - private void SetBitfields(Repository repository, IEnumerable enumerations) + private void SetBitfields(Repository repository, IEnumerable bitfields) { - foreach (Input.Model.Enum @enum in enumerations) - repository.Namespace.AddBitfield(_enumerationFactory.Create(@enum, repository, true)); + foreach (Input.Model.Bitfield bitfield in bitfields) + repository.Namespace.AddBitfield(_bitfieldFactory.Create(bitfield, repository)); } private void SetInterfaces(Repository repository, IEnumerable interfaces) diff --git a/GirLoader/Output/Model/PrimitiveValueType.cs b/GirLoader/Output/Model/PrimitiveValueType.cs index d761b4b9d..b4d03c729 100644 --- a/GirLoader/Output/Model/PrimitiveValueType.cs +++ b/GirLoader/Output/Model/PrimitiveValueType.cs @@ -12,8 +12,8 @@ internal override bool Matches(TypeReference typeReference) { return typeReference switch { - { CTypeReference: { } cr } => cr.CType == CType, { SymbolNameReference: { SymbolName: { } sn } } => sn.Value == CType?.Value, + { CTypeReference: { } cr } => cr.CType == CType, _ => throw new Exception($"Can't match {GetType().Name} with {nameof(TypeReference)} {typeReference}") }; } diff --git a/GirLoader/Output/Model/Type.cs b/GirLoader/Output/Model/Type.cs index 1d0bbaf26..736f13e73 100644 --- a/GirLoader/Output/Model/Type.cs +++ b/GirLoader/Output/Model/Type.cs @@ -27,5 +27,8 @@ public string GetMetadataString(string key) internal abstract bool Matches(TypeReference typeReference); internal abstract IEnumerable GetTypeReferences(); internal abstract bool GetIsResolved(); + + public override string ToString() + => Name; } } diff --git a/GirLoader/Output/Module.cs b/GirLoader/Output/Module.cs index 59f2014db..e981f8a2f 100644 --- a/GirLoader/Output/Module.cs +++ b/GirLoader/Output/Module.cs @@ -16,6 +16,7 @@ namespace GirLoader.Output [Register(typeof(Model.ParameterListFactory))] [Register(typeof(Model.CallbackFactory))] [Register(typeof(Model.EnumerationFactory))] + [Register(typeof(Model.BitfieldFactory))] [Register(typeof(Model.InterfaceFactory))] [Register(typeof(Model.RecordFactory))] [Register(typeof(Model.MethodFactory))] diff --git a/Libs/GObject-2.0/Records/Value.cs b/Libs/GObject-2.0/Records/Value.cs index 97efb2959..7e4360555 100644 --- a/Libs/GObject-2.0/Records/Value.cs +++ b/Libs/GObject-2.0/Records/Value.cs @@ -171,7 +171,7 @@ public void Set(object? value) public long GetLong() => Native.Value.Methods.GetLong(Handle); public double GetDouble() => Native.Value.Methods.GetDouble(Handle); public float GetFloat() => Native.Value.Methods.GetFloat(Handle); - public long GetFlags() => Native.Value.Methods.GetFlags(Handle); + public ulong GetFlags() => Native.Value.Methods.GetFlags(Handle); public long GetEnum() => Native.Value.Methods.GetEnum(Handle); public string? GetString() => StringHelper.ToNullableStringUtf8(Native.Value.Methods.GetString(Handle));