Compare commits

...

5 Commits

Author SHA1 Message Date
Marcel Hollerbach 9ff2f03c35 eo-files: reorder some functions
this started earlier today in IRC, the idea was that now, that we are
having a denser memory layout in the vtables, we could simply reorder
the functions in the .eo files into groups that are often called next to
each other, that rises the chance that the Vtable nodes as well as the
chains are still in the cache.

After searching a bit for these groups, the below changed .eo files came
up. When a object is constructed, we call a lot parent set constructor
finalizer and provider find stuff. So moving these together seems
logical.
During rendering we also call a lot of gfx hints, which is also paying
off, our cache miss number goes down quite a bit.

cache misses averages: (Messured on a Dell XPS i7-7700HQ)
   without this: 8945471
   with this: 8014886

Differential Revision: https://phab.enlightenment.org/D11548
2020-03-20 19:47:04 +01:00
Marcel Hollerbach 79d9b20aab eo: redo vtable mro creation
up to now we have created the vtable of a class by walking the mro from
the most upper element to the klass itself. To give a broader view, the
mro of a klass X that extends the class Y and implements A,B,C,D

The mro of X is then equal to [A,B,C,D] + the mro of Y. Which means, we
can simply copy over the vtables of Y, and start walking at D, which
will result in the same vtable.

The sideeffect of doing that is, that we do not allocate that much
memory anymore. Reason for this is quite simple:
For every mixin that is part of the mro, we are copying the vtable node,
to insert new API implemented by the mixin. However, the resulting new
vtable is every time the same. Which means, we could actaully copy them.

The same messurements as in the previous commits are taken:
malloc tracking:
  new: 452128
  old: 556656
  Safeup: ~102 KB

pmap:
  new: 542884K
  old: 542168K
  Safeup: ~716 KB

Differential Revision: https://phab.enlightenment.org/D11539
2020-03-20 19:46:06 +01:00
Marcel Hollerbach 228aacf593 eo: add a generic memory allocation tracking method
this is super usefull when checking memory allocations. Esp. when
checking out new memory allocation schemes.

Differential Revision: https://phab.enlightenment.org/D11538
2020-03-20 19:46:06 +01:00
Marcel Hollerbach 93a701d065 eo: rework vtable allocation scheme
with this commit a new way of allocating vtables arrived.
The old mechnism was to allocate a table big enough to carry *all*
functions at once, in order to not allocate that much memory for
functions that are not implemented on a specific klass, dichchains have
been used, which can be seens as a 2D matrix, where columns are only
allocated if min 1 entry needs to be written, this may have been a good
way to allocate back in the day when all this with eo started, however,
it showed to not pay off.

With this new way, we allocate a array of arrays. the first lvl array is
carrying enough slots, that *all* up to the time defined
interfaces/classes/abstracts/mixins can be implemented. The second lvl
array then has exactly the size of the defined APIs. The second lvl
array is obviously only allocated if needed.

When comparing the two methods, i messured two things, the usage based
on memory allocation for vtable-way-1 and vtable-way-2. Additionally, i
checked the overall memory usage of elementary_test using pmap. The
first messurement is a little bit more exact. The second messurement is
more biased, but captures the whole picture.

Memory allocation tracking:
   vtable-way-1 - vtable-way-2 = 74680 Byte

Pmap memory tracking:
   vtable-way1 - vtable-way-2 = 217088 Byte

The second messurement shows a bigger impact, likely because this is
also showing off all the sideeffects that we are taking place due to
fewer allocations.

Differential Revision: https://phab.enlightenment.org/D11535
2020-03-20 19:46:06 +01:00
Marcel Hollerbach a935695aad eo: do not allocate extension if deleting
->ext is getting freed during invalidate. If we unregister during
destruction (which is something that might happen) we should not
allocate the extension again.

Differential Revision: https://phab.enlightenment.org/D11524
2020-03-20 19:46:05 +01:00
6 changed files with 536 additions and 461 deletions

