/*
Copyright (C) 2001-2003, The Perl Foundation.
$Id: default.pmc 16275 2006-12-28 01:20:44Z particle $

=head1 NAME

src/pmc/default.pmc - Abstract root class

=head1 DESCRIPTION

These are the vtable functions for the default PMC class.

All methods which are not defined here get a default implementation
generated from F<vtable.tbl> by F<tools/build/pmc2c.pl>.

=head2 Functions

=over 4

=cut

*/

#include "parrot/parrot.h"

#define INT2KEY(i,k) key_new_integer((i), (k))

/* undef 'interface', because msvc is craptastic */
#undef interface

/*

=item C<static const char *caller(Interp *interp, PMC *pmc)>

Returns a C string for the name of C<*pmc>.

=cut

*/

static const char *
caller(Interp *interp, PMC * pmc)
{
    return pmc && pmc->vtable && pmc->vtable->whoami ?
    VTABLE_name(interp, pmc)->strstart : "(null)";
}

/*

=item C<static void cant_do_method(Interp *interp, PMC * pmc,
                                   const char *methname)>

Throws an exception "$methname() not implemented in class '$class'", used by
all unimplemented messages.

=cut

*/

static void
cant_do_method(Interp *interp, PMC * pmc, const char *methname)
{
    real_exception(interp, NULL, ILL_INHERIT,
                       "%s() not implemented in class '%s'", methname,
                       caller(interp, pmc));
}

/*

=item C<static INTVAL
check_set_std_props(Interp *interp, PMC *pmc, STRING *key, PMC *value)>

Called from C<setprop()>.

Returns a true value if C<setprop()> can avoid actually setting a property
in the prophash. If it returns true, the property setting will be reflected
in a future call to C<propagate_std_props()>

=cut

*/

