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