raster pushed a commit to branch master.

http://git.enlightenment.org/core/efl.git/commit/?id=efb15f510c70acde922d59708494539aa686c408

commit efb15f510c70acde922d59708494539aa686c408
Author: Carsten Haitzler (Rasterman) <[email protected]>
Date:   Mon Mar 23 09:04:33 2020 +0000

    Revert eo vtale rework...
    
    Revert "eo: redo vtable mro creation"
    This reverts commit b05110609b38b24f5a11b78bf9a92212da713ce3.
    Revert "eo: add a generic memory allocation tracking method"
    This reverts commit 44071e3102a1526370ae6d653b93a1d20fe87339.
    Revert "eo: rework vtable allocation scheme"
    This reverts commit 3bd16a46f1098f5533723208feae8abafae4e8ab.
    Revert "eo: do not allocate extension if deleting"
    This reverts commit 64f7edc7fc0596425cdc2a1db8aacf06430994f0.
    
    This seems to breal vector rendering in lottie:
    
    From: Hermet Park <[email protected]>
    To: Enlightenment developer list <[email protected]>
    Subject: Re: [E-devel] [EGIT] [core/efl] master 02/05: eo: rework
    vtable allocation scheme
    
    This patch occurs memory corruption, vector crashes :(
    Here is a sample if you'd like to see it.
    https://phab.enlightenment.org/F3858944
---
 src/lib/eo/eo.c                      | 536 +++++++++++++++--------------------
 src/lib/eo/eo_base_class.c           |   2 +-
 src/lib/eo/eo_private.h              |  32 ++-
 src/tests/eo/suite/eo_test_general.c |   1 +
 4 files changed, 248 insertions(+), 323 deletions(-)

diff --git a/src/lib/eo/eo.c b/src/lib/eo/eo.c
index 83d3e5e752..8ca94bf8fd 100644
--- a/src/lib/eo/eo.c
+++ b/src/lib/eo/eo.c
@@ -90,6 +90,7 @@ static _Efl_Class **_eo_classes = NULL;
 static Eo_Id _eo_classes_last_id = 0;
 static Eo_Id _eo_classes_alloc = 0;
 static int _efl_object_init_count = 0;
+static Efl_Object_Op _eo_ops_last_id = 0;
 static Eina_Hash *_ops_storage = NULL;
 static Eina_Spinlock _ops_storage_lock;
 
@@ -103,6 +104,7 @@ static void _eo_condtor_reset(_Eo_Object *obj);
 static inline void *_efl_data_scope_get(const _Eo_Object *obj, const 
_Efl_Class *klass);
 static inline void *_efl_data_xref_internal(const char *file, int line, 
_Eo_Object *obj, const _Efl_Class *klass, const _Eo_Object *ref_obj);
 static inline void _efl_data_xunref_internal(_Eo_Object *obj, void *data, 
const _Eo_Object *ref_obj);
+static void _vtable_init(Eo_Vtable *vtable, size_t size);
 
 static inline Efl_Object_Op _efl_object_api_op_id_get_internal(const void 
*api_func);
 
@@ -118,190 +120,96 @@ static inline Efl_Object_Op 
_efl_object_api_op_id_get_internal(const void *api_f
       (_eo_classes[_UNMASK_ID(id) - 1]) : NULL); \
       })
 
-#define EFL_OBJECT_OP_CLASS_PART(op) op >> 16
-#define EFL_OBJECT_OP_FUNC_PART(op) op & 0xffff
-#define EFL_OBJECT_OP_CREATE_OP_ID(class_id, func_id) ((unsigned 
short)class_id)<<16|((unsigned short)func_id&0xffff)
-
-static const _Efl_Class *
-_eo_op_class_get(Efl_Object_Op op)
+static inline void
+_vtable_chain2_unref(Dich_Chain2 *chain)
 {
-   short class_id = EFL_OBJECT_OP_CLASS_PART(op);
-   return _eo_classes[class_id];
+   if (--(chain->refcount) == 0)
+     {
+        free(chain);
+     }
 }
-#if defined(DEBUG_VTABLE_ALLOCATION)
-static int _allocated_memory = 0;
 
-static inline void*
-_vtable_alloc(unsigned long n, size_t elem)
-{
-   _allocated_memory += n*elem;
-   return calloc(n, elem);
-}
-#else
-static inline void*
-_vtable_alloc(unsigned long n, size_t elem)
-{
-   return calloc(n, elem);
-}
-#endif
-/**
- * This inits the vtable wit hthe current size of allocated tables
- */
-static void
-_vtable_init(Eo_Vtable *vtable)
+static inline void
+_vtable_chain_alloc(Dich_Chain1 *chain1)
 {
-   //we assume here that _eo_classes_last_id was called before
-   vtable->size = _eo_classes_last_id;
-   vtable->chain = _vtable_alloc(vtable->size, sizeof(Eo_Vtable_Node));
+   chain1->chain2 = calloc(1, sizeof(*(chain1->chain2)));
+   chain1->chain2->refcount = 1;
 }
 
