From dd89eb2fd1755c2816d97f28822639e03ed38608 Mon Sep 17 00:00:00 2001 From: Lauro Moura Date: Wed, 6 Feb 2019 17:50:28 -0200 Subject: [PATCH] efl-mono: Add support for Efl.Class Efl.Class (in practice, the return from the *_class_get() functions) can be used as argument to functions, like in Efl.Object.provider_find and Efl.Ui.Widget_Factory.item_class(get/set). This commits adds support by representing Efl.Class instances as System.Type in the C# API, allowing someone to do things like: `factory.ItemClass == typeof(MyFramework.MyButton)` It also supports user-defined classes that inherit from efl classes. --- src/bin/eolian_mono/eolian/mono/klass.hh | 29 ++-- .../eolian/mono/marshall_annotation.hh | 44 +++-- src/bin/eolian_mono/eolian/mono/type_impl.hh | 3 + .../mono/eina_mono/eina_container_common.cs | 16 +- src/bindings/mono/eo_mono/iwrapper.cs | 159 +++++++++++++++++- src/bindings/mono/meson.build | 1 + src/tests/efl_mono/Eo.cs | 13 ++ src/tests/efl_mono/dummy_test_object.eo | 1 + src/tests/efl_mono/libefl_mono_native_test.c | 12 ++ 9 files changed, 234 insertions(+), 44 deletions(-) diff --git a/src/bin/eolian_mono/eolian/mono/klass.hh b/src/bin/eolian_mono/eolian/mono/klass.hh index 6c02de67ed..e70729b6ae 100644 --- a/src/bin/eolian_mono/eolian/mono/klass.hh +++ b/src/bin/eolian_mono/eolian/mono/klass.hh @@ -347,6 +347,15 @@ struct klass return false; } + // Copied from nativeinherit class, used when setting up providers. + if(!as_generator( + scope_tab << "private static " << (root ? "" : "new ") << " IntPtr GetEflClassStatic()\n" + << scope_tab << "{\n" + << scope_tab << scope_tab << "return " << name_helpers::klass_get_full_name(cls) << "();\n" + << scope_tab << "}\n" + ).generate(sink, attributes::unused, inherit_cxt)) + return false; + if(!as_generator("}\n").generate(sink, attributes::unused, inherit_cxt)) return false; } @@ -454,7 +463,7 @@ struct klass << scope_tab << scope_tab << scope_tab << "if (((object)this).GetType() == typeof (" << inherit_name << "))\n" << scope_tab << scope_tab << scope_tab << scope_tab << "return " << native_inherit_full_name << ".GetEflClassStatic();\n" << scope_tab << scope_tab << scope_tab << "else\n" - << scope_tab << scope_tab << scope_tab << scope_tab << "return Efl.Eo.Globals.klasses[((object)this).GetType()];\n" + << scope_tab << scope_tab << scope_tab << scope_tab << "return Efl.Eo.ClassRegister.klassFromType[((object)this).GetType()];\n" << scope_tab << scope_tab << "}\n" << scope_tab << "}\n" ).generate(sink, attributes::unused, context)) @@ -512,7 +521,7 @@ struct klass // For constructors with arguments, the parent is also required, as optional parameters can't come before non-optional paramenters. << scope_tab << "public " << inherit_name << "(Efl.Object parent" << ((constructors.size() > 0) ? "" : "= null") << "\n" << scope_tab << scope_tab << scope_tab << *(", " << constructor_param ) << ") :\n" - << scope_tab << scope_tab << (root ? "this" : "base") << "(\"" << inherit_name << "\", " << name_helpers::klass_get_name(cls) << "(), typeof(" << inherit_name << "), parent)\n" + << scope_tab << scope_tab << (root ? "this" : "base") << "(" << name_helpers::klass_get_name(cls) << "(), typeof(" << inherit_name << "), parent)\n" << scope_tab << "{\n" << *(scope_tab << scope_tab << constructor_invocation << "\n" ) << scope_tab << scope_tab << "FinishInstantiation();\n" @@ -531,7 +540,7 @@ struct klass { return as_generator( scope_tab << "///Internal usage: Constructor to forward the wrapper initialization to the root class that interfaces with native code. Should not be used directly.\n" - << scope_tab << "protected " << inherit_name << "(String klass_name, IntPtr base_klass, System.Type managed_type, Efl.Object parent) : base(klass_name, base_klass, managed_type, parent) {}\n" + << scope_tab << "protected " << inherit_name << "(IntPtr base_klass, System.Type managed_type, Efl.Object parent) : base(base_klass, managed_type, parent) {}\n" ).generate(sink, attributes::unused, context); } @@ -539,22 +548,12 @@ struct klass // Detailed constructors go only in root classes. return as_generator( /// Actual root costructor that creates class and instantiates - scope_tab << "protected " << inherit_name << "(String klass_name, IntPtr base_klass, System.Type managed_type, Efl.Object parent)\n" + scope_tab << "protected " << inherit_name << "(IntPtr base_klass, System.Type managed_type, Efl.Object parent)\n" << scope_tab << "{\n" << scope_tab << scope_tab << "inherited = ((object)this).GetType() != managed_type;\n" << scope_tab << scope_tab << "IntPtr actual_klass = base_klass;\n" << scope_tab << scope_tab << "if (inherited) {\n" - << scope_tab << scope_tab << scope_tab << "if (!Efl.Eo.Globals.klasses.ContainsKey(((object)this).GetType())) {\n" - << scope_tab << scope_tab << scope_tab << scope_tab << "lock (klassAllocLock) {\n" - << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "actual_klass = Efl.Eo.Globals.register_class(klass_name, base_klass, ((object)this).GetType());\n" - << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "if (actual_klass == System.IntPtr.Zero) {\n" - << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "throw new System.InvalidOperationException(\"Failed to initialize class '" << inherit_name << "'\");\n" - << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "}\n" - << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << scope_tab << "Efl.Eo.Globals.klasses[((object)this).GetType()] = actual_klass;\n" - << scope_tab << scope_tab << scope_tab << scope_tab << "}\n" - << scope_tab << scope_tab << scope_tab << "}\n" - << scope_tab << scope_tab << scope_tab << "else\n" - << scope_tab << scope_tab << scope_tab << scope_tab << "actual_klass = Efl.Eo.Globals.klasses[((object)this).GetType()];\n" + << scope_tab << scope_tab << scope_tab << "actual_klass = Efl.Eo.ClassRegister.GetInheritKlassOrRegister(base_klass, ((object)this).GetType());\n" << scope_tab << scope_tab << "}\n" << scope_tab << scope_tab << "handle = Efl.Eo.Globals.instantiate_start(actual_klass, parent);\n" << scope_tab << scope_tab << "register_event_proxies();\n" diff --git a/src/bin/eolian_mono/eolian/mono/marshall_annotation.hh b/src/bin/eolian_mono/eolian/mono/marshall_annotation.hh index 6ddb990da8..744b4f8720 100644 --- a/src/bin/eolian_mono/eolian/mono/marshall_annotation.hh +++ b/src/bin/eolian_mono/eolian/mono/marshall_annotation.hh @@ -142,12 +142,22 @@ struct marshall_annotation_visitor_generate } bool operator()(attributes::klass_name const& klass_name) const { - const char no_return_prefix[] = "[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Efl.Eo.MarshalTest<"; - const char return_prefix[] = "[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Efl.Eo.MarshalTest<"; - return as_generator - ((is_return ? return_prefix : no_return_prefix) - << string << ", Efl.Eo." << (klass_name.base_qualifier & qualifier_info::is_own ? "OwnTag" : "NonOwnTag") << ">))]" - ).generate(sink, name_helpers::klass_full_concrete_name(klass_name), *context); + const char *return_prefix = is_return ? "return:" : ""; + const char *marshal_prefix = "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof("; + + std::string name = name_helpers::klass_full_concrete_name(klass_name); + + if (name == "Efl.Class") + name = "Efl.Eo.MarshalEflClass"; + else + { + auto own = klass_name.base_qualifier & qualifier_info::is_own ? "OwnTag" : "NonOwnTag"; + name = "Efl.Eo.MarshalTest<" + name + ", Efl.Eo." + own + ">"; + } + + return as_generator( + lit("[") << return_prefix << marshal_prefix << name << "))]" + ).generate(sink, name, *context); } bool operator()(attributes::complex_type_def const& c) const { @@ -252,12 +262,22 @@ struct marshall_native_annotation_visitor_generate } bool operator()(attributes::klass_name const& klass_name) const { - const char no_return_prefix[] = "[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Efl.Eo.MarshalTest<"; - const char return_prefix[] = "[return: MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Efl.Eo.MarshalTest<"; - return as_generator - ((is_return ? return_prefix : no_return_prefix) - << string << ", Efl.Eo." << (klass_name.base_qualifier & qualifier_info::is_own ? "OwnTag" : "NonOwnTag") << ">))]" - ).generate(sink, name_helpers::klass_full_concrete_name(klass_name), *context); + const char *return_prefix = is_return ? "return:" : ""; + const char *marshal_prefix = "MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof("; + + std::string name = name_helpers::klass_full_concrete_name(klass_name); + + if (name == "Efl.Class") + name = "Efl.Eo.MarshalEflClass"; + else + { + auto own = klass_name.base_qualifier & qualifier_info::is_own ? "OwnTag" : "NonOwnTag"; + name = "Efl.Eo.MarshalTest<" + name + ", Efl.Eo." + own + ">"; + } + + return as_generator( + lit("[") << return_prefix << marshal_prefix << name << "))]" + ).generate(sink, name, *context); } bool operator()(attributes::complex_type_def const& c) const { diff --git a/src/bin/eolian_mono/eolian/mono/type_impl.hh b/src/bin/eolian_mono/eolian/mono/type_impl.hh index 9810250909..f34c4796ec 100644 --- a/src/bin/eolian_mono/eolian/mono/type_impl.hh +++ b/src/bin/eolian_mono/eolian/mono/type_impl.hh @@ -309,6 +309,9 @@ struct visitor_generate } bool operator()(attributes::klass_name klass) const { + // Efl.Class is manually handled in a custom marshall to be represented by a System.Type. + if (name_helpers::klass_full_concrete_name(klass) == "Efl.Class") + return as_generator(lit("Type")).generate(sink, attributes::unused, *context); if(klass.type == attributes::class_type::regular || klass.type == attributes::class_type::abstract_) return as_generator(string).generate(sink, name_helpers::klass_full_concrete_name(klass), *context); else diff --git a/src/bindings/mono/eina_mono/eina_container_common.cs b/src/bindings/mono/eina_mono/eina_container_common.cs index 838a7edb07..bc2ce84a0c 100644 --- a/src/bindings/mono/eina_mono/eina_container_common.cs +++ b/src/bindings/mono/eina_mono/eina_container_common.cs @@ -530,23 +530,11 @@ public static class TraitFunctions if (type.IsInterface) { - string[] names = type.FullName.Split('.'); - names[names.Count() - 1] = names.Last().Substring(1); // Remove the leading 'I' (What about user-defined interfaces?) - - string fullName = string.Join(".", names); + string fullName = type.FullName + "Concrete"; return type.Assembly.GetType(fullName); // That was our best guess... } - - System.Type current = type; - while (current != null) - { - if (current.Name.EndsWith("Inherit")) - throw new Exception("Inherit-based classes are not currently supported."); - current = current.BaseType; - } - - return type; // Not inherited neither interface, so it should be a concrete. + return type; // Not interface, so it should be a concrete. } public static object RegisterTypeTraits() diff --git a/src/bindings/mono/eo_mono/iwrapper.cs b/src/bindings/mono/eo_mono/iwrapper.cs index 16acf6a0e6..5388116065 100644 --- a/src/bindings/mono/eo_mono/iwrapper.cs +++ b/src/bindings/mono/eo_mono/iwrapper.cs @@ -25,6 +25,8 @@ public class Globals { efl_unref(IntPtr eo); [DllImport(efl.Libs.Eo)] public static extern int efl_ref_count(IntPtr eo); + [DllImport(efl.Libs.Eo)] public static extern IntPtr + efl_class_name_get(IntPtr eo); [DllImport(efl.Libs.Eo)] public static extern IntPtr efl_class_new(IntPtr class_description, IntPtr parent, IntPtr term); [DllImport(efl.Libs.Eo)] public static extern IntPtr @@ -150,9 +152,6 @@ public class Globals { [DllImport(efl.Libs.Eo)] public static extern IntPtr efl_object_legacy_only_event_description_get([MarshalAs(UnmanagedType.LPStr)] String name); - public static System.Collections.Concurrent.ConcurrentDictionary klasses - = new System.Collections.Concurrent.ConcurrentDictionary(); - public const int RTLD_NOW = 2; public delegate byte class_initializer(IntPtr klass); @@ -529,6 +528,120 @@ public interface IWrapper } } +public static class ClassRegister +{ + public static System.Type GetManagedType(IntPtr klass) + { + System.Type t; + if (Efl.Eo.ClassRegister.typeFromKlass.TryGetValue(klass, out t)) + return t; + + // If it isn't on the dictionary then it is a Native binding class + IntPtr namePtr = Efl.Eo.Globals.efl_class_name_get(klass); + if (namePtr == IntPtr.Zero) { + throw new System.InvalidOperationException($"Could not get Native class name. Handle: {klass}"); + } + + string name = Eina.StringConversion.NativeUtf8ToManagedString(namePtr) + .Replace("_", ""); // Convert Efl C name to C# name + + var curr_asm = typeof(IWrapper).Assembly; + t = curr_asm.GetType(name); + if (t == null) + { + foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) + { + if (assembly == curr_asm) + continue; + + t = assembly.GetType(name); + if (t != null) + break; + } + if (t == null) { + throw new System.InvalidOperationException($"Could not find the C# binding class for the EFL class: {name}"); + } + } + AddToKlassTypeBiDictionary(klass, t); // Cache it in the dictionary + return t; + } + + public static IntPtr GetKlass(System.Type objectType) + { + IntPtr klass; + if (klassFromType.TryGetValue(objectType, out klass)) + return klass; + + // Check if it is a Native binding class + klass = GetNativeKlassPtr(objectType); + if (klass != IntPtr.Zero) { + // Add to the dictionary cache + AddToKlassTypeBiDictionary(klass, objectType); + return klass; + } + + // Unregistered Inherited class, let's register it + IntPtr baseKlass = GetNativeBaseKlassPtr(objectType); + if (baseKlass == IntPtr.Zero) + throw new System.InvalidOperationException($"Could not get base C# binding class for Inherited type: {objectType.FullName}"); + return RegisterKlass(baseKlass, objectType); + } + + public static IntPtr GetInheritKlassOrRegister(IntPtr baseKlass, System.Type objectType) + { + IntPtr klass; + if (klassFromType.TryGetValue(objectType, out klass)) + return klass; + + return RegisterKlass(baseKlass, objectType); + } + + private static IntPtr RegisterKlass(IntPtr baseKlass, System.Type objectType) + { + lock (klassAllocLock) { + IntPtr newKlass = Efl.Eo.Globals.register_class(objectType.FullName, baseKlass, objectType); + if (newKlass == IntPtr.Zero) { + throw new System.InvalidOperationException($"Failed to register class '{objectType.FullName}'"); + } + AddToKlassTypeBiDictionary(newKlass, objectType); + return newKlass; + } + } + + private static IntPtr GetNativeBaseKlassPtr(System.Type objectType) + { + for (System.Type t = objectType.BaseType; t != null; t = t.BaseType) + { + var method = t.GetMethod("GetEflClassStatic", + System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); + if (method != null) + return (IntPtr) method.Invoke(null, null); + } + throw new System.InvalidOperationException($"Class '{objectType.FullName}' is not an Efl object"); + } + + private static IntPtr GetNativeKlassPtr(System.Type objectType) + { + var method = objectType.GetMethod("GetEflClassStatic", + System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic); + return (IntPtr) method?.Invoke(null, null); + } + + public static void AddToKlassTypeBiDictionary(IntPtr klassPtr, System.Type objectType) + { + klassFromType[objectType] = klassPtr; + typeFromKlass[klassPtr] = objectType; + } + + public static System.Collections.Concurrent.ConcurrentDictionary klassFromType + = new System.Collections.Concurrent.ConcurrentDictionary(); + + public static System.Collections.Concurrent.ConcurrentDictionary typeFromKlass + = new System.Collections.Concurrent.ConcurrentDictionary(); + + private static readonly object klassAllocLock = new object(); +} + public interface IOwnershipTag { } @@ -587,6 +700,46 @@ public class MarshalTest : ICustomMarshaler } } +///Marshals between System.Type instances and Eo classes (IntPtrs). +public class MarshalEflClass : ICustomMarshaler +{ + public static ICustomMarshaler GetInstance(string cookie) + { + Eina.Log.Debug("MarshalTest.GetInstance cookie " + cookie); + return new MarshalEflClass(); + } + public void CleanUpManagedData(object ManagedObj) + { + } + + public void CleanUpNativeData(IntPtr pNativeData) + { + } + + public int GetNativeDataSize() + { + Eina.Log.Debug("MarshalTest.GetNativeDataSize"); + return 0; + } + + public IntPtr MarshalManagedToNative(object ManagedObj) + { + Eina.Log.Debug("MarshalTest.MarshallManagedToNative"); + if (ManagedObj == null) + return IntPtr.Zero; + var t = (System.Type) ManagedObj; + return Efl.Eo.ClassRegister.GetKlass(t); + } + + public object MarshalNativeToManaged(IntPtr pNativeData) + { + Eina.Log.Debug("MarshalTest.MarshalNativeToManaged"); + if (pNativeData == IntPtr.Zero) + return null; + return Efl.Eo.ClassRegister.GetManagedType(pNativeData); + } +} + public class StringPassOwnershipMarshaler : ICustomMarshaler { public object MarshalNativeToManaged(IntPtr pNativeData) { var ret = Eina.StringConversion.NativeUtf8ToManagedString(pNativeData); diff --git a/src/bindings/mono/meson.build b/src/bindings/mono/meson.build index a8f5c79da3..ed6f91b3d4 100644 --- a/src/bindings/mono/meson.build +++ b/src/bindings/mono/meson.build @@ -13,6 +13,7 @@ mono_sublibs = [ ] blacklisted_files = [ + 'efl_class.eo', 'efl_canvas_text.eo', 'efl_canvas_scene3d.eo', 'evas_canvas3d_camera.eo', diff --git a/src/tests/efl_mono/Eo.cs b/src/tests/efl_mono/Eo.cs index 9da7752e4b..c31c1ad219 100644 --- a/src/tests/efl_mono/Eo.cs +++ b/src/tests/efl_mono/Eo.cs @@ -400,4 +400,17 @@ class TestInterfaceConcrete } } +class TestProvider +{ + public static void test_find_provider() + { + // Tests only the direction C# -> C + var tmp = new Dummy.Numberwrapper(); + var obj = new Dummy.TestObject(); + Dummy.Numberwrapper provider = Dummy.Numberwrapper.static_cast(obj.FindProvider(typeof(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 c6f2bb7319..72750fb140 100644 --- a/src/tests/efl_mono/dummy_test_object.eo +++ b/src/tests/efl_mono/dummy_test_object.eo @@ -1392,6 +1392,7 @@ class Dummy.Test_Object extends Efl.Object implements Efl.Part, Dummy.Test_Iface class.destructor; Efl.Object.constructor; Efl.Part.part_get; + Efl.Object.provider_find; Dummy.Test_Iface.emit_test_conflicted; Dummy.Test_Iface.emit_nonconflicted; Dummy.Test_Iface.iface_prop { get; set; } diff --git a/src/tests/efl_mono/libefl_mono_native_test.c b/src/tests/efl_mono/libefl_mono_native_test.c index d702127d98..1769c61f89 100644 --- a/src/tests/efl_mono/libefl_mono_native_test.c +++ b/src/tests/efl_mono/libefl_mono_native_test.c @@ -73,6 +73,7 @@ typedef struct Dummy_Test_Object_Data Eina_List *list_for_accessor; int setter_only; int iface_prop; + Eo *provider; } Dummy_Test_Object_Data; typedef struct Dummy_Numberwrapper_Data @@ -150,6 +151,9 @@ _dummy_test_object_efl_object_constructor(Eo *obj, Dummy_Test_Object_Data *pd) pd->part_two = efl_add(DUMMY_TEST_OBJECT_CLASS, obj, efl_name_set(efl_added, "part_two")); } + pd->provider = efl_add(DUMMY_NUMBERWRAPPER_CLASS, obj); + dummy_numberwrapper_number_set(pd->provider, 1999); + return obj; } @@ -3920,6 +3924,14 @@ int _dummy_test_object_dummy_test_iface_iface_prop_get(EINA_UNUSED const Eo *obj return pd->iface_prop; } +Eo * _dummy_test_object_efl_object_provider_find(EINA_UNUSED const Eo *obj, Dummy_Test_Object_Data *pd, const Efl_Class *klass) +{ + EINA_LOG_ERR("klass: %p, NUMBERWRAPPER: %p", klass, DUMMY_NUMBERWRAPPER_CLASS); + if (klass == DUMMY_NUMBERWRAPPER_CLASS) + return pd->provider; + return efl_provider_find(efl_super(obj, DUMMY_TEST_OBJECT_CLASS), klass); +} + /// Dummy.Child static Efl_Object *