static INTVAL
check_set_std_props(Interp *interp, PMC *pmc, STRING *key, PMC *value)
{
    /* the quick hack below cannot be used because the string could
       be constructed in an interpreter that is later destroyed */
#if  0
    /* XXX
     * a quick hack, to prevent freeing that string during DOD
     * triggered in t/pmc/pmc_62.t when configured with --gc=libc
     */
    static STRING *ro;
    if (!ro)
        ro = string_make(interp, "_ro", 3, "ascii",
            PObj_constant_FLAG|PObj_external_FLAG);
    if (!string_compare(interp, key, ro)) {
#else

    /*
     * s2 in string_compare is freed here
     */
    if (!string_compare(interp, key,
        string_from_cstring(interp, "_ro", 3))) {
#endif
        /* pmc should set/clear readonly */
        INTVAL on = VTABLE_get_bool(interp, value);
        /* morph to Const/normal class or readonly class */
        /* XXX warn when this fails? */
        if (on && (pmc->vtable->flags & VTABLE_HAS_CONST_TOO))
            pmc->vtable = interp->vtables[pmc->vtable->base_type + 1];
        else if (!on && (pmc->vtable->flags & (VTABLE_IS_CONST_FLAG)))
            VTABLE_morph(interp, pmc, pmc->vtable->base_type - 1);
        else if (on && (pmc->vtable->flags & VTABLE_HAS_READONLY_FLAG))
            pmc->vtable = pmc->vtable->ro_variant_vtable;
        else if (!on && (pmc->vtable->flags & VTABLE_IS_READONLY_FLAG)
                && pmc->vtable->ro_variant_vtable)
            pmc->vtable = pmc->vtable->ro_variant_vtable;
        else
            return 0;

        return 1;
    }
    return 0;
}

/*

=item C<static void
propagate_std_props(Interp *interp, PMC *self, PMC *prop_hash)>

Set pending standard properties in C<prop_hash>.

=cut

*/

static void
propagate_std_props(Interp *interp, PMC *self, PMC *prop_hash) {
    if (self->vtable->flags & (VTABLE_IS_CONST_FLAG | VTABLE_IS_READONLY_FLAG)){
        PMC *pmc_true;
        pmc_true = pmc_new(interp, enum_class_Integer);
        PMC_int_val(pmc_true) = 1;
        VTABLE_set_pmc_keyed_str(interp, prop_hash,
            string_from_cstring(interp, "_ro", 3), pmc_true);
    }
}

/*

=item C<static INTVAL
has_pending_std_props(Interp *interp, PMC *self)>

Returns true if propagate_std_props() would create a non-empty prophash.

=cut

*/

static INTVAL
has_pending_std_props(Interp *interp, PMC *self) {
    if (self->vtable->flags & (VTABLE_IS_CONST_FLAG | VTABLE_IS_READONLY_FLAG))
        return 1;
    else
        return 0;
}


/*

=item C<static PMC*
check_get_std_props(Interp *interpeter, PMC *self, STRING *key)>

Checks if we can infer the value of C<key> property from C<self> without
looking at its prophash. Returns C<PMCNULL> if not, returns the value otherwise.

=cut
*/

static PMC*
check_get_std_props(Interp *interp, PMC *self, STRING *key) {
    if ((self->vtable->flags & (VTABLE_IS_CONST_FLAG | VTABLE_IS_READONLY_FLAG))
        &&!string_compare(interp, key,
                          string_from_cstring(interp, "_ro", 3))
        ) {
        PMC *ret_val;
        ret_val = pmc_new(interp, enum_class_Integer);
        PMC_int_val(ret_val) = 1;
        return ret_val;
    }
    else
        return PMCNULL;
}

/*

=item C<static PMC *
make_prop_hash(Interp *interp, PMC *self)>

Create a property hash for C<self>. Returns the created hash. Inferred
properties will be added to the hash and it will be set as
C<PMC_metadata(self)>.

=cut

*/

static PMC*
make_prop_hash(Interp *interp, PMC *self) {
    PMC *prop;

    if (!self->pmc_ext)
        add_pmc_ext(interp, self);

    PMC_metadata(self) = prop = pmc_new_noinit(interp, enum_class_Hash);
    DOD_WRITE_BARRIER(interp, self, NULL, prop);
    VTABLE_init(interp, prop);
    propagate_std_props(interp, self, prop);
    return prop;
}

/*

=item C<static INTVAL
does_isa(Interp* interp, STRING *method, STRING *what)>

Compares C<*method> and C<*what>.

=cut

*/

static INTVAL
does_isa(Interp* interp, STRING *method, STRING *what)
{
    INTVAL len, pos = 0;

    do {
        pos = string_str_index(interp, what, method, pos);
        if (pos < 0)
            return 0;
        if (pos >= (INTVAL)string_length(interp, what))
            return 0;
        len = string_length(interp, method);
        if (pos && string_index(interp, what, pos - 1) != 32) {
            pos += len;
            continue;
        }

        if (pos+len < (INTVAL)string_length(interp, what) &&
            string_index(interp, what, pos + len) != 32) {
            pos += len;
            continue;
        }
        return 1;
    } while (1);
    return 0;
}

pmclass default abstract noinit {

/*

=back

=head2 Methods

=over 4

=item C<void init()>

Does nothing.

=cut

*/

    void init() {
    }

/*

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

With a null C<initializer>, calls C<init()>, else throws an exception.

=cut

*/

    void init_pmc(PMC* initializer) {
        if (PMC_IS_NULL(initializer))
            DYNSELF.init();
        else
            real_exception(interp, NULL, ILL_INHERIT,
                       "init_pmc() not implemented in class '%s'",
                       caller(interp, pmc));
    }

/*

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

Default fallback. Creates a new PMC of the type of the class SELF and
calls init().

=cut

*/

    PMC* instantiate(PMC* sig) {
        INTVAL type = SELF->vtable->base_type;
        PMC* ret = pmc_new_noinit(INTERP, type);
        VTABLE_init(INTERP, ret);
        return ret;
    }

/*

=item C<void mark()>

Panics with a "no custom mark routine defined" error message.

=cut

*/

    void mark() {
        PANIC("custom_mark flag set but no custom mark routine defined");
    }

/*

=item C<PMC *getprop(STRING *key)>

Returns the property for C<*key>. If no property is defined then the
NULL PMC is returned.

=cut

*/

    PMC* getprop(STRING* key) {
        if (SELF->pmc_ext && PMC_metadata(SELF)) {
          return VTABLE_get_pmc_keyed_str(INTERP, PMC_metadata(SELF), key);
        }
        else {
          return check_get_std_props(interp, SELF, key);
        }
    }

/*

=item C<void setprop(STRING *key, PMC *value)>

Sets the property for C<*key> to C<*value>.

=cut

*/

    void setprop(STRING* key, PMC* value) {
        if (check_set_std_props(INTERP, SELF, key, value)) {
            return;
        }
        if (SELF->pmc_ext && PMC_metadata(SELF)) {
            VTABLE_set_pmc_keyed_str(INTERP,
                PMC_metadata(SELF), key, value);
        }
        else {
            PMC *prop;

            prop = make_prop_hash(INTERP, SELF);
            /* XXX is this too late for --gc-debug? */
            VTABLE_set_pmc_keyed_str(INTERP, prop, key, value);
#if 0
    PObj_report_SET(PMC_metadata(SELF));
#endif
        }
        return;
    }

/*

=item C<void delprop(STRING *key)>

Deletes the property for C<*key>.

=cut

*/

    void delprop(STRING* key) {
        if (SELF->pmc_ext && PMC_metadata(SELF)) {
            VTABLE_delete_keyed_str(INTERP, PMC_metadata(SELF), key);
        }
        return;
    }

/*

=item C<PMC *getprops()>

Returns the PMC's properties or the NULL PMC if no properties exist.

=cut

*/

    PMC* getprops() {
        if (!SELF->pmc_ext)
            add_pmc_ext(INTERP, SELF);
        if (!PMC_metadata(SELF)) {
            if (has_pending_std_props(INTERP, SELF))
                return make_prop_hash(INTERP, SELF);
            else
                return PMCNULL;
        }
        return PMC_metadata(SELF);
    }

/*

=item C<INTVAL type()>

Returns the PMC's type.

=cut

*/

    INTVAL type() {
        return SELF->vtable->base_type;
    }

/*

=item C<INTVAL type_keyed_int(INTVAL key)>

Converts C<key> to a PMC key and returns the result of calling
C<type_keyed()> with it.

=cut

*/

    INTVAL type_keyed_int(INTVAL key) {
        PMC* r_key = INT2KEY(INTERP, key);
        return DYNSELF.type_keyed(r_key);
    }

/*

=item C<STRING *name()>

Returns the name of the PMC.

=cut

*/

    STRING* name() {
        return SELF->vtable->whoami;
    }


/*

=item C<PMC* namespace>

Return the namespace for this PMC.

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

Looks up the method for C<*method_name> and returns it. If no method is
found then C<NULL> is returned.

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

Store the method as a global in the namespace of this class.

=cut

*/

    PMC* namespace() {
        return SELF->vtable->_namespace;
    }


    PMC* find_method(STRING* method_name) {
        return Parrot_find_method_with_cache(INTERP, SELF, method_name);
    }

    void add_method(STRING *method_name, PMC *sub_pmc) {
        VTABLE_set_pmc_keyed_str(INTERP,
                                 SELF->vtable->_namespace,
                                 method_name, sub_pmc);
    }
/*

=item C<INTVAL get_integer_keyed_int(INTVAL key)>

Converts C<key> to a PMC key and returns the result of calling
C<get_integer_keyed()> with it.

=cut

*/

    INTVAL get_integer_keyed_int(INTVAL key) {
        PMC* r_key = INT2KEY(INTERP, key);
        return DYNSELF.get_integer_keyed(r_key);
    }

/*

=item C<FLOATVAL get_number_keyed_int(INTVAL key)>

Converts C<key> to a PMC key and returns the result of calling
C<get_number_keyed()> with it.

=cut

*/

    FLOATVAL get_number_keyed_int(INTVAL key) {
        PMC* r_key = INT2KEY(INTERP, key);
        return DYNSELF.get_number_keyed(r_key);
    }


/*

=item C<STRING *get_string_keyed_int(INTVAL key)>

Converts C<key> to a PMC key and returns the result of calling
C<get_string_keyed()> with it.

=cut

*/

    STRING* get_string_keyed_int(INTVAL key) {
        PMC* r_key = INT2KEY(INTERP, key);
        return DYNSELF.get_string_keyed(r_key);
    }

/*

=item C<INTVAL get_bool_keyed_int(INTVAL key)>

Converts C<key> to a PMC key and returns the result of calling
C<get_bool_keyed()> with it.

=cut

*/

    INTVAL get_bool_keyed_int(INTVAL key) {
        PMC* r_key = INT2KEY(INTERP, key);
        return DYNSELF.get_bool_keyed(r_key);
    }

/*

=item C<INTVAL elements_keyed_int(INTVAL key)>

Converts C<key> to a PMC key and returns the result of calling
C<elements_keyed()> with it.

=cut

*/

    INTVAL elements_keyed_int(INTVAL key) {
        PMC* r_key = INT2KEY(INTERP, key);
        return DYNSELF.elements_keyed(r_key);
    }


/*

=item C<PMC *get_pmc_keyed_int(INTVAL key)>

Converts C<key> to a PMC key and returns the result of calling
C<get_pmc_keyed()> with it.

=cut

*/

    PMC* get_pmc_keyed_int(INTVAL key) {
        PMC* r_key = INT2KEY(INTERP, key);
        return DYNSELF.get_pmc_keyed(r_key);
    }

/*

=item C<void *get_pointer()>

Returns the address of the PMC.

=cut

*/

    void* get_pointer() {
        return SELF;
    }

/*

=item C<INTVAL is_same(PMC *value)>

Returns whether the PMC is numerically equal to C<value>.

=cut

*/

    INTVAL is_same(PMC* value) {
        return SELF == value;
    }

/*

=item C<void assign_pmc(PMC* value)>

=item C<void assign_string_native(PMC* value)>

Defaults fall back to C<set_pmc> and C<set_string_native>.

=cut

*/

    void assign_pmc(PMC* value) {
        STRING *undef = string_from_const_cstring(INTERP, "Undef", 5);

        if (VTABLE_isa(INTERP, value, undef))
            pmc_reuse(INTERP, SELF, value->vtable->base_type, 0);
        else
            DYNSELF.set_pmc(value);
    }

    void assign_string_native(STRING* value) {
        DYNSELF.set_string_native(value);
    }

/*

=item C<void morph(INTVAL type)>

Changes the PMC to a PMC of a new type

=cut

*/

    void morph(INTVAL type) {
        pmc_reuse(INTERP, SELF, type, 0);
    }

/*

=item C<void set_integer_keyed_int(INTVAL key, INTVAL value)>

Converts C<key> to a PMC key and calls C<set_integer_keyed()> with it
and C<value>.

=cut

*/

    void set_integer_keyed_int(INTVAL key, INTVAL value) {
        PMC* r_key = INT2KEY(INTERP, key);
        DYNSELF.set_integer_keyed(r_key, value);
    }

/*

=item C<void set_number_keyed_int(INTVAL key, FLOATVAL value)>

Converts C<key> to a PMC key and calls C<set_number_keyed()> with it
and C<value>.

=cut

*/

    void set_number_keyed_int(INTVAL key, FLOATVAL value) {
        PMC* r_key = INT2KEY(INTERP, key);
        DYNSELF.set_number_keyed(r_key, value);
    }


/*

=item C<void set_string_keyed_int(INTVAL key, STRING *string)>

Converts C<key> to a PMC key and calls C<set_string_keyed()> with it
and C<value>.

=cut

*/

    void set_string_keyed_int(INTVAL key, STRING* string) {
        PMC* r_key = INT2KEY(INTERP, key);
        DYNSELF.set_string_keyed(r_key, string);
    }

/*

=item C<void set_bool_keyed_int(INTVAL key, INTVAL value)>

Converts C<key> to a PMC key and calls C<set_bool_keyed()> with it
and C<value>.

=cut

*/

    void set_bool_keyed_int(INTVAL key, INTVAL value) {
        PMC* r_key = INT2KEY(INTERP, key);
        DYNSELF.set_bool_keyed(r_key, value);
    }

/*

=item C<void set_pmc_keyed_int(INTVAL key, PMC *value)>

Converts C<key> to a PMC key and calls C<set_pmc_keyed()> with it
and C<value>.

=cut

*/

    void set_pmc_keyed_int(INTVAL key, PMC* value) {
        PMC* r_key = INT2KEY(INTERP, key);
        DYNSELF.set_pmc_keyed(r_key, value);
    }

/*

=item C<INTVAL is_equal(PMC *value)>

Returns whether the PMC is equal to C<*value>.

=cut

*/

    INTVAL is_equal(PMC* value) {
        return (SELF->vtable == value->vtable
            && PMC_struct_val(SELF) == PMC_struct_val(value)
            && PMC_data(SELF) == PMC_data(value));
    }

/*

=item C<INTVAL is_equal_num(PMC *value)>

Returns whether the PMC is numerically equal to C<*value>.

=cut

*/

    INTVAL is_equal_num(PMC* value) {
        return (VTABLE_get_number(INTERP, SELF) ==
                VTABLE_get_number(INTERP, value));
    }

/*

=item C<INTVAL is_equal_str(PMC *value)>

Returns whether the PMC is numerically equal to C<*value>.

=cut

*/

    INTVAL is_equal_str(PMC* value) {
        return (0 == string_equal(INTERP, VTABLE_get_string(INTERP, SELF),
                                       VTABLE_get_string(INTERP, value)));
    }

/*



=item C<INTVAL exists_keyed_int(INTVAL key)>

Converts C<key> to a PMC key and returns the result of calling
C<exists_keyed()> with it.

=cut

*/

    INTVAL exists_keyed_int(INTVAL key) {
        PMC* r_key = INT2KEY(INTERP, key);
        return DYNSELF.exists_keyed(r_key);
    }

/*

=item C<INTVAL defined()>

Returns true.

=cut

*/

    INTVAL defined() {
        return 1;
    }


/*

=item C<INTVAL defined_keyed_int(INTVAL key)>

Converts C<key> to a PMC key and returns the result of calling
C<defined_keyed()> with it.

=cut

*/

    INTVAL defined_keyed_int(INTVAL key) {
        PMC* r_key = INT2KEY(INTERP, key);
        return DYNSELF.defined_keyed(r_key);
    }

/*

=item C<void delete_keyed_int(INTVAL key)>

Converts C<key> to a PMC key and calls C<delete_keyed()> with it.

=cut

*/

    void delete_keyed_int(INTVAL key) {
        PMC* r_key = INT2KEY(INTERP, key);
        DYNSELF.delete_keyed(r_key);
    }

/*

=item C<PMC *nextkey_keyed_int(INTVAL key, INTVAL w)>

Converts C<key> to a PMC key and returns the result of calling
C<nextkey_keyed()> with it.

=cut

*/

    PMC* nextkey_keyed_int(INTVAL key, INTVAL w) {
        PMC* r_key = INT2KEY(INTERP, key);
        return DYNSELF.nextkey_keyed(r_key, w);
    }

/*

=item C<INTVAL can(STRING *method)>

Returns whether the PMC "can" perform C<*method>.

=cut

*/

    INTVAL can(STRING* method) {
        return VTABLE_find_method(INTERP, SELF, method) != NULL;
    }

/*

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

Returns whether the PMC "does" perform C<*interface>.

=cut

*/

    INTVAL does(STRING* interface) {
        return does_isa(INTERP, interface, SELF->vtable->does_str);
    }

/*

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

Returns whether the PMC "isa" C<*class>.

=cut

*/

    INTVAL isa(STRING* class) {
        /* TODO FIXME - walk mro */
        return does_isa(INTERP, class, SELF->vtable->isa_str);
    }

/*

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

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

=cut

*/

    PMC* subclass(PMC *name) {
        return Parrot_single_subclass(INTERP, SELF, name);
    }

/*

=item C<PMC* get_class()>

Returns SELF. A PMC is it's own class.

=item C<PMC* get_attr_str(STRING* attr)>

Look for NCI methods and properties.

=cut

*/
    PMC* get_class() {
        return SELF;
    }

    PMC* get_attr_str(STRING* name) {
        PMC *p = NULL;
        HashBucket* b;
        /* lets look for props first
         * XXX do we need that in the default object system?
         */
        if (SELF->pmc_ext && PMC_metadata(SELF)) {
             b = parrot_hash_get_bucket(INTERP, (Hash*) PMC_metadata(SELF),
                                        name);
             if (b)
                 p = b->value;
        }
        if (PMC_IS_NULL(p)) {
            /* may be NCI? */
            p = VTABLE_find_method(INTERP, SELF, name);
        }
        if (PMC_IS_NULL(p)) {
            /* TODO AttributeError */
        }
        else if (VTABLE_isa(INTERP, p, CONST_STRING(INTERP, "NCI"))) {
            PMC *bound_meth = VTABLE_clone(INTERP, p);
            bound_meth->vtable = interp->vtables[enum_class_Bound_NCI];
            VTABLE_set_pmc(INTERP, bound_meth, SELF);
            return bound_meth;
        }
        else if (p->vtable->base_type == enum_class_MultiSub) {
            PMC *bound_meth = pmc_new(INTERP, enum_class_Bound_NCI);
            VTABLE_set_pmc(INTERP, bound_meth, SELF);
            PMC_struct_val(bound_meth) = p;
            PObj_get_FLAGS(bound_meth) |= PObj_private0_FLAG;
            return bound_meth;
        }
        /* TODO bound user functions */
        return p;
    }

/*

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

Add class C<parent> to the list of our parents.

=cut

*/

    void add_parent(PMC *parent) {
        Parrot_add_parent(INTERP, SELF, parent);
    }

/*

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

Used by DOD to mark the PMC.

=cut

*/

    void visit(visit_info *info) {
        /* default - mark prop hash */
        if (SELF->pmc_ext && PMC_metadata(SELF) &&
            info->extra_flags != EXTRA_IS_PROP_HASH) {
            info->extra_flags = EXTRA_IS_PROP_HASH;
            info->extra = PMC_metadata(SELF);
            /* place escape mark */
            (info->visit_pmc_now)(INTERP, SELF, info);
            /* place and the prop hash */
            (info->visit_pmc_now)(INTERP, PMC_metadata(SELF), info);
        }
    }

/*

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

Does nothing.

=cut

*/

    void freeze(visit_info *info) {
        /* default - no action */
    }

/*

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

Initializes the PMC during unarchiving.

=cut

*/

    void thaw(visit_info *info) {
        /* default - initialize the PMC */
        if (info->extra_flags == EXTRA_IS_PROP_HASH) {
            if (!SELF->pmc_ext)
                add_pmc_ext(INTERP, SELF);
            info->thaw_ptr = &PMC_metadata(SELF);
            info->container = SELF;
            (info->visit_pmc_now)(INTERP, PMC_metadata(SELF), info);
        }
        else
            DYNSELF.init();
    }

/*

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

Does nothing.

=cut

*/

    void thawfinish(visit_info *info) {
        /* default - no action */
    }

}

/*

=back

=cut

*/

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