/*
Copyright (C) 2001-2006, The Perl Foundation.
$Id: array.pmc 16283 2006-12-28 15:46:57Z particle $

=head1 NAME

src/pmc/array.pmc - Array PMC

=head1 DESCRIPTION

These are the vtable functions for the Array base class.

=head2 Functions

=over 4

=cut

*/

#include "parrot/parrot.h"
#include <assert.h>

/*

=item C<static PMC* undef(Interp* interp)>

Returns a C<Undef> PMC.

=cut

*/

static PMC* undef(Interp* interp)
{
    return pmc_new(interp, enum_class_Undef);
}

/*

=item C<static PMC* retval(Interp *interp, void *ret)>

Processes C<*ret>, returning the appropriate PMC, or raising an
exception if necessary.

=cut

*/

static PMC* retval(Interp *interp, void *ret)
{
    PMC *value;
    if (ret == 0)
    real_exception(interp, NULL, E_IndexError,
        "Array index out of bounds!");
    /* XXX getting non existent value, exception or undef?
     * current is for perlarray */
    if (ret == (void*) -1)
        value = undef(interp);
    else {
        value = *(PMC**) ret;
        if (value == NULL)  /* XXX same here */
            value = undef(interp);
    }
    return value;
}

/*

=item C<static PMC*
Parrot_Array_set_pmc_ptr(Interp *interp, List *list, INTVAL key)>

Returns a pointer to the element at index C<key> of C<*list>. If
this element was previously empty, then this function also creates
and assigns an "undef" PMC to that element.

=cut

*/

static PMC*
Parrot_Array_set_pmc_ptr(Interp *interp, List *list, INTVAL key)
{
    void * ret = list_get(interp, list, key, enum_type_PMC);
    PMC *value;

    if (ret == 0)
        real_exception(interp, NULL, E_IndexError,
            "Array index out of bounds!");
    /* assign into a sparse or not yet set value */
    if (ret == (void*) -1 || *(PMC**)ret == 0) {
        value = undef(interp);
        list_assign(interp, list, key, value, enum_type_PMC);
    }
    else
        value = *(PMC**) ret;
    return value;
}


