static void * unmarshal_callback(MVMThreadContext *tc, MVMObject *callback, MVMObject *sig_info) { MVMNativeCallbackCacheHead *callback_data_head = NULL; MVMNativeCallback **callback_data_handle; MVMString *cuid; if (!IS_CONCRETE(callback)) return NULL; /* Try to locate existing cached callback info. */ callback = MVM_frame_find_invokee(tc, callback, NULL); cuid = ((MVMCode *)callback)->body.sf->body.cuuid; MVM_string_flatten(tc, cuid); MVM_HASH_GET(tc, tc->native_callback_cache, cuid, callback_data_head); if (!callback_data_head) { callback_data_head = MVM_malloc(sizeof(MVMNativeCallbackCacheHead)); callback_data_head->head = NULL; MVM_HASH_BIND(tc, tc->native_callback_cache, cuid, callback_data_head); } callback_data_handle = &(callback_data_head->head); while (*callback_data_handle) { if ((*callback_data_handle)->target == callback) /* found it, break */ break; callback_data_handle = &((*callback_data_handle)->next); } if (!*callback_data_handle) { /* First, build the MVMNativeCallback */ MVMCallsite *cs; char *signature; MVMObject *typehash; MVMint64 num_info, i; MVMNativeCallback *callback_data; num_info = MVM_repr_elems(tc, sig_info); callback_data = MVM_malloc(sizeof(MVMNativeCallback)); callback_data->num_types = num_info; callback_data->typeinfos = MVM_malloc(num_info * sizeof(MVMint16)); callback_data->types = MVM_malloc(num_info * sizeof(MVMObject *)); callback_data->next = NULL; /* A dyncall signature looks like this: xxx)x * Argument types before the ) and return type after it. Thus, * num_info+1 must be NULL (zero-terminated string) and num_info-1 * must be the ). */ signature = MVM_malloc(num_info + 2); signature[num_info + 1] = '\0'; signature[num_info - 1] = ')'; /* We'll also build up a MoarVM callsite as we go. */ cs = MVM_malloc(sizeof(MVMCallsite)); cs->arg_flags = MVM_malloc(num_info * sizeof(MVMCallsiteEntry)); cs->arg_count = num_info - 1; cs->num_pos = num_info - 1; cs->has_flattening = 0; cs->is_interned = 0; cs->with_invocant = NULL; typehash = MVM_repr_at_pos_o(tc, sig_info, 0); callback_data->types[0] = MVM_repr_at_key_o(tc, typehash, tc->instance->str_consts.typeobj); callback_data->typeinfos[0] = MVM_nativecall_get_arg_type(tc, typehash, 1); signature[num_info] = get_signature_char(callback_data->typeinfos[0]); for (i = 1; i < num_info; i++) { typehash = MVM_repr_at_pos_o(tc, sig_info, i); callback_data->types[i] = MVM_repr_at_key_o(tc, typehash, tc->instance->str_consts.typeobj); callback_data->typeinfos[i] = MVM_nativecall_get_arg_type(tc, typehash, 0) & ~MVM_NATIVECALL_ARG_FREE_STR; signature[i - 1] = get_signature_char(callback_data->typeinfos[i]); switch (callback_data->typeinfos[i] & MVM_NATIVECALL_ARG_TYPE_MASK) { case MVM_NATIVECALL_ARG_CHAR: case MVM_NATIVECALL_ARG_SHORT: case MVM_NATIVECALL_ARG_INT: case MVM_NATIVECALL_ARG_LONG: case MVM_NATIVECALL_ARG_LONGLONG: cs->arg_flags[i - 1] = MVM_CALLSITE_ARG_INT; break; case MVM_NATIVECALL_ARG_UCHAR: case MVM_NATIVECALL_ARG_USHORT: case MVM_NATIVECALL_ARG_UINT: case MVM_NATIVECALL_ARG_ULONG: case MVM_NATIVECALL_ARG_ULONGLONG: /* TODO: should probably be UINT, when we can support that. */ cs->arg_flags[i - 1] = MVM_CALLSITE_ARG_INT; break; case MVM_NATIVECALL_ARG_FLOAT: case MVM_NATIVECALL_ARG_DOUBLE: cs->arg_flags[i - 1] = MVM_CALLSITE_ARG_NUM; break; default: cs->arg_flags[i - 1] = MVM_CALLSITE_ARG_OBJ; break; } } MVM_callsite_try_intern(tc, &cs); callback_data->tc = tc; callback_data->cs = cs; callback_data->target = callback; callback_data->cb = dcbNewCallback(signature, (DCCallbackHandler *)&callback_handler, callback_data); /* Now insert the MVMCallback into the linked list. */ *callback_data_handle = callback_data; MVM_free(signature); } return (*callback_data_handle)->cb; }
static void * unmarshal_callback(MVMThreadContext *tc, MVMObject *callback, MVMObject *sig_info) { MVMNativeCallbackCacheHead *callback_data_head = NULL; MVMNativeCallback **callback_data_handle; MVMString *cuid; if (!IS_CONCRETE(callback)) return NULL; /* Try to locate existing cached callback info. */ callback = MVM_frame_find_invokee(tc, callback, NULL); cuid = ((MVMCode *)callback)->body.sf->body.cuuid; MVM_HASH_GET(tc, tc->native_callback_cache, cuid, callback_data_head); if (!callback_data_head) { callback_data_head = MVM_malloc(sizeof(MVMNativeCallbackCacheHead)); callback_data_head->head = NULL; MVM_HASH_BIND(tc, tc->native_callback_cache, cuid, callback_data_head); } callback_data_handle = &(callback_data_head->head); while (*callback_data_handle) { if ((*callback_data_handle)->target == callback) /* found it, break */ break; callback_data_handle = &((*callback_data_handle)->next); } if (!*callback_data_handle) { /* First, build the MVMNativeCallback */ MVMCallsite *cs; MVMObject *typehash; MVMint64 num_info, i; MVMNativeCallback *callback_data; /* cb is a piece of executable memory we obtain from libffi. */ void *cb; ffi_cif *cif; ffi_closure *closure; ffi_status status; num_info = MVM_repr_elems(tc, sig_info); /* We'll also build up a MoarVM callsite as we go. */ cs = MVM_calloc(1, sizeof(MVMCallsite)); cs->flag_count = num_info - 1; cs->arg_flags = MVM_malloc(cs->flag_count * sizeof(MVMCallsiteEntry)); cs->arg_count = num_info - 1; cs->num_pos = num_info - 1; cs->has_flattening = 0; cs->is_interned = 0; cs->with_invocant = NULL; callback_data = MVM_malloc(sizeof(MVMNativeCallback)); callback_data->num_types = num_info; callback_data->typeinfos = MVM_malloc(num_info * sizeof(MVMint16)); callback_data->types = MVM_malloc(num_info * sizeof(MVMObject *)); callback_data->next = NULL; cif = (ffi_cif *)MVM_malloc(sizeof(ffi_cif)); callback_data->convention = FFI_DEFAULT_ABI; callback_data->ffi_arg_types = MVM_malloc(sizeof(ffi_type *) * (cs->arg_count ? cs->arg_count : 1)); /* Collect information about the return type. */ typehash = MVM_repr_at_pos_o(tc, sig_info, 0); callback_data->types[0] = MVM_repr_at_key_o(tc, typehash, tc->instance->str_consts.typeobj); callback_data->typeinfos[0] = MVM_nativecall_get_arg_type(tc, typehash, 1); callback_data->ffi_ret_type = MVM_nativecall_get_ffi_type(tc, callback_data->typeinfos[0]); for (i = 1; i < num_info; i++) { typehash = MVM_repr_at_pos_o(tc, sig_info, i); callback_data->types[i] = MVM_repr_at_key_o(tc, typehash, tc->instance->str_consts.typeobj); callback_data->typeinfos[i] = MVM_nativecall_get_arg_type(tc, typehash, 0) & ~MVM_NATIVECALL_ARG_FREE_STR; callback_data->ffi_arg_types[i - 1] = MVM_nativecall_get_ffi_type(tc, callback_data->typeinfos[i]); switch (callback_data->typeinfos[i] & MVM_NATIVECALL_ARG_TYPE_MASK) { case MVM_NATIVECALL_ARG_CHAR: case MVM_NATIVECALL_ARG_SHORT: case MVM_NATIVECALL_ARG_INT: case MVM_NATIVECALL_ARG_LONG: case MVM_NATIVECALL_ARG_LONGLONG: cs->arg_flags[i - 1] = MVM_CALLSITE_ARG_INT; break; case MVM_NATIVECALL_ARG_UCHAR: case MVM_NATIVECALL_ARG_USHORT: case MVM_NATIVECALL_ARG_UINT: case MVM_NATIVECALL_ARG_ULONG: case MVM_NATIVECALL_ARG_ULONGLONG: /* TODO: should probably be UINT, when we can support that. */ cs->arg_flags[i - 1] = MVM_CALLSITE_ARG_INT; break; case MVM_NATIVECALL_ARG_FLOAT: case MVM_NATIVECALL_ARG_DOUBLE: cs->arg_flags[i - 1] = MVM_CALLSITE_ARG_NUM; break; default: cs->arg_flags[i - 1] = MVM_CALLSITE_ARG_OBJ; break; } } MVM_callsite_try_intern(tc, &cs); callback_data->instance = tc->instance; callback_data->cs = cs; callback_data->target = callback; status = ffi_prep_cif(cif, callback_data->convention, (unsigned int)cs->arg_count, callback_data->ffi_ret_type, callback_data->ffi_arg_types); closure = ffi_closure_alloc(sizeof(ffi_closure), &cb); if (!closure) MVM_panic(1, "Unable to allocate memory for callback closure"); ffi_prep_closure_loc(closure, cif, callback_handler, callback_data, cb); callback_data->cb = cb; /* Now insert the MVMCallback into the linked list. */ *callback_data_handle = callback_data; } return (*callback_data_handle)->cb; }