1/*
2Copyright (C) 2001-2014, Parrot Foundation.
3
4
5=head1 NAME
6
7src/pmc/nci.pmc - Native Call Interface
8
9=head1 DESCRIPTION
10
11The vtable functions for the native C call functions.
12
13=head2 Methods
14
15=over 4
16
17=cut
18
19*/
20
21/* HEADERIZER HFILE: none */
22/* HEADERIZER BEGIN: static */
23/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
24
25PARROT_IGNORABLE_RESULT
26static nci_thunk_t /*@alt void@*/
27build_func(PARROT_INTERP,
28    ARGMOD(Parrot_NCI_attributes *nci_info))
29        __attribute__nonnull__(1)
30        __attribute__nonnull__(2)
31        FUNC_MODIFIES(*nci_info);
32
33static void pcc_params(PARROT_INTERP,
34    ARGIN(STRING *sig),
35    ARGMOD(Parrot_NCI_attributes *nci_info),
36    size_t sig_length)
37        __attribute__nonnull__(1)
38        __attribute__nonnull__(2)
39        __attribute__nonnull__(3)
40        FUNC_MODIFIES(*nci_info);
41
42#define ASSERT_ARGS_build_func __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
43       PARROT_ASSERT_ARG(interp) \
44    , PARROT_ASSERT_ARG(nci_info))
45#define ASSERT_ARGS_pcc_params __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
46       PARROT_ASSERT_ARG(interp) \
47    , PARROT_ASSERT_ARG(sig) \
48    , PARROT_ASSERT_ARG(nci_info))
49/* Don't modify between HEADERIZER BEGIN / HEADERIZER END.  Your changes will be lost. */
50/* HEADERIZER END: static */
51
52/*
53
54=item C<static void pcc_params(PARROT_INTERP, STRING *sig, Parrot_NCI_attributes
55*nci_info, size_t sig_length)>
56
57Check and validate parameter signatures
58
59=cut
60
61*/
62
63static void
64pcc_params(PARROT_INTERP, ARGIN(STRING *sig), ARGMOD(Parrot_NCI_attributes *nci_info),
65                size_t sig_length)
66{
67    ASSERT_ARGS(pcc_params)
68
69    /* NCI and PCC have a 1 to 1 mapping except an
70       extra char in PCC for invocant and slurpy */
71    size_t       buf_length = sig_length + 2 + 1;
72
73    /* avoid malloc churn on common signatures */
74    char         static_buf[16];
75    char * const sig_buf = sig_length <= sizeof static_buf ?
76                            static_buf :
77                            (char *)mem_sys_allocate(buf_length);
78
79    size_t j = 0;
80    size_t i;
81
82    for (i = 0; i < sig_length; ++i) {
83        const INTVAL c = STRING_ord(interp, sig, i);
84
85        PARROT_ASSERT(j < buf_length - 1);
86
87        switch (c) {
88          case (INTVAL)'0':    /* null ptr or such - doesn't consume a reg */
89            break;
90          case (INTVAL)'f':
91          case (INTVAL)'N':
92          case (INTVAL)'d':
93            sig_buf[j++] = 'N';
94            break;
95          case (INTVAL)'I':   /* INTVAL */
96          case (INTVAL)'l':   /* long */
97          case (INTVAL)'i':   /* int */
98          case (INTVAL)'s':   /* short */
99          case (INTVAL)'c':   /* char */
100            sig_buf[j++] = 'I';
101            break;
102          case (INTVAL)'S':
103          case (INTVAL)'t':   /* string, pass a cstring */
104            sig_buf[j++] = 'S';
105            break;
106          case (INTVAL)'J':   /* interpreter */
107            break;
108          case (INTVAL)'p':   /* push pmc->data */
109          case (INTVAL)'P':   /* push PMC * */
110          case (INTVAL)'V':   /* push PMC * */
111          case (INTVAL)'2':
112          case (INTVAL)'3':
113          case (INTVAL)'4':
114            sig_buf[j++] = 'P';
115            break;
116          case (INTVAL)'v':
117            /* null return */
118            if (j == 0)
119                sig_buf[j++] = '\0';
120            break;
121          case (INTVAL)'O':   /* push PMC * invocant */
122            sig_buf[j++] = 'P';
123            sig_buf[j++] = 'i';
124            break;
125          case (INTVAL)'@':   /* push PMC * slurpy */
126            sig_buf[j++] = 'P';
127            sig_buf[j++] = 's';
128            break;
129          case (INTVAL)'b': /* buffer (void*) pass Buffer_bufstart(SReg) */
130          case (INTVAL)'B': /* buffer (void**) pass &Buffer_bufstart(SReg) */
131            sig_buf[j++] = 'S';
132            break;
133          default:
134            Parrot_ex_throw_from_c_args(interp, NULL,
135                    EXCEPTION_JIT_ERROR,
136                    "Unknown param Signature %c\n", (char)c);
137            break;
138        }
139    }
140
141    PARROT_ASSERT(j < buf_length);
142    sig_buf[j++] = '\0';
143
144
145    nci_info->pcc_return_signature =
146        Parrot_str_new(interp, sig_buf, 1);
147
148    nci_info->pcc_params_signature = j > 1 ?
149        Parrot_str_new(interp, sig_buf + 1, j - 1) :
150        CONST_STRING(interp, "");
151
152    if (sig_buf != static_buf)
153        mem_sys_free(sig_buf);
154}
155
156/*
157
158=item C<static nci_thunk_t build_func(PARROT_INTERP, Parrot_NCI_attributes
159*nci_info)>
160
161Actually build the NCI thunk.
162
163=cut
164
165*/
166
167PARROT_IGNORABLE_RESULT
168static nci_thunk_t
169build_func(PARROT_INTERP, ARGMOD(Parrot_NCI_attributes *nci_info))
170{
171    ASSERT_ARGS(build_func)
172
173    STRING * const key      = nci_info->signature;
174    const size_t key_length = Parrot_str_byte_length(interp, key);
175
176    pcc_params(interp, key, nci_info, key_length);
177
178    /* Arity is length of that string minus one (the return type). */
179    nci_info->arity       = key_length - 1;
180
181    /* Build call function. */
182    nci_info->fb_info     = build_call_func(interp, key);
183    nci_info->func        = F2DPTR(VTABLE_get_pointer(interp, nci_info->fb_info));
184
185    return (nci_thunk_t)nci_info->func;
186}
187
188
189pmclass NCI auto_attrs provides invokable {
190    /* NCI thunk handling attributes */
191    /* NCI thunk handling attributes */
192    ATTR STRING    *signature;              /* The signature. */
193    ATTR void      *func;                   /* Function pointer to call. */
194    ATTR PMC       *fb_info;                /* Frame-builder info */
195    ATTR void      *orig_func;              /* Function pointer
196                                             * used to create func */
197    /* Parrot Sub-ish attributes */
198    ATTR STRING    *pcc_params_signature;
199    ATTR STRING    *pcc_return_signature;
200    ATTR INTVAL     arity;                  /* Cached arity of the NCI. */
201
202    /* MMD fields */
203    ATTR STRING    *long_signature;         /* The full signature. */
204    ATTR PMC       *multi_sig;              /* type tuple array (?) */
205
206/*
207
208=item C<METHOD get_multisig()>
209
210Return the MMD signature PMC, if any or a Null PMC.
211
212=cut
213
214*/
215
216    METHOD get_multisig() :no_wb {
217        PMC *sig;
218        GET_ATTR_multi_sig(INTERP, SELF, sig);
219        if (PMC_IS_NULL(sig))
220            sig = PMCNULL;
221        RETURN(PMC *sig);
222    }
223
224/*
225
226=item C<void init()>
227
228Initializes the NCI with a C<NULL> function pointer.
229
230=cut
231
232*/
233
234    VTABLE void init() {
235        PObj_custom_mark_SET(SELF);
236    }
237
238    VTABLE void *get_pointer() :no_wb {
239        return PARROT_NCI(SELF)->orig_func;
240    }
241
242/*
243
244=item C<void set_pointer_keyed_str(STRING *key, void *func)>
245
246Sets the specified function pointer and signature (C<*key>).
247
248=cut
249
250*/
251
252    VTABLE void set_pointer_keyed_str(STRING *key, void *func) {
253        Parrot_NCI_attributes * const nci_info   = PARROT_NCI(SELF);
254
255        /* Store the original function and signature. */
256        SET_ATTR_orig_func(INTERP, SELF, func);
257
258        /* ensure that the STRING signature is constant */
259        if (!PObj_constant_TEST(key)) {
260            char * const key_c      = Parrot_str_to_cstring(INTERP, key);
261            const size_t key_length = Parrot_str_byte_length(interp, key);
262            key                     = Parrot_str_new_init(interp, key_c, key_length,
263                                        Parrot_default_encoding_ptr, PObj_constant_FLAG);
264            Parrot_str_free_cstring(key_c);
265        }
266
267        nci_info->signature = key;
268    }
269
270/*
271
272=item C<void mark()>
273
274Mark any referenced strings and PMCs.
275
276=cut
277
278*/
279    VTABLE void mark() :no_wb {
280        if (PARROT_NCI(SELF)) {
281            Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);
282
283            Parrot_gc_mark_PMC_alive(interp, nci_info->fb_info);
284            Parrot_gc_mark_PMC_alive(interp, nci_info->multi_sig);
285
286            Parrot_gc_mark_STRING_alive(interp, nci_info->signature);
287            Parrot_gc_mark_STRING_alive(interp, nci_info->long_signature);
288            Parrot_gc_mark_STRING_alive(interp, nci_info->pcc_params_signature);
289            Parrot_gc_mark_STRING_alive(interp, nci_info->pcc_return_signature);
290        }
291    }
292
293/*
294
295=item C<PMC *clone()>
296
297Creates and returns a clone of the NCI.
298
299=cut
300
301*/
302
303    VTABLE PMC *clone() :no_wb {
304        Parrot_NCI_attributes * const nci_info_self = PARROT_NCI(SELF);
305        Parrot_NCI_attributes *nci_info_ret;
306        void                  *orig_func;
307
308        PMC * const ret     = Parrot_pmc_new(INTERP, SELF->vtable->base_type);
309        nci_info_ret        = PARROT_NCI(ret);
310
311        /* FIXME if data is malloced (JIT/i386!) then we need
312         * the length of data here, to memcpy it
313         * ManagedStruct or Buffer?
314         */
315        nci_info_ret->func                  = nci_info_self->func;
316        nci_info_ret->fb_info               = nci_info_self->fb_info;
317        nci_info_ret->orig_func             = nci_info_self->orig_func;
318        nci_info_ret->signature             = nci_info_self->signature;
319        nci_info_ret->pcc_params_signature  = nci_info_self->pcc_params_signature;
320        nci_info_ret->pcc_return_signature  = nci_info_self->pcc_params_signature;
321        nci_info_ret->arity                 = nci_info_self->arity;
322        PObj_get_FLAGS(ret)                 = PObj_get_FLAGS(SELF);
323
324        return ret;
325    }
326
327/*
328
329=item C<INTVAL defined()>
330
331Returns whether the NCI is defined.
332
333=cut
334
335*/
336
337    VTABLE INTVAL defined() :no_wb {
338        Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);
339        return nci_info->orig_func != NULL;
340    }
341
342/*
343
344=item C<opcode_t *invoke(void *next)>
345
346Calls the associated C function, returning C<*next>. If the invocant is a
347class, the PMC arguments are shifted down.
348
349=cut
350
351*/
352
353    VTABLE opcode_t *invoke(void *next) {
354        Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);
355        nci_thunk_t                   func;
356        PMC                          *fb_info;
357        char                         *sig_str;
358        void                         *orig_func;
359        PMC                          *cont;
360
361        GET_ATTR_orig_func(INTERP, SELF, orig_func);
362        func = (nci_thunk_t)D2FPTR(nci_info->func);
363
364        GET_ATTR_fb_info(INTERP, SELF, fb_info);
365
366        if (!func) {
367            /* build the thunk only when necessary */
368            func = build_func(interp, nci_info);
369
370            if (!func)
371                Parrot_ex_throw_from_c_noargs(INTERP,
372                    EXCEPTION_INVALID_OPERATION,
373                    "attempt to call NULL function");
374        }
375
376        func(INTERP, SELF, fb_info);
377        cont = INTERP->current_cont;
378
379        /*
380         * If the NCI function was tailcalled, the return result
381         * is already passed back to the caller of this frame
382         * - see  Parrot_init_ret_nci(). We therefore invoke the
383         * return continuation here, which gets rid of this frame
384         * and returns the real return address
385         */
386        if (cont && cont != NEED_CONTINUATION
387        && (PObj_get_FLAGS(cont) & SUB_FLAG_TAILCALL)) {
388            cont = Parrot_pcc_get_continuation(interp, CURRENT_CONTEXT(interp));
389            next = VTABLE_invoke(INTERP, cont, next);
390        }
391
392        return (opcode_t *)next;
393    }
394
395/*
396
397=item C<INTVAL get_integer()>
398
399Returns the function pointer as an integer.
400
401=cut
402
403*/
404
405    VTABLE INTVAL get_integer() :no_wb {
406        Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);
407        if (!nci_info->func)
408            build_func(INTERP, nci_info);
409        return (INTVAL)nci_info->func;
410    }
411
412/*
413
414=item C<INTVAL get_bool()>
415
416Returns the boolean value of the pointer.
417
418=cut
419
420*/
421
422    VTABLE INTVAL get_bool() :no_wb {
423        Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);
424        return (0 != (INTVAL)nci_info->orig_func);
425    }
426
427/*
428
429=item C<METHOD arity()>
430
431Return the arity of the NCI (the number of arguments).
432
433=cut
434
435*/
436    METHOD arity() :no_wb {
437        Parrot_NCI_attributes * const nci_info = PARROT_NCI(SELF);
438        INTVAL arity = 0;
439
440        if (nci_info) {
441            if (!nci_info->func)
442                build_func(INTERP, nci_info);
443            if (nci_info->func) {
444                arity = nci_info->arity;
445                RETURN(INTVAL arity);
446            }
447        }
448
449        Parrot_ex_throw_from_c_noargs(INTERP,
450            EXCEPTION_INVALID_OPERATION,
451            "You cannot get the arity of an undefined NCI.");
452    }
453}
454
455/*
456
457=back
458
459=head1 SEE ALSO
460
461F<docs/pdds/pdd03_calling_conventions.pod>.
462
463=head1 HISTORY
464
465Initial revision by sean 2002/08/04.
466
467=cut
468
469*/
470
471/*
472 * Local variables:
473 *   c-file-style: "parrot"
474 * End:
475 * vim: expandtab shiftwidth=4:
476 */
477