Skip to content

Commit

Permalink
Avoid Reflection
Browse files Browse the repository at this point in the history
  • Loading branch information
badcel committed Jan 20, 2024
1 parent f491be2 commit bba3e9b
Show file tree
Hide file tree
Showing 5 changed files with 79 additions and 98 deletions.
31 changes: 2 additions & 29 deletions src/Libs/GObject-2.0/Internal/BoxedWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
27 changes: 27 additions & 0 deletions src/Libs/GObject-2.0/Internal/DemoTypeRegistration.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;

namespace GObject.Internal;

internal class DemoTypeRegistration
{
internal static void RegisterTypes()
{
Register<GObject.Binding>(Binding.GetGType, (ptr, ownedRef) => new GObject.Binding(ptr, ownedRef), OSPlatform.Linux, OSPlatform.OSX, OSPlatform.Windows);
}

private static void Register<T>(Func<nuint> getType, Func<IntPtr, bool, GObject.Object> factory, params OSPlatform[] supportedPlatforms) where T : class
{
try
{
if (supportedPlatforms.Any(RuntimeInformation.IsOSPlatform))
InstanceFactory.AddFactoryForType(getType(), factory);
}
catch (System.Exception e)
{
Debug.WriteLine($"Could not register type '{nameof(T)}': {e.Message}");
}
}
}
47 changes: 47 additions & 0 deletions src/Libs/GObject-2.0/Internal/InstanceFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;

namespace GObject.Internal;

/// <summary>
/// Creates new instances of classes and records
/// </summary>
internal class InstanceFactory
{
private static readonly Dictionary<Type, Func<IntPtr, bool, object>> 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"
);

if (!Factories.TryGetValue(gtype, out var handleFactory))
throw new Exception("Cant create handle for unknown type");

//TODO: What happens if type is not registered. Original TypeDirectory had a fallback until a parent was found?!

return handleFactory(handle, ownedRef);
}

public static void AddFactoryForType(Type type, Func<IntPtr, bool, GObject.Object> factory)
{
Factories[type] = factory;
}

private static unsafe Type GetTypeFromInstance(IntPtr handle)
{
var gclass = Unsafe.AsRef<TypeInstanceData>((void*) handle).GClass;
var gtype = Unsafe.AsRef<TypeClassData>((void*) gclass).GType;

if (gtype == 0)
throw new Exception("Could not retrieve type from class struct - is the struct valid?");

return new Type(gtype);
}
}
38 changes: 3 additions & 35 deletions src/Libs/GObject-2.0/Internal/ObjectWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,43 +8,22 @@ namespace GObject.Internal;

public static class ObjectWrapper
{
public static T? WrapNullableHandle<T>(IntPtr handle, bool ownedRef) where T : class, IHandle
public static T? WrapNullableHandle<T>(IntPtr handle, bool ownedRef) where T : GObject.Object, IHandle
{
return handle == IntPtr.Zero
? null
: WrapHandle<T>(handle, ownedRef);
}

public static T WrapHandle<T>(IntPtr handle, bool ownedRef) where T : class, IHandle
public static T WrapHandle<T>(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<T>(IntPtr handle, bool ownedRef) where T : class, IHandle
Expand Down Expand Up @@ -78,17 +57,6 @@ public static T WrapInterfaceHandle<T>(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<TypeInstanceData>((void*) handle).GClass;
var gtype = Unsafe.AsRef<TypeClassData>((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
Expand Down
34 changes: 0 additions & 34 deletions src/Libs/GObject-2.0/Internal/TypeDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,6 @@ public enum BasicType
public static class TypeDictionary
{
private static readonly Dictionary<System.Type, Type> _systemTypeDict = new();
private static readonly Dictionary<Type, System.Type> _reverseTypeDict = new();

/// <summary>
/// Add a new mapping of (System.Type, GObject.Type) to the type dictionary.
Expand All @@ -63,7 +62,6 @@ public static void Add(System.Type systemType, Type type)
);

_systemTypeDict[systemType] = type;
_reverseTypeDict[type] = systemType;
}

/// <summary>
Expand All @@ -87,39 +85,7 @@ internal static Type GetGType(System.Type type)
return _systemTypeDict[type];
}

/// <summary>
/// For a given gtype, retrieve the corresponding managed type.
/// </summary>
/// <param name="gtype">A type from the GType type system</param>
/// <returns>The equivalent managed type</returns>
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);
}

0 comments on commit bba3e9b

Please sign in to comment.