pmclass Array need_ext does array {

/*

=back

=head2 Methods

=over 4

=item C<void class_init()>

Class initialization. Creates the required memory pools.

=cut

*/

    void class_init() {
        /* class_init_code */
        make_bufferlike_pool(INTERP, sizeof (List_chunk));
        make_bufferlike_pool(INTERP, sizeof (List));
    }

/*

=item C<void init()>

Initializes the PMC by calling the underlying C<list_new()> function.

=cut

*/

    void init() {
        list_pmc_new(INTERP, SELF);
        PObj_custom_mark_SET(SELF);
    }

/*

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

Calls C<list_new_init()> to initialize the underlying list.

C<*init> contains the initialization information specifying initial
size, number of dimensions, etc.

=cut

*/

    void init_pmc(PMC *init) {
        list_pmc_new_init(INTERP, SELF, init);
        PObj_custom_mark_SET(SELF);
    }

/*

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

Copy the contents of other to self.

=cut

*/

    void assign_pmc(PMC *other) {
        INTVAL size;
        INTVAL i;

        size = VTABLE_elements(INTERP,other);
        DYNSELF.set_integer_native(size);
        for (i = 0; i < size; i++) {
            PMC* elem = VTABLE_get_pmc_keyed_int(INTERP, other, i);
            DYNSELF.set_pmc_keyed_int(i, elem);
        }
    }


/*

=item C<void set_pmc(PMC *other)>

Currently, an alias to C<assign_pmc>.  Expected to go away soon.

=cut

*/

    void set_pmc(PMC *other) {
        DYNSELF.assign_pmc(other);
    }

/*

=item C<void mark()>

Mark the array and its contents as live.

=cut

*/

    void mark() {
        list_mark(INTERP, (List *) PMC_data(SELF));
    }

/*

=item C<PMC* clone()>

Return a clone of the array.

=cut

*/

    PMC* clone() {
        List *l;
        PMC* dest = pmc_new_noinit(INTERP, SELF->vtable->base_type);
        PObj_custom_mark_SET(dest);
        PMC_data(dest) = l = list_clone(INTERP, (List *) PMC_data(SELF));
        l->container = dest;
        return dest;
    }

/*

=item C<INTVAL get_integer()>

Returns the number of elements in the array.

=cut

*/

    INTVAL get_integer() {
        return DYNSELF.elements();
    }

/*

=item C<INTVAL get_bool()>

Returns true if the array has one or more elements.

=cut

*/

    INTVAL get_bool() {
        INTVAL size = DYNSELF.elements();
        return (INTVAL)(size != 0);
    }

/*

=item C<INTVAL elements()>

Returns the number of elements in the array.

=cut

*/

    INTVAL elements() {
        return ((List *) PMC_data(SELF))->length;
    }

/*

=item C<FLOATVAL get_number()>

Returns the number of elements in the array.

=cut

*/

    FLOATVAL get_number() {
        return (FLOATVAL)(DYNSELF.elements());
    }

/*

=item C<STRING* get_string()>

Returns a string representation of the array.

=cut

*/

    STRING* get_string() {
        return Parrot_sprintf_c(INTERP, "array[%p]", SELF);
    }

/*

=item C<INTVAL get_integer_keyed_int(INTVAL key)>

Returns the integer value of the element at index C<key>.

=cut

*/

    INTVAL get_integer_keyed_int(INTVAL key) {
        PMC* value;

        value = DYNSELF.get_pmc_keyed_int(key);
        return VTABLE_get_integer(INTERP, value);
    }

/*

=item C<INTVAL type_keyed_int(INTVAL key)>

=cut

*/

    INTVAL type_keyed_int(INTVAL key) {
        PMC* value;

        value = DYNSELF.get_pmc_keyed_int(key);
        return VTABLE_type(INTERP, value);
    }

/*

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

Returns the integer value of the element at index C<key>.

=cut

*/

    INTVAL get_integer_keyed(PMC* key) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return 0;
        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) return SELF.get_integer_keyed_int(ix);

        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) box = undef(INTERP);
        return VTABLE_get_integer_keyed(INTERP, box, nextkey);
    }

/*

=item C<FLOATVAL get_number_keyed_int(INTVAL key)>

Returns the float value of the element at index C<key>.

=cut

*/

    FLOATVAL get_number_keyed_int(INTVAL key) {
        PMC* value;

        value = DYNSELF.get_pmc_keyed_int(key);
        return VTABLE_get_number(INTERP, value);

    }

/*

=item C<FLOATVAL get_number_keyed(PMC* key)>

Returns the float value of the element at index C<key>.

=cut

*/

    FLOATVAL get_number_keyed(PMC* key) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return 0;
        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) return SELF.get_number_keyed_int(ix);

        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) box = undef(INTERP);
        return VTABLE_get_number_keyed(INTERP, box, nextkey);
    }

/*

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

Returns the string value of the element at index C<key>.

=cut

*/

    STRING* get_string_keyed_int(INTVAL key) {
        PMC* value;

        value = DYNSELF.get_pmc_keyed_int(key);
        return VTABLE_get_string(INTERP, value);
    }

/*

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

Returns the string value of the element at index C<key>.

=cut

*/

    STRING* get_string_keyed(PMC* key) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return 0;
        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) return SELF.get_string_keyed_int(ix);

        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) box = undef(INTERP);
        return VTABLE_get_string_keyed(INTERP, box, nextkey);
    }

/*

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

Returns the PMC value of the element at index C<key>.

=cut

*/

    PMC* get_pmc_keyed_int(INTVAL key) {

        return retval(INTERP, list_get(INTERP, (List*)PMC_data(SELF), key, enum_type_PMC));
    }

/*

=item C<PMC* get_pmc_keyed(PMC* key)>

Returns the PMC value of the element at index C<key>.

=cut

*/

    PMC* get_pmc_keyed(PMC* key) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return 0;
        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) return SELF.get_pmc_keyed_int(ix);

        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) box = undef(INTERP);
        return VTABLE_get_pmc_keyed(INTERP, box, nextkey);
    }

