*/
typedef void (*Eo_Del_Intercept) (Eo *obj_id);
+#include "eo_override.eo.h"
#include "eo_base.eo.h"
#define EO_CLASS EO_BASE_CLASS
} Eo_Op_Description;
/**
+ * @struct _Eo_Ops
+ *
+ * This struct holds the ops and the size of the ops.
+ * Please use the #EO_CLASS_DESCRIPTION_OPS macro when populating it.
+ */
+typedef struct _Eo_Ops
+{
+ const Eo_Op_Description *descs; /**< The op descriptions array of size count. */
+ size_t count; /**< Number of op descriptions. */
+} Eo_Ops;
+
+/**
* @struct _Eo_Class_Description
* This struct holds the description of a class.
* This description should be passed to eo_class_new.
unsigned int version; /**< The current version of eo, use #EO_VERSION */
const char *name; /**< The name of the class. */
Eo_Class_Type type; /**< The type of the class. */
- struct {
- const Eo_Op_Description *descs; /**< The op descriptions array of size count. */
- size_t count; /**< Number of op descriptions. */
- } ops; /**< The ops description, should be filled using #EO_CLASS_DESCRIPTION_OPS (later sorted by Eo). */
+ Eo_Ops ops; /**< The ops description, should be filled using #EO_CLASS_DESCRIPTION_OPS (later sorted by Eo). */
const Eo_Event_Description **events; /**< The event descriptions for this class. */
size_t data_size; /**< The size of data (private + protected + public) this class needs per object. */
void (*class_constructor)(Eo_Class *klass); /**< The constructor of the class. */
EAPI const Eo_Class *eo_class_new(const Eo_Class_Description *desc, const Eo_Class *parent, ...);
/**
+ * @brief Override Eo functions of this object.
+ * @param ops The op description to override with.
+ * @return true on success, false otherwise.
+ *
+ * This lets you override all of the Eo functions of this object (this
+ * one included) and repalce them with ad-hoc implementation.
+ * The contents of the array are copied so they can for example reside
+ * on the stack.
+ *
+ * You are only allowed to override functions that are defined in the
+ * class or any of its interfaces (that is, eo_isa returning true).
+ */
+EAPI Eina_Bool eo_override(Eo *obj, Eo_Ops ops);
+
+/**
* @brief Check if an object "is a" klass.
* @param obj The object to check
* @param klass The klass to check against.
EAPI Eina_Bool eo_shutdown(void);
// Helpers macro to help populating #Eo_Class_Description.
-#define EO_CLASS_DESCRIPTION_NOOPS() { NULL, 0}
-#define EO_CLASS_DESCRIPTION_OPS(op_descs) { op_descs, EINA_C_ARRAY_LENGTH(op_descs) }
+#define EO_CLASS_DESCRIPTION_NOOPS() ((Eo_Ops) { NULL, 0})
+#define EO_CLASS_DESCRIPTION_OPS(op_descs) ((Eo_Ops) { op_descs, EINA_C_ARRAY_LENGTH(op_descs) })
// to fetch internal function and object data at once
typedef struct _Eo_Op_Call_Data
#include "eo_private.h"
#include "eo_add_fallback.h"
+#include "eo_override.eo.c"
+
#define EO_CLASS_IDS_FIRST 1
#define EO_OP_IDS_FIRST 1
}
static inline Eina_Bool
-_vtable_func_set(_Eo_Class *klass, Eo_Op op, eo_op_func_type func)
+_vtable_func_set(Eo_Vtable *vtable, const _Eo_Class *klass, Eo_Op op, eo_op_func_type func)
{
op_type_funcs *fsrc;
size_t idx1 = DICH_CHAIN1(op);
- Dich_Chain1 *chain1 = &klass->vtable.chain[idx1];
+ Dich_Chain1 *chain1 = &vtable->chain[idx1];
_vtable_chain_alloc(chain1);
fsrc = &chain1->funcs[DICH_CHAIN_LAST(op)];
if (fsrc->src == klass)
return EINA_TRUE;
}
-static inline void
-_vtable_func_clean_all(_Eo_Class *klass)
+void
+_vtable_func_clean_all(Eo_Vtable *vtable)
{
size_t i;
- Dich_Chain1 *chain1 = klass->vtable.chain;
+ Dich_Chain1 *chain1 = vtable->chain;
- for (i = 0 ; i < klass->vtable.size ; i++, chain1++)
+ for (i = 0 ; i < vtable->size ; i++, chain1++)
{
if (chain1->funcs)
free(chain1->funcs);
}
- free(klass->vtable.chain);
- klass->vtable.chain = NULL;
+ free(vtable->chain);
+ vtable->chain = NULL;
}
/* END OF DICH */
const _Eo_Class *klass, *inputklass, *main_klass;
const _Eo_Class *cur_klass = NULL;
_Eo_Object *obj = NULL;
+ const Eo_Vtable *vtable = NULL;
const op_type_funcs *func;
Eina_Bool is_obj;
+ Eina_Bool is_override = EINA_FALSE;
if (((Eo_Id) eo_id) & MASK_SUPER_TAG)
{
EO_OBJ_POINTER_RETURN_VAL(eo_id, _obj, EINA_FALSE);
obj = _obj;
klass = _obj->klass;
+ vtable = obj->vtable;
+
+ if (_obj_is_override(obj) && cur_klass &&
+ (_eo_class_id_get(cur_klass) == EO_OVERRIDE_CLASS))
+ {
+ /* Doing a eo_super(obj, EO_OVERRIDE_CLASS) should result in calling
+ * as if it's a normal class. */
+ vtable = &klass->vtable;
+ cur_klass = NULL;
+ }
+
+ is_override = _obj_is_override(obj) && (cur_klass == NULL);
+
call->obj = obj;
_eo_ref(_obj);
}
{
EO_CLASS_POINTER_RETURN_VAL(eo_id, _klass, EINA_FALSE);
klass = _klass;
+ vtable = &klass->vtable;
call->obj = NULL;
call->data = NULL;
}
else
{
# if EO_CALL_CACHE_SIZE > 0
+ if (!is_override)
+ {
# if EO_CALL_CACHE_SIZE > 1
- int i;
+ int i;
- for (i = 0; i < EO_CALL_CACHE_SIZE; i++)
+ for (i = 0; i < EO_CALL_CACHE_SIZE; i++)
# else
- const int i = 0;
+ const int i = 0;
# endif
- {
- if ((const void *)inputklass == cache->index[i].klass)
{
- func = (const op_type_funcs *)cache->entry[i].func;
- call->func = func->func;
- if (is_obj)
+ if ((const void *)inputklass == cache->index[i].klass)
{
- call->data = (char *) obj + cache->off[i].off;
+ func = (const op_type_funcs *)cache->entry[i].func;
+ call->func = func->func;
+ if (is_obj)
+ {
+ call->data = (char *) obj + cache->off[i].off;
+ }
+ return EINA_TRUE;
}
- return EINA_TRUE;
}
}
#endif
- func = _vtable_func_get(&klass->vtable, cache->op);
+ func = _vtable_func_get(vtable, cache->op);
if (!func)
goto end;
}
# if EO_CALL_CACHE_SIZE > 0
- if (!cur_klass)
+ if (!cur_klass && !is_override)
{
# if EO_CALL_CACHE_SIZE > 1
const int slot = cache->next_slot;
return op;
}
+/* klass is the klass we are working on. hierarchy_klass is the class whe should
+ * use when validating. */
static Eina_Bool
-_eo_class_funcs_set(_Eo_Class *klass)
+_eo_class_funcs_set(Eo_Vtable *vtable, const Eo_Ops *ops, const _Eo_Class *hierarchy_klass, const _Eo_Class *klass, Eina_Bool override_only)
{
unsigned int i;
int op_id;
const Eo_Op_Description *op_desc;
const Eo_Op_Description *op_descs;
- op_id = klass->base_id;
- op_descs = klass->desc->ops.descs;
+ op_id = hierarchy_klass->base_id;
+ op_descs = ops->descs;
DBG("Set functions for class '%s':%p", klass->desc->name, klass);
if (!op_descs) return EINA_TRUE;
last_api_func = NULL;
- for (i = 0, op_desc = op_descs; i < klass->desc->ops.count; i++, op_desc++)
+ for (i = 0, op_desc = op_descs; i < ops->count; i++, op_desc++)
{
Eo_Op op = EO_NOOP;
if ((op_desc->op_type == EO_OP_TYPE_REGULAR) || (op_desc->op_type == EO_OP_TYPE_CLASS))
{
+ if (override_only)
+ {
+ ERR("Creation of new functions is not allowed when overriding an object's vtable.");
+ return EINA_FALSE;
+ }
+
if (_eo_api_func_equal(op_desc->api_func, last_api_func))
{
ERR("Class '%s': API previously defined (%p->%p '%s').",
else if ((op_desc->op_type == EO_OP_TYPE_REGULAR_OVERRIDE) || (op_desc->op_type == EO_OP_TYPE_CLASS_OVERRIDE))
{
const Eo_Op_Description *api_desc;
- api_desc = _eo_api_desc_get(op_desc->api_func, klass->parent, klass->extensions);
+ if (override_only)
+ {
+ api_desc = _eo_api_desc_get(op_desc->api_func, hierarchy_klass, NULL);
+ }
+ else
+ {
+ api_desc = _eo_api_desc_get(op_desc->api_func, hierarchy_klass->parent, hierarchy_klass->extensions);
+ }
if (api_desc == NULL)
{
ERR("Class '%s': Can't find api func description in class hierarchy (%p->%p) (%s).",
- klass->desc->name, op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
+ hierarchy_klass->desc->name, op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
return EINA_FALSE;
}
DBG("%p->%p '%s'", op_desc->api_func, op_desc->func, _eo_op_desc_name_get(op_desc));
- if (!_vtable_func_set(klass, op, op_desc->func))
+ if (!_vtable_func_set(vtable, klass, op, op_desc->func))
return EINA_FALSE;
last_api_func = op_desc->api_func;
}
static void
+_vtable_init(Eo_Vtable *vtable, size_t size)
+{
+ vtable->size = size;
+ vtable->chain = calloc(vtable->size, sizeof(vtable->chain));
+}
+
+static void
_eo_class_base_op_init(_Eo_Class *klass)
{
const Eo_Class_Description *desc = klass->desc;
_eo_ops_last_id += desc->ops.count + 1;
- klass->vtable.size = DICH_CHAIN1(_eo_ops_last_id) + 1;
- klass->vtable.chain = calloc(klass->vtable.size, sizeof(*klass->vtable.chain));
+ _vtable_init(&klass->vtable, DICH_CHAIN1(_eo_ops_last_id) + 1);
}
#ifdef EO_DEBUG
if (klass->desc->class_destructor)
klass->desc->class_destructor(_eo_class_id_get(klass));
- _vtable_func_clean_all(klass);
+ _vtable_func_clean_all(&klass->vtable);
}
EINA_TRASH_CLEAN(&klass->objects.trash, data)
{
const _Eo_Class *extn = *extn_itr;
/* Set it in the dich. */
- _vtable_func_set(klass, extn->base_id +
+ _vtable_func_set(&klass->vtable, klass, extn->base_id +
extn->desc->ops.count, _eo_class_isa_func);
}
- _vtable_func_set(klass, klass->base_id + klass->desc->ops.count,
+ _vtable_func_set(&klass->vtable, klass, klass->base_id + klass->desc->ops.count,
_eo_class_isa_func);
if (klass->parent)
{
- _vtable_func_set(klass,
+ _vtable_func_set(&klass->vtable, klass,
klass->parent->base_id + klass->parent->desc->ops.count,
_eo_class_isa_func);
}
}
- if (!_eo_class_funcs_set(klass))
+ if (!_eo_class_funcs_set(&klass->vtable, &(klass->desc->ops), klass, klass, EINA_FALSE))
{
eina_spinlock_free(&klass->objects.trash_lock);
eina_spinlock_free(&klass->iterators.trash_lock);
- _vtable_func_clean_all(klass);
+ _vtable_func_clean_all(&klass->vtable);
free(klass);
return NULL;
}
}
EAPI Eina_Bool
+eo_override(Eo *eo_id, Eo_Ops ops)
+{
+ EO_OBJ_POINTER_RETURN_VAL(eo_id, obj, EINA_FALSE);
+ EO_CLASS_POINTER_RETURN_VAL(EO_OVERRIDE_CLASS, klass, EINA_FALSE);
+ Eo_Vtable *previous = obj->vtable;
+ obj->vtable = calloc(1, sizeof(*obj->vtable));
+ _vtable_init(obj->vtable, previous->size);
+ _vtable_copy_all(obj->vtable, previous);
+
+ if (!_eo_class_funcs_set(obj->vtable, &ops, obj->klass, klass, EINA_TRUE))
+ {
+ ERR("Failed to override functions for %p", eo_id);
+ return EINA_FALSE;
+ }
+
+ return EINA_TRUE;
+}
+
+EAPI Eina_Bool
eo_isa(const Eo *eo_id, const Eo_Class *klass_id)
{
EO_OBJ_POINTER_RETURN_VAL(eo_id, obj, EINA_FALSE);
}
END_TEST
+#define OVERRIDE_A_SIMPLE 100859
+#define OVERRIDE_A 324000
+static int
+_simple_obj_override_a_get(Eo *obj, void *class_data EINA_UNUSED)
+{
+ return OVERRIDE_A + simple_a_get(eo_super(obj, EO_OVERRIDE_CLASS));
+}
+
+static void
+_simple_obj_override_a_double_set(Eo *obj, void *class_data EINA_UNUSED, int a)
+{
+ simple_a_set(eo_super(obj, EO_OVERRIDE_CLASS), 2 * a);
+}
+
+START_TEST(eo_override_tests)
+{
+ eo_init();
+
+ Eo_Op_Description override_descs[] = {
+ EO_OP_FUNC_OVERRIDE(simple_a_get, _simple_obj_override_a_get),
+ };
+
+ Eo *obj = eo_add(SIMPLE_CLASS, NULL);
+ fail_if(!obj);
+
+ /* First get the value before the override to make sure it works and to
+ * make sure we don't cache. */
+ ck_assert_int_eq(simple_a_get(obj), 0);
+
+ fail_if(!eo_override(obj, EO_CLASS_DESCRIPTION_OPS(override_descs)));
+
+ ck_assert_int_eq(simple_a_get(obj), OVERRIDE_A);
+
+ /* Check super works. */
+ simple_a_set(obj, OVERRIDE_A_SIMPLE);
+ ck_assert_int_eq(simple_a_get(obj), OVERRIDE_A + OVERRIDE_A_SIMPLE);
+
+
+ /* Override again. */
+ Eo_Op_Description override_descs2[] = {
+ EO_OP_FUNC_OVERRIDE(simple_a_set, _simple_obj_override_a_double_set),
+ };
+
+ fail_if(!eo_override(obj, EO_CLASS_DESCRIPTION_OPS(override_descs2)));
+
+ simple_a_set(obj, OVERRIDE_A_SIMPLE);
+ ck_assert_int_eq(simple_a_get(obj), OVERRIDE_A + (OVERRIDE_A_SIMPLE * 2));
+
+
+ /* Try introducing a new function */
+ Eo_Op_Description override_descs3[] = {
+ EO_OP_FUNC(simple2_class_beef_get, _simple_obj_override_a_double_set),
+ };
+
+ fail_if(eo_override(obj, (Eo_Ops) EO_CLASS_DESCRIPTION_OPS(override_descs3)));
+
+ eo_unref(obj);
+
+ eo_shutdown();
+}
+END_TEST
+
static int _eo_signals_cb_current = 0;
static int _eo_signals_cb_flag = 0;
{
tcase_add_test(tc, eo_simple);
tcase_add_test(tc, eo_singleton);
+ tcase_add_test(tc, eo_override_tests);
tcase_add_test(tc, eo_signals);
tcase_add_test(tc, eo_data_fetch);
tcase_add_test(tc, eo_isa_tests);