/*
Copyright (C) 2001-2008, The Perl Foundation.
$Id: /mirror/trunk/src/pmc/class.pmc 35552 2009-01-14T09:13:15.484200Z cotto  $

=head1 NAME

src/pmc/class.pmc - defines a class

=head1 DESCRIPTION

This class implements the Class PMC, as outlined in
F<docs/pdds/pdd15_objects.pod>.

Class is not derived from any other PMC.

=head2 Structure

The Class PMC structure (C<Parrot_Class>) consists of twelve items:

=over 4

=item C<id>

The type number of the PMC. [deprecated: See RT #48024]

=item C<name>

The name of the class -- a STRING.
An empty STRING is allocated during initialization.

=item C<namespace>

The namespace the class is associated with, if any.
A Null PMC is allocated during initialization.

=item C<instantiated>

A flag denoting whether this class has been instantiated since last
modification.  A native integer with value zero is allocated during
initialization.

=item C<parents>

An array of immediate parent classes.
An empty ResizablePMCArray PMC is allocated during initialization.

=item C<all_parents>

A cached array of ourself and all parent classes, in MRO order.
A ResizablePMCArray PMC is allocated during initialization,
and is populated with the current class.

=item C<roles>

An array of the roles this class has been composed from.
An empty ResizablePMCArray PMC is allocated during initialization.

=item C<methods>

A directory of method names and method bodies this class provides.
An empty Hash PMC is allocated during initialization.

=item C<vtable_overrides>

A directory of vtable method names and method bodies this class overrides.
An empty Hash PMC is allocated during initialization.

=item C<attrib_metadata>

A directory of attribute names and attribute metadata this class contains.
An empty Hash PMC is allocated during initialization.

=item C<attrib_index>

A lookup table for attributes in this class and parents.
A Null PMC is allocated during initialization.

=item C<attrib_cache>

A cache of visible attribute names to attribute indexes.
A Null PMC is allocated during initialization.

=item C<resolve_method>

A list of method names the class provides used for name conflict resolution.
An empty ResizablePMCArray PMC is allocated during initialization.

=cut

*/

#define PARROT_IN_OBJECTS_C /* To get the vtable.h imports we want. */
#include "parrot/parrot.h"
#include "parrot/oo_private.h"
#include "pmc_object.h"
#include "pmc_namespace.h"

/* This function builds the attribute index (table to map class name and
 * attribute name to an index) for the current class. */
static void
build_attrib_index(PARROT_INTERP, PMC *self)
{
    Parrot_Class_attributes * const _class      = PARROT_CLASS(self);
    int                  cur_index    = 0;
    PMC * const          attrib_index = pmc_new(interp, enum_class_Hash);
    PMC * const          cache        = pmc_new(interp, enum_class_Hash);
    const int            num_classes  = VTABLE_elements(interp, _class->all_parents);
    int                  i;

    /* We will go over the list of all parents to construct the attribute index. */
    for (i = 0; i < num_classes; i++) {
       /* Get the class and check that it respects the standard class interface
        * (if not we don't know how it stores its attributes, so we'll have to
        * delegate the lookup). */
        PMC * const cur_class = VTABLE_get_pmc_keyed_int(interp,
                                    _class->all_parents, i);

        if (PObj_is_class_TEST(cur_class)) {
            /* The attribute metadata hash. */
            Parrot_Class_attributes * const class_info = PARROT_CLASS(cur_class);
            PMC          * const attribs     = class_info->attrib_metadata;
            PMC          * const iter        = VTABLE_get_iter(interp, attribs);

            /* Build a string representing the fully qualified class name. */
            /* Retrieve the fully qualified class name for the class. */
            STRING       * const fq_class    = VTABLE_get_string(interp, cur_class);
            PMC          * const class_cache = pmc_new(interp, enum_class_Hash);
            VTABLE_set_pmc_keyed_str(interp, cache, fq_class, class_cache);

            /* Iterate over the attributes. */
            while (VTABLE_get_bool(interp, iter)) {
                /* Get attribute. */
                PMC    * const cur_attrib  = VTABLE_get_pmc_keyed_str(interp,
                        attribs, VTABLE_shift_string(interp, iter));

                /* Get attribute name and append it to the key. */
                STRING * const name_str    = CONST_STRING(interp, "name");
                STRING * const attrib_name = VTABLE_get_string_keyed_str(
                    interp, cur_attrib, name_str);

                STRING *full_key           = string_append(interp, fq_class, attrib_name);

                /* Insert into hash, along with index. */
                VTABLE_set_integer_keyed_str(interp, attrib_index, full_key, cur_index);
                VTABLE_set_integer_keyed_str(interp, class_cache, attrib_name, cur_index);
                cur_index++;
            }
        }
    }

    /* Store built attribute index and invalidate cache. */
    _class->attrib_index = attrib_index;
    _class->attrib_cache = cache;
}

