1 #ifdef HAVE_CONFIG_H
2 # include <config.h>
3 #endif
4 
5 #if defined HAVE_DLADDR && ! defined _WIN32
6 # include <dlfcn.h>
7 #endif
8 
9 #include <Eina.h>
10 
11 #ifdef _WIN32
12 # include <evil_private.h> /* evil_time_get dladdr */
13 #endif
14 
15 #if defined(__APPLE__) && defined(__MACH__)
16 # include <mach/mach_time.h>
17 #endif
18 
19 #include "Eo.h"
20 #include "eo_ptr_indirection.h"
21 #include "eo_private.h"
22 #include "eo_add_fallback.h"
23 
24 #include "eo_internal.h"
25 
26 #include "efl_object_override.eo.c"
27 #ifdef HAVE_EXECINFO_H
28 #include <execinfo.h>
29 #endif
30 
31 #ifdef HAVE_VALGRIND
32 # include <valgrind.h>
33 # include <memcheck.h>
34 #endif
35 static Eina_Bool _eo_trash_bypass = EINA_FALSE;
36 
37 #define EO_CLASS_IDS_FIRST 1
38 #define EFL_OBJECT_OP_IDS_FIRST 1
39 
40 /* Used inside the class_get functions of classes, see #EFL_DEFINE_CLASS */
41 EAPI Eina_Lock _efl_class_creation_lock;
42 EAPI unsigned int _efl_object_init_generation = 1;
43 int _eo_log_dom = -1;
44 Eina_Thread _efl_object_main_thread;
45 static unsigned int efl_del_api_generation = 0;
46 static Efl_Object_Op _efl_del_api_op_id = 0;
47 static Eina_Hash *class_overrides;
48 
49 typedef enum _Eo_Ref_Op {
50    EO_REF_OP_NONE,
51    EO_REF_OP_NEW,
52    EO_REF_OP_FREE,
53    EO_REF_OP_REF,
54    EO_REF_OP_UNREF,
55    EO_REF_OP_REUSE,
56 } Eo_Ref_Op;
57 
58 static inline void _eo_log_obj_init(void);
59 static inline void _eo_log_obj_shutdown(void);
60 static inline void _eo_log_obj_ref_op(const _Eo_Object *obj, Eo_Ref_Op ref_op);
61 #ifdef EO_DEBUG
62 #define EO_LOG_OBJS_BACKTRACE_MAX     1022
63 #define EO_LOG_OBJS_BACKTRACE_DEFAULT 62
64 static int _eo_log_objs_dom = -1;
65 static int _eo_log_objs_backtrace = EO_LOG_OBJS_BACKTRACE_DEFAULT; // 0 = enable tracking but no bt, N>0 = enable tracking and bt of size N
66 static Eo_Ref_Op _eo_log_objs_level = EO_REF_OP_NONE;
67 static Eina_Inarray _eo_log_objs_debug;
68 static Eina_Inarray _eo_log_objs_no_debug;
69 static double _eo_log_time_start;
70 static const char *_eo_ref_op_str[] = {
71    "ERROR",
72    "New  ",
73    "Free ",
74    "Ref  ",
75    "Unref",
76    "Reuse",
77 };
78 #ifdef HAVE_BACKTRACE
79 static Eina_Array _eo_log_objs;
80 static Eina_Spinlock _eo_log_objs_lock;
81 #endif
82 #else
_eo_log_obj_init(void)83 static inline void _eo_log_obj_init(void) { }
_eo_log_obj_shutdown(void)84 static inline void _eo_log_obj_shutdown(void) { }
_eo_log_obj_ref_op(const _Eo_Object * obj EINA_UNUSED,Eo_Ref_Op ref_op EINA_UNUSED)85 static inline void _eo_log_obj_ref_op(const _Eo_Object *obj EINA_UNUSED, Eo_Ref_Op ref_op EINA_UNUSED) { }
_eo_log_obj_report(const Eo_Id id EINA_UNUSED,int log_level EINA_UNUSED,const char * func_name EINA_UNUSED,const char * file EINA_UNUSED,int line EINA_UNUSED)86 void _eo_log_obj_report(const Eo_Id id EINA_UNUSED, int log_level EINA_UNUSED, const char *func_name EINA_UNUSED, const char *file EINA_UNUSED, int line EINA_UNUSED) { }
87 #endif
88 
89 static _Efl_Class **_eo_classes = NULL;
90 static Eo_Id _eo_classes_last_id = 0;
91 static Eo_Id _eo_classes_alloc = 0;
92 static int _efl_object_init_count = 0;
93 static Eina_Hash *_ops_storage = NULL;
94 static Eina_Spinlock _ops_storage_lock;
95 
96 static const Efl_Object_Optional efl_object_optional_cow_default = {};
97 Eina_Cow *efl_object_optional_cow = NULL;
98 
99 static size_t _eo_sz = 0;
100 static size_t _eo_class_sz = 0;
101 
102 static void _eo_condtor_reset(_Eo_Object *obj);
103 static inline void *_efl_data_scope_get(const _Eo_Object *obj, const _Efl_Class *klass);
104 static inline void *_efl_data_xref_internal(const char *file, int line, _Eo_Object *obj, const _Efl_Class *klass, const _Eo_Object *ref_obj);
105 static inline void _efl_data_xunref_internal(_Eo_Object *obj, void *data, const _Eo_Object *ref_obj);
106 
107 static inline Efl_Object_Op _efl_object_api_op_id_get_internal(const void *api_func);
108 
109 /* Start of Dich */
110 
111 
112 /* We are substracting the mask here instead of "AND"ing because it's a hot path,
113  * it should be a valid class at this point, and this lets the compiler do 1
114  * substraction at compile time. */
115 #define _UNMASK_ID(id) ((id) - MASK_CLASS_TAG)
116 #define ID_CLASS_GET(id) ({ \
117       (_Efl_Class *) (((_UNMASK_ID(id) <= _eo_classes_last_id) && (_UNMASK_ID(id) > 0)) ? \
118       (_eo_classes[_UNMASK_ID(id) - 1]) : NULL); \
119       })
120 
121 #define EFL_OBJECT_OP_CLASS_PART(op) op >> 16
122 #define EFL_OBJECT_OP_FUNC_PART(op) op & 0xffff
123 #define EFL_OBJECT_OP_CREATE_OP_ID(class_id, func_id) ((unsigned short)class_id)<<16|((unsigned short)func_id&0xffff)
124 
125 static const _Efl_Class *
_eo_op_class_get(Efl_Object_Op op)126 _eo_op_class_get(Efl_Object_Op op)
127 {
128    short class_id = EFL_OBJECT_OP_CLASS_PART(op);
129    return _eo_classes[class_id];
130 }
131 #if defined(DEBUG_VTABLE_ALLOCATION)
132 static int _allocated_memory = 0;
133 
134 static inline void*
_vtable_alloc(unsigned long n,size_t elem)135 _vtable_alloc(unsigned long n, size_t elem)
136 {
137    _allocated_memory += n*elem;
138    return calloc(n, elem);
139 }
140 #else
141 static inline void*
_vtable_alloc(unsigned long n,size_t elem)142 _vtable_alloc(unsigned long n, size_t elem)
143 {
144    return calloc(n, elem);
145 }
146 #endif
147 
148 
149 /**
150  * This inits the vtable with a given size
151  */
152 static void
_vtable_init_size(Eo_Vtable * vtable,unsigned int size)153 _vtable_init_size(Eo_Vtable *vtable, unsigned int size)
154 {
155    //we assume here that _eo_classes_last_id was called before
156    vtable->size = size;
157    vtable->chain = _vtable_alloc(vtable->size, sizeof(Eo_Vtable_Node));
158 }
159 
160 /**
161  * This inits the vtable wit hthe current size of allocated tables
162  */
163 static void
_vtable_init(Eo_Vtable * vtable)164 _vtable_init(Eo_Vtable *vtable)
165 {
166    //we assume here that _eo_classes_last_id was called before
167    _vtable_init_size(vtable, _eo_classes_last_id);
168 }
169 
170 /**
171  * This removes all nodes from the klass that are copied from mro
172  */
173 static void
_vtable_mro_free(const _Efl_Class * klass)174 _vtable_mro_free(const _Efl_Class *klass)
175 {
176    const _Efl_Class **mro_itr = klass->mro;
177    const Eo_Vtable *vtable = &klass->vtable;
178    for (  ; *mro_itr ; mro_itr++)
179      {
180         const Eo_Vtable *mro_vtable = &(*mro_itr)->vtable;
181         if ((*mro_itr) == klass)
182           continue;
183         for (unsigned int i = 0; i < mro_vtable->size; ++i)
184           {
185              if (i == klass->class_id)
186                continue;
187              if (vtable->chain[i].funcs && mro_vtable->chain[i].funcs == vtable->chain[i].funcs)
188                {
189                   vtable->chain[i].funcs = NULL;
190                }
191           }
192      }
193 }
194 
195 static void
_vtable_free(Eo_Vtable * vtable,const Eo_Vtable * root)196 _vtable_free(Eo_Vtable *vtable, const Eo_Vtable *root)
197 {
198    if (root)
199      {
200         EINA_SAFETY_ON_FALSE_RETURN(vtable->size == root->size);
201      }
202 
203    for (int i = 0; i < vtable->size; ++i)
204      {
205         if (root && root->chain[i].funcs == vtable->chain[i].funcs)
206           vtable->chain[i].count = 0;
207 
208         if (vtable->chain[i].count)
209           {
210              free(vtable->chain[i].funcs);
211           }
212      }
213    free(vtable->chain);
214 }
215 
216 /**
217  * This takes over all set chains of the src to dest.
218  * This should only be called on Eo_Vtables, which are initialized with this value.
219  * Previous setted values are going to be overwritten.
220  */
221 static void
_vtable_take_over(Eo_Vtable * dest,const Eo_Vtable * src)222 _vtable_take_over(Eo_Vtable *dest, const Eo_Vtable *src)
223 {
224    for (int i = 0; i < src->size; ++i)
225      {
226         if (src->chain[i].funcs)
227           {
228              dest->chain[i] = src->chain[i];
229           }
230      }
231 }
232 
233 /**
234  * Fills the node of the passed class id with a empty none NULL pointer.
235  * This is used to indicate that a specific node has a normal 0 size, but is set.
236  */
237 static void
_vtable_insert_empty_funcs(Eo_Vtable * vtable,unsigned short class_id)238 _vtable_insert_empty_funcs(Eo_Vtable *vtable, unsigned short class_id)
239 {
240    vtable->chain[class_id].funcs = (void*)0x1010101;
241    vtable->chain[class_id].count = 0;
242 }
243 
244 /**
245  * duplicate the source node, and write the duplicated values to the destination
246  * No logical changes are applied to src.
247  */
248 static void
_vtable_copy_node(Eo_Vtable_Node * dest,const Eo_Vtable_Node * src)249 _vtable_copy_node(Eo_Vtable_Node *dest, const Eo_Vtable_Node *src)
250 {
251    dest->count = src->count;
252    dest->funcs = _vtable_alloc(sizeof(op_type_funcs), src->count);
253    memcpy(dest->funcs, src->funcs, sizeof(op_type_funcs) * src->count);
254 }
255 
256 /**
257  * Initialize a node with a empty funcs array of the passed length
258  */
259 static void
_vtable_prepare_empty_node(Eo_Vtable * dest,unsigned int length,unsigned int class_id)260 _vtable_prepare_empty_node(Eo_Vtable *dest, unsigned int length, unsigned int class_id)
261 {
262    dest->chain[class_id].count = length;
263    dest->chain[class_id].funcs = _vtable_alloc(sizeof(op_type_funcs), dest->chain[class_id].count);
264 }
265 
266 /**
267  * Copy all setted APIs from src to dest.
268  * Already set function slots are going to be replaced.
269  */
270 static void
_vtable_merge_defined_api(Eo_Vtable * dest,const Eo_Vtable * src,Eina_Bool * hitmap)271 _vtable_merge_defined_api(Eo_Vtable *dest, const Eo_Vtable *src, Eina_Bool *hitmap)
272 {
273    for (unsigned int i = 0; i < src->size; ++i)
274      {
275         //if there is a source node evalulate if we need to copy it
276         if (src->chain[i].funcs)
277           {
278              if (!dest->chain[i].funcs)
279                {
280                   dest->chain[i] = src->chain[i];
281                   EINA_SAFETY_ON_FALSE_RETURN(hitmap[i] == EINA_FALSE);
282                }
283              else
284                {
285                   if (!hitmap[i])
286                     {
287                        const Eo_Vtable_Node node = dest->chain[i];
288                        if (!node.count)
289                          _vtable_insert_empty_funcs(dest, i);
290                        else
291                          _vtable_copy_node(&dest->chain[i], &node); //we copy what we have, and overwrite in the later for loop
292                        hitmap[i] = EINA_TRUE;
293                     }
294                   for (int j = 0; j < src->chain[i].count; ++j)
295                     {
296                        if (src->chain[i].funcs[j].func)
297                          dest->chain[i].funcs[j] = src->chain[i].funcs[j];
298                     }
299               }
300           }
301      }
302 }
303 
304 /**
305  * Ensure that all set nodes from src are also set on dest.
306  * No real values are copied, the newly taken or allocated slots will be empty.
307  */
308 static void
_vtable_merge_empty(Eo_Vtable * dest,const Eo_Vtable * src,Eina_Bool * hitmap)309 _vtable_merge_empty(Eo_Vtable *dest, const Eo_Vtable *src, Eina_Bool *hitmap)
310 {
311    for (unsigned int i = 0; i < src->size; ++i)
312      {
313         if (src->chain[i].funcs && !dest->chain[i].funcs)
314           {
315              if (!src->chain[i].count)
316                {
317                   dest->chain[i].funcs = src->chain[i].funcs;
318                   dest->chain[i].count = src->chain[i].count;
319                }
320              else
321                {
322                   _vtable_prepare_empty_node(dest, src->chain[i].count, i);
323                   hitmap[i] = EINA_TRUE;
324                }
325           }
326      }
327 }
328 
329 static inline const op_type_funcs *
_vtable_func_get(const Eo_Vtable * vtable,Efl_Object_Op op)330 _vtable_func_get(const Eo_Vtable *vtable, Efl_Object_Op op)
331 {
332    unsigned short class_id = EFL_OBJECT_OP_CLASS_PART(op);
333    unsigned short func_id = EFL_OBJECT_OP_FUNC_PART(op);
334 
335    if (EINA_UNLIKELY(vtable->size <= class_id))
336      return NULL;
337    if (EINA_UNLIKELY(vtable->chain[class_id].count <= func_id))
338      return NULL;
339 
340    return &vtable->chain[class_id].funcs[func_id];
341 }
342 
343 static inline Eina_Bool
_vtable_func_set(Eo_Vtable * vtable,const _Efl_Class * klass,const _Efl_Class * hierarchy_klass,Efl_Object_Op op,Eo_Op_Func_Type func,Eina_Bool allow_same_override)344 _vtable_func_set(Eo_Vtable *vtable, const _Efl_Class *klass,
345                  const _Efl_Class *hierarchy_klass, Efl_Object_Op op,
346                  Eo_Op_Func_Type func, Eina_Bool allow_same_override)
347 {
348    op_type_funcs *fsrc;
349    unsigned short class_id = EFL_OBJECT_OP_CLASS_PART(op);
350    unsigned short func_id = EFL_OBJECT_OP_FUNC_PART(op);
351    Eo_Vtable_Node *hirachy_node = NULL;
352    Eo_Vtable_Node *node = NULL;
353 
354    EINA_SAFETY_ON_FALSE_RETURN_VAL(vtable->size >= class_id, EINA_FALSE);
355 
356    if (klass->parent && klass->parent->vtable.size > class_id)
357      hirachy_node = &klass->parent->vtable.chain[class_id];
358    if (hierarchy_klass)
359      hirachy_node = &hierarchy_klass->vtable.chain[class_id];
360    node = &vtable->chain[class_id];
361 
362    EINA_SAFETY_ON_NULL_RETURN_VAL(node->funcs, EINA_FALSE);
363    EINA_SAFETY_ON_FALSE_RETURN_VAL(node->count >= func_id, EINA_FALSE);
364    fsrc = &node->funcs[func_id];
365 
366    if (hierarchy_klass && !func)
367      {
368         if (!func)
369           {
370              op_type_funcs funcs = hirachy_node->funcs[func_id];
371              klass = funcs.src;
372              func = funcs.func;
373           }
374      }
375    else
376      {
377         if (!allow_same_override && (fsrc->src == klass))
378           {
379              const _Efl_Class *op_kls = _eo_op_class_get(op);
380              ERR("Class '%s': Overriding already set func %p for op %d (%s) with %p.",
381                  klass->desc->name, fsrc->func, op, op_kls->desc->name, func);
382              return EINA_FALSE;
383           }
384      }
385 
386    fsrc->src = klass;
387    fsrc->func = func;
388 
389    return EINA_TRUE;
390 }
391 
392 /* END OF DICH */
393 
394 #define _EO_ID_GET(Id) ((Eo_Id) (Id))
395 
396 
397 static inline Eina_Bool
_eo_is_a_obj(const Eo * eo_id)398 _eo_is_a_obj(const Eo *eo_id)
399 {
400    Eo_Id oid = (Eo_Id) _EO_ID_GET(eo_id);
401    return !!(oid & MASK_OBJ_TAG);
402 }
403 
404 static inline Eina_Bool
_eo_is_a_class(const Eo * eo_id)405 _eo_is_a_class(const Eo *eo_id)
406 {
407    Eo_Id oid = (Eo_Id) _EO_ID_GET(eo_id);
408    return !!(oid & MASK_CLASS_TAG);
409 }
410 
411 static inline _Efl_Class *
_eo_class_pointer_get(const Efl_Class * klass_id)412 _eo_class_pointer_get(const Efl_Class *klass_id)
413 {
414    return ID_CLASS_GET((Eo_Id)klass_id);
415 }
416 
417 static const char *
_eo_op_desc_name_get(const Efl_Op_Description * desc)418 _eo_op_desc_name_get(const Efl_Op_Description *desc)
419 {
420    static const char *fct_name = "unknown";
421 
422    if (!desc)
423      {
424         return fct_name;
425      }
426 
427 #ifndef _WIN32
428 # ifdef HAVE_DLADDR
429    static Dl_info info;
430    if (dladdr(desc->api_func, &info) != 0)
431       fct_name = info.dli_sname;
432 # endif
433 #else
434    fct_name = desc->api_func; /* Same on windows */
435 #endif
436    return fct_name;
437 }
438 
439 static inline const op_type_funcs *
_eo_kls_itr_next(const _Efl_Class * orig_kls,const _Efl_Class * cur_klass,Efl_Object_Op op,Eina_Bool super)440 _eo_kls_itr_next(const _Efl_Class *orig_kls, const _Efl_Class *cur_klass,
441                  Efl_Object_Op op, Eina_Bool super)
442 {
443    const _Efl_Class **kls_itr = NULL;
444 
445    /* Find the kls itr. */
446    kls_itr = orig_kls->mro;
447    while (*kls_itr && (*kls_itr != cur_klass))
448       kls_itr++;
449 
450    if (*kls_itr)
451      {
452         if (super) kls_itr++;
453         while (*kls_itr)
454           {
455              const op_type_funcs *fsrc = _vtable_func_get(&(*kls_itr)->vtable, op);
456              if (!fsrc || !fsrc->func)
457                {
458                   kls_itr++;
459                   continue;
460                }
461              return fsrc;
462           }
463      }
464 
465    return NULL;
466 }
467 
468 static inline void
_apply_auto_unref(_Eo_Object * obj,const Eo * eo_obj)469 _apply_auto_unref(_Eo_Object *obj, const Eo *eo_obj)
470 {
471    if (EINA_UNLIKELY(obj && obj->auto_unref))
472      {
473         if (obj->finalized && !(--obj->auto_unref))
474           efl_unref(eo_obj);
475      }
476 }
477 
478 /************************************ EO ************************************/
479 
480 static EFL_FUNC_TLS _Efl_Class *_super_klass = NULL;
481 
482 static Eo *
_efl_super_cast(const Eo * eo_id,const Efl_Class * cur_klass,Eina_Bool super)483 _efl_super_cast(const Eo *eo_id, const Efl_Class *cur_klass, Eina_Bool super)
484 {
485    EO_CLASS_POINTER_GOTO(cur_klass, super_klass, err);
486 
487 #ifdef EO_DEBUG
488    if (EINA_UNLIKELY(!_eo_is_a_obj(eo_id) && !_eo_is_a_class(eo_id))) goto err_obj;
489 #endif
490 
491    if (EINA_UNLIKELY(!_eo_is_a_obj(eo_id)))
492      goto do_klass;
493 
494 #ifndef EO_DEBUG
495    if (!super && EINA_UNLIKELY(!efl_isa(eo_id, cur_klass)))
496 #else
497    if (EINA_UNLIKELY(!efl_isa(eo_id, cur_klass)))
498 #endif
499      goto err_obj_hierarchy;
500 
501    EO_OBJ_POINTER_RETURN_VAL(eo_id, obj, NULL);
502    obj->cur_klass = super_klass;
503    obj->super = super;
504    EO_OBJ_DONE(eo_id);
505 
506    return (Eo *) eo_id;
507 
508 do_klass:
509    // efl_super(Class) is extremely rarely used, so TLS write is fine
510    EINA_SAFETY_ON_FALSE_RETURN_VAL(super, NULL);
511    _super_klass = super_klass;
512    return (Eo *) eo_id;
513 
514 err:
515    _EO_POINTER_ERR(cur_klass, "Class (%p) is an invalid ref.", cur_klass);
516    return NULL;
517 #ifdef EO_DEBUG
518 err_obj:
519    _EO_POINTER_ERR(eo_id, "Object (%p) is an invalid ref, class=%p (%s).", eo_id, cur_klass, efl_class_name_get(cur_klass));
520    return NULL;
521 #endif
522 err_obj_hierarchy:
523    _EO_POINTER_ERR(eo_id, "Object (%p) class=%p (%s) is not an instance of class=%p (%s).", eo_id, efl_class_get(eo_id), efl_class_name_get(eo_id), cur_klass, efl_class_name_get(cur_klass));
524    return NULL;
525 }
526 
527 EAPI Eo *
efl_super(const Eo * eo_id,const Efl_Class * cur_klass)528 efl_super(const Eo *eo_id, const Efl_Class *cur_klass)
529 {
530    return _efl_super_cast(eo_id, cur_klass, EINA_TRUE);
531 }
532 
533 EAPI Eo *
efl_cast(const Eo * eo_id,const Efl_Class * cur_klass)534 efl_cast(const Eo *eo_id, const Efl_Class *cur_klass)
535 {
536    return _efl_super_cast(eo_id, cur_klass, EINA_FALSE);
537 }
538 
539 EAPI Eina_Bool
_efl_object_call_resolve(Eo * eo_id,const char * func_name,Efl_Object_Op_Call_Data * call,Efl_Object_Op op,const char * file,int line)540 _efl_object_call_resolve(Eo *eo_id, const char *func_name, Efl_Object_Op_Call_Data *call, Efl_Object_Op op, const char *file, int line)
541 {
542    const _Efl_Class *klass, *main_klass;
543    const _Efl_Class *cur_klass = NULL;
544    _Eo_Object *obj = NULL;
545    const Eo_Vtable *vtable = NULL;
546    const op_type_funcs *func;
547    Eina_Bool super = EINA_TRUE;
548 
549    if (EINA_UNLIKELY(!eo_id)) goto on_null;
550 
551 
552    EO_OBJ_POINTER_RETURN_VAL_PROXY(eo_id, _obj, EINA_FALSE);
553 
554    obj = _obj;
555    klass = _obj->klass;
556    vtable = EO_VTABLE2(obj);
557    if (EINA_UNLIKELY(_obj->cur_klass != NULL))
558      {
559         // YES this is a goto with a label to return. this is a
560         // micro-optimization to move infrequent code out of the
561         // hot path of the function
562         goto obj_super;
563      }
564 obj_super_back:
565    _efl_ref(_obj);
566 
567    main_klass =  klass;
568 
569    /* If we have a current class, we need to itr to the next. */
570    if (cur_klass)
571      {
572         // YES this is a goto with a label to return. this is a
573         // micro-optimization to move infrequent code out of the
574         // hot path of the function
575         goto ok_cur_klass;
576      }
577    else
578      {
579         func = _vtable_func_get(vtable, op);
580         EINA_PREFETCH_NOCACHE(func);
581         // this is not very likely to happen - but may if its an invalid
582         // call or a composite object, but either way, it's not very likely
583         // so make it a goto to save on instruction cache
584         if (!func) goto end;
585      }
586 ok_cur_klass_back:
587 
588    if (EINA_LIKELY(func->func && func->src))
589      {
590         call->eo_id = eo_id;
591         call->obj = obj;
592         call->func = func->func;
593         call->data = _efl_data_scope_get(obj, func->src);
594 
595         return EINA_TRUE;
596      }
597 
598    // very unlikely in general to use a goto to move code out of l1 cache
599    // ie instruction cache
600    if (func->src != NULL) goto err_func_src;
601 
602 end:
603    /* Try composite objects */
604      {
605         Eina_List *itr;
606         Eo *emb_obj_id;
607         EINA_LIST_FOREACH(obj->opt->composite_objects, itr, emb_obj_id)
608           {
609              EO_OBJ_POINTER_PROXY(emb_obj_id, emb_obj);
610              if (EINA_UNLIKELY(!emb_obj)) continue;
611 
612              func = _vtable_func_get(&emb_obj->klass->vtable, op);
613              if (func == NULL) goto composite_continue;
614 
615              if (EINA_LIKELY(func->func && func->src))
616                {
617                   call->eo_id = _eo_obj_id_get(emb_obj);
618                   call->obj = _efl_ref(emb_obj);
619                   call->func = func->func;
620                   call->data = _efl_data_scope_get(emb_obj, func->src);
621                   /* We reffed it above, but no longer need/use it. */
622                   _efl_unref(obj);
623                   EO_OBJ_DONE(emb_obj_id);
624                   return EINA_TRUE;
625                }
626 composite_continue:
627              EO_OBJ_DONE(emb_obj_id);
628           }
629      }
630 
631    // all of this is error handling at the end so... rare-ish
632    // If it's a do_super call.
633    if (cur_klass)
634      {
635         ERR("in %s:%d: func '%s' (%d) could not be resolved on %s for class '%s' for super of '%s'.",
636             file, line, func_name, op, efl_debug_name_get(eo_id), main_klass->desc->name,
637             cur_klass->desc->name);
638         goto err;
639      }
640    else
641      {
642         /* we should not be able to take this branch */
643         ERR("in %s:%d: func '%s' (%d) could not be resolved on %s for class '%s'.",
644             file, line, func_name, op, efl_debug_name_get(eo_id), main_klass->desc->name);
645         goto err;
646      }
647 
648 err_func_src:
649    ERR("in %s:%d: you called a pure virtual func '%s' (%d) of class '%s'.",
650        file, line, func_name, op, klass->desc->name);
651 err:
652    _apply_auto_unref(obj, eo_id);
653    _efl_unref(obj);
654    _eo_obj_pointer_done((Eo_Id)eo_id);
655 
656    return EINA_FALSE;
657 
658    // yes - special "move out of hot path" code blobs with goto's for
659    // speed reasons to have intr prefetches work better and miss less
660 ok_cur_klass:
661    func = _eo_kls_itr_next(klass, cur_klass, op, super);
662    if (!func) goto end;
663    klass = func->src;
664    goto ok_cur_klass_back;
665 
666 obj_super:
667    {
668       cur_klass = obj->cur_klass;
669       super = obj->super;
670       obj->cur_klass = NULL;
671 
672       if (_obj_is_override(obj) && cur_klass && super &&
673           (_eo_class_id_get(cur_klass) == EFL_OBJECT_OVERRIDE_CLASS))
674         {
675            /* Doing a efl_super(obj, EFL_OBJECT_OVERRIDE_CLASS) should
676             * result in calling as if it's a normal class. */
677            vtable = &klass->vtable;
678            cur_klass = NULL;
679         }
680 
681    }
682    goto obj_super_back;
683 
684 on_null:
685    if (EINA_UNLIKELY(efl_del_api_generation != _efl_object_init_generation))
686      {
687         _efl_del_api_op_id = _efl_object_api_op_id_get_internal(EFL_FUNC_COMMON_OP_FUNC(efl_del));
688         efl_del_api_generation = _efl_object_init_generation;
689      }
690    if (op != _efl_del_api_op_id)
691      WRN("NULL passed to function %s().", func_name);
692    return EINA_FALSE;
693 }
694 
695 EAPI void
_efl_object_call_end(Efl_Object_Op_Call_Data * call)696 _efl_object_call_end(Efl_Object_Op_Call_Data *call)
697 {
698    if (EINA_LIKELY(!!call->obj))
699      {
700         _apply_auto_unref(call->obj, call->eo_id);
701         _efl_unref(call->obj);
702         _eo_obj_pointer_done((Eo_Id)call->eo_id);
703      }
704 }
705 
706 static inline Eina_Bool
_eo_api_func_equal(const void * api_func1,const void * api_func2)707 _eo_api_func_equal(const void *api_func1, const void *api_func2)
708 {
709 #ifndef _WIN32
710    return (api_func1 == api_func2);
711 #else
712    /* On Windows, DLL API's will be exported using the dllexport flag.
713     * When used by another library or executable, they will be declared
714     * using the dllimport flag. What happens really is that two symbols are
715     * created, at two different addresses. So it's impossible to match
716     * them. We fallback to plain string comparison based on the
717     * function name itself. Slow, but this should rarely happen.
718     */
719    return (api_func2 && api_func1 && !strcmp(api_func2, api_func1));
720 #endif
721 }
722 
723 static inline Efl_Object_Op
_efl_object_api_op_id_get_internal(const void * api_func)724 _efl_object_api_op_id_get_internal(const void *api_func)
725 {
726    eina_spinlock_take(&_ops_storage_lock);
727 #ifndef _WIN32
728    Efl_Object_Op op = (uintptr_t) eina_hash_find(_ops_storage, &api_func);
729 #else
730    Efl_Object_Op op = (uintptr_t) eina_hash_find(_ops_storage, api_func);
731 #endif
732    eina_spinlock_release(&_ops_storage_lock);
733 
734    return op;
735 }
736 
737 /* LEGACY, should be removed before next release */
738 EAPI Efl_Object_Op
_efl_object_api_op_id_get(const void * api_func)739 _efl_object_api_op_id_get(const void *api_func)
740 {
741    Efl_Object_Op op = _efl_object_api_op_id_get_internal(api_func);
742 
743    if (op == EFL_NOOP)
744      {
745         ERR("Unable to resolve op for api func %p", api_func);
746      }
747 
748    return op;
749 }
750 
751 EAPI Efl_Object_Op
_efl_object_op_api_id_get(const void * api_func,const Eo * eo_obj,const char * api_func_name,const char * file,int line)752 _efl_object_op_api_id_get(const void *api_func, const Eo *eo_obj, const char *api_func_name, const char *file, int line)
753 {
754    Efl_Object_Op op;
755 
756 #ifndef EO_DEBUG
757    if (!eo_obj) return EFL_NOOP;
758 #endif
759    op = _efl_object_api_op_id_get_internal(api_func);
760    if (op == EFL_NOOP)
761      {
762         EO_OBJ_POINTER(eo_obj, obj);
763         eina_log_print(_eo_log_dom, EINA_LOG_LEVEL_ERR,
764                        file, api_func_name, line,
765                        "Unable to resolve op for api func %p for obj=%p (%s)",
766                        api_func, eo_obj, efl_class_name_get(eo_obj));
767         _apply_auto_unref(obj, eo_obj);
768         return EFL_NOOP;
769      }
770 
771    return op;
772 }
773 
774 /* klass is the klass we are working on. hierarchy_klass is the class whe should
775  * use when validating. */
776 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)777 _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)
778 {
779    unsigned int i, j;
780    unsigned int number_of_new_functions = 0;
781    const Efl_Op_Description *op_desc;
782    const Efl_Op_Description *op_descs;
783    const _Efl_Class *override_class;
784    const void **api_funcs;
785    Eina_Bool check_equal;
786 
787    op_descs = ops->descs;
788    override_class = override_only ? hierarchy_klass : NULL;
789 
790    DBG("Set functions for class '%s':%p", klass->desc->name, klass);
791 
792    if (!override_only)
793      _vtable_insert_empty_funcs(vtable, class_id);
794    if (!op_descs || !ops->count)
795      return EINA_TRUE;
796 
797 #ifdef EO_DEBUG
798    check_equal = EINA_TRUE;
799 #else
800    check_equal = !override_only;
801 #endif
802    api_funcs = alloca(ops->count * sizeof(api_funcs[0]));
803 
804    /* sanity checks */
805    for (i = 0, op_desc = op_descs; i < ops->count; i++, op_desc++)
806      {
807         if (op_desc->api_func == NULL)
808           {
809              ERR("Class '%s': NULL API not allowed (NULL->%p '%s').",
810                  klass->desc->name, op_desc->func, _eo_op_desc_name_get(op_desc));
811              return EINA_FALSE;
812           }
813 
814         if (check_equal)
815           {
816              for (j = 0; j < i; j++)
817                {
818                   if (_eo_api_func_equal(op_desc->api_func, api_funcs[j]))
819                     {
820                        ERR("Class '%s': API previously defined (%p->%p '%s').",
821                            klass->desc->name, op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
822                        return EINA_FALSE;
823                     }
824                }
825 
826              api_funcs[i] = op_desc->api_func;
827           }
828         if (_efl_object_api_op_id_get_internal(op_desc->api_func) == EFL_NOOP)
829           {
830              number_of_new_functions ++;
831           }
832      }
833 
834    if (!override_only)
835      {
836         if (number_of_new_functions)
837           {
838              //Before setting any real functions, allocate the node that will contain all the functions
839              _vtable_prepare_empty_node(vtable, number_of_new_functions, class_id);
840           }
841         hitmap[class_id] = EINA_TRUE;
842      }
843 
844    for (i = 0, j = 0, op_desc = op_descs; i < ops->count; i++, op_desc++)
845      {
846         Efl_Object_Op op2 = EFL_NOOP;
847         short op2_class_id;
848 
849         /* Get the opid for the function. */
850         op2 = _efl_object_api_op_id_get_internal(op_desc->api_func);
851 
852         if (op2 == EFL_NOOP)
853           {
854              //functions that do not have a op yet, are considered to be belonging to this class
855              if (override_only)
856                {
857                   ERR("Class '%s': Tried overriding a previously undefined function.", klass->desc->name);
858                   return EINA_FALSE;
859                }
860 
861              op2 = EFL_OBJECT_OP_CREATE_OP_ID(class_id, j);
862              eina_spinlock_take(&_ops_storage_lock);
863 #ifndef _WIN32
864              eina_hash_add(_ops_storage, &op_desc->api_func, (void *) (uintptr_t) op2);
865 #else
866              eina_hash_add(_ops_storage, op_desc->api_func, (void *) (uintptr_t) op2);
867 #endif
868              eina_spinlock_release(&_ops_storage_lock);
869              j ++;
870           }
871 
872 #ifdef EO_DEBUG
873         DBG("%p->%p '%s'", op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
874 #endif
875         op2_class_id = EFL_OBJECT_OP_CLASS_PART(op2);
876          //in case we are having a function overwrite for a specific type, copy the relevant vtable
877         if (!hitmap[op2_class_id])
878           {
879              const Eo_Vtable_Node node = vtable->chain[op2_class_id];
880              _vtable_copy_node(&vtable->chain[op2_class_id], &node);
881              hitmap[op2_class_id] = EINA_TRUE;
882           }
883         if (!_vtable_func_set(vtable, klass, override_class, op2, op_desc->func, EINA_TRUE))
884           return EINA_FALSE;
885      }
886    return EINA_TRUE;
887 }
888 
889 EAPI Eina_Bool
efl_class_functions_set(const Efl_Class * klass_id,const Efl_Object_Ops * object_ops,const Efl_Object_Property_Reflection_Ops * reflection_table)890 efl_class_functions_set(const Efl_Class *klass_id, const Efl_Object_Ops *object_ops, const Efl_Object_Property_Reflection_Ops *reflection_table)
891 {
892    EO_CLASS_POINTER_GOTO(klass_id, klass, err_klass);
893    Efl_Object_Ops empty_ops = { 0 };
894    Eina_Bool *hitmap;
895 
896    // not likely so use goto to alleviate l1 instruction cache of rare code
897    if (klass->functions_set) goto err_funcs;
898    klass->functions_set = EINA_TRUE;
899 
900    if (!object_ops) object_ops = &empty_ops;
901 
902    klass->reflection = reflection_table;
903 
904    klass->ops_count = object_ops->count;
905 
906    klass->class_id = _UNMASK_ID(klass->header.id) - 1;
907 
908    _vtable_init(&klass->vtable);
909    if (!klass->vtable.chain) goto err_vtable;
910 
911    hitmap = alloca(klass->vtable.size);
912    memset(hitmap, 0, klass->vtable.size);
913    /* Merge in all required vtable entries */
914      {
915         const _Efl_Class **mro_itr = klass->mro;
916         /* take over everything from the parent */
917         if (klass->parent)
918           {
919              _vtable_take_over(&klass->vtable, &klass->parent->vtable);
920           }
921         /*
922          * - jump to the mro entry containing the parent
923          * - everything further from the parent to the next elements is already
924          *   represented in the vtable of the parent.
925          */
926         for (  ; *mro_itr ; mro_itr++)
927           {
928              if (*mro_itr == klass->parent)
929                break;
930           }
931         /**
932          * merge in all the APIs that are extended in the current klass for this first time.
933          * That means, they are not extended anywhere from the parent further up.
934          */
935         for ( mro_itr-- ; mro_itr > klass->mro ; mro_itr--)
936           {
937              _vtable_merge_defined_api(&klass->vtable, &(*mro_itr)->vtable, hitmap);
938           }
939         /*
940          * add slots for the interfaces and mixins we are inheriting from
941          */
942         for (int i = 0; klass->extensions[i]; i++)
943           {
944              const _Efl_Class *ext = klass->extensions[i];
945              /*for all extensions of the class, ensure that *at least* empty vtables are available, so the efl_isa calls do succeed*/
946              _vtable_merge_empty(&klass->vtable, &ext->vtable, hitmap);
947           }
948      }
949      {
950         unsigned int i;
951 
952         for (i = 0; i < object_ops->count; i++)
953           {
954              Efl_Object_Op op = _efl_object_api_op_id_get_internal(object_ops->descs[i].api_func);
955              if (op == EFL_NOOP) continue; //EFL_NOOP means that this function is not yet defined, this will be handled later
956              short class_id = EFL_OBJECT_OP_CLASS_PART(op);
957              if (klass->vtable.chain[class_id].count == 0)
958                {
959                   const _Efl_Class *required_klass = _eo_classes[class_id];
960                   /* in case this type is not already inherited, error on everything that is not a mixin */
961                   if (klass->desc->type == EFL_CLASS_TYPE_MIXIN)
962                     {
963                        /* this is when a mixin implemets a regular api, we just prepare a empty node, the rest will be implemented later */
964                        _vtable_prepare_empty_node(&klass->vtable, required_klass->vtable.chain[class_id].count, class_id);
965                        hitmap[class_id] = EINA_TRUE;
966                     }
967                   else
968                     {
969                        ERR("There is an API implemented, whoms type is not part of this class. %s vs. %s", klass->desc->name, required_klass->desc->name);
970                        _vtable_prepare_empty_node(&klass->vtable, required_klass->vtable.chain[class_id].count, class_id);
971                        hitmap[class_id] = EINA_TRUE;
972                     }
973 
974                }
975           }
976      }
977    return _eo_class_funcs_set(&klass->vtable, object_ops, klass, klass, EINA_FALSE, klass->class_id, hitmap);
978 err_funcs:
979    ERR("Class %s already had its functions set..", klass->desc->name);
980    return EINA_FALSE;
981 err_klass:
982    _EO_POINTER_ERR(klass_id, "Class (%p) is an invalid ref.", klass_id);
983    return EINA_FALSE;
984 err_vtable:
985    ERR("failed to allocate vtable for class '%s'", klass->desc->name);
986    return EINA_FALSE;
987 }
988 
989 static Eo *
_efl_add_internal_start_do(const char * file,int line,const Efl_Class * klass_id,Eo * parent_id,Eina_Bool ref,Eina_Bool is_fallback,Efl_Substitute_Ctor_Cb substitute_ctor,void * sub_ctor_data)990 _efl_add_internal_start_do(const char *file, int line, const Efl_Class *klass_id, Eo *parent_id, Eina_Bool ref, Eina_Bool is_fallback, Efl_Substitute_Ctor_Cb substitute_ctor, void *sub_ctor_data)
991 {
992    const char *func_name = __func__;
993    _Eo_Object *obj;
994    Eo_Stack_Frame *fptr = NULL;
995 
996    if (is_fallback) fptr = _efl_add_fallback_stack_push(NULL);
997 
998    if (class_overrides)
999      {
1000         const Efl_Class *override = eina_hash_find(class_overrides, &klass_id);
1001         if (override) klass_id = override;
1002      }
1003 
1004    EO_CLASS_POINTER_GOTO_PROXY(klass_id, klass, err_klass);
1005 
1006    // Check that in the case of efl_add we do pass a parent.
1007    if (!ref && !parent_id)
1008      ERR("Creation of '%s' object at line %i in '%s' is done without parent. This should use efl_add_ref.",
1009          klass->desc->name, line, file);
1010 
1011    if (parent_id)
1012      {
1013         EO_OBJ_POINTER_GOTO_PROXY(parent_id, parent, err_parent);
1014      }
1015 
1016    // not likely so use goto to alleviate l1 instruction cache of rare code
1017    if (EINA_UNLIKELY(klass->desc->type != EFL_CLASS_TYPE_REGULAR))
1018      goto err_noreg;
1019 
1020    eina_spinlock_take(&klass->objects.trash_lock);
1021    obj = eina_trash_pop(&klass->objects.trash);
1022    if (obj)
1023      {
1024         memset(obj, 0, klass->obj_size);
1025         klass->objects.trash_count--;
1026      }
1027    else
1028      {
1029         obj = calloc(1, klass->obj_size);
1030      }
1031    eina_spinlock_release(&klass->objects.trash_lock);
1032 
1033    obj->opt = eina_cow_alloc(efl_object_optional_cow);
1034    _efl_ref(obj);
1035    obj->klass = klass;
1036 
1037    obj->header.id = _eo_id_allocate(obj, parent_id);
1038    Eo *eo_id = _eo_obj_id_get(obj);
1039 
1040    _eo_log_obj_ref_op(obj, EO_REF_OP_NEW);
1041 
1042    _eo_condtor_reset(obj);
1043 
1044    efl_ref(eo_id);
1045 
1046    /* Reference for the parent if is_ref is done in _efl_add_end */
1047    if (parent_id) efl_parent_set(eo_id, parent_id);
1048 
1049    /* eo_id can change here. Freeing is done on the resolved object. */
1050    if (!substitute_ctor) eo_id = efl_constructor(eo_id);
1051    else eo_id = substitute_ctor(sub_ctor_data, eo_id);
1052    // not likely so use goto to alleviate l1 instruction cache of rare code
1053    if (!eo_id) goto err_noid;
1054    // not likely so use goto to alleviate l1 instruction cache of rare code
1055    else if (eo_id != _eo_obj_id_get(obj)) goto ok_nomatch;
1056 ok_nomatch_back:
1057    if (is_fallback) fptr->obj = eo_id;
1058    if (parent_id) EO_OBJ_DONE(parent_id);
1059    return eo_id;
1060 
1061 ok_nomatch:
1062      {
1063         EO_OBJ_POINTER_GOTO_PROXY(eo_id, new_obj, err_newid);
1064         _efl_ref(new_obj);
1065         efl_ref(eo_id);
1066         /* We might have two refs on the old object at this point. */
1067         efl_parent_set((Eo *) obj->header.id, NULL);
1068         efl_unref(_eo_obj_id_get(obj));
1069         _efl_unref(obj);
1070         EO_OBJ_DONE(eo_id);
1071      }
1072    goto ok_nomatch_back;
1073 
1074 err_noid:
1075    ERR("in %s:%d: Object of class '%s' - Error while constructing object",
1076        file, line, klass->desc->name);
1077    /* We might have two refs at this point. */
1078    efl_parent_set((Eo *) obj->header.id, NULL);
1079    efl_unref(_eo_obj_id_get(obj));
1080    _efl_unref(obj);
1081 err_newid:
1082    if (parent_id) EO_OBJ_DONE(parent_id);
1083    return NULL;
1084 err_noreg:
1085    ERR("in %s:%d: Class '%s' is not instantiate-able. Aborting.", file, line, klass->desc->name);
1086    if (parent_id) EO_OBJ_DONE(parent_id);
1087    return NULL;
1088 
1089 err_klass:
1090    _EO_POINTER_ERR(klass_id, "in %s:%d: Class (%p) is an invalid ref.", file, line, klass_id);
1091 err_parent:
1092    return NULL;
1093 }
1094 
1095 EAPI Eo *
_efl_add_internal_start(const char * file,int line,const Efl_Class * klass_id,Eo * parent_id,Eina_Bool ref,Eina_Bool is_fallback)1096 _efl_add_internal_start(const char *file, int line, const Efl_Class *klass_id, Eo *parent_id, Eina_Bool ref, Eina_Bool is_fallback)
1097 {
1098    return _efl_add_internal_start_do(file, line, klass_id, parent_id, ref, is_fallback, NULL, NULL);
1099 }
1100 
_efl_add_internal_start_bindings(const char * file,int line,const Efl_Class * klass_id,Eo * parent_id,Eina_Bool ref,Eina_Bool is_fallback,Efl_Substitute_Ctor_Cb substitute_ctor,void * sub_ctor_data)1101 EAPI Eo * _efl_add_internal_start_bindings(const char *file, int line, const Efl_Class *klass_id, Eo *parent_id, Eina_Bool ref, Eina_Bool is_fallback, Efl_Substitute_Ctor_Cb substitute_ctor, void *sub_ctor_data)
1102 {
1103    return _efl_add_internal_start_do(file, line, klass_id, parent_id, ref, is_fallback, substitute_ctor, sub_ctor_data);
1104 }
1105 
1106 static Eo *
_efl_add_internal_end(Eo * eo_id,Eo * finalized_id)1107 _efl_add_internal_end(Eo *eo_id, Eo *finalized_id)
1108 {
1109    EO_OBJ_POINTER_RETURN_VAL(eo_id, obj, NULL);
1110 
1111    // rare so move error handling to end to save l1 instruction cache
1112    if (!obj->condtor_done) goto err_condtor;
1113    if (!finalized_id)
1114      {
1115         // XXX: Given EFL usage of objects, construction is a perfectly valid thing
1116         // to do. we shouldn't complain about it as handling a NULL obj creation is
1117         // the job of the caller. a perfect example here is ecore_con and ecore_ipc
1118         // where you create a con or ipc obj then set up type/destination/port and
1119         // the finalize of the constructor does the actual connect and thus this
1120         // fails or succeeds based on if service is there.
1121         //
1122         // until there is a better solution - don't complain here.
1123         //
1124         //             ERR("Object of class '%s' - Finalizing the object failed.",
1125         //                   klass->desc->name);
1126         goto cleanup;
1127      }
1128 
1129    obj->finalized = EINA_TRUE;
1130    _efl_unref(obj);
1131    EO_OBJ_DONE(eo_id);
1132    return (Eo *)eo_id;
1133 
1134 err_condtor:
1135      {
1136         const _Efl_Class *klass = obj->klass;
1137         ERR("Object of class '%s' - Not all of the object constructors have been executed.",
1138               klass->desc->name);
1139      }
1140 cleanup:
1141    efl_parent_set((Eo *) obj->header.id, NULL);
1142    efl_unref((Eo *) obj->header.id);
1143    _efl_unref(obj);
1144    EO_OBJ_DONE(eo_id);
1145    return NULL;
1146 }
1147 
1148 EAPI Eo *
_efl_add_end(Eo * eo_id,Eina_Bool is_ref,Eina_Bool is_fallback)1149 _efl_add_end(Eo *eo_id, Eina_Bool is_ref, Eina_Bool is_fallback)
1150 {
1151    if (!eo_id) return NULL;
1152    Eo *ret = efl_finalize(eo_id);
1153    ret = _efl_add_internal_end(eo_id, ret);
1154 
1155    if (ret && !is_ref)
1156      {
1157         efl_unref(ret);
1158      }
1159 
1160    if (is_fallback)
1161      {
1162         _efl_add_fallback_stack_pop();
1163      }
1164 
1165    return ret;
1166 }
1167 
1168 EAPI void
efl_reuse(const Eo * eo_id)1169 efl_reuse(const Eo *eo_id)
1170 {
1171    Eo *obj = (Eo *) eo_id;
1172    EO_OBJ_POINTER_RETURN(obj, _obj);
1173 
1174    efl_object_override(obj, NULL);
1175    _efl_object_reuse(_obj);
1176 
1177 #ifdef EO_DEBUG
1178    _eo_log_obj_ref_op(_obj, EO_REF_OP_REUSE);
1179 #endif
1180 
1181    EO_OBJ_DONE(eo_id);
1182 }
1183 
1184 void
_eo_free(_Eo_Object * obj,Eina_Bool manual_free EINA_UNUSED)1185 _eo_free(_Eo_Object *obj, Eina_Bool manual_free EINA_UNUSED)
1186 {
1187    _Efl_Class *klass = (_Efl_Class*) obj->klass;
1188 
1189    _eo_log_obj_ref_op(obj, EO_REF_OP_FREE);
1190 
1191 #ifdef EO_DEBUG
1192    if (manual_free)
1193      {
1194         Eo *obj_id = _eo_obj_id_get(obj);
1195         if (obj->datarefcount)
1196           {
1197              ERR("Object %p data still referenced %d time(s).", obj_id, obj->datarefcount);
1198           }
1199         while (obj->xrefs)
1200           {
1201              Eina_Inlist *nitr = obj->xrefs->next;
1202              Eo_Xref_Node *xref = EINA_INLIST_CONTAINER_GET(obj->data_xrefs, Eo_Xref_Node);
1203              ERR("Object %p is still referenced by object %p. Origin: %s:%d",
1204                  obj_id, xref->ref_obj, xref->file, xref->line);
1205              eina_freeq_ptr_main_add(xref, free, sizeof(*xref));
1206              obj->xrefs = nitr;
1207           }
1208         while (obj->data_xrefs)
1209           {
1210              Eina_Inlist *nitr = obj->data_xrefs->next;
1211              Eo_Xref_Node *xref = EINA_INLIST_CONTAINER_GET(obj->data_xrefs, Eo_Xref_Node);
1212              if (obj_id == xref->ref_obj)
1213                {
1214                   WRN("Object %p still has a reference to its own data (subclass: %s). Origin: %s:%d",
1215                       obj_id, xref->data_klass, xref->file, xref->line);
1216                }
1217              else
1218                {
1219                   ERR("Data of object %p (subclass: %s) is still referenced by object %p. Origin: %s:%d",
1220                       obj_id, xref->data_klass, xref->ref_obj, xref->file, xref->line);
1221                }
1222 
1223              eina_freeq_ptr_main_add(xref, free, sizeof(*xref));
1224              obj->data_xrefs = nitr;
1225           }
1226      }
1227 #endif
1228    if (obj->opt && _obj_is_override(obj))
1229      {
1230         _vtable_free(obj->opt->vtable, &obj->klass->vtable);
1231         EO_OPTIONAL_COW_SET(obj, vtable, NULL);
1232      }
1233 
1234    _eo_id_release((Eo_Id) _eo_obj_id_get(obj));
1235    eina_cow_free(efl_object_optional_cow, (Eina_Cow_Data *) &obj->opt);
1236 
1237    eina_spinlock_take(&klass->objects.trash_lock);
1238    if ((klass->objects.trash_count <= 8) && (EINA_LIKELY(!_eo_trash_bypass)))
1239      {
1240         eina_trash_push(&klass->objects.trash, obj);
1241         klass->objects.trash_count++;
1242      }
1243    else
1244      {
1245         eina_freeq_ptr_main_add(obj, free, klass->obj_size);
1246      }
1247    eina_spinlock_release(&klass->objects.trash_lock);
1248 }
1249 /*****************************************************************************/
1250 
1251 EAPI const Efl_Class *
efl_class_get(const Eo * eo_id)1252 efl_class_get(const Eo *eo_id)
1253 {
1254    const Efl_Class *klass;
1255 
1256    if (_eo_is_a_class(eo_id))
1257      {
1258         EO_CLASS_POINTER_GOTO(eo_id, _klass, err_klass);
1259         return EFL_CLASS_CLASS;
1260      }
1261 
1262    EO_OBJ_POINTER_GOTO(eo_id, obj, err_obj);
1263    klass = _eo_class_id_get(obj->klass);
1264    EO_OBJ_DONE(eo_id);
1265    return klass;
1266 
1267 err_klass:
1268    _EO_POINTER_ERR(eo_id, "Class (%p) is an invalid ref.", eo_id);
1269 err_obj:
1270    return NULL;
1271 }
1272 
1273 EAPI const char *
efl_class_name_get(const Efl_Class * eo_id)1274 efl_class_name_get(const Efl_Class *eo_id)
1275 {
1276    const _Efl_Class *klass;
1277 
1278    if (_eo_is_a_class(eo_id))
1279      {
1280         EO_CLASS_POINTER_GOTO(eo_id, _klass, err_klass);
1281         klass = _klass;
1282      }
1283    else
1284      {
1285         EO_OBJ_POINTER_GOTO(eo_id, obj, err_obj);
1286         klass = obj->klass;
1287         EO_OBJ_DONE(eo_id);
1288      }
1289    return klass->desc->name;
1290 
1291 err_klass:
1292    _EO_POINTER_ERR(eo_id, "Class (%p) is an invalid ref.", eo_id);
1293 err_obj:
1294    return NULL;
1295 }
1296 
1297 EAPI size_t
efl_class_memory_size_get(const Efl_Class * eo_id)1298 efl_class_memory_size_get(const Efl_Class *eo_id)
1299 {
1300    const _Efl_Class *klass;
1301 
1302    if (_eo_is_a_class(eo_id))
1303      {
1304         EO_CLASS_POINTER_GOTO(eo_id, _klass, err_klass);
1305         klass = _klass;
1306      }
1307    else
1308      {
1309         EO_OBJ_POINTER_GOTO(eo_id, obj, err_obj);
1310         klass = obj->klass;
1311         EO_OBJ_DONE(eo_id);
1312      }
1313    return klass->obj_size;
1314 
1315 err_klass:
1316    _EO_POINTER_ERR(eo_id, "Class (%p) is an invalid ref.", eo_id);
1317 err_obj:
1318    return 0;
1319 }
1320 
1321 static Eina_Bool
_eo_class_mro_has(const _Efl_Class * klass,const _Efl_Class * find)1322 _eo_class_mro_has(const _Efl_Class *klass, const _Efl_Class *find)
1323 {
1324    const _Efl_Class **itr;
1325    for (itr = klass->mro ; *itr ; itr++)
1326      {
1327         if (*itr == find)
1328           {
1329              return EINA_TRUE;
1330           }
1331      }
1332    return EINA_FALSE;
1333 }
1334 
1335 static Eina_List *
_eo_class_list_remove_duplicates(Eina_List * list)1336 _eo_class_list_remove_duplicates(Eina_List* list)
1337 {
1338    Eina_List *itr1, *itr2, *itr2n;
1339 
1340    itr1 = eina_list_last(list);
1341    while (itr1)
1342      {
1343         itr2 = eina_list_prev(itr1);
1344 
1345         while (itr2)
1346           {
1347              itr2n = eina_list_prev(itr2);
1348 
1349              if (eina_list_data_get(itr1) == eina_list_data_get(itr2))
1350                {
1351                   list = eina_list_remove_list(list, itr2);
1352                }
1353 
1354              itr2 = itr2n;
1355           }
1356 
1357         itr1 = eina_list_prev(itr1);
1358      }
1359 
1360    return list;
1361 }
1362 
1363 static Eina_List *
_eo_class_mro_add(Eina_List * mro,const _Efl_Class * klass)1364 _eo_class_mro_add(Eina_List *mro, const _Efl_Class *klass)
1365 {
1366    if (!klass)
1367      return mro;
1368 
1369    mro = eina_list_append(mro, klass);
1370 
1371    /* Recursively add MIXINS extensions. */
1372      {
1373         const _Efl_Class **extn_itr;
1374 
1375         for (extn_itr = klass->extensions ; *extn_itr ; extn_itr++)
1376           {
1377              const _Efl_Class *extn = *extn_itr;
1378              if (extn->desc->type == EFL_CLASS_TYPE_MIXIN)
1379                mro = _eo_class_mro_add(mro, extn);
1380           }
1381      }
1382 
1383    mro = _eo_class_mro_add(mro, klass->parent);
1384 
1385    return mro;
1386 }
1387 
1388 static Eina_List *
_eo_class_mro_init(const Efl_Class_Description * desc,const _Efl_Class * parent,Eina_List * extensions)1389 _eo_class_mro_init(const Efl_Class_Description *desc, const _Efl_Class *parent, Eina_List *extensions)
1390 {
1391    Eina_List *mro = NULL;
1392    Eina_List *extn_itr = NULL;
1393    Eina_List *extn_pos = NULL;
1394    const _Efl_Class *extn = NULL;
1395 
1396    /* Add MIXINS extensions. */
1397    EINA_LIST_FOREACH(extensions, extn_itr, extn)
1398      {
1399         if (extn->desc->type != EFL_CLASS_TYPE_MIXIN)
1400           continue;
1401 
1402         mro = _eo_class_mro_add(mro, extn);
1403         extn_pos = eina_list_append(extn_pos, eina_list_last(mro));
1404      }
1405 
1406    /* Check if we can create a consistent mro */
1407      {
1408         Eina_List *itr = extn_pos;
1409         EINA_LIST_FOREACH(extensions, extn_itr, extn)
1410           {
1411              if (extn->desc->type != EFL_CLASS_TYPE_MIXIN)
1412                 continue;
1413 
1414              /* Get the first one after the extension. */
1415              Eina_List *extn_list = eina_list_next(eina_list_data_get(itr));
1416 
1417              /* If we found the extension again. */
1418              if (eina_list_data_find_list(extn_list, extn))
1419                {
1420                   eina_list_free(mro);
1421                   eina_list_free(extn_pos);
1422                   ERR("Cannot create a consistent method resolution order for class '%s' because of '%s'.", desc->name, extn->desc->name);
1423                   return NULL;
1424                }
1425 
1426              itr = eina_list_next(itr);
1427           }
1428      }
1429 
1430    eina_list_free(extn_pos);
1431 
1432    mro = _eo_class_mro_add(mro, parent);
1433    mro = _eo_class_list_remove_duplicates(mro);
1434    /* Will be replaced with the actual class pointer */
1435    mro = eina_list_prepend(mro, NULL);
1436 
1437    return mro;
1438 }
1439 
1440 static Eina_Bool
_eo_class_initializer(_Efl_Class * klass)1441 _eo_class_initializer(_Efl_Class *klass)
1442 {
1443    if (klass->desc->class_initializer)
1444      return klass->desc->class_initializer(_eo_class_id_get(klass));
1445 
1446    return EINA_TRUE;
1447 }
1448 
1449 static void
_eo_class_constructor(_Efl_Class * klass)1450 _eo_class_constructor(_Efl_Class *klass)
1451 {
1452    klass->constructed = EINA_TRUE;
1453 
1454    klass->construction_thread = eina_thread_self();
1455 
1456    if (klass->desc->class_constructor)
1457      klass->desc->class_constructor(_eo_class_id_get(klass));
1458 }
1459 
1460 static void
eo_class_free(_Efl_Class * klass)1461 eo_class_free(_Efl_Class *klass)
1462 {
1463    void *data;
1464    Eina_Thread self = eina_thread_self();
1465 
1466    if ((self != _efl_object_main_thread) &&
1467        (self != klass->construction_thread))
1468      CRI("Calling class deconstructor from thread that did not call constructor and is not main thread!\n"
1469          "This will probably crash!");
1470 
1471    if (klass->constructed)
1472      {
1473         if (klass->desc->class_destructor)
1474            klass->desc->class_destructor(_eo_class_id_get(klass));
1475         _vtable_mro_free(klass);
1476         _vtable_free(&klass->vtable, NULL);
1477      }
1478 
1479    EINA_TRASH_CLEAN(&klass->objects.trash, data)
1480       eina_freeq_ptr_main_add(data, free, klass->obj_size);
1481 
1482    EINA_TRASH_CLEAN(&klass->iterators.trash, data)
1483       eina_freeq_ptr_main_add(data, free, 0);
1484 
1485    eina_spinlock_free(&klass->objects.trash_lock);
1486    eina_spinlock_free(&klass->iterators.trash_lock);
1487 
1488    eina_freeq_ptr_main_add(klass, free, 0);
1489 }
1490 
1491 static inline void
_eo_classes_release(void)1492 _eo_classes_release(void)
1493 {
1494 #ifdef HAVE_MMAP
1495 # ifdef HAVE_VALGRIND
1496    if (RUNNING_ON_VALGRIND) free(_eo_classes);
1497    else
1498 # endif
1499      {
1500         size_t size;
1501 
1502         size = _eo_classes_alloc * sizeof(_Efl_Class *);
1503         if (_eo_classes) munmap(_eo_classes, size);
1504      }
1505 #else
1506    free(_eo_classes);
1507 #endif
1508    _eo_classes = NULL;
1509    _eo_classes_last_id = 0;
1510    _eo_classes_alloc = 0;
1511 }
1512 
1513 static inline void
_eo_classes_expand(void)1514 _eo_classes_expand(void)
1515 {
1516    unsigned char *ptr;
1517    size_t newsize, psize;
1518 
1519    _eo_classes_last_id++;
1520    if (_eo_classes_last_id <= _eo_classes_alloc) return;
1521    psize = _eo_classes_alloc * sizeof(_Efl_Class *);
1522 #ifdef HAVE_MMAP
1523 # ifdef HAVE_VALGRIND
1524    if (RUNNING_ON_VALGRIND)
1525      {
1526         _eo_classes_alloc += 128;
1527         newsize = _eo_classes_alloc * sizeof(_Efl_Class *);
1528         ptr = realloc(_eo_classes, newsize);
1529         if (!ptr)
1530           {
1531              ERR("realloc of eo class table region faile!!");
1532              abort();
1533           }
1534      }
1535    else
1536 # endif
1537      {
1538         _eo_classes_alloc += (MEM_PAGE_SIZE / sizeof(_Efl_Class *));
1539         newsize = _eo_classes_alloc * sizeof(_Efl_Class *);
1540         ptr = mmap(NULL, newsize, PROT_READ | PROT_WRITE,
1541                    MAP_PRIVATE | MAP_ANON, -1, 0);
1542         if (ptr == MAP_FAILED)
1543           {
1544              ERR("mmap of eo class table region failed!");
1545              abort();
1546           }
1547         if (psize > 0) memcpy(ptr, _eo_classes, psize);
1548         if (_eo_classes) munmap(_eo_classes, psize);
1549      }
1550 #else
1551    _eo_classes_alloc += 128;
1552    newsize = _eo_classes_alloc * sizeof(_Efl_Class *);
1553    ptr = realloc(_eo_classes, newsize);
1554    if (!ptr)
1555      {
1556         ERR("realloc of eo class table region faile!!");
1557         abort();
1558      }
1559 #endif
1560    memset(ptr + psize, 0, newsize - psize);
1561    _eo_classes = (_Efl_Class **)ptr;
1562 }
1563 
1564 EAPI const Efl_Class *
efl_class_new(const Efl_Class_Description * desc,const Efl_Class * parent_id,...)1565 efl_class_new(const Efl_Class_Description *desc, const Efl_Class *parent_id, ...)
1566 {
1567    _Efl_Class *klass;
1568    va_list p_list;
1569    size_t extn_sz, mro_sz, mixins_sz;
1570    Eina_List *extn_list, *mro, *mixins;
1571    _Efl_Class *parent = NULL;
1572 
1573    EINA_SAFETY_ON_NULL_RETURN_VAL(desc, NULL);
1574    EINA_SAFETY_ON_NULL_RETURN_VAL(desc->name, NULL);
1575 
1576    if (parent_id)
1577      {
1578         parent = _eo_class_pointer_get(parent_id);
1579         if (!parent)
1580           return NULL;
1581      }
1582 
1583    /* Check restrictions on Interface types. */
1584    if (desc->type == EFL_CLASS_TYPE_INTERFACE)
1585      {
1586         EINA_SAFETY_ON_FALSE_RETURN_VAL(!desc->data_size, NULL);
1587      }
1588 
1589    /* Check parent */
1590    if (parent)
1591      {
1592         /* Verify the inheritance is allowed. */
1593         switch (desc->type)
1594           {
1595            case EFL_CLASS_TYPE_REGULAR:
1596            case EFL_CLASS_TYPE_REGULAR_NO_INSTANT:
1597               if ((parent->desc->type != EFL_CLASS_TYPE_REGULAR) &&
1598                     (parent->desc->type != EFL_CLASS_TYPE_REGULAR_NO_INSTANT))
1599                 {
1600                    ERR("Regular classes ('%s') aren't allowed to inherit from non-regular classes ('%s').",
1601                        desc->name, parent->desc->name);
1602                    return NULL;
1603                 }
1604               break;
1605            case EFL_CLASS_TYPE_INTERFACE:
1606            case EFL_CLASS_TYPE_MIXIN:
1607               if ((parent->desc->type != EFL_CLASS_TYPE_INTERFACE) &&
1608                     (parent->desc->type != EFL_CLASS_TYPE_MIXIN))
1609                 {
1610                    ERR("Non-regular classes ('%s') aren't allowed to inherit from regular classes ('%s').",
1611                        desc->name, parent->desc->name);
1612                    return NULL;
1613                 }
1614               break;
1615            default:
1616              ERR("type cannot be INVALID");
1617              return NULL;
1618           }
1619      }
1620 
1621    /* Build class extensions list */
1622      {
1623         DBG("Started building extensions list for class '%s'", desc->name);
1624         extn_list = NULL;
1625         const _Efl_Class *extn = NULL;
1626         const Eo_Id *extn_id = NULL;
1627 
1628         va_start(p_list, parent_id);
1629 
1630         extn_id = va_arg(p_list, Eo_Id *);
1631         while (extn_id)
1632           {
1633              extn = _eo_class_pointer_get((Efl_Class *)extn_id);
1634              if (EINA_LIKELY(extn != NULL))
1635                {
1636                   switch (extn->desc->type)
1637                     {
1638                      case EFL_CLASS_TYPE_REGULAR_NO_INSTANT:
1639                      case EFL_CLASS_TYPE_REGULAR:
1640                      case EFL_CLASS_TYPE_INTERFACE:
1641                      case EFL_CLASS_TYPE_MIXIN:
1642                        extn_list = eina_list_append(extn_list, extn);
1643                        break;
1644                      default:
1645                        ERR("type cannot be INVALID");
1646                        va_end(p_list);
1647                        return NULL;
1648                     }
1649                }
1650              extn_id = va_arg(p_list, Eo_Id *);
1651           }
1652 
1653         va_end(p_list);
1654 
1655         extn_list = _eo_class_list_remove_duplicates(extn_list);
1656 
1657         extn_sz = sizeof(_Efl_Class *) * (eina_list_count(extn_list) + 1);
1658 
1659         DBG("Finished building extensions list for class '%s'", desc->name);
1660      }
1661 
1662    /* Prepare mro list */
1663      {
1664         DBG("Started building MRO list for class '%s'", desc->name);
1665 
1666         mro = _eo_class_mro_init(desc, parent, extn_list);
1667         if (!mro)
1668           {
1669              eina_list_free(extn_list);
1670              return NULL;
1671           }
1672 
1673         mro_sz = sizeof(_Efl_Class *) * (eina_list_count(mro) + 1);
1674 
1675         DBG("Finished building MRO list for class '%s'", desc->name);
1676      }
1677 
1678    /* Prepare mixins list */
1679      {
1680         Eina_List *itr;
1681         const _Efl_Class *kls_itr;
1682 
1683         DBG("Started building Mixins list for class '%s'", desc->name);
1684 
1685         mixins = NULL;
1686         EINA_LIST_FOREACH(mro, itr, kls_itr)
1687           {
1688              if ((kls_itr) && (kls_itr->desc->type == EFL_CLASS_TYPE_MIXIN) &&
1689                    (kls_itr->desc->data_size > 0))
1690                mixins = eina_list_append(mixins, kls_itr);
1691           }
1692 
1693         mixins_sz = sizeof(Eo_Extension_Data_Offset) * (eina_list_count(mixins) + 1);
1694         if ((desc->type == EFL_CLASS_TYPE_MIXIN) && (desc->data_size > 0))
1695           mixins_sz += sizeof(Eo_Extension_Data_Offset);
1696 
1697         DBG("Finished building Mixins list for class '%s'", desc->name);
1698      }
1699 
1700    klass = calloc(1, _eo_class_sz + extn_sz + mro_sz + mixins_sz);
1701    eina_spinlock_new(&klass->objects.trash_lock);
1702    eina_spinlock_new(&klass->iterators.trash_lock);
1703    klass->parent = parent;
1704    klass->desc = desc;
1705    klass->extensions = (const _Efl_Class **) ((char *) klass + _eo_class_sz);
1706    klass->mro = (const _Efl_Class **) ((char *) klass->extensions + extn_sz);
1707    klass->extn_data_off = (Eo_Extension_Data_Offset *) ((char *) klass->mro + mro_sz);
1708 
1709    if (klass->parent)
1710      {
1711         /* FIXME: Make sure this alignment is enough. */
1712         klass->data_offset = klass->parent->data_offset +
1713            EO_ALIGN_SIZE(klass->parent->desc->data_size);
1714      }
1715    else
1716      {
1717         /* Data starts after the object size. */
1718         klass->data_offset = _eo_sz;
1719      }
1720 
1721    mro = eina_list_remove(mro, NULL);
1722    mro = eina_list_prepend(mro, klass);
1723    if ((desc->type == EFL_CLASS_TYPE_MIXIN) && (desc->data_size > 0))
1724      {
1725         mixins = eina_list_prepend(mixins, klass);
1726      }
1727 
1728    /* Copy the extensions and free the list */
1729      {
1730         const _Efl_Class *extn = NULL;
1731         const _Efl_Class **extn_itr = klass->extensions;
1732         EINA_LIST_FREE(extn_list, extn)
1733           {
1734              *(extn_itr++) = extn;
1735 
1736              DBG("Added '%s' extension", extn->desc->name);
1737           }
1738         *(extn_itr) = NULL;
1739      }
1740 
1741    /* Copy the mro and free the list. */
1742      {
1743         const _Efl_Class *kls_itr = NULL;
1744         const _Efl_Class **mro_itr = klass->mro;
1745         EINA_LIST_FREE(mro, kls_itr)
1746           {
1747              *(mro_itr++) = kls_itr;
1748 
1749              DBG("Added '%s' to MRO", kls_itr->desc->name);
1750           }
1751         *(mro_itr) = NULL;
1752      }
1753 
1754    size_t extn_data_off = klass->data_offset;
1755    if (klass->desc->type != EFL_CLASS_TYPE_MIXIN)
1756      {
1757       extn_data_off += EO_ALIGN_SIZE(klass->desc->data_size);
1758      }
1759 
1760    /* Feed the mixins data offsets and free the mixins list. */
1761      {
1762         const _Efl_Class *kls_itr = NULL;
1763         Eo_Extension_Data_Offset *extn_data_itr = klass->extn_data_off;
1764         EINA_LIST_FREE(mixins, kls_itr)
1765           {
1766              extn_data_itr->klass = kls_itr;
1767              extn_data_itr->offset = extn_data_off;
1768 
1769              extn_data_off += EO_ALIGN_SIZE(extn_data_itr->klass->desc->data_size);
1770              extn_data_itr++;
1771 
1772              DBG("Added '%s' to Data Offset info", kls_itr->desc->name);
1773           }
1774         extn_data_itr->klass = 0;
1775         extn_data_itr->offset = 0;
1776      }
1777 
1778    klass->obj_size = extn_data_off;
1779 
1780      {
1781         Eo_Id new_id;
1782 
1783         eina_lock_take(&_efl_class_creation_lock);
1784         new_id = (_eo_classes_last_id + 1) | MASK_CLASS_TAG;
1785         _eo_classes_expand();
1786         _eo_classes[_UNMASK_ID(new_id) - 1] = klass;
1787         eina_lock_release(&_efl_class_creation_lock);
1788 
1789         klass->header.id = new_id;
1790      }
1791 
1792    if (!_eo_class_initializer(klass))
1793      {
1794         return NULL;
1795      }
1796 
1797    /* If functions haven't been set, invoke it with an empty ops structure. */
1798    if (!klass->functions_set)
1799      {
1800         efl_class_functions_set(_eo_class_id_get(klass), NULL, NULL);
1801      }
1802 
1803    _eo_class_constructor(klass);
1804 
1805    DBG("Finished building class '%s'", klass->desc->name);
1806 
1807    return _eo_class_id_get(klass);
1808 }
1809 
1810 EAPI Eina_Bool
efl_object_override(Eo * eo_id,const Efl_Object_Ops * ops)1811 efl_object_override(Eo *eo_id, const Efl_Object_Ops *ops)
1812 {
1813    EO_OBJ_POINTER_RETURN_VAL(eo_id, obj, EINA_FALSE);
1814    EO_CLASS_POINTER_GOTO(EFL_OBJECT_OVERRIDE_CLASS, klass, err);
1815 
1816    if (ops)
1817      {
1818         Eo_Vtable *vtable = obj->opt->vtable;
1819         //copy all the vtable nodes that we are going to change later on
1820         Eina_Bool *hitmap;
1821 
1822         if (!vtable)
1823           {
1824              vtable = calloc(1, sizeof(*vtable));
1825              _vtable_init_size(vtable, obj->klass->vtable.size);
1826              _vtable_take_over(vtable, &obj->klass->vtable);
1827           }
1828 
1829         hitmap = alloca(vtable->size * sizeof(Eina_Bool));
1830         memset(hitmap, 0, vtable->size);
1831 
1832         if (!_eo_class_funcs_set(vtable, ops, obj->klass, klass, EINA_TRUE, obj->klass->class_id, hitmap))
1833           {
1834              ERR("Failed to override functions for %s@%p. All previous "
1835                  "overrides have been reset.", obj->klass->desc->name, eo_id);
1836              if (obj->opt->vtable == vtable)
1837                {
1838                   EO_OPTIONAL_COW_SET(obj, vtable, NULL);
1839                }
1840              else
1841                {
1842                   _vtable_free(vtable, &obj->klass->vtable);
1843                   free(vtable);
1844                }
1845 
1846              goto err;
1847           }
1848         EO_OPTIONAL_COW_SET(obj, vtable, vtable);
1849      }
1850    else
1851      {
1852         if (obj->opt->vtable)
1853           {
1854              _vtable_free(obj->opt->vtable, &obj->klass->vtable);
1855              EO_OPTIONAL_COW_SET(obj, vtable, NULL);
1856           }
1857      }
1858 
1859    EO_OBJ_DONE(eo_id);
1860    return EINA_TRUE;
1861 
1862 err:
1863    EO_OBJ_DONE(eo_id);
1864    return EINA_FALSE;
1865 }
1866 
1867 EAPI Eina_Bool
efl_isa(const Eo * eo_id,const Efl_Class * klass_id)1868 efl_isa(const Eo *eo_id, const Efl_Class *klass_id)
1869 {
1870    Efl_Id_Domain domain;
1871    Eo_Id_Data *data;
1872    Eo_Id_Table_Data *tdata;
1873    Eina_Bool isa = EINA_FALSE;
1874 
1875    if (EINA_UNLIKELY(!eo_id)) return EINA_FALSE;
1876 
1877    // Everything can add a override to an existing class, which pretty much means, everything is a efl override
1878    // This is required in order to support our debug-profile for the users of efl_override
1879    if (EINA_UNLIKELY(klass_id == EFL_OBJECT_OVERRIDE_CLASS)) return EINA_TRUE;
1880 
1881    // Case where we are looking if eo_id is a class that contain klass_id
1882    if (EINA_UNLIKELY(_eo_is_a_class(eo_id)))
1883      {
1884 
1885         EO_CLASS_POINTER_GOTO(klass_id, klass, err_class);
1886         EO_CLASS_POINTER_GOTO(eo_id, lookinto, err_class0);
1887 
1888         if (EINA_UNLIKELY(lookinto->vtable.size <= klass->class_id))
1889           return EINA_FALSE;
1890 
1891         return !!lookinto->vtable.chain[klass->class_id].funcs;
1892      }
1893 
1894    domain = ((Eo_Id)eo_id >> SHIFT_DOMAIN) & MASK_DOMAIN;
1895    data = _eo_table_data_get();
1896    tdata = _eo_table_data_table_get(data, domain);
1897    if (EINA_UNLIKELY(!tdata)) goto err;
1898 
1899    if (EINA_LIKELY(domain != EFL_ID_DOMAIN_SHARED))
1900      {
1901         if ((tdata->cache.isa_id == eo_id) &&
1902             (tdata->cache.klass == klass_id))
1903           {
1904              isa = tdata->cache.isa;
1905              return isa;
1906           }
1907 
1908         EO_OBJ_POINTER_GOTO(eo_id, obj, err_obj);
1909         EO_CLASS_POINTER_GOTO(klass_id, klass, err_class);
1910 
1911         const Eo_Vtable vtable = obj->klass->vtable;
1912         if (EINA_UNLIKELY(vtable.size <= klass->class_id))
1913           return EINA_FALSE;
1914 
1915         isa = !!vtable.chain[klass->class_id].funcs;
1916 
1917         // Caching the result as we do a lot of serial efl_isa due to evas_object_image using it.
1918         tdata->cache.isa_id = eo_id;
1919         tdata->cache.klass = klass_id;
1920         tdata->cache.isa = isa;
1921      }
1922    else
1923      {
1924         eina_lock_take(&(_eo_table_data_shared_data->obj_lock));
1925 
1926         if ((tdata->cache.isa_id == eo_id) &&
1927             (tdata->cache.klass == klass_id))
1928           {
1929              isa = tdata->cache.isa;
1930              // since this is the cache we hope this gets a lot of hits and
1931              // thus lets assume the hit is the mot important thing thus
1932              // put the lock release and return here inline in the l1
1933              // instruction cache hopefully already fetched
1934              eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
1935              return isa;
1936           }
1937 
1938         EO_OBJ_POINTER_GOTO(eo_id, obj, err_shared_obj);
1939         EO_CLASS_POINTER_GOTO(klass_id, klass, err_shared_class);
1940         if (EINA_UNLIKELY(obj->klass->vtable.size <= klass->class_id))
1941           goto err_vtable;
1942 
1943         isa = !!obj->klass->vtable.chain[klass->class_id].funcs;
1944 
1945         // Caching the result as we do a lot of serial efl_isa due to evas_object_image using it.
1946         tdata->cache.isa_id = eo_id;
1947         tdata->cache.klass = klass_id;
1948         tdata->cache.isa = isa;
1949 err_vtable:
1950         EO_OBJ_DONE(eo_id);
1951         eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
1952      }
1953    return isa;
1954 
1955 err_shared_class: EINA_COLD
1956    _EO_POINTER_ERR(klass_id, "Class (%p) is an invalid ref.", klass_id);
1957    EO_OBJ_DONE(eo_id);
1958 err_shared_obj: EINA_COLD
1959    eina_lock_release(&(_eo_table_data_shared_data->obj_lock));
1960    return EINA_FALSE;
1961 
1962 err_class0:
1963    _EO_POINTER_ERR(eo_id, "Class (%p) is an invalid ref.", eo_id);
1964    return EINA_FALSE;
1965 
1966 err_class: EINA_COLD
1967    _EO_POINTER_ERR(klass_id, "Class (%p) is an invalid ref.", klass_id);
1968 err_obj:
1969    return EINA_FALSE;
1970 
1971 err: EINA_COLD
1972    ERR("Object %p is not a valid object in this context: object domain: %d, "
1973        "current domain: %d, local domain: %d, available domains: [%s %s %s %s]."
1974        " Are you trying to access this object from another thread?",
1975        eo_id, (int)domain,
1976        (int)data->domain_stack[data->stack_top], (int)data->local_domain,
1977        (data->tables[0]) ? "0" : " ", (data->tables[1]) ? "1" : " ",
1978        (data->tables[2]) ? "2" : " ", (data->tables[3]) ? "3" : " ");
1979    return EINA_FALSE;
1980 }
1981 
1982 EAPI Eo *
efl_xref_internal(const char * file,int line,Eo * obj_id,const Eo * ref_obj_id)1983 efl_xref_internal(const char *file, int line, Eo *obj_id, const Eo *ref_obj_id)
1984 {
1985    efl_ref(obj_id);
1986 
1987 #ifdef EO_DEBUG
1988    const char *func_name = __func__;
1989    EO_OBJ_POINTER_RETURN_VAL_PROXY(obj_id, obj, obj_id);
1990 
1991    Eo_Xref_Node *xref = calloc(1, sizeof(*xref));
1992    xref->ref_obj = ref_obj_id;
1993    xref->file = file;
1994    xref->line = line;
1995 
1996    obj->xrefs = eina_inlist_prepend(obj->xrefs, EINA_INLIST_GET(xref));
1997    EO_OBJ_DONE(obj_id);
1998 #else
1999    (void) ref_obj_id;
2000    (void) file;
2001    (void) line;
2002 #endif
2003 
2004    return obj_id;
2005 }
2006 
2007 EAPI void
efl_xunref(Eo * obj_id,const Eo * ref_obj_id)2008 efl_xunref(Eo *obj_id, const Eo *ref_obj_id)
2009 {
2010    EO_OBJ_POINTER_RETURN(obj_id, obj);
2011 #ifdef EO_DEBUG
2012    Eo_Xref_Node *xref = NULL;
2013    EINA_INLIST_FOREACH(obj->xrefs, xref)
2014      {
2015         if (xref->ref_obj == ref_obj_id)
2016           break;
2017      }
2018 
2019    if (xref)
2020      {
2021         obj->xrefs = eina_inlist_remove(obj->xrefs, EINA_INLIST_GET(xref));
2022         eina_freeq_ptr_main_add(xref, free, sizeof(*xref));
2023      }
2024    else
2025      {
2026         ERR("ref_obj (%p) does not reference obj (%p). Aborting unref.", ref_obj_id, obj_id);
2027         EO_OBJ_DONE(obj_id);
2028         return;
2029      }
2030    EO_OBJ_DONE(obj_id);
2031 #else
2032    (void) ref_obj_id;
2033 #endif
2034    efl_unref(obj_id);
2035 }
2036 
2037 EAPI Eo *
efl_ref(const Eo * obj_id)2038 efl_ref(const Eo *obj_id)
2039 {
2040    EO_OBJ_POINTER_RETURN_VAL(obj_id, obj, (Eo *)obj_id);
2041 
2042    ++(obj->user_refcount);
2043    if (EINA_UNLIKELY(obj->user_refcount == 1))
2044      _efl_ref(obj);
2045    else if (EINA_UNLIKELY(obj->ownership_track && obj->user_refcount == 2))
2046      efl_event_callback_call((Eo *) obj_id, EFL_EVENT_OWNERSHIP_SHARED, NULL);
2047 
2048 #ifdef EO_DEBUG
2049    _eo_log_obj_ref_op(obj, EO_REF_OP_REF);
2050 #endif
2051    EO_OBJ_DONE(obj_id);
2052    return (Eo *)obj_id;
2053 }
2054 
2055 EAPI void
efl_unref(const Eo * obj_id)2056 efl_unref(const Eo *obj_id)
2057 {
2058    EO_OBJ_POINTER_RETURN(obj_id, obj);
2059 
2060    if (EINA_UNLIKELY((!obj->unref_compensate && obj->user_refcount == 1 && obj->parent) ||
2061                      (obj->unref_compensate && obj->user_refcount == 2 && obj->parent)))
2062      {
2063         if (!obj->allow_parent_unref)
2064           CRI("Calling efl_unref instead of efl_del or efl_parent_set(NULL). Temporary fallback in place triggered.");
2065         EO_OBJ_DONE(obj_id);
2066         efl_del(obj_id);
2067         return ;
2068      }
2069 
2070    _efl_ref(obj);
2071 
2072    if (EINA_UNLIKELY((obj->noref_event) && (!obj->unref_compensate) &&
2073                      ((obj->user_refcount == 1 && !obj->parent) ||
2074                       (obj->user_refcount == 2 && obj->parent))))
2075      {
2076         // We need to report efl_ref_count correctly during EFL_EVENT_NOREF, so fake it
2077         // by adjusting efl_ref_count while inside efl_unref (This should avoid
2078         // infinite loop)
2079         obj->unref_compensate = EINA_TRUE;
2080 
2081         // The noref event should happen before any object in the
2082         // tree get affected by the change in refcount.
2083         efl_event_callback_call((Eo *) obj_id, EFL_EVENT_NOREF, NULL);
2084 
2085         obj->unref_compensate = EINA_FALSE;
2086      }
2087 
2088    --(obj->user_refcount);
2089 
2090 #ifdef EO_DEBUG
2091    _eo_log_obj_ref_op(obj, EO_REF_OP_UNREF);
2092 #endif
2093    if (EINA_UNLIKELY((obj->user_refcount <= 0)))
2094      {
2095         if (obj->user_refcount < 0)
2096           {
2097              ERR("Obj:%s@%p. User refcount (%d) < 0. Too many unrefs.",
2098                  obj->klass->desc->name, obj_id, obj->user_refcount);
2099              _eo_log_obj_report((Eo_Id)obj_id, EINA_LOG_LEVEL_ERR, __func__, __FILE__, __LINE__);
2100              EO_OBJ_DONE(obj_id);
2101              _efl_unref(obj);
2102              return;
2103           }
2104         _efl_unref(obj);
2105      }
2106    else if (EINA_UNLIKELY(obj->ownership_track && obj->user_refcount == 1))
2107      {
2108         efl_event_callback_call((Eo *) obj_id, EFL_EVENT_OWNERSHIP_UNIQUE, NULL);
2109      }
2110 
2111    _apply_auto_unref(obj, obj_id);
2112 
2113    _efl_unref(obj);
2114    EO_OBJ_DONE(obj_id);
2115 }
2116 
2117 EAPI int
efl_ref_count(const Eo * obj_id)2118 efl_ref_count(const Eo *obj_id)
2119 {
2120    EO_OBJ_POINTER_RETURN_VAL(obj_id, obj, 0);
2121    int ref;
2122    ref = obj->user_refcount - (obj->unref_compensate ? 1 : 0);
2123    EO_OBJ_DONE(obj_id);
2124    return ref;
2125 }
2126 
2127 EAPI int
___efl_ref2_count(const Eo * obj_id)2128 ___efl_ref2_count(const Eo *obj_id)
2129 {
2130    EO_OBJ_POINTER_RETURN_VAL(obj_id, obj, 0);
2131    int ref;
2132    ref = obj->refcount;
2133    EO_OBJ_DONE(obj_id);
2134    return ref;
2135 }
2136 
2137 EAPI void
___efl_ref2_reset(const Eo * obj_id)2138 ___efl_ref2_reset(const Eo *obj_id)
2139 {
2140    EO_OBJ_POINTER_RETURN(obj_id, obj);
2141    obj->refcount = 0;
2142    EO_OBJ_DONE(obj_id);
2143 }
2144 
2145 
2146 EAPI void
efl_del_intercept_set(Eo * obj_id,Efl_Del_Intercept del_intercept_func)2147 efl_del_intercept_set(Eo *obj_id, Efl_Del_Intercept del_intercept_func)
2148 {
2149    EO_OBJ_POINTER_RETURN(obj_id, obj);
2150    EO_OPTIONAL_COW_SET(obj, del_intercept, del_intercept_func);
2151    EO_OBJ_DONE(obj_id);
2152 }
2153 
2154 EAPI Efl_Del_Intercept
efl_del_intercept_get(const Eo * obj_id)2155 efl_del_intercept_get(const Eo *obj_id)
2156 {
2157    Efl_Del_Intercept func;
2158 
2159    EO_OBJ_POINTER_RETURN_VAL(obj_id, obj, NULL);
2160    func = obj->opt->del_intercept;
2161    EO_OBJ_DONE(obj_id);
2162    return func;
2163 }
2164 
2165 void
_eo_condtor_done(Eo * obj_id)2166 _eo_condtor_done(Eo *obj_id)
2167 {
2168    EO_OBJ_POINTER_RETURN(obj_id, obj);
2169    if (obj->condtor_done)
2170      {
2171         ERR("Object %p is already constructed at this point.", obj);
2172         EO_OBJ_DONE(obj_id);
2173         return;
2174      }
2175    obj->condtor_done = EINA_TRUE;
2176    EO_OBJ_DONE(obj_id);
2177 }
2178 
2179 static inline void *
_efl_data_scope_safe_get(const _Eo_Object * obj,const _Efl_Class * klass)2180 _efl_data_scope_safe_get(const _Eo_Object *obj, const _Efl_Class *klass)
2181 {
2182    if (EINA_LIKELY(klass->desc->data_size > 0))
2183      {
2184         return _efl_data_scope_get(obj, klass);
2185      }
2186 
2187    return NULL;
2188 }
2189 
2190 static inline void *
_efl_data_scope_get(const _Eo_Object * obj,const _Efl_Class * klass)2191 _efl_data_scope_get(const _Eo_Object *obj, const _Efl_Class *klass)
2192 {
2193    if (EINA_LIKELY(klass->desc->type != EFL_CLASS_TYPE_MIXIN))
2194      return ((char *) obj) + klass->data_offset;
2195 
2196    if (EINA_UNLIKELY(klass->desc->data_size == 0))
2197      {
2198         return NULL;
2199      }
2200    else
2201      {
2202         Eo_Extension_Data_Offset *doff_itr = obj->klass->extn_data_off;
2203 
2204         if (!doff_itr)
2205           return NULL;
2206 
2207         while (doff_itr->klass)
2208           {
2209              if (doff_itr->klass == klass)
2210                return ((char *) obj) + doff_itr->offset;
2211              doff_itr++;
2212           }
2213      }
2214 
2215    return NULL;
2216 }
2217 
2218 static inline void *
_efl_data_xref_internal(const char * file,int line,_Eo_Object * obj,const _Efl_Class * klass,const _Eo_Object * ref_obj)2219 _efl_data_xref_internal(const char *file, int line, _Eo_Object *obj, const _Efl_Class *klass, const _Eo_Object *ref_obj)
2220 {
2221    void *data = NULL;
2222    if (klass != NULL)
2223      {
2224         data = _efl_data_scope_safe_get(obj, klass);
2225         if (data == NULL) return NULL;
2226      }
2227 #ifdef EO_DEBUG
2228    (obj->datarefcount)++;
2229    Eo_Xref_Node *xref = calloc(1, sizeof(*xref));
2230    xref->ref_obj = _eo_obj_id_get(ref_obj);
2231    xref->data_klass = klass ? klass->desc->name : NULL;
2232    xref->file = file;
2233    xref->line = line;
2234 
2235    obj->data_xrefs = eina_inlist_prepend(obj->data_xrefs, EINA_INLIST_GET(xref));
2236 #else
2237    (void) ref_obj;
2238    (void) file;
2239    (void) line;
2240 #endif
2241    return data;
2242 }
2243 
2244 static inline void
_efl_data_xunref_internal(_Eo_Object * obj EINA_UNUSED,void * data EINA_UNUSED,const _Eo_Object * ref_obj EINA_UNUSED)2245 _efl_data_xunref_internal(_Eo_Object *obj EINA_UNUSED, void *data EINA_UNUSED, const _Eo_Object *ref_obj EINA_UNUSED)
2246 {
2247 #ifdef EO_DEBUG
2248    const _Efl_Class *klass = obj->klass;
2249    Eo_Xref_Node *xref = NULL;
2250    Eina_Bool in_range = (((char *)data >= (((char *) obj) + _eo_sz)) &&
2251                          ((char *)data < (((char *) obj) + klass->obj_size)));
2252    if (!in_range)
2253      {
2254         ERR("Data %p is not in the data range of the object %p (%s).",
2255             data, _eo_obj_id_get(obj), obj->klass->desc->name);
2256      }
2257    if (obj->datarefcount == 0)
2258      {
2259         ERR("Data for object %p (%s) is already not referenced.",
2260             _eo_obj_id_get(obj), obj->klass->desc->name);
2261      }
2262    else
2263      {
2264         (obj->datarefcount)--;
2265      }
2266    EINA_INLIST_FOREACH(obj->data_xrefs, xref)
2267      {
2268         if (xref->ref_obj == _eo_obj_id_get(ref_obj))
2269           break;
2270      }
2271    if (xref)
2272      {
2273         obj->data_xrefs = eina_inlist_remove(obj->data_xrefs, EINA_INLIST_GET(xref));
2274         eina_freeq_ptr_main_add(xref, free, sizeof(*xref));
2275      }
2276    else
2277      {
2278         ERR("ref_obj %p (%s) does not reference data %p of obj %p (%s).",
2279             _eo_obj_id_get(ref_obj), ref_obj->klass->desc->name, data,
2280             _eo_obj_id_get(obj), obj->klass->desc->name);
2281      }
2282 #endif
2283 }
2284 
2285 EAPI void *
efl_data_scope_get(const Eo * obj_id,const Efl_Class * klass_id)2286 efl_data_scope_get(const Eo *obj_id, const Efl_Class *klass_id)
2287 {
2288    void *ret = NULL;
2289    EO_OBJ_POINTER_RETURN_VAL(obj_id, obj, NULL);
2290    EO_CLASS_POINTER_GOTO(klass_id, klass, err_klass);
2291 
2292 #ifndef EO_DEBUG
2293    ret = _efl_data_scope_safe_get(obj, klass);
2294 #else
2295    if (_eo_class_mro_has(obj->klass, klass))
2296      {
2297         ret = _efl_data_scope_safe_get(obj, klass);
2298         if (!ret && (klass->desc->data_size == 0))
2299           ERR("Tried getting data of class '%s', but it has none.", klass->desc->name);
2300      }
2301    else
2302      {
2303         ERR("Tried getting data of class '%s' from object of class '%s', but the former is not a direct inheritance of the latter.",
2304             klass->desc->name, obj->klass->desc->name);
2305      }
2306 #endif
2307 
2308 err_klass:
2309    EO_OBJ_DONE(obj_id);
2310    return ret;
2311 }
2312 
2313 EAPI void *
efl_data_scope_safe_get(const Eo * obj_id,const Efl_Class * klass_id)2314 efl_data_scope_safe_get(const Eo *obj_id, const Efl_Class *klass_id)
2315 {
2316    void *ret = NULL;
2317 
2318    if (!obj_id) return NULL;
2319    EO_OBJ_POINTER_RETURN_VAL(obj_id, obj, NULL);
2320    EO_CLASS_POINTER_GOTO(klass_id, klass, err_klass);
2321    if (obj->destructed) goto err_klass;
2322 
2323    if (_eo_class_mro_has(obj->klass, klass))
2324      {
2325         ret = _efl_data_scope_safe_get(obj, klass);
2326 
2327 #ifdef EO_DEBUG
2328         if (!ret && (klass->desc->data_size == 0))
2329           ERR("Tried getting data of class '%s', but it has none.", klass->desc->name);
2330 #endif
2331      }
2332 
2333 err_klass:
2334    EO_OBJ_DONE(obj_id);
2335    return ret;
2336 }
2337 
2338 EAPI void *
efl_data_xref_internal(const char * file,int line,const Eo * obj_id,const Efl_Class * klass_id,const Eo * ref_obj_id)2339 efl_data_xref_internal(const char *file, int line, const Eo *obj_id, const Efl_Class *klass_id, const Eo *ref_obj_id)
2340 {
2341    void *ret = NULL;
2342    _Efl_Class *klass = NULL;
2343    const char *func_name = __func__;
2344    EO_OBJ_POINTER_RETURN_VAL_PROXY(obj_id, obj, NULL);
2345    EO_OBJ_POINTER_PROXY(ref_obj_id, ref_obj);
2346    if (ref_obj)
2347      {
2348         if (klass_id)
2349           {
2350              EO_CLASS_POINTER_GOTO_PROXY(klass_id, klass2, err_klass);
2351              klass = klass2;
2352 #ifdef EO_DEBUG
2353              // rare to use goto to keep instruction cache cleaner
2354              if (!_eo_class_mro_has(obj->klass, klass)) goto err_mro;
2355 #endif
2356           }
2357 
2358         ret = _efl_data_xref_internal(file, line, obj, klass, ref_obj);
2359 #ifdef EO_DEBUG
2360         // rare to use goto to keep instruction cache cleaner
2361         if (klass && !ret && (klass->desc->data_size == 0)) goto err_ret;
2362 #endif
2363 err_klass:
2364         EO_OBJ_DONE(ref_obj_id);
2365      }
2366    EO_OBJ_DONE(obj_id);
2367    return ret;
2368 #ifdef EO_DEBUG
2369 err_ret:
2370    ERR("Tried getting data of class '%s', but it has none.", klass->desc->name);
2371    goto err;
2372 err_mro:
2373    ERR("Tried getting data of class '%s' from object of class '%s', but the former is not a direct inheritance of the latter.", klass->desc->name, obj->klass->desc->name);
2374 err:
2375    EO_OBJ_DONE(obj_id);
2376    EO_OBJ_DONE(ref_obj_id);
2377    return NULL;
2378 #endif
2379 }
2380 
2381 EAPI void
efl_data_xunref_internal(const Eo * obj_id,void * data,const Eo * ref_obj_id)2382 efl_data_xunref_internal(const Eo *obj_id, void *data, const Eo *ref_obj_id)
2383 {
2384    EO_OBJ_POINTER_RETURN(obj_id, obj);
2385    EO_OBJ_POINTER(ref_obj_id, ref_obj);
2386    if (ref_obj)
2387      {
2388         _efl_data_xunref_internal(obj, data, ref_obj);
2389         EO_OBJ_DONE(ref_obj_id);
2390      }
2391    EO_OBJ_DONE(obj_id);
2392 }
2393 
2394 static void
_eo_table_del_cb(void * in)2395 _eo_table_del_cb(void *in)
2396 {
2397    Eo_Id_Data *data = in;
2398    _eo_free_ids_tables(data);
2399 }
2400 
2401 /* FIXME: Support other domains and tables, at the moment only the main
2402  * domain and table.
2403  * This is used by the gdb debug helper script */
2404 Eo_Id_Data *_eo_gdb_main_domain = NULL;
2405 
2406 EAPI Eina_Bool
efl_object_init(void)2407 efl_object_init(void)
2408 {
2409    const char *log_dom = "eo";
2410    if (_efl_object_init_count++ > 0)
2411      return EINA_TRUE;
2412 
2413    eina_init();
2414 
2415 #if HAVE_VALGRIND
2416    _eo_trash_bypass = RUNNING_ON_VALGRIND;
2417 #endif
2418 
2419    _efl_object_main_thread = eina_thread_self();
2420 
2421    _eo_sz = EO_ALIGN_SIZE(sizeof(_Eo_Object));
2422    _eo_class_sz = EO_ALIGN_SIZE(sizeof(_Efl_Class));
2423 
2424    _eo_classes = NULL;
2425    _eo_classes_last_id = EO_CLASS_IDS_FIRST - 1;
2426    _eo_log_dom = eina_log_domain_register(log_dom, EINA_COLOR_LIGHTBLUE);
2427    if (_eo_log_dom < 0)
2428      {
2429         EINA_LOG_ERR("Could not register log domain: %s.", log_dom);
2430         return EINA_FALSE;
2431      }
2432 
2433    if (!eina_lock_recursive_new(&_efl_class_creation_lock))
2434      {
2435         ERR("Could not init lock.");
2436         return EINA_FALSE;
2437      }
2438 
2439    if (!eina_spinlock_new(&_ops_storage_lock))
2440      {
2441         ERR("Could not init lock.");
2442         return EINA_FALSE;
2443      }
2444 
2445    _eo_log_obj_init();
2446 
2447    eina_magic_string_static_set(EO_EINA_MAGIC, EO_EINA_MAGIC_STR);
2448    eina_magic_string_static_set(EO_FREED_EINA_MAGIC,
2449                                 EO_FREED_EINA_MAGIC_STR);
2450    eina_magic_string_static_set(EO_CLASS_EINA_MAGIC,
2451                                 EO_CLASS_EINA_MAGIC_STR);
2452 #ifndef _WIN32
2453    _ops_storage = eina_hash_pointer_new(NULL);
2454 #else
2455    _ops_storage = eina_hash_string_superfast_new(NULL);
2456 #endif
2457 
2458    _eo_table_data_shared = _eo_table_data_new(EFL_ID_DOMAIN_SHARED);
2459    if (!_eo_table_data_shared)
2460      {
2461         ERR("Could not allocate shared table data");
2462         return EINA_FALSE;
2463      }
2464    _eo_table_data_shared_data = _eo_table_data_shared->tables[EFL_ID_DOMAIN_SHARED];
2465 
2466    // specially force eoid data to be creanted so we can switch it to domain 0
2467    Eo_Id_Data *data = _eo_table_data_new(EFL_ID_DOMAIN_MAIN);
2468    _eo_gdb_main_domain = data;
2469    if (!data)
2470      {
2471         ERR("Could not allocate main table data");
2472         return EINA_FALSE;
2473      }
2474    if (!eina_tls_cb_new(&_eo_table_data, _eo_table_del_cb))
2475      {
2476         ERR("Could not allocate TLS for eo domain data");
2477         _eo_table_del_cb(data);
2478         return EINA_FALSE;
2479      }
2480    eina_tls_set(_eo_table_data, data);
2481    _efl_object_main_thread = eina_thread_self();
2482 
2483    efl_object_optional_cow =
2484          eina_cow_add("Efl Object Optional Data", sizeof(Efl_Object_Optional),
2485                       64, &efl_object_optional_cow_default, EINA_TRUE);
2486 
2487    _efl_add_fallback_init();
2488 
2489    eina_log_timing(_eo_log_dom,
2490                    EINA_LOG_STATE_STOP,
2491                    EINA_LOG_STATE_INIT);
2492 
2493    /* bootstrap EFL_CLASS_CLASS */
2494    const Eo *efl_klass = EFL_CLASS_CLASS;
2495    /* bootstrap EFL_OBJECT_CLASS */
2496    const Eo *efl_object = EFL_OBJECT_CLASS;
2497 
2498    return efl_klass && efl_object;
2499 }
2500 
2501 EAPI Eina_Bool
efl_object_shutdown(void)2502 efl_object_shutdown(void)
2503 {
2504    size_t i;
2505    _Efl_Class **cls_itr = _eo_classes + _eo_classes_last_id - 1;
2506 
2507    if (--_efl_object_init_count > 0)
2508      return EINA_TRUE;
2509 
2510 #ifdef EO_DEBUG
2511    {
2512       Efl_Object *obj;
2513       Eina_Iterator *objects;
2514       objects = eo_objects_iterator_new();
2515       printf("Objects leaked by EO:\n");
2516       printf("class@pointer - user-refcount internal-refcount\n");
2517       EINA_ITERATOR_FOREACH(objects, obj)
2518         {
2519            printf("%s@%p - %d %d \n", efl_class_name_get(obj), obj, efl_ref_count(obj), ___efl_ref2_count(obj));
2520         }
2521       eina_iterator_free(objects);
2522    }
2523 #endif
2524 
2525 
2526    eina_log_timing(_eo_log_dom,
2527                    EINA_LOG_STATE_START,
2528                    EINA_LOG_STATE_SHUTDOWN);
2529 
2530    _efl_add_fallback_shutdown();
2531 
2532    for (i = 0 ; i < _eo_classes_last_id ; i++, cls_itr--)
2533      {
2534         if (*cls_itr)
2535           eo_class_free(*cls_itr);
2536      }
2537 
2538    eina_lock_take(&_efl_class_creation_lock);
2539    _eo_classes_release();
2540    eina_lock_release(&_efl_class_creation_lock);
2541 
2542    eina_hash_free(_ops_storage);
2543    _ops_storage = NULL;
2544 
2545    eina_spinlock_free(&_ops_storage_lock);
2546    eina_lock_free(&_efl_class_creation_lock);
2547 
2548    _eo_free_ids_tables(_eo_table_data_get());
2549    eina_tls_free(_eo_table_data);
2550    if (_eo_table_data_shared)
2551      {
2552         _eo_free_ids_tables(_eo_table_data_shared);
2553         _eo_table_data_shared = NULL;
2554         _eo_table_data_shared_data = NULL;
2555      }
2556 
2557    eina_cow_del(efl_object_optional_cow);
2558    efl_object_optional_cow = NULL;
2559 
2560    _eo_log_obj_shutdown();
2561 
2562    eina_log_domain_unregister(_eo_log_dom);
2563    _eo_log_dom = -1;
2564 
2565    ++_efl_object_init_generation;
2566 
2567    eina_shutdown();
2568    return EINA_FALSE;
2569 }
2570 
2571 
2572 EAPI Efl_Id_Domain
efl_domain_get(void)2573 efl_domain_get(void)
2574 {
2575    Eo_Id_Data *data = _eo_table_data_get();
2576    return data->local_domain;
2577 }
2578 
2579 EAPI Efl_Id_Domain
efl_domain_current_get(void)2580 efl_domain_current_get(void)
2581 {
2582    Eo_Id_Data *data = _eo_table_data_get();
2583    return data->domain_stack[data->stack_top];
2584 }
2585 
2586 EAPI Eina_Bool
efl_domain_switch(Efl_Id_Domain domain)2587 efl_domain_switch(Efl_Id_Domain domain)
2588 {
2589    Eo_Id_Data *data = _eo_table_data_get();
2590    Eo_Id_Data *new_data;
2591    if ((domain < EFL_ID_DOMAIN_MAIN) || (domain > EFL_ID_DOMAIN_THREAD) ||
2592        (domain == EFL_ID_DOMAIN_SHARED))
2593      {
2594         ERR("Invalid domain %i being switched to", domain);
2595         return EINA_FALSE;
2596      }
2597    if ((data) && (data->local_domain == domain))
2598      return EINA_TRUE;
2599 
2600    new_data = _eo_table_data_new(domain);
2601    if (!new_data)
2602      {
2603         ERR("Could not allocate domain %i table data", domain);
2604         return EINA_FALSE;
2605      }
2606    if (data) _eo_free_ids_tables(data);
2607    new_data->local_domain = domain;
2608    new_data->domain_stack[new_data->stack_top] = domain;
2609    eina_tls_set(_eo_table_data, new_data);
2610    return EINA_TRUE;
2611 }
2612 
2613 static inline Eina_Bool
_efl_domain_push(Eo_Id_Data * data,Efl_Id_Domain domain)2614 _efl_domain_push(Eo_Id_Data *data, Efl_Id_Domain domain)
2615 {
2616    if (data->stack_top >= (sizeof(data->domain_stack) - 1))
2617      {
2618         ERR("Failed to push domain %i on stack. Out of stack space at %i",
2619             domain, data->stack_top);
2620         return EINA_FALSE;
2621      }
2622    data->stack_top++;
2623    data->domain_stack[data->stack_top] = domain;
2624    return EINA_TRUE;
2625 }
2626 
2627 static inline void
_efl_domain_pop(Eo_Id_Data * data)2628 _efl_domain_pop(Eo_Id_Data *data)
2629 {
2630    if (data->stack_top > 0) data->stack_top--;
2631 }
2632 
2633 EAPI Eina_Bool
efl_domain_current_push(Efl_Id_Domain domain)2634 efl_domain_current_push(Efl_Id_Domain domain)
2635 {
2636    Eo_Id_Data *data = _eo_table_data_get();
2637    return _efl_domain_push(data, domain);
2638 }
2639 
2640 EAPI void
efl_domain_current_pop(void)2641 efl_domain_current_pop(void)
2642 {
2643    Eo_Id_Data *data = _eo_table_data_get();
2644    _efl_domain_pop(data);
2645 }
2646 
2647 EAPI Eina_Bool
efl_domain_current_set(Efl_Id_Domain domain)2648 efl_domain_current_set(Efl_Id_Domain domain)
2649 {
2650    Eo_Id_Data *data = _eo_table_data_get();
2651    if ((domain < EFL_ID_DOMAIN_MAIN) || (domain > EFL_ID_DOMAIN_THREAD))
2652      {
2653         ERR("Invalid domain %i being set", domain);
2654         return EINA_FALSE;
2655      }
2656    data->domain_stack[data->stack_top] = domain;
2657    return EINA_TRUE;
2658 }
2659 
2660 EAPI Efl_Domain_Data *
efl_domain_data_get(void)2661 efl_domain_data_get(void)
2662 {
2663    Eo_Id_Data *data = _eo_table_data_get();
2664    return (Efl_Domain_Data *)data;
2665 }
2666 
2667 EAPI Efl_Id_Domain
efl_domain_data_adopt(Efl_Domain_Data * data_in)2668 efl_domain_data_adopt(Efl_Domain_Data *data_in)
2669 {
2670    Eo_Id_Data *data = _eo_table_data_get();
2671    Eo_Id_Data *data_foreign = (Eo_Id_Data *)data_in;
2672 
2673    if (!data_foreign)
2674      {
2675         ERR("Trying to adopt NULL domain data [data=%p in=%p]", data, data_in);
2676         return EFL_ID_DOMAIN_INVALID;
2677      }
2678    if (data_foreign->local_domain == data->local_domain)
2679      {
2680         ERR("Trying to adopt EO ID domain %i, is the same as the local %i [data=%p in=%p foreign=%p]",
2681             data_foreign->local_domain, data->local_domain, data, data_in, data_foreign);
2682         return EFL_ID_DOMAIN_INVALID;
2683      }
2684    if (data->tables[data_foreign->local_domain])
2685      {
2686         ERR("Trying to adopt an already adopted domain [data=%p in=%p foreign=%p]", data, data_in, data_foreign);
2687         return EFL_ID_DOMAIN_INVALID;
2688      }
2689    data->tables[data_foreign->local_domain] =
2690      data_foreign->tables[data_foreign->local_domain];
2691    _efl_domain_push(data, data_foreign->local_domain);
2692    return data->domain_stack[data->stack_top];
2693 }
2694 
2695 EAPI Eina_Bool
efl_domain_data_return(Efl_Id_Domain domain)2696 efl_domain_data_return(Efl_Id_Domain domain)
2697 {
2698    Eo_Id_Data *data = _eo_table_data_get();
2699 
2700    if ((domain < EFL_ID_DOMAIN_MAIN) || (domain > EFL_ID_DOMAIN_THREAD))
2701      {
2702         ERR("Invalid domain %i being returned to owning thread", domain);
2703         return EINA_FALSE;
2704      }
2705    if (domain == data->local_domain)
2706      {
2707         ERR("Cannot return the local domain %i back to its owner [data=%p]", domain, data);
2708         return EINA_FALSE;
2709      }
2710    data->tables[domain] = NULL;
2711    _efl_domain_pop(data);
2712    return EINA_TRUE;
2713 }
2714 
2715 EAPI Eina_Bool
efl_compatible(const Eo * obj,const Eo * obj_target)2716 efl_compatible(const Eo *obj, const Eo *obj_target)
2717 {
2718    Efl_Id_Domain domain1 = ((Eo_Id)obj >> SHIFT_DOMAIN) & MASK_DOMAIN;
2719    Efl_Id_Domain domain2 = ((Eo_Id)obj_target >> SHIFT_DOMAIN) & MASK_DOMAIN;
2720    if (domain1 == domain2) return EINA_TRUE;
2721    DBG("Object %p and %p are not compatible. Domain %i and %i do not match",
2722        obj, obj_target, domain1, domain2);
2723    return EINA_FALSE;
2724 }
2725 
2726 EAPI Eina_Bool
efl_class_override_register(const Efl_Class * klass,const Efl_Class * override)2727 efl_class_override_register(const Efl_Class *klass, const Efl_Class *override)
2728 {
2729    EINA_SAFETY_ON_NULL_RETURN_VAL(klass, EINA_FALSE);
2730    EINA_SAFETY_ON_NULL_RETURN_VAL(override, EINA_FALSE);
2731    EINA_SAFETY_ON_TRUE_RETURN_VAL(!efl_isa(override, klass), EINA_FALSE);
2732    if (!class_overrides)
2733      class_overrides = eina_hash_pointer_new(NULL);
2734    EINA_SAFETY_ON_NULL_RETURN_VAL(class_overrides, EINA_FALSE);
2735 
2736    eina_hash_set(class_overrides, &klass, override);
2737    return EINA_TRUE;
2738 }
2739 
2740 EAPI Eina_Bool
efl_class_override_unregister(const Efl_Class * klass,const Efl_Class * override)2741 efl_class_override_unregister(const Efl_Class *klass, const Efl_Class *override)
2742 {
2743    const Efl_Class *set;
2744    EINA_SAFETY_ON_NULL_RETURN_VAL(klass, EINA_FALSE);
2745    EINA_SAFETY_ON_NULL_RETURN_VAL(override, EINA_FALSE);
2746    if (!class_overrides) return EINA_TRUE;
2747 
2748    set = eina_hash_find(class_overrides, &klass);
2749    if (set != override) return EINA_FALSE;
2750    return eina_hash_del_by_key(class_overrides, &klass);
2751 }
2752 
2753 EAPI Eina_Bool
efl_destructed_is(const Eo * obj_id)2754 efl_destructed_is(const Eo *obj_id)
2755 {
2756    Eina_Bool is;
2757    EO_OBJ_POINTER_RETURN_VAL(obj_id, obj, EINA_FALSE);
2758    is = obj->destructed;
2759    EO_OBJ_DONE(obj_id);
2760    return is;
2761 }
2762 
2763 EAPI void
efl_manual_free_set(Eo * obj_id,Eina_Bool manual_free)2764 efl_manual_free_set(Eo *obj_id, Eina_Bool manual_free)
2765 {
2766    EO_OBJ_POINTER_RETURN(obj_id, obj);
2767    obj->manual_free = manual_free;
2768    EO_OBJ_DONE(obj_id);
2769 }
2770 
2771 EAPI Eina_Bool
efl_manual_free(Eo * obj_id)2772 efl_manual_free(Eo *obj_id)
2773 {
2774    EO_OBJ_POINTER_RETURN_VAL(obj_id, obj, EINA_FALSE);
2775 
2776    // rare to use goto to keep instruction cache cleaner
2777    if (obj->manual_free == EINA_FALSE) goto err_manual_free;
2778    // rare to use goto to keep instruction cache cleaner
2779    if (!obj->destructed) goto err_not_destructed;
2780    _eo_free(obj, EINA_TRUE);
2781    EO_OBJ_DONE(obj_id);
2782    return EINA_TRUE;
2783 
2784 err_manual_free:
2785    ERR("Tried to manually free the object %p while the option has not been set; see efl_manual_free_set for more information.", obj);
2786    goto err;
2787 err_not_destructed:
2788    ERR("Tried deleting the object %p while still referenced(%d).", obj_id, obj->refcount);
2789    goto err;
2790 err:
2791    EO_OBJ_DONE(obj_id);
2792    return EINA_FALSE;
2793 }
2794 
2795 EAPI const char *
efl_debug_name_get(const Eo * obj_id)2796 efl_debug_name_get(const Eo *obj_id)
2797 {
2798    const char *override = "";
2799    const char *name, *clsname;
2800    Eina_Strbuf *sb;
2801 
2802    if (!obj_id) return "(null)";
2803 
2804    if (_eo_is_a_class(obj_id))
2805      {
2806         const char *clstype;
2807 
2808         EO_CLASS_POINTER(obj_id, klass);
2809         if (!klass || !klass->desc)
2810           return eina_slstr_printf("Invalid_Class_ID(invalid)@%p", obj_id);
2811 
2812         switch (klass->desc->type)
2813           {
2814            case EFL_CLASS_TYPE_REGULAR: clstype = "regular"; break;
2815            case EFL_CLASS_TYPE_REGULAR_NO_INSTANT: clstype = "abstract"; break;
2816            case EFL_CLASS_TYPE_INTERFACE: clstype = "interface"; break;
2817            case EFL_CLASS_TYPE_MIXIN: clstype = "mixin"; break;
2818            default: clstype = "invalid"; break;
2819           }
2820 
2821         return eina_slstr_printf("%s(%s)@%p", klass->desc->name, clstype, obj_id);
2822      }
2823 
2824    EO_OBJ_POINTER(obj_id, obj);
2825    if (!obj) return eina_slstr_printf("Invalid_Object_ID@%p", obj_id);
2826 
2827    sb = eina_strbuf_new();
2828    name = efl_name_get(obj_id);
2829    clsname = obj->klass->desc->name;
2830    if (_obj_is_override(obj)) override = "(override)";
2831 
2832    if (name)
2833      eina_strbuf_append_printf(sb, "%s%s@%p[%d]:'%s'", clsname, override, obj_id, (int) obj->refcount, name);
2834    else
2835      eina_strbuf_append_printf(sb, "%s%s@%p[%d]", clsname, override, obj_id, (int) obj->refcount);
2836 
2837    if (!obj->cur_klass)
2838      {
2839         efl_debug_name_override((Eo *) obj_id, sb);
2840      }
2841    else
2842      {
2843         if (obj->super)
2844           efl_debug_name_override(efl_super(obj_id, (Efl_Class *) obj->cur_klass->header.id), sb);
2845         else
2846           efl_debug_name_override(efl_cast(obj_id, (Efl_Class *) obj->cur_klass->header.id), sb);
2847         obj->super = EINA_FALSE;
2848         obj->cur_klass = NULL;
2849      }
2850 
2851    EO_OBJ_DONE(obj_id);
2852    return eina_slstr_strbuf_new(sb);
2853 }
2854 
2855 EAPI int
efl_callbacks_cmp(const Efl_Callback_Array_Item * a,const Efl_Callback_Array_Item * b)2856 efl_callbacks_cmp(const Efl_Callback_Array_Item *a, const Efl_Callback_Array_Item *b)
2857 {
2858    if (a->desc == b->desc) return 0;
2859    else if (a->desc > b->desc) return 1;
2860    else return -1;
2861 }
2862 
2863 
2864 #ifdef EO_DEBUG
2865 /* NOTE: cannot use ecore_time_get()! */
2866 static inline double
_eo_log_time_now(void)2867 _eo_log_time_now(void)
2868 {
2869 #ifdef _WIN32
2870    return evil_time_get();
2871 #elif defined(__APPLE__) && defined(__MACH__)
2872    static double clk_conv = -1.0;
2873 
2874    if (EINA_UNLIKELY(clk_conv < 0))
2875      {
2876         mach_timebase_info_data_t info;
2877         kern_return_t err = mach_timebase_info(&info);
2878         if (err == 0)
2879           clk_conv = 1e-9 * (double)info.numer / (double)info.denom;
2880         else
2881           clk_conv = 1e-9;
2882      }
2883 
2884    return clk_conv * mach_absolute_time();
2885 #else
2886 #if defined (HAVE_CLOCK_GETTIME)
2887    struct timespec t;
2888    static int clk_id = -1;
2889 
2890    if (EINA_UNLIKELY(clk_id == -2)) goto try_gettimeofday;
2891    if (EINA_UNLIKELY(clk_id == -1))
2892      {
2893      retry_clk_id:
2894         clk_id = CLOCK_MONOTONIC;
2895         if (EINA_UNLIKELY(clock_gettime(clk_id, &t)))
2896           {
2897              WRN("CLOCK_MONOTONIC failed!");
2898              clk_id = CLOCK_REALTIME;
2899              if (EINA_UNLIKELY(clock_gettime(clk_id, &t)))
2900                {
2901                   WRN("CLOCK_REALTIME failed!");
2902                   clk_id = -2;
2903                   goto try_gettimeofday;
2904                }
2905           }
2906      }
2907    else
2908      {
2909         if (EINA_UNLIKELY(clock_gettime(clk_id, &t)))
2910           {
2911              WRN("clk_id=%d previously ok, now failed... retry", clk_id);
2912              goto retry_clk_id;
2913           }
2914      }
2915    return (double)t.tv_sec + (((double)t.tv_nsec) / 1000000000.0);
2916 
2917  try_gettimeofday:
2918 #endif
2919    {
2920       struct timeval timev;
2921 
2922       gettimeofday(&timev, NULL);
2923       return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000);
2924    }
2925 #endif
2926 }
2927 
2928 #ifdef HAVE_BACKTRACE
2929 typedef struct _Eo_Log_Obj_Entry {
2930    Eo_Id id;
2931    const _Eo_Object *obj;
2932    const _Efl_Class *klass;
2933    double timestamp;
2934    Eo_Ref_Op ref_op;
2935    unsigned bt_size;
2936    unsigned bt_hits;
2937    uintptr_t bt_hash;
2938    void *bt[];
2939 } Eo_Log_Obj_Entry;
2940 
2941 static void
_eo_log_obj_find(const Eo_Id id,const Eo_Log_Obj_Entry ** added,int * added_idx,const Eo_Log_Obj_Entry ** deleted)2942 _eo_log_obj_find(const Eo_Id id, const Eo_Log_Obj_Entry **added, int *added_idx, const Eo_Log_Obj_Entry **deleted)
2943 {
2944    const Eo_Log_Obj_Entry *entry;
2945    Eina_Array_Iterator it;
2946    unsigned int idx;
2947 
2948    *added_idx = -1;
2949    *added = NULL;
2950    *deleted = NULL;
2951 
2952    eina_spinlock_take(&_eo_log_objs_lock);
2953    EINA_ARRAY_ITER_NEXT(&_eo_log_objs, idx, entry, it)
2954      {
2955         if (EINA_UNLIKELY(id == entry->id))
2956           {
2957              if (entry->ref_op == EO_REF_OP_FREE)
2958                *deleted = entry;
2959              else if (entry->ref_op == EO_REF_OP_NEW)
2960                {
2961                   *added_idx = idx;
2962                   *added = entry;
2963                   *deleted = NULL; /* forget previous add, if any */
2964                }
2965           }
2966      }
2967    eina_spinlock_release(&_eo_log_objs_lock);
2968 }
2969 
2970 static void
_eo_log_obj_entry_show(const Eo_Log_Obj_Entry * entry,int log_level,const char * func_name,const char * file,int line,double now)2971 _eo_log_obj_entry_show(const Eo_Log_Obj_Entry *entry, int log_level, const char *func_name, const char *file, int line, double now)
2972 {
2973    unsigned i;
2974 
2975    eina_log_print(_eo_log_objs_dom, log_level, file, func_name, line,
2976                   "%s obj_id=%p obj=%p, class=%p (%s) [%0.4fs, %0.4fs ago] [%d hits]:",
2977                   _eo_ref_op_str[entry->ref_op],
2978                   (void *)entry->id,
2979                   entry->obj,
2980                   entry->klass,
2981                   entry->klass->desc->name,
2982                   entry->timestamp - _eo_log_time_start, now - entry->timestamp,
2983                   entry->bt_hits);
2984 
2985    // Skip EAPI and _eo_log_obj_ref_op()
2986    for (i = 2; i < entry->bt_size; i++)
2987      {
2988 #ifdef HAVE_DLADDR
2989         Dl_info info;
2990 
2991         if (dladdr(entry->bt[i], &info))
2992           {
2993              if (info.dli_sname)
2994                {
2995                   eina_log_print(_eo_log_objs_dom, log_level, file, func_name, line,
2996                                  "   0x%016llx: %s+%llu (in %s 0x%llx)",
2997                                  (unsigned long long)(uintptr_t)entry->bt[i],
2998                                  info.dli_sname,
2999                                  (unsigned long long)(uintptr_t)((char *)entry->bt[i] - (char *)info.dli_saddr),
3000                                  info.dli_fname ? info.dli_fname : "??",
3001                                  (unsigned long long)(uintptr_t)info.dli_fbase);
3002                   continue;
3003                }
3004              else if (info.dli_fname)
3005                {
3006                   const char *fname;
3007 
3008 #ifdef _WIN32
3009                   fname = strrchr(info.dli_fname, '\\');
3010 #else
3011                   fname = strrchr(info.dli_fname, '/');
3012 #endif
3013                   if (!fname) fname = info.dli_fname;
3014                   else fname++;
3015 
3016                   eina_log_print(_eo_log_objs_dom, log_level, file, func_name, line,
3017                                  "   0x%016llx: %s+%llu (in %s 0x%llx)",
3018                                  (unsigned long long)(uintptr_t)entry->bt[i],
3019                                  fname,
3020                                  (unsigned long long)(uintptr_t)((char *)entry->bt[i] - (char *)info.dli_fbase),
3021                                  info.dli_fname,
3022                                  (unsigned long long)(uintptr_t)info.dli_fbase);
3023                   continue;
3024                }
3025           }
3026 #endif
3027 
3028         eina_log_print(_eo_log_objs_dom, log_level, func_name, file, line,
3029                        "   0x%016llx", (unsigned long long)(uintptr_t)entry->bt[i]);
3030      }
3031 }
3032 #endif
3033 
3034 #ifdef HAVE_BACKTRACE
3035 static uintptr_t
_eo_log_obj_backtrace_hash(Eo_Log_Obj_Entry * entry)3036 _eo_log_obj_backtrace_hash(Eo_Log_Obj_Entry *entry)
3037 {
3038    if (!entry->bt_hash)
3039      {
3040         entry->bt_hash = (uintptr_t) 4294967291;
3041         for (unsigned k = 0; k < entry->bt_size; k++)
3042           entry->bt_hash ^= ((uintptr_t) (entry->bt[k]));
3043      }
3044 
3045    return entry->bt_hash;
3046 }
3047 
3048 static Eina_Bool
_eo_log_obj_entry_is_new_backtrace(const Eina_List * entries,Eo_Log_Obj_Entry * entry)3049 _eo_log_obj_entry_is_new_backtrace(const Eina_List *entries, Eo_Log_Obj_Entry *entry)
3050 {
3051    Eina_Bool ret = EINA_TRUE;
3052    Eo_Log_Obj_Entry *other;
3053    const Eina_List *li;
3054    uintptr_t hash;
3055 
3056    hash = _eo_log_obj_backtrace_hash(entry);
3057    EINA_LIST_FOREACH(entries, li, other)
3058      if (_eo_log_obj_backtrace_hash(other) == hash)
3059        {
3060           other->bt_hits++;
3061           ret = EINA_FALSE;
3062        }
3063 
3064    return ret;
3065 }
3066 
3067 static Eina_List *
_eo_log_obj_find_all(const Eo_Id id,int start_idx)3068 _eo_log_obj_find_all(const Eo_Id id, int start_idx)
3069 {
3070    Eo_Log_Obj_Entry *entry;
3071    Eina_List *entries = NULL;
3072    unsigned int idx;
3073 
3074    eina_spinlock_take(&_eo_log_objs_lock);
3075    for (idx = start_idx + 1; idx < eina_array_count(&_eo_log_objs); idx++)
3076      {
3077         entry = eina_array_data_get(&_eo_log_objs, idx);
3078         if (entry->id != id) continue;
3079         if (entry->ref_op > _eo_log_objs_level) continue;
3080         if (entry->ref_op == EO_REF_OP_FREE) break;
3081         if (_eo_log_obj_entry_is_new_backtrace(entries, entry))
3082           entries = eina_list_append(entries, entry);
3083      }
3084    eina_spinlock_release(&_eo_log_objs_lock);
3085    return entries;
3086 }
3087 #endif
3088 
3089 inline void
_eo_log_obj_report(const Eo_Id id,int log_level,const char * func_name,const char * file,int line)3090 _eo_log_obj_report(const Eo_Id id, int log_level, const char *func_name, const char *file, int line)
3091 {
3092 #ifdef HAVE_BACKTRACE
3093    const Eo_Log_Obj_Entry *added, *deleted;
3094    Eo_Log_Obj_Entry *current = NULL;
3095    int added_idx = -1;
3096    double now;
3097 
3098    if (EINA_LIKELY(!_eo_log_objs_level)) return;
3099 
3100    _eo_log_obj_find(id, &added, &added_idx, &deleted);
3101 
3102    if ((!added) && (!deleted))
3103      {
3104         if ((!_eo_log_objs_debug.len) && (!_eo_log_objs_no_debug.len))
3105           {
3106              eina_log_print(_eo_log_objs_dom, log_level, file, func_name, line,
3107                             "obj_id=%p was neither created or deleted.", (void *)id);
3108           }
3109         else if ((_eo_log_objs_debug.len) && (_eo_log_objs_no_debug.len))
3110           {
3111              eina_log_print(_eo_log_objs_dom, log_level, file, func_name, line,
3112                             "obj_id=%p was neither created or deleted (EO_LIFECYCLE_DEBUG='%s', EO_LIFECYCLE_NO_DEBUG='%s').",
3113                             (void *)id, getenv("EO_LIFECYCLE_DEBUG"), getenv("EO_LIFECYCLE_NO_DEBUG"));
3114           }
3115         else if (_eo_log_objs_debug.len)
3116           {
3117              eina_log_print(_eo_log_objs_dom, log_level, file, func_name, line,
3118                             "obj_id=%p was neither created or deleted (EO_LIFECYCLE_DEBUG='%s').",
3119                             (void *)id, getenv("EO_LIFECYCLE_DEBUG"));
3120           }
3121         else
3122           {
3123              eina_log_print(_eo_log_objs_dom, log_level, file, func_name, line,
3124                             "obj_id=%p was neither created or deleted (EO_LIFECYCLE_NO_DEBUG='%s').",
3125                             (void *)id, getenv("EO_LIFECYCLE_NO_DEBUG"));
3126           }
3127         return;
3128      }
3129 
3130    now = _eo_log_time_now();
3131 
3132 #ifdef HAVE_BACKTRACE
3133    if ((_eo_log_objs_backtrace >= 0) && deleted)
3134      {
3135         void **bt = NULL;
3136         int size = 0;
3137 
3138         if (_eo_log_objs_backtrace > 0)
3139           {
3140              bt = alloca(sizeof(void *) * (_eo_log_objs_backtrace + 2));
3141              size = backtrace(bt, (_eo_log_objs_backtrace + 2));
3142              if (EINA_UNLIKELY(size < 1))
3143                {
3144                   bt = NULL;
3145                   size = 0;
3146                }
3147           }
3148 
3149         current = calloc(1, sizeof(Eo_Log_Obj_Entry) + size * sizeof(void *));
3150         if (EINA_UNLIKELY(!current)) return;
3151 
3152         current->id = id;
3153         current->timestamp = now;
3154         current->obj = deleted->obj;
3155         current->klass = deleted->klass;
3156         current->ref_op = EO_REF_OP_NONE;
3157         current->bt_size = size;
3158         current->bt_hash = 0;
3159         current->bt_hits = 1;
3160         if (bt && size)
3161           memcpy(current->bt, bt, size * sizeof(void *));
3162      }
3163 #endif
3164 
3165    if (added)
3166      {
3167         _eo_log_obj_entry_show(added, log_level, func_name, file, line, now);
3168 
3169         if (_eo_log_objs_level > EO_REF_OP_FREE)
3170           {
3171              Eina_List *entries = _eo_log_obj_find_all(id, added_idx);
3172              const Eo_Log_Obj_Entry *entry;
3173 
3174              EINA_LIST_FREE(entries, entry)
3175                _eo_log_obj_entry_show(entry, log_level, func_name, file, line, now);
3176           }
3177      }
3178 
3179    if (deleted)
3180      {
3181         _eo_log_obj_entry_show(deleted, log_level, func_name, file, line, now);
3182         eina_log_print(_eo_log_objs_dom, log_level, file, func_name, line,
3183                        "obj_id=%p was already deleted %0.4f seconds ago!",
3184                        (void *)id, now - deleted->timestamp);
3185      }
3186 
3187    if (current)
3188      {
3189         eina_log_print(_eo_log_objs_dom, log_level, file, func_name, line,
3190                        "obj_id=%p current use from:", (void *)id);
3191         _eo_log_obj_entry_show(current, log_level, func_name, file, line, now);
3192         free(current);
3193      }
3194 
3195 #else
3196    (void)id;
3197    (void)log_level;
3198    (void)func_name;
3199    (void)file;
3200    (void)line;
3201 #endif
3202 }
3203 
3204 #ifdef HAVE_BACKTRACE
3205 static Eo_Log_Obj_Entry *
_eo_log_obj_entry_ref_op(const _Eo_Object * obj,Eo_Ref_Op refop,unsigned size,void * const * bt)3206 _eo_log_obj_entry_ref_op(const _Eo_Object *obj, Eo_Ref_Op refop, unsigned size, void *const *bt)
3207 {
3208    Eo_Log_Obj_Entry *entry;
3209    Eina_Bool ret;
3210 
3211    entry = calloc(1, sizeof(Eo_Log_Obj_Entry) + size * sizeof(void *));
3212    if (EINA_UNLIKELY(!entry)) return NULL;
3213 
3214    entry->id = (Eo_Id)_eo_obj_id_get(obj);
3215    entry->timestamp = _eo_log_time_now();
3216    entry->obj = obj;
3217    entry->klass = obj->klass;
3218    entry->ref_op = refop;
3219    entry->bt_size = size;
3220    entry->bt_hash = 0;
3221    entry->bt_hits = 1;
3222    if (size && bt)
3223      memcpy(entry->bt, bt, size * sizeof(void *));
3224 
3225    eina_spinlock_take(&_eo_log_objs_lock);
3226    ret = eina_array_push(&_eo_log_objs, entry);
3227    eina_spinlock_release(&_eo_log_objs_lock);
3228    if (!ret)
3229      {
3230         free(entry);
3231         return NULL;
3232      }
3233 
3234    return entry;
3235 }
3236 
3237 static inline void
_eo_log_obj_entry_free(Eo_Log_Obj_Entry * entry)3238 _eo_log_obj_entry_free(Eo_Log_Obj_Entry *entry)
3239 {
3240    free(entry);
3241 }
3242 #endif
3243 
3244 static int
_eo_class_name_slice_cmp(const void * pa,const void * pb)3245 _eo_class_name_slice_cmp(const void *pa, const void *pb)
3246 {
3247    const Eina_Slice *a = pa;
3248    const Eina_Slice *b = pb;
3249 
3250    if (a->len < b->len) return -1;
3251    if (a->len > b->len) return 1;
3252    return memcmp(a->mem, b->mem, a->len);
3253 }
3254 
3255 static Eina_Bool
_eo_log_obj_desired(const _Eo_Object * obj)3256 _eo_log_obj_desired(const _Eo_Object *obj)
3257 {
3258    Eina_Slice cls_name;
3259 
3260    if (EINA_LIKELY((_eo_log_objs_debug.len == 0) &&
3261                    (_eo_log_objs_no_debug.len == 0)))
3262      return EINA_TRUE;
3263 
3264    cls_name.mem = obj->klass->desc->name;
3265    cls_name.len = strlen(cls_name.mem);
3266 
3267    if (_eo_log_objs_no_debug.len)
3268      {
3269         if (eina_inarray_search_sorted(&_eo_log_objs_no_debug, &cls_name, _eo_class_name_slice_cmp) >= 0)
3270           return EINA_FALSE;
3271      }
3272 
3273    if (!_eo_log_objs_debug.len)
3274      return EINA_TRUE;
3275 
3276    if (eina_inarray_search_sorted(&_eo_log_objs_debug, &cls_name, _eo_class_name_slice_cmp) >= 0)
3277      return EINA_TRUE;
3278 
3279    return EINA_FALSE;
3280 }
3281 
3282 static void
_eo_log_obj_ref_op(const _Eo_Object * obj,Eo_Ref_Op ref_op)3283 _eo_log_obj_ref_op(const _Eo_Object *obj, Eo_Ref_Op ref_op)
3284 {
3285    if (EINA_LIKELY(_eo_log_objs_level < ref_op)) return;
3286    if (EINA_LIKELY(!_eo_log_obj_desired(obj))) return;
3287 
3288 #ifdef HAVE_BACKTRACE
3289    if (_eo_log_objs_backtrace >= 0)
3290      {
3291         void **bt = NULL;
3292         int size = 0;
3293 
3294         if (_eo_log_objs_backtrace > 0)
3295           {
3296              bt = alloca(sizeof(void *) * (_eo_log_objs_backtrace + 2));
3297              size = backtrace(bt, (_eo_log_objs_backtrace + 2));
3298              if (EINA_UNLIKELY(size < 1)) return;
3299           }
3300 
3301         _eo_log_obj_entry_ref_op(obj, ref_op, size, bt);
3302      }
3303 #endif
3304 
3305    EINA_LOG_DOM_DBG(_eo_log_objs_dom,
3306                     "%s obj_id=%p class=%p (%s) [%0.4f]",
3307                     _eo_ref_op_str[ref_op], _eo_obj_id_get(obj),
3308                     obj->klass, obj->klass->desc->name,
3309                     _eo_log_time_now() - _eo_log_time_start);
3310 }
3311 
3312 static inline void
_eo_log_obj_init(void)3313 _eo_log_obj_init(void)
3314 {
3315    const char *s;
3316 
3317    _eo_log_objs_dom = eina_log_domain_register("eo_lifecycle", EINA_COLOR_BLUE);
3318    _eo_log_time_start = _eo_log_time_now();
3319 
3320 #ifdef HAVE_BACKTRACE
3321    eina_array_step_set(&_eo_log_objs, sizeof(Eina_Array), 4096);
3322    eina_spinlock_new(&_eo_log_objs_lock);
3323 #endif
3324    eina_inarray_step_set(&_eo_log_objs_debug, sizeof(Eina_Inarray), sizeof(Eina_Slice), 0);
3325    eina_inarray_step_set(&_eo_log_objs_no_debug, sizeof(Eina_Inarray), sizeof(Eina_Slice), 0);
3326 
3327    s = getenv("EO_LIFECYCLE_BACKTRACE");
3328    if (s && *s)
3329      {
3330         _eo_log_objs_backtrace = atoi(s);
3331         if (_eo_log_objs_backtrace > EO_LOG_OBJS_BACKTRACE_MAX)
3332           _eo_log_objs_backtrace = EO_LOG_OBJS_BACKTRACE_MAX;
3333         else if (_eo_log_objs_backtrace < 0)
3334           _eo_log_objs_backtrace = 0;
3335      }
3336    else
3337      _eo_log_objs_backtrace = EO_LOG_OBJS_BACKTRACE_DEFAULT;
3338 
3339    s = getenv("EO_LIFECYCLE_DEBUG");
3340    if ((s) && (s[0] != '\0'))
3341      {
3342         char *es;
3343         int lvl = (int)strtol(s, &es, 10);
3344         _eo_log_objs_level = EO_REF_OP_FREE;
3345         if ((es != s) && (*es == ':'))
3346           {
3347              if (lvl >= 3)
3348                {
3349                   _eo_log_objs_level = EO_REF_OP_REUSE;
3350                   EINA_LOG_DOM_DBG(_eo_log_objs_dom,
3351                                    "will log new, free, ref, unref and reuse");
3352                }
3353              else if (lvl == 2)
3354                {
3355                   _eo_log_objs_level = EO_REF_OP_UNREF;
3356                   EINA_LOG_DOM_DBG(_eo_log_objs_dom,
3357                                    "will log new, free, ref and unref");
3358                }
3359              s = es + 1;
3360           }
3361 
3362         if ((strcmp(s, "*") == 0) || (strcmp(s, "1") == 0))
3363           {
3364              EINA_LOG_DOM_DBG(_eo_log_objs_dom,
3365                               "will log all object allocation and free");
3366           }
3367         else
3368           {
3369              Eina_Slice slice;
3370              const Eina_Slice *itr;
3371              do
3372                {
3373                   char *p = strchr(s, ',');
3374                   slice.mem = s;
3375                   if (p)
3376                     {
3377                        slice.len = p - s;
3378                        s = p + 1;
3379                     }
3380                   else
3381                     {
3382                        slice.len = strlen(s);
3383                        s = NULL;
3384                     }
3385                   eina_inarray_push(&_eo_log_objs_debug, &slice);
3386                }
3387              while (s);
3388              eina_inarray_sort(&_eo_log_objs_debug, _eo_class_name_slice_cmp);
3389 
3390              EINA_INARRAY_FOREACH(&_eo_log_objs_debug, itr)
3391                {
3392                   EINA_LOG_DOM_DBG(_eo_log_objs_dom,
3393                                 "will log class '" EINA_SLICE_STR_FMT "'",
3394                                    EINA_SLICE_STR_PRINT(*itr));
3395                }
3396           }
3397 #ifndef HAVE_BACKTRACE
3398         WRN("EO_LIFECYCLE_DEBUG='%s' but your system has no backtrace()!", s);
3399 #endif
3400      }
3401 
3402    if (EINA_LIKELY(!_eo_log_objs_level)) return;
3403 
3404    DBG("logging object allocation and free, use EINA_LOG_LEVELS=eo_lifecycle:4");
3405 
3406    s = getenv("EO_LIFECYCLE_NO_DEBUG");
3407    if ((s) && (s[0] != '\0'))
3408      {
3409         if ((strcmp(s, "*") == 0) || (strcmp(s, "1") == 0))
3410           {
3411              EINA_LOG_DOM_ERR(_eo_log_objs_dom,
3412                               "expected class names to not log allocation and free, got '%s'", s);
3413           }
3414         else
3415           {
3416              Eina_Slice slice;
3417              const Eina_Slice *itr;
3418              do
3419                {
3420                   char *p = strchr(s, ',');
3421                   slice.mem = s;
3422                   if (p)
3423                     {
3424                        slice.len = p - s;
3425                        s = p + 1;
3426                     }
3427                   else
3428                     {
3429                        slice.len = strlen(s);
3430                        s = NULL;
3431                     }
3432                   eina_inarray_push(&_eo_log_objs_no_debug, &slice);
3433                }
3434              while (s);
3435              eina_inarray_sort(&_eo_log_objs_no_debug, _eo_class_name_slice_cmp);
3436 
3437              EINA_INARRAY_FOREACH(&_eo_log_objs_no_debug, itr)
3438                {
3439                   EINA_LOG_DOM_DBG(_eo_log_objs_dom,
3440                                    "will NOT log class '" EINA_SLICE_STR_FMT "'",
3441                                    EINA_SLICE_STR_PRINT(*itr));
3442                }
3443           }
3444      }
3445 }
3446 
3447 static inline void
_eo_log_obj_shutdown(void)3448 _eo_log_obj_shutdown(void)
3449 {
3450 #ifdef HAVE_BACKTRACE
3451    Eo_Log_Obj_Entry *entry;
3452    Eina_Array_Iterator it;
3453    unsigned int idx;
3454 
3455    eina_spinlock_take(&_eo_log_objs_lock);
3456    if (eina_log_domain_level_check(_eo_log_objs_dom, EINA_LOG_LEVEL_INFO))
3457      {
3458         void * const *itr = _eo_log_objs.data;
3459         void * const *itr_end = itr + _eo_log_objs.count;
3460         double now = _eo_log_time_now();
3461         size_t leaks = 0;
3462 
3463         for (; itr < itr_end; itr++)
3464           {
3465              void * const *cur;
3466              entry = *itr;
3467              if (entry->ref_op == EO_REF_OP_FREE) continue;
3468              for (cur = itr + 1; cur < itr_end; cur++)
3469                {
3470                   const Eo_Log_Obj_Entry *cur_entry = *cur;
3471                   if (EINA_UNLIKELY((cur_entry->id == entry->id) && (cur_entry->ref_op == EO_REF_OP_FREE)))
3472                     break;
3473                }
3474              if (EINA_UNLIKELY(cur == itr_end))
3475                {
3476                   EINA_LOG_DOM_INFO(_eo_log_objs_dom,
3477                                     "leaking obj_id=%p obj=%p class=%p (%s) [%0.4fs, %0.4f ago]",
3478                                     (void *)entry->id,
3479                                     entry->obj,
3480                                     entry->klass,
3481                                     entry->klass->desc->name,
3482                                     entry->timestamp - _eo_log_time_start, now - entry->timestamp);
3483                   _eo_log_obj_entry_show(entry, EINA_LOG_LEVEL_DBG, __func__, __FILE__, __LINE__, now);
3484                   leaks++;
3485                }
3486           }
3487         if (leaks)
3488           EINA_LOG_DOM_WARN(_eo_log_objs_dom, "Leaked %zd objects! Check details with EINA_LOG_LEVELS=eo_lifecycle:4", leaks);
3489         else
3490           EINA_LOG_DOM_INFO(_eo_log_objs_dom, "No leaked objects!");
3491      }
3492    EINA_ARRAY_ITER_NEXT(&_eo_log_objs, idx, entry, it)
3493      _eo_log_obj_entry_free(entry);
3494    eina_array_flush(&_eo_log_objs);
3495    eina_spinlock_release(&_eo_log_objs_lock);
3496    eina_spinlock_free(&_eo_log_objs_lock);
3497 #endif
3498 
3499    eina_inarray_flush(&_eo_log_objs_debug);
3500    eina_inarray_flush(&_eo_log_objs_no_debug);
3501 }
3502 #endif
3503 
3504 typedef struct
3505 {
3506    Eina_Iterator iterator;
3507    unsigned int cur_kl_id;
3508 } _Eo_Classes_Iterator;
3509 
3510 static Eina_Bool
_eo_classes_iterator_next(Eina_Iterator * it,void ** data)3511 _eo_classes_iterator_next(Eina_Iterator *it, void **data)
3512 {
3513    _Eo_Classes_Iterator *eo_it = (_Eo_Classes_Iterator *)it;
3514 
3515    if (eo_it->cur_kl_id == _eo_classes_last_id) return EINA_FALSE;
3516    *data = _eo_class_id_get(_eo_classes[eo_it->cur_kl_id]);
3517    eo_it->cur_kl_id++;
3518    return EINA_TRUE;
3519 }
3520 
3521 static void
_eo_classes_iterator_free(Eina_Iterator * it)3522 _eo_classes_iterator_free(Eina_Iterator *it)
3523 {
3524    EINA_MAGIC_SET(it, EINA_MAGIC_NONE);
3525    free(it);
3526 }
3527 
3528 EAPI Eina_Iterator *
eo_classes_iterator_new(void)3529 eo_classes_iterator_new(void)
3530 {
3531    _Eo_Classes_Iterator *it;
3532 
3533    it = calloc(1, sizeof (*it));
3534    if (!it) return NULL;
3535 
3536    it->iterator.version = EINA_ITERATOR_VERSION;
3537    it->iterator.next = _eo_classes_iterator_next;
3538    it->iterator.free = _eo_classes_iterator_free;
3539    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
3540 
3541    return (Eina_Iterator *)it;
3542 }
3543 
3544 typedef struct
3545 {
3546    Eina_Iterator iterator;
3547    Eo_Id_Table_Data *tdata;
3548    Table_Index mid_table_id;
3549    Table_Index table_id;
3550    Table_Index entry_id;
3551 } _Eo_Objects_Iterator;
3552 
3553 static Eina_Bool
_eo_objects_iterator_next(Eina_Iterator * it,void ** data)3554 _eo_objects_iterator_next(Eina_Iterator *it, void **data)
3555 {
3556    Table_Index mid_table_id, table_id, entry_id;
3557    Eo_Id_Table_Data *tdata;
3558    _Eo_Objects_Iterator *eo_it = (_Eo_Objects_Iterator *)it;
3559    if (!eo_it->tdata) return EINA_FALSE;
3560 
3561    tdata = eo_it->tdata;
3562    mid_table_id = eo_it->mid_table_id;
3563    table_id = eo_it->table_id;
3564    entry_id = eo_it->entry_id;
3565    while (mid_table_id < MAX_MID_TABLE_ID)
3566      {
3567         if (tdata->eo_ids_tables[mid_table_id])
3568           {
3569              while (table_id < MAX_TABLE_ID)
3570                {
3571                   if (TABLE_FROM_IDS)
3572                     {
3573                        while (entry_id < MAX_ENTRY_ID)
3574                          {
3575                             _Eo_Id_Entry *entry = &(TABLE_FROM_IDS->entries[entry_id]);
3576                             if (entry->active)
3577                               {
3578                                  Eo *obj = _eo_header_id_get((Eo_Header *) entry->ptr);
3579                                  *data = obj;
3580                                  eo_it->mid_table_id = mid_table_id;
3581                                  eo_it->table_id = table_id;
3582                                  eo_it->entry_id = entry_id + 1;
3583                                  return EINA_TRUE;
3584                               }
3585                             entry_id++;
3586                          }
3587                        entry_id = 0;
3588                     }
3589                   table_id++;
3590                }
3591              table_id = 0;
3592           }
3593         mid_table_id++;
3594      }
3595    return EINA_FALSE;
3596 }
3597 
3598 static void
_eo_objects_iterator_free(Eina_Iterator * it)3599 _eo_objects_iterator_free(Eina_Iterator *it)
3600 {
3601    EINA_MAGIC_SET(it, EINA_MAGIC_NONE);
3602    free(it);
3603 }
3604 
3605 EAPI Eina_Iterator *
eo_objects_iterator_new(void)3606 eo_objects_iterator_new(void)
3607 {
3608    _Eo_Objects_Iterator *it;
3609    Eo_Id_Table_Data *tdata = _eo_table_data_table_get(_eo_table_data_get(), EFL_ID_DOMAIN_MAIN);
3610 
3611    if (!tdata) return NULL;
3612 
3613    it = calloc(1, sizeof (*it));
3614    if (!it) return NULL;
3615 
3616    it->tdata = tdata;
3617    it->iterator.version = EINA_ITERATOR_VERSION;
3618    it->iterator.next = _eo_objects_iterator_next;
3619    it->iterator.free = _eo_objects_iterator_free;
3620    EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);
3621 
3622    return (Eina_Iterator *)it;
3623 }
3624 
3625 static Eina_Bool
_eo_value_setup(const Eina_Value_Type * type EINA_UNUSED,void * mem)3626 _eo_value_setup(const Eina_Value_Type *type EINA_UNUSED, void *mem)
3627 {
3628    Eo **tmem = mem;
3629    *tmem = NULL;
3630    return EINA_TRUE;
3631 }
3632 
3633 static Eina_Bool
_eo_value_flush(const Eina_Value_Type * type EINA_UNUSED,void * mem)3634 _eo_value_flush(const Eina_Value_Type *type EINA_UNUSED, void *mem)
3635 {
3636    Eo **tmem = mem;
3637    if (*tmem)
3638      {
3639         efl_unref(*tmem);
3640         *tmem = NULL;
3641      }
3642    return EINA_TRUE;
3643 }
3644 
3645 static Eina_Bool
_eo_value_vset(const Eina_Value_Type * type EINA_UNUSED,void * mem,va_list args)3646 _eo_value_vset(const Eina_Value_Type *type EINA_UNUSED, void *mem, va_list args)
3647 {
3648    Eo **dst = mem;
3649    Eo *src = va_arg(args, Eo *);
3650    efl_replace(dst, src);
3651    return EINA_TRUE;
3652 }
3653 
3654 static Eina_Bool
_eo_value_pset(const Eina_Value_Type * type EINA_UNUSED,void * mem,const void * ptr)3655 _eo_value_pset(const Eina_Value_Type *type EINA_UNUSED,
3656               void *mem, const void *ptr)
3657 {
3658    Eo **dst = mem;
3659    Eo * const *src = ptr;
3660    efl_replace(dst, *src);
3661    return EINA_TRUE;
3662 }
3663 
3664 static Eina_Bool
_eo_value_pget(const Eina_Value_Type * type EINA_UNUSED,const void * mem,void * ptr)3665 _eo_value_pget(const Eina_Value_Type *type EINA_UNUSED,
3666               const void *mem, void *ptr)
3667 {
3668    Eo * const *src = mem;
3669    Eo **dst = ptr;
3670    *dst = *src;
3671    return EINA_TRUE;
3672 }
3673 
3674 static Eina_Bool
_eo_value_convert_to(const Eina_Value_Type * type EINA_UNUSED,const Eina_Value_Type * convert,const void * type_mem,void * convert_mem)3675 _eo_value_convert_to(const Eina_Value_Type *type EINA_UNUSED, const Eina_Value_Type *convert, const void *type_mem, void *convert_mem)
3676 {
3677    Eo * const *eo = type_mem;
3678 
3679    if (convert == EINA_VALUE_TYPE_STRINGSHARE ||
3680        convert == EINA_VALUE_TYPE_STRING)
3681      {
3682         const char *other_mem;
3683         char buf[256];
3684         snprintf(buf, sizeof(buf), "Object id: %p, class: %s, name: %s",
3685                  *eo, efl_class_name_get(efl_class_get(*eo)),
3686                  efl_debug_name_get(*eo));
3687         other_mem = buf;
3688         return eina_value_type_pset(convert, convert_mem, &other_mem);
3689      }
3690    return EINA_FALSE;
3691 }
3692 
3693 static Eina_Bool
_eo_value_copy(const Eina_Value_Type * type EINA_UNUSED,const void * mem,void * ptr)3694 _eo_value_copy(const Eina_Value_Type *type EINA_UNUSED, const void *mem, void *ptr)
3695 {
3696    Eo * const *src = mem;
3697    Eo **dst = ptr;
3698 
3699    if (!src || !dst) return EINA_FALSE;
3700    *dst = efl_ref(*src);
3701 
3702    return EINA_TRUE;
3703 }
3704 
3705 static const Eina_Value_Type _EINA_VALUE_TYPE_OBJECT = {
3706   .version = EINA_VALUE_TYPE_VERSION,
3707   .value_size = sizeof(Eo *),
3708   .name = "Efl_Object",
3709   .setup = _eo_value_setup,
3710   .flush = _eo_value_flush,
3711   .copy = _eo_value_copy,
3712   .compare = NULL,
3713   .convert_to = _eo_value_convert_to,
3714   .convert_from = NULL,
3715   .vset = _eo_value_vset,
3716   .pset = _eo_value_pset,
3717   .pget = _eo_value_pget
3718 };
3719 
3720 EOAPI const Eina_Value_Type *EINA_VALUE_TYPE_OBJECT = &_EINA_VALUE_TYPE_OBJECT;
3721 
3722 static const Efl_Object_Property_Reflection*
_efl_class_reflection_find(const _Efl_Class * klass,const char * property_name)3723 _efl_class_reflection_find(const _Efl_Class *klass, const char *property_name)
3724 {
3725    const _Efl_Class **klass_iter = klass->extensions;
3726    const Efl_Object_Property_Reflection_Ops *ref_ops = klass->reflection;
3727    unsigned int i;
3728 
3729    for (i = 0; ref_ops && i < ref_ops->count; ++i)
3730      {
3731         if (eina_streq(property_name, ref_ops->table[i].property_name))
3732           return &ref_ops->table[i];
3733      }
3734 
3735    if (klass->parent)
3736      {
3737         const Efl_Object_Property_Reflection *ref;
3738 
3739         ref = _efl_class_reflection_find(klass->parent, property_name);
3740         if (ref) return ref;
3741      }
3742 
3743    for (; *klass_iter; klass_iter++)
3744      {
3745         const Efl_Object_Property_Reflection *ref;
3746 
3747         ref = _efl_class_reflection_find(*klass_iter, property_name);
3748         if (ref) return ref;
3749      }
3750 
3751    return NULL;
3752 }
3753 
3754 EAPI Eina_Error
efl_property_reflection_set(Eo * obj_id,const char * property_name,Eina_Value value)3755 efl_property_reflection_set(Eo *obj_id, const char *property_name, Eina_Value value)
3756 {
3757    Eina_Error r = EINA_ERROR_NOT_IMPLEMENTED;
3758    Eina_Bool freed = EINA_FALSE;
3759 
3760    EO_OBJ_POINTER_GOTO(obj_id, obj, end);
3761    const Efl_Object_Property_Reflection *reflection = _efl_class_reflection_find(obj->klass, property_name);
3762 
3763    if (reflection && reflection->set)
3764      {
3765         r = reflection->set(obj_id, value);
3766         freed = EINA_TRUE;
3767      }
3768 
3769  end:
3770    if (!freed) eina_value_flush(&value);
3771    EO_OBJ_DONE(obj_id);
3772    return r;
3773 }
3774 
3775 EAPI Eina_Value
efl_property_reflection_get(const Eo * obj_id,const char * property_name)3776 efl_property_reflection_get(const Eo *obj_id, const char *property_name)
3777 {
3778    Eina_Value r = eina_value_error_init(EINA_ERROR_NOT_IMPLEMENTED);
3779 
3780    EO_OBJ_POINTER_GOTO(obj_id, obj, end);
3781    const Efl_Object_Property_Reflection *reflection = _efl_class_reflection_find(obj->klass, property_name);
3782 
3783    if (reflection && reflection->get)
3784      r = reflection->get(obj_id);
3785 
3786  end:
3787    EO_OBJ_DONE(obj_id);
3788 
3789    return r;
3790 }
3791 
3792 EAPI Eina_Bool
efl_property_reflection_exist(Eo * obj_id,const char * property_name)3793 efl_property_reflection_exist(Eo *obj_id, const char *property_name)
3794 {
3795    Eina_Bool r = EINA_FALSE;
3796    EO_OBJ_POINTER_GOTO(obj_id, obj, end);
3797    const Efl_Object_Property_Reflection *reflection = _efl_class_reflection_find(obj->klass, property_name);
3798 
3799    if (reflection) r = EINA_TRUE;
3800  end:
3801    EO_OBJ_DONE(obj_id);
3802    return r;
3803 }
3804 
3805 EAPI Efl_Class_Type
efl_class_type_get(const Efl_Class * klass_id)3806 efl_class_type_get(const Efl_Class *klass_id)
3807 {
3808    EO_CLASS_POINTER_RETURN_VAL(klass_id, klass, EFL_CLASS_TYPE_INVALID);
3809 
3810    return klass->desc->type;
3811 }
3812 
3813 
3814 EAPI Eina_Bool
efl_ownable_get(const Eo * obj)3815 efl_ownable_get(const Eo *obj)
3816 {
3817    int ref = efl_ref_count(obj);
3818 
3819    if (efl_parent_get(obj))
3820      ref --;
3821 
3822    if (ref <= 0)
3823      ERR("There is no free reference to pass this object. Please check that this object is really owned by you.");
3824    return (ref > 0);
3825 }
3826