diff --git a/src/bin/eolian_mono/eolian/mono/blacklist.hh b/src/bin/eolian_mono/eolian/mono/blacklist.hh index df94b6acb6..02cda670d9 100644 --- a/src/bin/eolian_mono/eolian/mono/blacklist.hh +++ b/src/bin/eolian_mono/eolian/mono/blacklist.hh @@ -16,6 +16,7 @@ inline bool is_function_blacklisted(std::string const& c_name) { return c_name == "efl_event_callback_array_priority_add" + || c_name == "efl_constructor" || c_name == "efl_player_position_get" || c_name == "efl_ui_widget_focus_set" || c_name == "efl_ui_widget_focus_get" diff --git a/src/bin/eolian_mono/eolian/mono/events.hh b/src/bin/eolian_mono/eolian/mono/events.hh index f688602b2d..ea042fb08e 100644 --- a/src/bin/eolian_mono/eolian/mono/events.hh +++ b/src/bin/eolian_mono/eolian/mono/events.hh @@ -73,7 +73,7 @@ struct unpack_event_args_visitor } bool operator()(grammar::attributes::klass_name const& cls) const { - return as_generator("new " + name_helpers::klass_full_concrete_name(cls) + "(evt.Info)").generate(sink, attributes::unused, *context); + return as_generator("(Efl.Eo.Globals.CreateWrapperFor(evt.Info) as " + name_helpers::klass_full_concrete_name(cls) + ")").generate(sink, attributes::unused, *context); } bool operator()(attributes::complex_type_def const&) const { diff --git a/src/bin/eolian_mono/eolian/mono/function_definition.hh b/src/bin/eolian_mono/eolian/mono/function_definition.hh index 0d6eff628e..3abf82fe11 100644 --- a/src/bin/eolian_mono/eolian/mono/function_definition.hh +++ b/src/bin/eolian_mono/eolian/mono/function_definition.hh @@ -103,7 +103,7 @@ struct native_function_definition_generator /****/ << scope_tab << scope_tab << "Eina.Log.Debug(\"function " << string << " was called\");\n" /****/ - << scope_tab << scope_tab << "Efl.Eo.IWrapper wrapper = Efl.Eo.Globals.data_get(pd);\n" + << scope_tab << scope_tab << "Efl.Eo.IWrapper wrapper = Efl.Eo.Globals.PrivateDataGet(pd);\n" << scope_tab << scope_tab << "if(wrapper != null) {\n" << scope_tab << scope_tab << scope_tab << eolian_mono::native_function_definition_preamble() << scope_tab << scope_tab << scope_tab << "try {\n" diff --git a/src/bin/eolian_mono/eolian/mono/klass.hh b/src/bin/eolian_mono/eolian/mono/klass.hh index 20045de39b..2f0bc62d6c 100644 --- a/src/bin/eolian_mono/eolian/mono/klass.hh +++ b/src/bin/eolian_mono/eolian/mono/klass.hh @@ -31,20 +31,6 @@ namespace eolian_mono { -template -static bool generate_static_cast_method(OutputIterator sink, grammar::attributes::klass_def const& cls, Context const &context) -{ - return as_generator( - scope_tab << "///Casts obj into an instance of this type.\n" - << scope_tab << "public " << (helpers::has_regular_ancestor(cls) ? "new " : "") <<"static " << name_helpers::klass_concrete_name(cls) << " static_cast(Efl.Object obj)\n" - << scope_tab << "{\n" - << scope_tab << scope_tab << "if (obj == null)\n" - << scope_tab << scope_tab << scope_tab << "throw new System.ArgumentNullException(\"obj\");\n" - << scope_tab << scope_tab << "return new " << name_helpers::klass_concrete_name(cls) << "(obj.NativeHandle);\n" - << scope_tab << "}\n" - ).generate(sink, nullptr, context); -} - template static bool generate_equals_method(OutputIterator sink, Context const &context) { @@ -110,7 +96,7 @@ struct klass suffix = "CLASS"; break; case attributes::class_type::abstract_: - class_type = "class"; + class_type = "abstract class"; suffix = "CLASS"; break; case attributes::class_type::mixin: @@ -207,7 +193,7 @@ struct klass }); // Concrete class for interfaces, mixins, etc. - if(class_type != "class") + if(class_type != "class" && class_type != "abstract class") { auto concrete_cxt = context_add_tag(class_context{class_context::concrete}, context); auto concrete_name = name_helpers::klass_concrete_name(cls); @@ -234,7 +220,7 @@ struct klass << ")] internal static extern System.IntPtr\n" << scope_tab << scope_tab << name_helpers::klass_get_name(cls) << "();\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 << "public " << concrete_name << "(System.IntPtr raw)" << (root ? "" : " : base(raw)") << "\n" + << scope_tab << "private " << concrete_name << "(System.IntPtr raw)" << (root ? "" : " : base(raw)") << "\n" << scope_tab << "{\n" << scope_tab << scope_tab << (root ? "handle = raw;\n" : "") << scope_tab << scope_tab << "RegisterEventProxies();\n" @@ -246,9 +232,6 @@ struct klass if (!generate_dispose_methods(sink, cls, concrete_cxt)) return false; - if (!generate_static_cast_method(sink, cls, concrete_cxt)) - return false; - if (!generate_equals_method(sink, concrete_cxt)) return false; @@ -296,7 +279,7 @@ struct klass } // Inheritable class - if(class_type == "class") + if(class_type == "class" || class_type == "abstract class") { auto inherit_cxt = context_add_tag(class_context{class_context::inherit}, context); @@ -327,9 +310,6 @@ struct klass if (!generate_dispose_methods(sink, cls, inherit_cxt)) return false; - if (!generate_static_cast_method(sink, cls, inherit_cxt)) - return false; - if (!generate_equals_method(sink, inherit_cxt)) return false; @@ -430,7 +410,7 @@ struct klass << scope_tab << "}\n" ).generate(sink, attributes::unused, inative_cxt)) return false; - + // Native method definitions if(!as_generator(*(native_function_definition(cls))) .generate(sink, helpers::get_all_implementable_methods(cls), inative_cxt)) return false; @@ -534,7 +514,7 @@ struct klass << scope_tab << scope_tab << "FinishInstantiation();\n" << scope_tab << "}\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 << "public " << inherit_name << "(System.IntPtr raw)" << (root ? "" : " : base(raw)") << "\n" + << scope_tab << "protected " << inherit_name << "(System.IntPtr raw)" << (root ? "" : " : base(raw)") << "\n" << scope_tab << "{\n" << scope_tab << scope_tab << (root ? "handle = raw;\n" : "") << scope_tab << scope_tab << "RegisterEventProxies();\n" @@ -542,6 +522,23 @@ struct klass ).generate(sink, std::make_tuple(constructors, constructors, constructors), context)) return false; + // Some abstract classes (like Efl.App) have a simple regular class that is used to instantiate them + // in a controlled manner. These fake-private classes can be returned from C and we use a similarly-named + // private class to be able to instantiate them when they get to the C# world. + if (cls.type == attributes::class_type::abstract_) + { + if (!as_generator( + scope_tab << "[Efl.Eo.PrivateNativeClass]\n" + << scope_tab << "private class " << inherit_name << "Realized : " << inherit_name << "\n" + << scope_tab << "{\n" + << scope_tab << scope_tab << "private " << inherit_name << "Realized(IntPtr ptr) : base(ptr)\n" + << scope_tab << scope_tab << "{\n" + << scope_tab << scope_tab << "}\n" + << scope_tab << "}\n" + ).generate(sink, attributes::unused, context)) + return false; + } + // Internal constructors if (!root) { @@ -564,13 +561,14 @@ struct klass << scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << "handle = Efl.Eo.Globals.instantiate_start(actual_klass, parent);\n" << scope_tab << scope_tab << "RegisterEventProxies();\n" + << scope_tab << scope_tab << "if (inherited)\n" + << scope_tab << scope_tab << "{\n" + << scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.PrivateDataSet(this);\n" + << scope_tab << scope_tab << "}\n" << scope_tab << "}\n" << scope_tab << "protected void FinishInstantiation()\n" << scope_tab << "{\n" - << scope_tab << scope_tab << "if (inherited) {\n" - << scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.data_set(this);\n" - << scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << "handle = Efl.Eo.Globals.instantiate_end(handle);\n" << scope_tab << scope_tab << "Eina.Error.RaiseIfUnhandledException();\n" << scope_tab << "}\n" diff --git a/src/bin/eolian_mono/eolian/mono/part_definition.hh b/src/bin/eolian_mono/eolian/mono/part_definition.hh index 7894086f95..06cdf1584f 100644 --- a/src/bin/eolian_mono/eolian/mono/part_definition.hh +++ b/src/bin/eolian_mono/eolian/mono/part_definition.hh @@ -27,8 +27,7 @@ struct part_definition_generator << scope_tab << "{\n" << scope_tab << scope_tab << "get\n" << scope_tab << scope_tab << "{\n" - << scope_tab << scope_tab << scope_tab << "Efl.Object obj = Efl.IPartNativeInherit.efl_part_get_ptr.Value.Delegate(NativeHandle, \"" << part.name << "\");\n" - << scope_tab << scope_tab << scope_tab << "return " << part_klass_name << ".static_cast(obj);\n" + << scope_tab << scope_tab << scope_tab << "return Efl.IPartNativeInherit.efl_part_get_ptr.Value.Delegate(NativeHandle, \"" << part.name << "\") as " << part_klass_name << ";\n" << scope_tab << scope_tab << "}\n" << scope_tab << "}\n" ).generate(sink, part.documentation, context); diff --git a/src/bin/eolian_mono/eolian/mono/struct_definition.hh b/src/bin/eolian_mono/eolian/mono/struct_definition.hh index 35f772554f..c059dd9cbe 100644 --- a/src/bin/eolian_mono/eolian/mono/struct_definition.hh +++ b/src/bin/eolian_mono/eolian/mono/struct_definition.hh @@ -159,10 +159,8 @@ struct to_external_field_convert_generator if (!as_generator( "\n" << indent << scope_tab << scope_tab << "_external_struct." << string - << " = (" << concrete_name << ") System.Activator.CreateInstance(typeof(" - << concrete_name << "), new System.Object[] {_internal_struct." << string << "});\n" - << indent << scope_tab << scope_tab << "Efl.Eo.Globals.efl_ref(_internal_struct." << string << ");\n") - .generate(sink, std::make_tuple(field_name, field_name, field_name), context)) + << " = (" << concrete_name << ") Efl.Eo.Globals.CreateWrapperFor(_internal_struct." << string << ");\n" + ).generate(sink, std::make_tuple(field_name, field_name), context)) return false; } else if (field.type.c_type == "Eina_Binbuf *" || field.type.c_type == "const Eina_Binbuf *") diff --git a/src/bindings/mono/eina_mono/eina_container_common.cs b/src/bindings/mono/eina_mono/eina_container_common.cs index 73a3a0f46a..9695c098da 100644 --- a/src/bindings/mono/eina_mono/eina_container_common.cs +++ b/src/bindings/mono/eina_mono/eina_container_common.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using System.Runtime.InteropServices; using System.Collections.Generic; +using System.Reflection; using Eina.Callbacks; using static Eina.HashNativeFunctions; @@ -196,13 +197,6 @@ public class StringElementTraits : IBaseElementTraits public class EflObjectElementTraits : IBaseElementTraits { - private System.Type concreteType = null; - - public EflObjectElementTraits(System.Type concrete) - { - concreteType = concrete; - } - public IntPtr ManagedToNativeAlloc(T man) { IntPtr h = ((Efl.Eo.IWrapper)man).NativeHandle; @@ -290,7 +284,7 @@ public class EflObjectElementTraits : IBaseElementTraits return default(T); } - return (T)Activator.CreateInstance(concreteType, Efl.Eo.Globals.efl_ref(nat)); + return (T) Efl.Eo.Globals.CreateWrapperFor(nat, shouldIncRef: true); } public T NativeToManagedRef(IntPtr nat) @@ -762,7 +756,9 @@ public static class TraitFunctions throw new Exception("Failed to get a suitable concrete class for this type."); } - traits = new EflObjectElementTraits(concrete); + // No need to pass concrete as the traits class will use reflection to get the actually most + // derived type returned. + traits = new EflObjectElementTraits(); } else if (IsString(type)) { diff --git a/src/bindings/mono/eo_mono/iwrapper.cs b/src/bindings/mono/eo_mono/iwrapper.cs index 43835aabd7..3600cdec50 100644 --- a/src/bindings/mono/eo_mono/iwrapper.cs +++ b/src/bindings/mono/eo_mono/iwrapper.cs @@ -4,6 +4,7 @@ using System; using System.Runtime.InteropServices; using System.Collections.Generic; using System.Diagnostics; +using System.Reflection; using System.Threading; using static Eina.NativeCustomExportFunctions; @@ -289,7 +290,7 @@ public class Globals return ifaces_lst; } - private static Efl.Eo.NativeClass get_native_class(System.Type type) + private static Efl.Eo.NativeClass GetNativeClass(System.Type type) { var attrs = System.Attribute.GetCustomAttributes(type); foreach (var attr in attrs) @@ -306,7 +307,7 @@ public class Globals public static byte class_initializer_call(IntPtr klass, System.Type type) { Eina.Log.Debug($"called with 0x{klass.ToInt64():x} {type}"); - Efl.Eo.NativeClass nativeClass = get_native_class(type.BaseType); + Efl.Eo.NativeClass nativeClass = GetNativeClass(type.BaseType); if (nativeClass != null) { @@ -320,7 +321,7 @@ public class Globals { if (!System.Array.Exists(base_interfaces, element => element == iface)) { - var nc = get_native_class(iface); + var nc = GetNativeClass(iface); if (nc != null) { var moredescs = nc.GetEoOps(type); @@ -442,7 +443,7 @@ public class Globals return eo; } - public static void data_set(Efl.Eo.IWrapper obj) + public static void PrivateDataSet(Efl.Eo.IWrapper obj) { Eina.Log.Debug($"Calling data_scope_get with obj {obj.NativeHandle.ToInt64():x} and klass {obj.NativeClass.ToInt64():x}"); IntPtr pd = Efl.Eo.Globals.efl_data_scope_get(obj.NativeHandle, obj.NativeClass); @@ -454,7 +455,7 @@ public class Globals } } - public static Efl.Eo.IWrapper data_get(IntPtr pd) + public static Efl.Eo.IWrapper PrivateDataGet(IntPtr pd) { EolianPD epd = (EolianPD)Marshal.PtrToStructure(pd, typeof(EolianPD)); if (epd.pointer != IntPtr.Zero) @@ -542,6 +543,80 @@ public class Globals return tcs.Task; } + + /// Returns whether the given type was generated by eolian-mono + /// The type to check. + /// True if generated by eolian-mono. False otherwise. + private static bool IsGeneratedClass(System.Type managedType) + { + return GetNativeClass(managedType) != null; + } + + /// Creates a new wrapper for the given Eo id. + /// + /// If the Eo was created from a non-generated class (i.e. C#-pure class), it returns + /// the C# instance handle stored in the Eo's private data. + /// + /// For generated-class Eo instance, we use reflection to get the correct C# type to re-wrap + /// it. + /// + /// + /// The Eo id to be wrapped. + /// Whether we should increase the refcount of the Eo instance. + /// The C# wrapper for this instance. + public static Efl.Eo.IWrapper CreateWrapperFor(System.IntPtr handle, bool shouldIncRef=true) + { + IntPtr eoKlass = efl_class_get(handle); + + if (eoKlass == IntPtr.Zero) + { + throw new InvalidOperationException($"Can't get Eo class for object handle 0x{handle.ToInt64():x}"); + } + + var managedType = ClassRegister.GetManagedType(eoKlass); + + if (managedType == null) + { + IntPtr nativeName = efl_class_name_get(eoKlass); + var name = Eina.StringConversion.NativeUtf8ToManagedString(nativeName); + + throw new InvalidOperationException($"Can't get Managed class for object handle 0x{handle.ToInt64():x} with native class [{name}]"); + } + + // Pure C# classes that inherit from generated classes store their C# instance in their + // Eo private data field. + if (!IsGeneratedClass(managedType)) + { + Efl.Eo.IWrapper instance = null; + IntPtr pd = efl_data_scope_get(handle, eoKlass); + + if (pd != IntPtr.Zero) + { + instance = PrivateDataGet(pd); + } + + return instance; + } + + System.Reflection.ConstructorInfo constructor = null; + + try + { + var flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + constructor = managedType.GetConstructor(flags, null, new Type[1] { typeof(System.IntPtr) }, null); + } + catch (InvalidOperationException) + { + throw new InvalidOperationException($"Can't get constructor for type {managedType}"); + } + + var ret = constructor.Invoke(new object[1] { handle }) as Efl.Eo.IWrapper; + + if (ret != null && shouldIncRef) + Efl.Eo.Globals.efl_ref(handle); + + return ret; + } } // Globals public static class Config @@ -560,7 +635,7 @@ public static class Config [System.AttributeUsage(System.AttributeTargets.Class | System.AttributeTargets.Interface, AllowMultiple = false, - Inherited = true) + Inherited = false) ] public abstract class NativeClass : System.Attribute { @@ -568,6 +643,22 @@ public abstract class NativeClass : System.Attribute public abstract System.Collections.Generic.List GetEoOps(System.Type type); } +/// Attribute for private native classes. +/// +/// For internal usage by generated code only. +public class PrivateNativeClass : NativeClass +{ + public override IntPtr GetEflClass() + { + return IntPtr.Zero; + } + + public override System.Collections.Generic.List GetEoOps(System.Type type) + { + return null; + } +} + public interface IWrapper { /// Pointer to internal Eo instance. @@ -605,6 +696,16 @@ public static class ClassRegister var klass_type = Efl.Eo.Globals.efl_class_type_get(klass); + // Check if this is an internal implementation of an abstract class + var abstract_impl_suffix = "Realized"; + if (name.EndsWith(abstract_impl_suffix)) + { + name = name.Substring(0, name.Length - abstract_impl_suffix.Length); + var lastDot = name.LastIndexOf("."); + var klassName = name.Substring(lastDot + 1); + name += "+" + klassName + abstract_impl_suffix; // '+' is the separator for nested classes + } + // When converting to managed, interfaces and mixins gets the 'I' prefix. if (klass_type == Efl.Eo.Globals.EflClassType.Interface || klass_type == Efl.Eo.Globals.EflClassType.Mixin) { @@ -795,6 +896,12 @@ public class MarshalTest : ICustomMarshaler public IntPtr MarshalManagedToNative(object ManagedObj) { Eina.Log.Debug("MarshalTest.MarshallManagedToNative"); + + if (ManagedObj == null) + { + return IntPtr.Zero; + } + var r = ((IWrapper)ManagedObj).NativeHandle; if (typeof(U) == typeof(OwnTag)) { @@ -806,14 +913,7 @@ public class MarshalTest : ICustomMarshaler public object MarshalNativeToManaged(IntPtr pNativeData) { - Eina.Log.Debug("MarshalTest.MarshalNativeToManaged"); - if (typeof(U) != typeof(OwnTag)) - { - Efl.Eo.Globals.efl_ref(pNativeData); - } - - return Activator.CreateInstance(typeof(T), new System.Object[] {pNativeData}); - //return null; + return Efl.Eo.Globals.CreateWrapperFor(pNativeData, shouldIncRef : typeof(U) != typeof(OwnTag)); } } diff --git a/src/tests/efl_mono/Eo.cs b/src/tests/efl_mono/Eo.cs index 578ad10790..f4e3f5d51f 100644 --- a/src/tests/efl_mono/Eo.cs +++ b/src/tests/efl_mono/Eo.cs @@ -131,7 +131,7 @@ class TestEoParent Test.AssertEquals(parent, child.GetParent()); - var parent_retrieved = Dummy.TestObject.static_cast(child.GetParent()); + var parent_retrieved = child.GetParent() as Dummy.TestObject; Test.AssertEquals(parent, parent_retrieved); } @@ -142,7 +142,7 @@ class TestEoParent Test.AssertEquals(parent, child.GetParent()); - Dummy.Numberwrapper parent_retrieved = Dummy.Numberwrapper.static_cast(child.GetParent()); + Dummy.Numberwrapper parent_retrieved = child.GetParent() as Dummy.Numberwrapper; Test.AssertEquals(parent, parent_retrieved); } @@ -160,7 +160,7 @@ class TestEoParent Test.AssertEquals(parent, child.GetParent()); - var parent_from_cast = Dummy.TestObject.static_cast(child.GetParent()); + var parent_from_cast = child.GetParent() as Derived; Test.AssertEquals(parent, parent_from_cast); } } @@ -430,7 +430,7 @@ class TestInterfaceConcrete public static void test_iface_concrete_methods() { var obj = new Dummy.TestObject(); - Dummy.ITestIface iface = Dummy.ITestIfaceConcrete.static_cast(obj); + Dummy.ITestIface iface = obj.ReturnIface(); iface.IfaceProp = 1970; Test.AssertEquals(iface.IfaceProp, 1970); @@ -443,7 +443,7 @@ class TestProvider { // Tests only the direction C# -> C var obj = new Dummy.TestObject(); - Dummy.Numberwrapper provider = Dummy.Numberwrapper.static_cast(obj.FindProvider(typeof(Dummy.Numberwrapper))); + Dummy.Numberwrapper provider = obj.FindProvider(typeof(Dummy.Numberwrapper)) as Dummy.Numberwrapper; Test.AssertEquals(provider.GetType(), typeof(Dummy.Numberwrapper)); Test.AssertEquals(provider.GetNumber(), 1999); } diff --git a/src/tests/efl_mono/dummy_test_object.eo b/src/tests/efl_mono/dummy_test_object.eo index 5475c81c3c..eabb6cd0ce 100644 --- a/src/tests/efl_mono/dummy_test_object.eo +++ b/src/tests/efl_mono/dummy_test_object.eo @@ -111,6 +111,10 @@ class Dummy.Test_Object extends Efl.Object implements Dummy.Test_Iface { return: Dummy.Test_Object; } + return_iface { + return: Dummy.Test_Iface; + } + int_out { params { @in x: int; diff --git a/src/tests/efl_mono/libefl_mono_native_test.c b/src/tests/efl_mono/libefl_mono_native_test.c index 7b4dd1a77f..8575ebac9c 100644 --- a/src/tests/efl_mono/libefl_mono_native_test.c +++ b/src/tests/efl_mono/libefl_mono_native_test.c @@ -45,11 +45,11 @@ # endif #endif +#include "dummy_test_iface.eo.h" +#include "dummy_inherit_iface.eo.h" #include "dummy_numberwrapper.eo.h" #include "dummy_test_object.eo.h" #include "dummy_child.eo.h" -#include "dummy_test_iface.eo.h" -#include "dummy_inherit_iface.eo.h" #include "dummy_inherit_helper.eo.h" #include "dummy_part_holder.eo.h" @@ -172,6 +172,11 @@ Efl_Object *_dummy_test_object_return_null_object(Eo *obj EINA_UNUSED, EINA_UNUS return NULL; } +Dummy_Test_Iface *_dummy_test_object_return_iface(Eo *obj, EINA_UNUSED Dummy_Test_Object_Data *pd) +{ + return obj; +} + void _dummy_test_object_int_out(EINA_UNUSED Eo *obj, EINA_UNUSED Dummy_Test_Object_Data *pd, int x, int *y) { *y = -x;