/* Takes a hash and initializes the class based on it. */
static void
init_class_from_hash(PARROT_INTERP, PMC *self, PMC *info)
{
    Parrot_Class_attributes * const _class      = PARROT_CLASS(self);
    STRING       * const name_str    = CONST_STRING(interp, "name");
    STRING       * const parents_str = CONST_STRING(interp, "parents");
    STRING       * const methods_str = CONST_STRING(interp, "methods");
    STRING       * const roles_str   = CONST_STRING(interp, "roles");
    STRING       * const attrs_str   = CONST_STRING(interp, "attributes");
    PMC          *old_ns;
    STRING       *resolve_method_str;
    INTVAL        type_num;

    /* Ensure we actually have some initialization info. */
    if (PMC_IS_NULL(info))
        return;

    /* Take a copy of the current namespace the class is attached to. */
    old_ns = _class->_namespace;

    /* Check if we have a name/namespace. */
    if (VTABLE_exists_keyed_str(interp, info, name_str)) {
        STRING *new_name;
        PMC    *new_namespace;
        PMC    *name_arg = VTABLE_get_pmc_keyed_str(interp, info, name_str);
        VTABLE *new_vtable;

        /* If we were passed a namespace PMC, set the namespace attribute
         * directly. Otherwise, lookup or create the appropriate namespace. */
        if (name_arg->vtable->base_type == enum_class_NameSpace) {
            new_namespace = name_arg;
            name_arg      = Parrot_ns_get_name(interp, new_namespace);
            VTABLE_shift_string(interp, name_arg);
        }
        else {
            PMC * const hll_ns = VTABLE_get_pmc_keyed_int(interp,
                    interp->HLL_namespace, CONTEXT(interp)->current_HLL);
            new_namespace = Parrot_make_namespace_keyed(interp, hll_ns, name_arg);
        }

        if (PMC_IS_NULL(new_namespace))
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                "Failed to set namespace for class.");

        /* Set the name of the class to the name of the innermost namespace
         * associated with the class.  */
        new_name = VTABLE_get_string(interp, new_namespace);

        if (STRING_IS_NULL(new_name) || STRING_IS_EMPTY(new_name))
              Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                "Failed to set name for class.");

        _class->_namespace = new_namespace;
        _class->name       = new_name;

        /* At this point we know the class isn't anonymous */
        CLASS_is_anon_CLEAR(self);

        /* Register a type number for the class. */
        type_num   = Parrot_oo_register_type(interp, name_arg, new_namespace);

        /* Link the type number with the class's vtable. */
        new_vtable = Parrot_clone_vtable(interp, self->vtable);

        new_vtable->base_type         = type_num;
        new_vtable->pmc_class         = self;
        new_vtable->whoami            = VTABLE_get_string(interp, self);
        new_vtable->mro               = _class->all_parents;
        new_vtable->ro_variant_vtable =
                Parrot_clone_vtable(interp, self->vtable->ro_variant_vtable);

        /* Store the class's vtable in the global table */
        interp->vtables[type_num]     = new_vtable;

        _class->id                    = type_num;
    }

    /* If we were attached to a namespace and are now attached to a new one,
     * need to unset ourselves in the old namespace. */
    if (!PMC_IS_NULL(old_ns) && _class->_namespace != old_ns)
        Parrot_PCCINVOKE(interp, old_ns, CONST_STRING(interp, "set_class"), "P->", PMCNULL);

    /* Link namespace to this class, if there is one. */
    if (!PMC_IS_NULL(_class->_namespace)) {
        STRING * const set_class_str = CONST_STRING(interp, "set_class");
        Parrot_PCCINVOKE(interp, _class->_namespace, set_class_str, "P->", self);
    }

    /* Initialize resolve_method. */
    resolve_method_str = CONST_STRING(interp, "resolve_method");
    if (VTABLE_exists_keyed_str(interp, info, resolve_method_str)) {
        /* Set it. */
        _class->resolve_method =
            VTABLE_get_pmc_keyed_str(interp, info, resolve_method_str);
    }

    /* Initialize parents, if we have any. */
    if (VTABLE_exists_keyed_str(interp, info, parents_str)) {
        /* Loop over parents array and add them. */
        PMC * const parent_list = VTABLE_get_pmc_keyed_str(interp, info,
            parents_str);
        const int  parent_count = VTABLE_elements(interp, parent_list);
        int i;

        for (i = 0; i < parent_count; i++)
            VTABLE_add_parent(interp, self,
                VTABLE_get_pmc_keyed_int(interp, parent_list, i));
    }

    /* Initialize roles, if we have any. */
    if (VTABLE_exists_keyed_str(interp, info, roles_str)) {
        /* Loop over roles array and compose them. */
        PMC * const role_list  = VTABLE_get_pmc_keyed_str(interp, info,
            roles_str);
        const int   role_count = VTABLE_elements(interp, role_list);
        int i;

        for (i = 0; i < role_count; i++)
            VTABLE_add_role(interp, self,
                VTABLE_get_pmc_keyed_int(interp, role_list, i));
    }

    /* Initialize attributes, if we have any. */
    if (VTABLE_exists_keyed_str(interp, info, attrs_str)) {
        /* Loop over attributes array and add them. */
        PMC * const attrs_name_list = VTABLE_get_pmc_keyed_str(interp, info,
                                            attrs_str);
        const int   attrib_count    = VTABLE_elements(interp, attrs_name_list);
        int i;

        for (i = 0; i < attrib_count; i++) {
            STRING * const attr_name = VTABLE_get_string_keyed_int(interp,
                attrs_name_list, i);
            VTABLE_add_attribute(interp, self, attr_name, PMCNULL);
        }
    }

    /* Initialize methods. */
    if (VTABLE_exists_keyed_str(interp, info, methods_str)) {
        /* Get the methods hash. */
        PMC * const methods = VTABLE_get_pmc_keyed_str(interp, info,
            methods_str);

        /* Iterate over the list of methods. */
        PMC * const iter    = VTABLE_get_iter(interp, methods);

        while (VTABLE_get_bool(interp, iter)) {
            /* Add the method. */
            STRING * const method_name = VTABLE_shift_string(interp, iter);
            PMC    * const method_pmc  = VTABLE_get_pmc_keyed_str(interp,
                methods, method_name);
            VTABLE_add_method(interp, self, method_name, method_pmc);
        }
    }

    /* Extract any methods from the namespace */
    Parrot_oo_extract_methods_from_namespace(interp, self, _class->_namespace);
}

static void
initialize_parents(PARROT_INTERP, PMC *object, PMC *all_parents)
{
    INTVAL  parent_index = VTABLE_elements(interp, all_parents) - 1;
    STRING * const name  = CONST_STRING(interp, "init");

    /* Loop through the parents in reverse MRO order. */
    for (; parent_index >= 0; parent_index--) {
        PMC *meth;
        PMC * const parent = VTABLE_get_pmc_keyed_int(interp,
            all_parents, parent_index);

        /* PMCProxy parents store an instance to delegate to */
        if (parent->vtable->base_type == enum_class_PMCProxy) {
            PMC    *proxy     = VTABLE_instantiate(interp, parent, PMCNULL);
            STRING *proxy_str = CONST_STRING(interp, "proxy");
            VTABLE_set_attr_keyed(interp, object, parent, proxy_str, proxy);
        }

        meth = Parrot_oo_find_vtable_override_for_class(interp, parent, name);

        if (!PMC_IS_NULL(meth))
            Parrot_run_meth_fromc_args(interp, meth, object, name, "v");
    }
}

static void
initialize_parents_pmc(PARROT_INTERP, PMC *object, PMC *all_parents, PMC *init)
{
    INTVAL  parent_index = VTABLE_elements(interp, all_parents) - 1;
    STRING * const name  = CONST_STRING(interp, "init_pmc");

    /* Loop through the parents in reverse MRO order. */
    for (; parent_index >= 0; parent_index--) {
        PMC *meth;
        PMC * const parent = VTABLE_get_pmc_keyed_int(interp,
            all_parents, parent_index);

        /* PMCProxy parents store an instance to delegate to */
        if (parent->vtable->base_type == enum_class_PMCProxy) {
            PMC    * const proxy     = VTABLE_instantiate(interp, parent, init);
            STRING * const proxy_str = CONST_STRING(interp, "proxy");
            VTABLE_set_attr_keyed(interp, object, parent, proxy_str, proxy);
        }

        meth = Parrot_oo_find_vtable_override_for_class(interp, parent, name);

        if (!PMC_IS_NULL(meth))
            Parrot_run_meth_fromc_args(interp, meth, object, name, "vP", init);
    }
}

/* This function makes and caches the name of this class, returning the string
 * directly.  VTABLE_isa() uses the name without copying it, for efficiency
 * reasons, as it does not modify the STRING.  VTABLE_get_string() makes a copy
 * of the STRING, so its callers are free to modify it. */
static STRING *
make_class_name(PARROT_INTERP, PMC *SELF)
{
    Parrot_Class_attributes * const  _class    = PARROT_CLASS(SELF);
    PMC          * const _namespace = _class->_namespace;

    if (!PMC_IS_NULL(_namespace)) {
        if (_class->fullname)
            return _class->fullname;
        else {

            /* Call the 'get_name' method on the class's associated
             * namespace to retrieve a fully qualified list of names, then
             * join the list with a semicolon.  */
            PMC * const names = Parrot_ns_get_name(interp, _namespace);

            if (!PMC_IS_NULL(names))
                /* remove the HLL namespace name */
                VTABLE_shift_string(interp, names);
                _class->fullname = string_join(interp, CONST_STRING(interp, ";"), names);
                return _class->fullname;
        }
    }

    /* Otherwise, copy the stored string name of the class. */
    return _class->name;
}

