summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVitor Sousa <vitorsousa@expertisesolutions.com.br>2019-05-31 17:43:11 -0300
committerVitor Sousa <vitorsousa@expertisesolutions.com.br>2019-05-31 17:44:12 -0300
commitfcf5f1d2e2d9ce877d550dd3352bbd6e0527299e (patch)
tree419d1e22dddf6d9283ce05cd92817df1a6371fa9 /src
parent937da0b12c5b2b07a173199078856c35becdb0e0 (diff)
csharp: Refactor wrapper lifetime.
Summary: This commit makes use of the `ownership,shared` and `ownership,unique` events from Efl.Object in order to avoid the C# wrapper from being collected while C code holds a reference to the object. For example, creating a list of items in a for loop and attaching events to them would fails without this commit, as the C# GC may collect the wrapper. The basic idea is that we use a `WrapperSupervisor`, which is stored in the Eo data storage, with a GCHandle allocated for the lifetime of the underlying Eo object. This supervisor takes care of holding either a weak C# reference (when in unique mode, allowing the wrapper to be GC'd) or a hard C# reference (when in shared mode, making the wrapper non-collectable while the Eo has extra references). One limitation is that object graphs can leak if a shared object in the graph - an Eo child for example - stores a hard reference to another object in the graph as a C# field. In this example, this causes the parent to always have a hard C# reference (from the child) as the child is non-collectable due to the parent holding an Eo reference to it. Depends on D8678 Test Plan: `ninja test` and `make test` Reviewers: lauromoura, felipealmeida, woohyun, segfaultxavi Reviewed By: lauromoura Subscribers: cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D9014
Diffstat (limited to 'src')
-rw-r--r--src/Makefile_Efl_Mono.am4
-rw-r--r--src/bin/eolian_mono/eolian/mono/events.hh3
-rw-r--r--src/bin/eolian_mono/eolian/mono/function_definition.hh6
-rw-r--r--src/bin/eolian_mono/eolian/mono/klass.hh309
-rw-r--r--src/bindings/mono/eina_mono/eina_container_common.cs2
-rw-r--r--src/bindings/mono/eo_mono/EoWrapper.cs253
-rw-r--r--src/bindings/mono/eo_mono/WrapperSupervisor.cs64
-rw-r--r--src/bindings/mono/eo_mono/iwrapper.cs197
-rw-r--r--src/bindings/mono/eo_mono/meson.build4
-rw-r--r--src/bindings/mono/eo_mono/workaround.cs9
-rw-r--r--src/lib/efl_mono/efl_custom_exports_mono.c43
-rw-r--r--src/tests/efl_mono/Eo.cs15
-rw-r--r--src/tests/efl_mono/Inheritance.cs89
-rw-r--r--src/tests/efl_mono/TestUtils.cs28
-rw-r--r--src/tests/efl_mono/dummy_part_holder.c23
-rw-r--r--src/tests/efl_mono/dummy_part_holder.eo1
16 files changed, 599 insertions, 451 deletions
diff --git a/src/Makefile_Efl_Mono.am b/src/Makefile_Efl_Mono.am
index 8caf083e05..0cf41589ff 100644
--- a/src/Makefile_Efl_Mono.am
+++ b/src/Makefile_Efl_Mono.am
@@ -6,7 +6,9 @@ efl_eo_mono_files = \
6 bindings/mono/eo_mono/iwrapper.cs \ 6 bindings/mono/eo_mono/iwrapper.cs \
7 bindings/mono/eo_mono/FunctionWrapper.cs \ 7 bindings/mono/eo_mono/FunctionWrapper.cs \
8 bindings/mono/eo_mono/NativeModule.cs \ 8 bindings/mono/eo_mono/NativeModule.cs \
9 bindings/mono/eo_mono/workaround.cs 9 bindings/mono/eo_mono/workaround.cs \
10 bindings/mono/eo_mono/EoWrapper.cs \
11 bindings/mono/eo_mono/WrapperSupervisor.cs
10 12
11if HAVE_WIN32 13if HAVE_WIN32
12 14
diff --git a/src/bin/eolian_mono/eolian/mono/events.hh b/src/bin/eolian_mono/eolian/mono/events.hh
index 350dd57ad4..0522a9be7a 100644
--- a/src/bin/eolian_mono/eolian/mono/events.hh
+++ b/src/bin/eolian_mono/eolian/mono/events.hh
@@ -431,10 +431,9 @@ struct event_definition_generator
431 << scope_tab << scope_tab << "{\n" 431 << scope_tab << scope_tab << "{\n"
432 << scope_tab << scope_tab << scope_tab << "lock (eventLock)\n" 432 << scope_tab << scope_tab << scope_tab << "lock (eventLock)\n"
433 << scope_tab << scope_tab << scope_tab << "{\n" 433 << scope_tab << scope_tab << scope_tab << "{\n"
434 << scope_tab << scope_tab << scope_tab << scope_tab << "var wRef = new WeakReference(this);\n"
435 << scope_tab << scope_tab << scope_tab << scope_tab << "Efl.EventCb callerCb = (IntPtr data, ref Efl.Event.NativeStruct evt) =>\n" 434 << scope_tab << scope_tab << scope_tab << scope_tab << "Efl.EventCb callerCb = (IntPtr data, ref Efl.Event.NativeStruct evt) =>\n"
436 << scope_tab << scope_tab << scope_tab << scope_tab << "{\n" 435 << scope_tab << scope_tab << scope_tab << scope_tab << "{\n"
437 << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "var obj = wRef.Target as Efl.Eo.IWrapper;\n" 436 << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "var obj = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(data).Target;\n"
438 << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "if (obj != null)\n" 437 << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "if (obj != null)\n"
439 << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "{\n" 438 << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "{\n"
440 << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << event_args 439 << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << event_args
diff --git a/src/bin/eolian_mono/eolian/mono/function_definition.hh b/src/bin/eolian_mono/eolian/mono/function_definition.hh
index d1ca5f573f..8fc7225bf3 100644
--- a/src/bin/eolian_mono/eolian/mono/function_definition.hh
+++ b/src/bin/eolian_mono/eolian/mono/function_definition.hh
@@ -104,14 +104,14 @@ struct native_function_definition_generator
104 << ")\n" 104 << ")\n"
105 << indent << "{\n" 105 << indent << "{\n"
106 << indent << scope_tab << "Eina.Log.Debug(\"function " << string << " was called\");\n" 106 << indent << scope_tab << "Eina.Log.Debug(\"function " << string << " was called\");\n"
107 << indent << scope_tab << "Efl.Eo.IWrapper wrapper = Efl.Eo.Globals.PrivateDataGet(pd);\n" 107 << indent << scope_tab << "var ws = Efl.Eo.Globals.GetWrapperSupervisor(obj);\n"
108 << indent << scope_tab << "if (wrapper != null)\n" 108 << indent << scope_tab << "if (ws != null)\n"
109 << indent << scope_tab << "{\n" 109 << indent << scope_tab << "{\n"
110 << eolian_mono::native_function_definition_preamble() 110 << eolian_mono::native_function_definition_preamble()
111 << indent << scope_tab << scope_tab << "try\n" 111 << indent << scope_tab << scope_tab << "try\n"
112 << indent << scope_tab << scope_tab << "{\n" 112 << indent << scope_tab << scope_tab << "{\n"
113 << indent << scope_tab << scope_tab << scope_tab << (return_type != "void" ? "_ret_var = " : "") 113 << indent << scope_tab << scope_tab << scope_tab << (return_type != "void" ? "_ret_var = " : "")
114 << (f.is_static ? "" : "((") << klass_cast_name << (f.is_static ? "." : ")wrapper).") << string 114 << (f.is_static ? "" : "((") << klass_cast_name << (f.is_static ? "." : ")ws.Target).") << string
115 << "(" << (native_argument_invocation % ", ") << ");\n" 115 << "(" << (native_argument_invocation % ", ") << ");\n"
116 << indent << scope_tab << scope_tab << "}\n" 116 << indent << scope_tab << scope_tab << "}\n"
117 << indent << scope_tab << scope_tab << "catch (Exception e)\n" 117 << indent << scope_tab << scope_tab << "catch (Exception e)\n"
diff --git a/src/bin/eolian_mono/eolian/mono/klass.hh b/src/bin/eolian_mono/eolian/mono/klass.hh
index 9d3711fb61..6ac4aa1c3c 100644
--- a/src/bin/eolian_mono/eolian/mono/klass.hh
+++ b/src/bin/eolian_mono/eolian/mono/klass.hh
@@ -31,37 +31,6 @@
31 31
32namespace eolian_mono { 32namespace eolian_mono {
33 33
34template <typename OutputIterator, typename Context>
35static bool generate_equals_method(OutputIterator sink, Context const &context)
36{
37 return as_generator(
38 scope_tab << "/// <summary>Verifies if the given object is equal to this one.</summary>\n"
39 << scope_tab << "/// <param name=\"instance\">The object to compare to.</param>\n"
40 << scope_tab << "/// <returns>True if both objects point to the same native object.</returns>\n"
41 << scope_tab << "public override bool Equals(object instance)\n"
42 << scope_tab << "{\n"
43 << scope_tab << scope_tab << "var other = instance as Efl.Object;\n"
44 << scope_tab << scope_tab << "if (other == null)\n"
45 << scope_tab << scope_tab << "{\n"
46 << scope_tab << scope_tab << scope_tab << "return false;\n"
47 << scope_tab << scope_tab << "}\n"
48 << scope_tab << scope_tab << "return this.NativeHandle == other.NativeHandle;\n"
49 << scope_tab << "}\n\n"
50 << scope_tab << "/// <summary>Gets the hash code for this object based on the native pointer it points to.</summary>\n"
51 << scope_tab << "/// <returns>The value of the pointer, to be used as the hash code of this object.</returns>\n"
52 << scope_tab << "public override int GetHashCode()\n"
53 << scope_tab << "{\n"
54 << scope_tab << scope_tab << "return this.NativeHandle.ToInt32();\n"
55 << scope_tab << "}\n\n"
56 << scope_tab << "/// <summary>Turns the native pointer into a string representation.</summary>\n"
57 << scope_tab << "/// <returns>A string with the type and the native pointer for this object.</returns>\n"
58 << scope_tab << "public override String ToString()\n"
59 << scope_tab << "{\n"
60 << scope_tab << scope_tab << "return $\"{this.GetType().Name}@[{this.NativeHandle.ToInt32():x}]\";\n"
61 << scope_tab << "}\n\n"
62 ).generate(sink, nullptr, context);
63}
64
65/* Get the actual number of functions of a class, checking for blacklisted ones */ 34/* Get the actual number of functions of a class, checking for blacklisted ones */
66template<typename Context> 35template<typename Context>
67static std::size_t 36static std::size_t
@@ -216,9 +185,9 @@ struct klass
216 if(!as_generator 185 if(!as_generator
217 ( 186 (
218 documentation 187 documentation
219 << "sealed public class " << concrete_name << " : " << "\n" 188 << "sealed public class " << concrete_name << " :\n"
220 << (klass_full_concrete_or_interface_name % ",") << "\n" 189 << scope_tab << (root ? "Efl.Eo.EoWrapper" : "") << (klass_full_concrete_or_interface_name % "") << "\n"
221 << (inherit_classes.size() > 0 ? ", " : "" ) << interface_name << "\n" 190 << scope_tab << ", " << interface_name << "\n"
222 << scope_tab << *(", " << name_helpers::klass_full_concrete_or_interface_name) << "\n" 191 << scope_tab << *(", " << name_helpers::klass_full_concrete_or_interface_name) << "\n"
223 << "{\n" 192 << "{\n"
224 ).generate(sink, std::make_tuple(cls, inherit_classes, inherit_interfaces), concrete_cxt)) 193 ).generate(sink, std::make_tuple(cls, inherit_classes, inherit_interfaces), concrete_cxt))
@@ -227,7 +196,6 @@ struct klass
227 if (!generate_fields(sink, cls, concrete_cxt)) 196 if (!generate_fields(sink, cls, concrete_cxt))
228 return false; 197 return false;
229 198
230 bool root = !helpers::has_regular_ancestor(cls);
231 if (!as_generator 199 if (!as_generator
232 ( 200 (
233 scope_tab << "[System.Runtime.InteropServices.DllImport(" << context_find_tag<library_context>(concrete_cxt).actual_library_name(cls.filename) 201 scope_tab << "[System.Runtime.InteropServices.DllImport(" << context_find_tag<library_context>(concrete_cxt).actual_library_name(cls.filename)
@@ -235,20 +203,13 @@ struct klass
235 << scope_tab << scope_tab << name_helpers::klass_get_name(cls) << "();\n" 203 << scope_tab << scope_tab << name_helpers::klass_get_name(cls) << "();\n"
236 << scope_tab << "/// <summary>Initializes a new instance of the <see cref=\"" << interface_name << "\"/> class.\n" 204 << scope_tab << "/// <summary>Initializes a new instance of the <see cref=\"" << interface_name << "\"/> class.\n"
237 << scope_tab << "/// Internal usage: This is used when interacting with C code and should not be used directly.</summary>\n" 205 << scope_tab << "/// Internal usage: This is used when interacting with C code and should not be used directly.</summary>\n"
238 << scope_tab << "private " << concrete_name << "(System.IntPtr raw)" << (root ? "" : " : base(raw)") << "\n" 206 << scope_tab << "private " << concrete_name << "(System.IntPtr raw) : base(raw)\n"
239 << scope_tab << "{\n" 207 << scope_tab << "{\n"
240 << scope_tab << scope_tab << (root ? "handle = raw;\n" : "") 208 << scope_tab << "}\n\n"
241 << scope_tab << "}\n"
242 ) 209 )
243 .generate(sink, attributes::unused, concrete_cxt)) 210 .generate(sink, attributes::unused, concrete_cxt))
244 return false; 211 return false;
245 212
246 if (!generate_dispose_methods(sink, cls, concrete_cxt))
247 return false;
248
249 if (!generate_equals_method(sink, concrete_cxt))
250 return false;
251
252 if (!generate_events(sink, cls, concrete_cxt)) 213 if (!generate_events(sink, cls, concrete_cxt))
253 return false; 214 return false;
254 215
@@ -305,10 +266,9 @@ struct klass
305 << "[" << name_helpers::klass_full_native_inherit_name(cls) << "]\n" 266 << "[" << name_helpers::klass_full_native_inherit_name(cls) << "]\n"
306 << "public " << class_type << " " << name_helpers::klass_concrete_name(cls) << " : " 267 << "public " << class_type << " " << name_helpers::klass_concrete_name(cls) << " : "
307 << (klass_full_concrete_or_interface_name % ",") // classes 268 << (klass_full_concrete_or_interface_name % ",") // classes
308 << (inherit_classes.empty() ? "" : ",") 269 << (root ? "Efl.Eo.EoWrapper" : "") // ... or root
309 << " Efl.Eo.IWrapper" << (root ? ", IDisposable" : "") 270 << (inherit_interfaces.empty() ? "" : ", ")
310 << (inherit_interfaces.empty() ? "" : ",") 271 << (klass_full_concrete_or_interface_name % ", ") // interfaces
311 << (klass_full_concrete_or_interface_name % ",") // interfaces
312 << "\n{\n" 272 << "\n{\n"
313 ) 273 )
314 .generate(sink, std::make_tuple(cls, inherit_classes, inherit_interfaces), inherit_cxt)) 274 .generate(sink, std::make_tuple(cls, inherit_classes, inherit_interfaces), inherit_cxt))
@@ -322,12 +282,6 @@ struct klass
322 if (!generate_constructors(sink, cls, inherit_cxt)) 282 if (!generate_constructors(sink, cls, inherit_cxt))
323 return false; 283 return false;
324 284
325 if (!generate_dispose_methods(sink, cls, inherit_cxt))
326 return false;
327
328 if (!generate_equals_method(sink, inherit_cxt))
329 return false;
330
331 if (!generate_events(sink, cls, inherit_cxt)) 285 if (!generate_events(sink, cls, inherit_cxt))
332 return false; 286 return false;
333 287
@@ -479,22 +433,14 @@ struct klass
479 bool generate_fields(OutputIterator sink, attributes::klass_def const& cls, Context const& context) const 433 bool generate_fields(OutputIterator sink, attributes::klass_def const& cls, Context const& context) const
480 { 434 {
481 std::string visibility = is_inherit_context(context) ? "protected " : "private "; 435 std::string visibility = is_inherit_context(context) ? "protected " : "private ";
482 bool root = !helpers::has_regular_ancestor(cls);
483 bool is_inherit = is_inherit_context(context);
484 436
485 std::string class_getter = name_helpers::klass_get_name(cls) + "()"; 437 std::string class_getter = name_helpers::klass_get_name(cls) + "()";
486 std::string native_inherit_full_name = name_helpers::klass_full_native_inherit_name(cls); 438 std::string native_inherit_full_name = name_helpers::klass_full_native_inherit_name(cls);
487 auto inherit_name = name_helpers::klass_concrete_name(cls); 439 auto inherit_name = name_helpers::klass_concrete_name(cls);
488 440
489 std::string raw_klass_modifier;
490 if (!root)
491 raw_klass_modifier = "override ";
492 else if (is_inherit)
493 raw_klass_modifier = "virtual ";
494
495 if(!as_generator( 441 if(!as_generator(
496 scope_tab << "///<summary>Pointer to the native class description.</summary>\n" 442 scope_tab << "///<summary>Pointer to the native class description.</summary>\n"
497 << scope_tab << "public " << raw_klass_modifier << "System.IntPtr NativeClass\n" 443 << scope_tab << "public override System.IntPtr NativeClass\n"
498 << scope_tab << "{\n" 444 << scope_tab << "{\n"
499 << scope_tab << scope_tab << "get\n" 445 << scope_tab << scope_tab << "get\n"
500 << scope_tab << scope_tab << "{\n" 446 << scope_tab << scope_tab << "{\n"
@@ -511,42 +457,12 @@ struct klass
511 ).generate(sink, attributes::unused, context)) 457 ).generate(sink, attributes::unused, context))
512 return false; 458 return false;
513 459
514 // The remaining fields aren't needed in children classes. 460 return true;
515 if (!root)
516 return true;
517
518 if (cls.get_all_events().size() > 0)
519 if (!as_generator(
520 scope_tab << "/// <summary>Internal usage by derived classes to track native events.</summary>\n"
521 << scope_tab << visibility << "Dictionary<(IntPtr desc, object evtDelegate), (IntPtr evtCallerPtr, Efl.EventCb evtCaller)> eoEvents = new Dictionary<(IntPtr desc, object evtDelegate), (IntPtr evtCallerPtr, Efl.EventCb evtCaller)>();\n"
522 << scope_tab << "/// <summary>Internal usage by derived classes to lock native event handlers.</summary>\n"
523 << scope_tab << visibility << "readonly object eventLock = new object();\n")
524 .generate(sink, attributes::unused, context))
525 return false;
526
527 if (is_inherit)
528 {
529 if (!as_generator(
530 scope_tab << "/// <summary>Internal usage to detect whether this instance is from a generated class or not.</summary>\n"
531 << scope_tab << "protected bool inherited;\n"
532 ).generate(sink, attributes::unused, context))
533 return false;
534 }
535
536 return as_generator(
537 scope_tab << visibility << " System.IntPtr handle;\n"
538 << scope_tab << "///<summary>Pointer to the native instance.</summary>\n"
539 << scope_tab << "public System.IntPtr NativeHandle\n"
540 << scope_tab << "{\n"
541 << scope_tab << scope_tab << "get { return handle; }\n"
542 << scope_tab << "}\n\n"
543 ).generate(sink, attributes::unused, context);
544 } 461 }
545 462
546 template <typename OutputIterator, typename Context> 463 template <typename OutputIterator, typename Context>
547 bool generate_constructors(OutputIterator sink, attributes::klass_def const& cls, Context const& context) const 464 bool generate_constructors(OutputIterator sink, attributes::klass_def const& cls, Context const& context) const
548 { 465 {
549 bool root = !helpers::has_regular_ancestor(cls);
550 auto inherit_name = name_helpers::klass_concrete_name(cls); 466 auto inherit_name = name_helpers::klass_concrete_name(cls);
551 467
552 if(!as_generator( 468 if(!as_generator(
@@ -571,7 +487,7 @@ struct klass
571 // For constructors with arguments, the parent is also required, as optional parameters can't come before non-optional paramenters. 487 // For constructors with arguments, the parent is also required, as optional parameters can't come before non-optional paramenters.
572 << scope_tab << "public " << inherit_name << "(Efl.Object parent" << ((constructors.size() > 0) ? "" : "= null") << "\n" 488 << scope_tab << "public " << inherit_name << "(Efl.Object parent" << ((constructors.size() > 0) ? "" : "= null") << "\n"
573 << scope_tab << scope_tab << scope_tab << *(", " << constructor_param ) << ") : " 489 << scope_tab << scope_tab << scope_tab << *(", " << constructor_param ) << ") : "
574 << (root ? "this" : "base") << "(" << name_helpers::klass_get_name(cls) << "(), typeof(" << inherit_name << "), parent)\n" 490 << "base(" << name_helpers::klass_get_name(cls) << "(), typeof(" << inherit_name << "), parent)\n"
575 << scope_tab << "{\n" 491 << scope_tab << "{\n"
576 << (*(scope_tab << scope_tab << constructor_invocation << "\n")) 492 << (*(scope_tab << scope_tab << constructor_invocation << "\n"))
577 << scope_tab << scope_tab << "FinishInstantiation();\n" 493 << scope_tab << scope_tab << "FinishInstantiation();\n"
@@ -579,9 +495,8 @@ struct klass
579 << scope_tab << "/// <summary>Initializes a new instance of the <see cref=\"" << inherit_name << "\"/> class.\n" 495 << scope_tab << "/// <summary>Initializes a new instance of the <see cref=\"" << inherit_name << "\"/> class.\n"
580 << 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.</summary>\n" 496 << 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.</summary>\n"
581 << scope_tab << "/// <param name=\"raw\">The native pointer to be wrapped.</param>\n" 497 << scope_tab << "/// <param name=\"raw\">The native pointer to be wrapped.</param>\n"
582 << scope_tab << "protected " << inherit_name << "(System.IntPtr raw)" << (root ? "" : " : base(raw)") << "\n" 498 << scope_tab << "protected " << inherit_name << "(System.IntPtr raw) : base(raw)\n"
583 << scope_tab << "{\n" 499 << scope_tab << "{\n"
584 << scope_tab << scope_tab << (root ? "handle = raw;\n" : "")
585 << scope_tab << "}\n\n" 500 << scope_tab << "}\n\n"
586 ).generate(sink, std::make_tuple(constructors, constructors, constructors), context)) 501 ).generate(sink, std::make_tuple(constructors, constructors, constructors), context))
587 return false; 502 return false;
@@ -603,124 +518,16 @@ struct klass
603 return false; 518 return false;
604 } 519 }
605 520
606 // Internal constructors
607 if (!root)
608 {
609 return as_generator(
610 scope_tab << "/// <summary>Initializes a new instance of the <see cref=\"" << inherit_name << "\"/> class.\n"
611 << scope_tab << "/// Internal usage: Constructor to forward the wrapper initialization to the root class that interfaces with native code. Should not be used directly.</summary>\n"
612 << scope_tab << "/// <param name=\"baseKlass\">The pointer to the base native Eo class.</param>\n"
613 << scope_tab << "/// <param name=\"managedType\">The managed type of the public constructor that originated this call.</param>\n"
614 << scope_tab << "/// <param name=\"parent\">The Efl.Object parent of this instance.</param>\n"
615 << scope_tab << "protected " << inherit_name << "(IntPtr baseKlass, System.Type managedType, Efl.Object parent) : base(baseKlass, managedType, parent)\n"
616 << scope_tab << "{\n"
617 << scope_tab << "}\n\n"
618 ).generate(sink, attributes::unused, context);
619
620 }
621
622 // Detailed constructors go only in root classes.
623 return as_generator(
624 /// Actual root costructor that creates class and instantiates
625 scope_tab << "/// <summary>Initializes a new instance of the <see cref=\"" << inherit_name << "\"/> class.\n"
626 << scope_tab << "/// Internal usage: Constructor to actually call the native library constructors. C# subclasses\n"
627 << scope_tab << "/// must use the public constructor only.</summary>\n"
628 << scope_tab << "/// <param name=\"baseKlass\">The pointer to the base native Eo class.</param>\n"
629 << scope_tab << "/// <param name=\"managedType\">The managed type of the public constructor that originated this call.</param>\n"
630 << scope_tab << "/// <param name=\"parent\">The Efl.Object parent of this instance.</param>\n"
631 << scope_tab << "protected " << inherit_name << "(IntPtr baseKlass, System.Type managedType, Efl.Object parent)\n"
632 << scope_tab << "{\n"
633 << scope_tab << scope_tab << "inherited = ((object)this).GetType() != managedType;\n"
634 << scope_tab << scope_tab << "IntPtr actual_klass = baseKlass;\n"
635 << scope_tab << scope_tab << "if (inherited)\n"
636 << scope_tab << scope_tab << "{\n"
637 << scope_tab << scope_tab << scope_tab << "actual_klass = Efl.Eo.ClassRegister.GetInheritKlassOrRegister(baseKlass, ((object)this).GetType());\n"
638 << scope_tab << scope_tab << "}\n\n"
639 << scope_tab << scope_tab << "handle = Efl.Eo.Globals.instantiate_start(actual_klass, parent);\n"
640 << scope_tab << scope_tab << "if (inherited)\n"
641 << scope_tab << scope_tab << "{\n"
642 << scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.PrivateDataSet(this);\n"
643 << scope_tab << scope_tab << "}\n"
644 << scope_tab << "}\n\n"
645
646 << scope_tab << "/// <summary>Finishes instantiating this object.\n"
647 << scope_tab << "/// Internal usage by generated code.</summary>\n"
648 << scope_tab << "protected void FinishInstantiation()\n"
649 << scope_tab << "{\n"
650 << scope_tab << scope_tab << "handle = Efl.Eo.Globals.instantiate_end(handle);\n"
651 << scope_tab << scope_tab << "Eina.Error.RaiseIfUnhandledException();\n"
652 << scope_tab << "}\n\n"
653
654 ).generate(sink, attributes::unused, context);
655 }
656
657
658 template <typename OutputIterator, typename Context>
659 bool generate_dispose_methods(OutputIterator sink, attributes::klass_def const& cls, Context const& context) const
660 {
661 if (helpers::has_regular_ancestor(cls))
662 return true;
663
664 std::string visibility = is_inherit_context(context) ? "protected virtual " : "private ";
665
666 auto inherit_name = name_helpers::klass_concrete_name(cls);
667
668 std::string events_gchandle;
669 if (cls.get_all_events().size() > 0)
670 {
671 auto events_gchandle_sink = std::back_inserter(events_gchandle);
672 if (!as_generator(scope_tab << scope_tab << scope_tab << "if (eoEvents.Count != 0)\n"
673 << scope_tab << scope_tab << scope_tab << "{\n"
674 << scope_tab << scope_tab << scope_tab << scope_tab << "GCHandle gcHandle = GCHandle.Alloc(eoEvents);\n"
675 << scope_tab << scope_tab << scope_tab << scope_tab << "gcHandlePtr = GCHandle.ToIntPtr(gcHandle);\n"
676 << scope_tab << scope_tab << scope_tab << "}\n\n")
677 .generate(events_gchandle_sink, attributes::unused, context))
678 return false;
679 }
680
681 return as_generator( 521 return as_generator(
682 522 scope_tab << "/// <summary>Initializes a new instance of the <see cref=\"" << inherit_name << "\"/> class.\n"
683 scope_tab << "///<summary>Destructor.</summary>\n" 523 << scope_tab << "/// Internal usage: Constructor to forward the wrapper initialization to the root class that interfaces with native code. Should not be used directly.</summary>\n"
684 << scope_tab << "~" << inherit_name << "()\n" 524 << scope_tab << "/// <param name=\"baseKlass\">The pointer to the base native Eo class.</param>\n"
685 << scope_tab << "{\n" 525 << scope_tab << "/// <param name=\"managedType\">The managed type of the public constructor that originated this call.</param>\n"
686 << scope_tab << scope_tab << "Dispose(false);\n" 526 << scope_tab << "/// <param name=\"parent\">The Efl.Object parent of this instance.</param>\n"
687 << scope_tab << "}\n\n" 527 << scope_tab << "protected " << inherit_name << "(IntPtr baseKlass, System.Type managedType, Efl.Object parent) : base(baseKlass, managedType, parent)\n"
688 528 << scope_tab << "{\n"
689 << scope_tab << "///<summary>Releases the underlying native instance.</summary>\n" 529 << scope_tab << "}\n\n"
690 << scope_tab << visibility << "void Dispose(bool disposing)\n" 530 ).generate(sink, attributes::unused, context);
691 << scope_tab << "{\n"
692 << scope_tab << scope_tab << "if (handle != System.IntPtr.Zero)\n"
693 << scope_tab << scope_tab << "{\n"
694 << scope_tab << scope_tab << scope_tab << "IntPtr h = handle;\n"
695 << scope_tab << scope_tab << scope_tab << "handle = IntPtr.Zero;\n\n"
696
697 << scope_tab << scope_tab << scope_tab << "IntPtr gcHandlePtr = IntPtr.Zero;\n"
698 << events_gchandle
699
700 << scope_tab << scope_tab << scope_tab << "if (disposing)\n"
701 << scope_tab << scope_tab << scope_tab << "{\n"
702 << scope_tab << scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.efl_mono_native_dispose(h, gcHandlePtr);\n"
703 << scope_tab << scope_tab << scope_tab << "}\n"
704 << scope_tab << scope_tab << scope_tab << "else\n"
705 << scope_tab << scope_tab << scope_tab << "{\n"
706
707 << scope_tab << scope_tab << scope_tab << scope_tab << "Monitor.Enter(Efl.All.InitLock);\n"
708 << scope_tab << scope_tab << scope_tab << scope_tab << "if (Efl.All.MainLoopInitialized)\n"
709 << scope_tab << scope_tab << scope_tab << scope_tab << "{\n"
710 << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.efl_mono_thread_safe_native_dispose(h, gcHandlePtr);\n"
711 << scope_tab << scope_tab << scope_tab << scope_tab << "}\n\n"
712 << scope_tab << scope_tab << scope_tab << scope_tab << "Monitor.Exit(Efl.All.InitLock);\n"
713 << scope_tab << scope_tab << scope_tab << "}\n"
714 << scope_tab << scope_tab << "}\n\n"
715 << scope_tab << "}\n\n"
716
717 << scope_tab << "///<summary>Releases the underlying native instance.</summary>\n"
718 << scope_tab << "public void Dispose()\n"
719 << scope_tab << "{\n"
720 << scope_tab << scope_tab << "Dispose(true);\n"
721 << scope_tab << scope_tab << "GC.SuppressFinalize(this);\n"
722 << scope_tab << "}\n\n"
723 ).generate(sink, attributes::unused, context);
724 } 531 }
725 532
726 template <typename OutputIterator, typename Context> 533 template <typename OutputIterator, typename Context>
@@ -730,80 +537,6 @@ struct klass
730 if (!has_events(cls)) 537 if (!has_events(cls))
731 return true; 538 return true;
732 539
733 std::string visibility = is_inherit_context(context) ? "protected " : "private ";
734
735 if (!helpers::has_regular_ancestor(cls))
736 {
737 // Callback registration functions
738 if (!as_generator(
739 scope_tab << "///<summary>Adds a new event handler, registering it to the native event. For internal use only.</summary>\n"
740 << scope_tab << "///<param name=\"lib\">The name of the native library definining the event.</param>\n"
741 << scope_tab << "///<param name=\"key\">The name of the native event.</param>\n"
742 << scope_tab << "///<param name=\"evtCaller\">Delegate to be called by native code on event raising.</param>\n"
743 << scope_tab << "///<param name=\"evtDelegate\">Managed delegate that will be called by evtCaller on event raising.</param>\n"
744 << scope_tab << visibility << "void AddNativeEventHandler(string lib, string key, Efl.EventCb evtCaller, object evtDelegate)\n"
745 << scope_tab << "{\n"
746
747 << scope_tab << scope_tab << "IntPtr desc = Efl.EventDescription.GetNative(lib, key);\n"
748 << scope_tab << scope_tab << "if (desc == IntPtr.Zero)\n"
749 << scope_tab << scope_tab << "{\n"
750 << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to get native event {key}\");\n"
751 << scope_tab << scope_tab << "}\n\n"
752
753 << scope_tab << scope_tab << "if (eoEvents.ContainsKey((desc, evtDelegate)))\n"
754 << scope_tab << scope_tab << "{\n"
755 << scope_tab << scope_tab << scope_tab << "Eina.Log.Warning($\"Event proxy for event {key} already registered!\");\n"
756 << scope_tab << scope_tab << scope_tab << "return;\n"
757 << scope_tab << scope_tab << "}\n\n"
758
759 << scope_tab << scope_tab << "IntPtr evtCallerPtr = Marshal.GetFunctionPointerForDelegate(evtCaller);\n"
760 << scope_tab << scope_tab << "if (!Efl.Eo.Globals.efl_event_callback_priority_add(handle, desc, 0, evtCallerPtr, IntPtr.Zero))\n"
761 << scope_tab << scope_tab << "{\n"
762 << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to add event proxy for event {key}\");\n"
763 << scope_tab << scope_tab << scope_tab << "return;\n"
764 << scope_tab << scope_tab << "}\n\n"
765
766 << scope_tab << scope_tab << "eoEvents[(desc, evtDelegate)] = (evtCallerPtr, evtCaller);\n"
767 << scope_tab << scope_tab << "Eina.Error.RaiseIfUnhandledException();\n"
768 << scope_tab << "}\n\n"
769
770 << scope_tab << "///<summary>Removes the given event handler for the given event. For internal use only.</summary>\n"
771 << scope_tab << "///<param name=\"lib\">The name of the native library definining the event.</param>\n"
772 << scope_tab << "///<param name=\"key\">The name of the native event.</param>\n"
773 << scope_tab << "///<param name=\"evtDelegate\">The delegate to be removed.</param>\n"
774 << scope_tab << visibility << "void RemoveNativeEventHandler(string lib, string key, object evtDelegate)\n"
775 << scope_tab << "{\n"
776
777 << scope_tab << scope_tab << "IntPtr desc = Efl.EventDescription.GetNative(lib, key);\n"
778 << scope_tab << scope_tab << "if (desc == IntPtr.Zero)\n"
779 << scope_tab << scope_tab << "{\n"
780 << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to get native event {key}\");\n"
781 << scope_tab << scope_tab << scope_tab << "return;\n"
782 << scope_tab << scope_tab << "}\n\n"
783
784 << scope_tab << scope_tab << "var evtPair = (desc, evtDelegate);\n"
785 << scope_tab << scope_tab << "if (eoEvents.TryGetValue(evtPair, out var caller))\n"
786 << scope_tab << scope_tab << "{\n"
787
788 << scope_tab << scope_tab << scope_tab << "if (!Efl.Eo.Globals.efl_event_callback_del(handle, desc, caller.evtCallerPtr, IntPtr.Zero))\n"
789 << scope_tab << scope_tab << scope_tab << "{\n"
790 << scope_tab << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Failed to remove event proxy for event {key}\");\n"
791 << scope_tab << scope_tab << scope_tab << scope_tab << "return;\n"
792 << scope_tab << scope_tab << scope_tab << "}\n\n"
793
794 << scope_tab << scope_tab << scope_tab << "eoEvents.Remove(evtPair);\n"
795 << scope_tab << scope_tab << scope_tab << "Eina.Error.RaiseIfUnhandledException();\n"
796 << scope_tab << scope_tab << "}\n"
797 << scope_tab << scope_tab << "else\n"
798 << scope_tab << scope_tab << "{\n"
799 << scope_tab << scope_tab << scope_tab << "Eina.Log.Error($\"Trying to remove proxy for event {key} when it is nothing registered.\");\n"
800 << scope_tab << scope_tab << "}\n"
801 << scope_tab << "}\n\n"
802 )
803 .generate(sink, NULL, context))
804 return false;
805 }
806
807 // Self events 540 // Self events
808 if (!as_generator(*(event_definition(cls, cls))).generate(sink, cls.events, context)) 541 if (!as_generator(*(event_definition(cls, cls))).generate(sink, cls.events, context))
809 return false; 542 return false;
diff --git a/src/bindings/mono/eina_mono/eina_container_common.cs b/src/bindings/mono/eina_mono/eina_container_common.cs
index 630b084207..7bc17d9215 100644
--- a/src/bindings/mono/eina_mono/eina_container_common.cs
+++ b/src/bindings/mono/eina_mono/eina_container_common.cs
@@ -229,7 +229,7 @@ public class EflObjectElementTraits<T> : IBaseElementTraits<T>
229 { 229 {
230 if (nat != IntPtr.Zero) 230 if (nat != IntPtr.Zero)
231 { 231 {
232 Efl.Eo.Globals.efl_mono_thread_safe_efl_unref(nat); 232 Efl.Eo.Globals.efl_mono_thread_safe_efl_unref(nat);
233 } 233 }
234 } 234 }
235 235
diff --git a/src/bindings/mono/eo_mono/EoWrapper.cs b/src/bindings/mono/eo_mono/EoWrapper.cs
new file mode 100644
index 0000000000..b6ea619923
--- /dev/null
+++ b/src/bindings/mono/eo_mono/EoWrapper.cs
@@ -0,0 +1,253 @@
1using System;
2using System.Runtime.InteropServices;
3using System.Runtime.CompilerServices;
4using System.Threading;
5
6namespace Efl
7{
8
9namespace Eo
10{
11
12public abstract class EoWrapper : IWrapper, IDisposable
13{
14 protected readonly object eventLock = new object();
15 protected bool inherited = false;
16 protected System.IntPtr handle = IntPtr.Zero;
17
18 private static Efl.EventCb ownershipUniqueDelegate = new Efl.EventCb(OwnershipUniqueCallback);
19 private static Efl.EventCb ownershipSharedDelegate = new Efl.EventCb(OwnershipSharedCallback);
20
21 /// <summary>Initializes a new instance of the <see cref="Object"/> class.
22 /// Internal usage: Constructs an instance from a native pointer. This is used when interacting with C code and should not be used directly.</summary>
23 /// <param name="raw">The native pointer to be wrapped.</param>
24 protected EoWrapper(System.IntPtr raw)
25 {
26 handle = raw;
27 AddWrapperSupervisor();
28 }
29
30 /// <summary>Initializes a new instance of the <see cref="Object"/> class.
31 /// Internal usage: Constructor to actually call the native library constructors. C# subclasses
32 /// must use the public constructor only.</summary>
33 /// <param name="baseKlass">The pointer to the base native Eo class.</param>
34 /// <param name="managedType">The managed type of the public constructor that originated this call.</param>
35 /// <param name="parent">The Efl.Object parent of this instance.</param>
36 /// <param name="file">Name of the file from where the constructor is called.</param>
37 /// <param name="line">Number of the line from where the constructor is called.</param>
38 protected EoWrapper(IntPtr baseKlass, System.Type managedType, Efl.Object parent,
39 [CallerFilePath] string file = null,
40 [CallerLineNumber] int line = 0)
41 {
42 inherited = ((object)this).GetType() != managedType;
43 IntPtr actual_klass = baseKlass;
44 if (inherited)
45 {
46 actual_klass = Efl.Eo.ClassRegister.GetInheritKlassOrRegister(baseKlass, ((object)this).GetType());
47 }
48
49 // Creation of the unfinalized Eo handle
50 Eina.Log.Debug($"Instantiating from klass 0x{actual_klass.ToInt64():x}");
51 System.IntPtr parent_ptr = System.IntPtr.Zero;
52 if (parent != null)
53 {
54 parent_ptr = parent.NativeHandle;
55 }
56
57 handle = Efl.Eo.Globals._efl_add_internal_start(file, line, actual_klass, parent_ptr, 1, 0);
58 if (handle == System.IntPtr.Zero)
59 {
60 throw new Exception("Instantiation failed");
61 }
62
63 Eina.Log.Debug($"Eo instance right after internal_start 0x{handle.ToInt64():x} with refcount {Efl.Eo.Globals.efl_ref_count(handle)}");
64 Eina.Log.Debug($"Parent was 0x{parent_ptr.ToInt64()}");
65
66 // Creation of wrapper supervisor
67 AddWrapperSupervisor();
68 }
69
70 /// <summary>Destructor.</summary>
71 ~EoWrapper()
72 {
73 Dispose(false);
74 }
75
76 /// <summary>Pointer to the native instance.</summary>
77 public System.IntPtr NativeHandle
78 {
79 get { return handle; }
80 }
81
82 /// <summary>Pointer to the native class description.</summary>
83 public abstract System.IntPtr NativeClass
84 {
85 get;
86 }
87
88 /// <summary>Releases the underlying native instance.</summary>
89 protected virtual void Dispose(bool disposing)
90 {
91 if (disposing && handle != System.IntPtr.Zero)
92 {
93 IntPtr h = handle;
94 handle = IntPtr.Zero;
95 Efl.Eo.Globals.efl_mono_native_dispose(h);
96 }
97 else
98 {
99 Monitor.Enter(Efl.All.InitLock);
100 if (Efl.All.MainLoopInitialized)
101 {
102 Efl.Eo.Globals.efl_mono_thread_safe_native_dispose(handle);
103 }
104
105 Monitor.Exit(Efl.All.InitLock);
106 }
107 }
108
109 /// <summary>Turns the native pointer into a string representation.</summary>
110 /// <returns>A string with the type and the native pointer for this object.</returns>
111 public override String ToString()
112 {
113 return $"{this.GetType().Name}@[0x{(UInt64)handle:x}]";
114 }
115
116 /// <summary>Releases the underlying native instance.</summary>
117 public void Dispose()
118 {
119 Dispose(true);
120 GC.SuppressFinalize(this);
121 }
122
123 /// <summary>Releases the underlying Eo object.
124 ///
125 /// This method is a C# counterpart to the C `efl_del` function. It removes the parent of the object
126 /// and releases the Eo reference it was holding.
127 /// </summary>
128 public void Del()
129 {
130 // FIXME Implement this
131 ((Efl.Object)this).SetParent(null);
132 Dispose();
133 }
134
135 /// <summary>Finishes instantiating this object.
136 /// Internal usage by generated code.</summary>
137 protected void FinishInstantiation()
138 {
139 Eina.Log.Debug("calling efl_add_internal_end");
140 var h = Efl.Eo.Globals._efl_add_end(handle, 1, 0);
141 Eina.Log.Debug($"efl_add_end returned eo 0x{handle.ToInt64():x}");
142
143 // if (h == IntPtr.Zero) // TODO
144 // {
145 // }
146
147 handle = h;
148 }
149
150 /// <summary>Adds a new event handler, registering it to the native event. For internal use only.</summary>
151 /// <param name="lib">The name of the native library definining the event.</param>
152 /// <param name="key">The name of the native event.</param>
153 /// <param name="evtCaller">Delegate to be called by native code on event raising.</param>
154 /// <param name="evtDelegate">Managed delegate that will be called by evtCaller on event raising.</param>
155 protected void AddNativeEventHandler(string lib, string key, Efl.EventCb evtCaller, object evtDelegate)
156 {
157 IntPtr desc = Efl.EventDescription.GetNative(lib, key);
158 if (desc == IntPtr.Zero)
159 {
160 Eina.Log.Error($"Failed to get native event {key}");
161 return;
162 }
163
164 var wsPtr = Efl.Eo.Globals.efl_mono_wrapper_supervisor_get(handle);
165 var ws = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(wsPtr);
166 if (ws.EoEvents.ContainsKey((desc, evtDelegate)))
167 {
168 Eina.Log.Warning($"Event proxy for event {key} already registered!");
169 return;
170 }
171
172 IntPtr evtCallerPtr = Marshal.GetFunctionPointerForDelegate(evtCaller);
173 if (!Efl.Eo.Globals.efl_event_callback_priority_add(handle, desc, 0, evtCallerPtr, wsPtr))
174 {
175 Eina.Log.Error($"Failed to add event proxy for event {key}");
176 return;
177 }
178
179 ws.EoEvents[(desc, evtDelegate)] = (evtCallerPtr, evtCaller);
180 Eina.Error.RaiseIfUnhandledException();
181 }
182
183 /// <summary>Removes the given event handler for the given event. For internal use only.</summary>
184 /// <param name="lib">The name of the native library definining the event.</param>
185 /// <param name="key">The name of the native event.</param>
186 /// <param name="evtDelegate">The delegate to be removed.</param>
187 protected void RemoveNativeEventHandler(string lib, string key, object evtDelegate)
188 {
189 IntPtr desc = Efl.EventDescription.GetNative(lib, key);
190 if (desc == IntPtr.Zero)
191 {
192 Eina.Log.Error($"Failed to get native event {key}");
193 return;
194 }
195
196 var wsPtr = Efl.Eo.Globals.efl_mono_wrapper_supervisor_get(handle);
197 var ws = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(wsPtr);
198 var evtPair = (desc, evtDelegate);
199 if (ws.EoEvents.TryGetValue(evtPair, out var caller))
200 {
201 if (!Efl.Eo.Globals.efl_event_callback_del(handle, desc, caller.evtCallerPtr, wsPtr))
202 {
203 Eina.Log.Error($"Failed to remove event proxy for event {key}");
204 return;
205 }
206
207 ws.EoEvents.Remove(evtPair);
208 Eina.Error.RaiseIfUnhandledException();
209 }
210 else
211 {
212 Eina.Log.Error($"Trying to remove proxy for event {key} when it is not registered.");
213 }
214 }
215
216 private static void OwnershipUniqueCallback(IntPtr data, ref Efl.Event.NativeStruct evt)
217 {
218 var ws = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(data);
219 ws.MakeUnique();
220 }
221
222 private static void OwnershipSharedCallback(IntPtr data, ref Efl.Event.NativeStruct evt)
223 {
224 var ws = Efl.Eo.Globals.WrapperSupervisorPtrToManaged(data);
225 ws.MakeShared();
226 }
227
228 /// <sumary>Create and set to the internal native state a C# supervisor for this Eo wrapper. For internal use only.</sumary>
229 private void AddWrapperSupervisor()
230 {
231 var ws = new Efl.Eo.WrapperSupervisor(this);
232 Efl.Eo.Globals.SetWrapperSupervisor(handle, ws);
233 if (Efl.Eo.Globals.efl_ref_count(handle) > 1)
234 {
235 ws.MakeShared();
236 }
237
238 AddOwnershipEventHandlers();
239 }
240
241 /// <summary>Register handlers to ownership events, in order to control the object lifetime. For internal use only.</summary>
242 private void AddOwnershipEventHandlers()
243 {
244 AddNativeEventHandler("eo", "_EFL_EVENT_INVALIDATE", ownershipUniqueDelegate, ownershipUniqueDelegate);
245 AddNativeEventHandler("eo", "_EFL_EVENT_OWNERSHIP_UNIQUE", ownershipUniqueDelegate, ownershipUniqueDelegate);
246 AddNativeEventHandler("eo", "_EFL_EVENT_OWNERSHIP_SHARED", ownershipSharedDelegate, ownershipSharedDelegate);
247 Eina.Error.RaiseIfUnhandledException();
248 }
249}
250
251} // namespace Global
252
253} // namespace Efl
diff --git a/src/bindings/mono/eo_mono/WrapperSupervisor.cs b/src/bindings/mono/eo_mono/WrapperSupervisor.cs
new file mode 100644
index 0000000000..21ef05268f
--- /dev/null
+++ b/src/bindings/mono/eo_mono/WrapperSupervisor.cs
@@ -0,0 +1,64 @@
1using System;
2using EventDictionary = System.Collections.Generic.Dictionary<(System.IntPtr desc, object evtDelegate), (System.IntPtr evtCallerPtr, Efl.EventCb evtCaller)>;
3
4namespace Efl
5{
6
7namespace Eo
8{
9
10/// <summary>Observe the ownership state of an Eo wrapper and control its life-cycle.</summary>
11public class WrapperSupervisor
12{
13 private System.WeakReference weakRef;
14#pragma warning disable CS0414
15 private Efl.Eo.IWrapper sharedRef;
16#pragma warning restore CS0414
17 private EventDictionary eoEvents;
18
19 /// <summary>Create a new supervisor for the given.</summary>
20 /// <param name="obj">Efl object to be supervised.</param>
21 public WrapperSupervisor(Efl.Eo.IWrapper obj)
22 {
23 weakRef = new WeakReference(obj);
24 sharedRef = null;
25 eoEvents = new EventDictionary();
26 }
27
28 /// <summary>Efl object being supervised.</summary>
29 public Efl.Eo.IWrapper Target
30 {
31 get
32 {
33 return (Efl.Eo.IWrapper) weakRef.Target;
34 }
35 }
36
37 /// <summary>Dictionary that holds the events related with the supervised object.</summary>
38 public EventDictionary EoEvents
39 {
40 get
41 {
42 return eoEvents;
43 }
44 }
45
46 /// <summary>To be called when the object is uniquely owned by C#, removing its strong reference and making it available to garbage collection.</summary>
47 public void MakeUnique()
48 {
49 sharedRef = null;
50 }
51
52 /// <summary>To be called when the object is owned in the native library too, adding a strong reference to it and making it unavailable for garbage collection.</summary>
53 public void MakeShared()
54 {
55 if (this.Target == null)
56 throw new InvalidOperationException("Tried to make a null reference shared.");
57 sharedRef = this.Target;
58 }
59}
60
61}
62
63}
64
diff --git a/src/bindings/mono/eo_mono/iwrapper.cs b/src/bindings/mono/eo_mono/iwrapper.cs
index a410a091bb..9e73ca4687 100644
--- a/src/bindings/mono/eo_mono/iwrapper.cs
+++ b/src/bindings/mono/eo_mono/iwrapper.cs
@@ -48,6 +48,10 @@ public class Globals
48 public delegate IntPtr 48 public delegate IntPtr
49 _efl_add_internal_start_delegate([MarshalAs(UnmanagedType.LPStr)] String file, int line, 49 _efl_add_internal_start_delegate([MarshalAs(UnmanagedType.LPStr)] String file, int line,
50 IntPtr klass, IntPtr parent, byte is_ref, byte is_fallback); 50 IntPtr klass, IntPtr parent, byte is_ref, byte is_fallback);
51
52 [DllImport(efl.Libs.CustomExports)] public static extern IntPtr efl_mono_wrapper_supervisor_get(IntPtr eo);
53 [DllImport(efl.Libs.CustomExports)] public static extern void efl_mono_wrapper_supervisor_set(IntPtr eo, IntPtr ws);
54
51 [DllImport(efl.Libs.Eo)] public static extern IntPtr 55 [DllImport(efl.Libs.Eo)] public static extern IntPtr
52 _efl_add_internal_start([MarshalAs(UnmanagedType.LPStr)] String file, int line, 56 _efl_add_internal_start([MarshalAs(UnmanagedType.LPStr)] String file, int line,
53 IntPtr klass, IntPtr parent, byte is_ref, byte is_fallback); 57 IntPtr klass, IntPtr parent, byte is_ref, byte is_fallback);
@@ -68,11 +72,11 @@ public class Globals
68 [DllImport(efl.Libs.Eo)] public static extern int 72 [DllImport(efl.Libs.Eo)] public static extern int
69 efl_ref_count(IntPtr eo); 73 efl_ref_count(IntPtr eo);
70 [DllImport(efl.Libs.CustomExports)] public static extern void 74 [DllImport(efl.Libs.CustomExports)] public static extern void
71 efl_mono_gchandle_callbacks_set(Efl.FreeGCHandleCb freeGCHandleCb, Efl.RemoveEventsCb removeEventsCb); 75 efl_mono_wrapper_supervisor_callbacks_set(Efl.FreeWrapperSupervisorCb freeWrapperSupervisorCb);
72 [DllImport(efl.Libs.CustomExports)] public static extern void 76 [DllImport(efl.Libs.CustomExports)] public static extern void
73 efl_mono_native_dispose(IntPtr eo, IntPtr gcHandle); 77 efl_mono_native_dispose(IntPtr eo);
74 [DllImport(efl.Libs.CustomExports)] public static extern void 78 [DllImport(efl.Libs.CustomExports)] public static extern void
75 efl_mono_thread_safe_native_dispose(IntPtr eo, IntPtr gcHandle); 79 efl_mono_thread_safe_native_dispose(IntPtr eo);
76 [DllImport(efl.Libs.CustomExports)] public static extern void 80 [DllImport(efl.Libs.CustomExports)] public static extern void
77 efl_mono_thread_safe_efl_unref(IntPtr eo); 81 efl_mono_thread_safe_efl_unref(IntPtr eo);
78 82
@@ -231,7 +235,7 @@ public class Globals
231 description.version = 2; // EO_VERSION 235 description.version = 2; // EO_VERSION
232 description.name = class_name; 236 description.name = class_name;
233 description.class_type = 0; // REGULAR 237 description.class_type = 0; // REGULAR
234 description.data_size = (UIntPtr)8; 238 description.data_size = (UIntPtr)0;
235 description.class_initializer = IntPtr.Zero; 239 description.class_initializer = IntPtr.Zero;
236 description.class_constructor = IntPtr.Zero; 240 description.class_constructor = IntPtr.Zero;
237 description.class_destructor = IntPtr.Zero; 241 description.class_destructor = IntPtr.Zero;
@@ -246,6 +250,8 @@ public class Globals
246 IntPtr description_ptr = Eina.MemoryNative.Alloc(Marshal.SizeOf(description)); 250 IntPtr description_ptr = Eina.MemoryNative.Alloc(Marshal.SizeOf(description));
247 Marshal.StructureToPtr(description, description_ptr, false); 251 Marshal.StructureToPtr(description, description_ptr, false);
248 252
253 // FIXME: description_ptr seems to be leaking memory even after an eo_shutdown
254
249 var interface_list = EoG.get_efl_interfaces(type); 255 var interface_list = EoG.get_efl_interfaces(type);
250 256
251 Eina.Log.Debug($"Going to register new class named {class_name}"); 257 Eina.Log.Debug($"Going to register new class named {class_name}");
@@ -442,60 +448,26 @@ public class Globals
442 } 448 }
443 } 449 }
444 450
445 public static IntPtr instantiate_start(IntPtr klass, Efl.Object parent, 451 public static Efl.Eo.WrapperSupervisor WrapperSupervisorPtrToManaged(IntPtr wsPtr)
446 [CallerFilePath] string file = null,
447 [CallerLineNumber] int line = 0)
448 { 452 {
449 Eina.Log.Debug($"Instantiating from klass 0x{klass.ToInt64():x}"); 453 return (Efl.Eo.WrapperSupervisor) GCHandle.FromIntPtr(wsPtr).Target;
450 System.IntPtr parent_ptr = System.IntPtr.Zero;
451 if (parent != null)
452 {
453 parent_ptr = parent.NativeHandle;
454 }
455
456 System.IntPtr eo = Efl.Eo.Globals._efl_add_internal_start(file, line, klass, parent_ptr, 1, 0);
457 if (eo == System.IntPtr.Zero)
458 {
459 throw new Exception("Instantiation failed");
460 }
461
462 Eina.Log.Debug($"Eo instance right after internal_start 0x{eo.ToInt64():x} with refcount {Efl.Eo.Globals.efl_ref_count(eo)}");
463 Eina.Log.Debug($"Parent was 0x{parent_ptr.ToInt64()}");
464 return eo;
465 } 454 }
466 455
467 public static IntPtr instantiate_end(IntPtr eo) 456 public static Efl.Eo.WrapperSupervisor GetWrapperSupervisor(IntPtr eo)
468 { 457 {
469 Eina.Log.Debug("calling efl_add_internal_end"); 458 var wsPtr = Efl.Eo.Globals.efl_mono_wrapper_supervisor_get(eo);
470 eo = Efl.Eo.Globals._efl_add_end(eo, 1, 0); 459 if (wsPtr == IntPtr.Zero)
471 Eina.Log.Debug($"efl_add_end returned eo 0x{eo.ToInt64():x}");
472 return eo;
473 }
474
475 public static void PrivateDataSet(Efl.Eo.IWrapper obj)
476 {
477 Eina.Log.Debug($"Calling data_scope_get with obj {obj.NativeHandle.ToInt64():x} and klass {obj.NativeClass.ToInt64():x}");
478 IntPtr pd = Efl.Eo.Globals.efl_data_scope_get(obj.NativeHandle, obj.NativeClass);
479 { 460 {
480 GCHandle gch = GCHandle.Alloc(obj); 461 return null;
481 EolianPD epd;
482 epd.pointer = GCHandle.ToIntPtr(gch);
483 Marshal.StructureToPtr(epd, pd, false);
484 } 462 }
463
464 return WrapperSupervisorPtrToManaged(wsPtr);
485 } 465 }
486 466
487 public static Efl.Eo.IWrapper PrivateDataGet(IntPtr pd) 467 public static void SetWrapperSupervisor(IntPtr eo, Efl.Eo.WrapperSupervisor ws)
488 { 468 {
489 EolianPD epd = (EolianPD)Marshal.PtrToStructure(pd, typeof(EolianPD)); 469 GCHandle gch = GCHandle.Alloc(ws);
490 if (epd.pointer != IntPtr.Zero) 470 Efl.Eo.Globals.efl_mono_wrapper_supervisor_set(eo, GCHandle.ToIntPtr(gch));
491 {
492 GCHandle gch = GCHandle.FromIntPtr(epd.pointer);
493 return (Efl.Eo.IWrapper)gch.Target;
494 }
495 else
496 {
497 return null;
498 }
499 } 471 }
500 472
501 public static void free_dict_values(Dictionary<String, IntPtr> dict) 473 public static void free_dict_values(Dictionary<String, IntPtr> dict)
@@ -601,93 +573,101 @@ public class Globals
601 return null; 573 return null;
602 } 574 }
603 575
604 IntPtr eoKlass = efl_class_get(handle); 576 Efl.Eo.Globals.efl_ref(handle);
605 577 try
606 if (eoKlass == IntPtr.Zero)
607 { 578 {
608 throw new InvalidOperationException($"Can't get Eo class for object handle 0x{handle.ToInt64():x}"); 579 var ws = Efl.Eo.Globals.GetWrapperSupervisor(handle);
609 } 580 if (ws != null && ws.Target != null)
581 {
582 if (!shouldIncRef)
583 {
584 Efl.Eo.Globals.efl_unref(handle);
585 }
610 586
611 var managedType = ClassRegister.GetManagedType(eoKlass); 587 return ws.Target;
588 }
612 589
613 if (managedType == null) 590 IntPtr eoKlass = efl_class_get(handle);
614 {
615 IntPtr nativeName = efl_class_name_get(eoKlass);
616 var name = Eina.StringConversion.NativeUtf8ToManagedString(nativeName);
617 591
618 throw new InvalidOperationException($"Can't get Managed class for object handle 0x{handle.ToInt64():x} with native class [{name}]"); 592 if (eoKlass == IntPtr.Zero)
619 } 593 {
594 throw new InvalidOperationException($"Can't get Eo class for object handle 0x{handle.ToInt64():x}");
595 }
620 596
621 // Pure C# classes that inherit from generated classes store their C# instance in their 597 var managedType = ClassRegister.GetManagedType(eoKlass);
622 // Eo private data field.
623 if (!IsGeneratedClass(managedType))
624 {
625 Efl.Eo.IWrapper instance = null;
626 IntPtr pd = efl_data_scope_get(handle, eoKlass);
627 598
628 if (pd != IntPtr.Zero) 599 if (managedType == null)
629 { 600 {
630 instance = PrivateDataGet(pd); 601 IntPtr nativeName = efl_class_name_get(eoKlass);
631 } 602 var name = Eina.StringConversion.NativeUtf8ToManagedString(nativeName);
632 603
633 return instance; 604 throw new InvalidOperationException($"Can't get Managed class for object handle 0x{handle.ToInt64():x} with native class [{name}]");
634 } 605 }
635 606
636 System.Reflection.ConstructorInfo constructor = null; 607 Debug.Assert(IsGeneratedClass(managedType));
608 System.Reflection.ConstructorInfo constructor = null;
637 609
638 try 610 try
639 { 611 {
640 var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; 612 var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
641 constructor = managedType.GetConstructor(flags, null, new Type[1] { typeof(System.IntPtr) }, null); 613 constructor = managedType.GetConstructor(flags, null, new Type[1] { typeof(System.IntPtr) }, null);
642 } 614 }
643 catch (InvalidOperationException) 615 catch (InvalidOperationException)
644 { 616 {
645 throw new InvalidOperationException($"Can't get constructor for type {managedType}"); 617 throw new InvalidOperationException($"Can't get constructor for type {managedType}");
646 } 618 }
647 619
648 var ret = constructor.Invoke(new object[1] { handle }) as Efl.Eo.IWrapper; 620 var ret = (Efl.Eo.IWrapper) constructor.Invoke(new object[1] { handle });
649 621
650 if (ret != null && shouldIncRef) 622 if (ret == null)
651 Efl.Eo.Globals.efl_ref(handle); 623 {
624 throw new InvalidOperationException($"Can't construct type {managedType} from IntPtr handle");
625 }
652 626
653 return ret; 627 if (shouldIncRef)
654 } 628 {
629 Efl.Eo.Globals.efl_ref(handle);
630 }
655 631
656 private static Efl.FreeGCHandleCb FreeGCHandleCallbackDelegate = new Efl.FreeGCHandleCb(FreeGCHandleCallback); 632 return ret;
657 public static void FreeGCHandleCallback(IntPtr gcHandlePtr)
658 {
659 try
660 {
661 GCHandle gcHandle = GCHandle.FromIntPtr(gcHandlePtr);
662 gcHandle.Free();
663 } 633 }
664 catch (Exception e) 634 finally
665 { 635 {
666 Eina.Log.Error(e.ToString()); 636 Efl.Eo.Globals.efl_unref(handle);
667 Eina.Error.Set(Eina.Error.UNHANDLED_EXCEPTION);
668 } 637 }
669 } 638 }
670 639
671 private static Efl.RemoveEventsCb RemoveEventsCallbackDelegate = new Efl.RemoveEventsCb(RemoveEventsCallback); 640 private static Efl.FreeWrapperSupervisorCb FreeWrapperSupervisorCallbackDelegate = new Efl.FreeWrapperSupervisorCb(FreeWrapperSupervisorCallback);
672 public static void RemoveEventsCallback(IntPtr obj, IntPtr gcHandlePtr) 641 public static void FreeWrapperSupervisorCallback(IntPtr eo)
673 { 642 {
674 try 643 try
675 { 644 {
676 GCHandle gcHandle = GCHandle.FromIntPtr(gcHandlePtr); 645 var wsPtr = Efl.Eo.Globals.efl_mono_wrapper_supervisor_get(eo);
677 var eoEvents = gcHandle.Target as Dictionary<(IntPtr desc, object evtDelegate), (IntPtr evtCallerPtr, Efl.EventCb evtCaller)>; 646 if (wsPtr == IntPtr.Zero)
678 if (eoEvents == null)
679 { 647 {
680 Eina.Log.Error($"Invalid event dictionary [GCHandle pointer: {gcHandlePtr}]"); 648 Eina.Log.Error($"Invalid wrapper supervisor [Eo pointer: {eo.ToInt64():x}]");
681 return; 649 return;
682 } 650 }
683 651
684 foreach (var item in eoEvents) 652 Efl.Eo.Globals.efl_mono_wrapper_supervisor_set(eo, IntPtr.Zero);
653
654 GCHandle gch = GCHandle.FromIntPtr(wsPtr);
655 var ws = (Efl.Eo.WrapperSupervisor) gch.Target;
656 foreach (var item in ws.EoEvents)
685 { 657 {
686 if (!efl_event_callback_del(obj, item.Key.desc, item.Value.evtCallerPtr, IntPtr.Zero)) 658 if (!efl_event_callback_del(eo, item.Key.desc, item.Value.evtCallerPtr, wsPtr))
687 { 659 {
688 Eina.Log.Error($"Failed to remove event proxy for event {item.Key.desc} [cb: {item.Value.evtCallerPtr}]"); 660 Eina.Log.Error($"Failed to remove event proxy for event {item.Key.desc} [eo: {eo.ToInt64():x}; cb: {item.Value.evtCallerPtr.ToInt64():x}]");
689 } 661 }
690 } 662 }
663
664 // Free the native eo
665 Efl.Eo.Globals.efl_unref(eo);
666
667 // now the WrapperSupervisor can be collected, and so its member:
668 // - the event dictionary
669 // - and the EoWrapper if it is still pinned
670 gch.Free();
691 } 671 }
692 catch (Exception e) 672 catch (Exception e)
693 { 673 {
@@ -698,7 +678,7 @@ public class Globals
698 678
699 public static void SetNativeDisposeCallbacks() 679 public static void SetNativeDisposeCallbacks()
700 { 680 {
701 efl_mono_gchandle_callbacks_set(FreeGCHandleCallbackDelegate, RemoveEventsCallbackDelegate); 681 efl_mono_wrapper_supervisor_callbacks_set(FreeWrapperSupervisorCallbackDelegate);
702 } 682 }
703 683
704 public static void ThreadSafeFreeCbExec(EinaFreeCb cbFreeCb, IntPtr cbData) 684 public static void ThreadSafeFreeCbExec(EinaFreeCb cbFreeCb, IntPtr cbData)
@@ -800,8 +780,6 @@ public static class ClassRegister
800 string name = Eina.StringConversion.NativeUtf8ToManagedString(namePtr) 780 string name = Eina.StringConversion.NativeUtf8ToManagedString(namePtr)
801 .Replace("_", ""); // Convert Efl C name to C# name 781 .Replace("_", ""); // Convert Efl C name to C# name
802 782
803 var klass_type = Efl.Eo.Globals.efl_class_type_get(klass);
804
805 // Check if this is an internal implementation of an abstract class 783 // Check if this is an internal implementation of an abstract class
806 var abstract_impl_suffix = "Realized"; 784 var abstract_impl_suffix = "Realized";
807 if (name.EndsWith(abstract_impl_suffix)) 785 if (name.EndsWith(abstract_impl_suffix))
@@ -813,6 +791,7 @@ public static class ClassRegister
813 } 791 }
814 792
815 // When converting to managed, interfaces and mixins gets the 'I' prefix. 793 // When converting to managed, interfaces and mixins gets the 'I' prefix.
794 var klass_type = Efl.Eo.Globals.efl_class_type_get(klass);
816 if (klass_type == Efl.Eo.Globals.EflClassType.Interface || klass_type == Efl.Eo.Globals.EflClassType.Mixin) 795 if (klass_type == Efl.Eo.Globals.EflClassType.Interface || klass_type == Efl.Eo.Globals.EflClassType.Mixin)
817 { 796 {
818 var pos = name.LastIndexOf("."); 797 var pos = name.LastIndexOf(".");
diff --git a/src/bindings/mono/eo_mono/meson.build b/src/bindings/mono/eo_mono/meson.build
index 8aca4004df..013b1cc97f 100644
--- a/src/bindings/mono/eo_mono/meson.build
+++ b/src/bindings/mono/eo_mono/meson.build
@@ -2,7 +2,9 @@ mono_files += files(
2 'iwrapper.cs', 2 'iwrapper.cs',
3 'workaround.cs', 3 'workaround.cs',
4 'FunctionWrapper.cs', 4 'FunctionWrapper.cs',
5 'NativeModule.cs' 5 'NativeModule.cs',
6 'EoWrapper.cs',
7 'WrapperSupervisor.cs'
6) 8)
7 9
8if host_machine.system() == 'windows' 10if host_machine.system() == 'windows'
diff --git a/src/bindings/mono/eo_mono/workaround.cs b/src/bindings/mono/eo_mono/workaround.cs
index e32c921862..9f22b90fa6 100644
--- a/src/bindings/mono/eo_mono/workaround.cs
+++ b/src/bindings/mono/eo_mono/workaround.cs
@@ -44,12 +44,6 @@ public struct Efl_Object_Ops
44 public UIntPtr count; 44 public UIntPtr count;
45}; 45};
46 46
47[StructLayout(LayoutKind.Sequential)]
48public struct EolianPD
49{
50 public IntPtr pointer;
51}
52
53#pragma warning disable 0169 47#pragma warning disable 0169
54 48
55public struct EvasObjectBoxLayout 49public struct EvasObjectBoxLayout
@@ -115,8 +109,7 @@ public struct EventDescription
115}; 109};
116 110
117public delegate void EventCb(System.IntPtr data, ref Event.NativeStruct evt); 111public delegate void EventCb(System.IntPtr data, ref Event.NativeStruct evt);
118public delegate void FreeGCHandleCb(System.IntPtr gcHandle); 112public delegate void FreeWrapperSupervisorCb(System.IntPtr obj);
119public delegate void RemoveEventsCb(System.IntPtr obj, System.IntPtr gcHandle);
120 113
121[StructLayout(LayoutKind.Sequential)] 114[StructLayout(LayoutKind.Sequential)]
122public struct TextCursorCursor 115public struct TextCursorCursor
diff --git a/src/lib/efl_mono/efl_custom_exports_mono.c b/src/lib/efl_mono/efl_custom_exports_mono.c
index c4a3b54bc5..55f0054da0 100644
--- a/src/lib/efl_mono/efl_custom_exports_mono.c
+++ b/src/lib/efl_mono/efl_custom_exports_mono.c
@@ -23,44 +23,39 @@
23# endif 23# endif
24#endif /* ! _WIN32 */ 24#endif /* ! _WIN32 */
25 25
26typedef void (*Efl_Mono_Free_GCHandle_Cb)(void *gchandle);
27typedef void (*Efl_Mono_Remove_Events_Cb)(Eo *obj, void *gchandle);
28 26
29static Efl_Mono_Free_GCHandle_Cb _efl_mono_free_gchandle_call = NULL; 27EAPI const char *efl_mono_wrapper_supervisor_key_get()
30static Efl_Mono_Remove_Events_Cb _efl_mono_remove_events_call = NULL; 28{
29 return "__c#_wrapper_supervisor";
30}
31 31
32EAPI void efl_mono_gchandle_callbacks_set(Efl_Mono_Free_GCHandle_Cb free_gchandle_cb, Efl_Mono_Remove_Events_Cb remove_events_cb) 32EAPI void *efl_mono_wrapper_supervisor_get(Eo *eo)
33{ 33{
34 _efl_mono_free_gchandle_call = free_gchandle_cb; 34 return efl_key_data_get(eo, efl_mono_wrapper_supervisor_key_get());
35 _efl_mono_remove_events_call = remove_events_cb;
36} 35}
37 36
38EAPI void efl_mono_native_dispose(Eo *obj, void* gchandle) 37EAPI void efl_mono_wrapper_supervisor_set(Eo *eo, void *ws)
39{ 38{
40 if (gchandle) _efl_mono_remove_events_call(obj, gchandle); 39 efl_key_data_set(eo, efl_mono_wrapper_supervisor_key_get(), ws);
41 efl_unref(obj);
42 if (gchandle) _efl_mono_free_gchandle_call(gchandle);
43} 40}
44 41
45typedef struct _Efl_Mono_Native_Dispose_Data 42typedef void (*Efl_Mono_Free_Wrapper_Supervisor_Cb)(Eo *obj);
43
44static Efl_Mono_Free_Wrapper_Supervisor_Cb _efl_mono_free_wrapper_supervisor_call = NULL;
45
46EAPI void efl_mono_wrapper_supervisor_callbacks_set(Efl_Mono_Free_Wrapper_Supervisor_Cb free_wrapper_supervisor_cb)
46{ 47{
47 Eo *obj; 48 _efl_mono_free_wrapper_supervisor_call = free_wrapper_supervisor_cb;
48 void *gchandle; 49}
49} Efl_Mono_Native_Dispose_Data;
50 50
51static void _efl_mono_native_dispose_cb(void *data) 51EAPI void efl_mono_native_dispose(Eo *obj)
52{ 52{
53 Efl_Mono_Native_Dispose_Data *dd = data; 53 _efl_mono_free_wrapper_supervisor_call(obj);
54 efl_mono_native_dispose(dd->obj, dd->gchandle);
55 free(dd);
56} 54}
57 55
58EAPI void efl_mono_thread_safe_native_dispose(Eo *obj, void* gchandle) 56EAPI void efl_mono_thread_safe_native_dispose(Eo *obj)
59{ 57{
60 Efl_Mono_Native_Dispose_Data *dd = malloc(sizeof(Efl_Mono_Native_Dispose_Data)); 58 ecore_main_loop_thread_safe_call_async((Ecore_Cb)efl_mono_native_dispose, obj);
61 dd->obj = obj;
62 dd->gchandle = gchandle;
63 ecore_main_loop_thread_safe_call_async(_efl_mono_native_dispose_cb, dd);
64} 59}
65 60
66static void _efl_mono_unref_cb(void *obj) 61static void _efl_mono_unref_cb(void *obj)
diff --git a/src/tests/efl_mono/Eo.cs b/src/tests/efl_mono/Eo.cs
index 95b6b6395e..be66f0842c 100644
--- a/src/tests/efl_mono/Eo.cs
+++ b/src/tests/efl_mono/Eo.cs
@@ -510,4 +510,19 @@ class TestProvider
510 } 510 }
511} 511}
512 512
513class TestObjectDeletion
514{
515 public static void test_object_deletion()
516 {
517 var obj = new Dummy.PartHolder();
518 var part = obj.OnePart;
519
520 Test.AssertNotNull(part);
521
522 part.Del();
523
524 Test.AssertNull(obj.OnePart);
525 }
526}
527
513} 528}
diff --git a/src/tests/efl_mono/Inheritance.cs b/src/tests/efl_mono/Inheritance.cs
index befdd3a7b4..74c1086e78 100644
--- a/src/tests/efl_mono/Inheritance.cs
+++ b/src/tests/efl_mono/Inheritance.cs
@@ -35,6 +35,46 @@ class TestInheritance
35 } 35 }
36 } 36 }
37 37
38 internal class Inherit3Parent : Dummy.TestObject
39 {
40 public bool disposed = false;
41 public bool childDisposed = false;
42
43 ~Inherit3Parent()
44 {
45 Console.WriteLine("finalizer called for parent");
46 }
47
48 protected override void Dispose(bool disposing)
49 {
50 Console.WriteLine("Dispose parent");
51 base.Dispose(disposing);
52 }
53 }
54
55 internal class Inherit3Child : Dummy.TestObject
56 {
57 Inherit3Parent parent;
58 public Inherit3Child(Inherit3Parent parent) : base(parent)
59 {
60 // WARNING: Uncommenting the line below causes the parent-child cycle to leak.
61 // The GC won't be able to collect it.
62 // this.parent = parent;
63 }
64
65 ~Inherit3Child()
66 {
67 Console.WriteLine("finalizer called for child");
68 }
69
70 protected override void Dispose(bool disposing)
71 {
72 /* parent.childDisposed = true; */
73 Console.WriteLine("Dispose parent");
74 base.Dispose(disposing);
75 }
76 }
77
38 public static void test_inherit_from_regular_class() 78 public static void test_inherit_from_regular_class()
39 { 79 {
40 var obj = new Inherit1(); 80 var obj = new Inherit1();
@@ -50,6 +90,55 @@ class TestInheritance
50 string s = Dummy.InheritHelper.ReceiveDummyAndCallInStringshare(obj); 90 string s = Dummy.InheritHelper.ReceiveDummyAndCallInStringshare(obj);
51 Test.AssertEquals ("Hello World", s); 91 Test.AssertEquals ("Hello World", s);
52 } 92 }
93
94 private static void CreateAndCheckInheritedObjects(out WeakReference parentWRef, out WeakReference childWRef)
95 {
96 var parent = new Inherit3Parent();
97 var child = new Inherit3Child(parent);
98
99 parentWRef = new WeakReference(parent);
100 childWRef = new WeakReference(child);
101
102 Console.WriteLine($"Parent [{parent.ToString()}] has {Efl.Eo.Globals.efl_ref_count(parent.NativeHandle)} refs");
103 Console.WriteLine($"Child [{child.ToString()}] has {Efl.Eo.Globals.efl_ref_count(child.NativeHandle)} refs");
104
105 child = null;
106
107 System.GC.Collect(System.GC.MaxGeneration, GCCollectionMode.Forced, true, true);
108 System.GC.WaitForPendingFinalizers();
109 Efl.App.AppMain.Iterate();
110
111 child = (Inherit3Child) childWRef.Target;
112
113 Test.AssertNotNull(parent);
114 Test.AssertNotNull(child);
115 Test.AssertEquals(false, parent.disposed);
116 Test.AssertEquals(false, parent.childDisposed);
117
118 Console.WriteLine($"Parent [{parent.ToString()}] has {Efl.Eo.Globals.efl_ref_count(parent.NativeHandle)} refs");
119 Console.WriteLine($"Child [{child.ToString()}] has {Efl.Eo.Globals.efl_ref_count(child.NativeHandle)} refs");
120
121 parent = null;
122 child = null;
123 }
124
125 public static void test_inherit_lifetime()
126 {
127 WeakReference parentWRef;
128 WeakReference childWRef;
129
130 CreateAndCheckInheritedObjects(out parentWRef, out childWRef);
131
132 // Two invocations to iterate a the child wasn't being released with a single one
133 Test.CollectAndIterate();
134 Test.CollectAndIterate();
135
136 var parent = (Dummy.TestObject) parentWRef.Target;
137 var child = (Dummy.TestObject) childWRef.Target;
138
139 Test.AssertNull(parent);
140 Test.AssertNull(child);
141 }
53} 142}
54 143
55} 144}
diff --git a/src/tests/efl_mono/TestUtils.cs b/src/tests/efl_mono/TestUtils.cs
index 657bdb0300..b66f15a0bc 100644
--- a/src/tests/efl_mono/TestUtils.cs
+++ b/src/tests/efl_mono/TestUtils.cs
@@ -43,13 +43,14 @@ public static class Test
43 [CallerFilePath] string file = null, 43 [CallerFilePath] string file = null,
44 [CallerMemberName] string member = null) 44 [CallerMemberName] string member = null)
45 { 45 {
46 if (file == null) 46 if (expected == null && actual == null)
47 file = "(unknown file)"; 47 return;
48 if (member == null) 48 if (expected == null || !expected.Equals(actual))
49 member = "(unknown member)"; 49 {
50 if (expected == null) 50 if (file == null)
51 throw new AssertionException($"{file}:{line} ({member}) Null expected value. Use AssertNull."); 51 file = "(unknown file)";
52 if (!expected.Equals(actual)) { 52 if (member == null)
53 member = "(unknown member)";
53 if (msg == null || msg.Length == 0) 54 if (msg == null || msg.Length == 0)
54 msg = $"Expected \"{expected}\", actual \"{actual}\""; 55 msg = $"Expected \"{expected}\", actual \"{actual}\"";
55 throw new AssertionException($"{file}:{line} ({member}) {msg}"); 56 throw new AssertionException($"{file}:{line} ({member}) {msg}");
@@ -62,13 +63,12 @@ public static class Test
62 [CallerFilePath] string file = null, 63 [CallerFilePath] string file = null,
63 [CallerMemberName] string member = null) 64 [CallerMemberName] string member = null)
64 { 65 {
65 if (file == null) 66 if (expected == null ? actual == null : expected.Equals(actual))
66 file = "(unknown file)"; 67 {
67 if (member == null) 68 if (file == null)
68 member = "(unknown member)"; 69 file = "(unknown file)";
69 if (expected == null) 70 if (member == null)
70 throw new AssertionException($"{file}:{line} ({member}) Null expected value. Use AssertNull."); 71 member = "(unknown member)";
71 if (expected.Equals(actual)) {
72 if (msg == null || msg.Length == 0) 72 if (msg == null || msg.Length == 0)
73 msg = $"Expected \"{expected}\" shouldn't be equal to actual \"{actual}\""; 73 msg = $"Expected \"{expected}\" shouldn't be equal to actual \"{actual}\"";
74 throw new AssertionException($"{file}:{line} ({member}) {msg}"); 74 throw new AssertionException($"{file}:{line} ({member}) {msg}");
diff --git a/src/tests/efl_mono/dummy_part_holder.c b/src/tests/efl_mono/dummy_part_holder.c
index b595ac1f8d..aaeee6cec8 100644
--- a/src/tests/efl_mono/dummy_part_holder.c
+++ b/src/tests/efl_mono/dummy_part_holder.c
@@ -6,6 +6,16 @@ typedef struct Dummy_Part_Holder_Data
6 Eo *two; 6 Eo *two;
7} Dummy_Part_Holder_Data; 7} Dummy_Part_Holder_Data;
8 8
9void part_deleted_cb(void *data, const Efl_Event *evt)
10{
11 Dummy_Part_Holder_Data *pd = data;
12
13 if (evt->object == pd->one)
14 pd->one = NULL;
15 else if (evt->object == pd->two)
16 pd->two = NULL;
17}
18
9// Part holder 19// Part holder
10static Efl_Object* 20static Efl_Object*
11_dummy_part_holder_efl_object_constructor(Eo *obj, Dummy_Part_Holder_Data *pd) 21_dummy_part_holder_efl_object_constructor(Eo *obj, Dummy_Part_Holder_Data *pd)
@@ -16,12 +26,25 @@ _dummy_part_holder_efl_object_constructor(Eo *obj, Dummy_Part_Holder_Data *pd)
16 if (!efl_parent_get(obj)) 26 if (!efl_parent_get(obj))
17 { 27 {
18 pd->one = efl_add(DUMMY_TEST_OBJECT_CLASS, obj, efl_name_set(efl_added, "part_one")); 28 pd->one = efl_add(DUMMY_TEST_OBJECT_CLASS, obj, efl_name_set(efl_added, "part_one"));
29 efl_event_callback_add(pd->one, EFL_EVENT_DEL, part_deleted_cb, pd);
19 pd->two = efl_add(DUMMY_TEST_OBJECT_CLASS, obj, efl_name_set(efl_added, "part_two")); 30 pd->two = efl_add(DUMMY_TEST_OBJECT_CLASS, obj, efl_name_set(efl_added, "part_two"));
31 efl_event_callback_add(pd->two, EFL_EVENT_DEL, part_deleted_cb, pd);
32
33
20 } 34 }
21 35
22 return obj; 36 return obj;
23} 37}
24 38
39static void
40_dummy_part_holder_efl_object_destructor(EINA_UNUSED Eo* obj, Dummy_Part_Holder_Data *pd)
41{
42 if (pd->one)
43 efl_parent_set(pd->one, NULL);
44 if (pd->two)
45 efl_parent_set(pd->two, NULL);
46}
47
25Efl_Object *_dummy_part_holder_efl_part_part_get(EINA_UNUSED const Eo *obj, Dummy_Part_Holder_Data *pd, const char *name) 48Efl_Object *_dummy_part_holder_efl_part_part_get(EINA_UNUSED const Eo *obj, Dummy_Part_Holder_Data *pd, const char *name)
26{ 49{
27 if (!strcmp(name, "one")) 50 if (!strcmp(name, "one"))
diff --git a/src/tests/efl_mono/dummy_part_holder.eo b/src/tests/efl_mono/dummy_part_holder.eo
index d65fd12e5b..b95f1a5cdc 100644
--- a/src/tests/efl_mono/dummy_part_holder.eo
+++ b/src/tests/efl_mono/dummy_part_holder.eo
@@ -9,5 +9,6 @@ class @beta Dummy.Part_Holder extends Dummy.Test_Object implements Efl.Part {
9 implements { 9 implements {
10 Efl.Part.part_get; 10 Efl.Part.part_get;
11 Efl.Object.constructor; 11 Efl.Object.constructor;
12 Efl.Object.destructor;
12 } 13 }
13} 14}