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 7e5a865 commit 60c8b63
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 36 deletions.
2 changes: 2 additions & 0 deletions src/Libs/GObject-2.0/Internal/BoxedWrapper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ public class BoxedWrapper
{
public static object WrapHandle(IntPtr handle, bool ownsHandle, Type gtype)
{
return InstanceFactory.Create(handle, ownsHandle);

System.Type trueType = TypeDictionary.GetSystemType(gtype);

if (handle == IntPtr.Zero)
Expand Down
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}");
}
}
}
48 changes: 48 additions & 0 deletions src/Libs/GObject-2.0/Internal/InstanceFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace GObject.Internal;

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");

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 klassPtr = Unsafe.AsRef<TypeInstanceData>((void*) handle).GClass;
var typeId = Unsafe.AsRef<TypeClassData>((void*) klassPtr).GType;


TypeInstanceData instance = Marshal.PtrToStructure<TypeInstanceData>(handle);
TypeClassData klass = Marshal.PtrToStructure<TypeClassData>(instance.GClass);
var typeid = klass.GType;

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

return new Type(typeid);
}
}
39 changes: 3 additions & 36 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,18 +57,6 @@ public static T WrapInterfaceHandle<T>(IntPtr handle, bool ownedRef) where T : c
return (T) ctor.Invoke(new object[] { handle, ownedRef });
}

private static Type GetTypeFromInstance(IntPtr handle)
{
TypeInstanceData instance = Marshal.PtrToStructure<TypeInstanceData>(handle);
TypeClassData klass = Marshal.PtrToStructure<TypeClassData>(instance.GClass);
var typeid = klass.GType;

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

return new Type(typeid);
}

private static ConstructorInfo? GetObjectConstructor(System.Type type)
{
// Create using 'IntPtr' constructor
Expand Down

0 comments on commit 60c8b63

Please sign in to comment.