-/**
- * This removes all nodes from the klass that are copied from mro
- */
-static void
-_vtable_mro_free(const _Efl_Class *klass)
-{
-   const _Efl_Class **mro_itr = klass->mro;
-   const Eo_Vtable *vtable = &klass->vtable;
-
-   for (  ; *mro_itr ; mro_itr++)
-     {
-        const Eo_Vtable *mro_vtable = &(*mro_itr)->vtable;
-        for (int i = 0; i < mro_vtable->size; ++i)
-          {
-             if (mro_vtable->chain[i].funcs == vtable->chain[i].funcs)
-               vtable->chain[i].funcs = NULL;
-          }
-     }
-}
+static inline void _vtable_chain_write_prepare(Dich_Chain1 *dst);
 
-static void
-_vtable_free(Eo_Vtable *vtable, const Eo_Vtable *root)
+static inline void
+_vtable_chain_merge(Dich_Chain1 *dst, const Dich_Chain1 *src)
 {
-   if (root)
+   size_t j;
+   const op_type_funcs *sf = src->chain2->funcs;
+   op_type_funcs *df = dst->chain2->funcs;
+
+   if (df == sf)
      {
-        EINA_SAFETY_ON_FALSE_RETURN(vtable->size == root->size);
+        /* Skip if the chain is the same. */
+        return;
      }
 
-   for (int i = 0; i < vtable->size; ++i)
+   for (j = 0 ; j < DICH_CHAIN_LAST_SIZE ; j++, df++, sf++)
      {
-        if (root && root->chain[i].funcs == vtable->chain[i].funcs)
-          vtable->chain[i].count = 0;
-
-        if (vtable->chain[i].count)
+        if (sf->func && memcmp(df, sf, sizeof(*df)))
           {
-             free(vtable->chain[i].funcs);
+             _vtable_chain_write_prepare(dst);
+             df = dst->chain2->funcs + j;
+             memcpy(df, sf, sizeof(*df));
           }
      }
-   free(vtable->chain);
 }
 
-/**
- * This takes over all set chains of the src to dest.
- * This should only be called on Eo_Vtables, which are initialized with this 
value.
- * Previous setted values are going to be overwritten.
- */
-static void
-_vtable_take_over(Eo_Vtable *dest, const Eo_Vtable *src)
+static inline void
+_vtable_chain_write_prepare(Dich_Chain1 *dst)
 {
-   for (int i = 0; i < src->size; ++i)
+   if (!dst->chain2)
      {
-        if (src->chain[i].funcs)
-          {
-             dest->chain[i] = src->chain[i];
-          }
+        _vtable_chain_alloc(dst);
+        return;
+     }
+   else if (dst->chain2->refcount == 1)
+     {
+        /* We own it, no need to duplicate */
+        return;
      }
-}
 
-/**
- * Fills the node of the passed class id with a empty none NULL pointer.
- * This is used to indicate that a specific node has a normal 0 size, but is 
set.
- */
-static void
-_vtable_insert_empty_funcs(Eo_Vtable *vtable, unsigned short class_id)
-{
-   vtable->chain[class_id].funcs = (void*)0x1010101;
-   vtable->chain[class_id].count = 0;
-}
+   Dich_Chain1 old;
+   old.chain2 = dst->chain2;
 
-/**
- * duplicate the source node, and write the duplicated values to the 
destination
- * No logical changes are applied to src.
- */
-static void
-_vtable_copy_node(Eo_Vtable_Node *dest, const Eo_Vtable_Node *src)
-{
-   dest->count = src->count;
-   dest->funcs = _vtable_alloc(sizeof(op_type_funcs), src->count);
-   memcpy(dest->funcs, src->funcs, sizeof(op_type_funcs) * src->count);
-}
+   _vtable_chain_alloc(dst);
+   _vtable_chain_merge(dst, &old);
 
-/**
- * Initialize a node with a empty funcs array of the passed length
- */
-static void
-_vtable_prepare_empty_node(Eo_Vtable *dest, unsigned int length, unsigned int 
class_id)
-{
-   dest->chain[class_id].count = length;
-   dest->chain[class_id].funcs = _vtable_alloc(sizeof(op_type_funcs), 
dest->chain[class_id].count);
+   _vtable_chain2_unref(old.chain2);
 }
 
-/**
- * Copy all setted APIs from src to dest.
- * Already set function slots are going to be replaced.
- */
-static void
-_vtable_merge_defined_api(Eo_Vtable *dest, const Eo_Vtable *src, Eina_Bool 
*hitmap)
+static inline void
+_vtable_chain_copy_ref(Dich_Chain1 *dst, const Dich_Chain1 *src)
 {
-   for (unsigned int i = 0; i < src->size; ++i)
+   if (dst->chain2)
      {
-        //if there is a source node evalulate if we need to copy it
-        if (src->chain[i].funcs)
-          {
-             if (!dest->chain[i].funcs)
-               {
-                  dest->chain[i] = src->chain[i];
-                  EINA_SAFETY_ON_FALSE_RETURN(hitmap[i] == EINA_FALSE);
-               }
-             else
-               {
-                  if (!hitmap[i])
-                    {
-                       const Eo_Vtable_Node node = dest->chain[i];
-                       _vtable_copy_node(&dest->chain[i], &node); //we copy 
what we have, and overwrite in the later for loop
-                       hitmap[i] = EINA_TRUE;
-                    }
-                  for (int j = 0; j < src->chain[i].count; ++j)
-                    {
-                       if (src->chain[i].funcs[j].func)
-                         dest->chain[i].funcs[j] = src->chain[i].funcs[j];
-                    }
-              }
-          }
+        _vtable_chain_merge(dst, src);
+     }
+   else
+     {
+        dst->chain2 = src->chain2;
+        dst->chain2->refcount++;
      }
 }
 
