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
This commit is contained in:
Felipe Magno de Almeida 2019-08-23 14:00:26 -03:00
parent 8aae656b80
commit 1aa05ab41b
7 changed files with 244 additions and 25 deletions

View 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;
}
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())
if (!as_generator(scope_tab << scope_tab << set_scope << "set " << (interface ? ";" : "{ " + name_helpers::managed_method_name(*property.setter) + "(" + dir_mod + "value); }") << "\n"
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;

View 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;

View 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

View File

@ -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

View 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

View 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;

View 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 {}