/*

=back

=head2 Functions

=over 4

=cut

*/

pmclass Class
    need_ext {

    ATTR INTVAL id;             /* The type number of the PMC. [deprecated: See RT #48024] */
    ATTR STRING *name;          /* The name of the class. */
    ATTR STRING *fullname;      /* The name of the class. */
    ATTR PMC *_namespace;       /* The namespace it's linked to, if any. */
    ATTR INTVAL instantiated;   /* Any instantiations since last modification? */
    ATTR PMC *parents;          /* Immediate parent classes. */
    ATTR PMC *all_parents;      /* Cached list of ourself and all parents, in MRO order. */
    ATTR PMC *roles;            /* An array of roles. */
    ATTR PMC *methods;          /* Hash of method names to methods in this class. */
    ATTR PMC *vtable_overrides; /* Hash of Parrot v-table methods we override. */
    ATTR PMC *attrib_metadata;  /* Hash of attributes in this class to hashes of metadata. */
    ATTR PMC *attrib_index;     /* Lookup table for attributes in this and parents. */
    ATTR PMC *attrib_cache;     /* Cache of visible attrib names to indexes. */
    ATTR PMC *resolve_method;   /* List of method names the class provides to resolve
                                 * conflicts with methods from roles. */
    ATTR PMC *parent_overrides;

/*

=item C<void init()>

Initializes a Class PMC.

=item C<void init_pmc(PMC *init_data)>

The actual class creation code, called from C<newclass> opcode. The C<init_data>
argument may be either the name of the class or a hash of initialization
arguments. The class is attached to the current HLL namespace.

=cut

*/

    VTABLE void init() {
        Parrot_Class_attributes * const _class = mem_allocate_zeroed_typed(Parrot_Class_attributes);

        /* Set flags for custom DOD mark and destroy. */
        PObj_custom_mark_SET(SELF);
        PObj_active_destroy_SET(SELF);

        /* Set up the object. */
        PMC_data(SELF)          = _class;
        _class->name            = CONST_STRING(interp, "");
        _class->_namespace      = PMCNULL;
        _class->parents         = pmc_new(interp, enum_class_ResizablePMCArray);
        _class->all_parents     = pmc_new(interp, enum_class_ResizablePMCArray);
        _class->roles           = pmc_new(interp, enum_class_ResizablePMCArray);
        _class->methods         = pmc_new(interp, enum_class_Hash);
        _class->attrib_metadata = pmc_new(interp, enum_class_Hash);
        _class->attrib_index    = PMCNULL;
        _class->attrib_cache    = PMCNULL;
        _class->resolve_method  = pmc_new(interp, enum_class_ResizablePMCArray);

        _class->vtable_overrides = pmc_new(interp, enum_class_Hash);
        _class->parent_overrides = constant_pmc_new(interp, enum_class_Hash);

        /* We put ourself on the all parents list. */
        VTABLE_push_pmc(interp, _class->all_parents, SELF);

        /* We are a class. */
        PObj_is_class_SET(SELF);

        /* By default we're anonymous. */
        CLASS_is_anon_SET(SELF);
    }

    VTABLE void init_pmc(PMC *init_data) {
        PMC           *arg, *ns_name;
        const INTVAL   arg_type = VTABLE_type(interp, init_data);
        STRING * const name_str = CONST_STRING(interp, "name");

        /* Set up the object. */
        SELF.init();

        /* fast attempt to determine init_data type */
        switch (arg_type) {
            case enum_class_String:
            case enum_class_Key:
            case enum_class_ResizableStringArray:
            case enum_class_NameSpace:
                /* set only the name property */
                arg = pmc_new(interp, enum_class_Hash);
                VTABLE_set_pmc_keyed_str(interp, arg, name_str, init_data);
                break;

            case enum_class_Hash:
                arg = init_data;
                break;

            /* slow attempt to determine init_data type */
            default:
                if (VTABLE_isa(interp, init_data, CONST_STRING(interp, "String"))
                || VTABLE_isa(interp, init_data, CONST_STRING(interp, "Key"))
                || VTABLE_isa(interp, init_data, CONST_STRING(interp, "ResizableStringArray"))) {
                    /* set only the name property */
                    arg = pmc_new(interp, enum_class_Hash);
                    VTABLE_set_pmc_keyed_str(interp, arg, name_str, init_data);
                }

                if (VTABLE_isa(interp, init_data, CONST_STRING(interp, "Hash")))
                    arg = init_data;
                else
                    Parrot_ex_throw_from_c_args(INTERP, NULL,
                        EXCEPTION_INVALID_OPERATION,
                            "Invalid class name key in init_pmc for Class");
                break;
        }

        /* Initialize the class with the supplied data. */
        init_class_from_hash(interp, SELF, arg);
    }

/*

=item C<PMC *subclass(PMC *name)>

Creates a subclass, optionally with a given C<name>.

=cut

*/

    PMC *subclass(PMC *name) {
        PMC *child_class;

        /* Create a new class. Set the classname, if we have one. */
        if (PMC_IS_NULL(name)) {
            child_class = pmc_new(interp, enum_class_Class);
        }
        else {
            PMC * const naming_hash = pmc_new(interp, enum_class_Hash);

            VTABLE_set_pmc_keyed_str(interp, naming_hash, CONST_STRING(interp, "name"), name);
            child_class = pmc_new_init(interp, enum_class_Class, naming_hash);
        }

        /* Make the new class a subclass of the current class */
        VTABLE_add_parent(interp, child_class, SELF);

        return child_class;
    }

/*

=item C<void destroy()>

Frees the memory associated with the class's underlying struct.

=cut

*/

    VTABLE void destroy() {
        mem_sys_free(PMC_data(SELF));
    }

/*

=item C<STRING *get_string()>

Returns the name of the class (without the HLL namespace).

=cut

*/

    VTABLE STRING *get_string() {
        return string_copy(interp, make_class_name(interp, SELF));
    }

/*

=item C<void mark()>

Marks any referenced strings and PMCs in the structure as live.

=cut

*/

    VTABLE void mark() {
        Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
        if (_class->name)
            pobject_lives(interp, (PObj *)_class->name);
        if (_class->fullname)
            pobject_lives(interp, (PObj *)_class->fullname);
        if (_class->_namespace)
            pobject_lives(interp, (PObj *)_class->_namespace);
        if (_class->parents)
            pobject_lives(interp, (PObj *)_class->parents);
        if (_class->all_parents)
            pobject_lives(interp, (PObj *)_class->all_parents);
        if (_class->roles)
            pobject_lives(interp, (PObj *)_class->roles);
        if (_class->methods)
            pobject_lives(interp, (PObj *)_class->methods);
        if (_class->vtable_overrides)
            pobject_lives(interp, (PObj *)_class->vtable_overrides);
        if (_class->parent_overrides)
            pobject_lives(interp, (PObj *)_class->parent_overrides);
        if (_class->attrib_metadata)
            pobject_lives(interp, (PObj *)_class->attrib_metadata);
        if (_class->attrib_index)
            pobject_lives(interp, (PObj *)_class->attrib_index);
        if (_class->attrib_cache)
            pobject_lives(interp, (PObj *)_class->attrib_cache);
        if (_class->resolve_method)
            pobject_lives(interp, (PObj *)_class->resolve_method);
    }


/*

=item C<void add_attribute(STRING *name, PMC *type)>

Adds the given attribute (C<name>) with an optional C<type>.
Creates a new class if the current class has been instantiated.
Enters the attribute in the C<attrib_metadata> table.
Returns an error if an attribute of C<name> already exists.

=cut

*/

    VTABLE void add_attribute(STRING *name, PMC *type) {
        Parrot_Class_attributes * const _class        = PARROT_CLASS(SELF);
        PMC          * const new_attribute = pmc_new(interp, enum_class_Hash);

        /* If we've been instantiated already, not allowed. */
        if (_class->instantiated)
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                "Modifications to classes are not allowed after instantiation.");

        /* If we already have an attribute of this name, it's an error. */
        if (VTABLE_exists_keyed_str(interp, _class->attrib_metadata, name))
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                "Attribute '%Ss' already exists in '%Ss'.", name,
                VTABLE_get_string(INTERP, SELF));

        /* Set name and type. */
        VTABLE_set_string_keyed_str(interp, new_attribute, CONST_STRING(interp, "name"), name);

        if (!PMC_IS_NULL(type))
            VTABLE_set_pmc_keyed_str(interp, new_attribute, CONST_STRING(interp, "type"), type);

        /* Enter the attribute in the attrib_metadata table. */
        VTABLE_set_pmc_keyed_str(interp, _class->attrib_metadata, name,
            new_attribute);
    }

