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.
This commit is contained in:
Daniel Kolesa 2019-01-18 17:24:12 +01:00
parent dab4d8d8a4
commit 7feeda0410
9 changed files with 143 additions and 13 deletions

View File

@ -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

View File

@ -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);

View File

@ -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;
}

View File

@ -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), \

View File

@ -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);

View File

@ -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;

View File

@ -0,0 +1,6 @@
interface Iface {
methods {
foo {}
bar {}
}
}

View File

@ -0,0 +1,8 @@
class Unimpl implements Iface {
composite {
Iface;
}
implements {
Iface.foo;
}
}

View File

@ -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);
}