From 99e1b9de256762a4d35d5e0dc9cbd440d812a773 Mon Sep 17 00:00:00 2001 From: Felipe Magno de Almeida Date: Fri, 23 Aug 2019 14:00:26 -0300 Subject: [PATCH] eolian-mono: Add support for multi-value properties with tuples Summary: T8133 Reviewers: woohyun, segfaultxavi, felipealmeida Reviewed By: felipealmeida Subscribers: cedric, #reviewers, #committers Tags: #efl Differential Revision: https://phab.enlightenment.org/D9577 --- .../eolian_mono/eolian/mono/function_definition.hh | 156 ++++++++++++++++++--- src/bin/eolian_mono/eolian/mono/name_helpers.hh | 1 + src/bin/eolian_mono/eolian/mono/parameter.hh | 24 ++-- src/lib/eolian_cxx/grammar/counter.hpp | 59 ++++++++ src/tests/efl_mono/Eo.cs | 8 ++ src/tests/efl_mono/dummy_test_object.c | 14 ++ src/tests/efl_mono/dummy_test_object.eo | 9 ++ 7 files changed, 245 insertions(+), 26 deletions(-) create mode 100644 src/lib/eolian_cxx/grammar/counter.hpp diff --git a/src/bin/eolian_mono/eolian/mono/function_definition.hh b/src/bin/eolian_mono/eolian/mono/function_definition.hh index f010f6d..b8b811f 100644 --- a/src/bin/eolian_mono/eolian/mono/function_definition.hh +++ b/src/bin/eolian_mono/eolian/mono/function_definition.hh @@ -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 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 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; diff --git a/src/bin/eolian_mono/eolian/mono/name_helpers.hh b/src/bin/eolian_mono/eolian/mono/name_helpers.hh index ca36d04..d6064b2 100644 --- a/src/bin/eolian_mono/eolian/mono/name_helpers.hh +++ b/src/bin/eolian_mono/eolian/mono/name_helpers.hh @@ -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; diff --git a/src/bin/eolian_mono/eolian/mono/parameter.hh b/src/bin/eolian_mono/eolian/mono/parameter.hh index 954ac25..52afaf4 100644 --- a/src/bin/eolian_mono/eolian/mono/parameter.hh +++ b/src/bin/eolian_mono/eolian/mono/parameter.hh @@ -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 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 index 0000000..d5af506 --- /dev/null +++ b/src/lib/eolian_cxx/grammar/counter.hpp @@ -0,0 +1,59 @@ +#ifndef EOLIAN_CXX_COUNTER_HH_HH +#define EOLIAN_CXX_COUNTER_HH_HH + +#include +#include + +#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 count; + + template + 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{new std::size_t{initial}}}; + } +} const counter = {}; + +counter_generator as_generator(counter_terminal) { return {std::shared_ptr{new std::size_t{0u}}}; } + +template <> +struct is_eager_generator : std::true_type {}; +template <> +struct is_eager_generator : std::true_type {}; +template <> +struct is_generator : std::true_type {}; +template <> +struct is_generator : std::true_type {}; + +namespace type_traits { +template <> +struct attributes_needed : std::integral_constant {}; +template <> +struct attributes_needed : std::integral_constant {}; +} + +} } } + +#endif diff --git a/src/tests/efl_mono/Eo.cs b/src/tests/efl_mono/Eo.cs index aa3565d..56813b3 100644 --- a/src/tests/efl_mono/Eo.cs +++ b/src/tests/efl_mono/Eo.cs @@ -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 diff --git a/src/tests/efl_mono/dummy_test_object.c b/src/tests/efl_mono/dummy_test_object.c index 8ba7536..6c32f80 100644 --- a/src/tests/efl_mono/dummy_test_object.c +++ b/src/tests/efl_mono/dummy_test_object.c @@ -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; diff --git a/src/tests/efl_mono/dummy_test_object.eo b/src/tests/efl_mono/dummy_test_object.eo index b6841db..2e5cbfc 100644 --- a/src/tests/efl_mono/dummy_test_object.eo +++ b/src/tests/efl_mono/dummy_test_object.eo @@ -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 {} -- 2.7.4