From patchwork Mon Jul 25 01:44:35 2011 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Anthony Liguori X-Patchwork-Id: 106601 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [140.186.70.17]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (Client did not present a certificate) by ozlabs.org (Postfix) with ESMTPS id 42E58B6F83 for ; Mon, 25 Jul 2011 12:48:59 +1000 (EST) Received: from localhost ([::1]:55490 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QlAEt-0005jT-TT for incoming@patchwork.ozlabs.org; Sun, 24 Jul 2011 21:45:47 -0400 Received: from eggs.gnu.org ([140.186.70.92]:42251) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QlAEB-0003iy-61 for qemu-devel@nongnu.org; Sun, 24 Jul 2011 21:45:08 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1QlAE7-0005RE-BG for qemu-devel@nongnu.org; Sun, 24 Jul 2011 21:45:03 -0400 Received: from e3.ny.us.ibm.com ([32.97.182.143]:43451) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1QlAE7-0005Qy-5x for qemu-devel@nongnu.org; Sun, 24 Jul 2011 21:44:59 -0400 Received: from d01relay06.pok.ibm.com (d01relay06.pok.ibm.com [9.56.227.116]) by e3.ny.us.ibm.com (8.14.4/8.13.1) with ESMTP id p6P1LMEd004674 for ; Sun, 24 Jul 2011 21:21:22 -0400 Received: from d01av04.pok.ibm.com (d01av04.pok.ibm.com [9.56.224.64]) by d01relay06.pok.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id p6P1iw0M1454290 for ; Sun, 24 Jul 2011 21:44:58 -0400 Received: from d01av04.pok.ibm.com (loopback [127.0.0.1]) by d01av04.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVout) with ESMTP id p6P1iwms006898 for ; Sun, 24 Jul 2011 21:44:58 -0400 Received: from titi.austin.rr.com (sig-9-65-207-230.mts.ibm.com [9.65.207.230]) by d01av04.pok.ibm.com (8.14.4/8.13.1/NCO v10.0 AVin) with ESMTP id p6P1itKi006824; Sun, 24 Jul 2011 21:44:57 -0400 From: Anthony Liguori To: qemu-devel@nongnu.org Date: Sun, 24 Jul 2011 20:44:35 -0500 Message-Id: <1311558293-5855-4-git-send-email-aliguori@us.ibm.com> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1311558293-5855-1-git-send-email-aliguori@us.ibm.com> References: <1311558293-5855-1-git-send-email-aliguori@us.ibm.com> X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6, seldom 2.4 (older, 4) X-Received-From: 32.97.182.143 Cc: Anthony Liguori Subject: [Qemu-devel] [PATCH 03/21] qom: Add core type system X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org This patch implements the basic type system used by QEMU Object Model. This infrastructure supports the registration of classes and instantiation of objects through a generic factory interface. Classes support polymophism and single inheritance. Interfaces can also be used to approximate multiple inheritance. Signed-off-by: Anthony Liguori --- Makefile.qom | 4 + Qconfig | 1 + configure | 2 +- include/qemu/type.h | 513 ++++++++++++++++++++++++++++++++++++++++++++++++++ qom/Makefile | 1 + qom/Qconfig | 6 + qom/type.c | 523 +++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1049 insertions(+), 1 deletions(-) create mode 100644 include/qemu/type.h create mode 100644 qom/Makefile create mode 100644 qom/Qconfig create mode 100644 qom/type.c diff --git a/Makefile.qom b/Makefile.qom index 1b06970..34f1f91 100644 --- a/Makefile.qom +++ b/Makefile.qom @@ -9,3 +9,7 @@ config-qom.mak: $(SRC_PATH)/Qconfig $(QCONFIGS) include $(SRC_PATH)/qapi/Makefile common-obj-y += $(addprefix qapi/,$(qapi-obj-y)) + +include $(SRC_PATH)/qom/Makefile +common-obj-y += $(addprefix qom/,$(qom-obj-y)) + diff --git a/Qconfig b/Qconfig index cdf8f6c..889dfa6 100644 --- a/Qconfig +++ b/Qconfig @@ -1 +1,2 @@ source qapi/Qconfig +source qom/Qconfig diff --git a/configure b/configure index 600da9b..93e5e97 100755 --- a/configure +++ b/configure @@ -3516,7 +3516,7 @@ DIRS="$DIRS pc-bios/spapr-rtas" DIRS="$DIRS roms/seabios roms/vgabios" DIRS="$DIRS fsdev ui" DIRS="$DIRS qapi" -DIRS="$DIRS qga" +DIRS="$DIRS qga qom" FILES="Makefile tests/Makefile" FILES="$FILES tests/cris/Makefile tests/cris/.gdbinit" FILES="$FILES pc-bios/optionrom/Makefile pc-bios/keymaps" diff --git a/include/qemu/type.h b/include/qemu/type.h new file mode 100644 index 0000000..170f485 --- /dev/null +++ b/include/qemu/type.h @@ -0,0 +1,513 @@ +/* + * QEMU Object Model + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#ifndef QEMU_TYPE_H +#define QEMU_TYPE_H + +#include "qemu-common.h" +#include + +#define MAX_ID (32 + 1) + +typedef uint64_t Type; + +typedef struct TypeClass TypeClass; +typedef struct TypeInstance TypeInstance; +typedef struct TypeInfo TypeInfo; + +typedef struct InterfaceClass InterfaceClass; +typedef struct Interface Interface; +typedef struct InterfaceInfo InterfaceInfo; + +/** + * @TypeClass: + * + * The base for all classes. The only thing that @TypeClass contains is an + * integer type handle. + */ +struct TypeClass +{ + /** + * @type the handle of the type for a class + */ + Type type; +}; + +/** + * @TypeInstance: + * + * The base for all objects. The first member of this object is a pointer to + * a @TypeClass. Since C guarantees that the first member of a structure + * always begins at byte 0 of that structure, as long as any sub-object places + * its parent as the first member, we can cast directly to a @TypeInstance. + * + * As a result, @TypeInstance contains a reference to the objects type as its + * first member. This allows identification of the real type of the object at + * run time. + * + * @TypeInstance also contains a list of @Interfaces that this object + * implements. + */ +struct TypeInstance +{ + /** + * @class the type of the instantiated object. + */ + TypeClass *class; + + /** + * @id the name of the object + */ + char id[MAX_ID]; + + /** + * @interfaces a list of @Interface objects implemented by this object + */ + GSList *interfaces; +}; + +/** + * @TypeInfo: + * + */ +struct TypeInfo +{ + /** + * @name the name of the type + */ + const char *name; + + /** + * @parent the name of the parent type + */ + const char *parent; + + /** + * Instance Initialization + * + * This functions manage the instance construction and destruction of a + * type. + */ + + /** + * @instance_size the size of the object (derivative of @TypeInstance). If + * @instance_size is 0, then the size of the object will be the size of the + * parent object. + */ + size_t instance_size; + + /** + * @instance_init + * + * This function is called to initialize an object. The parent class will + * have already been initialized so the type is only responsible for + * initializing its own members. + */ + void (*instance_init)(TypeInstance *obj); + + /** + * @instance_finalize + * + * This function is called during object destruction. This is called before + * the parent @instance_finalize function has been called. An object should + * only free the members that are unique to its type in this function. + */ + void (*instance_finalize)(TypeInstance *obj); + + /** + * @abstract + * + * If this field is true, then the class is considered abstract and cannot + * be directly instantiated. + */ + bool abstract; + + /** + * Class Initialization + * + * Before an object is initialized, the class for the object must be + * initialized. There is only one class object for all instance objects + * that is created lazily. + * + * Classes are initialized by first initializing any parent classes (if + * necessary). After the parent class object has initialized, it will be + * copied into the current class object and any additional storage in the + * class object is zero filled. + * + * The effect of this is that classes automatically inherit any virtual + * function pointers that the parent class has already initialized. All + * other fields will be zero filled. + * + * After this initial copy, @base_init is invoked. This is meant to handle + * the case where a class may have a dynamic field that was copied via + * a shallow copy but needs to be deep copied. @base_init is called for + * each parent class but not for the class being instantiated. + * + * Once all of the parent classes have been initialized and their @base_init + * functions have been called, @class_init is called to let the class being + * instantiated provide default initialize for it's virtual functions. + */ + + /** + * @class_size the size of the class object (derivative of @TypeClass) for + * this object. If @class_size is 0, then the size of the class will be + * assumed to be the size of the parent class. This allows a type to avoid + * implementing an explicit class type if they are not adding additional + * virtual functions. + */ + size_t class_size; + + /** + * @base_init + * + * This function is called after memcpy()'ing the base class into the new + * class to reinitialize any members that require deep copy. + */ + void (*base_init)(TypeClass *klass); + + /** + * @base_finalize + * + * This function is called during a class's destruction and is meant to + * allow any dynamic parameters allocated by @base_init to be released. + */ + void (*base_finalize)(TypeClass *klass); + + /** + * @class_init + * + * This function is called after all parent class initialization has occured + * to allow a class to set its default virtual method pointers. This is + * also the function to use to override virtual methods from a parent class. + */ + void (*class_init)(TypeClass *klass); + + /** + * @class_finalize + * + * This function is called during class destruction and is meant to release + * and dynamic parameters allocated by @class_init. + */ + void (*class_finalize)(TypeClass *klass); + + /** + * Interfaces + * + * Interfaces allow a limited form of multiple inheritance. Instances are + * similar to normal types except for the fact that are only defined by + * their classes and never carry any state. You can cast an object to one + * of its @Interface types and vice versa. + */ + + /** + * @interfaces the list of interfaces associated with this type. This + * should point to a static array that's terminated with a zero filled + * element. + */ + InterfaceInfo *interfaces; +}; + +/** + * @TYPE_INSTANCE + * + * Converts an object to a @TypeInstance. Since all objects are @TypeInstances, + * this function will always succeed. + */ +#define TYPE_INSTANCE(obj) \ + ((TypeInstance *)(obj)) + +/** + * @TYPE_CHECK + * + * A type safe version of @type_dynamic_cast_assert. Typically each class will + * define a macro based on this type to perform type safe dynamic_casts to + * this object type. + * + * If an invalid object is passed to this function, a run time assert will be + * generated. + */ +#define TYPE_CHECK(type, obj, name) \ + ((type *)type_dynamic_cast_assert((TypeInstance *)(obj), (name))) + +/** + * @TYPE_CLASS_CHECK + * + * A type safe version of @type_check_class. This macro is typically wrapped + * by each type to perform type safe casts of a class to a specific class type. + */ +#define TYPE_CLASS_CHECK(class, obj, name) \ + ((class *)type_check_class((TypeClass *)(obj), (name))) + +/** + * @TYPE_GET_CLASS + * + * This function will return a specific class for a given object. Its generally + * used by each type to provide a type safe macro to get a specific class type + * from an object. + */ +#define TYPE_GET_CLASS(class, obj, name) \ + TYPE_CLASS_CHECK(class, type_get_class(TYPE_INSTANCE(obj)), name) + +/** + * @Interface: + * + * The base for all Interfaces. This is a subclass of TypeInstance. Subclasses + * of @Interface should never have an instance that contains anything other than + * a single @Interface member. + */ +struct Interface +{ + /** + * @parent base class + */ + TypeInstance parent; + + /* private */ + + /** + * @obj a pointer to the object that implements this interface. This is + * used to allow casting from an interface to the base object. + */ + TypeInstance *obj; +}; + +/** + * @InterfaceClass: + * + * The class for all interfaces. Subclasses of this class should only add + * virtual methods. + */ +struct InterfaceClass +{ + /** + * @parent_class the base class + */ + TypeClass parent_class; +}; + +/** + * @InterfaceInfo: + * + * The information associated with an interface. + */ +struct InterfaceInfo +{ + /** + * @type the name of the interface + */ + const char *type; + + /** + * @interface_initfn is called during class initialization and is used to + * initialize an interface associated with a class. This function should + * initialize any default virtual functions for a class and/or override + * virtual functions in a parent class. + */ + void (*interface_initfn)(TypeClass *class); +}; + +#define TYPE_INTERFACE "interface" +#define INTERFACE(obj) TYPE_CHECK(Interface, obj, TYPE_INTERFACE) + +/** + * @type_new: + * + * This function will initialize a new object using heap allocated memory. This + * function should be paired with @type_delete to free the resources associated + * with the object. + * + * @typename: The name of the type of the object to instantiate + * + * @id: The id of the object. This must be unique. + * + * Returns: The newly allocated and instantiated object. + * + */ +TypeInstance *type_new(const char *typename, const char *id); + +/** + * @type_delete: + * + * Finalize an object and then free the memory associated with it. This should + * be paired with @type_new to free the resources associated with an object. + * + * @obj: The object to free. + * + */ +void type_delete(TypeInstance *obj); + +/** + * @type_initialize: + * + * This function will initialize an object. The memory for the object should + * have already been allocated. + * + * @obj: A pointer to the memory to be used for the object. + * + * @typename: The name of the type of the object to instantiate + * + * @id: The id of the object. This must be unique. + * + */ +void type_initialize(void *obj, const char *typename, const char *id); + +/** + * @type_finalize: + * + * This function destroys and object without freeing the memory associated with + * it. + * + * @obj: + * + */ +void type_finalize(void *obj); + +/** + * @type_dynamic_cast: + * + * This function will determine if @obj is-a @typename. @obj can refer to an + * object or an interface associated with an object. + * + * @obj: The object to cast. + * + * @typename: The @typename + * + * Returns: + * + */ +TypeInstance *type_dynamic_cast(TypeInstance *obj, const char *typename); + +/** + * @type_dynamic_cast_assert: + * + * @obj: + * + * @typename: + * + * Returns: + * + */ +TypeInstance *type_dynamic_cast_assert(TypeInstance *obj, const char *typename); + +/** + * @type_is_type: + * + * @obj: + * + * @typename: + * + * Returns: + * + */ +bool type_is_type(TypeInstance *obj, const char *typename); + +/** + * @type_get_class: + * + * @obj: + * + * Returns: + * + */ +TypeClass *type_get_class(TypeInstance *obj); + +/** + * @type_get_id: + * + * @obj: + * + * Returns: + * + */ +const char *type_get_id(TypeInstance *obj); + +/** + * @type_get_type: + * + * @obj: + * + * Returns: + */ +const char *type_get_type(TypeInstance *obj); + +/** + * @type_get_super: + * + * @obj: + * + * Returns: + */ +TypeClass *type_get_super(TypeInstance *obj); + +/**/ + +/** + * @type_register_static: + * + * @info: + * + * Returns: + */ +Type type_register_static(const TypeInfo *info); + +/** + * @type_find_by_id: + * + * @id: + * + * Returns: + * + */ +TypeInstance *type_find_by_id(const char *id); + +/** + * @type_check_class: + * + * @obj: + * + * @typename: + * + * Returns: + */ +TypeClass *type_check_class(TypeClass *obj, const char *typename); + +/** + * @type_get_by_name: + * + * @name: + * + * Returns: + */ +Type type_get_by_name(const char *name); + +/** + * @type_get_name: + * + * @type: + * + * Returns: + */ +const char *type_get_name(Type type); + +/** + * @type_foreach: + * + * @enumfn: + * + * @opaque: + * + */ +void type_foreach(void (*enumfn)(TypeInstance *obj, void *opaque), + void *opaque); + +#endif diff --git a/qom/Makefile b/qom/Makefile new file mode 100644 index 0000000..838054f --- /dev/null +++ b/qom/Makefile @@ -0,0 +1 @@ +qom-obj-$(CONFIG_QOM) += type.o diff --git a/qom/Qconfig b/qom/Qconfig new file mode 100644 index 0000000..16660b4 --- /dev/null +++ b/qom/Qconfig @@ -0,0 +1,6 @@ +config QOM + bool "QEMU Object Model Support" + default y + depends on QAPI + help + This is the core object model used by QEMU. If in doubt, say y here. diff --git a/qom/type.c b/qom/type.c new file mode 100644 index 0000000..28e8114 --- /dev/null +++ b/qom/type.c @@ -0,0 +1,523 @@ +/* + * QEMU Object Model + * + * Copyright IBM, Corp. 2011 + * + * Authors: + * Anthony Liguori + * + * This work is licensed under the terms of the GNU GPL, version 2. See + * the COPYING file in the top-level directory. + * + */ + +#include "qemu/type.h" +#include + +#define MAX_INTERFACES 32 + +typedef struct InterfaceImpl +{ + const char *parent; + void (*interface_initfn)(TypeClass *class); + Type type; +} InterfaceImpl; + +typedef struct TypeImpl +{ + const char *name; + Type type; + + size_t class_size; + + size_t instance_size; + + void (*base_init)(TypeClass *klass); + void (*base_finalize)(TypeClass *klass); + + void (*class_init)(TypeClass *klass); + void (*class_finalize)(TypeClass *klass); + + void (*instance_init)(TypeInstance *obj); + void (*instance_finalize)(TypeInstance *obj); + + bool abstract; + + const char *parent; + + TypeClass *class; + + int num_interfaces; + InterfaceImpl interfaces[MAX_INTERFACES]; +} TypeImpl; + +static int num_types = 1; +static TypeImpl type_table[128]; + +Type type_register_static(const TypeInfo *info) +{ + Type type = num_types++; + TypeImpl *ti; + + ti = &type_table[type]; + + assert(info->name != NULL); + + ti->name = info->name; + ti->parent = info->parent; + ti->type = type; + + ti->class_size = info->class_size; + ti->instance_size = info->instance_size; + + ti->base_init = info->base_init; + ti->base_finalize = info->base_finalize; + + ti->class_init = info->class_init; + ti->class_finalize = info->class_finalize; + + ti->instance_init = info->instance_init; + ti->instance_finalize = info->instance_finalize; + + ti->abstract = info->abstract; + + if (info->interfaces) { + int i; + + for (i = 0; info->interfaces[i].type; i++) { + ti->interfaces[i].parent = info->interfaces[i].type; + ti->interfaces[i].interface_initfn = info->interfaces[i].interface_initfn; + ti->num_interfaces++; + } + } + + return type; +} + +static Type type_register_anonymous(const TypeInfo *info) +{ + Type type = num_types++; + TypeImpl *ti; + char buffer[32]; + static int count; + + ti = &type_table[type]; + + snprintf(buffer, sizeof(buffer), "", count++); + ti->name = qemu_strdup(buffer); + ti->parent = qemu_strdup(info->parent); + ti->type = type; + + ti->class_size = info->class_size; + ti->instance_size = info->instance_size; + + ti->base_init = info->base_init; + ti->base_finalize = info->base_finalize; + + ti->class_init = info->class_init; + ti->class_finalize = info->class_finalize; + + ti->instance_init = info->instance_init; + ti->instance_finalize = info->instance_finalize; + + if (info->interfaces) { + int i; + + for (i = 0; info->interfaces[i].type; i++) { + ti->interfaces[i].parent = info->interfaces[i].type; + ti->interfaces[i].interface_initfn = info->interfaces[i].interface_initfn; + ti->num_interfaces++; + } + } + + return type; +} + +static TypeImpl *type_get_instance(Type type) +{ + assert(type != 0); + assert(type < num_types); + + return &type_table[type]; +} + +Type type_get_by_name(const char *name) +{ + int i; + + if (name == NULL) { + return 0; + } + + for (i = 1; i < num_types; i++) { + if (strcmp(name, type_table[i].name) == 0) { + return i; + } + } + + return 0; +} + +static void type_class_base_init(TypeImpl *base_ti, const char *typename) +{ + TypeImpl *ti; + + if (!typename) { + return; + } + + ti = type_get_instance(type_get_by_name(typename)); + + type_class_base_init(base_ti, ti->parent); + + if (ti->base_init) { + ti->base_init(base_ti->class); + } +} + +static size_t type_class_get_size(TypeImpl *ti) +{ + if (ti->class_size) { + return ti->class_size; + } + + if (ti->parent) { + return type_class_get_size(type_get_instance(type_get_by_name(ti->parent))); + } + + return sizeof(TypeClass); +} + +static void type_class_interface_init(TypeImpl *ti, InterfaceImpl *iface) +{ + TypeInfo info = { + .instance_size = sizeof(Interface), + .parent = iface->parent, + .class_size = sizeof(InterfaceClass), + .class_init = iface->interface_initfn, + .abstract = true, + }; + + iface->type = type_register_anonymous(&info); +} + +static void type_class_init(TypeImpl *ti) +{ + size_t class_size = sizeof(TypeClass); + int i; + + if (ti->class) { + return; + } + + ti->class_size = type_class_get_size(ti); + + ti->class = qemu_malloc(ti->class_size); + ti->class->type = ti->type; + + if (ti->parent) { + TypeImpl *ti_parent; + + ti_parent = type_get_instance(type_get_by_name(ti->parent)); + + type_class_init(ti_parent); + + class_size = ti_parent->class_size; + assert(ti_parent->class_size <= ti->class_size); + + memcpy((void *)ti->class + sizeof(TypeClass), + (void *)ti_parent->class + sizeof(TypeClass), + ti_parent->class_size - sizeof(TypeClass)); + } + + memset((void *)ti->class + class_size, 0, ti->class_size - class_size); + + type_class_base_init(ti, ti->parent); + + for (i = 0; i < ti->num_interfaces; i++) { + type_class_interface_init(ti, &ti->interfaces[i]); + } + + if (ti->class_init) { + ti->class_init(ti->class); + } +} + +static void type_instance_interface_init(TypeInstance *obj, InterfaceImpl *iface) +{ + TypeImpl *ti = type_get_instance(iface->type); + Interface *iface_obj; + static int count; + char buffer[32]; + + snprintf(buffer, sizeof(buffer), "__anonymous_%d", count++); + iface_obj = INTERFACE(type_new(ti->name, buffer)); + iface_obj->obj = obj; + + obj->interfaces = g_slist_prepend(obj->interfaces, iface_obj); +} + +static void type_instance_init(TypeInstance *obj, const char *typename) +{ + TypeImpl *ti = type_get_instance(type_get_by_name(typename)); + int i; + + if (ti->parent) { + type_instance_init(obj, ti->parent); + } + + for (i = 0; i < ti->num_interfaces; i++) { + type_instance_interface_init(obj, &ti->interfaces[i]); + } + + if (ti->instance_init) { + ti->instance_init(obj); + } +} + +static GHashTable *global_object_table; + +void type_initialize(void *data, const char *typename, const char *id) +{ + TypeImpl *ti = type_get_instance(type_get_by_name(typename)); + TypeInstance *obj = data; + + g_assert(ti->instance_size >= sizeof(TypeClass)); + + type_class_init(ti); + + g_assert(ti->abstract == false); + + memset(obj, 0, ti->instance_size); + + obj->class = ti->class; + snprintf(obj->id, sizeof(obj->id), "%s", id); + + if (global_object_table == NULL) { + global_object_table = g_hash_table_new(g_str_hash, g_str_equal); + } + + g_hash_table_insert(global_object_table, obj->id, obj); + + type_instance_init(obj, typename); +} + +static void type_instance_finalize(TypeInstance *obj, const char *typename) +{ + TypeImpl *ti = type_get_instance(type_get_by_name(typename)); + + if (ti->instance_finalize) { + ti->instance_finalize(obj); + } + + while (obj->interfaces) { + Interface *iface_obj = obj->interfaces->data; + obj->interfaces = g_slist_delete_link(obj->interfaces, obj->interfaces); + type_delete(TYPE_INSTANCE(iface_obj)); + } + + if (ti->parent) { + type_instance_init(obj, ti->parent); + } +} + +void type_finalize(void *data) +{ + TypeInstance *obj = data; + TypeImpl *ti = type_get_instance(obj->class->type); + + g_hash_table_remove(global_object_table, obj->id); + + type_instance_finalize(obj, ti->name); +} + +const char *type_get_name(Type type) +{ + TypeImpl *ti = type_get_instance(type); + return ti->name; +} + +TypeInstance *type_new(const char *typename, const char *id) +{ + TypeImpl *ti = type_get_instance(type_get_by_name(typename)); + TypeInstance *obj; + + obj = qemu_malloc(ti->instance_size); + type_initialize(obj, typename, id); + + return obj; +} + +void type_delete(TypeInstance *obj) +{ + type_finalize(obj); + qemu_free(obj); +} + +TypeInstance *type_find_by_id(const char *id) +{ + gpointer data; + + if (global_object_table == NULL) { + return NULL; + } + + data = g_hash_table_lookup(global_object_table, id); + + if (!data) { + return NULL; + } + + return TYPE_INSTANCE(data); +} + +bool type_is_type(TypeInstance *obj, const char *typename) +{ + Type target_type = type_get_by_name(typename); + Type type = obj->class->type; + GSList *i; + + /* Check if typename is a direct ancestor of type */ + while (type) { + TypeImpl *ti = type_get_instance(type); + + if (ti->type == target_type) { + return true; + } + + type = type_get_by_name(ti->parent); + } + + /* Check if obj has an interface of typename */ + for (i = obj->interfaces; i; i = i->next) { + Interface *iface = i->data; + + if (type_is_type(TYPE_INSTANCE(iface), typename)) { + return true; + } + } + + return false; +} + +TypeInstance *type_dynamic_cast(TypeInstance *obj, const char *typename) +{ + GSList *i; + + /* Check if typename is a direct ancestor */ + if (type_is_type(obj, typename)) { + return obj; + } + + /* Check if obj has an interface of typename */ + for (i = obj->interfaces; i; i = i->next) { + Interface *iface = i->data; + + if (type_is_type(TYPE_INSTANCE(iface), typename)) { + return TYPE_INSTANCE(iface); + } + } + + /* Check if obj is an interface and it's containing object is a direct ancestor of typename */ + if (type_is_type(obj, TYPE_INTERFACE)) { + Interface *iface = INTERFACE(obj); + + if (type_is_type(iface->obj, typename)) { + return iface->obj; + } + } + + return NULL; +} + + +static void register_interface(void) +{ + static TypeInfo interface_info = { + .name = TYPE_INTERFACE, + .instance_size = sizeof(Interface), + .abstract = true, + }; + + type_register_static(&interface_info); +} + +device_init(register_interface); + +TypeInstance *type_dynamic_cast_assert(TypeInstance *obj, const char *typename) +{ + TypeInstance *inst; + + inst = type_dynamic_cast(obj, typename); + + if (!inst) { + fprintf(stderr, "Object %p is not an instance of type %s\n", obj, typename); + abort(); + } + + return inst; +} + +TypeClass *type_check_class(TypeClass *class, const char *typename) +{ + Type target_type = type_get_by_name(typename); + Type type = class->type; + + while (type) { + TypeImpl *ti = type_get_instance(type); + + if (ti->type == target_type) { + return class; + } + + type = type_get_by_name(ti->parent); + } + + fprintf(stderr, "Object %p is not an instance of type %d\n", class, (int)type); + abort(); + + return NULL; +} + +const char *type_get_id(TypeInstance *obj) +{ + return obj->id; +} + +const char *type_get_type(TypeInstance *obj) +{ + return type_get_name(obj->class->type); +} + +TypeClass *type_get_class(TypeInstance *obj) +{ + return obj->class; +} + +typedef struct TypeForeachData +{ + void (*enumfn)(TypeInstance *obj, void *opaque); + void *opaque; +} TypeForeachData; + +static void type_foreach_tramp(gpointer key, gpointer value, gpointer opaque) +{ + TypeForeachData *data = opaque; + data->enumfn(TYPE_INSTANCE(value), data->opaque); +} + +void type_foreach(void (*enumfn)(TypeInstance *obj, void *opaque), void *opaque) +{ + TypeForeachData data = { + .enumfn = enumfn, + .opaque = opaque, + }; + + g_hash_table_foreach(global_object_table, type_foreach_tramp, &data); +} + +TypeClass *type_get_super(TypeInstance *obj) +{ + return type_get_instance(type_get_by_name(type_get_instance(obj->class->type)->parent))->class; +} +