View File

@ -23,6 +23,97 @@ interface Efl.Gfx.Hint
]]
event_c_prefix: efl_gfx_entity;
methods {
@property hint_size_max {
[[Hints on the object's maximum size.
This is not a size enforcement in any way, it's just a hint
that should be used whenever appropriate.
The object container is in charge of fetching this property and
placing the object accordingly.
Values -1 will be treated as unset hint components, when
queried by managers.
Note: Smart objects (such as elementary) can have their own
hint policy. So calling this API may or may not affect
the size of smart objects.
Note: It is an error for the @.hint_size_max to be smaller in either axis
than @.hint_size_min. In this scenario, the max size hint will be
prioritized over the user min size hint.
]]
values {
sz: Eina.Size2D; [[Maximum size (hint) in pixels, (-1, -1) by
default for canvas objects).]]
}
}
@property hint_margin {
[[Hints for an object's margin or padding space.
This is not a size enforcement in any way, it's just a hint
that should be used whenever appropriate.
The object container is in charge of fetching this property and
placing the object accordingly.
Note: Smart objects (such as elementary) can have their own
hint policy. So calling this API may or may not affect
the size of smart objects.
]]
/*
@image html padding-hints.png
*/
values {
l: int; [[Integer to specify left padding.]]
r: int; [[Integer to specify right padding.]]
t: int; [[Integer to specify top padding.]]
b: int; [[Integer to specify bottom padding.]]
}
}
@property hint_align {
[[Hints for an object's alignment.
These are hints on how to align this object inside the
boundaries of its container/manager.
This is not a size enforcement in any way, it's just a hint
that should be used whenever appropriate.
]]
/*
@image html alignment-hints.png
*/
values {
x: Efl.Gfx.Align(0.5); [[Controls the horizontal alignment.]]
y: Efl.Gfx.Align(0.5); [[Controls the vertical alignment.]]
}
}
@property hint_weight {
[[Hints for an object's weight.
This is a hint on how a container object should resize a given
child within its area. Containers may adhere to the simpler
logic of just expanding the child object's dimensions to fit
its own (see the @Efl.Gfx.Hint_Expand helper weight macro) or
the complete one of taking each child's weight hint as real
weights to how much of its size to allocate for them in each
axis. A container is supposed to, after normalizing the
weights of its children (with weight hints), distribut
the space it has to layout them by those factors -- most
weighted children get larger in this process than the least
ones.
Accepted values are zero or positive values. Some containers might
use this hint as a boolean, but some others might consider it as a
proportion, see documentation of each container.
Note: Default weight hint values are 0.0, for both axis.
]]
values {
x: double; [[Non-negative double value to use as horizontal weight hint.]]
y: double; [[Non-negative double value to use as vertical weight hint.]]
}
}
@property hint_aspect {
[[Defines the aspect ratio to respect when scaling this object.
@ -50,31 +141,6 @@ interface Efl.Gfx.Hint
*/
}
}
@property hint_size_max {
[[Hints on the object's maximum size.
This is not a size enforcement in any way, it's just a hint
that should be used whenever appropriate.
The object container is in charge of fetching this property and
placing the object accordingly.
Values -1 will be treated as unset hint components, when
queried by managers.
Note: Smart objects (such as elementary) can have their own
hint policy. So calling this API may or may not affect
the size of smart objects.
Note: It is an error for the @.hint_size_max to be smaller in either axis
than @.hint_size_min. In this scenario, the max size hint will be
prioritized over the user min size hint.
]]
values {
sz: Eina.Size2D; [[Maximum size (hint) in pixels, (-1, -1) by
default for canvas objects).]]
}
}
@property hint_size_restricted_max {
[[Internal hints for an object's maximum size.
@ -187,72 +253,6 @@ interface Efl.Gfx.Hint
sz: Eina.Size2D; [[Minimum size (hint) in pixels.]]
}
}
@property hint_margin {
[[Hints for an object's margin or padding space.
This is not a size enforcement in any way, it's just a hint
that should be used whenever appropriate.
The object container is in charge of fetching this property and
placing the object accordingly.
Note: Smart objects (such as elementary) can have their own
hint policy. So calling this API may or may not affect
the size of smart objects.
]]
/*
@image html padding-hints.png
*/
values {
l: int; [[Integer to specify left padding.]]
r: int; [[Integer to specify right padding.]]
t: int; [[Integer to specify top padding.]]
b: int; [[Integer to specify bottom padding.]]
}
}
@property hint_weight {
[[Hints for an object's weight.
This is a hint on how a container object should resize a given
child within its area. Containers may adhere to the simpler
logic of just expanding the child object's dimensions to fit
its own (see the @Efl.Gfx.Hint_Expand helper weight macro) or
the complete one of taking each child's weight hint as real
weights to how much of its size to allocate for them in each
axis. A container is supposed to, after normalizing the
weights of its children (with weight hints), distribut
the space it has to layout them by those factors -- most
weighted children get larger in this process than the least
ones.
Accepted values are zero or positive values. Some containers might
use this hint as a boolean, but some others might consider it as a
proportion, see documentation of each container.
Note: Default weight hint values are 1.0, for both axis.
]]
values {
x: double; [[Non-negative double value to use as horizontal weight hint.]]
y: double; [[Non-negative double value to use as vertical weight hint.]]
}
}
@property hint_align {
[[Hints for an object's alignment.
These are hints on how to align this object inside the
boundaries of its container/manager.
This is not a size enforcement in any way, it's just a hint
that should be used whenever appropriate.
]]
/*
@image html alignment-hints.png
*/
values {
x: Efl.Gfx.Align(0.5); [[Controls the horizontal alignment.]]
y: Efl.Gfx.Align(0.5); [[Controls the vertical alignment.]]
}
}
@property hint_fill {
[[Hints for an object's fill property that used to specify "justify"
or "fill" by some users. @.hint_fill specify whether to fill

View File

@ -75,6 +75,50 @@ abstract Efl.Object
c_prefix: efl;
methods {
constructor {
[[Implement this method to provide optional initialization code for your object.
See the Life Cycle section in this class' description.]]
return: Efl.Object; [[The new object, can be $NULL if aborted.]]
}
destructor {
[[Implement this method to provide deinitialization code for your object if you need it.
Will be called once @.invalidate has returned.
See the Life Cycle section in this class' description.]]
}
finalize {
[[Implement this method to finish the initialization of your object
after all (if any) user-provided configuration methods have been
executed.
Use this method to delay expensive operations until user configuration
has finished, to avoid building the object in a "default" state in the
constructor, just to have to throw it all away because a user
configuration (a property being set, for example) requires a different
state.
This is the last call inside efl_add() and will set @.finalized to $true
once it returns.
This is an optimization and implementing this method is optional if you
already perform all your initialization in the @.constructor method.
See the Life Cycle section in this class' description.]]
return: Efl.Object; [[The new object. Return $NULL to abort object creation.]]
}
invalidate {
[[Implement this method to perform special actions when your object loses
its parent, if you need to.
It is called when the parent reference is lost or set to $NULL. After this
call returns, @.invalidated is set to $true.
This allows a simpler tear down of complex hierarchies, by performing
object destruction in two steps, first all object relationships are
broken and then the isolated objects are destroyed. Performing everything
in the @.destructor can sometimes lead to deadlocks, but implementing
this method is optional if this is not your case.
When an object with a parent is destroyed, it first receives a call to
@.invalidate and then to @.destructor.
See the Life Cycle section in this class' description.]]
}
@property parent {
[[The parent of an object.
@ -103,55 +147,6 @@ abstract Efl.Object
parent: Efl.Object; [[The new parent.]]
}
}
@property name {
[[The name of the object.
Every EFL object can have a name. Names may not contain the
following characters: / ? * [ ] ! \ :
Using any of these in a name will result in undefined behavior
later on. An empty string is considered the same as a $NULL string
or no string for the name.
]]
set {
}
get {
}
values {
name: string; [[The name.]]
}
}
@property comment {
[[A human readable comment for the object.
Every EFL object can have a comment. This is intended for developers
and debugging. An empty string is considered the same as a $NULL
string or no string for the comment.
]]
set {
}
get {
}
values {
comment: string; [[The comment.]]
}
}
debug_name_override {
[[Build a read-only name for this object used for debugging.
Multiple calls using efl_super() can be chained in order to build
the entire debug name, from parent to child classes. In C the usual
way to build the string is as follows:
efl_debug_name_override(efl_super(obj, MY_CLASS), sb);
eina_strbuf_append_printf(sb, "new_information");
Usually more debug information should be added to $sb after
calling the super function.
]]
params {
@in sb: strbuf; [[A string buffer, must not be $null.]]
}
}
@property event_global_freeze_count @static {
[[Return the global count of freeze events.
@ -224,64 +219,6 @@ abstract Efl.Object
}
return : Efl.Object; [[Object from the provider list.]]
}
constructor {
[[Implement this method to provide optional initialization code for your object.
See the Life Cycle section in this class' description.]]
return: Efl.Object; [[The new object, can be $NULL if aborted.]]
}
destructor {
[[Implement this method to provide deinitialization code for your object if you need it.
Will be called once @.invalidate has returned.
See the Life Cycle section in this class' description.]]
}
finalize {
[[Implement this method to finish the initialization of your object
after all (if any) user-provided configuration methods have been
executed.
Use this method to delay expensive operations until user configuration
has finished, to avoid building the object in a "default" state in the
constructor, just to have to throw it all away because a user
configuration (a property being set, for example) requires a different
state.
This is the last call inside efl_add() and will set @.finalized to $true
once it returns.
This is an optimization and implementing this method is optional if you
already perform all your initialization in the @.constructor method.
See the Life Cycle section in this class' description.]]
return: Efl.Object; [[The new object. Return $NULL to abort object creation.]]
}
invalidate {
[[Implement this method to perform special actions when your object loses
its parent, if you need to.
It is called when the parent reference is lost or set to $NULL. After this
call returns, @.invalidated is set to $true.
This allows a simpler tear down of complex hierarchies, by performing
object destruction in two steps, first all object relationships are
broken and then the isolated objects are destroyed. Performing everything
in the @.destructor can sometimes lead to deadlocks, but implementing
this method is optional if this is not your case.
When an object with a parent is destroyed, it first receives a call to
@.invalidate and then to @.destructor.
See the Life Cycle section in this class' description.]]
}
name_find @const @beta {
[[Find a child object with the given name and return it.
The search string can be a glob (shell style, using *). It can also
specify class name in the format of "class:name" where ":"
separates class and name. Both class and name can be globs.
If the class is specified but the name is empty like "class:" then
the search will match any object of that class.
]]
params {
@in search: string; [[The name search string.]]
}
return: Efl.Object; [[The first object found.]]
}
event_thaw {
[[Thaw events of object.
@ -355,6 +292,20 @@ abstract Efl.Object
@in new_obj: Efl.Object; [[The object to emit events from]]
}
}
name_find @const @beta {
[[Find a child object with the given name and return it.
The search string can be a glob (shell style, using *). It can also
specify class name in the format of "class:name" where ":"
separates class and name. Both class and name can be globs.
If the class is specified but the name is empty like "class:" then
the search will match any object of that class.
]]
params {
@in search: string; [[The name search string.]]
}
return: Efl.Object; [[The first object found.]]
}
children_iterator_new {
[[Get an iterator on all children.]]
return: iterator<Efl.Object> @move @no_unused; [[Children iterator]]
@ -429,6 +380,55 @@ abstract Efl.Object
}
return: bool; [[$true if successfully unregistered, $false otherwise.]]
}
@property name {
[[The name of the object.
Every EFL object can have a name. Names may not contain the
following characters: / ? * [ ] ! \ :
Using any of these in a name will result in undefined behavior
later on. An empty string is considered the same as a $NULL string
or no string for the name.
]]
set {
}
get {
}
values {
name: string; [[The name.]]
}
}
@property comment {
[[A human readable comment for the object.
Every EFL object can have a comment. This is intended for developers
and debugging. An empty string is considered the same as a $NULL
string or no string for the comment.
]]
set {
}
get {
}
values {
comment: string; [[The comment.]]
}
}
debug_name_override {
[[Build a read-only name for this object used for debugging.
Multiple calls using efl_super() can be chained in order to build
the entire debug name, from parent to child classes. In C the usual
way to build the string is as follows:
efl_debug_name_override(efl_super(obj, MY_CLASS), sb);
eina_strbuf_append_printf(sb, "new_information");
Usually more debug information should be added to $sb after
calling the super function.
]]
params {
@in sb: strbuf; [[A string buffer, must not be $null.]]
}
}
}
implements {
class.constructor;

View File

@ -90,7 +90,6 @@ 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;
@ -104,7 +103,6 @@ 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);
@ -120,96 +118,190 @@ static inline Efl_Object_Op _efl_object_api_op_id_get_internal(const void *api_f
(_eo_classes[_UNMASK_ID(id) - 1]) : NULL); \
})
static inline void
_vtable_chain2_unref(Dich_Chain2 *chain)
#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)
{
if (--(chain->refcount) == 0)
{
free(chain);
}
short class_id = EFL_OBJECT_OP_CLASS_PART(op);
return _eo_classes[class_id];
}
#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)
{
//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));
}
static inline void
_vtable_chain_alloc(Dich_Chain1 *chain1)
/**
* This removes all nodes from the klass that are copied from mro
*/
static void
_vtable_mro_free(const _Efl_Class *klass)
{
chain1->chain2 = calloc(1, sizeof(*(chain1->chain2)));
chain1->chain2->refcount = 1;
}
const _Efl_Class **mro_itr = klass->mro;
const Eo_Vtable *vtable = &klass->vtable;
static inline void _vtable_chain_write_prepare(Dich_Chain1 *dst);
static inline void
_vtable_chain_merge(Dich_Chain1 *dst, const Dich_Chain1 *src)
{
size_t j;
const op_type_funcs *sf = src->chain2->funcs;
op_type_funcs *df = dst->chain2->funcs;
if (df == sf)
for ( ; *mro_itr ; mro_itr++)
{
/* Skip if the chain is the same. */
return;
}
for (j = 0 ; j < DICH_CHAIN_LAST_SIZE ; j++, df++, sf++)
{
if (sf->func && memcmp(df, sf, sizeof(*df)))
const Eo_Vtable *mro_vtable = &(*mro_itr)->vtable;
for (int i = 0; i < mro_vtable->size; ++i)
{
_vtable_chain_write_prepare(dst);
df = dst->chain2->funcs + j;
memcpy(df, sf, sizeof(*df));
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)
{
if (!dst->chain2)
if (root)
{
_vtable_chain_alloc(dst);
return;
}
else if (dst->chain2->refcount == 1)
{
/* We own it, no need to duplicate */
return;
EINA_SAFETY_ON_FALSE_RETURN(vtable->size == root->size);
}
Dich_Chain1 old;
old.chain2 = dst->chain2;
_vtable_chain_alloc(dst);
_vtable_chain_merge(dst, &old);
_vtable_chain2_unref(old.chain2);
}
static inline void
_vtable_chain_copy_ref(Dich_Chain1 *dst, const Dich_Chain1 *src)
{
if (dst->chain2)
for (int i = 0; i < vtable->size; ++i)
{
_vtable_chain_merge(dst, src);
}
else
{
dst->chain2 = src->chain2;
dst->chain2->refcount++;
}
}
if (root && root->chain[i].funcs == vtable->chain[i].funcs)
vtable->chain[i].count = 0;
static inline void
_vtable_copy_all(Eo_Vtable *dst, const Eo_Vtable *src)
{
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 (sc1->chain2)
if (vtable->chain[i].count)
{
_vtable_chain_copy_ref(dc1, sc1);
free(vtable->chain[i].funcs);
}
}
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)
{
for (int i = 0; i < src->size; ++i)
{
if (src->chain[i].funcs)
{
dest->chain[i] = src->chain[i];
}
}
}
/**
* 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;
}
/**
* 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);
}
/**
* 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);
}
/**
* 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)
{
for (unsigned int i = 0; i < src->size; ++i)
{
//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];
}
}
}
}
}
/**
* 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)
{
for (unsigned int i = 0; i < src->size; ++i)
{
if (src->chain[i].funcs && !dest->chain[i].funcs)
{
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;
}
}
}
}
@ -217,37 +309,15 @@ _vtable_copy_all(Eo_Vtable *dst, const Eo_Vtable *src)
static inline const op_type_funcs *
_vtable_func_get(const Eo_Vtable *vtable, Efl_Object_Op 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)];
}
unsigned short class_id = EFL_OBJECT_OP_CLASS_PART(op);
unsigned short func_id = EFL_OBJECT_OP_FUNC_PART(op);
/* 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;
if (EINA_UNLIKELY(vtable->size <= class_id))
return NULL;
if (EINA_UNLIKELY(vtable->chain[class_id].count <= func_id))
return NULL;
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;
return &vtable->chain[class_id].funcs[func_id];
}
static inline Eina_Bool
@ -256,24 +326,30 @@ _vtable_func_set(Eo_Vtable *vtable, const _Efl_Class *klass,
Eo_Op_Func_Type func, Eina_Bool allow_same_override)
{
op_type_funcs *fsrc;
size_t idx1 = DICH_CHAIN1(op);
Dich_Chain1 *chain1;
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;
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)];
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];
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 *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;
op_type_funcs funcs = hirachy_node->funcs[func_id];
klass = funcs.src;
func = funcs.func;
}
}
else
@ -287,27 +363,12 @@ _vtable_func_set(Eo_Vtable *vtable, const _Efl_Class *klass,
}
}
fsrc->func = func;
fsrc->src = klass;
fsrc->func = func;
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))
@ -478,7 +539,7 @@ _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Da
obj = _obj;
klass = _obj->klass;
vtable = EO_VTABLE(obj);
vtable = EO_VTABLE2(obj);
if (EINA_UNLIKELY(_obj->cur_klass != NULL))
{
// YES this is a goto with a label to return. this is a
@ -544,7 +605,7 @@ end:
EO_OBJ_POINTER_PROXY(emb_obj_id, emb_obj);
if (EINA_UNLIKELY(!emb_obj)) continue;
func = _vtable_func_get(EO_VTABLE(emb_obj), op);
func = _vtable_func_get(&emb_obj->klass->vtable, op);
if (func == NULL) goto composite_continue;
if (EINA_LIKELY(func->func && func->src))
@ -727,23 +788,25 @@ _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, Efl_Object_Op id_offset, Eina_Bool override_only)
_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)
{
unsigned int i, j;
Efl_Object_Op op_id;
unsigned int number_of_new_functions = 0;
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 (!op_descs || !ops->count) return EINA_TRUE;
if (!override_only)
_vtable_insert_empty_funcs(vtable, class_id);
if (!op_descs || !ops->count)
return EINA_TRUE;
#ifdef EO_DEBUG
check_equal = EINA_TRUE;
@ -776,43 +839,61 @@ _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 ++;
}
}
for (i = 0, op_desc = op_descs; i < ops->count; i++, op_desc++)
if (!override_only)
{
Efl_Object_Op op = EFL_NOOP;
//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++)
{
Efl_Object_Op op2 = EFL_NOOP;
short op2_class_id;
/* Get the opid for the function. */
op = _efl_object_api_op_id_get_internal(op_desc->api_func);
op2 = _efl_object_api_op_id_get_internal(op_desc->api_func);
if (op == EFL_NOOP)
if (op2 == 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;
}
op = op_id;
op2 = EFL_OBJECT_OP_CREATE_OP_ID(class_id, j);
eina_spinlock_take(&_ops_storage_lock);
#ifndef _WIN32
eina_hash_add(_ops_storage, &op_desc->api_func, (void *) (uintptr_t) op);
eina_hash_add(_ops_storage, &op_desc->api_func, (void *) (uintptr_t) op2);
#else
eina_hash_add(_ops_storage, op_desc->api_func, (void *) (uintptr_t) op);
eina_hash_add(_ops_storage, op_desc->api_func, (void *) (uintptr_t) op2);
#endif
eina_spinlock_release(&_ops_storage_lock);
op_id++;
j ++;
}
#ifdef EO_DEBUG
DBG("%p->%p '%s'", op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
#endif
if (!_vtable_func_set(vtable, klass, override_class, op, op_desc->func, EINA_TRUE))
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))
return EINA_FALSE;
}
return EINA_TRUE;
}
@ -821,6 +902,7 @@ 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;
@ -832,23 +914,75 @@ efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_
klass->ops_count = object_ops->count;
klass->base_id = _eo_ops_last_id;
_eo_ops_last_id += klass->ops_count + 1;
klass->class_id = _UNMASK_ID(klass->header.id) - 1;
_vtable_init(&klass->vtable, DICH_CHAIN1(_eo_ops_last_id) + 1);
_vtable_init(&klass->vtable);
/* Flatten the function array */
hitmap = alloca(klass->vtable.size);
memset(hitmap, 0, klass->vtable.size);
/* Merge in all required vtable entries */
{
const _Efl_Class **mro_itr = klass->mro;
for ( ; *mro_itr ; mro_itr++) ;
/* Skip ourselves. */
/* 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 > klass->mro ; mro_itr--)
_vtable_copy_all(&klass->vtable, &(*mro_itr)->vtable);
{
_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);
}
}
{
unsigned int i;
return _eo_class_funcs_set(&klass->vtable, object_ops, klass, klass, 0, EINA_FALSE);
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, EINA_FALSE, klass->class_id, hitmap);
err_funcs:
ERR("Class %s already had its functions set..", klass->desc->name);
return EINA_FALSE;
@ -1098,8 +1232,8 @@ _eo_free(_Eo_Object *obj, Eina_Bool manual_free EINA_UNUSED)
#endif
if (_obj_is_override(obj))
{
_vtable_func_clean_all(obj->opt->vtable);
eina_freeq_ptr_main_add(obj->opt->vtable, free, 0);
if (obj->opt)
_vtable_free(obj->opt->vtable, &obj->klass->vtable);
EO_OPTIONAL_COW_SET(obj, vtable, NULL);
}
@ -1190,22 +1324,6 @@ 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)
{
@ -1360,8 +1478,8 @@ eo_class_free(_Efl_Class *klass)
{
if (klass->desc->class_destructor)
klass->desc->class_destructor(_eo_class_id_get(klass));
_vtable_func_clean_all(&klass->vtable);
_vtable_mro_free(klass);
_vtable_free(&klass->vtable, NULL);
}
EINA_TRASH_CLEAN(&klass->objects.trash, data)
@ -1376,32 +1494,6 @@ 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)
{
@ -1710,12 +1802,6 @@ 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);
@ -1732,32 +1818,41 @@ 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, obj->klass->vtable.size);
_vtable_copy_all(vtable, &obj->klass->vtable);
_vtable_init(vtable);
_vtable_take_over(vtable, &obj->klass->vtable);
}
if (!_eo_class_funcs_set(vtable, ops, obj->klass, klass, 0, EINA_TRUE))
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))
{
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);
{
_vtable_free(vtable, &obj->klass->vtable);
}
goto err;
}
EO_OPTIONAL_COW_SET(obj, vtable, vtable);
}
else
{
if (obj->opt->vtable)
{
_vtable_free(obj->opt->vtable);
_vtable_free(obj->opt->vtable, &obj->klass->vtable);
EO_OPTIONAL_COW_SET(obj, vtable, NULL);
}
}
@ -1791,10 +1886,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);
const op_type_funcs *func = _vtable_func_get
(&lookinto->vtable, klass->base_id + klass->ops_count);
if (EINA_UNLIKELY(lookinto->vtable.size <= klass->class_id))
return EINA_FALSE;
return (func && (func->func == _eo_class_isa_func));;
return !!lookinto->vtable.chain[klass->class_id].funcs;
}
domain = ((Eo_Id)eo_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
@ -1813,15 +1908,17 @@ 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 op_type_funcs *func = _vtable_func_get
(EO_VTABLE(obj), klass->base_id + klass->ops_count);
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;
// 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;
// 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));
tdata->cache.isa = isa;
}
else
{
@ -1841,15 +1938,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);
const op_type_funcs *func = _vtable_func_get
(EO_VTABLE(obj), klass->base_id + klass->ops_count);
if (EINA_UNLIKELY(obj->klass->vtable.size <= klass->class_id))
return EINA_FALSE;
isa = !!obj->klass->vtable.chain[klass->class_id].funcs;
// 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;
// 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));
tdata->cache.isa = isa;
EO_OBJ_DONE(eo_id);
eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
}
@ -2326,7 +2423,6 @@ 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)
{
@ -2384,12 +2480,6 @@ 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);

View File

@ -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 = _efl_object_extension_need(dpd);
ext = dpd->ext;
if (!ext) return ;
EINA_LIST_FOREACH(eina_hash_find(ext->forwarders, desc), l, forwarder)

View File

@ -70,17 +70,14 @@ static inline void _eo_id_release(const Eo_Id obj_id);
void _eo_condtor_done(Eo *obj);
typedef struct _Dich_Chain1 Dich_Chain1;
typedef struct _Eo_Vtable_Node Eo_Vtable_Node;
typedef struct _Eo_Vtable
{
Dich_Chain1 *chain;
unsigned int size;
Eo_Vtable_Node *chain;
unsigned short size;
} Eo_Vtable;
/* Clean the vtable. */
void _vtable_func_clean_all(Eo_Vtable *vtable);
struct _Eo_Header
{
Eo_Id id;
@ -88,7 +85,7 @@ struct _Eo_Header
struct _Efl_Object_Optional
{
Eo_Vtable *vtable;
Eo_Vtable *vtable;
Eina_List *composite_objects;
Efl_Del_Intercept del_intercept;
};
@ -129,12 +126,6 @@ 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)
@ -146,6 +137,7 @@ 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);
@ -155,15 +147,9 @@ typedef struct
const _Efl_Class *src;
} op_type_funcs;
typedef struct _Dich_Chain2
{
op_type_funcs funcs[DICH_CHAIN_LAST_SIZE];
unsigned short refcount;
} Dich_Chain2;
struct _Dich_Chain1
{
Dich_Chain2 *chain2;
struct _Eo_Vtable_Node{
op_type_funcs *funcs;
unsigned short count;
};
typedef struct
@ -203,7 +189,7 @@ struct _Efl_Class
} iterators;
unsigned int obj_size; /**< size of an object of this class */
unsigned int base_id;
unsigned int class_id; /**< the id which can be used to find the slot in _eo_classes and vtables chains */
unsigned int data_offset; /* < Offset of the data within object data. */
unsigned int ops_count; /* < Offset of the data within object data. */

View File

@ -329,7 +329,6 @@ 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);