/* Gets the flag for whether to free a string after a call or not. */ static MVMint16 get_str_free_flag(MVMThreadContext *tc, MVMObject *info) { MVMString *flag = tc->instance->str_consts.free_str; if (MVM_repr_exists_key(tc, info, flag)) if (!MVM_repr_get_int(tc, MVM_repr_at_key_o(tc, info, flag))) return MVM_NATIVECALL_ARG_NO_FREE_STR; return MVM_NATIVECALL_ARG_FREE_STR; }
/* Gets the flag for whether an arg is rw or not. */ static MVMint16 get_rw_flag(MVMThreadContext *tc, MVMObject *info) { MVMString *flag = tc->instance->str_consts.rw; if (MVM_repr_exists_key(tc, info, flag)) { if (MVM_repr_get_int(tc, MVM_repr_at_key_o(tc, info, flag))) return MVM_NATIVECALL_ARG_RW; } return MVM_NATIVECALL_ARG_NO_RW; }
static void code_pair_configure_container_spec(MVMThreadContext *tc, MVMSTable *st, MVMObject *config) { CodePairContData *data = (CodePairContData *)st->container_data; MVMROOT(tc, config, { MVMString *fetch = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "fetch"); MVMString *store; if (!MVM_repr_exists_key(tc, config, fetch)) MVM_exception_throw_adhoc(tc, "Container spec 'code_pair' must be configured with a fetch"); MVM_ASSIGN_REF(tc, &(st->header), data->fetch_code, MVM_repr_at_key_o(tc, config, fetch)); store = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "store"); if (!MVM_repr_exists_key(tc, config, store)) MVM_exception_throw_adhoc(tc, "Container spec 'code_pair' must be configured with a store"); MVM_ASSIGN_REF(tc, &(st->header), data->store_code, MVM_repr_at_key_o(tc, config, store)); });
/* 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_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); }
/* Helper for finding a slot number. */ static MVMint32 try_get_slot(MVMThreadContext *tc, MVMCStructREPRData *repr_data, MVMObject *class_key, MVMString *name) { if (repr_data->name_to_index_mapping) { MVMCStructNameMap *cur_map_entry = repr_data->name_to_index_mapping; while (cur_map_entry->class_key != NULL) { if (cur_map_entry->class_key == class_key) { MVMObject *slot_obj = MVM_repr_at_key_o(tc, cur_map_entry->name_map, name); if (IS_CONCRETE(slot_obj)) return MVM_repr_get_int(tc, slot_obj); break; } cur_map_entry++; } } return -1; }
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); }
/* Gets the flag for whether an arg is rw or not. */ static MVMint16 get_refresh_flag(MVMThreadContext *tc, MVMObject *info) { MVMString *typeobj_str = tc->instance->str_consts.typeobj; if (MVM_repr_exists_key(tc, info, typeobj_str)) { MVMObject *typeobj = MVM_repr_at_key_o(tc, info, typeobj_str); if (REPR(typeobj)->ID == MVM_REPR_ID_MVMCArray) { MVMCArrayREPRData *repr_data = (MVMCArrayREPRData *)STABLE(typeobj)->REPR_data; /* No need to refresh numbers. They're stored directly in the array. */ if (repr_data->elem_kind == MVM_CARRAY_ELEM_KIND_NUMERIC) return MVM_NATIVECALL_ARG_NO_REFRESH; return MVM_NATIVECALL_ARG_REFRESH; } } /* We don't know, so fail safe by assuming we have to refresh */ return MVM_NATIVECALL_ARG_REFRESH; }
MVMint64 MVM_6model_can_method_cache_only(MVMThreadContext *tc, MVMObject *obj, MVMString *name) { MVMObject *cache; if (MVM_is_null(tc, obj)) MVM_exception_throw_adhoc(tc, "Cannot look for method '%s' on a null object", MVM_string_utf8_encode_C_string(tc, name)); /* Consider the method cache. */ 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)) { return 1; } if (STABLE(obj)->mode_flags & MVM_METHOD_CACHE_AUTHORITATIVE) { return 0; } } return -1; }
/* Locates all of the attributes. Puts them onto a flattened, ordered * list of attributes (populating the passed flat_list). Also builds * the index mapping for doing named lookups. Note index is not related * to the storage position. */ static MVMObject * index_mapping_and_flat_list(MVMThreadContext *tc, MVMObject *mro, MVMCPPStructREPRData *repr_data) { MVMInstance *instance = tc->instance; MVMObject *flat_list, *class_list, *attr_map_list; MVMint32 num_classes, i, current_slot = 0; MVMCPPStructNameMap *result; MVMint32 mro_idx = MVM_repr_elems(tc, mro); MVM_gc_root_temp_push(tc, (MVMCollectable **)&mro); flat_list = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type); MVM_gc_root_temp_push(tc, (MVMCollectable **)&flat_list); class_list = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type); MVM_gc_root_temp_push(tc, (MVMCollectable **)&class_list); attr_map_list = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type); MVM_gc_root_temp_push(tc, (MVMCollectable **)&attr_map_list); /* Walk through the parents list. */ while (mro_idx) { /* Get current class in MRO. */ MVMObject *type_info = MVM_repr_at_pos_o(tc, mro, --mro_idx); MVMObject *current_class = MVM_repr_at_pos_o(tc, type_info, 0); /* Get its local parents; make sure we're not doing MI. */ MVMObject *parents = MVM_repr_at_pos_o(tc, type_info, 2); MVMint32 num_parents = MVM_repr_elems(tc, parents); if (num_parents <= 1) { /* Get attributes and iterate over them. */ MVMObject *attributes = MVM_repr_at_pos_o(tc, type_info, 1); MVMIter * const attr_iter = (MVMIter *)MVM_iter(tc, attributes); MVMObject *attr_map = NULL; if (MVM_iter_istrue(tc, attr_iter)) { MVM_gc_root_temp_push(tc, (MVMCollectable **)&attr_iter); attr_map = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_hash_type); MVM_gc_root_temp_push(tc, (MVMCollectable **)&attr_map); } while (MVM_iter_istrue(tc, attr_iter)) { MVMObject *current_slot_obj = MVM_repr_box_int(tc, MVM_hll_current(tc)->int_box_type, current_slot); MVMObject *attr, *name_obj; MVMString *name; MVM_repr_shift_o(tc, (MVMObject *)attr_iter); /* Get attribute. */ attr = MVM_iterval(tc, attr_iter); /* Get its name. */ name_obj = MVM_repr_at_key_o(tc, attr, instance->str_consts.name); name = MVM_repr_get_str(tc, name_obj); MVM_repr_bind_key_o(tc, attr_map, name, current_slot_obj); current_slot++; /* Push attr onto the flat list. */ MVM_repr_push_o(tc, flat_list, attr); } if (attr_map) { MVM_gc_root_temp_pop_n(tc, 2); } /* Add to class list and map list. */ MVM_repr_push_o(tc, class_list, current_class); MVM_repr_push_o(tc, attr_map_list, attr_map); } else { MVM_exception_throw_adhoc(tc, "CPPStruct representation does not support multiple inheritance"); } } MVM_gc_root_temp_pop_n(tc, 4); /* We can now form the name map. */ num_classes = MVM_repr_elems(tc, class_list); result = (MVMCPPStructNameMap *) MVM_malloc(sizeof(MVMCPPStructNameMap) * (1 + num_classes)); for (i = 0; i < num_classes; i++) { result[i].class_key = MVM_repr_at_pos_o(tc, class_list, i); result[i].name_map = MVM_repr_at_pos_o(tc, attr_map_list, i); } /* set the end to be NULL, it's useful for iteration. */ result[i].class_key = NULL; repr_data->name_to_index_mapping = result; return flat_list; }
/* Locates a method by name, checking in the method cache only. */ MVMObject * MVM_6model_find_method_cache_only(MVMThreadContext *tc, MVMObject *obj, MVMString *name) { MVMObject *cache = get_method_cache(tc, STABLE(obj)); if (cache && IS_CONCRETE(cache)) return MVM_repr_at_key_o(tc, cache, name); return NULL; }
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; }
return (MVMNativeCallBody *)REPR(obj)->box_funcs.get_boxed_ref(tc, STABLE(obj), obj, OBJECT_BODY(obj), MVM_REPR_ID_MVMNativeCall); } /* Gets the flag for whether to free a string after a call or not. */ static MVMint16 get_str_free_flag(MVMThreadContext *tc, MVMObject *info) { MVMString *flag = tc->instance->str_consts.free_str; if (MVM_repr_exists_key(tc, info, flag)) if (!MVM_repr_get_int(tc, MVM_repr_at_key_o(tc, info, flag))) return MVM_NATIVECALL_ARG_NO_FREE_STR; return MVM_NATIVECALL_ARG_FREE_STR; } /* Takes a hash describing a type hands back an argument type code. */ static MVMint16 get_arg_type(MVMThreadContext *tc, MVMObject *info, MVMint16 is_return) { MVMString *typename = MVM_repr_get_str(tc, MVM_repr_at_key_o(tc, info, tc->instance->str_consts.type)); char *ctypename = MVM_string_utf8_encode_C_string(tc, typename); MVMint16 result; if (strcmp(ctypename, "void") == 0) { if (!is_return) { free(ctypename); MVM_exception_throw_adhoc(tc, "Cannot use 'void' type except for on native call return values"); } result = MVM_NATIVECALL_ARG_VOID; } else if (strcmp(ctypename, "char") == 0) result = MVM_NATIVECALL_ARG_CHAR; else if (strcmp(ctypename, "short") == 0) result = MVM_NATIVECALL_ARG_SHORT; else if (strcmp(ctypename, "int") == 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_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; }
/* This works out an allocation strategy for the object. It takes care of * "inlining" storage of attributes that are natively typed, as well as * noting unbox targets. */ static void compute_allocation_strategy(MVMThreadContext *tc, MVMObject *repr_info, MVMCStructREPRData *repr_data) { /* Compute index mapping table and get flat list of attributes. */ MVMObject *flat_list = index_mapping_and_flat_list(tc, repr_info, repr_data); /* If we have no attributes in the index mapping, then just the header. */ if (repr_data->name_to_index_mapping[0].class_key == NULL) { repr_data->struct_size = 1; /* avoid 0-byte malloc */ } /* Otherwise, we need to compute the allocation strategy. */ else { /* We track the size of the struct, which is what we'll want offsets into. */ MVMint32 cur_size = 0; /* The structure itself will be the multiple of its biggest element in size. * So we keep track of that biggest element. */ MVMint32 multiple_of = 1; /* Get number of attributes and set up various counters. */ MVMint32 num_attrs = MVM_repr_elems(tc, flat_list); MVMint32 info_alloc = num_attrs == 0 ? 1 : num_attrs; MVMint32 cur_obj_attr = 0; MVMint32 cur_init_slot = 0; MVMint32 i; /* Allocate location/offset arrays and GC mark info arrays. */ repr_data->num_attributes = num_attrs; repr_data->attribute_locations = (MVMint32 *) MVM_malloc(info_alloc * sizeof(MVMint32)); repr_data->struct_offsets = (MVMint32 *) MVM_malloc(info_alloc * sizeof(MVMint32)); repr_data->flattened_stables = (MVMSTable **) MVM_calloc(info_alloc, sizeof(MVMObject *)); repr_data->member_types = (MVMObject **) MVM_calloc(info_alloc, sizeof(MVMObject *)); /* Go over the attributes and arrange their allocation. */ for (i = 0; i < num_attrs; i++) { /* Fetch its type; see if it's some kind of unboxed type. */ MVMObject *attr = MVM_repr_at_pos_o(tc, flat_list, i); MVMObject *type = MVM_repr_at_key_o(tc, attr, tc->instance->str_consts.type); MVMObject *inlined_val = MVM_repr_at_key_o(tc, attr, tc->instance->str_consts.inlined); MVMint64 inlined = !MVM_is_null(tc, inlined_val) && MVM_repr_get_int(tc, inlined_val); MVMint32 bits = sizeof(void *) * 8; MVMint32 align = ALIGNOF(void *); if (!MVM_is_null(tc, type)) { /* See if it's a type that we know how to handle in a C struct. */ const MVMStorageSpec *spec = REPR(type)->get_storage_spec(tc, STABLE(type)); MVMint32 type_id = REPR(type)->ID; if (spec->inlineable == MVM_STORAGE_SPEC_INLINED && (spec->boxed_primitive == MVM_STORAGE_SPEC_BP_INT || spec->boxed_primitive == MVM_STORAGE_SPEC_BP_NUM)) { /* It's a boxed int or num; pretty easy. It'll just live in the * body of the struct. Instead of masking in i here (which * would be the parallel to how we handle boxed types) we * repurpose it to store the bit-width of the type, so * that get_attribute_ref can find it later. */ bits = spec->bits; align = spec->align; repr_data->attribute_locations[i] = (bits << MVM_CSTRUCT_ATTR_SHIFT) | MVM_CSTRUCT_ATTR_IN_STRUCT; repr_data->flattened_stables[i] = STABLE(type); if (REPR(type)->initialize) { if (!repr_data->initialize_slots) repr_data->initialize_slots = (MVMint32 *) MVM_calloc(info_alloc + 1, sizeof(MVMint32)); repr_data->initialize_slots[cur_init_slot] = i; cur_init_slot++; } } else if (spec->can_box & MVM_STORAGE_SPEC_CAN_BOX_STR) { /* It's a string of some kind. */ repr_data->num_child_objs++; repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CSTRUCT_ATTR_SHIFT) | MVM_CSTRUCT_ATTR_STRING; repr_data->member_types[i] = type; repr_data->flattened_stables[i] = STABLE(type); if (REPR(type)->initialize) { if (!repr_data->initialize_slots) repr_data->initialize_slots = (MVMint32 *) MVM_calloc(info_alloc + 1, sizeof(MVMint32)); repr_data->initialize_slots[cur_init_slot] = i; cur_init_slot++; } } else if (type_id == MVM_REPR_ID_MVMCArray) { /* It's a CArray of some kind. */ repr_data->num_child_objs++; repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CSTRUCT_ATTR_SHIFT) | MVM_CSTRUCT_ATTR_CARRAY; repr_data->member_types[i] = type; } else if (type_id == MVM_REPR_ID_MVMCStruct) { /* It's a CStruct. */ repr_data->num_child_objs++; repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CSTRUCT_ATTR_SHIFT) | MVM_CSTRUCT_ATTR_CSTRUCT; repr_data->member_types[i] = type; if (inlined) { MVMCStructREPRData *cstruct_repr_data = (MVMCStructREPRData *)STABLE(type)->REPR_data; bits = cstruct_repr_data->struct_size * 8; align = cstruct_repr_data->struct_size; repr_data->attribute_locations[i] |= MVM_CSTRUCT_ATTR_INLINED; } } else if (type_id == MVM_REPR_ID_MVMCPPStruct) { /* It's a CPPStruct. */ repr_data->num_child_objs++; repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CSTRUCT_ATTR_SHIFT) | MVM_CSTRUCT_ATTR_CPPSTRUCT; repr_data->member_types[i] = type; if (inlined) { MVMCPPStructREPRData *cppstruct_repr_data = (MVMCPPStructREPRData *)STABLE(type)->REPR_data; bits = cppstruct_repr_data->struct_size * 8; align = cppstruct_repr_data->struct_size; repr_data->attribute_locations[i] |= MVM_CSTRUCT_ATTR_INLINED; } } else if (type_id == MVM_REPR_ID_MVMCUnion) { /* It's a CUnion. */ repr_data->num_child_objs++; repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CSTRUCT_ATTR_SHIFT) | MVM_CSTRUCT_ATTR_CUNION; repr_data->member_types[i] = type; if (inlined) { MVMCUnionREPRData *cunion_repr_data = (MVMCUnionREPRData *)STABLE(type)->REPR_data; bits = cunion_repr_data->struct_size * 8; align = cunion_repr_data->struct_size; repr_data->attribute_locations[i] |= MVM_CSTRUCT_ATTR_INLINED; } } else if (type_id == MVM_REPR_ID_MVMCPointer) { /* It's a CPointer. */ repr_data->num_child_objs++; repr_data->attribute_locations[i] = (cur_obj_attr++ << MVM_CSTRUCT_ATTR_SHIFT) | MVM_CSTRUCT_ATTR_CPTR; repr_data->member_types[i] = type; } else { MVM_exception_throw_adhoc(tc, "CStruct representation only handles int, num, CArray, CPointer, CStruct, CPPStruct and CUnion"); } } else { MVM_exception_throw_adhoc(tc, "CStruct representation requires the types of all attributes to be specified"); } if (bits % 8) { MVM_exception_throw_adhoc(tc, "CStruct only supports native types that are a multiple of 8 bits wide (was passed: %"PRId32")", bits); } /* Do allocation. */ /* C structure needs careful alignment. If cur_size is not aligned * to align bytes (cur_size % align), make sure it is before we * add the next element. */ if (cur_size % align) { cur_size += align - cur_size % align; } repr_data->struct_offsets[i] = cur_size; cur_size += bits / 8; if (bits / 8 > multiple_of) multiple_of = bits / 8; } /* Finally, put computed allocation size in place; it's body size plus * header size. Also number of markables and sentinels. */ if (multiple_of > sizeof(void *)) multiple_of = sizeof(void *); repr_data->struct_size = ceil((double)cur_size / (double)multiple_of) * multiple_of; if (repr_data->initialize_slots) repr_data->initialize_slots[cur_init_slot] = -1; } }