/* Locates a method by name. Returns the method if it exists, or throws an * exception if it can not be found. */ void MVM_6model_find_method(MVMThreadContext *tc, MVMObject *obj, MVMString *name, MVMRegister *res) { MVMObject *cache, *HOW, *find_method, *code; if (MVM_is_null(tc, obj)) MVM_exception_throw_adhoc(tc, "Cannot call method '%s' on a null object", MVM_string_utf8_encode_C_string(tc, name)); /* First try to find it in the cache. If we find it, we have a result. * If we don't find it, but the cache is authoritative, then error. */ cache = STABLE(obj)->method_cache; if (cache && IS_CONCRETE(cache)) { MVMObject *meth = MVM_repr_at_key_o(tc, cache, name); if (!MVM_is_null(tc, meth)) { res->o = meth; return; } if (STABLE(obj)->mode_flags & MVM_METHOD_CACHE_AUTHORITATIVE) { MVMObject *handler = MVM_hll_current(tc)->method_not_found_error; if (!MVM_is_null(tc, handler)) { handler = MVM_frame_find_invokee(tc, handler, NULL); MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, &mnfe_callsite); tc->cur_frame->args[0].o = obj; tc->cur_frame->args[1].s = name; STABLE(handler)->invoke(tc, handler, &mnfe_callsite, tc->cur_frame->args); return; } else { MVM_exception_throw_adhoc(tc, "Cannot find method '%s'", MVM_string_utf8_encode_C_string(tc, name)); } } } /* Otherwise, need to call the find_method method. We make the assumption * that the invocant's meta-object's type is composed. */ HOW = STABLE(obj)->HOW; find_method = MVM_6model_find_method_cache_only(tc, HOW, tc->instance->str_consts.find_method); if (MVM_is_null(tc, find_method)) MVM_exception_throw_adhoc(tc, "Cannot find method '%s': no method cache and no .^find_method", MVM_string_utf8_encode_C_string(tc, name)); /* Set up the call, using the result register as the target. */ code = MVM_frame_find_invokee(tc, find_method, NULL); MVM_args_setup_thunk(tc, res, MVM_RETURN_OBJ, &fm_callsite); tc->cur_frame->args[0].o = HOW; tc->cur_frame->args[1].o = obj; tc->cur_frame->args[2].s = name; STABLE(code)->invoke(tc, code, &fm_callsite, tc->cur_frame->args); }
void MVM_6model_can_method(MVMThreadContext *tc, MVMObject *obj, MVMString *name, MVMRegister *res) { MVMObject *HOW, *find_method, *code; MVMCallsite *findmeth_callsite; MVMint64 can_cached = MVM_6model_can_method_cache_only(tc, obj, name); if (can_cached == 0 || can_cached == 1) { res->i64 = can_cached; return; } /* If no method in cache and the cache is not authoritative, need to make * a late-bound call to find_method. */ HOW = MVM_6model_get_how(tc, STABLE(obj)); find_method = MVM_6model_find_method_cache_only(tc, HOW, tc->instance->str_consts.find_method); if (MVM_is_null(tc, find_method)) { /* This'll count as a "no"... */ res->i64 = 0; return; } /* Set up the call, using the result register as the target. A little bad * as we're really talking about */ code = MVM_frame_find_invokee(tc, find_method, NULL); findmeth_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_FIND_METHOD); MVM_args_setup_thunk(tc, res, MVM_RETURN_OBJ, findmeth_callsite); tc->cur_frame->special_return = late_bound_can_return; tc->cur_frame->special_return_data = res; tc->cur_frame->args[0].o = HOW; tc->cur_frame->args[1].o = obj; tc->cur_frame->args[2].s = name; STABLE(code)->invoke(tc, code, findmeth_callsite, tc->cur_frame->args); }
static void code_pair_fetch_internal(MVMThreadContext *tc, MVMObject *cont, MVMRegister *res, MVMReturnType res_type) { CodePairContData *data = (CodePairContData *)STABLE(cont)->container_data; MVMObject *code = MVM_frame_find_invokee(tc, data->fetch_code, NULL); MVMCallsite *inv_arg_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG); MVM_args_setup_thunk(tc, res, res_type, inv_arg_callsite); tc->cur_frame->args[0].o = cont; STABLE(code)->invoke(tc, code, inv_arg_callsite, tc->cur_frame->args); }
static void code_pair_store_internal(MVMThreadContext *tc, MVMObject *cont, MVMRegister value, MVMCallsite *cs) { CodePairContData *data = (CodePairContData *)STABLE(cont)->container_data; MVMObject *code = MVM_frame_find_invokee(tc, data->store_code, NULL); MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, cs); tc->cur_frame->args[0].o = cont; tc->cur_frame->args[1] = value; STABLE(code)->invoke(tc, code, cs, tc->cur_frame->args); }
static void code_pair_fetch(MVMThreadContext *tc, MVMObject *cont, MVMRegister *res) { CodePairContData *data = (CodePairContData *)STABLE(cont)->container_data; MVMObject *code = MVM_frame_find_invokee(tc, data->fetch_code, NULL); MVM_args_setup_thunk(tc, res, MVM_RETURN_OBJ, &fetch_arg_callsite); tc->cur_frame->args[0].o = cont; STABLE(code)->invoke(tc, code, &fetch_arg_callsite, tc->cur_frame->args); }
static void rakudo_scalar_store(MVMThreadContext *tc, MVMObject *cont, MVMObject *value) { RakudoContData *data = (RakudoContData *)STABLE(cont)->container_data; MVMObject *code = MVM_frame_find_invokee(tc, data->store, NULL); MVMCallsite *cs = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TWO_OBJ); MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, cs); tc->cur_frame->args[0].o = cont; tc->cur_frame->args[1].o = value; STABLE(code)->invoke(tc, code, cs, tc->cur_frame->args); }
static void code_pair_store(MVMThreadContext *tc, MVMObject *cont, MVMObject *obj) { CodePairContData *data = (CodePairContData *)STABLE(cont)->container_data; MVMObject *code = MVM_frame_find_invokee(tc, data->store_code, NULL); MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, &store_arg_callsite); tc->cur_frame->args[0].o = cont; tc->cur_frame->args[1].o = obj; STABLE(code)->invoke(tc, code, &store_arg_callsite, tc->cur_frame->args); }
static void code_pair_fetch(MVMThreadContext *tc, MVMObject *cont, MVMRegister *res) { CodePairContData *data = (CodePairContData *)STABLE(cont)->container_data; MVMObject *code = MVM_frame_find_invokee(tc, data->fetch_code); tc->cur_frame->return_value = res; tc->cur_frame->return_type = MVM_RETURN_OBJ; tc->cur_frame->return_address = *(tc->interp_cur_op); tc->cur_frame->args[0].o = cont; STABLE(code)->invoke(tc, code, &fetch_arg_callsite, tc->cur_frame->args); }
static void run_handler(MVMThreadContext *tc, LocatedHandler lh, MVMObject *ex_obj, MVMuint32 category) { switch (lh.handler->action) { case MVM_EX_ACTION_GOTO: if (lh.jit_handler) { void **labels = lh.frame->spesh_cand->jitcode->labels; MVMuint8 *pc = lh.frame->spesh_cand->jitcode->bytecode; lh.frame->jit_entry_label = labels[lh.jit_handler->goto_label]; MVM_frame_unwind_to(tc, lh.frame, pc, 0, NULL); } else { MVM_frame_unwind_to(tc, lh.frame, NULL, lh.handler->goto_offset, NULL); } break; case MVM_EX_ACTION_INVOKE: { /* Create active handler record. */ MVMActiveHandler *ah = MVM_malloc(sizeof(MVMActiveHandler)); MVMFrame *cur_frame = tc->cur_frame; MVMObject *handler_code; /* Ensure we have an exception object. */ if (ex_obj == NULL) { ex_obj = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTException); ((MVMException *)ex_obj)->body.category = category; } /* Find frame to invoke. */ handler_code = MVM_frame_find_invokee(tc, lh.frame->work[lh.handler->block_reg].o, NULL); /* Install active handler record. */ ah->frame = MVM_frame_inc_ref(tc, lh.frame); ah->handler = lh.handler; ah->jit_handler = lh.jit_handler; ah->ex_obj = ex_obj; ah->next_handler = tc->active_handlers; tc->active_handlers = ah; /* Set up special return to unwinding after running the * handler. */ cur_frame->return_value = (MVMRegister *)&tc->last_handler_result; cur_frame->return_type = MVM_RETURN_OBJ; cur_frame->special_return = unwind_after_handler; cur_frame->special_unwind = cleanup_active_handler; cur_frame->special_return_data = ah; /* Invoke the handler frame and return to runloop. */ STABLE(handler_code)->invoke(tc, handler_code, MVM_callsite_get_common(tc, MVM_CALLSITE_ID_NULL_ARGS), cur_frame->args); break; } default: MVM_panic(1, "Unimplemented handler action"); } }
static void code_pair_store(MVMThreadContext *tc, MVMObject *cont, MVMObject *obj) { CodePairContData *data = (CodePairContData *)STABLE(cont)->container_data; MVMObject *code = MVM_frame_find_invokee(tc, data->store_code); tc->cur_frame->return_value = NULL; tc->cur_frame->return_type = MVM_RETURN_VOID; tc->cur_frame->return_address = *(tc->interp_cur_op); tc->cur_frame->args[0].o = cont; tc->cur_frame->args[1].o = obj; STABLE(code)->invoke(tc, code, &store_arg_callsite, tc->cur_frame->args); }
void MVM_6model_find_method(MVMThreadContext *tc, MVMObject *obj, MVMString *name, MVMRegister *res) { MVMObject *cache, *HOW, *find_method, *code; MVMCallsite *findmeth_callsite; if (MVM_is_null(tc, obj)) MVM_exception_throw_adhoc(tc, "Cannot call method '%s' on a null object", MVM_string_utf8_encode_C_string(tc, name)); /* First try to find it in the cache. If we find it, we have a result. * If we don't find it, but the cache is authoritative, then error. */ cache = get_method_cache(tc, STABLE(obj)); if (cache && IS_CONCRETE(cache)) { MVMObject *meth = MVM_repr_at_key_o(tc, cache, name); if (!MVM_is_null(tc, meth)) { res->o = meth; return; } if (STABLE(obj)->mode_flags & MVM_METHOD_CACHE_AUTHORITATIVE) { die_over_missing_method(tc, obj, name); return; } } /* Otherwise, need to call the find_method method. We make the assumption * that the invocant's meta-object's type is composed. */ HOW = MVM_6model_get_how(tc, STABLE(obj)); find_method = MVM_6model_find_method_cache_only(tc, HOW, tc->instance->str_consts.find_method); if (MVM_is_null(tc, find_method)) MVM_exception_throw_adhoc(tc, "Cannot find method '%s': no method cache and no .^find_method", MVM_string_utf8_encode_C_string(tc, name)); /* Set up the call, using the result register as the target. */ code = MVM_frame_find_invokee(tc, find_method, NULL); findmeth_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_FIND_METHOD); MVM_args_setup_thunk(tc, res, MVM_RETURN_OBJ, findmeth_callsite); { FindMethodSRData *fm = MVM_malloc(sizeof(FindMethodSRData)); fm->obj = obj; fm->name = name; fm->res = res; tc->cur_frame->special_return = late_bound_find_method_return; tc->cur_frame->special_return_data = fm; tc->cur_frame->mark_special_return_data = mark_find_method_sr_data; } tc->cur_frame->args[0].o = HOW; tc->cur_frame->args[1].o = obj; tc->cur_frame->args[2].s = name; STABLE(code)->invoke(tc, code, findmeth_callsite, tc->cur_frame->args); }
/* Sets the passed thread context's thread up so that we'll run a finalize * handler on it in the near future. */ static void finalize_handler_caller(MVMThreadContext *tc, void *sr_data) { MVMObject *handler = MVM_hll_current(tc)->finalize_handler; if (handler) { /* Drain the finalizing queue to an array. */ MVMObject *drain = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray); while (tc->num_finalizing > 0) MVM_repr_push_o(tc, drain, tc->finalizing[--tc->num_finalizing]); /* Invoke the handler. */ handler = MVM_frame_find_invokee(tc, handler, NULL); MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG)); tc->cur_frame->args[0].o = drain; STABLE(handler)->invoke(tc, handler, MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG), tc->cur_frame->args); } }
void MVM_frame_unwind_to(MVMThreadContext *tc, MVMFrame *frame, MVMuint8 *abs_addr, MVMuint32 rel_addr, MVMObject *return_value) { while (tc->cur_frame != frame) { if (tc->cur_frame->static_info->body.has_exit_handler && !(tc->cur_frame->flags & MVM_FRAME_FLAG_EXIT_HAND_RUN)) { /* We're unwinding a frame with an exit handler. Thus we need to * pause the unwind, run the exit handler, and keep enough info * around in order to finish up the unwind afterwards. */ MVMFrame *caller = tc->cur_frame->caller; MVMHLLConfig *hll = MVM_hll_current(tc); MVMObject *handler; if (!caller) MVM_exception_throw_adhoc(tc, "Entry point frame cannot have an exit handler"); if (tc->cur_frame == tc->thread_entry_frame) MVM_exception_throw_adhoc(tc, "Thread entry point frame cannot have an exit handler"); MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, &exit_arg_callsite); tc->cur_frame->args[0].o = tc->cur_frame->code_ref; tc->cur_frame->args[1].o = NULL; tc->cur_frame->special_return = continue_unwind; { MVMUnwindData *ud = malloc(sizeof(MVMUnwindData)); ud->frame = frame; ud->abs_addr = abs_addr; ud->rel_addr = rel_addr; if (return_value) MVM_exception_throw_adhoc(tc, "return_value + exit_handler case NYI"); tc->cur_frame->special_return_data = ud; } tc->cur_frame->flags |= MVM_FRAME_FLAG_EXIT_HAND_RUN; handler = MVM_frame_find_invokee(tc, hll->exit_handler, NULL); STABLE(handler)->invoke(tc, handler, &exit_arg_callsite, tc->cur_frame->args); return; } else { /* No exit handler, so just remove the frame. */ if (!remove_one_frame(tc, 1)) MVM_panic(1, "Internal error: Unwound entire stack and missed handler"); } } if (abs_addr) *tc->interp_cur_op = abs_addr; else if (rel_addr) *tc->interp_cur_op = *tc->interp_bytecode_start + rel_addr; if (return_value) MVM_args_set_result_obj(tc, return_value, 1); }
static void die_over_missing_method(MVMThreadContext *tc, MVMObject *obj, MVMString *name) { MVMObject *handler = MVM_hll_current(tc)->method_not_found_error; if (!MVM_is_null(tc, handler)) { handler = MVM_frame_find_invokee(tc, handler, NULL); MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, &mnfe_callsite); tc->cur_frame->args[0].o = obj; tc->cur_frame->args[1].s = name; STABLE(handler)->invoke(tc, handler, &mnfe_callsite, tc->cur_frame->args); return; } else { MVM_exception_throw_adhoc(tc, "Cannot find method '%s'", MVM_string_utf8_encode_C_string(tc, name)); } }
MVMuint64 MVM_frame_try_return(MVMThreadContext *tc) { if (tc->cur_frame->static_info->body.has_exit_handler && !(tc->cur_frame->flags & MVM_FRAME_FLAG_EXIT_HAND_RUN)) { /* Set us up to run exit handler, and make it so we'll really exit the * frame when that has been done. */ MVMFrame *caller = tc->cur_frame->caller; MVMHLLConfig *hll = MVM_hll_current(tc); MVMObject *handler; MVMObject *result; if (!caller) MVM_exception_throw_adhoc(tc, "Entry point frame cannot have an exit handler"); if (tc->cur_frame == tc->thread_entry_frame) MVM_exception_throw_adhoc(tc, "Thread entry point frame cannot have an exit handler"); switch (caller->return_type) { case MVM_RETURN_OBJ: result = caller->return_value->o; break; case MVM_RETURN_INT: result = MVM_repr_box_int(tc, hll->int_box_type, caller->return_value->i64); break; case MVM_RETURN_NUM: result = MVM_repr_box_num(tc, hll->num_box_type, caller->return_value->n64); break; case MVM_RETURN_STR: result = MVM_repr_box_str(tc, hll->str_box_type, caller->return_value->s); break; default: result = NULL; } MVM_args_setup_thunk(tc, NULL, MVM_RETURN_VOID, &exit_arg_callsite); tc->cur_frame->args[0].o = tc->cur_frame->code_ref; tc->cur_frame->args[1].o = result; tc->cur_frame->special_return = remove_after_handler; tc->cur_frame->flags |= MVM_FRAME_FLAG_EXIT_HAND_RUN; handler = MVM_frame_find_invokee(tc, hll->exit_handler, NULL); STABLE(handler)->invoke(tc, handler, &exit_arg_callsite, tc->cur_frame->args); return 1; } else { /* No exit handler, so a straight return. */ return remove_one_frame(tc, 0); } }
void MVM_coerce_smart_stringify(MVMThreadContext *tc, MVMObject *obj, MVMRegister *res_reg) { MVMObject *strmeth; const MVMStorageSpec *ss; /* Handle null case. */ if (MVM_is_null(tc, obj)) { res_reg->s = tc->instance->str_consts.empty; return; } /* If it can unbox as a string, that wins right off. */ ss = REPR(obj)->get_storage_spec(tc, STABLE(obj)); if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR && IS_CONCRETE(obj)) { res_reg->s = REPR(obj)->box_funcs.get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj)); return; } /* Check if there is a Str method. */ strmeth = MVM_6model_find_method_cache_only(tc, obj, tc->instance->str_consts.Str); if (!MVM_is_null(tc, strmeth)) { /* We need to do the invocation; just set it up with our result reg as * the one for the call. */ MVMObject *code = MVM_frame_find_invokee(tc, strmeth, NULL); MVMCallsite *inv_arg_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG); MVM_args_setup_thunk(tc, res_reg, MVM_RETURN_STR, inv_arg_callsite); tc->cur_frame->args[0].o = obj; STABLE(code)->invoke(tc, code, inv_arg_callsite, tc->cur_frame->args); return; } /* Otherwise, guess something appropriate. */ if (!IS_CONCRETE(obj)) res_reg->s = tc->instance->str_consts.empty; else { if (REPR(obj)->ID == MVM_REPR_ID_MVMException) res_reg->s = ((MVMException *)obj)->body.message; else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT) res_reg->s = MVM_coerce_i_s(tc, REPR(obj)->box_funcs.get_int(tc, STABLE(obj), obj, OBJECT_BODY(obj))); else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_NUM) res_reg->s = MVM_coerce_n_s(tc, REPR(obj)->box_funcs.get_num(tc, STABLE(obj), obj, OBJECT_BODY(obj))); else MVM_exception_throw_adhoc(tc, "cannot stringify this"); } }
void MVM_coerce_smart_numify(MVMThreadContext *tc, MVMObject *obj, MVMRegister *res_reg) { MVMObject *nummeth; /* Handle null case. */ if (MVM_is_null(tc, obj)) { res_reg->n64 = 0.0; return; } /* Check if there is a Num method. */ nummeth = MVM_6model_find_method_cache_only(tc, obj, tc->instance->str_consts.Num); if (!MVM_is_null(tc, nummeth)) { /* We need to do the invocation; just set it up with our result reg as * the one for the call. */ MVMObject *code = MVM_frame_find_invokee(tc, nummeth, NULL); MVMCallsite *inv_arg_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG); MVM_args_setup_thunk(tc, res_reg, MVM_RETURN_NUM, inv_arg_callsite); tc->cur_frame->args[0].o = obj; STABLE(code)->invoke(tc, code, inv_arg_callsite, tc->cur_frame->args); return; } /* Otherwise, guess something appropriate. */ if (!IS_CONCRETE(obj)) { res_reg->n64 = 0.0; } else { const MVMStorageSpec *ss = REPR(obj)->get_storage_spec(tc, STABLE(obj)); if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_INT) res_reg->n64 = (MVMnum64)REPR(obj)->box_funcs.get_int(tc, STABLE(obj), obj, OBJECT_BODY(obj)); else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_NUM) res_reg->n64 = REPR(obj)->box_funcs.get_num(tc, STABLE(obj), obj, OBJECT_BODY(obj)); else if (ss->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) res_reg->n64 = MVM_coerce_s_n(tc, REPR(obj)->box_funcs.get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj))); else if (REPR(obj)->ID == MVM_REPR_ID_MVMArray) res_reg->n64 = (MVMnum64)REPR(obj)->elems(tc, STABLE(obj), obj, OBJECT_BODY(obj)); else if (REPR(obj)->ID == MVM_REPR_ID_MVMHash) res_reg->n64 = (MVMnum64)REPR(obj)->elems(tc, STABLE(obj), obj, OBJECT_BODY(obj)); else MVM_exception_throw_adhoc(tc, "cannot numify this"); } }
void MVM_6model_can_method(MVMThreadContext *tc, MVMObject *obj, MVMString *name, MVMRegister *res) { MVMObject *cache, *HOW, *find_method, *code; if (!obj) MVM_exception_throw_adhoc(tc, "Cannot look for method '%s' on a null object", MVM_string_utf8_encode_C_string(tc, name)); /* First consider method cache. */ cache = STABLE(obj)->method_cache; if (cache && IS_CONCRETE(cache)) { MVMObject *meth = MVM_repr_at_key_o(tc, cache, name); if (meth) { res->i64 = 1; return; } if (STABLE(obj)->mode_flags & MVM_METHOD_CACHE_AUTHORITATIVE) { res->i64 = 0; return; } } /* If no method in cache and the cache is not authoritative, need to make * a late-bound call to find_method. */ HOW = STABLE(obj)->HOW; find_method = MVM_6model_find_method_cache_only(tc, HOW, tc->instance->str_consts.find_method); if (find_method == NULL) { /* This'll count as a "no"... */ res->i64 = 0; return; } /* Set up the call, using the result register as the target. A little bad * as we're really talking about */ code = MVM_frame_find_invokee(tc, find_method, NULL); MVM_args_setup_thunk(tc, res, MVM_RETURN_OBJ, &fm_callsite); tc->cur_frame->special_return = late_bound_can_return; tc->cur_frame->special_return_data = res; tc->cur_frame->args[0].o = HOW; tc->cur_frame->args[1].o = obj; tc->cur_frame->args[2].s = name; STABLE(code)->invoke(tc, code, &fm_callsite, tc->cur_frame->args); }
/* Checks if an object has a given type, delegating to the type_check or * accepts_type methods as needed. */ static void do_accepts_type_check(MVMThreadContext *tc, MVMObject *obj, MVMObject *type, MVMRegister *res) { MVMObject *HOW = MVM_6model_get_how(tc, STABLE(type)); MVMObject *meth = MVM_6model_find_method_cache_only(tc, HOW, tc->instance->str_consts.accepts_type); if (!MVM_is_null(tc, meth)) { /* Set up the call, using the result register as the target. */ MVMObject *code = MVM_frame_find_invokee(tc, meth, NULL); MVM_args_setup_thunk(tc, res, MVM_RETURN_INT, &tc_callsite); tc->cur_frame->args[0].o = HOW; tc->cur_frame->args[1].o = type; tc->cur_frame->args[2].o = obj; STABLE(code)->invoke(tc, code, &tc_callsite, tc->cur_frame->args); return; } else { MVM_exception_throw_adhoc(tc, "Expected 'accepts_type' method, but none found in meta-object"); } }
static void run_handler(MVMThreadContext *tc, LocatedHandler lh, MVMObject *ex_obj) { switch (lh.handler->action) { case MVM_EX_ACTION_GOTO: unwind_to_frame(tc, lh.frame); *tc->interp_cur_op = *tc->interp_bytecode_start + lh.handler->goto_offset; break; case MVM_EX_ACTION_INVOKE: { /* Create active handler record. */ MVMActiveHandler *ah = malloc(sizeof(MVMActiveHandler)); /* Find frame to invoke. */ MVMObject *handler_code = MVM_frame_find_invokee(tc, lh.frame->work[lh.handler->block_reg].o); /* Ensure we have an exception object. */ /* TODO: Can make one up. */ if (ex_obj == NULL) MVM_panic(1, "Exception object creation NYI"); /* Install active handler record. */ ah->frame = lh.frame; ah->handler = lh.handler; ah->ex_obj = ex_obj; ah->next_handler = tc->active_handlers; tc->active_handlers = ah; /* Set up special return to unwinding after running the * handler. */ tc->cur_frame->return_value = NULL; tc->cur_frame->return_type = MVM_RETURN_VOID; tc->cur_frame->special_return = unwind_after_handler; tc->cur_frame->special_return_data = ah; /* Inovke the handler frame and return to runloop. */ STABLE(handler_code)->invoke(tc, handler_code, &no_arg_callsite, tc->cur_frame->args); break; } default: MVM_panic(1, "Unimplemented handler action"); } }
void MVM_coerce_smart_stringify(MVMThreadContext *tc, MVMObject *obj, MVMRegister *res_reg) { MVMObject *strmeth; /* Handle null case. */ if (!obj) { res_reg->s = tc->instance->str_consts->empty; return; } /* Check if there is a Str method. */ strmeth = MVM_6model_find_method_cache_only(tc, obj, tc->instance->str_consts->Str); if (strmeth) { /* We need to do the invocation; just set it up with our result reg as * the one for the call. */ MVMObject *code = MVM_frame_find_invokee(tc, strmeth); tc->cur_frame->return_value = res_reg; tc->cur_frame->return_type = MVM_RETURN_STR; tc->cur_frame->return_address = *(tc->interp_cur_op); tc->cur_frame->args[0].o = obj; STABLE(code)->invoke(tc, code, get_inv_callsite(), tc->cur_frame->args); return; } /* Otherwise, guess something appropriate. */ if (!IS_CONCRETE(obj)) res_reg->s = tc->instance->str_consts->empty; else { MVMStorageSpec ss = REPR(obj)->get_storage_spec(tc, STABLE(obj)); if (REPR(obj)->ID == MVM_REPR_ID_MVMString) res_reg->s = (MVMString *)obj; else if (ss.can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) res_reg->s = REPR(obj)->box_funcs->get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj)); else if (ss.can_box & MVM_STORAGE_SPEC_CAN_BOX_INT) res_reg->s = MVM_coerce_i_s(tc, REPR(obj)->box_funcs->get_int(tc, STABLE(obj), obj, OBJECT_BODY(obj))); else if (ss.can_box & MVM_STORAGE_SPEC_CAN_BOX_NUM) res_reg->s = MVM_coerce_n_s(tc, REPR(obj)->box_funcs->get_num(tc, STABLE(obj), obj, OBJECT_BODY(obj))); else MVM_exception_throw_adhoc(tc, "cannot stringify this"); } }
void MVM_continuation_reset(MVMThreadContext *tc, MVMObject *tag, MVMObject *code, MVMRegister *res_reg) { /* Save the tag. */ MVMContinuationTag *tag_record = MVM_malloc(sizeof(MVMContinuationTag)); tag_record->tag = tag; tag_record->active_handlers = tc->active_handlers; tag_record->next = tc->cur_frame->continuation_tags; tc->cur_frame->continuation_tags = tag_record; /* Were we passed code or a continuation? */ if (REPR(code)->ID == MVM_REPR_ID_MVMContinuation) { /* Continuation; invoke it. */ MVM_continuation_invoke(tc, (MVMContinuation *)code, NULL, res_reg); } else { /* Run the passed code. */ MVMCallsite *null_args_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_NULL_ARGS); code = MVM_frame_find_invokee(tc, code, NULL); MVM_args_setup_thunk(tc, res_reg, MVM_RETURN_OBJ, null_args_callsite); tc->cur_frame->special_return = clear_tag; tc->cur_frame->special_return_data = tag_record; STABLE(code)->invoke(tc, code, null_args_callsite, tc->cur_frame->args); } }
void MVM_6model_istype(MVMThreadContext *tc, MVMObject *obj, MVMObject *type, MVMRegister *res) { MVMObject **cache; MVMSTable *st; MVMint64 mode; /* Null never type-checks. */ if (MVM_is_null(tc, obj)) { res->i64 = 0; return; } st = STABLE(obj); mode = STABLE(type)->mode_flags & MVM_TYPE_CHECK_CACHE_FLAG_MASK; cache = st->type_check_cache; if (cache) { /* We have the cache, so just look for the type object we * want to be in there. */ MVMint64 elems = STABLE(obj)->type_check_cache_length; MVMint64 i; for (i = 0; i < elems; i++) { if (cache[i] == type) { res->i64 = 1; return; } } /* If the type check cache is definitive, we're done. */ if ((mode & MVM_TYPE_CHECK_CACHE_THEN_METHOD) == 0 && (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) == 0) { res->i64 = 0; return; } } /* If we get here, need to call .^type_check on the value we're * checking, unless it's an accepts check. */ if (!cache || (mode & MVM_TYPE_CHECK_CACHE_THEN_METHOD)) { MVMObject *HOW = MVM_6model_get_how(tc, st); MVMObject *meth = MVM_6model_find_method_cache_only(tc, HOW, tc->instance->str_consts.type_check); if (!MVM_is_null(tc, meth)) { /* Set up the call, using the result register as the target. */ MVMObject *code = MVM_frame_find_invokee(tc, meth, NULL); MVMCallsite *typecheck_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_TYPECHECK); MVM_args_setup_thunk(tc, res, MVM_RETURN_INT, typecheck_callsite); tc->cur_frame->args[0].o = HOW; tc->cur_frame->args[1].o = obj; tc->cur_frame->args[2].o = type; if (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) { AcceptsTypeSRData *atd = MVM_malloc(sizeof(AcceptsTypeSRData)); atd->obj = obj; atd->type = type; atd->res = res; tc->cur_frame->special_return = accepts_type_sr; tc->cur_frame->special_return_data = atd; tc->cur_frame->mark_special_return_data = mark_sr_data; } STABLE(code)->invoke(tc, code, typecheck_callsite, tc->cur_frame->args); return; } } /* If the flag to call .accepts_type on the target value is set, do so. */ if (mode & MVM_TYPE_CHECK_NEEDS_ACCEPTS) { do_accepts_type_check(tc, obj, type, res); } else { /* If all else fails... */ res->i64 = 0; } }
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; }
void MVM_coerce_istrue(MVMThreadContext *tc, MVMObject *obj, MVMRegister *res_reg, MVMuint8 *true_addr, MVMuint8 *false_addr, MVMuint8 flip) { MVMint64 result; if (obj == NULL) { result = 0; } else { MVMBoolificationSpec *bs = obj->st->boolification_spec; switch (bs == NULL ? MVM_BOOL_MODE_NOT_TYPE_OBJECT : bs->mode) { case MVM_BOOL_MODE_CALL_METHOD: if (res_reg) { /* We need to do the invocation, and set this register * the result. Then we just do the call. For the flip * case, just set up special return handler to flip * the register. */ MVMObject *code = MVM_frame_find_invokee(tc, bs->method); tc->cur_frame->return_value = res_reg; tc->cur_frame->return_type = MVM_RETURN_INT; tc->cur_frame->return_address = *(tc->interp_cur_op); tc->cur_frame->args[0].o = obj; if (flip) { tc->cur_frame->special_return = flip_return; tc->cur_frame->special_return_data = res_reg; } STABLE(code)->invoke(tc, code, get_inv_callsite(), tc->cur_frame->args); } else { /* Need to set up special return hook. */ MVMObject *code = MVM_frame_find_invokee(tc, bs->method); BoolMethReturnData *data = malloc(sizeof(BoolMethReturnData)); data->true_addr = true_addr; data->false_addr = false_addr; data->flip = flip; tc->cur_frame->special_return = boolify_return; tc->cur_frame->special_return_data = data; tc->cur_frame->return_value = &data->res_reg; tc->cur_frame->return_type = MVM_RETURN_INT; tc->cur_frame->return_address = *(tc->interp_cur_op); tc->cur_frame->args[0].o = obj; STABLE(code)->invoke(tc, code, get_inv_callsite(), tc->cur_frame->args); return; } break; case MVM_BOOL_MODE_UNBOX_INT: result = !IS_CONCRETE(obj) || REPR(obj)->box_funcs->get_int(tc, STABLE(obj), obj, OBJECT_BODY(obj)) == 0 ? 0 : 1; break; case MVM_BOOL_MODE_UNBOX_NUM: result = !IS_CONCRETE(obj) || REPR(obj)->box_funcs->get_num(tc, STABLE(obj), obj, OBJECT_BODY(obj)) == 0.0 ? 0 : 1; break; case MVM_BOOL_MODE_UNBOX_STR_NOT_EMPTY: result = !IS_CONCRETE(obj) || NUM_GRAPHS(REPR(obj)->box_funcs->get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj))) == 0 ? 0 : 1; break; case MVM_BOOL_MODE_UNBOX_STR_NOT_EMPTY_OR_ZERO: { MVMString *str; if (!IS_CONCRETE(obj)) { result = 0; break; } str = REPR(obj)->box_funcs->get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj)); result = MVM_coerce_istrue_s(tc, str); break; } case MVM_BOOL_MODE_NOT_TYPE_OBJECT: result = !IS_CONCRETE(obj) ? 0 : 1; break; case MVM_BOOL_MODE_ITER: result = IS_CONCRETE(obj) ? MVM_iter_istrue(tc, (MVMIter *)obj) : 0; break; case MVM_BOOL_MODE_HAS_ELEMS: result = IS_CONCRETE(obj) ? MVM_repr_elems(tc, obj) != 0 : 0; break; default: MVM_exception_throw_adhoc(tc, "Invalid boolification spec mode used"); } } if (flip) result = result ? 0 : 1; if (res_reg) { res_reg->i64 = result; } else { if (result) *(tc->interp_cur_op) = true_addr; else *(tc->interp_cur_op) = false_addr; } }
void MVM_coerce_istrue(MVMThreadContext *tc, MVMObject *obj, MVMRegister *res_reg, MVMuint8 *true_addr, MVMuint8 *false_addr, MVMuint8 flip) { MVMint64 result = 0; if (!MVM_is_null(tc, obj)) { MVMBoolificationSpec *bs = obj->st->boolification_spec; switch (bs == NULL ? MVM_BOOL_MODE_NOT_TYPE_OBJECT : bs->mode) { case MVM_BOOL_MODE_CALL_METHOD: { MVMObject *code = MVM_frame_find_invokee(tc, bs->method, NULL); MVMCallsite *inv_arg_callsite = MVM_callsite_get_common(tc, MVM_CALLSITE_ID_INV_ARG); if (res_reg) { /* We need to do the invocation, and set this register * the result. Then we just do the call. For the flip * case, just set up special return handler to flip * the register. */ MVM_args_setup_thunk(tc, res_reg, MVM_RETURN_INT, inv_arg_callsite); tc->cur_frame->args[0].o = obj; if (flip) { tc->cur_frame->special_return = flip_return; tc->cur_frame->special_return_data = res_reg; } STABLE(code)->invoke(tc, code, inv_arg_callsite, tc->cur_frame->args); } else { /* Need to set up special return hook. */ BoolMethReturnData *data = MVM_malloc(sizeof(BoolMethReturnData)); data->true_addr = true_addr; data->false_addr = false_addr; data->flip = flip; tc->cur_frame->special_return = boolify_return; tc->cur_frame->special_return_data = data; MVM_args_setup_thunk(tc, &data->res_reg, MVM_RETURN_INT, inv_arg_callsite); tc->cur_frame->args[0].o = obj; STABLE(code)->invoke(tc, code, inv_arg_callsite, tc->cur_frame->args); } return; } case MVM_BOOL_MODE_UNBOX_INT: result = !IS_CONCRETE(obj) || REPR(obj)->box_funcs.get_int(tc, STABLE(obj), obj, OBJECT_BODY(obj)) == 0 ? 0 : 1; break; case MVM_BOOL_MODE_UNBOX_NUM: result = !IS_CONCRETE(obj) || REPR(obj)->box_funcs.get_num(tc, STABLE(obj), obj, OBJECT_BODY(obj)) == 0.0 ? 0 : 1; break; case MVM_BOOL_MODE_UNBOX_STR_NOT_EMPTY: { MVMString *str; if (!IS_CONCRETE(obj)) { result = 0; break; } str = REPR(obj)->box_funcs.get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj)); result = MVM_coerce_istrue_s(tc, str); break; } case MVM_BOOL_MODE_UNBOX_STR_NOT_EMPTY_OR_ZERO: { MVMString *str; MVMint64 chars; if (!IS_CONCRETE(obj)) { result = 0; break; } str = REPR(obj)->box_funcs.get_str(tc, STABLE(obj), obj, OBJECT_BODY(obj)); if (str == NULL || !IS_CONCRETE(str)) { result = 0; break; } chars = MVM_string_graphs(tc, str); result = chars == 0 || (chars == 1 && MVM_string_get_grapheme_at_nocheck(tc, str, 0) == 48) ? 0 : 1; break; } case MVM_BOOL_MODE_NOT_TYPE_OBJECT: result = !IS_CONCRETE(obj) ? 0 : 1; break; case MVM_BOOL_MODE_BIGINT: result = IS_CONCRETE(obj) ? MVM_bigint_bool(tc, obj) : 0; break; case MVM_BOOL_MODE_ITER: result = IS_CONCRETE(obj) ? MVM_iter_istrue(tc, (MVMIter *)obj) : 0; break; case MVM_BOOL_MODE_HAS_ELEMS: result = IS_CONCRETE(obj) ? MVM_repr_elems(tc, obj) != 0 : 0; break; default: MVM_exception_throw_adhoc(tc, "Invalid boolification spec mode used"); } } if (flip) result = result ? 0 : 1; if (res_reg) { res_reg->i64 = result; } else { if (result) *(tc->interp_cur_op) = true_addr; else *(tc->interp_cur_op) = false_addr; } }
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; }