diff --git a/src/bin/eolian_mono/eolian/mono/klass.hh b/src/bin/eolian_mono/eolian/mono/klass.hh
index 6ac4aa1c3c..8b018f6413 100644
--- a/src/bin/eolian_mono/eolian/mono/klass.hh
+++ b/src/bin/eolian_mono/eolian/mono/klass.hh
@@ -196,6 +196,17 @@ struct klass
if (!generate_fields(sink, cls, concrete_cxt))
return false;
+ if (!as_generator
+ (
+ scope_tab << "/// Constructor to be used when objects are expected to be constructed from native code.\n"
+ << scope_tab << "/// Tag struct storing the native handle of the object being constructed.\n"
+ << scope_tab << "private " << concrete_name << "(ConstructingHandle ch) : base(ch)\n"
+ << scope_tab << "{\n"
+ << scope_tab << "}\n\n"
+ )
+ .generate(sink, attributes::unused, concrete_cxt))
+ return false;
+
if (!as_generator
(
scope_tab << "[System.Runtime.InteropServices.DllImport(" << context_find_tag(concrete_cxt).actual_library_name(cls.filename)
@@ -246,7 +257,7 @@ struct klass
).generate(sink, attributes::unused, concrete_cxt))
return false;
- if(!generate_native_inherit_class(sink, cls, change_indentation(indent.inc(), context)))
+ if(!generate_native_inherit_class(sink, cls, change_indentation(indent.inc(), concrete_cxt)))
return true;
if(!as_generator("}\n").generate(sink, attributes::unused, concrete_cxt)) return false;
@@ -318,7 +329,7 @@ struct klass
).generate(sink, attributes::unused, inherit_cxt))
return false;
- if(!generate_native_inherit_class(sink, cls, change_indentation(indent.inc(), context)))
+ if(!generate_native_inherit_class(sink, cls, change_indentation(indent.inc(), inherit_cxt)))
return true;
if(!as_generator("}\n").generate(sink, attributes::unused, inherit_cxt)) return false;
@@ -357,7 +368,7 @@ struct klass
(
indent << lit("/// Wrapper for native methods and virtual method delegates.\n")
<< indent << "/// For internal use by generated code only.\n"
- << indent << "public " << (root ? "" : "new " ) << "class " << native_inherit_name << " " << (root ? " : Efl.Eo.NativeClass" : (": " + base_name)) <<"\n"
+ << indent << "public new class " << native_inherit_name << " : " << (root ? "Efl.Eo.EoWrapper.NativeMethods" : base_name) << "\n"
<< indent << "{\n"
).generate(sink, attributes::unused, inative_cxt))
return false;
@@ -396,7 +407,7 @@ struct klass
).generate(sink, attributes::unused, inative_cxt))
return false;
- if(!root)
+ if (!root || context_find_tag(context).current_wrapper_kind != class_context::concrete)
if(!as_generator(indent << scope_tab << scope_tab << "descs.AddRange(base.GetEoOps(type));\n").generate(sink, attributes::unused, inative_cxt))
return false;
@@ -492,6 +503,11 @@ struct klass
<< (*(scope_tab << scope_tab << constructor_invocation << "\n"))
<< scope_tab << scope_tab << "FinishInstantiation();\n"
<< scope_tab << "}\n\n"
+ << scope_tab << "/// Constructor to be used when objects are expected to be constructed from native code.\n"
+ << scope_tab << "/// Tag struct storing the native handle of the object being constructed.\n"
+ << scope_tab << "protected " << inherit_name << "(ConstructingHandle ch) : base(ch)\n"
+ << scope_tab << "{\n"
+ << scope_tab << "}\n\n"
<< scope_tab << "/// Initializes a new instance of the class.\n"
<< scope_tab << "/// Internal usage: Constructs an instance from a native pointer. This is used when interacting with C code and should not be used directly.\n"
<< scope_tab << "/// The native pointer to be wrapped.\n"
diff --git a/src/bindings/mono/eo_mono/EoWrapper.cs b/src/bindings/mono/eo_mono/EoWrapper.cs
index b6ea619923..7e512acf56 100644
--- a/src/bindings/mono/eo_mono/EoWrapper.cs
+++ b/src/bindings/mono/eo_mono/EoWrapper.cs
@@ -2,6 +2,7 @@ using System;
using System.Runtime.InteropServices;
using System.Runtime.CompilerServices;
using System.Threading;
+using System.Reflection;
namespace Efl
{
@@ -18,6 +19,33 @@ public abstract class EoWrapper : IWrapper, IDisposable
private static Efl.EventCb ownershipUniqueDelegate = new Efl.EventCb(OwnershipUniqueCallback);
private static Efl.EventCb ownershipSharedDelegate = new Efl.EventCb(OwnershipSharedCallback);
+
+ /// Constructor to be used when objects are expected to be constructed from native code.
+ /// For a class that inherited from an EFL# class to be properly constructed from native code
+ /// one must create a constructor with this signature and calls this base constructor from it.
+ /// This constructor will take care of calling base constructors of the native classes and
+ /// perform additional setup so objects are ready to use.
+ /// It is advisable to check for the property in the top level
+ /// constructor and signal an error when it has a value of IntPtr.Zero after this
+ /// constructor completion.
+ /// Warning: Do not use this constructor directly from a `new` statement.
+ /// Tag struct storing the native handle of the object being constructed.
+ protected EoWrapper(ConstructingHandle ch)
+ {
+ inherited = true;
+ handle = Efl.Eo.Globals.efl_constructor(Efl.Eo.Globals.efl_super(ch.NativeHandle, Efl.Eo.Globals.efl_class_get(ch.NativeHandle)));
+ if (handle == IntPtr.Zero)
+ {
+ Eina.Log.Warning("Natice constructor returned NULL");
+ return;
+ }
+
+ AddWrapperSupervisor();
+ // Make an additional reference to C#
+ // - Will also call EVENT_OWNERSHIP_SHARED
+ Efl.Eo.Globals.efl_ref(handle);
+ }
+
/// Initializes a new instance of the class.
/// Internal usage: Constructs an instance from a native pointer. This is used when interacting with C code and should not be used directly.
/// The native pointer to be wrapped.
@@ -54,7 +82,17 @@ public abstract class EoWrapper : IWrapper, IDisposable
parent_ptr = parent.NativeHandle;
}
- handle = Efl.Eo.Globals._efl_add_internal_start(file, line, actual_klass, parent_ptr, 1, 0);
+ if (!inherited)
+ {
+ handle = Efl.Eo.Globals._efl_add_internal_start(file, line, actual_klass, parent_ptr, 1, 0);
+ }
+ else
+ {
+ handle = Efl.Eo.Globals._efl_add_internal_start_bindings(file, line, actual_klass, parent_ptr, 1, 0,
+ Efl.Eo.Globals.efl_mono_avoid_top_level_constructor_callback_addr_get(),
+ IntPtr.Zero);
+ }
+
if (handle == System.IntPtr.Zero)
{
throw new Exception("Instantiation failed");
@@ -246,8 +284,78 @@ public abstract class EoWrapper : IWrapper, IDisposable
AddNativeEventHandler("eo", "_EFL_EVENT_OWNERSHIP_SHARED", ownershipSharedDelegate, ownershipSharedDelegate);
Eina.Error.RaiseIfUnhandledException();
}
+
+ protected struct ConstructingHandle
+ {
+ public ConstructingHandle(IntPtr h)
+ {
+ NativeHandle = h;
+ }
+
+ public IntPtr NativeHandle { get; set; }
+ }
+
+ public abstract class NativeMethods : Efl.Eo.NativeClass
+ {
+ private static EflConstructorDelegate csharpEflConstructorStaticDelegate = new EflConstructorDelegate(Constructor);
+ private static Efl.Eo.NativeModule EoModule = new Efl.Eo.NativeModule("eo");
+
+ private delegate IntPtr EflConstructorDelegate(IntPtr obj, IntPtr pd);
+
+ public override System.Collections.Generic.List GetEoOps(Type type)
+ {
+ var descs = new System.Collections.Generic.List();
+
+ descs.Add(new Efl_Op_Description()
+ {
+ api_func = Efl.Eo.FunctionInterop.LoadFunctionPointer(EoModule.Module, "efl_constructor"),
+ func = Marshal.GetFunctionPointerForDelegate(csharpEflConstructorStaticDelegate)
+ });
+
+ return descs;
+ }
+
+ private static IntPtr Constructor(IntPtr obj, IntPtr pd)
+ {
+ try
+ {
+ var eoKlass = Efl.Eo.Globals.efl_class_get(obj);
+ var managedType = ClassRegister.GetManagedType(eoKlass);
+ if (managedType == null)
+ {
+ IntPtr nativeName = Efl.Eo.Globals.efl_class_name_get(eoKlass);
+ var name = Eina.StringConversion.NativeUtf8ToManagedString(nativeName);
+ Eina.Log.Warning($"Can't get Managed class for object handle 0x{(UInt64)obj:x} with native class [{name}]");
+ return IntPtr.Zero;
+ }
+
+ var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
+ ConstructorInfo constructor = managedType.GetConstructor(flags, null, new Type[1] { typeof(ConstructingHandle) }, null);
+ if (constructor == null)
+ {
+ Eina.Log.Error($"Type {managedType.FullName} lacks a constructor that receives a ConstructingHandle. It can not be constructed from native code.");
+ return IntPtr.Zero;
+ }
+
+ var eoWrapper = (Efl.Eo.IWrapper) constructor.Invoke(new object[1] { new ConstructingHandle(obj) });
+ if (eoWrapper == null)
+ {
+ Eina.Log.Warning("Constructor was unable to create a new object");
+ return IntPtr.Zero;
+ }
+
+ return eoWrapper.NativeHandle;
+ }
+ catch (Exception e)
+ {
+ Eina.Log.Warning($"Inherited constructor error: {e.ToString()}");
+ Eina.Error.Set(Eina.Error.UNHANDLED_EXCEPTION);
+ return IntPtr.Zero;
+ }
+ }
+ }
}
-} // namespace Global
+} // namespace Eo
} // namespace Efl
diff --git a/src/bindings/mono/eo_mono/iwrapper.cs b/src/bindings/mono/eo_mono/iwrapper.cs
index 82c08ccf3f..966759b314 100644
--- a/src/bindings/mono/eo_mono/iwrapper.cs
+++ b/src/bindings/mono/eo_mono/iwrapper.cs
@@ -44,10 +44,6 @@ public class Globals
public static FunctionWrapper efl_object_shutdown_ptr = new FunctionWrapper(efl.Libs.EoModule, "efl_object_shutdown");
public static void efl_object_shutdown() => efl_object_shutdown_ptr.Value.Delegate();
// [DllImport(efl.Libs.Eo)] public static extern void efl_object_shutdown();
- public static FunctionWrapper<_efl_add_internal_start_delegate> _efl_add_internal_start_ptr = new FunctionWrapper<_efl_add_internal_start_delegate>(efl.Libs.EoModule, "_efl_add_internal_start");
- public delegate IntPtr
- _efl_add_internal_start_delegate([MarshalAs(UnmanagedType.LPStr)] String file, int line,
- IntPtr klass, IntPtr parent, byte is_ref, byte is_fallback);
[DllImport(efl.Libs.CustomExports)] public static extern IntPtr efl_mono_wrapper_supervisor_get(IntPtr eo);
[DllImport(efl.Libs.CustomExports)] public static extern void efl_mono_wrapper_supervisor_set(IntPtr eo, IntPtr ws);
@@ -55,6 +51,9 @@ public class Globals
[DllImport(efl.Libs.Eo)] public static extern IntPtr
_efl_add_internal_start([MarshalAs(UnmanagedType.LPStr)] String file, int line,
IntPtr klass, IntPtr parent, byte is_ref, byte is_fallback);
+ [DllImport(efl.Libs.Eo)] public static extern IntPtr
+ _efl_add_internal_start_bindings([MarshalAs(UnmanagedType.LPStr)] String file, int line, IntPtr klass, IntPtr parent,
+ byte is_ref, byte is_fallback, IntPtr substitute_ctor, IntPtr data);
public delegate IntPtr
_efl_add_end_delegate(IntPtr eo, byte is_ref, byte is_fallback);
[DllImport(efl.Libs.Eo)] public static extern IntPtr
@@ -196,6 +195,10 @@ public class Globals
public delegate IntPtr dlerror_delegate();
[DllImport(efl.Libs.Evil)] public static extern IntPtr dlerror();
+ [DllImport(efl.Libs.Eo)] public static extern IntPtr efl_constructor(IntPtr obj);
+
+ [DllImport(efl.Libs.CustomExports)] public static extern IntPtr efl_mono_avoid_top_level_constructor_callback_addr_get();
+
[DllImport(efl.Libs.Eo)] [return: MarshalAs(UnmanagedType.U1)] public static extern bool
efl_event_callback_priority_add(IntPtr obj, IntPtr desc, short priority, IntPtr cb, IntPtr data);
diff --git a/src/lib/efl_mono/efl_custom_exports_mono.c b/src/lib/efl_mono/efl_custom_exports_mono.c
index ad74babe52..8d57ad730f 100644
--- a/src/lib/efl_mono/efl_custom_exports_mono.c
+++ b/src/lib/efl_mono/efl_custom_exports_mono.c
@@ -166,6 +166,16 @@ EAPI Eina_Free_Cb efl_mono_native_efl_unref_addr_get()
return (Eina_Free_Cb)efl_mono_thread_safe_efl_unref;
}
+static Eo *_efl_mono_avoid_top_level_constructor_cb(void *data EINA_UNUSED, Eo *obj)
+{
+ return efl_constructor(efl_super(obj, efl_class_get(obj)));
+}
+
+EAPI Efl_Substitute_Ctor_Cb efl_mono_avoid_top_level_constructor_callback_addr_get()
+{
+ return &_efl_mono_avoid_top_level_constructor_cb;
+}
+
// Iterator Wrapper //
typedef struct _Eina_Iterator_Wrapper_Mono
diff --git a/src/tests/efl_mono/EoConstruction.cs b/src/tests/efl_mono/EoConstruction.cs
new file mode 100644
index 0000000000..4708befc5b
--- /dev/null
+++ b/src/tests/efl_mono/EoConstruction.cs
@@ -0,0 +1,96 @@
+using System;
+
+class InheritedConstructibleObject : Dummy.ConstructibleObject
+{
+ public InheritedConstructibleObject() : base()
+ {
+ if (this.NativeConstructionCount != 1)
+ {
+ DefaultConstrutorCallCount = -100;
+ }
+
+ ++DefaultConstrutorCallCount;
+ this.IncrementDefaultConstructionCount();
+ }
+
+ private InheritedConstructibleObject(ConstructingHandle ch) : base(ch)
+ {
+ if (this.NativeConstructionCount != 1)
+ {
+ SpecialConstrutorCallCount = -100;
+ }
+
+ ++SpecialConstrutorCallCount;
+ this.IncrementSpecialConstructionCount();
+ }
+
+ public int DefaultConstrutorCallCount { get; set; } = 0;
+ public int SpecialConstrutorCallCount { get; set; } = 0;
+
+ public bool InheritedFlag
+ {
+ get { return inherited; }
+ }
+
+ public override int MultiplyIntegerValue(int v)
+ {
+ return 3 * v;
+ }
+}
+
+namespace TestSuite
+{
+
+class TestEoConstruction
+{
+ public static void TestGeneratedEoDirectConstruction()
+ {
+ var obj = new Dummy.ConstructibleObject();
+ Test.AssertEquals(obj.NativeConstructionCount, 1);
+ Test.AssertEquals(obj.DefaultConstructionCount, 0);
+ Test.AssertEquals(obj.SpecialConstructionCount, 0);
+ Test.AssertEquals(obj.MultiplyIntegerValue(21), 42);
+ obj.Dispose();
+ }
+
+ public static void TestInheritedEoDirectConstruction()
+ {
+ var obj = new InheritedConstructibleObject();
+ Test.AssertEquals(obj.InheritedFlag, true);
+ Test.AssertEquals(obj.NativeConstructionCount, 1);
+ Test.AssertEquals(obj.DefaultConstructionCount, 1);
+ Test.AssertEquals(obj.SpecialConstructionCount, 0);
+ Test.AssertEquals(obj.DefaultConstrutorCallCount, 1);
+ Test.AssertEquals(obj.SpecialConstrutorCallCount, 0);
+ Test.AssertEquals(obj.MultiplyIntegerValue(21), 63);
+ obj.Dispose();
+ }
+
+ public static void TestInheritedEoIndirectConstruction()
+ {
+ var obj = new Dummy.ConstructibleObject();
+ Test.AssertEquals(obj.NativeConstructionCount, 1);
+ Test.AssertEquals(obj.DefaultConstructionCount, 0);
+ Test.AssertEquals(obj.SpecialConstructionCount, 0);
+ Test.AssertEquals(obj.MultiplyIntegerValue(21), 42);
+
+ var obj2 = (InheritedConstructibleObject) obj.ConstructTypeAndStore(typeof(InheritedConstructibleObject));
+ Test.AssertEquals(obj2.InheritedFlag, true);
+ Test.AssertEquals(obj2.NativeConstructionCount, 1);
+ Test.AssertEquals(obj2.DefaultConstructionCount, 0);
+ Test.AssertEquals(obj2.SpecialConstructionCount, 1);
+ Test.AssertEquals(obj2.DefaultConstrutorCallCount, 0);
+ Test.AssertEquals(obj2.SpecialConstrutorCallCount, 1);
+ Test.AssertEquals(obj2.MultiplyIntegerValue(21), 63);
+
+ var internalObj = obj.InternalObject;
+ Test.Assert(ReferenceEquals(obj2, internalObj)); // Ensure it always use the same object instance
+ Test.AssertEquals(obj2.NativeConstructionCount, 1); // And that constructors are not called again
+ Test.AssertEquals(obj2.MultiplyIntegerValue(32), 96); // And that it is still usable
+
+ obj.Dispose();
+ obj2.Dispose();
+ }
+}
+
+}
diff --git a/src/tests/efl_mono/dummy_constructible_object.c b/src/tests/efl_mono/dummy_constructible_object.c
new file mode 100644
index 0000000000..870895066c
--- /dev/null
+++ b/src/tests/efl_mono/dummy_constructible_object.c
@@ -0,0 +1,77 @@
+#include "libefl_mono_native_test.h"
+
+typedef struct _Dummy_Constructible_Object_Data
+{
+ Eo *internal_obj;
+ int native_construction_count;
+ int default_construction_count;
+ int special_construction_count;
+} Dummy_Constructible_Object_Data;
+
+
+EOLIAN static Eo *
+_dummy_constructible_object_efl_object_constructor(Eo *obj, Dummy_Constructible_Object_Data *pd)
+{
+ ++(pd->native_construction_count);
+ return efl_constructor(efl_super(obj, DUMMY_CONSTRUCTIBLE_OBJECT_CLASS));
+}
+
+EOLIAN static void
+_dummy_constructible_object_efl_object_destructor(Eo *obj, Dummy_Constructible_Object_Data *pd)
+{
+ if (pd->internal_obj)
+ efl_unref(pd->internal_obj);
+ efl_destructor(efl_super(obj, DUMMY_CONSTRUCTIBLE_OBJECT_CLASS));
+}
+
+EOLIAN static Efl_Object *
+_dummy_constructible_object_construct_type_and_store(Eo *obj EINA_UNUSED, Dummy_Constructible_Object_Data *pd, const Efl_Class *klass)
+{
+ pd->internal_obj = efl_add_ref(klass, NULL);
+ return pd->internal_obj;
+}
+
+EOLIAN static void
+_dummy_constructible_object_increment_default_construction_count(Eo *obj EINA_UNUSED, Dummy_Constructible_Object_Data *pd)
+{
+ ++(pd->default_construction_count);
+}
+
+EOLIAN static void
+_dummy_constructible_object_increment_special_construction_count(Eo *obj EINA_UNUSED, Dummy_Constructible_Object_Data *pd)
+{
+ ++(pd->special_construction_count);
+}
+
+EOLIAN static int
+_dummy_constructible_object_native_construction_count_get(const Eo *obj EINA_UNUSED, Dummy_Constructible_Object_Data *pd)
+{
+ return pd->native_construction_count;
+}
+
+EOLIAN static int
+_dummy_constructible_object_default_construction_count_get(const Eo *obj EINA_UNUSED, Dummy_Constructible_Object_Data *pd)
+{
+ return pd->default_construction_count;
+}
+
+EOLIAN static int
+_dummy_constructible_object_special_construction_count_get(const Eo *obj EINA_UNUSED, Dummy_Constructible_Object_Data *pd)
+{
+ return pd->special_construction_count;
+}
+
+EOLIAN static Efl_Object *
+_dummy_constructible_object_internal_object_get(const Eo *obj EINA_UNUSED, Dummy_Constructible_Object_Data *pd)
+{
+ return pd->internal_obj;
+}
+
+
+EOLIAN static int
+_dummy_constructible_object_multiply_integer_value(const Eo *obj EINA_UNUSED, Dummy_Constructible_Object_Data *pd, int v)
+{
+ return 2 * v;
+}
+
+#include "dummy_constructible_object.eo.c"
diff --git a/src/tests/efl_mono/dummy_constructible_object.eo b/src/tests/efl_mono/dummy_constructible_object.eo
new file mode 100644
index 0000000000..ea508ed15c
--- /dev/null
+++ b/src/tests/efl_mono/dummy_constructible_object.eo
@@ -0,0 +1,53 @@
+class Dummy.Constructible_Object extends Efl.Object {
+ methods {
+ construct_type_and_store {
+ params {
+ @in type: const(Efl.Class);
+ }
+ return: Efl.Object;
+ }
+ increment_default_construction_count {
+ }
+ increment_special_construction_count {
+ }
+ @property native_construction_count {
+ get {
+ }
+ values {
+ value: int;
+ }
+ }
+ @property default_construction_count {
+ get {
+ }
+ values {
+ value: int;
+ }
+ }
+ @property special_construction_count {
+ get {
+ }
+ values {
+ value: int;
+ }
+ }
+ @property internal_object {
+ get {
+ }
+ values {
+ value: Efl.Object;
+ }
+ }
+ multiply_integer_value @const {
+ params {
+ v: int;
+ }
+ return: int;
+ }
+ }
+ implements {
+ Efl.Object.constructor;
+ Efl.Object.destructor;
+ }
+}
+
diff --git a/src/tests/efl_mono/dummy_test_object.c b/src/tests/efl_mono/dummy_test_object.c
index 4fdc69bbed..57d2a06ae6 100644
--- a/src/tests/efl_mono/dummy_test_object.c
+++ b/src/tests/efl_mono/dummy_test_object.c
@@ -59,9 +59,9 @@ Dummy_Numberwrapper **_new_obj_ref(int n)
return &r;
}
-// ############ //
-// Test.Testing //
-// ############ //
+// ################# //
+// Dummy.Test_Object //
+// ################# //
static Efl_Object*
_dummy_test_object_efl_object_constructor(Eo *obj, Dummy_Test_Object_Data *pd)
diff --git a/src/tests/efl_mono/libefl_mono_native_test.h b/src/tests/efl_mono/libefl_mono_native_test.h
index 9251118fc6..b726bd05c1 100644
--- a/src/tests/efl_mono/libefl_mono_native_test.h
+++ b/src/tests/efl_mono/libefl_mono_native_test.h
@@ -55,6 +55,7 @@
#include "dummy_inherit_helper.eo.h"
#include "dummy_part_holder.eo.h"
#include "dummy_event_manager.eo.h"
+#include "dummy_constructible_object.eo.h"
#include
diff --git a/src/tests/efl_mono/meson.build b/src/tests/efl_mono/meson.build
index f75a5b8bb9..e464cdd2a8 100644
--- a/src/tests/efl_mono/meson.build
+++ b/src/tests/efl_mono/meson.build
@@ -7,6 +7,7 @@ eo_files = [
'dummy_inherit_iface.eo',
'dummy_part_holder.eo',
'dummy_event_manager.eo',
+ 'dummy_constructible_object.eo',
]
eo_file_targets = []
@@ -33,6 +34,7 @@ efl_mono_native_test = library('efl_mono_native_test',
'dummy_part_holder.c',
'dummy_test_object.c',
'dummy_event_manager.c',
+ 'dummy_constructible_object.c',
],
dependencies : [ecore, eo, efl],
)
@@ -65,6 +67,7 @@ efl_mono_src = [
'Eldbus.cs',
'Eo.cs',
'EoPromises.cs',
+ 'EoConstruction.cs',
'Errors.cs',
'Events.cs',
'FunctionPointers.cs',