/*
Copyright (C) 2001-2008, The Perl Foundation.
$Id: /mirror/trunk/src/pmc/nci.pmc 35702 2009-01-17T10:43:56.666163Z bernhard  $

=head1 NAME

src/pmc/nci.pmc - Native Call Interface

=head1 DESCRIPTION

The vtable functions for the native C call functions.

=head2 Methods

=over 4

=cut

*/

#include "parrot/parrot.h"

void pcc_params(PARROT_INTERP, STRING *sig, Parrot_NCI_attributes * const nci_info);
void pcc_params(PARROT_INTERP, STRING *sig, Parrot_NCI_attributes * const nci_info) {
        char   *sig_c      = string_to_cstring(interp, sig);
        size_t  sig_length = strlen(sig_c);
        char   *param_sig  = mem_allocate_n_zeroed_typed(sig_length, char);
        size_t  j          = 0;
        size_t  i;

        for (i = 1; i < sig_length; i++) {
            switch (sig_c[i]) {
                case '0':    /* null ptr or such - doesn't consume a reg */
                    break;
                case 'f':
                case 'N':
                case 'd':
                    param_sig[j++] = 'N';
                    break;
                case 'I':   /* INTVAL */
                case 'l':   /* long */
                case 'i':   /* int */
                case 's':   /* short */
                case 'c':   /* char */
                    param_sig[j++] = 'I';
                    break;
                case 'S':
                case 't':   /* string, pass a cstring */
                    param_sig[j++] = 'S';
                    break;
                case 'J':   /* interpreter */
                    break;
                case 'p':   /* push pmc->data */
                case 'O':   /* push PMC * object in P2 */
                case 'P':   /* push PMC * */
                case 'V':   /* push PMC * */
                    param_sig[j++] = 'P';
                case 'v':
                    break;
                    /* I have no idea how to handle these */
                case '2':
                case '3':
                case '4':
                    param_sig[j++] = 'I';
                    break;
                case '@':
                    param_sig[j++] = '@';
                    break;
                case 'b':   /* buffer (void*) pass PObj_bufstart(SReg) */
                case 'B':   /* buffer (void**) pass &PObj_bufstart(SReg) */
                    param_sig[j++] = 'S';
                    break;
                default:
                    Parrot_ex_throw_from_c_args(interp, NULL,
                        EXCEPTION_JIT_ERROR,
                        "Unknown param Signature %c\n", sig_c[i]);
                    break;
            }
        }

        PARROT_ASSERT(j <= sig_length);

        /* use only the signature-significant part of the string buffer */
        if (j) {
            nci_info->pcc_params_signature = string_make(interp, param_sig, j,
                NULL, PObj_constant_FLAG);
        }
        else
            nci_info->pcc_params_signature = CONST_STRING(interp, "");

        mem_sys_free(param_sig);
        string_cstring_free(sig_c);
    }


pmclass NCI need_ext {
    ATTR STRING    *signature;              /* The signature. */
    ATTR void      *func;                   /* Function pointer to what we'll call. */
    ATTR STRING    *pcc_params_signature;   /* The signature. */
    ATTR INTVAL     arity;                  /* Cached arity of the NCI. */
    ATTR INTVAL     jitted;                 /* Is this a jitted NCI stub. */

/*

=item C<METHOD get_multisig()>

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

=cut

*/

    METHOD get_multisig() {
        PMC *sig = PMC_pmc_val(SELF) ? PMC_pmc_val(SELF) : PMCNULL;
        RETURN(PMC *sig);
    }

/*

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

Sets the specified function pointer and raw flag.

=cut

*/

    METHOD make_raw_nci(PMC *func) {
        VTABLE_set_pointer(interp, SELF, (void *)func);
    }

/*

=item C<void init()>

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

=cut

*/

    VTABLE void init() {
        PMC_struct_val(SELF) = NULL;
        PMC_pmc_val(SELF)    = PMCNULL;
        PMC_data(SELF)       = mem_allocate_zeroed_typed(Parrot_NCI_attributes);

        /* Mark that we're not a raw NCI. */
        PObj_flag_CLEAR(private2, SELF);
        PObj_custom_mark_SET(SELF);
        PObj_active_destroy_SET(SELF);
    }

/*

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

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

=cut

*/

    VTABLE void set_pointer(void *ptr) {
        PMC_struct_val(SELF) = ptr;
        PObj_flag_SET(private2, SELF);
    }

    VTABLE void set_pointer_keyed_str(STRING *key, void *func) {
        Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);
        int                             jitted = 0;
        char                     * const key_c = string_to_cstring(INTERP, key);

        /* Store the original function and signature. */
        PMC_struct_val(SELF) = func;
        nci_info->signature  = string_make(interp, key_c, strlen(key_c),
                                    NULL, PObj_constant_FLAG);
        string_cstring_free(key_c);
        pcc_params(INTERP, key, nci_info);

        /* Arity is length of that string minus one (the return type). */
        nci_info->arity      = string_length(INTERP, key) - 1;

        /* Build call function. */
        nci_info->func       = (PMC *)(build_call_func(INTERP, SELF,
                                            key, &jitted));
        nci_info->jitted = jitted;
    }

