Skip to content

Commit

Permalink
Try to avoid factory methods.
Browse files Browse the repository at this point in the history
It can not be avoided completely, du to data structures of GDK.Event. Hopefully the workaround gets obsolete once we support GDK4.
  • Loading branch information
badcel committed Aug 7, 2021
1 parent 2161cc3 commit 2e25750
Show file tree
Hide file tree
Showing 9 changed files with 107 additions and 87 deletions.
10 changes: 5 additions & 5 deletions Generator/Convert.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,15 @@ internal static string NativeToManaged(TransferableAnyType transferable, string
{ Type: String } when (transfer == Transfer.None) && (transferable is ReturnValue) => $"GLib.Native.StringHelper.ToStringUtf8({fromParam})",

// Record Conversions (safe handles)
ArrayTypeReference { Type: Record r, TypeReference: { CTypeReference: { IsPointer: true } } } when useSafeHandle => $"{fromParam}.Select(x => {r.Write(Target.Managed, currentNamespace)}.__FactoryNew(x)).ToArray()",
ResolveableTypeReference { Type: Record r, CTypeReference: { IsPointer: true } } when useSafeHandle => $"{r.Write(Target.Managed, currentNamespace)}.__FactoryNew({fromParam})",
ArrayTypeReference { Type: Record r, TypeReference: { CTypeReference: { IsPointer: true } } } when useSafeHandle => $"{fromParam}.Select(x => new {r.Write(Target.Managed, currentNamespace)}(x)).ToArray()",
ResolveableTypeReference { Type: Record r, CTypeReference: { IsPointer: true } } when useSafeHandle => $"new {r.Write(Target.Managed, currentNamespace)}({fromParam})",

// Record Conversions (raw pointers)
ArrayTypeReference { Type: Record r, TypeReference: { CTypeReference: { IsPointer: true } } } when !useSafeHandle => $"{fromParam}.Select(x => {r.Write(Target.Managed, currentNamespace)}.__FactoryNew(new {SafeHandleFromRecord(r)}(x))).ToArray()",
ResolveableTypeReference { Type: Record r, CTypeReference: { IsPointer: true } } when !useSafeHandle => $"{r.Write(Target.Managed, currentNamespace)}.__FactoryNew(new {SafeHandleFromRecord(r)}({fromParam}))",
ArrayTypeReference { Type: Record r, TypeReference: { CTypeReference: { IsPointer: true } } } when !useSafeHandle => $"{fromParam}.Select(x => new {r.Write(Target.Managed, currentNamespace)}(new {SafeHandleFromRecord(r)}(x))).ToArray()",
ResolveableTypeReference { Type: Record r, CTypeReference: { IsPointer: true } } when !useSafeHandle => $"new {r.Write(Target.Managed, currentNamespace)}(new {SafeHandleFromRecord(r)}({fromParam}))",

//Record Conversions without pointers are not working yet
ArrayTypeReference { Type: Record r, TypeReference: { CTypeReference: { IsPointer: false } } } => $"({qualifiedType}[]) {fromParam}.Select(x => {qualifiedType}.__FactoryNew({SafeHandleFromRecord(r, true)}(x))).ToArray();",
ArrayTypeReference { Type: Record r, TypeReference: { CTypeReference: { IsPointer: false } } } => $"({qualifiedType}[]) {fromParam}.Select(x => new {qualifiedType}({SafeHandleFromRecord(r, true)}(x))).ToArray();",
ResolveableTypeReference { Type: Record r, CTypeReference: { IsPointer: false } } => $"({r.Write(Target.Managed, currentNamespace)}) default!; //TODO: Fixme",

// Class Conversions
Expand Down
10 changes: 3 additions & 7 deletions Generator/Templates/record.sbntxt
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,14 @@ namespace {{ namespace.name }}
// Override this to perform additional steps in the constructor
partial void Initialize();

private {{ $record_name }}({{ $safe_handle }} handle)
public {{ $record_name }}(IntPtr ptr) : this(new {{$safe_handle}}(ptr)){ }

public {{ $record_name }}({{ $safe_handle }} handle)
{
_handle = handle;
Initialize();
}

public static {{ $record_name }} __FactoryNew({{ $safe_handle }} handle)
=> new {{ $record_name }}(handle);

public static {{ $record_name }} __FactoryNew(IntPtr ptr)
=> new {{ $record_name }}(new {{ $safe_handle }}(ptr));

// TODO: Default Constructor (allocate in managed memory and free on Dispose?)
// We need to be able to create instances of records with full access to
// fields, e.g. Gdk.Rectangle, Gtk.TreeIter, etc.
Expand Down
2 changes: 1 addition & 1 deletion Libs/GObject-2.0/Classes/Object.Properties.cs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ protected Value GetProperty(string name)
var handle = Native.Value.ManagedHandle.Create();
Native.Object.Instance.Methods.GetProperty(Handle, name, handle);

return Value.__FactoryNew(handle);
return new Value(handle);
}