/*

=item C<void set_integer_native(INTVAL size)>

Sets the length of the array to C<size>.

=cut

*/

    void set_integer_native(INTVAL size) {
        list_set_length(INTERP, (List *) PMC_data(SELF) ,size);
    }
/*

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

Sets the length of the array to the number of elements in C<*value>.

=cut

*/

    void set_integer_same(PMC* value) {
        INTVAL size = VTABLE_elements(INTERP, value);
        list_set_length(INTERP, (List *) PMC_data(SELF) ,size);
    }

/*

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

Sets the integer value of the PMC at element C<key> to C<value>.

=cut

*/

    void set_integer_keyed_int(INTVAL key, INTVAL value) {
        PMC * ptr;

        ptr = Parrot_Array_set_pmc_ptr(INTERP, (List *) PMC_data(SELF), key);
        VTABLE_set_integer_native(INTERP, ptr, value);
    }

/*

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

Sets the integer value of the PMC at element C<key> to C<value>.

=cut

*/

    void set_integer_keyed(PMC* key, INTVAL value) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return;

        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) {
            DYNSELF.set_integer_keyed_int(ix, value);
            return;
        }
        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) {
            /* autovivify an Array */
            box = pmc_new(INTERP, DYNSELF.type());
        }
        VTABLE_set_integer_keyed(INTERP, box, nextkey, value);
    }

/*

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

Sets the numeric value of the PMC at element C<key> to C<value>.

=cut

*/

    void set_number_keyed_int(INTVAL key, FLOATVAL value) {
        PMC* ptr;

        ptr = Parrot_Array_set_pmc_ptr(INTERP, (List *) PMC_data(SELF), key);
        VTABLE_set_number_native(INTERP, ptr, value);
    }

/*

=item C<void set_number_keyed(PMC* key, FLOATVAL value)>

Sets the numeric value of the PMC at element C<key> to C<value>.

=cut

*/

    void set_number_keyed(PMC* key, FLOATVAL value) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return;
            ix = key_integer(INTERP, key);
            nextkey = key_next(INTERP, key);
            if (nextkey == NULL) {
            SELF.set_number_keyed_int(ix, value);
            return;
        }
        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) {
            /* autovivify an Array */
            box = pmc_new(INTERP, DYNSELF.type());
        }
        VTABLE_set_number_keyed(INTERP, box, nextkey, value);
    }

/*

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

Sets the string value of the PMC at element C<key> to C<value>.

=cut

*/

    void set_string_keyed_int(INTVAL key, STRING* value) {
        PMC* ptr;

        ptr = Parrot_Array_set_pmc_ptr(INTERP, (List *) PMC_data(SELF), key);
        VTABLE_set_string_native(INTERP, ptr, value);
    }

/*

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

Sets the string value of the PMC at element C<key> to C<value>.

=cut

*/

    void set_string_keyed(PMC* key, STRING* value) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        if (!key) return;
        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) {
            VTABLE_set_string_keyed_int(INTERP, SELF, ix, value);
            return;
        }
        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) {
            /* autovivify an Array */
            box = pmc_new(INTERP, DYNSELF.type());
        }
        VTABLE_set_string_keyed(INTERP, box, nextkey, value);
    }

/*

=item C<void set_pmc_keyed_int(INTVAL idx, PMC* src)>

Sets the PMC at element C<idx> to C<*src>.

=cut

*/

    void set_pmc_keyed_int(INTVAL idx, PMC* src) {
        INTVAL length;
        length = ((List *) PMC_data(SELF))->length;
        if (idx >= length || -idx > length) {
            real_exception(INTERP, NULL, E_IndexError,
                "Array index out of bounds!");
        }

        list_assign(INTERP, (List *) PMC_data(SELF), idx,
            (void*)src, enum_type_PMC);
    }

/*

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

Sets the PMC at index C<key> to C<value>.

=cut

*/

    void set_pmc_keyed(PMC* key, PMC* value) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL) {
            VTABLE_set_pmc_keyed_int(INTERP, SELF, ix, value);
            return;
        }
        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL) {
            /* autovivify an Array */
            box = pmc_new(INTERP, DYNSELF.type());
        }
        VTABLE_set_pmc_keyed(INTERP, box, nextkey, value);
    }

