Eobj: Add named (by obj ptr) ref - xref.
[profile/ivi/eobj.git] / lib / eobj.c
1 #include <Eina.h>
2
3 #include "Eobj.h"
4 #include "eobj_private.h"
5
6 #include "config.h"
7
8 typedef int Eobj_Class_Id;
9
10 int _eobj_log_dom = -1;
11
12 static Eobj_Class **_eobj_classes;
13 static Eobj_Class_Id _eobj_classes_last_id;
14 static Eina_Bool _eobj_init_count = 0;
15
16 static void _eobj_callback_remove_all(Eobj *obj);
17 static void eobj_class_constructor(Eobj *obj, const Eobj_Class *klass);
18 static void eobj_class_destructor(Eobj *obj, const Eobj_Class *klass);
19 static void eobj_constructor_error_unset(Eobj *obj);
20
21 typedef struct _Eobj_Callback_Description Eobj_Callback_Description;
22
23 struct _Eobj {
24      EINA_MAGIC
25      Eobj *parent;
26      const Eobj_Class *klass;
27      void *data_blob;
28      int refcount;
29 #ifndef NDEBUG
30      Eina_Inlist *xrefs;
31 #endif
32
33      Eina_List *composite_objects;
34
35      Eina_Inlist *callbacks;
36      int walking_list;
37
38      Eina_Inlist *kls_itr;
39
40      Eina_Bool delete:1;
41      Eina_Bool construct_error:1;
42 };
43
44 /* Start of Dich */
45 /* Dich search, split to 0xff 0xff 0xffff */
46
47 #define DICH_CHAIN1_MASK (0xff)
48 #define DICH_CHAIN2_MASK (0xff)
49 #define DICH_CHAIN_LAST_MASK (0xffff)
50 #define DICH_CHAIN1_SIZE (DICH_CHAIN1_MASK + 1)
51 #define DICH_CHAIN2_SIZE (DICH_CHAIN2_MASK + 1)
52 #define DICH_CHAIN_LAST_SIZE (DICH_CHAIN_LAST_MASK + 1)
53 #define DICH_CHAIN1(x) (((x) >> 24) & DICH_CHAIN1_MASK)
54 #define DICH_CHAIN2(x) (((x) >> 16) & DICH_CHAIN2_MASK)
55 #define DICH_CHAIN_LAST(x) ((x) & DICH_CHAIN_LAST_MASK)
56
57 #define OP_CLASS_OFFSET 16
58 #define OP_CLASS_OFFSET_GET(x) (((x) >> OP_CLASS_OFFSET) & 0xffff)
59 #define OP_CLASS_GET(op) ({ \
60       Eobj_Class_Id tmp = OP_CLASS_OFFSET_GET(op); \
61       (Eobj_Class *) ((tmp <= _eobj_classes_last_id) && (tmp > 0)) ? \
62       (_eobj_classes[tmp - 1]) : NULL; \
63       })
64 #define OP_SUB_ID_GET(op) ((op) & 0xffff)
65
66 /* Structure of Eobj_Op is:
67  * 16bit: class
68  * 16bit: op.
69  */
70
71 typedef struct _Dich_Chain1 Dich_Chain1;
72
73 typedef struct
74 {
75    eobj_op_func_type func;
76 } op_type_funcs;
77
78 typedef struct
79 {
80    op_type_funcs *funcs;
81 } Dich_Chain2;
82
83 struct _Dich_Chain1
84 {
85    Dich_Chain2 *chain;
86 };
87
88 typedef struct
89 {
90      EINA_INLIST;
91      const Eobj_Class *klass;
92 } Eobj_Extension_Node;
93
94 struct _Eobj_Class
95 {
96    Eobj_Class_Id class_id;
97    const Eobj_Class *parent;
98    const Eobj_Class_Description *desc;
99    Dich_Chain1 chain[DICH_CHAIN1_SIZE];
100    Eina_Inlist *extensions;
101
102    const Eobj_Class **mro;
103
104    size_t data_offset; /* < Offset of the data within object data. */
105
106    Eina_Bool constructed : 1;
107 };
108
109 static inline eobj_op_func_type
110 dich_func_get(const Eobj_Class *klass, Eobj_Op op)
111 {
112    const Dich_Chain1 *chain1 = &klass->chain[DICH_CHAIN1(op)];
113    if (!chain1) return NULL;
114    if (!chain1->chain) return NULL;
115    Dich_Chain2 *chain2 = &chain1->chain[DICH_CHAIN2(op)];
116    if (!chain2) return NULL;
117    if (!chain2->funcs) return NULL;
118
119    /* num_ops is calculated from the class. */
120    const Eobj_Class *op_klass = OP_CLASS_GET(op);
121    if (!op_klass || (DICH_CHAIN_LAST(op) >= op_klass->desc->ops.count))
122       return NULL;
123
124    return chain2->funcs[DICH_CHAIN_LAST(op)].func;
125 }
126
127 static inline void
128 dich_func_set(Eobj_Class *klass, Eobj_Op op, eobj_op_func_type func)
129 {
130    const Eobj_Class *op_klass = OP_CLASS_GET(op);
131    size_t num_ops;
132
133    /* Verify op is valid. */
134    if (op_klass)
135      {
136         /* num_ops is calculated from the class. */
137         num_ops = op_klass->desc->ops.count;
138         if (DICH_CHAIN_LAST(op) >= num_ops)
139           {
140              ERR("OP %x is too big for the domain '%s', expected value < %x.",
141                    op, op_klass->desc->name, op_klass->desc->ops.count);
142           }
143      }
144    else
145      {
146         ERR("OP %x is from an illegal class.", op);
147         return;
148      }
149
150    Dich_Chain1 *chain1 = &klass->chain[DICH_CHAIN1(op)];
151    if (!chain1->chain)
152      {
153         klass->chain[DICH_CHAIN1(op)].chain =
154            chain1->chain =
155            calloc(DICH_CHAIN2_SIZE, sizeof(*(chain1->chain)));
156      }
157
158    Dich_Chain2 *chain2 = &chain1->chain[DICH_CHAIN2(op)];
159    if (!chain2->funcs)
160      {
161         chain2->funcs = chain1->chain[DICH_CHAIN2(op)].funcs =
162            calloc(num_ops, sizeof(*(chain2->funcs)));
163      }
164
165    chain2->funcs[DICH_CHAIN_LAST(op)].func = func;
166 }
167
168 static inline void
169 dich_func_clean_all(Eobj_Class *klass)
170 {
171    int i;
172    Dich_Chain1 *chain1 = klass->chain;
173
174    for (i = 0 ; i < DICH_CHAIN1_SIZE ; i++, chain1++)
175      {
176         int j;
177         Dich_Chain2 *chain2 = chain1->chain;
178
179         if (!chain2)
180            continue;
181
182         for (j = 0 ; j < DICH_CHAIN2_SIZE ; j++, chain2++)
183           {
184              free(chain2->funcs);
185           }
186         free(chain1->chain);
187         chain1->chain = NULL;
188      }
189 }
190
191 /* END OF DICH */
192
193 typedef struct
194 {
195    EINA_INLIST;
196    Eobj_Op op;
197    const Eobj_Class **kls_itr;
198 } Eobj_Kls_Itr_Node;
199
200 static inline Eina_Bool
201 _eobj_kls_itr_init(Eobj *obj, Eobj_Op op)
202 {
203    if (obj->kls_itr)
204      {
205         Eobj_Kls_Itr_Node *node =
206            EINA_INLIST_CONTAINER_GET(obj->kls_itr, Eobj_Kls_Itr_Node);
207         if (node->op == op)
208           {
209              return EINA_FALSE;
210           }
211      }
212
213
214      {
215         Eobj_Kls_Itr_Node *node = calloc(1, sizeof(*node));
216         node->op = op;
217         node->kls_itr = obj->klass->mro;
218         obj->kls_itr = eina_inlist_prepend(obj->kls_itr,
219               EINA_INLIST_GET(node));
220
221         return EINA_TRUE;
222      }
223 }
224
225 static inline void
226 _eobj_kls_itr_end(Eobj *obj, Eobj_Op op)
227 {
228    Eobj_Kls_Itr_Node *node =
229       EINA_INLIST_CONTAINER_GET(obj->kls_itr, Eobj_Kls_Itr_Node);
230
231    if (node->op != op)
232       return;
233
234    obj->kls_itr = eina_inlist_remove(obj->kls_itr, obj->kls_itr);
235    free(node);
236 }
237
238 static inline const Eobj_Class *
239 _eobj_kls_itr_get(Eobj *obj)
240 {
241    Eobj_Kls_Itr_Node *node =
242       EINA_INLIST_CONTAINER_GET(obj->kls_itr, Eobj_Kls_Itr_Node);
243
244    return (node) ? *(node->kls_itr) : NULL;
245 }
246
247 static inline const Eobj_Class *
248 _eobj_kls_itr_next(Eobj *obj)
249 {
250    Eobj_Kls_Itr_Node *node =
251       EINA_INLIST_CONTAINER_GET(obj->kls_itr, Eobj_Kls_Itr_Node);
252    const Eobj_Class **kls_itr = node->kls_itr;
253    if (*kls_itr)
254      {
255         kls_itr++;
256         node->kls_itr = kls_itr;
257         return *kls_itr;
258      }
259    else
260      {
261         return NULL;
262      }
263 }
264
265 static inline Eina_Bool
266 _eobj_kls_itr_reached_end(const Eobj *obj)
267 {
268    Eobj_Kls_Itr_Node *node =
269       EINA_INLIST_CONTAINER_GET(obj->kls_itr, Eobj_Kls_Itr_Node);
270    const Eobj_Class **kls_itr = node->kls_itr;
271    return !(*kls_itr && *(kls_itr + 1));
272 }
273
274 static const Eobj_Op_Description *
275 _eobj_op_id_desc_get(Eobj_Op op)
276 {
277    const Eobj_Class *klass = OP_CLASS_GET(op);
278    Eobj_Op sub_id = OP_SUB_ID_GET(op);
279
280    if (klass && (sub_id < klass->desc->ops.count))
281       return klass->desc->ops.descs + sub_id;
282
283    return NULL;
284 }
285
286 static Eina_Bool
287 _eobj_op_internal(Eobj *obj, Eobj_Op op, va_list *p_list)
288 {
289    const Eobj_Class *klass;
290    Eina_Bool ret = EINA_FALSE;
291    Eina_Bool _itr_init;
292
293    _itr_init = _eobj_kls_itr_init(obj, op);
294    klass = _eobj_kls_itr_get(obj);
295    while (klass)
296      {
297         eobj_op_func_type func = dich_func_get(klass, op);
298
299         if (func)
300           {
301              func(obj, eobj_data_get(obj, klass), p_list);
302              ret = EINA_TRUE;
303              goto end;
304           }
305
306         klass = _eobj_kls_itr_next(obj);
307      }
308
309    /* Try composite objects */
310      {
311         Eina_List *itr;
312         Eobj *emb_obj;
313         EINA_LIST_FOREACH(obj->composite_objects, itr, emb_obj)
314           {
315              if (_eobj_op_internal(emb_obj, op, p_list))
316                {
317                   ret = EINA_TRUE;
318                   goto end;
319                }
320           }
321      }
322
323 end:
324
325    if (_itr_init) _eobj_kls_itr_end(obj, op);
326    return ret;
327 }
328
329 static inline Eina_Bool
330 _eobj_ops_internal(Eobj *obj, va_list *p_list)
331 {
332    Eina_Bool ret = EINA_TRUE;
333    Eobj_Op op = 0;
334
335    op = va_arg(*p_list, Eobj_Op);
336    while (op)
337      {
338         if (!_eobj_op_internal(obj, op, p_list))
339           {
340              const Eobj_Op_Description *desc = _eobj_op_id_desc_get(op);
341              const char *_id_name = (desc) ? desc->name : NULL;
342              const Eobj_Class *op_klass = OP_CLASS_GET(op);
343              const char *_dom_name = (op_klass) ? op_klass->desc->name : NULL;
344              ERR("Can't find func for op %x ('%s' of domain '%s') for class '%s'. Aborting.",
345                    op, _id_name, _dom_name,
346                    obj->klass->desc->name);
347              ret = EINA_FALSE;
348              break;
349           }
350         op = va_arg(*p_list, Eobj_Op);
351      }
352
353    return ret;
354 }
355
356 EAPI Eina_Bool
357 eobj_do_internal(Eobj *obj, ...)
358 {
359    Eina_Bool ret;
360    va_list p_list;
361    eobj_ref(obj);
362    va_start(p_list, obj);
363    ret = _eobj_ops_internal(obj, &p_list);
364    va_end(p_list);
365    eobj_unref(obj);
366    return ret;
367 }
368
369 EAPI Eina_Bool
370 eobj_do_super(Eobj *obj, Eobj_Op op, ...)
371 {
372    const Eobj_Class *obj_klass;
373    Eina_Bool ret = EINA_TRUE;
374    va_list p_list;
375
376    va_start(p_list, op);
377
378    /* Advance the kls itr. */
379    obj_klass = _eobj_kls_itr_next(obj);
380    if (!_eobj_op_internal(obj, op, &p_list))
381      {
382         const Eobj_Op_Description *desc = _eobj_op_id_desc_get(op);
383         const char *_id_name = (desc) ? desc->name : NULL;
384         const Eobj_Class *op_klass = OP_CLASS_GET(op);
385         const char *_dom_name = (op_klass) ? op_klass->desc->name : NULL;
386         ERR("Can't find func for op %x ('%s' of domain '%s') for class '%s'. Aborting.",
387               op, _id_name, _dom_name,
388               (obj_klass) ? obj_klass->desc->name : NULL);
389         ret = EINA_FALSE;
390      }
391    va_end(p_list);
392
393    return ret;
394 }
395
396 EAPI const Eobj_Class *
397 eobj_class_get(const Eobj *obj)
398 {
399    return obj->klass;
400 }
401
402 EAPI const char *
403 eobj_class_name_get(const Eobj_Class *klass)
404 {
405    return klass->desc->name;
406 }
407
408 static void
409 _eobj_class_base_op_init(Eobj_Class *klass)
410 {
411    const Eobj_Class_Description *desc = klass->desc;
412    if (!desc || !desc->ops.base_op_id)
413       return;
414
415    /* FIXME: Depends on values defined above! */
416    *(desc->ops.base_op_id) = klass->class_id << OP_CLASS_OFFSET;
417 }
418
419 static Eina_List *
420 _eobj_class_mro_add(Eina_List *mro, const Eobj_Class *klass)
421 {
422    if (!klass)
423       return mro;
424
425    mro = eina_list_append(mro, klass);
426
427      {
428         Eobj_Extension_Node *extn;
429         EINA_INLIST_FOREACH(klass->extensions, extn)
430           {
431              mro = _eobj_class_mro_add(mro, extn->klass);
432           }
433      }
434
435    mro = _eobj_class_mro_add(mro, klass->parent);
436
437    return mro;
438 }
439
440 static void
441 _eobj_class_mro_init(Eobj_Class *klass)
442 {
443    Eina_List *mro = NULL;
444
445    DBG("Started creating MRO for class '%s'", klass->desc->name);
446    mro = _eobj_class_mro_add(mro, klass);
447
448      {
449         Eina_List *itr1, *itr2, *itr2n;
450
451         itr1 = eina_list_last(mro);
452         while (itr1)
453           {
454              itr2 = eina_list_prev(itr1);
455
456              while (itr2)
457                {
458                   itr2n = eina_list_prev(itr2);
459
460                   if (eina_list_data_get(itr1) == eina_list_data_get(itr2))
461                     {
462                        mro = eina_list_remove_list(mro, itr2);
463                     }
464
465                   itr2 = itr2n;
466                }
467
468              itr1 = eina_list_prev(itr1);
469           }
470      }
471
472    /* Copy the mro and free the list. */
473      {
474         const Eobj_Class *kls_itr;
475         const Eobj_Class **mro_itr;
476         klass->mro = calloc(sizeof(*klass->mro), eina_list_count(mro) + 1);
477
478         mro_itr = klass->mro;
479
480         EINA_LIST_FREE(mro, kls_itr)
481           {
482              *(mro_itr++) = kls_itr;
483
484              DBG("Added '%s' to MRO", kls_itr->desc->name);
485           }
486         *(mro_itr) = NULL;
487      }
488
489    DBG("Finished creating MRO for class '%s'", klass->desc->name);
490 }
491
492 static void
493 _eobj_class_constructor(Eobj_Class *klass)
494 {
495    if (klass->constructed)
496       return;
497
498    klass->constructed = EINA_TRUE;
499
500    if (klass->desc->class_constructor)
501       klass->desc->class_constructor(klass);
502
503    _eobj_class_mro_init(klass);
504 }
505
506 EAPI void
507 eobj_class_funcs_set(Eobj_Class *klass, const Eobj_Op_Func_Description *func_descs)
508 {
509    const Eobj_Op_Func_Description *itr;
510    itr = func_descs;
511    if (itr)
512      {
513         for ( ; itr->op != 0 ; itr++)
514           {
515              dich_func_set(klass, itr->op, itr->func);
516           }
517      }
518 }
519
520 void
521 eobj_class_free(Eobj_Class *klass)
522 {
523    if (klass->constructed)
524      {
525         if (klass->desc->class_destructor)
526            klass->desc->class_destructor(klass);
527
528         dich_func_clean_all(klass);
529      }
530
531      {
532         Eina_Inlist *itrn;
533         Eobj_Extension_Node *extn;
534         EINA_INLIST_FOREACH_SAFE(klass->extensions, itrn, extn)
535           {
536              free(extn);
537           }
538      }
539
540    free(klass->mro);
541
542    free(klass);
543 }
544
545 /* DEVCHECK */
546 static Eina_Bool
547 _eobj_class_check_op_descs(const Eobj_Class *klass)
548 {
549    const Eobj_Class_Description *desc = klass->desc;
550    const Eobj_Op_Description *itr;
551    size_t i;
552
553    if (desc->ops.count > 0)
554      {
555         if (!desc->ops.base_op_id)
556           {
557              ERR("Class '%s' has a non-zero ops count, but base_id is NULL.",
558                    desc->name);
559              return EINA_FALSE;
560           }
561
562         if (!desc->ops.descs)
563           {
564              ERR("Class '%s' has a non-zero ops count, but there are no descs.",
565                    desc->name);
566              return EINA_FALSE;
567           }
568      }
569
570    itr = desc->ops.descs;
571    for (i = 0 ; i < desc->ops.count ; i++, itr++)
572      {
573         if (itr->sub_op != i)
574           {
575              if (itr->name)
576                {
577                   ERR("Wrong order in Ops description for class '%s'. Expected %d and got %d", desc->name, i, itr->sub_op);
578                }
579              else
580                {
581                   ERR("Found too few Ops description for class '%s'. Expected %d descriptions, but found %d.", desc->name, desc->ops.count, i);
582                }
583              return EINA_FALSE;
584           }
585      }
586
587    if (itr && itr->name)
588      {
589         ERR("Found extra Ops description for class '%s'. Expected %d descriptions, but found more.", desc->name, desc->ops.count);
590         return EINA_FALSE;
591      }
592
593    return EINA_TRUE;
594 }
595
596 EAPI const Eobj_Class *
597 eobj_class_new(const Eobj_Class_Description *desc, const Eobj_Class *parent, ...)
598 {
599    Eobj_Class *klass;
600    va_list p_list;
601
602    va_start(p_list, parent);
603
604 #define _CLS_NEW_CHECK(x) \
605    do \
606      { \
607         if (!x) \
608           { \
609              ERR("%s must not be NULL! Aborting.", #x); \
610              return NULL; \
611           } \
612      } \
613    while(0)
614
615    _CLS_NEW_CHECK(desc);
616    _CLS_NEW_CHECK(desc->name);
617
618    klass = calloc(1, sizeof(Eobj_Class));
619    klass->parent = parent;
620
621    /* Handle class extensions */
622      {
623         Eobj_Class *extn = NULL;
624
625         extn = va_arg(p_list, Eobj_Class *);
626         while (extn)
627           {
628              switch (extn->desc->type)
629                {
630                 case EOBJ_CLASS_TYPE_REGULAR:
631                 case EOBJ_CLASS_TYPE_REGULAR_NO_INSTANT:
632                    /* Use it like an interface. */
633                 case EOBJ_CLASS_TYPE_INTERFACE:
634                    break;
635                 case EOBJ_CLASS_TYPE_MIXIN:
636                      {
637                         Eobj_Extension_Node *node = calloc(1, sizeof(*node));
638                         node->klass = extn;
639                         klass->extensions =
640                            eina_inlist_append(klass->extensions,
641                                  EINA_INLIST_GET(node));
642                      }
643                    break;
644                }
645
646              extn = va_arg(p_list, Eobj_Class *);
647           }
648      }
649
650    klass->desc = desc;
651
652    /* Handle the inheritance */
653    if (klass->parent)
654      {
655         /* Verify the inheritance is allowed. */
656         switch (klass->desc->type)
657           {
658            case EOBJ_CLASS_TYPE_REGULAR:
659            case EOBJ_CLASS_TYPE_REGULAR_NO_INSTANT:
660               if ((klass->parent->desc->type != EOBJ_CLASS_TYPE_REGULAR) &&
661                     (klass->parent->desc->type != EOBJ_CLASS_TYPE_REGULAR_NO_INSTANT))
662                 {
663                    ERR("Regular classes ('%s') aren't allowed to inherit from non-regular classes ('%s').", klass->desc->name, klass->parent->desc->name);
664                    goto cleanup;
665                 }
666               break;
667            case EOBJ_CLASS_TYPE_INTERFACE:
668            case EOBJ_CLASS_TYPE_MIXIN:
669               if ((klass->parent->desc->type != EOBJ_CLASS_TYPE_INTERFACE) &&
670                     (klass->parent->desc->type != EOBJ_CLASS_TYPE_MIXIN))
671                 {
672                    ERR("Non-regular classes ('%s') aren't allowed to inherit from regular classes ('%s').", klass->desc->name, klass->parent->desc->name);
673                    goto cleanup;
674                 }
675               break;
676           }
677
678
679         /* Update the current offset. */
680         /* FIXME: Make sure this alignment is enough. */
681         klass->data_offset = klass->parent->data_offset +
682            klass->parent->desc->data_size +
683            (sizeof(void *) -
684                   (klass->parent->desc->data_size % sizeof(void *)));
685      }
686
687    if (!_eobj_class_check_op_descs(klass))
688      {
689         goto cleanup;
690      }
691
692    klass->class_id = ++_eobj_classes_last_id;
693      {
694         /* FIXME: Handle errors. */
695         Eobj_Class **tmp;
696         tmp = realloc(_eobj_classes, _eobj_classes_last_id * sizeof(*_eobj_classes));
697         _eobj_classes = tmp;
698         _eobj_classes[klass->class_id - 1] = klass;
699      }
700
701    _eobj_class_base_op_init(klass);
702
703    /* FIXME: Shouldn't be called here - should be called from eobj_add. */
704    _eobj_class_constructor(klass);
705
706    va_end(p_list);
707
708    return klass;
709
710 cleanup:
711    eobj_class_free(klass);
712    return NULL;
713 }
714 #undef _CLS_NEW_CHECK
715
716 EAPI Eobj *
717 eobj_add(const Eobj_Class *klass, Eobj *parent)
718 {
719    if (klass->desc->type != EOBJ_CLASS_TYPE_REGULAR)
720      {
721         ERR("Class '%s' is not instantiate-able. Aborting.", klass->desc->name);
722         return NULL;
723      }
724
725    Eobj *obj = calloc(1, sizeof(*obj));
726    obj->klass = klass;
727    obj->parent = parent;
728
729    obj->refcount++;
730
731    obj->data_blob = calloc(1, klass->data_offset + klass->desc->data_size);
732
733    _eobj_kls_itr_init(obj, EOBJ_NOOP);
734    eobj_constructor_error_unset(obj);
735
736    eobj_ref(obj);
737    eobj_class_constructor(obj, klass);
738
739    if (eobj_constructor_error_get(obj))
740      {
741         ERR("Type '%s' - One of the object constructors have failed.", klass->desc->name);
742         goto fail;
743      }
744
745    if (!_eobj_kls_itr_reached_end(obj))
746      {
747         ERR("Type '%s' - Not all of the object constructors have been executed.", klass->desc->name);
748         goto fail;
749      }
750    _eobj_kls_itr_end(obj, EOBJ_NOOP);
751    eobj_unref(obj);
752
753    return obj;
754
755 fail:
756    /* Unref twice, once for the ref above, and once for the basic object ref. */
757    eobj_unref(obj);
758    eobj_unref(obj);
759    return NULL;
760 }
761
762 typedef struct
763 {
764    EINA_INLIST;
765    const Eobj *ref_obj;
766    const char *file;
767    int line;
768 } Eobj_Xref_Node;
769
770 EAPI Eobj *
771 eobj_xref_internal(Eobj *obj, const Eobj *ref_obj, const char *file, int line)
772 {
773    eobj_ref(obj);
774
775 #ifndef NDEBUG
776    Eobj_Xref_Node *xref = calloc(1, sizeof(*xref));
777    xref->ref_obj = ref_obj;
778    xref->file = file;
779    xref->line = line;
780
781    /* FIXME: Make it sorted. */
782    obj->xrefs = eina_inlist_prepend(obj->xrefs, EINA_INLIST_GET(xref));
783 #else
784    (void) ref_obj;
785    (void) file;
786    (void) line;
787 #endif
788
789    return obj;
790 }
791
792 EAPI void
793 eobj_xunref(Eobj *obj, const Eobj *ref_obj)
794 {
795 #ifndef NDEBUG
796    Eobj_Xref_Node *xref = NULL;
797    EINA_INLIST_FOREACH(obj->xrefs, xref)
798      {
799         if (xref->ref_obj == ref_obj)
800            break;
801      }
802
803    if (xref)
804      {
805         obj->xrefs = eina_inlist_remove(obj->xrefs, EINA_INLIST_GET(xref));
806         free(xref);
807      }
808    else
809      {
810         ERR("ref_obj (%p) does not reference obj (%p). Aborting unref.", ref_obj, obj);
811         return;
812      }
813 #else
814    (void) ref_obj;
815 #endif
816    eobj_unref(obj);
817 }
818
819 EAPI Eobj *
820 eobj_ref(Eobj *obj)
821 {
822    obj->refcount++;
823    return obj;
824 }
825
826 EAPI void
827 eobj_unref(Eobj *obj)
828 {
829    if (--(obj->refcount) == 0)
830      {
831         /* We need that for the event callbacks that may ref/unref. */
832         obj->refcount++;
833
834         if (!obj->delete)
835           {
836              eobj_event_callback_call(obj, EOBJ_EV_DEL, NULL);
837              obj->delete = EINA_TRUE;
838           }
839         eobj_event_callback_call(obj, EOBJ_EV_FREE, NULL);
840
841         obj->refcount--;
842
843         const Eobj_Class *klass = eobj_class_get(obj);
844         _eobj_kls_itr_init(obj, EOBJ_NOOP);
845         eobj_constructor_error_unset(obj);
846         eobj_class_destructor(obj, klass);
847         if (eobj_constructor_error_get(obj))
848           {
849              ERR("Type '%s' - One of the object destructors have failed.", klass->desc->name);
850           }
851
852         if (!_eobj_kls_itr_reached_end(obj))
853           {
854              ERR("Type '%s' - Not all of the object destructors have been executed.", klass->desc->name);
855           }
856         _eobj_kls_itr_end(obj, EOBJ_NOOP);
857         /*FIXME: add eobj_class_unref(klass) ? - just to clear the caches. */
858
859         /* If for some reason it's not empty, clear it. */
860         while (obj->kls_itr)
861           {
862              WRN("Kls_Itr is not empty, possibly a bug, please report. - An error will be reported for each kls_itr in the stack.");
863              Eina_Inlist *nitr = nitr->next;
864              free(EINA_INLIST_CONTAINER_GET(obj->kls_itr, Eobj_Kls_Itr_Node));
865              obj->kls_itr = nitr;
866           }
867
868 #ifndef NDEBUG
869         /* If for some reason it's not empty, clear it. */
870         while (obj->xrefs)
871           {
872              WRN("obj->xrefs is not empty, possibly a bug, please report. - An error will be reported for each xref in the stack.");
873              Eina_Inlist *nitr = nitr->next;
874              free(EINA_INLIST_CONTAINER_GET(obj->xrefs, Eobj_Kls_Itr_Node));
875              obj->xrefs = nitr;
876           }
877 #endif
878
879         Eina_List *itr, *itr_n;
880         Eobj *emb_obj;
881         EINA_LIST_FOREACH_SAFE(obj->composite_objects, itr, itr_n, emb_obj)
882           {
883              /* FIXME: Should probably be unref. */
884              eobj_del(emb_obj);
885              obj->composite_objects =
886                 eina_list_remove_list(obj->composite_objects, itr);
887           }
888
889         _eobj_callback_remove_all(obj);
890
891         if (obj->data_blob)
892            free(obj->data_blob);
893
894         free(obj);
895      }
896 }
897
898 EAPI int
899 eobj_ref_get(const Eobj *obj)
900 {
901    return obj->refcount;
902 }
903
904 /* Weak reference. */
905 Eina_Bool
906 _eobj_weak_ref_cb(void *data, Eobj *obj __UNUSED__, const Eobj_Event_Description *desc __UNUSED__, void *event_info __UNUSED__)
907 {
908    Eobj_Weak_Ref *wref = data;
909    wref->obj = NULL;
910
911    return EINA_TRUE;
912 }
913
914 EAPI Eobj_Weak_Ref *
915 eobj_weak_ref_new(const Eobj *_obj)
916 {
917    Eobj *obj = (Eobj *) _obj;
918    Eobj_Weak_Ref *wref = calloc(1, sizeof(*wref));
919    wref->obj = obj;
920    eobj_event_callback_add(obj, EOBJ_EV_DEL, _eobj_weak_ref_cb, wref);
921
922    return wref;
923 }
924
925 EAPI void
926 eobj_weak_ref_free(Eobj_Weak_Ref *wref)
927 {
928    if (wref->obj)
929      {
930         eobj_event_callback_del_full(wref->obj, EOBJ_EV_DEL, _eobj_weak_ref_cb,
931               wref);
932      }
933    free(wref);
934 }
935
936 /* EOF Weak reference. */
937
938 EAPI void
939 eobj_del(Eobj *obj)
940 {
941    if (!obj->delete)
942      {
943         eobj_event_callback_call(obj, EOBJ_EV_DEL, NULL);
944         obj->delete = EINA_TRUE;
945      }
946    eobj_unref(obj);
947 }
948
949 EAPI Eobj *
950 eobj_parent_get(Eobj *obj)
951 {
952    return obj->parent;
953 }
954
955 EAPI void
956 eobj_constructor_error_set(Eobj *obj)
957 {
958    obj->construct_error = EINA_TRUE;
959 }
960
961 static void
962 eobj_constructor_error_unset(Eobj *obj)
963 {
964    obj->construct_error = EINA_FALSE;
965 }
966
967 EAPI Eina_Bool
968 eobj_constructor_error_get(const Eobj *obj)
969 {
970    return obj->construct_error;
971 }
972
973 static inline void
974 _eobj_constructor_default(Eobj *obj)
975 {
976    eobj_constructor_super(obj);
977 }
978
979 static inline void
980 _eobj_destructor_default(Eobj *obj)
981 {
982    eobj_destructor_super(obj);
983 }
984
985 static void
986 eobj_class_constructor(Eobj *obj, const Eobj_Class *klass)
987 {
988    if (!klass)
989       return;
990
991    if (klass->desc->constructor)
992       klass->desc->constructor(obj, eobj_data_get(obj, klass));
993    else
994       _eobj_constructor_default(obj);
995 }
996
997 static void
998 eobj_class_destructor(Eobj *obj, const Eobj_Class *klass)
999 {
1000    if (!klass)
1001       return;
1002
1003    if (klass->desc->destructor)
1004       klass->desc->destructor(obj, eobj_data_get(obj, klass));
1005    else
1006       _eobj_destructor_default(obj);
1007 }
1008
1009 EAPI void
1010 eobj_constructor_super(Eobj *obj)
1011 {
1012    eobj_class_constructor(obj, _eobj_kls_itr_next(obj));
1013 }
1014
1015 EAPI void
1016 eobj_destructor_super(Eobj *obj)
1017 {
1018    eobj_class_destructor(obj, _eobj_kls_itr_next(obj));
1019 }
1020
1021 EAPI void *
1022 eobj_data_get(Eobj *obj, const Eobj_Class *klass)
1023 {
1024    /* FIXME: Add a check that this is of the right klass and we don't seg.
1025     * Probably just return NULL. */
1026    if (klass->desc->data_size > 0)
1027       return ((char *) obj->data_blob) + klass->data_offset;
1028    else
1029       return NULL;
1030 }
1031
1032 EAPI Eina_Bool
1033 eobj_init(void)
1034 {
1035    const char *log_dom = "eobj";
1036    if (_eobj_init_count++ > 0)
1037       return EINA_TRUE;
1038
1039    eina_init();
1040
1041    _eobj_classes = NULL;
1042    _eobj_classes_last_id = 0;
1043    _eobj_log_dom = eina_log_domain_register(log_dom, EINA_COLOR_LIGHTBLUE);
1044    if (_eobj_log_dom < 0)
1045      {
1046         EINA_LOG_ERR("Could not register log domain: %s", log_dom);
1047         return EINA_FALSE;
1048      }
1049
1050    return EINA_TRUE;
1051 }
1052
1053 EAPI Eina_Bool
1054 eobj_shutdown(void)
1055 {
1056    int i;
1057    Eobj_Class **cls_itr = _eobj_classes;
1058
1059    if (--_eobj_init_count > 0)
1060       return EINA_TRUE;
1061
1062    for (i = 0 ; i < _eobj_classes_last_id ; i++, cls_itr++)
1063      {
1064         if (*cls_itr)
1065            eobj_class_free(*cls_itr);
1066      }
1067
1068    if (_eobj_classes)
1069       free(_eobj_classes);
1070
1071    eina_log_domain_unregister(_eobj_log_dom);
1072    _eobj_log_dom = -1;
1073
1074    eina_shutdown();
1075    return EINA_TRUE;
1076 }
1077
1078 EAPI void
1079 eobj_composite_object_attach(Eobj *obj, Eobj *emb_obj)
1080 {
1081    eobj_ref(emb_obj);
1082    obj->composite_objects = eina_list_prepend(obj->composite_objects, emb_obj);
1083 }
1084
1085 EAPI void
1086 eobj_composite_object_detach(Eobj *obj, Eobj *emb_obj)
1087 {
1088    obj->composite_objects = eina_list_remove(obj->composite_objects, emb_obj);
1089    eobj_unref(emb_obj);
1090 }
1091
1092 EAPI Eina_Bool
1093 eobj_composite_is(Eobj *emb_obj)
1094 {
1095    Eobj *obj = eobj_parent_get(emb_obj);
1096    Eina_List *itr;
1097    Eobj *tmp;
1098
1099    if (!obj)
1100       return EINA_FALSE;
1101
1102    EINA_LIST_FOREACH(obj->composite_objects, itr, tmp)
1103      {
1104         if (tmp == emb_obj)
1105            return EINA_TRUE;
1106      }
1107
1108    return EINA_FALSE;
1109 }
1110
1111 /* Callbacks */
1112 struct _Eobj_Callback_Description
1113 {
1114    EINA_INLIST;
1115    const Eobj_Event_Description *event;
1116    Eobj_Event_Cb func;
1117    void *func_data;
1118    Eobj_Callback_Priority priority;
1119    Eina_Bool delete_me : 1;
1120 };
1121
1122 /* Actually remove, doesn't care about walking list, or delete_me */
1123 static void
1124 _eobj_callback_remove(Eobj *obj, Eobj_Callback_Description *cb)
1125 {
1126    obj->callbacks = eina_inlist_remove(obj->callbacks,
1127          EINA_INLIST_GET(cb));
1128    free(cb);
1129 }
1130
1131 /* Actually remove, doesn't care about walking list, or delete_me */
1132 static void
1133 _eobj_callback_remove_all(Eobj *obj)
1134 {
1135    Eina_Inlist *initr;
1136    Eobj_Callback_Description *cb;
1137    EINA_INLIST_FOREACH_SAFE(obj->callbacks, initr, cb)
1138      {
1139         _eobj_callback_remove(obj, cb);
1140      }
1141 }
1142
1143 static void
1144 _eobj_callbacks_clear(Eobj *obj)
1145 {
1146    Eina_Inlist *itn;
1147    Eobj_Callback_Description *cb;
1148
1149    /* Abort if we are currently walking the list. */
1150    if (obj->walking_list > 0)
1151       return;
1152
1153    EINA_INLIST_FOREACH_SAFE(obj->callbacks, itn, cb)
1154      {
1155         if (cb->delete_me)
1156           {
1157              _eobj_callback_remove(obj, cb);
1158           }
1159      }
1160 }
1161
1162 static int
1163 _callback_priority_cmp(const void *_a, const void *_b)
1164 {
1165    const Eobj_Callback_Description *a, *b;
1166    a = (const Eobj_Callback_Description *) _a;
1167    b = (const Eobj_Callback_Description *) _b;
1168    if (a->priority < b->priority)
1169       return -1;
1170    else
1171       return 1;
1172 }
1173
1174 EAPI Eina_Bool
1175 eobj_event_callback_priority_add(Eobj *obj,
1176       const Eobj_Event_Description *desc,
1177       Eobj_Callback_Priority priority,
1178       Eobj_Event_Cb func,
1179       const void *data)
1180 {
1181    Eobj_Callback_Description *cb = calloc(1, sizeof(*cb));
1182    cb->event = desc;
1183    cb->func = func;
1184    cb->func_data = (void *) data;
1185    cb->priority = priority;
1186    obj->callbacks = eina_inlist_sorted_insert(obj->callbacks,
1187          EINA_INLIST_GET(cb), _callback_priority_cmp);
1188
1189    eobj_event_callback_call(obj, EOBJ_EV_CALLBACK_ADD, desc);
1190
1191    return EINA_TRUE;
1192 }
1193
1194 EAPI void *
1195 eobj_event_callback_del(Eobj *obj, const Eobj_Event_Description *desc, Eobj_Event_Cb func)
1196 {
1197    void *ret = NULL;
1198    Eobj_Callback_Description *cb;
1199    EINA_INLIST_FOREACH(obj->callbacks, cb)
1200      {
1201         if ((cb->event == desc) && (cb->func == func))
1202           {
1203              void *data;
1204
1205              data = cb->func_data;
1206              cb->delete_me = EINA_TRUE;
1207              _eobj_callbacks_clear(obj);
1208              ret = data;
1209              goto found;
1210           }
1211      }
1212
1213    return NULL;
1214
1215 found:
1216    eobj_event_callback_call(obj, EOBJ_EV_CALLBACK_DEL, desc);
1217    return ret;
1218 }
1219
1220 EAPI void *
1221 eobj_event_callback_del_full(Eobj *obj, const Eobj_Event_Description *desc, Eobj_Event_Cb func, const void *user_data)
1222 {
1223    void *ret = NULL;
1224    Eobj_Callback_Description *cb;
1225    EINA_INLIST_FOREACH(obj->callbacks, cb)
1226      {
1227         if ((cb->event == desc) && (cb->func == func) &&
1228               (cb->func_data == user_data))
1229           {
1230              void *data;
1231
1232              data = cb->func_data;
1233              cb->delete_me = EINA_TRUE;
1234              _eobj_callbacks_clear(obj);
1235              ret = data;
1236              goto found;
1237           }
1238      }
1239
1240    return NULL;
1241
1242 found:
1243    eobj_event_callback_call(obj, EOBJ_EV_CALLBACK_DEL, desc);
1244    return ret;
1245 }
1246
1247 EAPI Eina_Bool
1248 eobj_event_callback_call(Eobj *obj, const Eobj_Event_Description *desc,
1249       const void *event_info)
1250 {
1251    Eina_Bool ret = EINA_TRUE;
1252    Eobj_Callback_Description *cb;
1253
1254    eobj_ref(obj);
1255    obj->walking_list++;
1256
1257    EINA_INLIST_FOREACH(obj->callbacks, cb)
1258      {
1259         if (!cb->delete_me  && (cb->event == desc))
1260           {
1261              /* Abort callback calling if the func says so. */
1262              if (!cb->func((void *) cb->func_data, obj, desc,
1263                       (void *) event_info))
1264                {
1265                   ret = EINA_FALSE;
1266                   break;
1267                }
1268           }
1269         if (obj->delete)
1270           break;
1271      }
1272    obj->walking_list--;
1273    _eobj_callbacks_clear(obj);
1274    eobj_unref(obj);
1275
1276    return ret;
1277 }
1278
1279 static Eina_Bool
1280 _eobj_event_forwarder_callback(void *data, Eobj *obj, const Eobj_Event_Description *desc, void *event_info)
1281 {
1282    (void) obj;
1283    Eobj *new_obj = (Eobj *) data;
1284    return eobj_event_callback_call(new_obj, desc, event_info);
1285 }
1286
1287 /* FIXME: Change default priority? Maybe call later? */
1288 EAPI Eina_Bool
1289 eobj_event_callback_forwarder_add(Eobj *obj, const Eobj_Event_Description *desc, Eobj *new_obj)
1290 {
1291    return eobj_event_callback_add(obj, desc, _eobj_event_forwarder_callback, new_obj);
1292 }
1293
1294 EAPI Eina_Bool
1295 eobj_event_callback_forwarder_del(Eobj *obj, const Eobj_Event_Description *desc, Eobj *new_obj)
1296 {
1297    eobj_event_callback_del_full(obj, desc, _eobj_event_forwarder_callback, new_obj);
1298    return EINA_TRUE;
1299 }
1300