/// <summary>
Expand Down
68 changes: 68 additions & 0 deletions Libs/GObject-2.0/Native/Classes/BoxedWrapper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.InteropServices;
using GLib;

namespace GObject.Native
{
public class BoxedWrapper
{
public static object WrapHandle(IntPtr handle, 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);

object? result;

if (ctr == null)
{
//If we do not find an constructor we try to find our secret factory method.
//TODO: This is a workaround for Gdk.Event Should get obsolete with GTK4
var methodInfo = GetSecretFactoryMethod(trueType);

if(methodInfo is null)
throw new Exception($"Type {trueType} does not define an IntPtr constructor. This could mean improperly defined bindings");

result = methodInfo.Invoke(null, new object[] { handle });
}
else
{
result = ctr.Invoke(new object[] { handle });
}

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 MethodInfo? GetSecretFactoryMethod(System.Type type)
{
// Create using 'IntPtr' constructor
MethodInfo? ctor = type.GetMethod("__FactoryNew",
System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Static,
null, new[] { typeof(IntPtr) }, null
);
return ctor;
}

private static ConstructorInfo? GetBoxedConstructor(System.Type type)
{
// Create using 'IntPtr' constructor
ConstructorInfo? ctor = type.GetConstructor(
System.Reflection.BindingFlags.NonPublic
| System.Reflection.BindingFlags.Public
| System.Reflection.BindingFlags.Instance,
null, new[] { typeof(IntPtr), }, null
);
return ctor;
}
}
}
44 changes: 0 additions & 44 deletions Libs/GObject-2.0/Native/Classes/RecordWrapper.cs

This file was deleted.

4 changes: 1 addition & 3 deletions Libs/GObject-2.0/Records/Value.cs
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,7 @@ public void Set(object? value)
// method which plays nice with AOT compilation.

// TODO: Should this be GetBoxed/TakeBoxed/DupBoxed?
return RecordWrapper.WrapHandle(Native.Value.Methods.GetBoxed(Handle), new Type(type));

throw new NotSupportedException($"Can't get boxed value. Type '{new Type((nuint)type)}' is not supported.");
return BoxedWrapper.WrapHandle(Native.Value.Methods.GetBoxed(Handle), new Type(type));
}

