eolian-mono: Add support for multi-value properties with tuples
authorFelipe Magno de Almeida <felipe@expertisesolutions.com.br>
Fri, 23 Aug 2019 17:00:26 +0000 (14:00 -0300)
committerHosang Kim <hosang12.kim@samsung.com>
Wed, 28 Aug 2019 04:30:52 +0000 (13:30 +0900)
Summary: T8133

Reviewers: woohyun, segfaultxavi, felipealmeida

Reviewed By: felipealmeida

Subscribers: cedric, #reviewers, #committers

Tags: #efl

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

src/bin/eolian_mono/eolian/mono/function_definition.hh
src/bin/eolian_mono/eolian/mono/name_helpers.hh
src/bin/eolian_mono/eolian/mono/parameter.hh
src/lib/eolian_cxx/grammar/counter.hpp [new file with mode: 0644]
src/tests/efl_mono/Eo.cs
src/tests/efl_mono/dummy_test_object.c
src/tests/efl_mono/dummy_test_object.eo

index f010f6d..b8b811f 100644 (file)
@@ -6,10 +6,12 @@
 #include "grammar/generator.hpp"
 #include "grammar/klass_def.hpp"
 
+#include "grammar/kleene.hpp"
 #include "grammar/indentation.hpp"
 #include "grammar/list.hpp"
 #include "grammar/alternative.hpp"
 #include "grammar/attribute_reorder.hpp"
+#include "grammar/counter.hpp"
 #include "logging.hh"
 #include "type.hh"
 #include "name_helpers.hh"