/*

=item C<void push_integer(INTVAL value)>

Extends the array by adding an element of value C<value> to the end of
the array.

=cut

*/

    void push_integer(INTVAL value) {
        INTVAL nextix = DYNSELF.elements();
        DYNSELF.set_integer_keyed_int(nextix, value);
    }

/*

=item C<void push_float(FLOATVAL value)>

Extends the array by adding an element of value C<value> to the end of
the array.

=cut

*/

    void push_float(FLOATVAL value) {
        INTVAL nextix = DYNSELF.elements();
        DYNSELF.set_number_keyed_int(nextix, value);
    }

/*

=item C<void push_string(STRING* value)>

Extends the array by adding an element of value C<*value> to the end of
the array.

=cut

*/

    void push_string(STRING* value) {
        INTVAL nextix = DYNSELF.elements();
        DYNSELF.set_string_keyed_int(nextix, value);
    }

/*

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

Extends the array by adding an element of value C<*value> to the end of
the array.

=cut

*/

    void push_pmc(PMC* value) {
        INTVAL nextix = DYNSELF.elements();
        DYNSELF.set_pmc_keyed_int(nextix, value);
    }

/*

=item C<void unshift_integer(INTVAL value)>

Extends the array by adding an element of value C<value> to the start
of the array.

=cut

*/

    void unshift_integer(INTVAL value) {
        PMC * val = undef(INTERP);
        list_unshift(INTERP, (List*)PMC_data(SELF), val, enum_type_PMC);
        VTABLE_set_integer_native(INTERP, val, value);
    }

/*

=item C<void unshift_float(FLOATVAL value)>

Extends the array by adding an element of value C<value> to the start
of the array.

=cut

*/

    void unshift_float(FLOATVAL value) {
        PMC * val = undef(INTERP);
        list_unshift(INTERP, (List*)PMC_data(SELF), val, enum_type_PMC);
        VTABLE_set_number_native(INTERP, val, value);
    }

/*

=item C<void unshift_string(STRING *value)>

Extends the array by adding an element of value C<*value> to the start
of the array.

=cut

*/

    void unshift_string(STRING *value) {
        PMC * val = undef(INTERP);
        list_unshift(INTERP, (List*)PMC_data(SELF), val, enum_type_PMC);
        VTABLE_set_string_native(INTERP, val, value);
    }

/*

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

Extends the array by adding an element of value C<*value> to the start
of the array.

=cut

*/

    void unshift_pmc(PMC *value) {
        list_unshift(INTERP, (List*)PMC_data(SELF), value, enum_type_PMC);
    }

/*

=item C<INTVAL pop_integer()>

Removes and returns an integer from the end of the array.

=cut

*/

    INTVAL pop_integer() {
        PMC *ptr = DYNSELF.pop_pmc();
        return VTABLE_get_integer(INTERP, ptr);
    }

/*

=item C<FLOATVAL pop_float()>

Removes and returns a float value from the end of the array.

=cut

*/

    FLOATVAL pop_float() {
        PMC *ptr = DYNSELF.pop_pmc();
        return VTABLE_get_number(INTERP, ptr);
    }

/*

=item C<STRING* pop_string()>

Removes and returns a string from the end of the array.

=cut

*/

    STRING* pop_string() {
        PMC *ptr = DYNSELF.pop_pmc();
        return VTABLE_get_string(INTERP, ptr);
    }

/*

=item C<PMC* pop_pmc()>

Removes and returns a PMC from the end of the array.

=cut

*/

    PMC* pop_pmc() {
        return retval(INTERP, list_pop(INTERP, (List*)PMC_data(SELF), enum_type_PMC));
    }

/*

=item C<INTVAL shift_integer()>

Removes and returns an integer from the start of the array.

=cut

*/

    INTVAL shift_integer() {
        PMC *ptr = DYNSELF.shift_pmc();
        return VTABLE_get_integer(INTERP, ptr);
    }