/*

=item C<void add_method(STRING *name, PMC *sub)>

Adds the given sub PMC as a method with the given name.

=cut

*/
    VTABLE void add_method(STRING *name, PMC *sub) {
        Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);

        /* If we have already added a method with this name... */
        if (VTABLE_exists_keyed_str(interp, _class->methods, name))
            /* RT #46095 Need to handle multi methods here. */
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                "A method named '%S' already exists in class '%S'. "
                "It may have been supplied by a role.",
                name, VTABLE_get_string(interp, SELF));

        /* Enter it into the table. */
        VTABLE_set_pmc_keyed_str(interp, _class->methods, name, sub);
    }

/*

=item C<void remove_method(STRING *name, PMC *sub)>

Removes the method with the given name.

=cut

*/
    VTABLE void remove_method(STRING *name) {
        Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
        if (VTABLE_exists_keyed_str(interp, _class->methods, name))
            VTABLE_delete_keyed_str(interp, _class->methods, name);
        else
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                "No method named '%S' to remove in class '%S'.",
                name, VTABLE_get_string(interp, SELF));
    }

/*

=item C<void add_vtable_override(STRING *name, PMC *sub)>

Adds the given sub PMC as a vtable override with the given name.

=cut

*/
    VTABLE void add_vtable_override(STRING *name, PMC *sub) {
        Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);

        /* If we have already added a vtable override with this name... */
        if (VTABLE_exists_keyed_str(interp, _class->vtable_overrides, name))
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                "A vtable override named '%S' already exists in class '%S'. "
                "It may have been supplied by a role.",
                name, VTABLE_get_string(interp, SELF));

        /* Check that the name is actually valid as a vtable override */
        if (Parrot_get_vtable_index(interp, name) == -1)
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_METHOD_NOT_FOUND,
                "'%S' is not a valid vtable function name.", name);

        /* Add it to vtable methods list. */
        VTABLE_set_pmc_keyed_str(interp, _class->vtable_overrides, name, sub);
    }

/*

=item C<void add_parent(PMC *parent)>

Adds the supplied PMC to the list of parents for the class.

=cut

*/
    VTABLE void add_parent(PMC *parent) {
        Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);

        /* get name of the parent being added */
        STRING * const parent_name  = VTABLE_get_string(interp, parent);

        /* get number of direct parents */
        int parent_count      = VTABLE_elements(interp, _class->parents);

        int index; /* loop iterator */

        /* If we've been instantiated already, not allowed. */
        if (_class->instantiated)
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                "Modifications to classes are not allowed after instantiation.");

        /* Ensure it really is a class. */
        if (!PObj_is_class_TEST(parent))
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                    "Parent isn't a Class.");

        /* iterate over all direct parents, check whether this class already has
         * the parent being added. */
        for (index = 0; index < parent_count; index++) {
            /* get the next parent */
            PMC * const current_parent = VTABLE_get_pmc_keyed_int(interp,
                                      _class->parents, index);

            /* get the name of the current parent */
            STRING * const current_name = VTABLE_get_string(interp, current_parent);

            /* throw an exception if we already have this parent */
            if (string_equal(interp, current_name, parent_name) == 0)
                Parrot_ex_throw_from_c_args(interp, NULL,
                    EXCEPTION_INVALID_OPERATION,
                    "The class '%S' already has a parent class '%S'. "
                    "It may have been supplied by a role.",
                    VTABLE_get_string(interp, SELF), parent_name);
        }
        /* Check is not self */
        if (parent == SELF)
            Parrot_ex_throw_from_c_args(interp, NULL,
                    EXCEPTION_INVALID_OPERATION, "Can't be own parent");

        /* Check that none of the parents is self */
        parent_count = VTABLE_elements(interp,
            PARROT_CLASS(parent)->all_parents);
        for (index = 0; index < parent_count; index++) {
            /* get the next parent */
            PMC * const current_parent = VTABLE_get_pmc_keyed_int(interp,
                PARROT_CLASS(parent)->all_parents, index);
            if (current_parent == SELF)
                Parrot_ex_throw_from_c_args(interp, NULL,
                    EXCEPTION_INVALID_OPERATION,
                    "Loop in class hierarchy: '%S' is an ancestor of '%S'.",
                    VTABLE_get_string(interp, SELF),
                    VTABLE_get_string(interp, parent));
        }

        /* Add to the lists of our immediate parents and all parents. */
        VTABLE_push_pmc(interp, _class->parents, parent);
        _class->all_parents = Parrot_ComputeMRO_C3(interp, SELF);

        /* Anonymous classes have no entry in the vtable array */
        if (!CLASS_is_anon_TEST(SELF))
            interp->vtables[VTABLE_type(interp, SELF)]->mro = _class->all_parents;
    }

/*

=item C<void add_role(PMC *role)>

Adds the supplied PMC to the list of roles for the class, provided there are
no conflicts.

=cut

*/
    VTABLE void add_role(PMC *role) {
        Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);

        /* Do the composition. */
        Parrot_ComposeRole(interp, role,
            _class->resolve_method, !PMC_IS_NULL(_class->resolve_method),
           PMCNULL, 0, _class->methods, _class->roles);
    }