-/**
- * Ensure that all set nodes from src are also set on dest.
- * No real values are copied, the newly taken or allocated slots will be empty.
- */
-static void
-_vtable_merge_empty(Eo_Vtable *dest, const Eo_Vtable *src, Eina_Bool *hitmap)
+static inline void
+_vtable_copy_all(Eo_Vtable *dst, const Eo_Vtable *src)
 {
-   for (unsigned int i = 0; i < src->size; ++i)
+   Efl_Object_Op i;
+   const Dich_Chain1 *sc1 = src->chain;
+   Dich_Chain1 *dc1 = dst->chain;
+   for (i = 0 ; i < src->size ; i++, sc1++, dc1++)
      {
-        if (src->chain[i].funcs && !dest->chain[i].funcs)
+        if (sc1->chain2)
           {
-             if (!src->chain[i].count)
-               {
-                  dest->chain[i].funcs = src->chain[i].funcs;
-                  dest->chain[i].count = src->chain[i].count;
-               }
-             else
-               {
-                  _vtable_prepare_empty_node(dest, src->chain[i].count, i);
-                  hitmap[i] = EINA_TRUE;
-               }
+             _vtable_chain_copy_ref(dc1, sc1);
           }
      }
 }
@@ -309,15 +217,37 @@ _vtable_merge_empty(Eo_Vtable *dest, const Eo_Vtable 
*src, Eina_Bool *hitmap)
 static inline const op_type_funcs *
 _vtable_func_get(const Eo_Vtable *vtable, Efl_Object_Op op)
 {
-   unsigned short class_id = EFL_OBJECT_OP_CLASS_PART(op);
-   unsigned short func_id = EFL_OBJECT_OP_FUNC_PART(op);
+   size_t idx1 = DICH_CHAIN1(op);
+   if (EINA_UNLIKELY(idx1 >= vtable->size))
+      return NULL;
+   Dich_Chain1 *chain1 = &vtable->chain[idx1];
+   if (EINA_UNLIKELY(!chain1->chain2))
+      return NULL;
+   return &chain1->chain2->funcs[DICH_CHAIN_LAST(op)];
+}
 
-   if (EINA_UNLIKELY(vtable->size <= class_id))
-     return NULL;
-   if (EINA_UNLIKELY(vtable->chain[class_id].count <= func_id))
-     return NULL;
+/* XXX: Only used for a debug message below. Doesn't matter that it's slow. */
+static const _Efl_Class *
+_eo_op_class_get(Efl_Object_Op op)
+{
+   _Efl_Class **itr = _eo_classes;
+   int mid, max, min;
 
-   return &vtable->chain[class_id].funcs[func_id];
+   min = 0;
+   max = _eo_classes_last_id - 1;
+   while (min <= max)
+     {
+        mid = (min + max) / 2;
+
+        if (itr[mid]->base_id + itr[mid]->ops_count < op)
+           min = mid + 1;
+        else if (itr[mid]->base_id  > op)
+           max = mid - 1;
+        else
+           return itr[mid];
+     }
+
+   return NULL;
 }
 
 static inline Eina_Bool
@@ -326,30 +256,24 @@ _vtable_func_set(Eo_Vtable *vtable, const _Efl_Class 
*klass,
                  Eo_Op_Func_Type func, Eina_Bool allow_same_override)
 {
    op_type_funcs *fsrc;
-   unsigned short class_id = EFL_OBJECT_OP_CLASS_PART(op);
-   unsigned short func_id = EFL_OBJECT_OP_FUNC_PART(op);
-   Eo_Vtable_Node *hirachy_node = NULL;
-   Eo_Vtable_Node *node = NULL;
+   size_t idx1 = DICH_CHAIN1(op);
+   Dich_Chain1 *chain1;
 
-   EINA_SAFETY_ON_FALSE_RETURN_VAL(vtable->size >= class_id, EINA_FALSE);
-
-   if (klass->parent && klass->parent->vtable.size > class_id)
-     hirachy_node = &klass->parent->vtable.chain[class_id];
+   EINA_SAFETY_ON_FALSE_RETURN_VAL(idx1 < vtable->size, EINA_FALSE);
+   chain1 = &vtable->chain[idx1];
+   _vtable_chain_write_prepare(chain1);
+   fsrc = &chain1->chain2->funcs[DICH_CHAIN_LAST(op)];
    if (hierarchy_klass)
-     hirachy_node = &hierarchy_klass->vtable.chain[class_id];
-   node = &vtable->chain[class_id];
-
-   EINA_SAFETY_ON_NULL_RETURN_VAL(node->funcs, EINA_FALSE);
-   EINA_SAFETY_ON_FALSE_RETURN_VAL(node->count >= func_id, EINA_FALSE);
-   fsrc = &node->funcs[func_id];
-
-   if (hierarchy_klass && !func)
      {
         if (!func)
           {
-             op_type_funcs funcs = hirachy_node->funcs[func_id];
-             klass = funcs.src;
-             func = funcs.func;
+             op_type_funcs *fsrc_orig;
+             Dich_Chain1 *chain1_orig;
+
+             chain1_orig = &hierarchy_klass->vtable.chain[idx1];
+             fsrc_orig = &chain1_orig->chain2->funcs[DICH_CHAIN_LAST(op)];
+             func = fsrc_orig->func;
+             klass = fsrc_orig->src;
           }
      }
    else
@@ -363,12 +287,27 @@ _vtable_func_set(Eo_Vtable *vtable, const _Efl_Class 
*klass,
           }
      }
 