/*

=item C<FLOATVAL shift_float()>

Removes and returns a float from the start of the array.

=cut

*/

    FLOATVAL shift_float() {
        PMC *ptr = DYNSELF.shift_pmc();
        return VTABLE_get_number(INTERP, ptr);
    }

/*

=item C<STRING* shift_string()>

Removes and returns a string from the start of the array.

=cut

*/

    STRING* shift_string() {
        PMC *ptr = DYNSELF.shift_pmc();
        return VTABLE_get_string(INTERP, ptr);
    }

/*

=item C<PMC* shift_pmc()>

Removes and returns a PMC from the start of the array.

=cut

*/

    PMC* shift_pmc() {
        return retval(INTERP, list_shift(INTERP, (List*)PMC_data(SELF), enum_type_PMC));
    }

/*

=item C<void splice(PMC* value, INTVAL offset, INTVAL count)>

Replaces C<count> elements starting at C<offset> with the elements in
C<value>.

If C<count> is 0 then the elements in C<value> will be inserted after
C<offset>.

=cut

*/

    void splice(PMC* value, INTVAL offset, INTVAL count) {
        if (SELF->vtable->base_type != value->vtable->base_type)
            real_exception(INTERP, NULL, E_TypeError,
                "Type mismatch in splice");
        list_splice(INTERP, (List*) PMC_data(SELF),
            (List *) PMC_data(value), offset, count);
    }

/*

=item C<INTVAL defined_keyed_int(INTVAL key)>

Returns TRUE is the element at C<key> is defined; otherwise returns false.

=cut

*/

    INTVAL defined_keyed_int(INTVAL key) {
        PMC* value;
        void *ret;

        ret = list_get(INTERP, (List *) PMC_data(pmc), key, enum_type_PMC);
        if (ret == 0 || ret == (void *) -1)
            return 0;
        value = *(PMC**) ret;
        if (!value)
            return 0;
        return VTABLE_defined(INTERP, value);
    }

/*

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

Returns TRUE is the element at C<key> is defined; otherwise returns false.

=cut

*/

    INTVAL defined_keyed(PMC* key) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL)
        return SELF.defined_keyed_int(ix);

        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL)
        return 0;
        return VTABLE_defined_keyed(INTERP, box, nextkey);
    }

/*

=item C<INTVAL exists_keyed_int(INTVAL key)>

Returns TRUE is the element at C<key> exists; otherwise returns false.

=cut

*/

    INTVAL exists_keyed_int(INTVAL key) {
        void * ret;

        ret = list_get(INTERP, (List *) PMC_data(pmc), key, enum_type_PMC);
        if (ret == 0 || ret == (void *) -1)
            return 0;
        return *(PMC**) ret != NULL;
    }

/*

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

Returns TRUE is the element at C<key> exists; otherwise returns false.

=cut

*/

    INTVAL exists_keyed(PMC* key) {
        INTVAL ix;
        PMC* nextkey;
        PMC* box;

        ix = key_integer(INTERP, key);
        nextkey = key_next(INTERP, key);
        if (nextkey == NULL)
        return SELF.exists_keyed_int(ix);

        box = SELF.get_pmc_keyed_int(ix);
        if (box == NULL)
        return 0;
        return VTABLE_exists_keyed(INTERP, box, nextkey);
    }

/*

=item C<void delete_keyed_int(INTVAL key)>

Removes the element at C<key>.

=cut

*/

    void delete_keyed_int(INTVAL key) {
        list_splice(INTERP, (List *) PMC_data(pmc), NULL, key, 1);
    }

/*

=item C<void delete_keyed(PMC* key)>

Removes the element at C<*key>.

=cut

*/

    void delete_keyed(PMC* key) {
        INTVAL ix = key_integer(INTERP, key);
        list_splice(INTERP, (List *) PMC_data(pmc), NULL, ix, 1);
    }