/*

=item C<void mark()>

Mark any referenced strings and PMCs.

=cut

*/
    VTABLE void mark() {
        if (PARROT_NCI(SELF)) {
            Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);

            if (nci_info->signature)
                pobject_lives(interp, (PObj*)nci_info->signature);
            if (nci_info->pcc_params_signature)
                pobject_lives(interp, (PObj*)nci_info->pcc_params_signature);
        }
    }

/*

=item C<void destroy()>

Destroys the NCI, freeing any allocated memory.

=cut

*/

    VTABLE void destroy() {
        if (PMC_data(SELF)) {
            Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);

            mem_sys_free(nci_info);
            PMC_data(SELF) = NULL;
        }
    }

/*

=item C<PMC *clone()>

Creates and returns a clone of the NCI.

=cut

*/

    VTABLE PMC *clone() {
        Parrot_NCI_attributes * const nci_info_self = PARROT_NCI(SELF);
        Parrot_NCI_attributes *nci_info_ret;

        PMC * const ret     = pmc_new_noinit(INTERP, SELF->vtable->base_type);
        PMC_struct_val(ret) = PMC_struct_val(SELF);
        PMC_pmc_val(ret)    = PMCNULL;
        PMC_data(ret)       = mem_allocate_zeroed_typed(Parrot_NCI_attributes);
        nci_info_ret        = PARROT_NCI(ret);

        /* FIXME if data is malloced (JIT/i386!) then we need
         * the length of data here, to memcpy it
         * ManagedStruct or Buffer?
         */
        nci_info_ret->func                  = nci_info_self->func;
        nci_info_ret->signature             = nci_info_self->signature;
        nci_info_ret->pcc_params_signature  = nci_info_self->pcc_params_signature;
        nci_info_ret->arity                 = nci_info_self->arity;
        nci_info_ret->jitted                = nci_info_self->jitted;
        PObj_get_FLAGS(ret)    |= (PObj_get_FLAGS(SELF) & 0x7);

        return ret;
    }

/*

=item C<INTVAL defined()>

Returns whether the NCI is defined.

=cut

*/

    VTABLE INTVAL defined() {
        Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);
        return nci_info->func != NULL;
    }

/*

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

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

=cut

*/

    VTABLE opcode_t *invoke(void *next) {
        Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);

        typedef INTVAL (*nci_sub_t)(PARROT_INTERP, PMC *);

        nci_sub_t func = PObj_flag_TEST(private2, SELF)
            ? (nci_sub_t) D2FPTR(PMC_struct_val(SELF))
            : (nci_sub_t) D2FPTR(nci_info->func);

        PMC *cont;

        if (!func)
            Parrot_ex_throw_from_c_args(INTERP, NULL, EXCEPTION_INVALID_OPERATION,
                "attempt to call NULL function");

        if (nci_info->jitted) {
            typedef INTVAL (*nci_jit_sub_t)(PARROT_INTERP, PMC *, char *);

            nci_jit_sub_t jit_func = (nci_jit_sub_t) D2FPTR(nci_info->func);

            /* Parrot_eprintf(interp, "JITTED %S\n", nci_info->signature); */
            jit_func(INTERP, SELF,
                (char *) nci_info->pcc_params_signature->strstart);
        }
        else {
            if (PObj_flag_TEST(private2, SELF)) {
                /* Parrot_eprintf(interp, "RAW NCI CALL\n"); */
            }
            else {
                /* Parrot_eprintf(interp, "HACKED %S\n", nci_info->signature); */
            }
            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)->current_cont;
            next = VTABLE_invoke(INTERP, cont, next);
        }

        return (opcode_t *)next;
    }

/*

=item C<INTVAL get_integer()>

Returns the function pointer as an integer.

=cut

*/

    VTABLE INTVAL get_integer() {
        Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);
        return (INTVAL)nci_info->func;
    }

/*

=item C<INTVAL get_bool()>

Returns the boolean value of the pointer.

=cut

*/

    VTABLE INTVAL get_bool() {
        Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);
        return (0 != (INTVAL)nci_info->func);
    }

/*

=item C<METHOD arity()>

Return the arity of the NCI (the number of arugments).

=cut

*/
    METHOD arity() {
        Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);
        INTVAL arity = 0;

        if (nci_info && nci_info->func)
            arity = nci_info->arity;
        else
            Parrot_ex_throw_from_c_args(INTERP, NULL,
                EXCEPTION_INVALID_OPERATION,
                "You cannot get the arity of an undefined NCI.");

        RETURN(INTVAL arity);
    }
}

/*

=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:
 */