/*

=item C<PMC *inspect_str(STRING *what)>

Provides introspection of a specific piece of information about the class. The
available information is:

=over 8

=item name

String PMC containing the name of the class

=item namespace

NameSpace PMC of the the namespace attached to the class.

=item attributes

Hash keyed on attribute name, where the value is a hash describing it.

=item methods

Hash keyed on method name, value is an invokable PMC. Includes methods composed
in from roles.

=item roles

Array of Role PMCs. Includes roles done by the roles that were composed into
this class.

=item parents

Array of Class PMCs representing the direct parents of this class.

=back

=cut

*/
    VTABLE PMC *inspect_str(STRING *what) {
        Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);

        /* What should we return? */
        PMC *found;

        if (string_equal(interp, what, CONST_STRING(interp, "name")) == 0) {
            found = pmc_new(interp, enum_class_String);
            VTABLE_set_string_native(interp, found, _class->name);
        }
        else if (string_equal(interp, what, CONST_STRING(interp, "id")) == 0) {
            found = pmc_new(interp, enum_class_Integer);
            VTABLE_set_integer_native(interp, found, _class->id);
        }
        else if (string_equal(interp, what, CONST_STRING(interp, "namespace")) == 0) {
            /* Should not clone this. */
            return _class->_namespace;
        }
        else if (string_equal(interp, what, CONST_STRING(interp, "attributes")) == 0) {
            found = _class->attrib_metadata;
        }
        else if (string_equal(interp, what, CONST_STRING(interp, "attrib_index")) == 0) {
            found = _class->attrib_index;
        }
        else if (string_equal(interp, what, CONST_STRING(interp, "methods")) == 0) {
            found = _class->methods;
        }
        else if (string_equal(interp, what, CONST_STRING(interp, "vtable_overrides")) == 0) {
            found = _class->vtable_overrides;
        }
        else if (string_equal(interp, what, CONST_STRING(interp, "parents")) == 0) {
            found = _class->parents;
        }
        else if (string_equal(interp, what, CONST_STRING(interp, "all_parents")) == 0) {
            found = _class->all_parents;
        }
        else if (string_equal(interp, what, CONST_STRING(interp, "roles")) == 0) {
            found = _class->roles;
        }
        else if (string_equal(interp, what, CONST_STRING(interp, "flags")) == 0) {
            found = pmc_new(interp, enum_class_Integer);
            VTABLE_set_integer_native(interp, found,
                (INTVAL)PObj_get_FLAGS(SELF));
        }
        else
            Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
                "Unknown introspection value '%S'", what);

        /* return found value */
        if (PMC_IS_NULL(found)) { return PMCNULL; }
        if (found->vtable->base_type == enum_class_Hash) {
            /* for Hash return values, create and return a shallow
             * clone because the VTABLE_clone does a deep clone */
            PMC * const hash = pmc_new(interp, enum_class_Hash);
            PMC * const iter = VTABLE_get_iter(interp, found);
            while (VTABLE_get_bool(interp, iter)) {
                STRING * key = VTABLE_shift_string(interp, iter);
                PMC * value  = VTABLE_get_pmc_keyed_str(interp, found, key);
                VTABLE_set_pmc_keyed_str(interp, hash, key, value);
            }
            return hash;
        }
        return VTABLE_clone(interp, found);
    }

/*

=item C<PMC *inspect()>

Returns a Hash describing the class, with key/value pairs as described in
inspect_str.

=cut

*/
    VTABLE PMC *inspect() {
        /* Create a hash, then use inspect_str to get all of the data to
         * fill it up with. */
        PMC    * const metadata    = pmc_new(interp, enum_class_Hash);
        STRING * const name_str    = CONST_STRING(interp, "name");
        STRING * const ns_str      = CONST_STRING(interp, "namespace");
        STRING * const attrs_str   = CONST_STRING(interp, "attributes");
        STRING * const meths_str   = CONST_STRING(interp, "methods");
        STRING * const parents_str = CONST_STRING(interp, "parents");
        STRING * const roles_str   = CONST_STRING(interp, "roles");
        STRING * const flags_str   = CONST_STRING(interp, "flags");

        VTABLE_set_pmc_keyed_str(interp, metadata, name_str,
            VTABLE_inspect_str(interp, SELF, name_str));

        VTABLE_set_pmc_keyed_str(interp, metadata, ns_str,
            VTABLE_inspect_str(interp, SELF, ns_str));

        VTABLE_set_pmc_keyed_str(interp, metadata, attrs_str,
            VTABLE_inspect_str(interp, SELF, attrs_str));

        VTABLE_set_pmc_keyed_str(interp, metadata, meths_str,
            VTABLE_inspect_str(interp, SELF, meths_str));

        VTABLE_set_pmc_keyed_str(interp, metadata, parents_str,
            VTABLE_inspect_str(interp, SELF, parents_str));

        VTABLE_set_pmc_keyed_str(interp, metadata, roles_str,
            VTABLE_inspect_str(interp, SELF, roles_str));

        VTABLE_set_pmc_keyed_str(interp, metadata, flags_str,
            VTABLE_inspect_str(interp, SELF, flags_str));

        return metadata;
    }

/*

=item C<PMC *clone()>

Returns an anonymous copy of the class (with no name and no link to a
namespace). Unsets the instantiated flag, allowing modifications.

=cut

*/

    VTABLE PMC *clone() {
        Parrot_Class_attributes * const _class    = PARROT_CLASS(SELF);

        /* Create the new class PMC, of the same type of this one (we may
         * have been subclassed). */
        PMC          * const copy      = SUPER();

        /* Clone parents, roles, methods, attributes and resolve data. We do
         * not copy name/namespace related stuff (need anonymous clone) or
         * stuff that gets computed on the first instantiation. */

        Parrot_Class_attributes * const new_class = PARROT_CLASS(copy);

        new_class->name                = CONST_STRING(interp, "");
        new_class->_namespace          = PMCNULL;
        new_class->parents             = VTABLE_clone(interp, _class->parents);
        new_class->roles               = VTABLE_clone(interp, _class->roles);
        new_class->methods             = VTABLE_clone(interp, _class->methods);
        new_class->vtable_overrides    = VTABLE_clone(interp,
                                            _class->vtable_overrides);
        new_class->parent_overrides    = VTABLE_clone(interp,
                                            _class->parent_overrides);
        new_class->attrib_metadata     = VTABLE_clone(interp,
                                            _class->attrib_metadata);
        new_class->resolve_method      = VTABLE_clone(interp,
                                            _class->resolve_method);

        /* Return cloned class. */
        return copy;
    }

/*

=item C<PMC *clone_pmc(PMC *args)>

Makes a copy of the class, then modifies or adds to it based upon the contents
of the supplied initialization data. If a new name or namespace is not supplied
in C<args> then the cloned class will be anonymous. The instantiated flag is
unset to allow further modifications.

=cut

*/

    VTABLE PMC *clone_pmc(PMC *args) {
        /* Do the standard clone. */
        PMC * const copy = SELF.clone();

        init_class_from_hash(interp, copy, args);

        return copy;
    }

