eolian: add support for composite section into regular classes
authorDaniel Kolesa <d.kolesa@samsung.com>
Fri, 18 Jan 2019 16:24:12 +0000 (17:24 +0100)
committerJunsuChoi <jsuya.choi@samsung.com>
Thu, 24 Jan 2019 05:20:18 +0000 (14:20 +0900)
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
src/lib/eolian/database_class.c
src/lib/eolian/database_validate.c
src/lib/eolian/eo_lexer.h
src/lib/eolian/eo_parser.c
src/lib/eolian/eolian_database.h
src/tests/eolian/data/iface.eo [new file with mode: 0644]
src/tests/eolian/data/unimpl.eo [new file with mode: 0644]
src/tests/eolian/eolian_parsing.c

index 5e1e2d01aff7f13037ceecf24a4182f68a60959f..2c8d531d9a350b5a9df9e6f070acbc725bad53a3 100644 (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
index bbe398ed0bf43e061b6545c879c7c153611a1af9..a28d36b42e9b677a0ade7507ebd4f35f7f7762aa 100644 (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);
index 2a4c646647bf596cc277239c7410e0ce62d7f93f..a8496107e0cc68a3655e8822445f60bbd44f4b8a 100644 (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;
 }
index 522cc4127179a53c767549a3c15cdf0b37620d79..67724ec9d02224989fd0e69ea3883abc66a7570f 100644 (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), \
index 356c4ab56a09b8fbfa9f5ba774dae951c67dc25a..b3d117d5183acfe9cbadaf15d58e611081f12d37 100644 (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);
index 805b547109601c78870e121682629aea6e07f401..d0d7873fdd95ce6969a18c47050fe45a3aebd178 100644 (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;
diff --git a/src/tests/eolian/data/iface.eo b/src/tests/eolian/data/iface.eo
new file mode 100644 (file)
index 0000000..153bc0f
--- /dev/null
@@ -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 (file)
index 0000000..a1420e0
--- /dev/null
@@ -0,0 +1,8 @@
+class Unimpl implements Iface {
+    composite {
+        Iface;
+    }
+    implements {
+        Iface.foo;
+    }
+}
index ce2ea68e2f4b2b628318fd469c342c8a241ce147..b029a4ddad6c37d5caaa675aa59faec369b52c6f 100644 (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);
 }