@@ -292,6 +294,11 @@ struct property_wrapper_definition_generator
    template<typename OutputIterator, typename Context>
    bool generate(OutputIterator sink, attributes::property_def const& property, Context const& context) const
    {
+      using efl::eolian::grammar::attribute_reorder;
+      using efl::eolian::grammar::counter;
+      using efl::eolian::grammar::attributes::parameter_direction;
+      using efl::eolian::grammar::attributes::parameter_def;
+
       if (blacklist::is_property_blacklisted(property, *implementing_klass, context))
         return true;
 
@@ -304,7 +311,7 @@ struct property_wrapper_definition_generator
         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;
+      //auto set_params = property.setter.is_engaged() ? property.setter->parameters.size() : 0;
 
       // C# properties must have a single value.
       //
@@ -312,16 +319,62 @@ struct property_wrapper_definition_generator
       // meaning they should have 0 parameters.
       //
       // For setters, we ignore the return type - usually boolean.
-      if (get_params > 0 || set_params > 1)
+      // if (get_params > 0 || set_params > 1)
+      //   return true;
+
+      if (property.getter
+          && std::find_if (property.getter->parameters.begin()
+                           , property.getter->parameters.end()
+                           , [] (parameter_def const& p)
+                           {
+                             return p.direction != parameter_direction::out;
+                           }) != property.getter->parameters.end())
+        return true;
+      if (property.setter
+          && std::find_if (property.setter->parameters.begin()
+                           , property.setter->parameters.end()
+                           , [] (parameter_def const& p)
+                           {
+                             return p.direction != parameter_direction::in;
+                           }) != property.setter->parameters.end())
         return true;
 
-      attributes::type_def prop_type;
+      if (property.getter && property.setter)
+      {
+        if (get_params != 0 && property.setter->parameters.size() != property.getter->parameters.size())
+          return true;
+      }
 
-      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
+      std::vector<attributes::parameter_def> parameters;
+
+      if (property.setter.is_engaged())
+      {
+        std::transform (property.setter->parameters.begin(), property.setter->parameters.end()
+                        , std::back_inserter(parameters)
+                        , [] (parameter_def p) -> parameter_def
+                        {
+                          //p.direction = efl::eolian::attributes::parameter_direction::in;
+                          return p;
+                        });
+      }
+      else if (property.getter.is_engaged())
+      {
+        // if getter has parameters, then we ignore return type, otherwise
+        // we use the return type.
+        if (get_params == 0)
+          parameters.push_back({parameter_direction::in
+                , property.getter->return_type, "propertyResult", {}
+                , property.getter->unit});
+        else
+          std::transform (property.getter->parameters.begin(), property.getter->parameters.end()
+                          , std::back_inserter(parameters)
+                          , [] (parameter_def p) -> parameter_def
+                          {
+                            p.direction = parameter_direction::in;
+                            return p;
+                          });
+      }
+        else
         {
            EINA_CXX_DOM_LOG_ERR(eolian_mono::domain) << "Property must have either a getter or a setter." << std::endl;
            return false;
@@ -365,21 +418,88 @@ struct property_wrapper_definition_generator
            set_scope = "";
         }
 
-      if (!as_generator(
-                  documentation(1)
-                  << scope_tab << scope << (is_static ? "static " : "") << type(true) << " " << managed_name << " {\n"
-            ).generate(sink, std::make_tuple(property, prop_type), context))
-        return false;
+      if (parameters.size() == 1)
+      {
+        if (!as_generator(
+                    documentation(1)
+                    << scope_tab << scope << (is_static ? "static " : "") << type(true) << " " << managed_name << " {\n"
+              ).generate(sink, std::make_tuple(property, parameters[0].type), context))
+          return false;
+      }
+      else
+      {
+        if (!as_generator
+            (
+             documentation(1)
+             << scope_tab << scope << (is_static ? "static (" : "(")
+             << (attribute_reorder<1, -1>(type(true) /*<< " " << argument*/) % ", ") << ") "
+             << managed_name << " {\n"
+            ).generate(sink, std::make_tuple(property, parameters), context))
+          return false;
+      }
 
-      if (property.getter.is_engaged())
-        if (!as_generator(scope_tab << scope_tab << get_scope << "get " << (interface ? ";" : "{ return " + name_helpers::managed_method_name(*property.getter) + "(); }") << "\n"
+      if (property.getter.is_engaged() && interface)
+      {
+        if (!as_generator(scope_tab << scope_tab << set_scope <<  "get;\n"
+                          ).generate(sink, attributes::unused, context))
+          return false;
+      }
+      else if (property.getter.is_engaged() && get_params == 0/*parameters.size() == 1 && property.getter.is_engaged()*/)
+      {
+        if (!as_generator
+            (scope_tab << scope_tab << get_scope
+             << "get " << "{ return " + name_helpers::managed_method_name(*property.getter) + "(); }\n"
             ).generate(sink, attributes::unused, context))
           return false;
-
-      if (property.setter.is_engaged())
-        if (!as_generator(scope_tab << scope_tab << set_scope <<  "set " << (interface ? ";" : "{ " + name_helpers::managed_method_name(*property.setter) + "(" + dir_mod + "value); }") << "\n"
+      }
+      else if (parameters.size() >= 1 && property.getter)
+      {
+        if (!as_generator
+                 (scope_tab << scope_tab << get_scope << "get "
+                  << "{\n"
+                  << *attribute_reorder<1, -1, 1>
+                    (scope_tab(3) << type(true) << " _out_"
+                     << argument(false) << " = default(" << type(true) << ");\n"
+                    )
+                  << scope_tab(3) << name_helpers::managed_method_name(*property.getter)
+                  << "(" << (("out _out_" << argument(false)) % ",") << ");\n"
+                  << scope_tab(3) << "return (" << (("_out_"<< argument(false)) % ",") << ");\n"
+                  << scope_tab(2) << "}" << "\n"
+                 ).generate(sink, std::make_tuple(parameters, parameters, parameters), context))
+          return false;
+      }
+      // else if (parameters.size() == 1)
+      // {
+      //   if (!as_generator
+      //            (scope_tab << scope_tab << get_scope << "get "
+      //             << "{\n"
+      //             << *attribute_reorder<1, -1, 1>(scope_tab(3) << type(true) << " _out_" << argument(false) << " = default(" << type(true) << ");\n")
+      //             << scope_tab(3) << name_helpers::managed_method_name(*property.getter)
+      //             << "(" << (("out _out_" << argument(false)) % ",") << ");\n"
+      //             << scope_tab(3) << "return " << (("_out_"<< argument(false)) % ",") << ";\n"
+      //             << scope_tab(2) << "}" << "\n"
+      //            ).generate(sink, std::make_tuple(parameters, parameters, parameters), context))
+      //     return false;
+      // }
+
+      if (property.setter.is_engaged() && interface)
+      {
+        if (!as_generator(scope_tab << scope_tab << set_scope <<  "set;\n"
+                          ).generate(sink, attributes::unused, context))
+          return false;
+      }
+      else if (parameters.size() == 1 && property.setter.is_engaged())
+      {
+        if (!as_generator(scope_tab << scope_tab << set_scope <<  "set " << "{ " + name_helpers::managed_method_name(*property.setter) + "(" + dir_mod + "value); }\n"
             ).generate(sink, attributes::unused, context))
           return false;
+      }
+      else if (parameters.size() > 1 && property.setter.is_engaged())
+      {
+        if (!as_generator(scope_tab << scope_tab << set_scope <<  "set " << ("{ " + name_helpers::managed_method_name(*property.setter) + "(" + dir_mod) << ((" value.Item" << counter(1)) % ", ") << "); }" << "\n"
+           ).generate(sink, parameters, context))
+          return false;
+      }
 
       if (!as_generator(scope_tab << "}\n").generate(sink, attributes::unused, context))
         return false;
index ca36d04..d6064b2 100644 (file)
@@ -60,6 +60,7 @@ inline std::string escape_keyword(std::string const& name)
      || is_equal(name, "string")
      || is_equal(name, "internal")
      || is_equal(name, "fixed")
+     || is_equal(name, "var")
      || is_equal(name, "base"))
     return "kw_" + name;
 
index 954ac25..52afaf4 100644 (file)
@@ -488,23 +488,31 @@ struct marshall_parameter_generator
 // FIXME This seems to be used only in the else branch of the native function definition. Is it really needed?
 struct argument_generator
 {
+   bool generate_direction;
+   argument_generator () : generate_direction(true) {}
+   argument_generator (bool r) : generate_direction(r) {}
+
    template <typename OutputIterator, typename Context>
    bool generate(OutputIterator sink, attributes::parameter_def const& param, Context const& context) const
    {
      std::string param_name = escape_keyword(param.param_name);
      std::string direction = marshall_direction_modifier(param);
 
-     if (!param.type.original_type.visit(is_fp_visitor{}))
-       return as_generator(
-                direction << param_name
-           ).generate(sink, attributes::unused, context);
-
-    return as_generator(
-            param_name << "_data, " << param_name << ", " << param_name << "_free_cb"
-          ).generate(sink, attributes::unused, context);
+     if (generate_direction && !param.type.original_type.visit(is_fp_visitor{}))
+       return as_generator(direction << param_name).generate(sink, attributes::unused, context);
+     if (!generate_direction && !param.type.original_type.visit(is_fp_visitor{}))
+       return as_generator(param_name).generate(sink, attributes::unused, context);
+     else
+       return as_generator
+         (param_name << "_data, " << param_name << ", " << param_name << "_free_cb"
+         ).generate(sink, attributes::unused, context);
 
    }
 
+   argument_generator operator ()(bool r) const
+   {
+     return {r};
+   }
 } const argument {};
 
 struct native_argument_invocation_generator
diff --git a/src/lib/eolian_cxx/grammar/counter.hpp b/src/lib/eolian_cxx/grammar/counter.hpp
new file mode 100644 (file)
index 0000000..d5af506
--- /dev/null
@@ -0,0 +1,59 @@
+#ifndef EOLIAN_CXX_COUNTER_HH_HH
+#define EOLIAN_CXX_COUNTER_HH_HH
+
+#include <cstdlib>
+#include <cstring>
+
+#include "grammar/generator.hpp"
+#include "grammar/attributes.hpp"
+#include "grammar/case.hpp"
+#include "grammar/integral.hpp"
+
+namespace efl { namespace eolian { namespace grammar {
+
+namespace detail {
+
+}
+
+struct counter_generator
+{
+   std::shared_ptr<std::size_t> count;
+
+   template <typename OutputIterator, typename Attribute, typename Context>
+   bool generate(OutputIterator sink, Attribute const&, Context const&) const
+   {
+      detail::generate_integral(sink, *count);
+      ++*count;
+      return true;
+   }
+};
+
+struct counter_terminal
+{
+  counter_generator operator()(std::size_t initial) const
+  {
+    return {std::shared_ptr<std::size_t>{new std::size_t{initial}}};
+  }
+} const counter = {};
+
+counter_generator as_generator(counter_terminal) { return {std::shared_ptr<std::size_t>{new std::size_t{0u}}}; }
+
+template <>
+struct is_eager_generator<counter_generator> : std::true_type {};
+template <>
+struct is_eager_generator<counter_terminal> : std::true_type {};
+template <>
+struct is_generator<counter_terminal> : std::true_type {};
+template <>
+struct is_generator<counter_generator> : std::true_type {};
+
+namespace type_traits {
+template <>
+struct attributes_needed<counter_generator> : std::integral_constant<int, 0> {};
+template <>
+struct attributes_needed<counter_terminal> : std::integral_constant<int, 0> {};
+}
+
+} } }
+
+#endif
index aa3565d..56813b3 100644 (file)
@@ -348,6 +348,14 @@ class TestCsharpProperties
         iface.IfaceProp = val;
         Test.AssertEquals(val, iface.IfaceProp);
     }
+
+    public static void test_csharp_multi_valued_prop()
+    {
+        var obj = new Dummy.TestObject();
+        obj.MultiValuedProp = (1, 2);
+        var ret = obj.MultiValuedProp;
+        Test.AssertEquals(ret, (1, 2));
+    }
 }
 
 class TestEoGrandChildrenFinalize
index 8ba7536..6c32f80 100644 (file)
@@ -16,6 +16,8 @@ typedef struct Dummy_Test_Object_Data
   int iface_prop;
   Eo *provider;
   Eo *iface_provider;
+  int prop1;
+  int prop2;
 
   // Containers passed to C# as iterator/accessors
   Eina_Array *out_array;
@@ -4570,6 +4572,18 @@ Dummy_MyInt _dummy_test_object_bypass_typedef(EINA_UNUSED Eo *obj, EINA_UNUSED D
     return data;
 }
 
+void _dummy_test_object_multi_valued_prop_get(Eo const* obj EINA_UNUSED, Dummy_Test_Object_Data* pd, int* prop1, int* prop2)
+{
+    *prop1 = pd->prop1;
+    *prop2 = pd->prop2;
+}
+
+void _dummy_test_object_multi_valued_prop_set(Eo* obj EINA_UNUSED, Dummy_Test_Object_Data* pd, int prop1, int prop2)
+{
+    pd->prop1 = prop1;
+    pd->prop2 = prop2;
+}
+
 /* Class Properties */
 static int _dummy_test_object_klass_prop = 0;
 
index b6841db..2e5cbfc 100644 (file)
@@ -1569,6 +1569,15 @@ class Dummy.Test_Object extends Efl.Object implements Dummy.Test_Iface {
          return: Dummy.MyInt;
       }
 
+      @property multi_valued_prop {
+         get {}
+         set {}
+         values {
+           prop1: int;
+           prop2: int;
+         }
+      }
+
       @property klass_prop @static {
          get {}
          set {}