/*

=item C<PMC *instantiate(PMC *init)>

Creates a new PMC object of the type of the class and calls init().

=cut

*/

    VTABLE PMC *instantiate(PMC *init) {
        Parrot_Class_attributes  * const _class    = PARROT_CLASS(SELF);
        STRING                   * const class_str = CONST_STRING(interp, "Class");

        Parrot_Object_attributes *obj_guts;
        PMC                      *object;

        /* If we've not been instantiated before... */
        if (!_class->instantiated) {
            /* Check that we have all methods listed in resolve list. */
            const int resolve_count = VTABLE_elements(interp, _class->resolve_method);
            INTVAL    cur_hll       = CONTEXT(interp)->current_HLL;
            INTVAL    mro_length;
            int       i;

            /* don't use HLL mappings for internal-only data */
            CONTEXT(interp)->current_HLL = 0;

            for (i = 0; i < resolve_count; i++) {
                STRING * const check_meth =
                    VTABLE_get_string_keyed_int(interp, _class->resolve_method, i);
                if (!VTABLE_exists_keyed_str(interp, _class->methods, check_meth))
                    Parrot_ex_throw_from_c_args(interp, NULL,
                        EXCEPTION_METHOD_NOT_FOUND, "The method '%S' was named "
                        "in the resolve list, but not supplied", check_meth);
            }

            /* Build full parents list.
             * RT #46101 Need pluggable MRO, for now always do C3. */
            _class->all_parents = Parrot_ComputeMRO_C3(interp, SELF);

            if (!CLASS_is_anon_TEST(SELF))
                interp->vtables[VTABLE_type(interp, SELF)]->mro = _class->all_parents;

            build_attrib_index(interp, SELF);

            if (PMC_IS_NULL(_class->attrib_index))
                return PMCNULL;

            /* See if we have any parents from other universes and if so set a
             * flag stating so. */
            mro_length = VTABLE_elements(interp, _class->all_parents);

            for (i = 0; i < mro_length; i++) {
                PMC * const class_check = VTABLE_get_pmc_keyed_int(interp,
                    _class->all_parents, i);
                if (class_check->vtable->base_type != enum_class_Class) {
                    /* Found one; that's enough. */
                    CLASS_has_alien_parents_SET(SELF);
                    break;
                }
            }

            CONTEXT(interp)->current_HLL = cur_hll;
        }

        /* Set instantiated flag. */
        _class->instantiated = 1;

        /* Create object. */
        object = pmc_new_noinit(interp, enum_class_Object);

        /* Set custom DOD mark and destroy on the object. */
        PObj_custom_mark_SET(object);
        PObj_active_destroy_SET(object);

        /* Flag that it is an object */
        PObj_is_object_SET(object);

        /* Initialize the object's underlying structure, pointing it to this
         * class. */
        obj_guts               = mem_allocate_zeroed_typed(Parrot_Object_attributes);
        obj_guts->_class       = SELF;
        PMC_data(object)       = obj_guts;
        obj_guts->attrib_store = pmc_new(interp, enum_class_ResizablePMCArray);

        if (!PMC_IS_NULL(init)) {
            /* Initialize attributes with the supplied values. */
            PMC * const iter = VTABLE_get_iter(interp, init);

            while (VTABLE_get_bool(interp, iter)) {
                /* Get name and value. */
                STRING * const attr_name  = VTABLE_shift_string(interp, iter);
                PMC    * const attr_value = VTABLE_get_pmc_keyed_str(interp, init,
                    attr_name);

                /* Set the attribute. */
                VTABLE_set_attr_str(interp, object, attr_name, attr_value);
            }
        }

        /* Check for overrides on the init or init_pmc vtable function and call
         * them if they exist */
        if (PMC_IS_NULL(init))
            initialize_parents(interp, object, _class->all_parents);
        else
            initialize_parents_pmc(interp, object, _class->all_parents, init);

        return object;
    }


/*

=item C<INTVAL isa_pmc(PMC *class)>

Returns whether the class is or inherits from C<*class>.

=cut

*/

    VTABLE INTVAL isa_pmc(PMC *lookup) {
        Parrot_Class_attributes * const _class    = PARROT_CLASS(SELF);
        PMC          *classobj;
        INTVAL        i, num_classes;

        if (PMC_IS_NULL(lookup))
            return 0;

        if (SUPER(lookup))
            return 1;

        classobj = Parrot_oo_get_class(interp, lookup);

        if (PMC_IS_NULL(classobj))
            return 0;

        /* Check if the class object is the same as self's class object */
        if (VTABLE_is_same(interp, SELF, classobj))
            return 1;
        else {
            STRING *classname = VTABLE_get_string(interp, classobj);
            INTVAL  is_proxy  = SELF->vtable->base_type == enum_class_PMCProxy
                               ? 1 : 0;

            /* avoid the expensive string copy, if possible */
            STRING *self_name = is_proxy
                               ? VTABLE_get_string(interp, SELF)
                               : make_class_name(interp, SELF);

            /* Check if the passed name is the same as the stored short name. */
            INTVAL name_match = string_equal(interp, classname, self_name) == 0;

            string_free(interp, classname);
            if (is_proxy)
                string_free(interp, self_name);

            if (name_match)
                return 1;
        }

        /* Iterate over all the parents and check if they respond true
         * for 'isa' on the original comparison. */
        num_classes = VTABLE_elements(interp, _class->parents);

        for (i = 0; i < num_classes; i++) {
            PMC * const cur_class = VTABLE_get_pmc_keyed_int(interp,
                    _class->parents, i);

            if (VTABLE_isa_pmc(interp, cur_class, lookup))
                return 1;
        }

        return 0;
    }

/*

=item C<INTVAL isa(STRING *classname)>

Returns whether the class is or inherits from C<*classname>.

=cut

*/

    VTABLE INTVAL isa(STRING *classname) {
        Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
        PMC                            *want_class;

        /* hard-code this one exception right away */
        if (string_equal(interp, classname, CONST_STRING(interp, "Class")) == 0)
            return 1;

        want_class = Parrot_oo_get_class_str(interp, classname);

        if (PMC_IS_NULL(want_class))
            return 0;

        if (SELF == want_class)
            return 1;

        return VTABLE_isa_pmc(interp, SELF, want_class);
    }

/*

=item C<INTVAL does(STRING *role_name)>

Returns whether the class does the role with the given C<*role_name>.

=cut

*/
    INTVAL does(STRING *role_name) {
        Parrot_Class_attributes * const _class    = PARROT_CLASS(SELF);
        PMC          * const role_list = _class->roles;
        INTVAL i, count;

        if (!role_list)
            return 0;

        count = VTABLE_elements(interp, role_list);

        for (i = 0; i < count; i++) {
            PMC * const role = VTABLE_get_pmc_keyed_int(interp, role_list, i);

            if (VTABLE_does(interp, role, role_name))
                return 1;
        }

        /* Iterate over all the parents and check if they respond true
         * for 'does' on the original comparison. */
        count = VTABLE_elements(interp, _class->parents);

        for (i = 0; i < count; i++) {
            PMC * const cur_class = VTABLE_get_pmc_keyed_int(interp,
                    _class->parents, i);

            if (VTABLE_does(interp, cur_class, role_name))
                return 1;
        }

        return VTABLE_isa(interp, SELF, role_name);
    }

/*

=item C<INTVAL does_pmc(PMC *role)>

Returns whether the class does the given C<*role>.

=cut

*/
    INTVAL does_pmc(PMC *role) {
        Parrot_Class_attributes * const _class    = PARROT_CLASS(SELF);
        PMC * const role_list          = _class->roles;
        INTVAL i, role_count, count;

        if (!role_list)
            return 0;

        role_count = VTABLE_elements(interp, role_list);

        for (i = 0; i < role_count; i++) {
            INTVAL  role_does;
            PMC    * const test_role = VTABLE_get_pmc_keyed_int(interp, role_list, i);

            role_does = VTABLE_does_pmc(interp, test_role, role);

            if (role_does)
                return 1;
        }

        /* Iterate over all the parents and check if they respond true
         * for 'does' on the original comparison. */
        count = VTABLE_elements(interp, _class->parents);

        for (i = 0; i < count; i++) {
            PMC * const cur_class = VTABLE_get_pmc_keyed_int(interp,
                    _class->parents, i);

            if (VTABLE_does_pmc(interp, cur_class, role))
                return 1;
        }

        return VTABLE_isa_pmc(interp, SELF, role);
    }

