From 342758a3f98c3220be648f879033ae94dc2dc45e Mon Sep 17 00:00:00 2001 From: Marcel Hollerbach Date: Tue, 5 Feb 2019 15:40:41 +0100 Subject: [PATCH] eo: here comes reflection API this adds support in eo to generate a reflection API. To get the actaul reflection to the klass, the API efl_class_reflection_table_set needs to be called, the table in the end can be generated by eolian. Reflection API is inherited by the extended class. This means, if you have two reflection tables, first, the most upperst is called, then the next lower one is called. For now this API accepts NULL setter or getter, and will ignore them silently when they are called. fix T7681 Differential Revision: https://phab.enlightenment.org/D7879 --- src/Makefile_Eo.am | 3 +- src/lib/eo/Eo.h | 58 ++++++++++++++++++++++++++- src/lib/eo/eo.c | 64 +++++++++++++++++++++++++++++- src/lib/eo/eo_private.h | 2 + src/tests/eo/suite/eo_suite.c | 1 + src/tests/eo/suite/eo_suite.h | 1 + src/tests/eo/suite/eo_test_class_simple.c | 28 ++++++++++++- src/tests/eo/suite/eo_test_reflection.c | 66 +++++++++++++++++++++++++++++++ src/tests/eo/suite/meson.build | 3 +- 9 files changed, 219 insertions(+), 7 deletions(-) create mode 100644 src/tests/eo/suite/eo_test_reflection.c diff --git a/src/Makefile_Eo.am b/src/Makefile_Eo.am index 43778be..5b602af 100644 --- a/src/Makefile_Eo.am +++ b/src/Makefile_Eo.am @@ -149,7 +149,8 @@ tests/eo/suite/eo_test_value.c \ tests/eo/suite/eo_test_event.c \ tests/eo/suite/eo_test_threaded_calls.c \ tests/eo/suite/eo_test_init.c \ -tests/eo/suite/eo_test_lifecycle.c +tests/eo/suite/eo_test_lifecycle.c \ +tests/eo/suite/eo_test_reflection.c tests_eo_eo_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ -DPACKAGE_BUILD_DIR=\"$(abs_top_builddir)\" \ diff --git a/src/lib/eo/Eo.h b/src/lib/eo/Eo.h index 1406984..d258a47 100644 --- a/src/lib/eo/Eo.h +++ b/src/lib/eo/Eo.h @@ -826,6 +826,40 @@ struct _Efl_Class_Description void (*class_constructor)(Efl_Class *klass); /**< The constructor of the class. */ void (*class_destructor)(Efl_Class *klass); /**< The destructor of the class. */ }; +/** + * Setter type which is used to set an #Eina_Value, this function should access one particular property field + */ +typedef void (*Efl_Object_Property_Reflection_Setter)(Eo *obj, Eina_Value value); + +/** + * Getter type which is used to get an #Eina_Value, this function should access one particular property field + */ +typedef Eina_Value (*Efl_Object_Property_Reflection_Getter)(Eo *obj); + +/** + * @struct _Efl_Object_Property_Reflection + * + * This structure holds one line of the reflection table. + * The two fields get and set might be NULL, + * the property_name is a normal c string containing the name of the property + * that the get and set function changes. + */ +typedef struct _Efl_Object_Property_Reflection{ + const char *property_name; /**< The name of the property */ + Efl_Object_Property_Reflection_Setter set; /**< The function used to set a generic #Eina_Value on this property of the object. */ + Efl_Object_Property_Reflection_Getter get; /**< The function used to retrieve a generic #Eina_Value from this property of the object. */ +} Efl_Object_Property_Reflection; + +/** + * @struct _Efl_Object_Property_Reflection_Ops + * + * This structure holds the reflection table and the size of this table. + */ +typedef struct _Efl_Object_Property_Reflection_Ops +{ + const Efl_Object_Property_Reflection *table; /**< The reflection table. */ + size_t count; /**< Number of table lines descriptions. */ +} Efl_Object_Property_Reflection_Ops; /** * @typedef Efl_Class_Description @@ -860,10 +894,11 @@ EAPI const Efl_Class *efl_class_new(const Efl_Class_Description *desc, const Efl * @return True on success, False otherwise. * * This should only be called from within the initializer function. - * + * The reflection_table contains a getter and setter per property name. Which are called when either + * efl_property_reflection_set() or efl_property_reflection_get() is called. * @see #EFL_DEFINE_CLASS */ -EAPI Eina_Bool efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_ops, const Efl_Object_Ops *class_ops, const void *reflection_table); +EAPI Eina_Bool efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_ops, const Efl_Object_Ops *class_ops, const Efl_Object_Property_Reflection_Ops *reflection_table); /** * @brief Override Eo functions of this object. @@ -1946,6 +1981,25 @@ EAPI Eina_Bool efl_manual_free(Eo *obj); EAPI Eina_Bool efl_destructed_is(const Eo *obj); /** + * @brief Set the given #Eina_Value to the property with the specified \c property_name. + * @param obj The object to set the property on + * @param property_name The name of the property to modify. + * @param value The value to set, the value passed here will be flushed by the function + * + */ +EAPI void efl_property_reflection_set(Eo *obj, const char *property_name, Eina_Value value); + +/** + * @brief Retrieve an #Eina_Value containing the current value of the property specified with \c property_name. + * @param obj The object to set the property on + * @param property_name The name of the property to get. + * + * @return The value that got returned by the actual property in form of a generic Eina_Value. The user of this API is owning the returned Value. + */ +EAPI Eina_Value efl_property_reflection_get(Eo *obj, const char *property_name); + + +/** * @addtogroup Efl_Class_Class Eo's Class class. * @{ */ diff --git a/src/lib/eo/eo.c b/src/lib/eo/eo.c index 0d1ae5b..beed059 100644 --- a/src/lib/eo/eo.c +++ b/src/lib/eo/eo.c @@ -819,7 +819,7 @@ _eo_class_funcs_set(Eo_Vtable *vtable, const Efl_Object_Ops *ops, const _Efl_Cla } EAPI Eina_Bool -efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_ops, const Efl_Object_Ops *class_ops, const void *reflection_table) +efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_ops, const Efl_Object_Ops *class_ops, const Efl_Object_Property_Reflection_Ops *reflection_table) { EO_CLASS_POINTER_GOTO(klass_id, klass, err_klass); Efl_Object_Ops empty_ops = { 0 }; @@ -832,6 +832,8 @@ efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_ if (!class_ops) class_ops = &empty_ops; + klass->reflection = reflection_table; + klass->ops_count = object_ops->count + class_ops->count; klass->base_id = _eo_ops_last_id; @@ -3597,3 +3599,63 @@ static const Eina_Value_Type _EINA_VALUE_TYPE_OBJECT = { }; EOAPI const Eina_Value_Type *EINA_VALUE_TYPE_OBJECT = &_EINA_VALUE_TYPE_OBJECT; + +static const Efl_Object_Property_Reflection* +_efl_class_reflection_find(const _Efl_Class *klass, const char *property_name) +{ + const _Efl_Class **klass_iter = klass->extensions; + const Efl_Object_Property_Reflection_Ops *ref = klass->reflection; + unsigned int i; + + for (i = 0; ref && i < ref->count; ++i) + { + if (eina_streq(property_name, ref->table[i].property_name)) + return &ref->table[i]; + } + + if (klass->parent) + { + const Efl_Object_Property_Reflection *ref; + + ref = _efl_class_reflection_find(klass->parent, property_name); + if (ref) return ref; + } + + for (; *klass_iter; klass_iter++) + { + return _efl_class_reflection_find(*klass_iter, property_name); + } + + return NULL; +} + +EAPI void +efl_property_reflection_set(Eo *obj_id, const char *property_name, Eina_Value value) +{ + EO_OBJ_POINTER_GOTO(obj_id, obj, end); + const Efl_Object_Property_Reflection *reflection = _efl_class_reflection_find(obj->klass, property_name); + + if (!reflection || !reflection->set) goto end; + + reflection->set(obj_id, value); + EO_OBJ_DONE(obj_id); + return; +end: + eina_value_flush(&value); + EO_OBJ_DONE(obj_id); +} + +EAPI Eina_Value +efl_property_reflection_get(Eo *obj_id, const char *property_name) +{ + EO_OBJ_POINTER(obj_id, obj); + const Efl_Object_Property_Reflection *reflection = _efl_class_reflection_find(obj->klass, property_name); + + if (!reflection || !reflection->get) goto end; + + return reflection->get(obj_id); +end: + EO_OBJ_DONE(obj_id); + + return EINA_VALUE_EMPTY; +} diff --git a/src/lib/eo/eo_private.h b/src/lib/eo/eo_private.h index 7145faa..7c7ae9e 100644 --- a/src/lib/eo/eo_private.h +++ b/src/lib/eo/eo_private.h @@ -185,6 +185,8 @@ struct _Efl_Class const _Efl_Class **mro; + const Efl_Object_Property_Reflection_Ops *reflection; + /* cached object for faster allocation */ struct { Eina_Trash *trash; diff --git a/src/tests/eo/suite/eo_suite.c b/src/tests/eo/suite/eo_suite.c index 57b7c61..fe8e419 100644 --- a/src/tests/eo/suite/eo_suite.c +++ b/src/tests/eo/suite/eo_suite.c @@ -20,6 +20,7 @@ static const Efl_Test_Case etc[] = { { "Eo threaded eo calls", eo_test_threaded_calls }, { "Eo event calls", eo_test_event}, { "Eo lifecycle", eo_test_lifecycle}, + { "Eo Reflection", eo_test_reflection}, { NULL, NULL } }; diff --git a/src/tests/eo/suite/eo_suite.h b/src/tests/eo/suite/eo_suite.h index 700325c..84da205 100644 --- a/src/tests/eo/suite/eo_suite.h +++ b/src/tests/eo/suite/eo_suite.h @@ -12,4 +12,5 @@ void eo_test_value(TCase *tc); void eo_test_threaded_calls(TCase *tc); void eo_test_event(TCase *tc); void eo_test_lifecycle(TCase *tc); +void eo_test_reflection(TCase *tc); #endif /* _EO_SUITE_H */ diff --git a/src/tests/eo/suite/eo_test_class_simple.c b/src/tests/eo/suite/eo_test_class_simple.c index dfba73f..8d82b1e 100644 --- a/src/tests/eo/suite/eo_test_class_simple.c +++ b/src/tests/eo/suite/eo_test_class_simple.c @@ -23,6 +23,16 @@ _a_set(Eo *obj EINA_UNUSED, void *class_data, int a) efl_event_callback_legacy_call(obj, EV_A_CHANGED, &pd->a); } +static void +_a_set_reflect(Eo *obj, Eina_Value value) +{ + int a; + + eina_value_int_convert(&value, &a); + simple_a_set(obj, a); + eina_value_flush(&value); +} + static int _a_get(Eo *obj EINA_UNUSED, void *class_data) { @@ -31,6 +41,14 @@ _a_get(Eo *obj EINA_UNUSED, void *class_data) return pd->a; } +static Eina_Value +_a_get_reflect(Eo *obj) +{ + int a = simple_a_get(obj); + + return eina_value_int_init(a); +} + static Eina_Bool _a_print(Eo *obj EINA_UNUSED, void *class_data) { @@ -103,8 +121,14 @@ _class_initializer(Efl_Class *klass) EFL_OPS_DEFINE(cops, EFL_OBJECT_OP_FUNC(simple_class_hi_print, _class_hi_print), ); - - return efl_class_functions_set(klass, &ops, &cops, NULL); + static const Efl_Object_Property_Reflection reflection_table[] = { + {"simple_a", _a_set_reflect, _a_get_reflect}, + }; + static const Efl_Object_Property_Reflection_Ops ref_ops = { + reflection_table, EINA_C_ARRAY_LENGTH(reflection_table) + }; + + return efl_class_functions_set(klass, &ops, &cops, &ref_ops); } static const Efl_Class_Description class_desc = { diff --git a/src/tests/eo/suite/eo_test_reflection.c b/src/tests/eo/suite/eo_test_reflection.c new file mode 100644 index 0000000..ae1e11b --- /dev/null +++ b/src/tests/eo/suite/eo_test_reflection.c @@ -0,0 +1,66 @@ +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include + +#include + +#include "eo_suite.h" +#include "eo_test_class_simple.h" + + +EFL_START_TEST(eo_test_reflection_invalid) +{ + Eina_Value numb_val = eina_value_int_init(1337); + Eo *simple = efl_new(SIMPLE_CLASS); + + simple_a_set(simple, 22); + efl_property_reflection_set(simple, "simple_a_asdf", numb_val); + fail_if(efl_property_reflection_get(simple, "simple_a_invalid").type != EINA_VALUE_EMPTY.type); +} +EFL_END_TEST + +EFL_START_TEST(eo_test_reflection_inherited) +{ + const int numb = 42; + int number_ref; + Eina_Value numb_val = eina_value_int_init(numb); + Eo *simple = efl_new(SIMPLE3_CLASS); + + simple_a_set(simple, 22); + efl_property_reflection_set(simple, "simple_a", numb_val); + ck_assert_int_eq(simple_a_get(simple), numb); + + simple_a_set(simple, 22); + Eina_Value res = efl_property_reflection_get(simple, "simple_a"); + eina_value_int_convert(&res, &number_ref); + ck_assert_int_eq(number_ref, 22); + +} +EFL_END_TEST + +EFL_START_TEST(eo_test_reflection_simple) +{ + const int numb = 42; + int number_ref; + Eina_Value numb_val = eina_value_int_init(numb); + Eo *simple = efl_new(SIMPLE_CLASS); + + simple_a_set(simple, 22); + efl_property_reflection_set(simple, "simple_a", numb_val); + ck_assert_int_eq(simple_a_get(simple), numb); + + simple_a_set(simple, 22); + Eina_Value res = efl_property_reflection_get(simple, "simple_a"); + eina_value_int_convert(&res, &number_ref); + ck_assert_int_eq(number_ref, 22); +} +EFL_END_TEST + +void eo_test_reflection(TCase *tc) +{ + tcase_add_test(tc, eo_test_reflection_simple); + tcase_add_test(tc, eo_test_reflection_inherited); + tcase_add_test(tc, eo_test_reflection_invalid); +} diff --git a/src/tests/eo/suite/meson.build b/src/tests/eo/suite/meson.build index 6e7f4ba..39f7378 100644 --- a/src/tests/eo/suite/meson.build +++ b/src/tests/eo/suite/meson.build @@ -17,7 +17,8 @@ eo_suite_src = [ 'eo_test_event.c', 'eo_test_threaded_calls.c', 'eo_test_init.c', - 'eo_test_lifecycle.c' + 'eo_test_lifecycle.c', + 'eo_test_reflection.c' ] eo_suite = executable('eo_suite', -- 2.7.4