-   fsrc->src = klass;
    fsrc->func = func;
+   fsrc->src = klass;
 
    return EINA_TRUE;
 }
 
+void
+_vtable_func_clean_all(Eo_Vtable *vtable)
+{
+   size_t i;
+   Dich_Chain1 *chain1 = vtable->chain;
+
+   for (i = 0 ; i < vtable->size ; i++, chain1++)
+     {
+        if (chain1->chain2)
+           _vtable_chain2_unref(chain1->chain2);
+     }
+   free(vtable->chain);
+   vtable->chain = NULL;
+}
+
 /* END OF DICH */
 
 #define _EO_ID_GET(Id) ((Eo_Id) (Id))
@@ -539,7 +478,7 @@ _efl_object_call_resolve(Eo *eo_id, const char *func_name, 
Efl_Object_Op_Call_Da
 
         obj = _obj;
         klass = _obj->klass;
-        vtable = EO_VTABLE2(obj);
+        vtable = EO_VTABLE(obj);
         if (EINA_UNLIKELY(_obj->cur_klass != NULL))
           {
              // YES this is a goto with a label to return. this is a
@@ -605,7 +544,7 @@ end:
              EO_OBJ_POINTER_PROXY(emb_obj_id, emb_obj);
              if (EINA_UNLIKELY(!emb_obj)) continue;
 
-             func = _vtable_func_get(&emb_obj->klass->vtable, op);
+             func = _vtable_func_get(EO_VTABLE(emb_obj), op);
              if (func == NULL) goto composite_continue;
 
              if (EINA_LIKELY(func->func && func->src))
@@ -788,25 +727,23 @@ _efl_object_op_api_id_get(const void *api_func, const Eo 
*eo_obj, const char *ap
 /* klass is the klass we are working on. hierarchy_klass is the class whe 
should
  * use when validating. */
 static Eina_Bool
-_eo_class_funcs_set(Eo_Vtable *vtable, const Efl_Object_Ops *ops, const 
_Efl_Class *hierarchy_klass, const _Efl_Class *klass, Eina_Bool override_only, 
unsigned int class_id, Eina_Bool *hitmap)
+_eo_class_funcs_set(Eo_Vtable *vtable, const Efl_Object_Ops *ops, const 
_Efl_Class *hierarchy_klass, const _Efl_Class *klass, Efl_Object_Op id_offset, 
Eina_Bool override_only)
 {
    unsigned int i, j;
-   unsigned int number_of_new_functions = 0;
+   Efl_Object_Op op_id;
    const Efl_Op_Description *op_desc;
    const Efl_Op_Description *op_descs;
    const _Efl_Class *override_class;
    const void **api_funcs;
    Eina_Bool check_equal;
 
+   op_id = hierarchy_klass->base_id + id_offset;
    op_descs = ops->descs;
    override_class = override_only ? hierarchy_klass : NULL;
 
    DBG("Set functions for class '%s':%p", klass->desc->name, klass);
 
-   if (!override_only)
-     _vtable_insert_empty_funcs(vtable, class_id);
-   if (!op_descs || !ops->count)
-     return EINA_TRUE;
+   if (!op_descs || !ops->count) return EINA_TRUE;
 
 #ifdef EO_DEBUG
    check_equal = EINA_TRUE;
@@ -839,61 +776,43 @@ _eo_class_funcs_set(Eo_Vtable *vtable, const 
Efl_Object_Ops *ops, const _Efl_Cla
 
              api_funcs[i] = op_desc->api_func;
           }
-        if (_efl_object_api_op_id_get_internal(op_desc->api_func) == EFL_NOOP)
-          {
-             number_of_new_functions ++;
-          }
      }
 
-   if (!override_only)
-     {
-        //Before setting any real functions, allocate the node that will 
contain all the functions
-        _vtable_prepare_empty_node(vtable, number_of_new_functions, class_id);
-        hitmap[class_id] = EINA_TRUE;
-     }
-
-   for (i = 0, j = 0, op_desc = op_descs; i < ops->count; i++, op_desc++)
+   for (i = 0, op_desc = op_descs; i < ops->count; i++, op_desc++)
      {
-        Efl_Object_Op op2 = EFL_NOOP;
-        short op2_class_id;
+        Efl_Object_Op op = EFL_NOOP;
 
         /* Get the opid for the function. */
-        op2 = _efl_object_api_op_id_get_internal(op_desc->api_func);
+        op = _efl_object_api_op_id_get_internal(op_desc->api_func);
 
-        if (op2 == EFL_NOOP)
+        if (op == EFL_NOOP)
           {
-             //functions that do not have a op yet, are considered to be 
belonging to this class
              if (override_only)
                {
                   ERR("Class '%s': Tried overriding a previously undefined 
function.", klass->desc->name);
                   return EINA_FALSE;
                }
 
-             op2 = EFL_OBJECT_OP_CREATE_OP_ID(class_id, j);
+             op = op_id;
              eina_spinlock_take(&_ops_storage_lock);
 #ifndef _WIN32
-             eina_hash_add(_ops_storage, &op_desc->api_func, (void *) 
(uintptr_t) op2);
+             eina_hash_add(_ops_storage, &op_desc->api_func, (void *) 
(uintptr_t) op);
 #else
-             eina_hash_add(_ops_storage, op_desc->api_func, (void *) 
(uintptr_t) op2);
+             eina_hash_add(_ops_storage, op_desc->api_func, (void *) 
(uintptr_t) op);
 #endif
              eina_spinlock_release(&_ops_storage_lock);
-             j ++;
+
+             op_id++;
           }
 
 #ifdef EO_DEBUG
         DBG("%p->%p '%s'", op_desc->api_func, op_desc->func, 
_eo_op_desc_name_get(op_desc));
 #endif
-        op2_class_id = EFL_OBJECT_OP_CLASS_PART(op2);
-         //in case we are having a function overwrite for a specific type, 
copy the relevant vtable
-        if (!hitmap[op2_class_id])
-          {
-             const Eo_Vtable_Node node = vtable->chain[op2_class_id];
-             _vtable_copy_node(&vtable->chain[op2_class_id], &node);
-             hitmap[op2_class_id] = EINA_TRUE;
-          }
-        if (!_vtable_func_set(vtable, klass, override_class, op2, 
op_desc->func, EINA_TRUE))
+
+        if (!_vtable_func_set(vtable, klass, override_class, op, 
op_desc->func, EINA_TRUE))
           return EINA_FALSE;
      }
+
    return EINA_TRUE;
 }
 
@@ -902,7 +821,6 @@ efl_class_functions_set(const Efl_Class *klass_id, const 
Efl_Object_Ops *object_
 {
    EO_CLASS_POINTER_GOTO(klass_id, klass, err_klass);
    Efl_Object_Ops empty_ops = { 0 };
-   Eina_Bool *hitmap;
 
    // not likely so use goto to alleviate l1 instruction cache of rare code
    if (klass->functions_set) goto err_funcs;
@@ -914,75 +832,23 @@ efl_class_functions_set(const Efl_Class *klass_id, const 
Efl_Object_Ops *object_
 
    klass->ops_count = object_ops->count;
 
-   klass->class_id = _UNMASK_ID(klass->header.id) - 1;
+   klass->base_id = _eo_ops_last_id;
+   _eo_ops_last_id += klass->ops_count + 1;
 
-   _vtable_init(&klass->vtable);
+   _vtable_init(&klass->vtable, DICH_CHAIN1(_eo_ops_last_id) + 1);
 
-   hitmap = alloca(klass->vtable.size);
-   memset(hitmap, 0, klass->vtable.size);
-   /* Merge in all required vtable entries */
+   /* Flatten the function array */
      {
         const _Efl_Class **mro_itr = klass->mro;
-        /* take over everything from the parent */
-        if (klass->parent)
-          {
-             _vtable_take_over(&klass->vtable, &klass->parent->vtable);
-          }
-        /*
-         * - jump to the mro entry containing the parent
-         * - everything further from the parent to the next elements is already
-         *   represented in the vtable of the parent.
-         */
-        for (  ; *mro_itr ; mro_itr++)
-          {
-             if (*mro_itr == klass->parent)
-               break;
-          }
-        /**
-         * merge in all the APIs that are extended in the current klass for 
this first time.
-         * That means, they are not extended anywhere from the parent further 
up.
-         */
+        for (  ; *mro_itr ; mro_itr++) ;
+
+        /* Skip ourselves. */
         for ( mro_itr-- ; mro_itr > klass->mro ; mro_itr--)
-          {
-             _vtable_merge_defined_api(&klass->vtable, &(*mro_itr)->vtable, 
hitmap);
-          }
-        /*
-         * add slots for the interfaces and mixins we are inheriting from
-         */
-        for (int i = 0; klass->extensions[i]; i++)
-          {
-             const _Efl_Class *ext = klass->extensions[i];
-             /*for all extensions of the class, ensure that *at least* empty 
vtables are available, so the efl_isa calls do succeed*/
-             _vtable_merge_empty(&klass->vtable, &ext->vtable, hitmap);
-          }
+          _vtable_copy_all(&klass->vtable, &(*mro_itr)->vtable);
      }
-     {
-        unsigned int i;
 
-        for (i = 0; i < object_ops->count; i++)
-          {
-             Efl_Object_Op op = 
_efl_object_api_op_id_get_internal(object_ops->descs[i].api_func);
-             if (op == EFL_NOOP) continue; //EFL_NOOP means that this function 
is not yet defined, this will be handled later
-             short class_id = EFL_OBJECT_OP_CLASS_PART(op);
-             if (klass->vtable.chain[class_id].count == 0)
-               {
-                  const _Efl_Class *required_klass = _eo_classes[class_id];
-                  /* in case this type is not already inherited, error on 
everything that is not a mixin */
-                  if (klass->desc->type == EFL_CLASS_TYPE_MIXIN)
-                    {
-                       /* this is when a mixin implemets a regular api, we 
just prepare a empty node, the rest will be implemented later */
-                       _vtable_prepare_empty_node(&klass->vtable, 
required_klass->vtable.chain[class_id].count, class_id);
-                    }
-                  else
-                    {
-                       ERR("There is an API implemented, whoms type is not 
part of this class. %s vs. %s", klass->desc->name, required_klass->desc->name);
-                       _vtable_take_over(&klass->vtable, 
&required_klass->vtable);
-                    }
+   return _eo_class_funcs_set(&klass->vtable, object_ops, klass, klass, 0, 
EINA_FALSE);
 
-               }
-          }
-     }
-   return _eo_class_funcs_set(&klass->vtable, object_ops, klass, klass, 
EINA_FALSE, klass->class_id, hitmap);
 err_funcs:
    ERR("Class %s already had its functions set..", klass->desc->name);
    return EINA_FALSE;
@@ -1232,8 +1098,8 @@ _eo_free(_Eo_Object *obj, Eina_Bool manual_free 
EINA_UNUSED)
 #endif
    if (_obj_is_override(obj))
      {
-        if (obj->opt)
-          _vtable_free(obj->opt->vtable, &obj->klass->vtable);
+        _vtable_func_clean_all(obj->opt->vtable);
+        eina_freeq_ptr_main_add(obj->opt->vtable, free, 0);
         EO_OPTIONAL_COW_SET(obj, vtable, NULL);
      }
 
@@ -1324,6 +1190,22 @@ err_obj:
    return 0;
 }
 
+
+static void
+_vtable_init(Eo_Vtable *vtable, size_t size)
+{
+   vtable->size = size;
+   vtable->chain = calloc(vtable->size, sizeof(*vtable->chain));
+}
+
+static void
+_vtable_free(Eo_Vtable *vtable)
+{
+   if (!vtable) return;
+   _vtable_func_clean_all(vtable);
+   eina_freeq_ptr_main_add(vtable, free, sizeof(*vtable));
+}
+
 static Eina_Bool
 _eo_class_mro_has(const _Efl_Class *klass, const _Efl_Class *find)
 {
@@ -1478,8 +1360,8 @@ eo_class_free(_Efl_Class *klass)
      {
         if (klass->desc->class_destructor)
            klass->desc->class_destructor(_eo_class_id_get(klass));
-         _vtable_mro_free(klass);
-        _vtable_free(&klass->vtable, NULL);
+
+        _vtable_func_clean_all(&klass->vtable);
      }
 
    EINA_TRASH_CLEAN(&klass->objects.trash, data)
@@ -1494,6 +1376,32 @@ eo_class_free(_Efl_Class *klass)
    eina_freeq_ptr_main_add(klass, free, 0);
 }
 
+/* Not really called, just used for the ptr... */
+static void
+_eo_class_isa_func(Eo *eo_id EINA_UNUSED, void *class_data EINA_UNUSED)
+{
+   /* Do nonthing. */
+}
+
+static void
+_eo_class_isa_recursive_set(_Efl_Class *klass, const _Efl_Class *cur)
+{
+   const _Efl_Class **extn_itr;
+
+   _vtable_func_set(&klass->vtable, klass, NULL, cur->base_id + cur->ops_count,
+                    _eo_class_isa_func, EINA_TRUE);
+
+   for (extn_itr = cur->extensions ; *extn_itr ; extn_itr++)
+     {
+        _eo_class_isa_recursive_set(klass, *extn_itr);
+     }
+
+   if (cur->parent)
+     {
+        _eo_class_isa_recursive_set(klass, cur->parent);
+     }
+}
+
 static inline void
 _eo_classes_release(void)
 {
@@ -1802,6 +1710,12 @@ efl_class_new(const Efl_Class_Description *desc, const 
Efl_Class *parent_id, ...
         efl_class_functions_set(_eo_class_id_get(klass), NULL, NULL);
      }
 
+   /* Mark which classes we implement */
+   if (klass->vtable.size)
+     {
+        _eo_class_isa_recursive_set(klass, klass);
+     }
+
    _eo_class_constructor(klass);
 
    DBG("Finished building class '%s'", klass->desc->name);
@@ -1818,41 +1732,32 @@ efl_object_override(Eo *eo_id, const Efl_Object_Ops 
*ops)
    if (ops)
      {
         Eo_Vtable *vtable = obj->opt->vtable;
-        //copy all the vtable nodes that we are going to change later on
-        Eina_Bool *hitmap;
 
         if (!vtable)
           {
              vtable = calloc(1, sizeof(*vtable));
-             _vtable_init(vtable);
-             _vtable_take_over(vtable, &obj->klass->vtable);
+             _vtable_init(vtable, obj->klass->vtable.size);
+             _vtable_copy_all(vtable, &obj->klass->vtable);
           }
 
-        hitmap = alloca(vtable->size * sizeof(Eina_Bool));
-        memset(hitmap, 0, vtable->size);
-
-        if (!_eo_class_funcs_set(vtable, ops, obj->klass, klass, EINA_TRUE, 
obj->klass->class_id, hitmap))
+        if (!_eo_class_funcs_set(vtable, ops, obj->klass, klass, 0, EINA_TRUE))
           {
              ERR("Failed to override functions for %s@%p. All previous "
                  "overrides have been reset.", obj->klass->desc->name, eo_id);
              if (obj->opt->vtable == vtable)
-               {
-                  EO_OPTIONAL_COW_SET(obj, vtable, NULL);
-               }
+               EO_OPTIONAL_COW_SET(obj, vtable, NULL);
              else
-               {
-                  _vtable_free(vtable, &obj->klass->vtable);
-               }
-
+               _vtable_free(vtable);
              goto err;
           }
+
         EO_OPTIONAL_COW_SET(obj, vtable, vtable);
      }
    else
      {
         if (obj->opt->vtable)
           {
-             _vtable_free(obj->opt->vtable, &obj->klass->vtable);
+             _vtable_free(obj->opt->vtable);
              EO_OPTIONAL_COW_SET(obj, vtable, NULL);
           }
      }
@@ -1886,10 +1791,10 @@ efl_isa(const Eo *eo_id, const Efl_Class *klass_id)
         EO_CLASS_POINTER_GOTO(klass_id, klass, err_class);
         EO_CLASS_POINTER_GOTO(eo_id, lookinto, err_class0);
 
-        if (EINA_UNLIKELY(lookinto->vtable.size <= klass->class_id))
-          return EINA_FALSE;
+        const op_type_funcs *func = _vtable_func_get
+          (&lookinto->vtable, klass->base_id + klass->ops_count);
 
-        return !!lookinto->vtable.chain[klass->class_id].funcs;
+        return (func && (func->func == _eo_class_isa_func));;
      }
 
    domain = ((Eo_Id)eo_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
@@ -1908,17 +1813,15 @@ efl_isa(const Eo *eo_id, const Efl_Class *klass_id)
 
         EO_OBJ_POINTER_GOTO(eo_id, obj, err_obj);
         EO_CLASS_POINTER_GOTO(klass_id, klass, err_class);
-
-        const Eo_Vtable vtable = obj->klass->vtable;
-        if (EINA_UNLIKELY(vtable.size <= klass->class_id))
-          return EINA_FALSE;
-
-        isa = !!vtable.chain[klass->class_id].funcs;
+        const op_type_funcs *func = _vtable_func_get
+          (EO_VTABLE(obj), klass->base_id + klass->ops_count);
 
         // Caching the result as we do a lot of serial efl_isa due to 
evas_object_image using it.
         tdata->cache.isa_id = eo_id;
         tdata->cache.klass = klass_id;
-        tdata->cache.isa = isa;
+        // Currently implemented by reusing the LAST op id. Just marking it 
with
+        // _eo_class_isa_func.
+        isa = tdata->cache.isa = (func && (func->func == _eo_class_isa_func));
      }
    else
      {
@@ -1938,15 +1841,15 @@ efl_isa(const Eo *eo_id, const Efl_Class *klass_id)
 
         EO_OBJ_POINTER_GOTO(eo_id, obj, err_shared_obj);
         EO_CLASS_POINTER_GOTO(klass_id, klass, err_shared_class);
-        if (EINA_UNLIKELY(obj->klass->vtable.size <= klass->class_id))
-          return EINA_FALSE;
-
-        isa = !!obj->klass->vtable.chain[klass->class_id].funcs;
+        const op_type_funcs *func = _vtable_func_get
+          (EO_VTABLE(obj), klass->base_id + klass->ops_count);
 
         // Caching the result as we do a lot of serial efl_isa due to 
evas_object_image using it.
         tdata->cache.isa_id = eo_id;
         tdata->cache.klass = klass_id;
-        tdata->cache.isa = isa;
+        // Currently implemented by reusing the LAST op id. Just marking it 
with
+        // _eo_class_isa_func.
+        isa = tdata->cache.isa = (func && (func->func == _eo_class_isa_func));
         EO_OBJ_DONE(eo_id);
         eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
      }
@@ -2423,6 +2326,7 @@ efl_object_init(void)
 
    _eo_classes = NULL;
    _eo_classes_last_id = EO_CLASS_IDS_FIRST - 1;
+   _eo_ops_last_id = EFL_OBJECT_OP_IDS_FIRST;
    _eo_log_dom = eina_log_domain_register(log_dom, EINA_COLOR_LIGHTBLUE);
    if (_eo_log_dom < 0)
      {
@@ -2480,6 +2384,12 @@ efl_object_init(void)
    eina_tls_set(_eo_table_data, data);
    _efl_object_main_thread = eina_thread_self();
 
+#ifdef EO_DEBUG
+   /* Call it just for coverage purposes. Ugly I know, but I like it better 
than
+    * casting everywhere else. */
+   _eo_class_isa_func(NULL, NULL);
+#endif
+
    efl_object_optional_cow =
          eina_cow_add("Efl Object Optional Data", sizeof(Efl_Object_Optional),
                       64, &efl_object_optional_cow_default, EINA_TRUE);
diff --git a/src/lib/eo/eo_base_class.c b/src/lib/eo/eo_base_class.c
index 44fe92cf4a..27c21a3e78 100644
--- a/src/lib/eo/eo_base_class.c
+++ b/src/lib/eo/eo_base_class.c
@@ -2325,7 +2325,7 @@ _efl_object_event_callback_forwarder_del(Eo *obj, 
Efl_Object_Data *pd EINA_UNUSE
    dpd = efl_data_scope_safe_get(new_obj, EFL_OBJECT_CLASS);
    if (!dpd) return ;
 
-   ext = dpd->ext;
+   ext = _efl_object_extension_need(dpd);
    if (!ext) return ;
 
    EINA_LIST_FOREACH(eina_hash_find(ext->forwarders, desc), l, forwarder)
diff --git a/src/lib/eo/eo_private.h b/src/lib/eo/eo_private.h
index 2c902d8456..c64dee5f5e 100644
--- a/src/lib/eo/eo_private.h
+++ b/src/lib/eo/eo_private.h
@@ -70,14 +70,17 @@ static inline void _eo_id_release(const Eo_Id obj_id);
 
 void _eo_condtor_done(Eo *obj);
 
-typedef struct _Eo_Vtable_Node Eo_Vtable_Node;
+typedef struct _Dich_Chain1 Dich_Chain1;
 
 typedef struct _Eo_Vtable
 {
-   Eo_Vtable_Node *chain;
-   unsigned short size;
+   Dich_Chain1 *chain;
+   unsigned int size;
 } Eo_Vtable;
 
+/* Clean the vtable. */
+void _vtable_func_clean_all(Eo_Vtable *vtable);
+
 struct _Eo_Header
 {
      Eo_Id id;
@@ -85,7 +88,7 @@ struct _Eo_Header
 
 struct _Efl_Object_Optional
 {
-   Eo_Vtable         *vtable;
+   Eo_Vtable          *vtable;
    Eina_List          *composite_objects;
    Efl_Del_Intercept   del_intercept;
 };
@@ -126,6 +129,12 @@ struct _Eo_Object
      Eina_Bool ownership_track:1;
 };
 
+/* How we search and store the implementations in classes. */
+#define DICH_CHAIN_LAST_BITS 5
+#define DICH_CHAIN_LAST_SIZE (1 << DICH_CHAIN_LAST_BITS)
+#define DICH_CHAIN1(x) ((x) >> DICH_CHAIN_LAST_BITS)
+#define DICH_CHAIN_LAST(x) ((x) & ((1 << DICH_CHAIN_LAST_BITS) - 1))
+
 extern Eina_Cow *efl_object_optional_cow;
 #define EO_OPTIONAL_COW_WRITE(_obj) ({ Efl_Object_Optional *_cow = 
eina_cow_write(efl_object_optional_cow, (const Eina_Cow_Data**)&(_obj->opt)); 
_cow; })
 #define EO_OPTIONAL_COW_END(_cow, _obj) eina_cow_done(efl_object_optional_cow, 
(const Eina_Cow_Data**)&(_obj->opt), _cow, EINA_TRUE)
@@ -137,7 +146,6 @@ extern Eina_Cow *efl_object_optional_cow;
       EO_OPTIONAL_COW_END(_obj##_cow, _obj); \
    }} while (0)
 #define EO_VTABLE(_obj) ((_obj)->opt->vtable ?: &((_obj)->klass->vtable))
-#define EO_VTABLE2(_obj) ((_obj)->opt->vtable ?: &((_obj)->klass->vtable))
 
 typedef void (*Eo_Op_Func_Type)(Eo *, void *class_data);
 
@@ -147,9 +155,15 @@ typedef struct
    const _Efl_Class *src;
 } op_type_funcs;
 
-struct _Eo_Vtable_Node{
-   op_type_funcs *funcs;
-   unsigned short count;
+typedef struct _Dich_Chain2
+{
+   op_type_funcs funcs[DICH_CHAIN_LAST_SIZE];
+   unsigned short refcount;
+} Dich_Chain2;
+
+struct _Dich_Chain1
+{
+   Dich_Chain2 *chain2;
 };
 
 typedef struct
@@ -189,7 +203,7 @@ struct _Efl_Class
    } iterators;
 
    unsigned int obj_size; /**< size of an object of this class */
-   unsigned int class_id; /**< the id which can be used to find the slot in 
_eo_classes and vtables chains */
+   unsigned int base_id;
    unsigned int data_offset; /* < Offset of the data within object data. */
    unsigned int ops_count; /* < Offset of the data within object data. */
 
diff --git a/src/tests/eo/suite/eo_test_general.c 
b/src/tests/eo/suite/eo_test_general.c
index ae026a27f4..1494f46c95 100644
--- a/src/tests/eo/suite/eo_test_general.c
+++ b/src/tests/eo/suite/eo_test_general.c
@@ -329,6 +329,7 @@ EFL_END_TEST
 
 EFL_START_TEST(efl_data_safe_fetch)
 {
+
    Eo *obj = efl_add_ref(SIMPLE2_CLASS, NULL);
    fail_if(!obj || !efl_data_scope_safe_get(obj, SIMPLE2_CLASS));
    efl_unref(obj);

-- 


Reply via email to