From cf8251d5a3d42de83b7ff22f902392d08dc4a75f Mon Sep 17 00:00:00 2001 From: Daniel Kolesa Date: Fri, 18 Jan 2019 17:24:12 +0100 Subject: [PATCH] eolian: add support for composite section into regular classes Each regular class can now have a section called 'composite', which can contain interfaces and interfaces only. This defines a list of interfaces that are allowed to be unimplemented on the class, as it is assumed the class will be composited with some other class implementing those interfaces. Only regular classes can have this, as only regular classes can be instantiated. It will also be necessary to check whether the classes in the section appear somewhere within the inheritance tree. For now, this testing is not being done. Example of usage: composite { Some.Magic.Interface; Another.Magic.Interface; } directly in the class body. --- src/Makefile_Eolian.am | 2 + src/lib/eolian/database_class.c | 1 + src/lib/eolian/database_validate.c | 78 +++++++++++++++++++++++++----- src/lib/eolian/eo_lexer.h | 2 +- src/lib/eolian/eo_parser.c | 42 ++++++++++++++++ src/lib/eolian/eolian_database.h | 1 + src/tests/eolian/data/iface.eo | 6 +++ src/tests/eolian/data/unimpl.eo | 8 +++ src/tests/eolian/eolian_parsing.c | 16 ++++++ 9 files changed, 143 insertions(+), 13 deletions(-) create mode 100644 src/tests/eolian/data/iface.eo create mode 100644 src/tests/eolian/data/unimpl.eo diff --git a/src/Makefile_Eolian.am b/src/Makefile_Eolian.am index 5e1e2d01af..2c8d531d9a 100644 --- a/src/Makefile_Eolian.am +++ b/src/Makefile_Eolian.am @@ -105,6 +105,8 @@ tests/eolian/data/function_types.eot \ tests/eolian/data/import_types.eot \ tests/eolian/data/class_requires.eo \ tests/eolian/data/mixins_require.eo \ +tests/eolian/data/iface.eo \ +tests/eolian/data/unimpl.eo \ tests/eolian/data_aux/aux_a.eo \ tests/eolian/data_aux/aux_b.eo \ tests/eolian/data_aux/aux_c.eo diff --git a/src/lib/eolian/database_class.c b/src/lib/eolian/database_class.c index bbe398ed0b..a28d36b42e 100644 --- a/src/lib/eolian/database_class.c +++ b/src/lib/eolian/database_class.c @@ -31,6 +31,7 @@ database_class_del(Eolian_Class *cl) EINA_LIST_FREE(cl->parts, pt) database_part_del(pt); eina_list_free(cl->requires); eina_list_free(cl->callables); + eina_list_free(cl->composite); if (cl->legacy_prefix) eina_stringshare_del(cl->legacy_prefix); if (cl->eo_prefix) eina_stringshare_del(cl->eo_prefix); diff --git a/src/lib/eolian/database_validate.c b/src/lib/eolian/database_validate.c index 2a4c646647..a8496107e0 100644 --- a/src/lib/eolian/database_validate.c +++ b/src/lib/eolian/database_validate.c @@ -715,7 +715,14 @@ _db_fill_callables(Eolian_Class *cl, Eolian_Class *icl, Eina_Hash *fs, Eina_Bool EINA_LIST_FOREACH(icl->callables, l, impl) { Impl_Status ost = (Impl_Status)eina_hash_find(fs, &impl->foo_id); - Eina_Bool extd = _extend_impl(fs, impl, !allow_impl); + Eina_Bool extd = (ost != IMPL_STATUS_FULL); + if (icl->type == EOLIAN_CLASS_REGULAR) + /* stuff coming from full classes is assumed to be already checked + * so we are sure that everything is implemented or composite'd + */ + eina_hash_set(fs, &impl->foo_id, (void *)IMPL_STATUS_FULL); + else + extd = _extend_impl(fs, impl, !allow_impl); if (extd) { /* we had an unimplementation in the list, replace @@ -739,7 +746,7 @@ _db_fill_callables(Eolian_Class *cl, Eolian_Class *icl, Eina_Hash *fs, Eina_Bool } static Eina_Bool -_db_check_implemented(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fs) +_db_check_implemented(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fs, Eina_Hash *cs) { if (cl->type != EOLIAN_CLASS_REGULAR) return EINA_TRUE; @@ -753,8 +760,14 @@ _db_check_implemented(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fs) Eolian_Implement *impl; EINA_LIST_FOREACH(cl->callables, l, impl) { - Impl_Status st = (Impl_Status)eina_hash_find(fs, &impl->foo_id); const Eolian_Function *fid = impl->foo_id; + Impl_Status st = (Impl_Status)eina_hash_find(fs, &fid); + /* found an interface this func was originally defined in in the + * composite list; in that case, ignore it and assume it will come + * from a composite object later + */ + if (eina_hash_find(cs, &fid->klass)) + continue; switch (st) { case IMPL_STATUS_NONE: @@ -880,8 +893,9 @@ end: static Eina_Bool _db_swap_inherit(Eolian_Class *cl, Eina_Bool succ, Eina_Stringshare *in_cl, - Eolian_Class **out_cl) + Eolian_Class **out_cl, Eina_Bool iface_only) { + char buf[PATH_MAX]; if (!succ) { eina_stringshare_del(in_cl); @@ -891,10 +905,23 @@ _db_swap_inherit(Eolian_Class *cl, Eina_Bool succ, Eina_Stringshare *in_cl, if (!icl) { succ = EINA_FALSE; - char buf[PATH_MAX]; snprintf(buf, sizeof(buf), "unknown inherit '%s' (incorrect case?)", in_cl); _obj_error(&cl->base, buf); } + else if (iface_only && (icl->type != EOLIAN_CLASS_INTERFACE)) + { + succ = EINA_FALSE; + snprintf(buf, sizeof(buf), "non-interface class '%s' in composite list", icl->base.name); + _obj_error(&cl->base, buf); + } + else if (iface_only && !_get_impl_class(cl, icl->base.name)) + { + /* TODO: optimize check using a lookup hash later */ + succ = EINA_FALSE; + snprintf(buf, sizeof(buf), "interface '%s' not found within the inheritance tree of '%s'", + icl->base.name, cl->base.name); + _obj_error(&cl->base, buf); + } else *out_cl = icl; eina_stringshare_del(in_cl); @@ -919,7 +946,7 @@ _db_fill_inherits(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fhash) if (cl->parent_name) { - succ = _db_swap_inherit(cl, succ, cl->parent_name, &cl->parent); + succ = _db_swap_inherit(cl, succ, cl->parent_name, &cl->parent, EINA_FALSE); if (succ) { /* fill if not found, but do not return right away because @@ -933,7 +960,7 @@ _db_fill_inherits(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fhash) EINA_LIST_FREE(il, inn) { Eolian_Class *out_cl = NULL; - succ = _db_swap_inherit(cl, succ, inn, &out_cl); + succ = _db_swap_inherit(cl, succ, inn, &out_cl, EINA_FALSE); if (!succ) continue; cl->extends = eina_list_append(cl->extends, out_cl); @@ -945,7 +972,7 @@ _db_fill_inherits(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fhash) EINA_LIST_FREE(rl, inn) { Eolian_Class *out_cl = NULL; - succ = _db_swap_inherit(cl, succ, inn, &out_cl); + succ = _db_swap_inherit(cl, succ, inn, &out_cl, EINA_FALSE); if (succ && !(out_cl->type == EOLIAN_CLASS_REGULAR || out_cl->type == EOLIAN_CLASS_ABSTRACT)) { char buf[PATH_MAX]; @@ -964,11 +991,29 @@ _db_fill_inherits(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fhash) } } + /* a set of interfaces for quick checks */ + Eina_Hash *ch = eina_hash_pointer_new(NULL); + + il = cl->composite; + cl->composite = NULL; + EINA_LIST_FREE(il, inn) + { + Eolian_Class *out_cl = NULL; + succ = _db_swap_inherit(cl, succ, inn, &out_cl, EINA_TRUE); + if (!succ) + continue; + cl->composite = eina_list_append(cl->composite, out_cl); + eina_hash_set(ch, &out_cl, out_cl); + } + /* failed on the way, no point in filling further * the failed stuff will get dropped so it's ok if it's inconsistent */ if (!succ) - return EINA_FALSE; + { + eina_hash_free(ch); + return EINA_FALSE; + } eina_hash_add(fhash, &cl->base.name, cl); @@ -977,10 +1022,18 @@ _db_fill_inherits(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fhash) /* make sure impls/ctors are filled first, but do it only once */ if (!_db_fill_implements(cl, fh)) - return EINA_FALSE; + { + eina_hash_free(ch); + eina_hash_free(fh); + return EINA_FALSE; + } if (!_db_fill_ctors(cl)) - return EINA_FALSE; + { + eina_hash_free(ch); + eina_hash_free(fh); + return EINA_FALSE; + } /* fill callables list with stuff from inheritance tree, the current * class stuff is already filled in _db_fill_implements, this is needed @@ -994,10 +1047,11 @@ _db_fill_inherits(Validate_State *vals, Eolian_Class *cl, Eina_Hash *fhash) _db_fill_callables(cl, icl, fh, EINA_FALSE); /* verify that all methods are implemented on the class */ - if (!_db_check_implemented(vals, cl, fh)) + if (!_db_check_implemented(vals, cl, fh, ch)) vals->warned = EINA_TRUE; eina_hash_free(fh); + eina_hash_free(ch); return EINA_TRUE; } diff --git a/src/lib/eolian/eo_lexer.h b/src/lib/eolian/eo_lexer.h index 522cc41271..67724ec9d0 100644 --- a/src/lib/eolian/eo_lexer.h +++ b/src/lib/eolian/eo_lexer.h @@ -24,7 +24,7 @@ enum Tokens * they just fill in the "kw" field of the token */ #define KEYWORDS KW(class), KW(const), KW(enum), KW(return), KW(struct), \ \ - KW(abstract), KW(constructor), KW(constructors), KW(data), \ + KW(abstract), KW(composite), KW(constructor), KW(constructors), KW(data), \ KW(destructor), KW(eo), KW(eo_prefix), KW(event_prefix), KW(events), \ KW(extends), KW(free), KW(get), KW(implements), KW(import), KW(interface), \ KW(keys), KW(legacy), KW(legacy_prefix), KW(methods), KW(mixin), KW(params), \ diff --git a/src/lib/eolian/eo_parser.c b/src/lib/eolian/eo_parser.c index 356c4ab56a..b3d117d518 100644 --- a/src/lib/eolian/eo_parser.c +++ b/src/lib/eolian/eo_parser.c @@ -1815,6 +1815,43 @@ parse_parts(Eo_Lexer *ls) check_match(ls, '}', '{', line, col); } +static void +parse_composite(Eo_Lexer *ls) +{ + int line, col; + if (ls->klass->type != EOLIAN_CLASS_REGULAR) + eo_lexer_syntax_error(ls, "composite section only allowed in regular classes"); + eo_lexer_get(ls); + line = ls->line_number, col = ls->column; + check_next(ls, '{'); + while (ls->t.token != '}') + { + Eina_Strbuf *buf = eina_strbuf_new(); + eo_lexer_dtor_push(ls, EINA_FREE_CB(eina_strbuf_free), buf); + eo_lexer_context_push(ls); + parse_name(ls, buf); + const char *nm = eina_strbuf_string_get(buf); + char *fnm = database_class_to_filename(nm); + if (!eina_hash_find(ls->state->filenames_eo, fnm)) + { + free(fnm); + char ebuf[PATH_MAX]; + eo_lexer_context_restore(ls); + snprintf(ebuf, sizeof(ebuf), "unknown interface '%s'", nm); + eo_lexer_syntax_error(ls, ebuf); + return; + } + /* do not introduce a dependency */ + database_defer(ls->state, fnm, EINA_FALSE); + free(fnm); + ls->klass->composite = eina_list_append(ls->klass->composite, + eina_stringshare_add(nm)); + eo_lexer_dtor_pop(ls); + check_next(ls, ';'); + } + check_match(ls, '}', '{', line, col); +} + static void parse_implements(Eo_Lexer *ls, Eina_Bool iface) { @@ -1886,6 +1923,7 @@ parse_class_body(Eo_Lexer *ls, Eolian_Class_Type type) has_data = EINA_FALSE, has_methods = EINA_FALSE, has_parts = EINA_FALSE, + has_composite = EINA_FALSE, has_implements = EINA_FALSE, has_constructors = EINA_FALSE, has_events = EINA_FALSE; @@ -1941,6 +1979,10 @@ parse_class_body(Eo_Lexer *ls, Eolian_Class_Type type) CASE_LOCK(ls, parts, "parts definition") parse_parts(ls); break; + case KW_composite: + CASE_LOCK(ls, composite, "composite definition") + parse_composite(ls); + break; case KW_implements: CASE_LOCK(ls, implements, "implements definition") parse_implements(ls, type == EOLIAN_CLASS_INTERFACE); diff --git a/src/lib/eolian/eolian_database.h b/src/lib/eolian/eolian_database.h index 805b547109..d0d7873fdd 100644 --- a/src/lib/eolian/eolian_database.h +++ b/src/lib/eolian/eolian_database.h @@ -191,6 +191,7 @@ struct _Eolian_Class Eina_List *constructors; /* Eolian_Constructor */ Eina_List *events; /* Eolian_Event */ Eina_List *parts; /* Eolian_Part */ + Eina_List *composite; /* Eolian_Class */ Eina_List *requires; /* a list of required other classes only used internally */ Eina_List *callables; /* internal for now */ Eina_Bool class_ctor_enable:1; diff --git a/src/tests/eolian/data/iface.eo b/src/tests/eolian/data/iface.eo new file mode 100644 index 0000000000..153bc0f914 --- /dev/null +++ b/src/tests/eolian/data/iface.eo @@ -0,0 +1,6 @@ +interface Iface { + methods { + foo {} + bar {} + } +} diff --git a/src/tests/eolian/data/unimpl.eo b/src/tests/eolian/data/unimpl.eo new file mode 100644 index 0000000000..a1420e07c6 --- /dev/null +++ b/src/tests/eolian/data/unimpl.eo @@ -0,0 +1,8 @@ +class Unimpl implements Iface { + composite { + Iface; + } + implements { + Iface.foo; + } +} diff --git a/src/tests/eolian/eolian_parsing.c b/src/tests/eolian/eolian_parsing.c index ce2ea68e2f..b029a4ddad 100644 --- a/src/tests/eolian/eolian_parsing.c +++ b/src/tests/eolian/eolian_parsing.c @@ -1633,6 +1633,21 @@ EFL_START_TEST(eolian_class_requires_classes) } EFL_END_TEST +EFL_START_TEST(eolian_class_unimpl) +{ + Eolian_State *eos = eolian_state_new(); + + fail_if(!eolian_state_directory_add(eos, TESTS_SRC_DIR"/data")); + + setenv("EOLIAN_CLASS_UNIMPLEMENTED_WARN", "1", 1); + const Eolian_Unit *unit = eolian_state_file_parse(eos, TESTS_SRC_DIR"/data/unimpl.eo"); + unsetenv("EOLIAN_CLASS_UNIMPLEMENTED_WARN"); + fail_if(!unit); + + eolian_state_free(eos); +} +EFL_END_TEST + void eolian_parsing_test(TCase *tc) { tcase_add_test(tc, eolian_simple_parsing); @@ -1658,4 +1673,5 @@ void eolian_parsing_test(TCase *tc) tcase_add_test(tc, eolian_parts); tcase_add_test(tc, eolian_mixins_require); tcase_add_test(tc, eolian_class_requires_classes); + tcase_add_test(tc, eolian_class_unimpl); } -- 2.34.1