/*

=item C<INTVAL type()>

Returns the integer type of the class.

=cut

*/

    VTABLE INTVAL type() {
        Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
        return _class->id;
    }

/*

=item C<void visit(visit_info *info)>

This is used by freeze/thaw to visit the contents of the class.

C<*info> is the visit info, (see F<include/parrot/pmc_freeze.h>).

=cut

*/

    VTABLE void visit(visit_info *info) {
        Parrot_Class_attributes * const class_data = PARROT_CLASS(SELF);
        PMC **pos;

        /* 1) visit the attribute description hash */
        pos            = &class_data->attrib_metadata;
        info->thaw_ptr = pos;
        (info->visit_pmc_now)(INTERP, *pos, info);

        /* 2) visit list of parents */
        pos            = &class_data->parents;
        info->thaw_ptr = pos;
        (info->visit_pmc_now)(INTERP, *pos, info);

        /* 3) visit list of roles */
        pos            = &class_data->roles;
        info->thaw_ptr = pos;
        (info->visit_pmc_now)(INTERP, *pos, info);

        /* 4) visit hash of methods */
        pos            = &class_data->methods;
        info->thaw_ptr = pos;
        (info->visit_pmc_now)(INTERP, *pos, info);

        /* 5) visit hash of vtable overrides */
        pos            = &class_data->vtable_overrides;
        info->thaw_ptr = pos;
        (info->visit_pmc_now)(INTERP, *pos, info);

        /* 6) visit list of resolve methods */
        pos            = &class_data->resolve_method;
        info->thaw_ptr = pos;
        (info->visit_pmc_now)(INTERP, *pos, info);
    }

/*

=item C<void freeze(visit_info *info)>

Used to archive the class.

=cut

*/

    VTABLE void freeze(visit_info *info) {
        IMAGE_IO     * const io         = info->image_io;
        Parrot_Class_attributes * const class_data = PARROT_CLASS(SELF);
        STRING       *serial_namespace  = CONST_STRING(interp, "");

        /* 1) freeze class id */
        VTABLE_push_integer(INTERP, io, class_data->id);

        /* 2) freeze class name */
        VTABLE_push_string(INTERP, io, class_data->name);

        /* 3) serialize namespace name, including HLL */
        if (!PMC_IS_NULL(class_data->_namespace)) {
            PMC * const names = Parrot_ns_get_name(interp,
                    class_data->_namespace);
            if (!PMC_IS_NULL(names))
                serial_namespace = string_join(interp, CONST_STRING(interp, ";"), names);
        }
        VTABLE_push_string(INTERP, io, serial_namespace);
    }

/*

=item C<void thaw(visit_info *info)>

Used to unarchive the class.

=cut

*/

    VTABLE void thaw(visit_info *info) {
        /* The class might already exist in the interpreter, so create it as an
         * anonymous class and later decide whether to link it into the
         * namespace.  */

        if (info->extra_flags == EXTRA_IS_PROP_HASH) {
            SUPER(info);
        }
        else if (info->extra_flags == EXTRA_IS_NULL) {
            IMAGE_IO * const io = info->image_io;

            /* 1) thaw class id */
            const INTVAL id = VTABLE_shift_integer(INTERP, io);

            /* 2) thaw class name */
            STRING * const name = VTABLE_shift_string(INTERP, io);

            /* 3) deserialize namespace name, including HLL */
            STRING * const serial_namespace = VTABLE_shift_string(INTERP, io);
            STRING * const semicolon_str = CONST_STRING(INTERP, ";");
            PMC    * const namespace_array =
                string_split(INTERP, semicolon_str, serial_namespace);
            PMC *ns = Parrot_get_namespace_keyed(interp,
                    INTERP->root_namespace, namespace_array);

            /* If the namespace doesn't exist, we create it, and initialize
             * ourselves in it */
            if (PMC_IS_NULL(ns)) {
                ns = Parrot_make_namespace_keyed(interp,
                        INTERP->root_namespace, namespace_array);
                SELF.init_pmc(ns);
            }
            /* If the namespace exists already, we point to it, but otherwise
             * act as an anonymous class. */
            else {
                SELF.init();
                PARROT_CLASS(SELF)->_namespace = ns;
            }

            /* Set the class's short name to the frozen name */
            PARROT_CLASS(SELF)->name = name;

            /* Set the class's id the frozen id */
            PARROT_CLASS(SELF)->id = id;
        }
    }

/*

=item C<void thawfinish(visit_info *info)>

Called after the class has been thawed.

=cut

*/

    void thawfinish(visit_info *info) {
        Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);

        /* Recalculate full MRO from thawed parents */
        _class->all_parents         = Parrot_ComputeMRO_C3(interp, SELF);
        _class->parent_overrides    = pmc_new(interp, enum_class_Hash);

        /* Rebuild attribute index from thawed attribute metadata */
        build_attrib_index(interp, SELF);
    }

    /* **********************************************************************
     * Below here are non-vtable methods that eventually will go in a role
     * that is composed into here to optionally give a nice interface from
     * PIR (ParrotClass isa Class does ClassMethods or something like this).
     * **********************************************************************/

/*

=item C<void name(STRING *name :optional, int has_name :opt_flag)>

Sets the name of the class, and updates the namespace accordingly.

=cut

*/
    METHOD name(STRING *name :optional, int has_name :opt_flag) {
        Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);
        STRING *ret_name;

        if (has_name) {
            /* We'll build a hash just containing the name, then give this to
             * init_class_from_hash - saves some code duplication. */
            PMC    * const naming_hash = pmc_new(interp, enum_class_Hash);
            STRING * const name_str    = CONST_STRING(interp, "name");

            VTABLE_set_string_keyed_str(interp, naming_hash, name_str, name);
            init_class_from_hash(interp, SELF, naming_hash);
        }

        ret_name = _class->name;
        RETURN(STRING *ret_name);
    }

/*

=item C<void get_namespace()>

Gets the namespace that this class is attached to.

=cut

*/
    METHOD get_namespace(PMC *_namespace :optional, int has_name :opt_flag) {
        Parrot_Class_attributes * const _class        = PARROT_CLASS(SELF);
        PMC          * const ret_namespace = _class->_namespace;
        UNUSED(_namespace);
        UNUSED(has_name);
        RETURN(PMC *ret_namespace);
    }

/*

=item C<void resolve_method()>

Sets the list of method names that the class provides to resolve conflicts in
methods from roles. When called with no parameter, returns the list.

=cut

*/
    METHOD resolve_method(PMC *resolve_list :optional, int has_list :opt_flag) {
        Parrot_Class_attributes * const _class   = PARROT_CLASS(SELF);
        PMC *ret_list;

        /* Store list. */
        if (has_list)
            _class->resolve_method = resolve_list;

        ret_list = _class->resolve_method;
        RETURN(PMC *ret_list);
    }

