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/nci.pmc - Native Call Interface

=head1 DESCRIPTION

The vtable functions for the native C call functions.

=head2 Methods

=over 4

=cut

*/

/* HEADERIZER HFILE: none */
/* HEADERIZER BEGIN: static */
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */

PARROT_IGNORABLE_RESULT
static nci_thunk_t /*@alt void@*/
build_func(PARROT_INTERP,
    ARGIN(PMC *obj),
    ARGMOD(Parrot_NCI_attributes *nci))
        __attribute__nonnull__(1)
        __attribute__nonnull__(2)
        __attribute__nonnull__(3)
        FUNC_MODIFIES(*nci);

#define ASSERT_ARGS_build_func __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
       PARROT_ASSERT_ARG(interp) \
    , PARROT_ASSERT_ARG(obj) \
    , PARROT_ASSERT_ARG(nci))
/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
/* HEADERIZER END: static */

/*

=item C<static nci_thunk_t build_func(PARROT_INTERP, PMC *obj,
Parrot_NCI_attributes *nci)>

Actually build the NCI thunk.

=cut

*/

PARROT_IGNORABLE_RESULT
static nci_thunk_t
build_func(PARROT_INTERP, ARGIN(PMC *obj), ARGMOD(Parrot_NCI_attributes *nci))
{
    ASSERT_ARGS(build_func)

    Parrot_nci_sig_to_pcc(interp, nci->signature,
                            &nci->pcc_params_signature,
                            &nci->pcc_return_signature);

    /* Arity is length of the signature minus one (the return type). */
    nci->arity       = VTABLE_elements(interp, nci->signature) - 1;

    /* Build call function. */
    nci->fb_info     = build_call_func(interp, nci->signature);
    nci->func        = F2DPTR(VTABLE_get_pointer(interp, nci->fb_info));
    PARROT_GC_WRITE_BARRIER(interp, obj);

    return (nci_thunk_t)nci->func;
}


pmclass NCI auto_attrs provides invokable {
    /* NCI thunk handling attributes */
    ATTR PMC       *signature; /* parsed signature */
    ATTR void      *func;      /* function pointer to call */
    ATTR PMC       *fb_info;   /* frame-builder info */
    ATTR void      *orig_func; /* pointer to wrapped function */

    /* Parrot Sub-ish attributes */
    ATTR STRING    *pcc_params_signature;
    ATTR STRING    *pcc_return_signature;
    ATTR INTVAL     arity;

    /* MMD fields */
    ATTR STRING    *long_signature;
    ATTR PMC       *multi_sig;

/*

=item C<METHOD get_multisig()>

Return the MMD signature PMC, if any or C<PMCNULL>.

=cut

*/

    METHOD get_multisig() {
        PMC *sig;
        GET_ATTR_multi_sig(INTERP, SELF, sig);
        if (PMC_IS_NULL(sig))
            sig = PMCNULL;
        RETURN(PMC *sig);
    }

/*

=item C<void init()>

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

=cut

*/

    VTABLE void init() {
        PObj_custom_mark_SET(SELF);
    }

    VTABLE void *get_pointer() {
        return PARROT_NCI(SELF)->orig_func;
    }

/*

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

=item C<void set_pmc_keyed_str(STRING *key, PMC *p)>

Call the equivalent C<set_pointer*> function.

=cut

*/

    VTABLE void set_pmc_keyed(PMC *key, PMC *p) {
        STATICSELF.set_pointer_keyed(key, VTABLE_get_pointer(INTERP, p));
    }

    VTABLE void set_pmc_keyed_str(STRING *key, PMC *p) {
        STATICSELF.set_pointer_keyed_str(key, VTABLE_get_pointer(INTERP, p));
    }

/*

=item C<void set_pointer_keyed(PMC *key, void *func)>

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

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

Sets the specified function pointer and siganture as described in the string C<key>.

=cut

*/

    VTABLE void set_pointer_keyed(PMC *key, void *func) {
        /* Store the original function and signature. */
        SET_ATTR_orig_func(INTERP, SELF, func);
        SET_ATTR_signature(INTERP, SELF, key);
    }

    VTABLE void set_pointer_keyed_str(STRING *key, void *func) {
        SELF.set_pointer_keyed(Parrot_nci_parse_signature(INTERP, key), func);
    }

/*

=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);

            Parrot_gc_mark_PMC_alive(interp, nci_info->signature);
            Parrot_gc_mark_PMC_alive(interp, nci_info->fb_info);
            Parrot_gc_mark_PMC_alive(interp, nci_info->multi_sig);

            Parrot_gc_mark_STRING_alive(interp, nci_info->long_signature);
            Parrot_gc_mark_STRING_alive(interp, nci_info->pcc_params_signature);
            Parrot_gc_mark_STRING_alive(interp, nci_info->pcc_return_signature);
        }
    }

/*

=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);

        PMC * const ret     = Parrot_pmc_new(INTERP, SELF->vtable->base_type);
        Parrot_NCI_attributes * const 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->fb_info               = nci_info_self->fb_info;
        nci_info_ret->orig_func             = nci_info_self->orig_func;
        nci_info_ret->signature             = nci_info_self->signature;
        nci_info_ret->pcc_params_signature  = nci_info_self->pcc_params_signature;
        nci_info_ret->pcc_return_signature  = nci_info_self->pcc_params_signature;
        nci_info_ret->arity                 = nci_info_self->arity;
        PObj_get_FLAGS(ret)                 = PObj_get_FLAGS(SELF);

        return ret;
    }

/*

=item C<INTVAL defined()>

Returns whether the NCI is defined.

=cut

*/

    VTABLE INTVAL defined() {
        const Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);
        return nci_info->orig_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);
        nci_thunk_t                   func;
        PMC                          *fb_info;
        PMC                          *cont;

        func = (nci_thunk_t)D2FPTR(nci_info->func);

        if (!func) {
            /* build the thunk only when necessary */
            func = build_func(interp, SELF, nci_info);

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

        GET_ATTR_fb_info(INTERP, SELF, fb_info);

        cont = INTERP->current_cont;
        func(INTERP, SELF, fb_info);

        /*
         * 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 (!PMC_IS_NULL(cont)
        && (PObj_get_FLAGS(cont) & SUB_FLAG_TAILCALL)) {
            cont = Parrot_pcc_get_continuation(interp, CURRENT_CONTEXT(interp));
            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);
        if (!nci_info->func)
            build_func(INTERP, SELF, nci_info);
        return (INTVAL)nci_info->func;
    }

/*

=item C<INTVAL get_bool()>

Returns the boolean value of the pointer.

=cut

*/

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

/*

=item C<METHOD arity()>

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

=cut

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

        if (nci_info) {
            if (!nci_info->func)
                build_func(INTERP, SELF, nci_info);
            if (nci_info->func) {
                const INTVAL arity = nci_info->arity;
                RETURN(INTVAL arity);
            }
        }

        Parrot_ex_throw_from_c_args(INTERP, NULL,
            EXCEPTION_INVALID_OPERATION,
            "You cannot get the arity of an undefined NCI.");
    }
}

/*

=back

=head1 SEE ALSO

F<docs/pdds/pdd03_calling_conventions.pod>.

=cut

*/

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