diff --git a/src/bin/eolian_mono/eolian/mono/blacklist.hh b/src/bin/eolian_mono/eolian/mono/blacklist.hh index 99ec12e91d..646452e57c 100644 --- a/src/bin/eolian_mono/eolian/mono/blacklist.hh +++ b/src/bin/eolian_mono/eolian/mono/blacklist.hh @@ -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"; +} + } } diff --git a/src/bin/eolian_mono/eolian/mono/function_definition.hh b/src/bin/eolian_mono/eolian/mono/function_definition.hh index 93765065b8..2821360a04 100644 --- a/src/bin/eolian_mono/eolian/mono/function_definition.hh +++ b/src/bin/eolian_mono/eolian/mono/function_definition.hh @@ -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 + 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(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 {}; + +template <> +struct attributes_needed< ::eolian_mono::property_wrapper_definition_generator> : std::integral_constant {}; } } } } diff --git a/src/bin/eolian_mono/eolian/mono/klass.hh b/src/bin/eolian_mono/eolian/mono/klass.hh index 077a0fad07..f10c3a38d4 100644 --- a/src/bin/eolian_mono/eolian/mono/klass.hh +++ b/src/bin/eolian_mono/eolian/mono/klass.hh @@ -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; } diff --git a/src/bin/eolian_mono/eolian/mono/name_helpers.hh b/src/bin/eolian_mono/eolian/mono/name_helpers.hh index 05bf783384..06291f412c 100644 --- a/src/bin/eolian_mono/eolian/mono/name_helpers.hh +++ b/src/bin/eolian_mono/eolian/mono/name_helpers.hh @@ -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 names = utils::split(part.name, '_'); diff --git a/src/tests/efl_mono/Eo.cs b/src/tests/efl_mono/Eo.cs index 535c309e1c..7d5df72457 100644 --- a/src/tests/efl_mono/Eo.cs +++ b/src/tests/efl_mono/Eo.cs @@ -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); + } +} + } diff --git a/src/tests/efl_mono/dummy_test_iface.eo b/src/tests/efl_mono/dummy_test_iface.eo index f7295e719e..9cad4c41c2 100644 --- a/src/tests/efl_mono/dummy_test_iface.eo +++ b/src/tests/efl_mono/dummy_test_iface.eo @@ -5,6 +5,13 @@ interface Dummy.Test_Iface () } emit_nonconflicted { } + @property iface_prop { + get {} + set {} + values { + data: int; + } + } } events { conflicted: void; diff --git a/src/tests/efl_mono/dummy_test_object.eo b/src/tests/efl_mono/dummy_test_object.eo index a72e13ace3..e37616c979 100644 --- a/src/tests/efl_mono/dummy_test_object.eo +++ b/src/tests/efl_mono/dummy_test_object.eo @@ -1641,6 +1641,17 @@ class Dummy.Test_Object extends Efl.Object implements Efl.Part, Dummy.Test_Iface } return: accessor @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 { diff --git a/src/tests/efl_mono/libefl_mono_native_test.c b/src/tests/efl_mono/libefl_mono_native_test.c index 8a4592e35b..52876e6859 100644 --- a/src/tests/efl_mono/libefl_mono_native_test.c +++ b/src/tests/efl_mono/libefl_mono_native_test.c @@ -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"