diff --git a/src/Libs/GObject-2.0/Internal/BoxedWrapper.cs b/src/Libs/GObject-2.0/Internal/BoxedWrapper.cs index 373efac44..0f499b91e 100644 --- a/src/Libs/GObject-2.0/Internal/BoxedWrapper.cs +++ b/src/Libs/GObject-2.0/Internal/BoxedWrapper.cs @@ -8,34 +8,7 @@ public class BoxedWrapper { public static object WrapHandle(IntPtr handle, bool ownsHandle, Type gtype) { - System.Type trueType = TypeDictionary.GetSystemType(gtype); - - if (handle == IntPtr.Zero) - throw new NullReferenceException($"Failed to wrap handle as type <{trueType}>. Null handle passed to WrapHandle."); - - // Get constructor for the true type - var ctr = GetBoxedConstructor(trueType); - - if (ctr is null) - throw new Exception($"Type {trueType} does not define an IntPtr constructor. This could mean improperly defined bindings"); - - var result = ctr.Invoke(new object[] { handle, ownsHandle }); - - if (result == null) - throw new Exception($"Type {trueType}'s factory method returned a null object. This could mean improperly defined bindings"); - - return result; - } - - private static ConstructorInfo? GetBoxedConstructor(System.Type type) - { - // Create using 'IntPtr, ownsHandle' constructor - ConstructorInfo? ctor = type.GetConstructor( - System.Reflection.BindingFlags.NonPublic - | System.Reflection.BindingFlags.Public - | System.Reflection.BindingFlags.Instance, - null, new[] { typeof(IntPtr), typeof(bool) }, null - ); - return ctor; + //TODO: REMOVE BOXED WRAPPER + return InstanceFactory.Create(handle, ownsHandle); } } diff --git a/src/Libs/GObject-2.0/Internal/DemoTypeRegistration.cs b/src/Libs/GObject-2.0/Internal/DemoTypeRegistration.cs new file mode 100644 index 000000000..998bfef7a --- /dev/null +++ b/src/Libs/GObject-2.0/Internal/DemoTypeRegistration.cs @@ -0,0 +1,28 @@ +using System; +using System.Diagnostics; +using System.Linq; +using System.Reflection.Metadata; +using System.Runtime.InteropServices; + +namespace GObject.Internal; + +internal class DemoTypeRegistration +{ + internal static void RegisterTypes() + { + Register(Binding.GetGType, OSPlatform.Linux, OSPlatform.OSX, OSPlatform.Windows); + } + + private static void Register(Func getType, params OSPlatform[] supportedPlatforms) where T : Constructable + { + try + { + if (supportedPlatforms.Any(RuntimeInformation.IsOSPlatform)) + InstanceFactory.AddFactoryForType(getType(), T.Create); + } + catch (System.Exception e) + { + Debug.WriteLine($"Could not register type '{nameof(T)}': {e.Message}"); + } + } +} diff --git a/src/Libs/GObject-2.0/Internal/InstanceFactory.cs b/src/Libs/GObject-2.0/Internal/InstanceFactory.cs new file mode 100644 index 000000000..6a2c295b9 --- /dev/null +++ b/src/Libs/GObject-2.0/Internal/InstanceFactory.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace GObject.Internal; + +public interface Constructable +{ + public static abstract object Create(IntPtr handle, bool ownedRef); +} + +/// +/// Creates new instances of classes and records +/// +internal class InstanceFactory +{ + private static readonly Dictionary> Factories = new(); + + public static object Create(IntPtr handle, bool ownedRef) + { + var gtype = GetTypeFromInstance(handle); + + Debug.Assert( + condition: Functions.TypeName(gtype.Value).ConvertToString() == Functions.TypeNameFromInstance(new TypeInstanceUnownedHandle(handle)).ConvertToString(), + message: "GType name of instance and class do not match" + ); + + var factory = GetFactory(gtype); + + return factory(handle, ownedRef); + } + + public static void AddFactoryForType(Type type, Func factory) + { + Factories[type] = factory; + } + + private static Func GetFactory(Type gtype) + { + if (Factories.TryGetValue(gtype, out var factory)) + return factory; + + do + { + var parentType = new Type(Functions.TypeParent(gtype)); + if (parentType.Value is (nuint) BasicType.Invalid or (nuint) BasicType.None) + throw new Exception("Could not retrieve parent type - is the typeid valid?"); + + if (!Factories.TryGetValue(gtype, out var parentFactory)) + continue; + + //Store parent factory for later use + AddFactoryForType(gtype, parentFactory); + + } while(true); + } + + private static unsafe Type GetTypeFromInstance(IntPtr handle) + { + var gclass = Unsafe.AsRef((void*) handle).GClass; + var gtype = Unsafe.AsRef((void*) gclass).GType; + + if (gtype == 0) + throw new Exception("Could not retrieve type from class struct - is the struct valid?"); + + return new Type(gtype); + } +} diff --git a/src/Libs/GObject-2.0/Internal/ObjectWrapper.cs b/src/Libs/GObject-2.0/Internal/ObjectWrapper.cs index 48640d48b..cbdd96e90 100644 --- a/src/Libs/GObject-2.0/Internal/ObjectWrapper.cs +++ b/src/Libs/GObject-2.0/Internal/ObjectWrapper.cs @@ -8,43 +8,22 @@ namespace GObject.Internal; public static class ObjectWrapper { - public static T? WrapNullableHandle(IntPtr handle, bool ownedRef) where T : class, IHandle + public static T? WrapNullableHandle(IntPtr handle, bool ownedRef) where T : GObject.Object, IHandle { return handle == IntPtr.Zero ? null : WrapHandle(handle, ownedRef); } - public static T WrapHandle(IntPtr handle, bool ownedRef) where T : class, IHandle + public static T WrapHandle(IntPtr handle, bool ownedRef) where T : GObject.Object, IHandle { - Debug.Assert( - condition: typeof(T).IsClass && typeof(T).IsAssignableTo(typeof(GObject.Object)), - message: "Type 'T' must be a GObject-based class" - ); - if (handle == IntPtr.Zero) throw new NullReferenceException($"Failed to wrap handle as type <{typeof(T).FullName}>. Null handle passed to WrapHandle."); if (ObjectMapper.TryGetObject(handle, out T? obj)) return obj; - //In case of classes prefer the type reported by the gobject type system over - //the expected type as often an API returns a less derived class in it's public - //API then the actual one. - Type gtype = GetTypeFromInstance(handle); - - Debug.Assert( - condition: Functions.TypeName(gtype.Value).ConvertToString() == Functions.TypeNameFromInstance(new TypeInstanceUnownedHandle(handle)).ConvertToString(), - message: "GType name of instance and class do not match" - ); - - System.Type trueType = TypeDictionary.GetSystemType(gtype); - ConstructorInfo? ctor = GetObjectConstructor(trueType); - - if (ctor == null) - throw new Exception($"Type {typeof(T).FullName} does not define an IntPtr constructor. This could mean improperly defined bindings"); - - return (T) ctor.Invoke(new object[] { handle, ownedRef }); + return (T) InstanceFactory.Create(handle, ownedRef); } public static T? WrapNullableInterfaceHandle(IntPtr handle, bool ownedRef) where T : class, IHandle @@ -78,17 +57,6 @@ public static T WrapInterfaceHandle(IntPtr handle, bool ownedRef) where T : c return (T) ctor.Invoke(new object[] { handle, ownedRef }); } - private static unsafe Type GetTypeFromInstance(IntPtr handle) - { - var gclass = Unsafe.AsRef((void*) handle).GClass; - var gtype = Unsafe.AsRef((void*) gclass).GType; - - if (gtype == 0) - throw new Exception("Could not retrieve type from class struct - is the struct valid?"); - - return new Type(gtype); - } - private static ConstructorInfo? GetObjectConstructor(System.Type type) { // Create using 'IntPtr' constructor diff --git a/src/Libs/GObject-2.0/Internal/TypeDictionary.cs b/src/Libs/GObject-2.0/Internal/TypeDictionary.cs index d2e52aa5e..85c1beec8 100644 --- a/src/Libs/GObject-2.0/Internal/TypeDictionary.cs +++ b/src/Libs/GObject-2.0/Internal/TypeDictionary.cs @@ -47,7 +47,6 @@ public enum BasicType public static class TypeDictionary { private static readonly Dictionary _systemTypeDict = new(); - private static readonly Dictionary _reverseTypeDict = new(); /// /// Add a new mapping of (System.Type, GObject.Type) to the type dictionary. @@ -63,7 +62,6 @@ public static void Add(System.Type systemType, Type type) ); _systemTypeDict[systemType] = type; - _reverseTypeDict[type] = systemType; } /// @@ -87,39 +85,7 @@ internal static Type GetGType(System.Type type) return _systemTypeDict[type]; } - /// - /// For a given gtype, retrieve the corresponding managed type. - /// - /// A type from the GType type system - /// The equivalent managed type - internal static System.Type GetSystemType(Type gtype) - { - if (_reverseTypeDict.TryGetValue(gtype, out System.Type? sysType)) - return sysType; - - // If gtype is not in the type dictionary, walk up the - // tree until we find a type that is. As all objects are - // descended from GObject, we will eventually find a parent - // type that is registered. - - while (!_reverseTypeDict.TryGetValue(gtype, out sysType)) - { - gtype = new Type(Functions.TypeParent(gtype.Value)); - if (gtype.Value == (nuint) BasicType.Invalid || - gtype.Value == (nuint) BasicType.None) - throw new Exception("Could not retrieve parent type - is the typeid valid?"); - } - - // Store for future lookups - _reverseTypeDict[gtype] = sysType; - - return sysType; - } - // These may be unneeded - keep for now - internal static bool ContainsGType(Type gtype) - => _reverseTypeDict.ContainsKey(gtype); - internal static bool ContainsSystemType(System.Type type) => _systemTypeDict.ContainsKey(type); }