From 2e2575061fca2cbdc7f359195e59ec7411ed998b Mon Sep 17 00:00:00 2001 From: badcel <1218031+badcel@users.noreply.github.com> Date: Sat, 7 Aug 2021 18:55:23 +0200 Subject: [PATCH] Try to avoid factory methods. It can not be avoided completely, du to data structures of GDK.Event. Hopefully the workaround gets obsolete once we support GDK4. --- Generator/Convert.cs | 10 +-- Generator/Templates/record.sbntxt | 10 +-- Libs/GObject-2.0/Classes/Object.Properties.cs | 2 +- .../Native/Classes/BoxedWrapper.cs | 68 +++++++++++++++++++ .../Native/Classes/RecordWrapper.cs | 44 ------------ Libs/GObject-2.0/Records/Value.cs | 4 +- Libs/Gdk-3.0/Records/Event.cs | 50 +++++++------- Libs/Gio-2.0/Classes/DBusConnection.cs | 4 +- Libs/Gtk-3.0/Classes/TreeSelection.cs | 2 +- 9 files changed, 107 insertions(+), 87 deletions(-) create mode 100644 Libs/GObject-2.0/Native/Classes/BoxedWrapper.cs delete mode 100644 Libs/GObject-2.0/Native/Classes/RecordWrapper.cs diff --git a/Generator/Convert.cs b/Generator/Convert.cs index 16d19a6af..5065ff904 100644 --- a/Generator/Convert.cs +++ b/Generator/Convert.cs @@ -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 diff --git a/Generator/Templates/record.sbntxt b/Generator/Templates/record.sbntxt index 2fe1ac1de..f6bdc8527 100644 --- a/Generator/Templates/record.sbntxt +++ b/Generator/Templates/record.sbntxt @@ -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. diff --git a/Libs/GObject-2.0/Classes/Object.Properties.cs b/Libs/GObject-2.0/Classes/Object.Properties.cs index bb5e67ccc..ce9327ef0 100644 --- a/Libs/GObject-2.0/Classes/Object.Properties.cs +++ b/Libs/GObject-2.0/Classes/Object.Properties.cs @@ -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); } /// diff --git a/Libs/GObject-2.0/Native/Classes/BoxedWrapper.cs b/Libs/GObject-2.0/Native/Classes/BoxedWrapper.cs new file mode 100644 index 000000000..942898d75 --- /dev/null +++ b/Libs/GObject-2.0/Native/Classes/BoxedWrapper.cs @@ -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; + } + } +} diff --git a/Libs/GObject-2.0/Native/Classes/RecordWrapper.cs b/Libs/GObject-2.0/Native/Classes/RecordWrapper.cs deleted file mode 100644 index 7743b92bd..000000000 --- a/Libs/GObject-2.0/Native/Classes/RecordWrapper.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Diagnostics; -using System.Reflection; -using System.Runtime.InteropServices; -using GLib; - -namespace GObject.Native -{ - public class RecordWrapper - { - 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 - MethodInfo? factory = GetRecordFactory(trueType); - - if (factory == null) - throw new Exception($"Type {trueType} does not define an IntPtr constructor. This could mean improperly defined bindings"); - - object? result = factory.Invoke(null, 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? GetRecordFactory(System.Type type) - { - // Create using 'IntPtr' constructor - MethodInfo? factory = type.GetMethod("__FactoryNew", - System.Reflection.BindingFlags.NonPublic - | System.Reflection.BindingFlags.Public - | System.Reflection.BindingFlags.Static, - null, new[] { typeof(IntPtr) }, null - ); - return factory; - } - } -} diff --git a/Libs/GObject-2.0/Records/Value.cs b/Libs/GObject-2.0/Records/Value.cs index 95ca3d448..173396547 100644 --- a/Libs/GObject-2.0/Records/Value.cs +++ b/Libs/GObject-2.0/Records/Value.cs @@ -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() diff --git a/Libs/Gdk-3.0/Records/Event.cs b/Libs/Gdk-3.0/Records/Event.cs index b21700d37..f9cfa8873 100644 --- a/Libs/Gdk-3.0/Records/Event.cs +++ b/Libs/Gdk-3.0/Records/Event.cs @@ -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(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: diff --git a/Libs/Gio-2.0/Classes/DBusConnection.cs b/Libs/Gio-2.0/Classes/DBusConnection.cs index fccf4e206..d52988250 100644 --- a/Libs/Gio-2.0/Classes/DBusConnection.cs +++ b/Libs/Gio-2.0/Classes/DBusConnection.cs @@ -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 @@ -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() diff --git a/Libs/Gtk-3.0/Classes/TreeSelection.cs b/Libs/Gtk-3.0/Classes/TreeSelection.cs index 4227f2bee..25401845a 100644 --- a/Libs/Gtk-3.0/Classes/TreeSelection.cs +++ b/Libs/Gtk-3.0/Classes/TreeSelection.cs @@ -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(modelPtr, false); - iter = TreeIter.__FactoryNew(iterHandle); + iter = new TreeIter(iterHandle); } } }