forked from enlightenment/efl
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
This commit is contained in:
parent
c998af19fc
commit
455dedd49d
|
@ -103,6 +103,8 @@ tests/eolian/data/typedef.eo \
|
||||||
tests/eolian/data/var.eo \
|
tests/eolian/data/var.eo \
|
||||||
tests/eolian/data/function_types.eot \
|
tests/eolian/data/function_types.eot \
|
||||||
tests/eolian/data/import_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_a.eo \
|
||||||
tests/eolian/data_aux/aux_b.eo \
|
tests/eolian/data_aux/aux_b.eo \
|
||||||
tests/eolian/data_aux/aux_c.eo
|
tests/eolian/data_aux/aux_c.eo
|
||||||
|
|
|
@ -508,6 +508,12 @@ _get_impl_class(const Eolian_Class *cl, const char *cln)
|
||||||
if (fcl)
|
if (fcl)
|
||||||
return 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)
|
EINA_LIST_FOREACH(cl->extends, l, icl)
|
||||||
{
|
{
|
||||||
const Eolian_Class *fcl = _get_impl_class(icl, cln);
|
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))
|
if (eina_hash_find(cl->base.unit->state->main.unit.classes, cl->base.name))
|
||||||
return EINA_TRUE;
|
return EINA_TRUE;
|
||||||
|
|
||||||
Eina_List *il = cl->extends;
|
Eina_List *il = cl->extends, *rl = cl->requires;
|
||||||
Eina_Stringshare *inn = NULL;
|
Eina_Stringshare *inn = NULL;
|
||||||
cl->extends = NULL;
|
cl->extends = NULL;
|
||||||
|
cl->requires = NULL;
|
||||||
Eina_Bool succ = EINA_TRUE;
|
Eina_Bool succ = EINA_TRUE;
|
||||||
|
|
||||||
if (cl->parent_name)
|
if (cl->parent_name)
|
||||||
|
@ -771,6 +778,30 @@ _db_fill_inherits(Eolian_Class *cl, Eina_Hash *fhash)
|
||||||
succ = _db_fill_inherits(out_cl, 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
|
/* failed on the way, no point in filling further
|
||||||
* the failed stuff will get dropped so it's ok if it's inconsistent
|
* 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);
|
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
|
static Eina_Bool
|
||||||
_validate_class(Validate_State *vals, Eolian_Class *cl,
|
_validate_class(Validate_State *vals, Eolian_Class *cl,
|
||||||
Eina_Hash *nhash, Eina_Hash *ehash, Eina_Hash *chash)
|
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_Part *part;
|
||||||
Eolian_Implement *impl;
|
Eolian_Implement *impl;
|
||||||
Eolian_Class *icl;
|
Eolian_Class *icl;
|
||||||
|
Eina_List *required_classes = NULL;
|
||||||
|
|
||||||
if (!cl)
|
if (!cl)
|
||||||
return EINA_FALSE; /* if this happens something is very wrong though */
|
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)
|
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)
|
if (!valid && vals->ext_regular) switch (icl->type)
|
||||||
{
|
{
|
||||||
case EOLIAN_CLASS_REGULAR:
|
case EOLIAN_CLASS_REGULAR:
|
||||||
|
@ -869,6 +930,35 @@ _validate_class(Validate_State *vals, Eolian_Class *cl,
|
||||||
if (!_validate_class(vals, icl, nhash, ehash, chash))
|
if (!_validate_class(vals, icl, nhash, ehash, chash))
|
||||||
return EINA_FALSE;
|
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)
|
EINA_LIST_FOREACH(cl->properties, l, func)
|
||||||
if (!_validate_function(vals, func, nhash))
|
if (!_validate_function(vals, func, nhash))
|
||||||
|
|
|
@ -28,7 +28,7 @@ enum Tokens
|
||||||
KW(destructor), KW(eo), KW(eo_prefix), KW(event_prefix), KW(events), \
|
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(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(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(auto), KWAT(beta), KWAT(class), KWAT(const), KWAT(cref), KWAT(empty), \
|
||||||
KWAT(extern), KWAT(free), KWAT(hot), KWAT(in), KWAT(inout), KWAT(nonull), \
|
KWAT(extern), KWAT(free), KWAT(hot), KWAT(in), KWAT(inout), KWAT(nonull), \
|
||||||
|
|
|
@ -2009,6 +2009,24 @@ inherit_dup:
|
||||||
eo_lexer_syntax_error(ls, ebuf);
|
eo_lexer_syntax_error(ls, ebuf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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
|
static void
|
||||||
parse_class(Eo_Lexer *ls, Eolian_Class_Type type)
|
parse_class(Eo_Lexer *ls, Eolian_Class_Type type)
|
||||||
{
|
{
|
||||||
|
@ -2052,6 +2070,20 @@ parse_class(Eo_Lexer *ls, Eolian_Class_Type type)
|
||||||
Eina_Strbuf *ibuf = eina_strbuf_new();
|
Eina_Strbuf *ibuf = eina_strbuf_new();
|
||||||
eo_lexer_dtor_push(ls, EINA_FREE_CB(eina_strbuf_free), ibuf);
|
eo_lexer_dtor_push(ls, EINA_FREE_CB(eina_strbuf_free), ibuf);
|
||||||
/* new inherits syntax, keep alongside old for now */
|
/* 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)))
|
if (ls->t.kw == KW_extends || (is_reg && (ls->t.kw == KW_implements)))
|
||||||
{
|
{
|
||||||
Eina_Bool ext = (ls->t.kw == KW_extends);
|
Eina_Bool ext = (ls->t.kw == KW_extends);
|
||||||
|
|
|
@ -191,6 +191,7 @@ struct _Eolian_Class
|
||||||
Eina_List *constructors; /* Eolian_Constructor */
|
Eina_List *constructors; /* Eolian_Constructor */
|
||||||
Eina_List *events; /* Eolian_Event */
|
Eina_List *events; /* Eolian_Event */
|
||||||
Eina_List *parts; /* Eolian_Part */
|
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_ctor_enable:1;
|
||||||
Eina_Bool class_dtor_enable:1;
|
Eina_Bool class_dtor_enable:1;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import base;
|
||||||
|
import mixins_require;
|
||||||
|
|
||||||
|
class Class.Requires (Base, Mixins.Require) {
|
||||||
|
methods {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
import base;
|
||||||
|
import class_simple;
|
||||||
|
|
||||||
|
mixin Mixins.Require requires Base {
|
||||||
|
methods {
|
||||||
|
test {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
implements {
|
||||||
|
Base.constructor;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1552,6 +1552,66 @@ EFL_START_TEST(eolian_parts)
|
||||||
}
|
}
|
||||||
EFL_END_TEST
|
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)
|
void eolian_parsing_test(TCase *tc)
|
||||||
{
|
{
|
||||||
tcase_add_test(tc, eolian_simple_parsing);
|
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_types);
|
||||||
tcase_add_test(tc, eolian_function_as_arguments);
|
tcase_add_test(tc, eolian_function_as_arguments);
|
||||||
tcase_add_test(tc, eolian_parts);
|
tcase_add_test(tc, eolian_parts);
|
||||||
|
tcase_add_test(tc, eolian_mixins_require);
|
||||||
|
tcase_add_test(tc, eolian_class_requires_classes);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue