eolian_mono: Add support for C# style properties
authorLauro Moura <lauromoura@expertisesolutions.com.br>
Fri, 14 Dec 2018 20:17:23 +0000 (18:17 -0200)
committerWonki Kim <wonki_.kim@samsung.com>
Tue, 18 Dec 2018 04:23:55 +0000 (13:23 +0900)
Summary: Syntatic sugar around the Get/Set functions for now.

Test Plan: Run efl-mono-suite

Reviewers: vitor.sousa, segfaultxavi, felipealmeida, Jaehyun_Cho

Reviewed By: vitor.sousa

Subscribers: cedric

Tags: #efl_language_bindings

Differential Revision: https://phab.enlightenment.org/D7394

src/bin/eolian_mono/eolian/mono/blacklist.hh
src/bin/eolian_mono/eolian/mono/function_definition.hh
src/bin/eolian_mono/eolian/mono/klass.hh
src/bin/eolian_mono/eolian/mono/name_helpers.hh
src/tests/efl_mono/Eo.cs
src/tests/efl_mono/dummy_test_iface.eo
src/tests/efl_mono/dummy_test_object.eo
src/tests/efl_mono/libefl_mono_native_test.c

index 779c69c..7d0d10e 100644 (file)
@@ -15,9 +15,6 @@ inline bool is_function_blacklisted(std::string const& c_name)
   return
     c_name == "efl_event_callback_array_priority_add"
     || c_name == "efl_player_position_get"
-    || c_name == "efl_text_font_source_get"
-    || c_name == "efl_text_font_source_set"
-    || c_name == "efl_ui_focus_manager_focus_get"
     || c_name == "efl_ui_widget_focus_set"
     || c_name == "efl_ui_widget_focus_get"
     || c_name == "efl_ui_text_password_get"
@@ -25,8 +22,6 @@ inline bool is_function_blacklisted(std::string const& c_name)
     || c_name == "elm_interface_scrollable_repeat_events_get"
     || c_name == "elm_interface_scrollable_repeat_events_set"
     || c_name == "elm_wdg_item_del"
-    || c_name == "elm_wdg_item_focus_get"
-    || c_name == "elm_wdg_item_focus_set"
     || c_name == "elm_interface_scrollable_mirrored_set"
     || c_name == "evas_obj_table_mirrored_get"
     || c_name == "evas_obj_table_mirrored_set"
@@ -82,6 +77,14 @@ inline bool is_alias_blacklisted(attributes::alias_def const& alias)
    return name_helpers::alias_full_eolian_name(alias) == "Eina.Error";
 }
 
+inline bool is_property_blacklisted(attributes::property_def const& property)
+{
+    auto name = name_helpers::klass_full_concrete_or_interface_name(property.klass) + "." + name_helpers::property_managed_name(property);
+    return name == "Efl.Input.Key.Key"
+        || name == "Efl.Input.Hold.Hold"
+        || name == "Efl.Text.Text";
+}
+
 }
 
 }
index 9376506..2821360 100644 (file)
@@ -137,8 +137,6 @@ struct function_definition_generator
   bool generate(OutputIterator sink, attributes::function_def const& f, Context const& context) const
   {
     EINA_CXX_DOM_LOG_DBG(eolian_mono::domain) << "function_definition_generator: " << f.c_name << std::endl;
-    if(!do_super && f.is_static) // Static methods goes only on Concrete classes.
-      return true;
     if(blacklist::is_function_blacklisted(f.c_name))
       return true;
 
@@ -209,6 +207,75 @@ struct native_function_definition_parameterized
   }
 } const native_function_definition;
 
