/*
Copyright (C) 2001-2003, The Perl Foundation.
$Id: nci.pmc 16768 2007-01-23 20:36:50Z particle $

=head1 NAME

src/pmc/nci.pmc - Native Call Interface

=head1 DESCRIPTION

The vtable functions for the native C call functions.

Invoking an NCI function changes some registers according to PDD 3.

The caller has to preserve registers if needed.

=head2 Methods

=over 4

=cut

*/

#include "parrot/parrot.h"
pmclass NCI need_ext {

/*

=item C<METHOD PMC* get_multisig()>

Return the MMD signature PMC, if any or a Null PMC.

=cut

*/

    METHOD PMC* get_multisig() {
        return PMC_pmc_val(SELF) ? PMC_pmc_val(SELF) : PMCNULL;
    }

/*

=item C<METHOD PMC* set_raw_nci_ptr(void *func)>

Sets the specified function pointer and raw flag.

=cut

*/

    METHOD void make_raw_nci(void *func) {
        PMC_struct_val(SELF) = func;
        PObj_flag_SET(private2, SELF);
    }

/*

=item C<void init()>

Initializes the NCI with a C<NULL> function pointer.

=cut

*/

    void init() {
        PMC_struct_val(SELF) = NULL;
        PMC_pmc_val(SELF) = NULL;
        PObj_flag_CLEAR(private2, SELF);
    }

/*

=item C<void set_pointer_keyed_str(STRING *key, void *func)>

Sets the specified function pointer and signature (C<*key>).

=cut

*/

    void set_pointer_keyed_str(STRING *key, void *func) {
        /* key = signature */
        PMC_struct_val(SELF) = func;
        PMC_data(SELF) = build_call_func(INTERP, SELF, key);
    }

/*

=item C<void destroy()>

Destroys the NCI, freeing any allocated memory.

=cut

*/

    void destroy() {
        if (PMC_data(SELF))
            mem_free_executable(PMC_data(SELF));
    }

/*

=item C<PMC *clone()>

Creates and returns a clone of the NCI.

=cut

*/

    PMC* clone() {
        PMC* ret = pmc_new_noinit(INTERP, SELF->vtable->base_type);
        PMC_struct_val(ret) = PMC_struct_val(SELF);
        PMC_pmc_val(ret) = NULL;
        /* FIXME if data is malloced (JIT/i386!) then we need
         * the length of data here, to memcpy it
         * ManagedStruct or Buffer?
         */
        PMC_data(ret) = PMC_data(SELF);
        PObj_get_FLAGS(ret) |= (PObj_get_FLAGS(SELF) & 0x3);
        return ret;
    }

/*

=item C<INTVAL defined()>

Returns whether the NCI is defined.

=cut

*/

    INTVAL defined() {
        return PMC_data(SELF) != NULL;
    }

/*

=item C<void *invoke(void *next)>

Calls the associated C function, returning C<*next>. If
the invocant is a class, the PMC arguments are
shifted down.

=cut

*/

    void* invoke(void * next) {
        typedef INTVAL(*nci_sub_t)(Interp * , PMC * );
        nci_sub_t func = PObj_flag_TEST(private2, SELF)
            ? (nci_sub_t) PMC_struct_val(SELF)
            : (nci_sub_t) D2FPTR(PMC_data(SELF));
        PMC *cont;

        if (!func)
            real_exception(INTERP, NULL, INVALID_OPERATION,
                "attempt to call NULL function");
        func(INTERP, SELF);
        cont = INTERP->current_cont;
        /*
         * If the NCI function was tailcalled, the return result
         * is already passed back to the caller of this frame
         * - see  Parrot_init_ret_nci(). We therefore invoke the
         * return continuation here, which gets rid of this frame
         * and returns the real return address
         */
        if (cont && cont != NEED_CONTINUATION &&
                (PObj_get_FLAGS(cont) & SUB_FLAG_TAILCALL)) {
            cont = CONTEXT(interp->ctx)->current_cont;
            next = VTABLE_invoke(INTERP, cont, next);
        }
        return next;
    }

/*

=item C<INTVAL get_integer()>

Returns the function pointer as an integer.

=cut

*/

    INTVAL get_integer() {
        return ((INTVAL)PMC_data(SELF));
    }

/*

=item C<INTVAL get_bool()>

Returns the boolean value of the pointer.

=cut

*/

    INTVAL get_bool() {
        return (0 != (INTVAL)PMC_data(SELF));
    }

}

/*

=back

=head1 SEE ALSO

F<docs/pdds/pdd03_calling_conventions.pod>.

=head1 HISTORY

Initial revision by sean 2002/08/04.

=cut

*/

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