/*

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

The C<==> operation. Compares two array to hold equal elements.

=cut

*/

    INTVAL is_equal(PMC* value) {
        INTVAL j, n;

        if (value->vtable->base_type != enum_class_Array)
            return 0;
        n = SELF.elements();
        if (VTABLE_elements(INTERP, value) != n)
            return 0;
        for (j = 0; j < n; ++j) {
            PMC *item1, *item2;
            item1 = SELF.get_pmc_keyed_int(j);
            item2 = VTABLE_get_pmc_keyed_int(INTERP, value, j);
            if (item1 == item2)
                continue;
            if (!mmd_dispatch_i_pp(INTERP, item1, item2, MMD_EQ))
                return 0;
        }
        return 1;
    }

/*

=item C<PMC* slice(PMC *key, INTVAL f)>

Return a new iterator for the slice PMC C<key> if f == 0.

Return a new pythonic array slice if f == 1.

=item C<PMC* get_iter()>

Return a new iterator for SELF.

=cut

*/

    PMC* slice(PMC* key, INTVAL f) {
        switch (f) {
            case 0:
                {
                    PMC *iter = pmc_new_init(INTERP,
                            enum_class_Iterator, SELF);
                    PMC_struct_val(iter) = key;
                    return iter;
                }
        }
        real_exception(INTERP, NULL, E_TypeError,
            "Array: Unknown slice type");
        return NULL;
    }

    PMC* get_iter() {
        PMC *iter = pmc_new_init(INTERP, enum_class_Iterator, SELF);
        PMC *key =  pmc_new(INTERP, enum_class_Key);
        PMC_struct_val(iter) = key;
        PObj_get_FLAGS(key) |= KEY_integer_FLAG;
        PMC_int_val(key) = 0;
        if (!((List *) PMC_data(SELF))->length)
            PMC_int_val(key) = -1;
        return iter;
    }

/*

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

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

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

=cut

*/

    void visit(visit_info *info) {
        list_visit(INTERP, (List *) PMC_data(SELF), info);
        SUPER(info);
    }

/*

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

Used to archive the array.

=cut

*/

    void freeze(visit_info *info) {
        IMAGE_IO *io = info->image_io;
        SUPER(info);
        io->vtable->push_integer(INTERP, io, VTABLE_elements(INTERP, SELF));
    }

/*

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

Used to unarchive the array.

=cut

*/

    void thaw(visit_info *info) {
        IMAGE_IO *io = info->image_io;
        SUPER(info);
        if (info->extra_flags == EXTRA_IS_NULL) {
            DYNSELF.set_integer_native(io->vtable->shift_integer(INTERP, io));
    }
}

/*

=item C<void share_ro()>

Recursively make the array read-only and shared.

=cut

*/
    PMC* share_ro() {
        PMC *true;
        PMC *ret;

        /* prevent infinite recursion */
        if (PObj_is_PMC_shared_TEST(SELF)) {
            real_exception(INTERP, NULL, INVALID_OPERATION, "share_ro on "
                           "something that already is shared");
        }

        true = pmc_new(INTERP, enum_class_Integer);
        VTABLE_set_integer_native(INTERP, true, 1);

        ret = pt_shared_fixup(INTERP, SELF);

        /* first set readonly */
        VTABLE_setprop(INTERP, ret, const_string(INTERP, "_ro"), true);

        /* XXX do something that deals better with sparse lists */
        {
            INTVAL i;
            INTVAL max;
            max = VTABLE_elements(INTERP, ret);
            for (i = 0; i < max; ++i) {
                PMC *value;

                value = list_get(INTERP, PMC_data(ret), i, enum_type_PMC);
                if (!PMC_IS_NULL(value)) {
                    PMC *new_value;
                    /* XXX do we need to clone first? */
                    new_value = VTABLE_share_ro(INTERP, value);
                    if (new_value != value) {
                        list_assign(INTERP, PMC_data(ret),
                            i, new_value, enum_type_PMC);
                    }
                }
            }
        }

        /* XXX FIXME workaround lack of metadata sharing */
        PMC_metadata(SELF) = NULL;

        return ret;
    }
}

/*

=back

=head1 SEE ALSO

F<src/list.c>, F<include/parrot/list.h>

=head1 TODO

Create global immutable undef object.

=cut

*/

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