+struct property_wrapper_definition_generator
+{
+   template<typename OutputIterator, typename Context>
+   bool generate(OutputIterator sink, attributes::property_def const& property, Context context) const
+   {
+      if (blacklist::is_property_blacklisted(property))
+        return true;
+
+      bool interface = context_find_tag<class_context>(context).current_wrapper_kind == class_context::interface;
+      bool is_static = (property.getter.is_engaged() && property.getter->is_static
+                       || property.setter.is_engaged() && property.setter->is_static);
+
+
+      if (interface && is_static)
+        return true;
+
+      auto get_params = property.getter.is_engaged() ? property.getter->parameters.size() : 0;
+      auto set_params = property.setter.is_engaged() ? property.setter->parameters.size() : 0;
+
+      // C# properties must have a single value.
+      //
+      // Single values in getters are automatically converted to return_type,
+      // meaning they should have 0 parameters.
+      //
+      // For setters, we ignore the return type - usually boolean.
+      if (get_params > 0 || set_params > 1)
+        return true;
+
+      attributes::type_def prop_type;
+
+      if (property.getter.is_engaged())
+        prop_type = property.getter->return_type;
+      else if (property.setter.is_engaged())
+        prop_type = property.setter->parameters[0].type;
+      else
+        {
+           EINA_CXX_DOM_LOG_ERR(eolian_mono::domain) << "Property must have either a getter or a setter." << std::endl;
+           return false;
+        }
+
+      std::string dir_mod;
+      if (property.setter.is_engaged())
+        dir_mod = direction_modifier(property.setter->parameters[0]);
+
+      std::string managed_name = name_helpers::property_managed_name(property);
+
+      if (!as_generator(
+                  scope_tab << documentation
+                  << scope_tab << (interface ? "" : "public ") << (is_static ? "static " : "") << type(true) << " " << managed_name << " {\n"
+            ).generate(sink, std::make_tuple(property, prop_type), context))
+        return false;
+
+      if (property.getter.is_engaged())
+        if (!as_generator(scope_tab << scope_tab << "get " << (interface ? ";" : "{ return Get" + managed_name + "(); }") << "\n"
+            ).generate(sink, attributes::unused, context))
+          return false;
+
+      if (property.setter.is_engaged())
+        if (!as_generator(scope_tab << scope_tab << "set " << (interface ? ";" : "{ Set" + managed_name + "(" + dir_mod + "value); }") << "\n"
+            ).generate(sink, attributes::unused, context))
+          return false;
+
+      if (!as_generator(scope_tab << "}\n").generate(sink, attributes::unused, context))
+        return false;
+
+      return true;
+   }
+} const property_wrapper_definition;
+
 }
 
 namespace efl { namespace eolian { namespace grammar {
@@ -218,11 +285,15 @@ struct is_eager_generator< ::eolian_mono::function_definition_generator> : std::
 template <>
 struct is_eager_generator< ::eolian_mono::native_function_definition_generator> : std::true_type {};
 template <>
+struct is_eager_generator< ::eolian_mono::property_wrapper_definition_generator> : std::true_type {};
+template <>
 struct is_generator< ::eolian_mono::function_definition_generator> : std::true_type {};
 template <>
 struct is_generator< ::eolian_mono::native_function_definition_generator> : std::true_type {};
 template <>
 struct is_generator< ::eolian_mono::function_definition_parameterized> : std::true_type {};
+template <>
+struct is_generator< ::eolian_mono::property_wrapper_definition_generator> : std::true_type {};
 
 namespace type_traits {
 template <>
@@ -233,6 +304,9 @@ struct attributes_needed< ::eolian_mono::function_definition_parameterized> : st
 
 template <>
 struct attributes_needed< ::eolian_mono::native_function_definition_generator> : std::integral_constant<int, 1> {};
+
+template <>
+struct attributes_needed< ::eolian_mono::property_wrapper_definition_generator> : std::integral_constant<int, 1> {};
 }
       
 } } }
index 077a0fa..f10c3a3 100644 (file)
@@ -164,6 +164,9 @@ struct klass
             ).generate(sink, p, iface_cxt))
          return false;
 
+     if (!as_generator(*(property_wrapper_definition)).generate(sink, cls.properties, iface_cxt))
+       return false;
+
      // End of interface declaration
      if(!as_generator("}\n").generate(sink, attributes::unused, iface_cxt)) return false;
 
@@ -255,6 +258,18 @@ struct klass
          if(!as_generator(*(async_function_definition)).generate(sink, implemented_methods, concrete_cxt))
            return false;
 
+         // Property wrappers
+         if (!as_generator(*(property_wrapper_definition)).generate(sink, cls.properties, concrete_cxt))
+           return false;
+
+         for (auto&& klass : helpers::non_implemented_interfaces(cls))
+           {
+              attributes::klass_def c(get_klass(klass, cls.unit), cls.unit);
+              if (!as_generator(*(property_wrapper_definition)).generate(sink, c.properties, concrete_cxt))
+                return false;
+           }
+
+
          if(!as_generator("}\n").generate(sink, attributes::unused, concrete_cxt)) return false;
 
        }
@@ -315,6 +330,17 @@ struct klass
          if(!as_generator(*(async_function_definition(true))).generate(sink, implemented_methods, inherit_cxt))
            return false;
 
+         // Property wrappers
+         if (!as_generator(*(property_wrapper_definition)).generate(sink, cls.properties, inherit_cxt))
+           return false;
+
+         for (auto&& klass : helpers::non_implemented_interfaces(cls))
+           {
+              attributes::klass_def c(get_klass(klass, cls.unit), cls.unit);
+              if (!as_generator(*(property_wrapper_definition)).generate(sink, c.properties, inherit_cxt))
+                return false;
+           }
+
          if(!as_generator("}\n").generate(sink, attributes::unused, inherit_cxt)) return false;
        }
 