public Object? GetObject()
Expand Down
50 changes: 26 additions & 24 deletions Libs/Gdk-3.0/Records/Event.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,72 +5,74 @@ namespace Gdk
{
public partial class Event
{
public static Event __FactoryNew(IntPtr ptr)
//TODO: This method is our "secret factory method". it gets called via reflection.
//See BoxedWrapper.cs
private static Event __FactoryNew(IntPtr ptr)
{
var ev = Marshal.PtrToStructure<Native.EventAny.Struct>(ptr);
switch (ev.Type)
{
case EventType.Expose:
return EventExpose.__FactoryNew(ptr);
return new EventExpose(ptr);
case EventType.VisibilityNotify:
return EventVisibility.__FactoryNew(ptr);
return new EventVisibility(ptr);
case EventType.MotionNotify:
return EventMotion.__FactoryNew(ptr);
return new EventMotion(ptr);
case EventType.ButtonPress:
case EventType.TwoButtonPress:
case EventType.ThreeButtonPress:
case EventType.ButtonRelease:
return EventButton.__FactoryNew(ptr);
return new EventButton(ptr);
case EventType.TouchBegin:
return EventTouch.__FactoryNew(ptr);
return new EventTouch(ptr);
case EventType.Scroll:
return EventScroll.__FactoryNew(ptr);
return new EventScroll(ptr);
case EventType.KeyPress:
case EventType.KeyRelease:
return EventKey.__FactoryNew(ptr);
return new EventKey(ptr);
case EventType.EnterNotify:
case EventType.LeaveNotify:
return EventCrossing.__FactoryNew(ptr);
return new EventCrossing(ptr);
case EventType.FocusChange:
return EventFocus.__FactoryNew(ptr);
return new EventFocus(ptr);
case EventType.Configure:
return EventConfigure.__FactoryNew(ptr);
return new EventConfigure(ptr);
case EventType.PropertyNotify:
return EventProperty.__FactoryNew(ptr);
return new EventProperty(ptr);
case EventType.SelectionClear:
case EventType.SelectionNotify:
case EventType.SelectionRequest:
return EventSelection.__FactoryNew(ptr);
return new EventSelection(ptr);
case EventType.OwnerChange:
return EventOwnerChange.__FactoryNew(ptr);
return new EventOwnerChange(ptr);
case EventType.ProximityIn:
case EventType.ProximityOut:
return EventProximity.__FactoryNew(ptr);
return new EventProximity(ptr);
case EventType.DragEnter:
case EventType.DragLeave:
case EventType.DragMotion:
case EventType.DragStatus:
case EventType.DropStart:
case EventType.DropFinished:
return EventDND.__FactoryNew(ptr);
return new EventDND(ptr);
case EventType.WindowState:
return EventWindowState.__FactoryNew(ptr);
return new EventWindowState(ptr);
case EventType.Setting:
return EventSetting.__FactoryNew(ptr);
return new EventSetting(ptr);
case EventType.GrabBroken:
return EventGrabBroken.__FactoryNew(ptr);
return new EventGrabBroken(ptr);
case EventType.TouchpadSwipe:
return EventTouchpadSwipe.__FactoryNew(ptr);
return new EventTouchpadSwipe(ptr);
case EventType.TouchpadPinch:
return EventTouchpadPinch.__FactoryNew(ptr);
return new EventTouchpadPinch(ptr);
case EventType.PadButtonPress:
case EventType.PadButtonRelease:
return EventPadButton.__FactoryNew(ptr);
return new EventPadButton(ptr);
case EventType.PadRing:
case EventType.PadStrip:
return EventPadAxis.__FactoryNew(ptr);
return new EventPadAxis(ptr);
case EventType.PadGroupMode:
return EventPadGroupMode.__FactoryNew(ptr);
return new EventPadGroupMode(ptr);
// Default/Not Implemented
case EventType.Map:
case EventType.Unmap:
Expand Down
4 changes: 2 additions & 2 deletions Libs/Gio-2.0/Classes/DBusConnection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ void Callback(GObject.Object sourceObject, AsyncResult res)
var ret = Native.DBusConnection.Instance.Methods.CallFinish(sourceObject.Handle, (res as GObject.Object).Handle, out var error);
Error.ThrowOnError(error);

tcs.SetResult(Variant.__FactoryNew(ret));
tcs.SetResult(new Variant(ret));
}

//TODO: Use on time CallbackHandler
Expand All @@ -55,7 +55,7 @@ public Variant Call(string busName, string objectPath, string interfaceName, str

Error.ThrowOnError(error);

return Variant.__FactoryNew(ret);
return new Variant(ret);
}

public override void Dispose()
Expand Down
2 changes: 1 addition & 1 deletion Libs/Gtk-3.0/Classes/TreeSelection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ public void GetSelected(out TreeModel model, out TreeIter iter)
Native.TreeSelection.Instance.Methods.GetSelected(Handle, out var modelPtr, iterHandle);

model = ObjectWrapper.WrapHandle<TreeModel>(modelPtr, false);
iter = TreeIter.__FactoryNew(iterHandle);
iter = new TreeIter(iterHandle);
}
}
}

0 comments on commit 2e25750

Please sign in to comment.