The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
/*
Copyright (C) 2001-2011, Parrot Foundation.


=head1 NAME

src/pmc/packfileopmap.pmc - Packfile Op Map PMC

=head1 DESCRIPTION

This class implements a PackfileOpMap, which provides a map between op
numbers in a bytecode segment and the libraries and offsets within those
libraries that the ops come from.

=head2 Vtable functions

=over 4

=cut

*/

#include "pmc/pmc_parrotlibrary.h"

/* HEADERIZER HFILE: none */
/* HEADERIZER BEGIN: static */
/* HEADERIZER END: static */


pmclass PackfileOpMap auto_attrs {
    ATTR PMC    *op_maps;       /* RPA of Hashes of op lib maps */
    ATTR PMC    *map_cache;     /* Hash mapping full op names to numbers */
    ATTR PMC    *ops;           /* RSA of mapped ops. */

/*

=item C<init>

Create empty PackfileOpMap for a given op library.

=cut

*/

    VTABLE void init() {
        PMC *core_ops_info, *core_ops_pmc;
        STRING *oplib_name, *oplib_str, *lib_ops, *table_ops;
        Parrot_PackfileOpMap_attributes *attrs = PARROT_PACKFILEOPMAP(SELF);

        PObj_custom_mark_SET(SELF);

        attrs->op_maps    = Parrot_pmc_new(INTERP, enum_class_ResizablePMCArray);
        attrs->map_cache  = Parrot_pmc_new(INTERP, enum_class_Hash);
        attrs->ops        = Parrot_pmc_new(INTERP, enum_class_ResizableStringArray);

        core_ops_pmc = Parrot_pmc_new(INTERP, enum_class_String);
        VTABLE_set_string_native(INTERP, core_ops_pmc, CONST_STRING(INTERP, "core_ops"));
        lib_ops      = CONST_STRING(INTERP, "lib_ops");
        table_ops    = CONST_STRING(INTERP, "table_ops");
        oplib_name   = CONST_STRING(INTERP, "oplib_name");
        oplib_str    = CONST_STRING(INTERP, "oplib");

        core_ops_info = Parrot_pmc_new(INTERP, enum_class_Hash);
        VTABLE_set_pmc_keyed_str(INTERP, core_ops_info, oplib_name, core_ops_pmc);
        VTABLE_set_pmc_keyed_str(INTERP, core_ops_info, oplib_str,
                Parrot_pmc_new_init(INTERP, enum_class_OpLib, core_ops_pmc));
        VTABLE_set_pmc_keyed_str(INTERP, core_ops_info, lib_ops,
                Parrot_pmc_new(INTERP, enum_class_ResizableIntegerArray));
        VTABLE_set_pmc_keyed_str(INTERP, core_ops_info, table_ops,
                Parrot_pmc_new(INTERP, enum_class_ResizableIntegerArray));
        VTABLE_push_pmc(INTERP, attrs->op_maps, core_ops_info);
    }

/*

=item C<get_integer_keyed>

Return the integer which maps to the given op, if any.  If the op is not
already in the map, a new map will be created.

=cut

*/

    VTABLE INTVAL get_integer_keyed(PMC *key) {
        STRING *key_str, *oplib_str;
        PMC    *map_cache, *op_maps, *ops;
        INTVAL  op_map_count, i;

        GET_ATTR_map_cache(INTERP, SELF, map_cache);

        if (VTABLE_exists_keyed(INTERP, map_cache, key)) {
            return VTABLE_get_integer_keyed(INTERP, map_cache, key);
        }

        key_str = Parrot_key_string(INTERP, key);
        oplib_str = CONST_STRING(INTERP, "oplib");
        GET_ATTR_op_maps(INTERP, SELF, op_maps);
        op_map_count = VTABLE_elements(INTERP, op_maps);
        for (i = 0; i < op_map_count; i++) {

            PMC *table_ops, *lib_ops;
            STRING *table_ops_str, *lib_ops_str;
            INTVAL op_count;

            PMC * const op_map  = VTABLE_get_pmc_keyed_int(INTERP, op_maps, i);
            PMC * const oplib   = VTABLE_get_pmc_keyed_str(INTERP, op_map, oplib_str);
            const INTVAL op_num = VTABLE_get_integer_keyed(INTERP, oplib, key);

            /* Not in this oplib */
            if (op_num == -1) {
                continue;
            }

            table_ops_str = CONST_STRING(INTERP, "table_ops");
            lib_ops_str   = CONST_STRING(INTERP, "lib_ops");

            table_ops = VTABLE_get_pmc_keyed_str(INTERP, op_map, table_ops_str);
            lib_ops   = VTABLE_get_pmc_keyed_str(INTERP, op_map, lib_ops_str);

            GET_ATTR_ops(INTERP, SELF, ops);
            op_count = VTABLE_elements(INTERP, ops);

            VTABLE_set_integer_keyed(INTERP, map_cache, key, op_count);
            VTABLE_push_integer(INTERP, table_ops, op_count);
            VTABLE_push_integer(INTERP, lib_ops, op_num);

            VTABLE_push_string(INTERP, ops, key_str);

            return op_count;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
                        "Couldn't find '%Ss' op.", key_str);
        return -1;
    }

    VTABLE INTVAL get_integer_keyed_str(STRING *str_key) {
        PMC * const pmc_key = Parrot_key_new_string(INTERP, str_key);
        return SELF.get_integer_keyed(pmc_key);
    }


/*
=item C<get_pmc_keyed_str()>

Get Op PMC by name.

=cut
*/

    VTABLE PMC* get_pmc_keyed_str(STRING *name) {
        PMC    *op_maps;
        INTVAL  op_map_count, i;
        STRING * const oplib_str = CONST_STRING(INTERP, "oplib");

        GET_ATTR_op_maps(INTERP, SELF, op_maps);
        op_map_count = VTABLE_elements(INTERP, op_maps);
        for (i = 0; i < op_map_count; i++) {
            PMC * const op_map = VTABLE_get_pmc_keyed_int(INTERP, op_maps, i);
            PMC * const oplib  = VTABLE_get_pmc_keyed_str(INTERP, op_map, oplib_str);
            PMC * const op     = VTABLE_get_pmc_keyed_str(INTERP, oplib, name);

            if (!PMC_IS_NULL(op))
                return op;
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
                        "Couldn't find '%Ss' op.", name);
        return PMCNULL;
    }

    VTABLE PMC* get_pmc_keyed(PMC *key) {
        return STATICSELF.get_pmc_keyed_str(Parrot_key_string(INTERP, key));
    }

/*
=item C<get_string_keyed_int()>

=item C<get_pmc_keyed_int()>

Lookup op name by mapped id.

=cut
*/
    VTABLE STRING* get_string_keyed_int(INTVAL key) {
        PMC *ops;
        GET_ATTR_ops(INTERP, SELF, ops);
        return VTABLE_get_string_keyed_int(INTERP, ops, key);
    }

    VTABLE PMC* get_pmc_keyed_int(INTVAL key) {
        return Parrot_pmc_box_string(INTERP, STATICSELF.get_string_keyed_int(key));
    }


/*

=item C<void mark()>

Marks the object as live.

=cut

*/

    VTABLE void mark() {
        PMC *tmp;

        GET_ATTR_op_maps(INTERP, SELF, tmp);
        Parrot_gc_mark_PMC_alive(INTERP, tmp);

        GET_ATTR_map_cache(INTERP, SELF, tmp);
        Parrot_gc_mark_PMC_alive(INTERP, tmp);

        GET_ATTR_ops(INTERP, SELF, tmp);
        Parrot_gc_mark_PMC_alive(INTERP, tmp);
    }


/*

=item C<INTVAL get_integer()>

Get the number of opcode mappings.

=cut

*/

    VTABLE INTVAL get_integer() {
        PMC *ops;
        GET_ATTR_ops(INTERP, SELF, ops);
        return VTABLE_elements(INTERP, ops);
    }

/*

=item C<METHOD load_lib>

Ensure that an op library is loaded and accessible to this OpMap.

=cut

*/
    METHOD load_lib(STRING *lib_name) {
        /* check if the library has been loaded */
        PMC *op_maps, *ops_info, *oplib, *lib_name_pmc;
        INTVAL i, op_map_count;
        STRING *oplib_name, *lib_ops, *table_ops, *oplib_str;
        Parrot_PackfileOpMap_attributes * const attrs =
                PARROT_PACKFILEOPMAP(SELF);

        oplib_name = CONST_STRING(INTERP, "oplib_name");

        GET_ATTR_op_maps(INTERP, SELF, op_maps);
        op_map_count = VTABLE_elements(INTERP, op_maps);

        for (i = 0; i < op_map_count; i++) {
            PMC * const map = VTABLE_get_pmc_keyed_int(INTERP, op_maps, i);
            if (Parrot_str_equal(INTERP, lib_name,
                        VTABLE_get_string_keyed_str(INTERP, map, oplib_name))) {
                RETURN(INTVAL 1);
            }
        }

        /* create a new OpLib etc */
        lib_name_pmc = Parrot_pmc_new(INTERP, enum_class_String);
        VTABLE_set_string_native(INTERP, lib_name_pmc, lib_name);
        /* OpLib.init will throw exception on failure */
        oplib = Parrot_pmc_new_init(INTERP, enum_class_OpLib, lib_name_pmc);

        lib_ops   = CONST_STRING(INTERP, "lib_ops");
        table_ops = CONST_STRING(INTERP, "table_ops");
        oplib_str = CONST_STRING(INTERP, "oplib");

        ops_info = Parrot_pmc_new(INTERP, enum_class_Hash);
        VTABLE_set_pmc_keyed_str(INTERP, ops_info, oplib_name, lib_name_pmc);
        VTABLE_set_pmc_keyed_str(INTERP, ops_info, oplib_str, oplib);
        VTABLE_set_pmc_keyed_str(INTERP, ops_info, lib_ops,
                Parrot_pmc_new(INTERP, enum_class_ResizableIntegerArray));
        VTABLE_set_pmc_keyed_str(INTERP, ops_info, table_ops,
                Parrot_pmc_new(INTERP, enum_class_ResizableIntegerArray));
        VTABLE_push_pmc(INTERP, attrs->op_maps, ops_info);

        RETURN(INTVAL 1);
    }


/*
=item C<get_pointer>

Construct PackFile_ByteCode_OpMapping.

=cut
*/

    VTABLE void *get_pointer() {
        PMC                         *op_maps;
        PackFile_ByteCode_OpMapping *m;
        INTVAL                       i;

        GET_ATTR_op_maps(INTERP, SELF, op_maps);

        /* Allocate OpMapping. Caller must free results */
        m = mem_gc_allocate_zeroed_typed(INTERP, PackFile_ByteCode_OpMapping);

        m->n_libs = VTABLE_get_integer(INTERP, op_maps);
        m->libs   = mem_gc_allocate_n_zeroed_typed(INTERP, m->n_libs,
                            PackFile_ByteCode_OpMappingEntry);

        /* Fill OpMappingEntry */
        for (i = 0; i < m->n_libs; i++) {
            INTVAL j;
            PackFile_ByteCode_OpMappingEntry *om = &m->libs[i];
            PMC * const mapping = VTABLE_get_pmc_keyed_int(INTERP, op_maps, i);

            STRING *tmp;
            PMC    *oplib, *table_ops, *lib_ops;

            tmp     = CONST_STRING(INTERP, "oplib");
            oplib   = VTABLE_get_pmc_keyed_str(INTERP, mapping, tmp);
            om->lib = (op_lib_t *)VTABLE_get_pointer(INTERP, oplib);

            tmp       = CONST_STRING(INTERP, "table_ops");
            table_ops = VTABLE_get_pmc_keyed_str(INTERP, mapping, tmp);

            om->n_ops = VTABLE_get_integer(INTERP, table_ops);

            om->table_ops = mem_gc_allocate_n_zeroed_typed(INTERP, om->n_ops, opcode_t);
            for (j = 0; j < om->n_ops; j++) {
                om->table_ops[j] = VTABLE_get_integer_keyed_int(INTERP, table_ops, j);
            }

            tmp = CONST_STRING(INTERP, "lib_ops");
            lib_ops     = VTABLE_get_pmc_keyed_str(INTERP, mapping, tmp);
            om->lib_ops = mem_gc_allocate_n_zeroed_typed(INTERP, om->n_ops, opcode_t);
            for (j = 0; j < om->n_ops; j++) {
                om->lib_ops[j] = VTABLE_get_integer_keyed_int(INTERP, lib_ops, j);
            }

        }

        return m;
    }

/*

=back

=head2 Methods

=over 4

=item C<PMC *oplibs()>

Returns an hash of name to C<OpLib> PMCs for all oplibs loaded in the map.

=cut

*/

    METHOD oplibs() {
        STRING *oplib_str  = CONST_STRING(INTERP, "oplib");
        STRING *oplib_name = CONST_STRING(INTERP, "oplib_name");
        PMC *op_maps, *result;
        INTVAL i, size;

        GET_ATTR_op_maps(INTERP, SELF, op_maps);

        result = Parrot_pmc_new(INTERP, enum_class_Hash);

        size = VTABLE_elements(INTERP, op_maps);
        for (i = 0; i < size; ++i) {
            PMC * const op_map  = VTABLE_get_pmc_keyed_int(INTERP, op_maps, i);
            PMC * const oplib   = VTABLE_get_pmc_keyed_str(INTERP, op_map, oplib_str);
            STRING * const name = VTABLE_get_string_keyed_str(INTERP, op_map, oplib_name);

            VTABLE_set_pmc_keyed_str(INTERP, result, name, oplib);
        }

        RETURN(PMC *result);
    }

}


/*

=back

=cut

*/

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