index 05bf783..06291f4 100644 (file)
@@ -243,6 +243,14 @@ inline std::string to_field_name(std::string const& in)
   return utils::capitalize(in);
 }
 
+inline std::string property_managed_name(attributes::property_def const& property)
+{
+  auto names = utils::split(property.name, '_');
+  // No need to escape keyword here as it will be capitalized and already
+  // namespaced inside the owner class.
+  return utils::to_pascal_case(names);
+}
+
 inline std::string managed_part_name(attributes::part_def const& part)
 {
   std::vector<std::string> names = utils::split(part.name, '_');
index 535c309..7d5df72 100644 (file)
@@ -303,4 +303,46 @@ class TestEoMultipleChildClasses
     }
 }
 
+class TestCsharpProperties
+{
+    public static void test_csharp_properties()
+    {
+        var obj = new Dummy.TestObject();
+        var name = "My Name";
+        obj.Name = name;
+
+        Test.AssertEquals(name, obj.Name);
+    }
+
+    public static void test_getter_only()
+    {
+        var obj = new Dummy.TestObject();
+        Test.Assert(!obj.Invalidating);
+    }
+
+    public static void test_setter_only()
+    {
+        var obj = new Dummy.TestObject();
+        int val = -1984;
+
+        obj.SetterOnly = val;
+        Test.AssertEquals(val, obj.GetSetterOnly());
+    }
+
+    public static void test_class_property()
+    {
+        int val = -42;
+        Dummy.TestObject.KlassProp = val;
+        Test.AssertEquals(val, Dummy.TestObject.KlassProp);
+    }
+
+    public static void test_iface_property()
+    {
+        int val = -33;
+        Dummy.TestIface iface = new Dummy.TestObject();
+        iface.IfaceProp = val;
+        Test.AssertEquals(val, iface.IfaceProp);
+    }
+}
+
 }
index f7295e7..9cad4c4 100644 (file)
@@ -5,6 +5,13 @@ interface Dummy.Test_Iface ()
       }
       emit_nonconflicted {
       }
+      @property iface_prop {
+         get {}
+         set {}
+         values {
+            data: int;
+         }
+      }
    }
    events {
       conflicted: void;
index a72e13a..e37616c 100644 (file)
@@ -1641,6 +1641,17 @@ class Dummy.Test_Object extends Efl.Object implements Efl.Part, Dummy.Test_Iface
          }
          return: accessor<ptr(int)> @owned;
       }
+
+      @property setter_only {
+         set {}
+         values {
+           prop: int;
+         }
+      }
+
+      get_setter_only {
+         return: int;
+      }
    }
    implements {
       class.constructor;
@@ -1649,6 +1660,7 @@ class Dummy.Test_Object extends Efl.Object implements Efl.Part, Dummy.Test_Iface
       Efl.Part.part_get;
       Dummy.Test_Iface.emit_test_conflicted;
       Dummy.Test_Iface.emit_nonconflicted;
+      Dummy.Test_Iface.iface_prop { get; set; }
       Dummy.Another_Iface.emit_another_conflicted;
    }
    events {
index 8a4592e..52876e6 100644 (file)
@@ -57,6 +57,8 @@ typedef struct Dummy_Test_Object_Data
   Eo *part_two;
   Eina_Promise *promise;
   Eina_List *list_for_accessor;
+  int setter_only;
+  int iface_prop;
 } Dummy_Test_Object_Data;
 
 typedef struct Dummy_Numberwrapper_Data
@@ -3876,7 +3878,25 @@ void _dummy_test_object_dummy_another_iface_emit_another_conflicted(Eo *obj, Dum
     efl_event_callback_legacy_call(obj, DUMMY_ANOTHER_IFACE_EVENT_CONFLICTED, NULL);
 }
 
+void _dummy_test_object_setter_only_set(EINA_UNUSED Eo *obj, Dummy_Test_Object_Data *pd, int value)
+{
+    pd->setter_only = value;
+}
+
+int _dummy_test_object_get_setter_only(EINA_UNUSED Eo *obj, Dummy_Test_Object_Data *pd)
+{
+    return pd->setter_only;
+}
 
+void _dummy_test_object_dummy_test_iface_iface_prop_set(EINA_UNUSED Eo *obj, Dummy_Test_Object_Data *pd, int value)
+{
+    pd->iface_prop = value;
+}
+
+int _dummy_test_object_dummy_test_iface_iface_prop_get(EINA_UNUSED const Eo *obj, Dummy_Test_Object_Data *pd)
+{
+    return pd->iface_prop;
+}
 
 #include "dummy_test_object.eo.c"
 #include "dummy_numberwrapper.eo.c"