From: Marcel Hollerbach Date: Thu, 10 Jan 2019 11:10:47 +0000 (+0100) Subject: eolian: introduce the keyword required X-Git-Tag: submit/tizen/20190116.045417~25 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=e0ad6f07a4757c5d79da764ae60036f4246553c9;p=platform%2Fupstream%2Fefl.git eolian: introduce the keyword required This introduces a new keyword called required. It only works on mixins. You can specify a list of regular/abstract classes in there. Classes specified after the required keyword are later used to verify the usage of the mixin. With this feature a mixin can define a list of types that the inheriting object (the object that inherits from a mixin) needs to fullfill, if one class that is required is not in the implemented classes, then eolian will bail out. Differential Revision: https://phab.enlightenment.org/D7584 --- diff --git a/src/Makefile_Eolian.am b/src/Makefile_Eolian.am index 6d607ef..5e1e2d0 100644 --- a/src/Makefile_Eolian.am +++ b/src/Makefile_Eolian.am @@ -103,6 +103,8 @@ tests/eolian/data/typedef.eo \ tests/eolian/data/var.eo \ 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_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_validate.c b/src/lib/eolian/database_validate.c index a96e389..09004c8 100644 --- a/src/lib/eolian/database_validate.c +++ b/src/lib/eolian/database_validate.c @@ -508,6 +508,12 @@ _get_impl_class(const Eolian_Class *cl, const char *cln) if (fcl) return fcl; } + EINA_LIST_FOREACH(cl->requires, l, icl) + { + const Eolian_Class *fcl = _get_impl_class(icl, cln); + if (fcl) + return fcl; + } EINA_LIST_FOREACH(cl->extends, l, icl) { const Eolian_Class *fcl = _get_impl_class(icl, cln); @@ -743,9 +749,10 @@ _db_fill_inherits(Eolian_Class *cl, Eina_Hash *fhash) if (eina_hash_find(cl->base.unit->state->main.unit.classes, cl->base.name)) return EINA_TRUE; - Eina_List *il = cl->extends; + Eina_List *il = cl->extends, *rl = cl->requires; Eina_Stringshare *inn = NULL; cl->extends = NULL; + cl->requires = NULL; Eina_Bool succ = EINA_TRUE; if (cl->parent_name) @@ -771,6 +778,30 @@ _db_fill_inherits(Eolian_Class *cl, Eina_Hash *fhash) succ = _db_fill_inherits(out_cl, fhash); } + if (succ && cl->type == EOLIAN_CLASS_MIXIN) + { + EINA_LIST_FREE(rl, inn) + { + Eolian_Class *out_cl = NULL; + succ = _db_swap_inherit(cl, succ, inn, &out_cl); + if (succ && !(out_cl->type == EOLIAN_CLASS_REGULAR || out_cl->type == EOLIAN_CLASS_ABSTRACT)) + { + char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), "requires only allows regulars or abstracts"); + _obj_error(&cl->base, buf); + succ = EINA_FALSE; + } + if (succ) + { + _db_fill_inherits(out_cl, fhash); + } + if (!succ) + continue; + if (!eina_list_data_find(cl->requires, out_cl)) + cl->requires = eina_list_append(cl->requires, 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 */ @@ -805,6 +836,24 @@ _validate_implement(Eolian_Implement *impl) return _validate(&impl->base); } +static Eina_List* +_required_classes(Eolian_Class *mixin) +{ + Eina_List *result = NULL, *n; + Eolian_Class *extension; + + + result = eina_list_clone(mixin->requires); + + if (mixin->parent) + result = eina_list_merge(result, _required_classes(mixin->parent)); + + EINA_LIST_FOREACH(mixin->extends, n, extension) + result = eina_list_merge(result, _required_classes(extension)); + + return result; +} + static Eina_Bool _validate_class(Validate_State *vals, Eolian_Class *cl, Eina_Hash *nhash, Eina_Hash *ehash, Eina_Hash *chash) @@ -815,6 +864,7 @@ _validate_class(Validate_State *vals, Eolian_Class *cl, Eolian_Part *part; Eolian_Implement *impl; Eolian_Class *icl; + Eina_List *required_classes = NULL; if (!cl) return EINA_FALSE; /* if this happens something is very wrong though */ @@ -849,6 +899,17 @@ _validate_class(Validate_State *vals, Eolian_Class *cl, EINA_LIST_FOREACH(cl->extends, l, icl) { + if (icl->type == EOLIAN_CLASS_MIXIN) + { + Eina_List *res = _required_classes(icl); + Eolian_Class *required_class; + Eina_List *n; + EINA_LIST_FOREACH(res, n, required_class) + { + if (!eina_list_data_find(required_classes, required_class)) + required_classes = eina_list_append(required_classes, required_class); + } + } if (!valid && vals->ext_regular) switch (icl->type) { case EOLIAN_CLASS_REGULAR: @@ -869,6 +930,35 @@ _validate_class(Validate_State *vals, Eolian_Class *cl, if (!_validate_class(vals, icl, nhash, ehash, chash)) return EINA_FALSE; } + if (cl->type == EOLIAN_CLASS_ABSTRACT || cl->type == EOLIAN_CLASS_REGULAR) + { + //walk up the parent list and remove all classes from there + icl = cl; + while (icl) + { + required_classes = eina_list_remove(required_classes, icl); + icl = icl->parent; + } + //if there are a few left, drop, and error + if (required_classes) + { + Eina_Strbuf *classes = eina_strbuf_new(); + Eolian_Class *required_class; + Eina_List *n; + EINA_LIST_FOREACH(required_classes, n, required_class) + { + eina_strbuf_append(classes, required_class->base.name); + eina_strbuf_append_char(classes, ' '); + } + char buf[PATH_MAX]; + snprintf(buf, sizeof(buf), "required classes %sare not in the inherit chain of %s", + eina_strbuf_string_get(classes), cl->base.name); + eina_strbuf_free(classes); + _obj_error(&cl->base, buf); + return EINA_FALSE; + } + } + EINA_LIST_FOREACH(cl->properties, l, func) if (!_validate_function(vals, func, nhash)) diff --git a/src/lib/eolian/eo_lexer.h b/src/lib/eolian/eo_lexer.h index d805357..522cc41 100644 --- a/src/lib/eolian/eo_lexer.h +++ b/src/lib/eolian/eo_lexer.h @@ -28,7 +28,7 @@ enum Tokens 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), \ - KW(parse), KW(parts), KW(ptr), KW(set), KW(type), KW(values), KW(var), \ + KW(parse), KW(parts), KW(ptr), KW(set), KW(type), KW(values), KW(var), KW(requires), \ \ KWAT(auto), KWAT(beta), KWAT(class), KWAT(const), KWAT(cref), KWAT(empty), \ KWAT(extern), KWAT(free), KWAT(hot), KWAT(in), KWAT(inout), KWAT(nonull), \ diff --git a/src/lib/eolian/eo_parser.c b/src/lib/eolian/eo_parser.c index 37bed0a..a06b69b 100644 --- a/src/lib/eolian/eo_parser.c +++ b/src/lib/eolian/eo_parser.c @@ -2010,6 +2010,24 @@ inherit_dup: } static void +_requires_add(Eo_Lexer *ls, Eina_Strbuf *buf) +{ + const char *required; + char *fnm; + + eo_lexer_context_push(ls); + parse_name(ls, buf); + required = eina_strbuf_string_get(buf); + fnm = database_class_to_filename(required); + + ls->klass->requires = eina_list_append(ls->klass->requires, eina_stringshare_add(required)); + database_defer(ls->state, fnm, EINA_TRUE); + eo_lexer_context_pop(ls); + + free(fnm); +} + +static void parse_class(Eo_Lexer *ls, Eolian_Class_Type type) { const char *bnm; @@ -2052,6 +2070,20 @@ parse_class(Eo_Lexer *ls, Eolian_Class_Type type) Eina_Strbuf *ibuf = eina_strbuf_new(); eo_lexer_dtor_push(ls, EINA_FREE_CB(eina_strbuf_free), ibuf); /* new inherits syntax, keep alongside old for now */ + if (ls->t.kw == KW_requires) + { + if (type != EOLIAN_CLASS_MIXIN) + { + eo_lexer_syntax_error(ls, "\"requires\" keyword is only needed for mixin classes"); + } + eo_lexer_get(ls); + do + _requires_add(ls, ibuf); + while (test_next(ls, ',')); + if (ls->t.token == '{') + goto inherit_done; + } + if (ls->t.kw == KW_extends || (is_reg && (ls->t.kw == KW_implements))) { Eina_Bool ext = (ls->t.kw == KW_extends); diff --git a/src/lib/eolian/eolian_database.h b/src/lib/eolian/eolian_database.h index 9804449..fdaee09 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 *requires; /* a list of required other classes only used internally */ Eina_Bool class_ctor_enable:1; Eina_Bool class_dtor_enable:1; }; diff --git a/src/tests/eolian/data/class_requires.eo b/src/tests/eolian/data/class_requires.eo new file mode 100644 index 0000000..173c660 --- /dev/null +++ b/src/tests/eolian/data/class_requires.eo @@ -0,0 +1,8 @@ +import base; +import mixins_require; + +class Class.Requires (Base, Mixins.Require) { + methods { + + } +} diff --git a/src/tests/eolian/data/mixins_require.eo b/src/tests/eolian/data/mixins_require.eo new file mode 100644 index 0000000..474e1a3 --- /dev/null +++ b/src/tests/eolian/data/mixins_require.eo @@ -0,0 +1,13 @@ +import base; +import class_simple; + +mixin Mixins.Require requires Base { + methods { + test { + + } + } + implements { + Base.constructor; + } +} diff --git a/src/tests/eolian/eolian_parsing.c b/src/tests/eolian/eolian_parsing.c index 6df473f..d93906b 100644 --- a/src/tests/eolian/eolian_parsing.c +++ b/src/tests/eolian/eolian_parsing.c @@ -1552,6 +1552,66 @@ EFL_START_TEST(eolian_parts) } EFL_END_TEST +EFL_START_TEST(eolian_mixins_require) +{ + const Eolian_Unit *unit; + const Eolian_Class *cl; + const Eolian_Class *base; + + Eolian_State *eos = eolian_state_new(); + + fail_if(!eolian_state_directory_add(eos, TESTS_SRC_DIR"/data")); + + fail_if(!(unit = eolian_state_file_parse(eos, TESTS_SRC_DIR"/data/mixins_require.eo"))); + + fail_if (!(cl = eolian_state_class_by_name_get(eos, "Mixins.Require"))); + fail_if (!(base = eolian_state_class_by_name_get(eos, "Base"))); + + ck_assert_ptr_eq(eolian_class_parent_get(cl), NULL); + + //Check that implements is empty + { + Eolian_Class *extc; + Eina_Iterator *ext = eolian_class_extensions_get (cl); + + EINA_ITERATOR_FOREACH(ext, extc) + { + ck_abort_msg("Iterator should be empty"); + } + eina_iterator_free(ext); + } + //check that implements contains this one class + { + Eolian_Implement *impl; + Eina_Iterator *i = eolian_class_extensions_get (cl); + + EINA_ITERATOR_FOREACH(i, impl) + { + ck_assert_ptr_eq(eolian_implement_class_get(impl), base); + } + eina_iterator_free(i); + } + eolian_state_free(eos); +} +EFL_END_TEST + +EFL_START_TEST(eolian_class_requires_classes) +{ + const Eolian_Unit *unit; + const Eolian_Class *cl; + + Eolian_State *eos = eolian_state_new(); + + fail_if(!eolian_state_directory_add(eos, TESTS_SRC_DIR"/data")); + + fail_if(!(unit = eolian_state_file_parse(eos, TESTS_SRC_DIR"/data/class_requires.eo"))); + + fail_if (!(cl = eolian_state_class_by_name_get(eos, "Class.Requires"))); + + eolian_state_free(eos); +} +EFL_END_TEST + void eolian_parsing_test(TCase *tc) { tcase_add_test(tc, eolian_simple_parsing); @@ -1575,4 +1635,6 @@ void eolian_parsing_test(TCase *tc) tcase_add_test(tc, eolian_function_types); tcase_add_test(tc, eolian_function_as_arguments); tcase_add_test(tc, eolian_parts); + tcase_add_test(tc, eolian_mixins_require); + tcase_add_test(tc, eolian_class_requires_classes); }