/*

=item C<void new(PMC *args :slurpy :named)>

Creates an instance of the object. Initializes any attributes specified in the
parameter list.

=cut

*/
    METHOD new(PMC *args :slurpy :named) {
        /* Check if any arguments are in the slurpy hash, don't pass an empty
         * hash to instantiate */
        PMC * const obj =
            VTABLE_elements(interp, args) > 0
                ? VTABLE_instantiate(interp, SELF, args)
                : VTABLE_instantiate(interp, SELF, PMCNULL);

        RETURN(PMC *obj);
     }

/*

=item C<void attributes()>

Return a hash where the keys are attribute names and the values are hashes
providing a set of key/value pairs describing the attribute.

=cut

*/
    METHOD attributes() {
        STRING * const attr_str            = CONST_STRING(interp, "attributes");
        PMC    * const ret_attrib_metadata = SELF.inspect_str(attr_str);

        RETURN(PMC *ret_attrib_metadata);
    }

/*

=item C<void add_attribute()>

Add an attribute to the class. Requires a name and, optionally, a type.

=cut

*/
    METHOD add_attribute(STRING *attribute_name,
            PMC *attribute_type :optional, int has_type :opt_flag) {
        PMC * const type = has_type ? attribute_type : PMCNULL;
        SELF.add_attribute(attribute_name, type);
    }

/*

=item C<void methods()>

Return a hash where the keys are method names and the values are methods.

=cut

*/
    METHOD methods() {
        PMC * const ret_methods = SELF.inspect_str(CONST_STRING(interp, "methods"));

        RETURN(PMC *ret_methods);
    }

/*

=item C<void add_method(STRING *name, PMC *sub)>

Adds the given sub PMC as a method with the given name. Delegates to the
C<add_method> vtable method.

=cut

*/
    METHOD add_method(STRING *name, PMC *sub,
                              int vtable     :optional :named("vtable"),
                              int has_vtable :opt_flag,
                              int anon       :optional :named("anon"),
                              int has_anon   :opt_flag)
    {
        /* If it's a vtable method... */
        if (has_vtable && vtable)
            VTABLE_add_vtable_override(interp, SELF, name, sub);

        /* Provided it's not anonymous, add it to the methods list. */
        if (!has_anon || !anon)
            SELF.add_method(name, sub);
    }

/*

=item C<void add_vtable_override(STRING *name, PMC *sub)>

Adds the given sub PMC as a vtable override with the given name. Delegates to
the C<add_vtable_override> vtable method.

=cut

*/
    METHOD add_vtable_override(STRING *name, PMC *sub) {
        VTABLE_add_vtable_override(interp, SELF, name, sub);
    }

/*

=item C<void remove_method(STRING *name)>

Removes the method with the given name.

=cut

*/
    METHOD remove_method(STRING *name)
    {
        VTABLE_remove_method(interp, SELF, name);
    }

/*

=item C<PMC *find_method(STRING *name)>

Walks the MRO of the class and finds the method with the given name.

=cut

*/

    METHOD find_method(STRING *name) {
        Parrot_Class_attributes * const  _class    = PARROT_CLASS(SELF);
        int i;

        /* Walk and search. One day, we'll use the cache first. */
        const int num_classes = VTABLE_elements(interp, _class->all_parents);

        for (i = 0; i < num_classes; i++) {
            /* Get the class and see if it has the method. */
            PMC * const cur_class =
                VTABLE_get_pmc_keyed_int(interp, _class->all_parents, i);
            const Parrot_Class_attributes * const class_info = PARROT_CLASS(cur_class);

            /* Found it! */
            if (VTABLE_exists_keyed_str(interp, class_info->methods, name)) {
                PMC * const ret = VTABLE_get_pmc_keyed_str(interp, class_info->methods, name);
                RETURN(PMC *ret);
            }
        }

        RETURN(PMC *PMCNULL);
    }

/*

=item C<void parents()>

Returns the parents array PMC.

=cut

*/
    METHOD parents() {
        PMC * const ret_parents = SELF.inspect_str(CONST_STRING(interp, "parents"));

        RETURN(PMC *ret_parents);
    }

/*

=item C<void add_parent(PMC *parent)>

Adds the supplied PMC to the list of parents for the class.

=cut

*/
    METHOD add_parent(PMC *parent) {
        SELF.add_parent(parent);
    }

/*

=item C<void roles()>

Returns the roles array PMC.

=cut

*/
    METHOD roles() {
        PMC * const ret_roles = SELF.inspect_str(CONST_STRING(interp, "roles"));

        RETURN(PMC *ret_roles);
    }

/*

=item C<void add_role(PMC *role, PMC *exclude :optional :named("exclude"),
PMC *alias :optional :named("alias"))>

Composes a role into a class with the given exclusions and aliases.

=cut

*/
    METHOD add_role(PMC *role,
            PMC *exclude_method     :optional :named("exclude_method"),
            int has_exclude_method  :opt_flag,
            PMC *alias_method       :optional :named("alias_method"),
            int has_alias_method    :opt_flag) {

        Parrot_Class_attributes * const _class = PARROT_CLASS(SELF);

        /* Add everything on the resolve list to the exclude list; if we have
         * no exclude list, pass along the resolve list in its place if it has
         * any methods listed in it. */
        if (!has_exclude_method) {
            if (VTABLE_elements(interp, _class->resolve_method) != 0) {
                exclude_method     = _class->resolve_method;
                has_exclude_method = 1;
            }
        }
        else {
            const int resolve_count = VTABLE_elements(interp, _class->resolve_method);
            int i;

            for (i = 0; i < resolve_count; i++) {
                STRING * const meth_name = VTABLE_get_string_keyed_int(interp,
                    _class->resolve_method, i);
                VTABLE_push_string(interp, exclude_method, meth_name);
            }
        }

        /* Do the composition. */
        Parrot_ComposeRole(interp, role, exclude_method, has_exclude_method,
                           alias_method, has_alias_method,
                           _class->methods, _class->roles);
    }

/*

=item C<void inspect(STRING *what :optional)>

Gets all introspection data for the class or, if the optional string
parameter is supplied, a particular item of introspection data.

=cut

*/
    METHOD inspect(STRING *what :optional, int has_what :opt_flag) {
        PMC *found;

        /* Just delegate to the appropriate vtable method. */
        if (has_what)
            found = SELF.inspect_str(what);
        else
            found = SELF.inspect();

        RETURN(PMC *found);
    }

/*

=item C<void isa(STRING *class_name)>

Returns true if this object is or derives from the class named in
C<class_name>, false otherwise.

=cut

*/
    METHOD isa(STRING *class_name) {
        const INTVAL isa = SELF.isa(class_name);

        RETURN(INTVAL isa);
    }

/*

=item C<void does(STRING *role_name)>

Returns true if this object or one of its parents performs the named role,
false otherwise.

=cut

*/
    METHOD does(STRING *role_name) {
        INTVAL does = VTABLE_does(interp, SELF, role_name);
        RETURN(INTVAL does);
    }

} /* END pmclass */

/*

=back

=head1 SEE ALSO

F<docs/pdds/pdd15_objects.pod>.

=cut

*/

/*
 * Local variables:
 *   c-file-style: "parrot"
 * End:
 * vim: expandtab shiftwidth=4:
 */
