/* Returns the location of one string in another or -1 */ MVMint64 MVM_string_index(MVMThreadContext *tc, MVMString *haystack, MVMString *needle, MVMint64 start) { MVMint64 result = -1; size_t index = (size_t)start; MVMStringIndex hgraphs = NUM_GRAPHS(haystack), ngraphs = NUM_GRAPHS(needle); if (!IS_CONCRETE((MVMObject *)haystack)) { MVM_exception_throw_adhoc(tc, "index needs a concrete search target"); } if (!IS_CONCRETE((MVMObject *)needle)) { MVM_exception_throw_adhoc(tc, "index needs a concrete search term"); } if (!ngraphs && !hgraphs) return 0; /* the empty strings are equal and start at zero */ if (!hgraphs) return -1; if (start < 0 || start >= hgraphs) /* maybe return -1 instead? */ MVM_exception_throw_adhoc(tc, "index start offset out of range"); if (ngraphs > hgraphs || ngraphs < 1) return -1; /* brute force for now. horrible, yes. halp. */ while (index <= hgraphs - ngraphs) { if (MVM_string_substrings_equal_nocheck(tc, needle, 0, ngraphs, haystack, index)) { result = (MVMint64)index; break; } index++; } return result; }
/* Evaluates the argument guards. Returns >= 0 if there is a matching spesh * candidate, or -1 if there is not. */ MVMint32 MVM_spesh_arg_guard_run(MVMThreadContext *tc, MVMSpeshArgGuard *ag, MVMCallsite *cs, MVMRegister *args, MVMint32 *certain) { MVMuint32 current_node = 0; MVMObject *test = NULL; MVMint32 current_result = -1; if (!ag) return -1; do { MVMSpeshArgGuardNode *agn = &(ag->nodes[current_node]); switch (agn->op) { case MVM_SPESH_GUARD_OP_CALLSITE: current_node = agn->cs == cs ? agn->yes : agn->no; break; case MVM_SPESH_GUARD_OP_LOAD_ARG: test = args[agn->arg_index].o; current_node = agn->yes; break; case MVM_SPESH_GUARD_OP_STABLE_CONC: current_node = IS_CONCRETE(test) && test->st == agn->st ? agn->yes : agn->no; break; case MVM_SPESH_GUARD_OP_STABLE_TYPE: current_node = !IS_CONCRETE(test) && test->st == agn->st ? agn->yes : agn->no; break; case MVM_SPESH_GUARD_OP_DEREF_VALUE: { /* TODO Use offset approach later to avoid these calls. */ MVMRegister dc; test->st->container_spec->fetch(tc, test, &dc); test = dc.o; current_node = test ? agn->yes : agn->no; break; } case MVM_SPESH_GUARD_OP_DEREF_RW: /* TODO Use offset approach later to avoid these calls. */ current_node = STABLE(test)->container_spec->can_store(tc, test) ? agn->yes : agn->no; break; case MVM_SPESH_GUARD_OP_CERTAIN_RESULT: current_result = agn->result; if (certain) *certain = agn->result; current_node = agn->yes; break; case MVM_SPESH_GUARD_OP_RESULT: return agn->result; } } while (current_node != 0); return current_result; }
/* XXX inline parent's strands if it's a rope too? */ MVMString * MVM_string_concatenate(MVMThreadContext *tc, MVMString *a, MVMString *b) { MVMString *result; MVMStrandIndex strand_count = 0; MVMStrand *strands; MVMStringIndex index = 0; MVMStrandIndex max_strand_depth = 0; MVMStringIndex agraphs = NUM_GRAPHS(a), bgraphs = NUM_GRAPHS(b), rgraphs; if (!IS_CONCRETE((MVMObject *)a) || !IS_CONCRETE((MVMObject *)b)) { MVM_exception_throw_adhoc(tc, "Concatenate needs concrete strings"); } MVM_gc_root_temp_push(tc, (MVMCollectable **)&a); result = (MVMString *)REPR(a)->allocate(tc, STABLE(a)); MVM_gc_root_temp_pop(tc); /* there could be unattached combining chars at the beginning of b, so, XXX TODO handle this */ result->body.flags = MVM_STRING_TYPE_ROPE; rgraphs = agraphs + bgraphs; if (agraphs) strand_count = 1; if (bgraphs) ++strand_count; strands = result->body.strands = calloc(sizeof(MVMStrand), strand_count + 1); strand_count = 0; if (agraphs) { strands->string = a; strands->string_offset = 0; strands->compare_offset = index; index = agraphs; strand_count = 1; max_strand_depth = STRAND_DEPTH(a); } if (bgraphs) { strands[strand_count].string = b; strands[strand_count].string_offset = 0; strands[strand_count].compare_offset = index; index += bgraphs; ++strand_count; if (STRAND_DEPTH(b) > max_strand_depth) max_strand_depth = STRAND_DEPTH(b); } strands[strand_count].graphs = index; result->body.num_strands = strand_count; result->body.flags = MVM_STRING_TYPE_ROPE; _STRAND_DEPTH(result) = max_strand_depth + 1; return result; }
/* more general form of has_at; compares two substrings for equality */ MVMint64 MVM_string_have_at(MVMThreadContext *tc, MVMString *a, MVMint64 starta, MVMint64 length, MVMString *b, MVMint64 startb) { if (!IS_CONCRETE((MVMObject *)a) || !IS_CONCRETE((MVMObject *)b)) { MVM_exception_throw_adhoc(tc, "have_at needs concrete strings"); } if (starta < 0 || startb < 0) return 0; if (length == 0) return 1; if (starta + length > NUM_GRAPHS(a) || startb + length > NUM_GRAPHS(b)) return 0; return MVM_string_substrings_equal_nocheck(tc, a, starta, length, b, startb); }
MVMint64 MVM_coerce_istrue_s(MVMThreadContext *tc, MVMString *str) { return str == NULL || !IS_CONCRETE(str) || MVM_string_graphs(tc, str) == 0 || (MVM_string_graphs(tc, str) == 1 && MVM_string_get_grapheme_at_nocheck(tc, str, 0) == 48) ? 0 : 1; }
MVMObject * MVM_hll_set_config(MVMThreadContext *tc, MVMString *name, MVMObject *config_hash) { MVMHLLConfig *config; config = MVM_hll_get_config_for(tc, name); if (!config_hash || REPR(config_hash)->ID != MVM_REPR_ID_MVMHash || !IS_CONCRETE(config_hash)) { MVM_exception_throw_adhoc(tc, "set hll config needs concrete hash"); } /* MVM_string_utf8_decode() can potentially allocate, and hence gc. */ MVMROOT(tc, config_hash, { check_config_key(tc, config_hash, "int_box", int_box_type, config); check_config_key(tc, config_hash, "num_box", num_box_type, config); check_config_key(tc, config_hash, "str_box", str_box_type, config); check_config_key(tc, config_hash, "slurpy_array", slurpy_array_type, config); check_config_key(tc, config_hash, "slurpy_hash", slurpy_hash_type, config); check_config_key(tc, config_hash, "array_iter", array_iterator_type, config); check_config_key(tc, config_hash, "hash_iter", hash_iterator_type, config); check_config_key(tc, config_hash, "foreign_type_int", foreign_type_int, config); check_config_key(tc, config_hash, "foreign_type_num", foreign_type_num, config); check_config_key(tc, config_hash, "foreign_type_str", foreign_type_str, config); check_config_key(tc, config_hash, "foreign_transform_array", foreign_transform_array, config); check_config_key(tc, config_hash, "foreign_transform_hash", foreign_transform_hash, config); check_config_key(tc, config_hash, "foreign_transform_code", foreign_transform_code, config); check_config_key(tc, config_hash, "null_value", null_value, config); check_config_key(tc, config_hash, "exit_handler", exit_handler, config); check_config_key(tc, config_hash, "bind_error", bind_error, config); check_config_key(tc, config_hash, "method_not_found_error", method_not_found_error, config); });
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"); } }
/* Takes two objects, which must be of VMArray representation and holding * 32-bit integers. Performs normalization to the specified form. */ static void assert_codepoint_array(MVMThreadContext *tc, MVMObject *arr, char *error) { if (IS_CONCRETE(arr) && REPR(arr)->ID == MVM_REPR_ID_MVMArray) { MVMuint8 slot_type = ((MVMArrayREPRData *)STABLE(arr)->REPR_data)->slot_type; if (slot_type == MVM_ARRAY_I32 || slot_type == MVM_ARRAY_U32) return; } MVM_exception_throw_adhoc(tc, "%s", error); }
/* Returns the body of a P6bigint, containing the bigint/smallint union, for * operations that want to explicitly handle the two. */ static MVMP6bigintBody * get_bigint_body(MVMThreadContext *tc, MVMObject *obj) { if (IS_CONCRETE(obj)) return (MVMP6bigintBody *)REPR(obj)->box_funcs.get_boxed_ref(tc, STABLE(obj), obj, OBJECT_BODY(obj), MVM_REPR_ID_P6bigint); else MVM_exception_throw_adhoc(tc, "Can only perform big integer operations on concrete objects"); }
MVMint64 MVM_6model_container_iscont_rw(MVMThreadContext *tc, MVMObject *cont) { if (cont && IS_CONCRETE(cont)) { const MVMContainerSpec *cs = STABLE(cont)->container_spec; if (cs && cs->can_store(tc, cont)) { return 1; } } return 0; }
/* Invocation protocol handler. */ static void invoke_handler(MVMThreadContext *tc, MVMObject *invokee, MVMCallsite *callsite, MVMRegister *args) { if (IS_CONCRETE(invokee)) { MVMCode *code = (MVMCode *)invokee; MVM_frame_invoke(tc, code->body.sf, callsite, args, code->body.outer, invokee); } else { MVM_exception_throw_adhoc(tc, "Cannot invoke code type object"); } }
/* Tests whether one string a has the other string b as a substring at that index */ MVMint64 MVM_string_equal_at(MVMThreadContext *tc, MVMString *a, MVMString *b, MVMint64 offset) { MVMStringIndex agraphs, bgraphs; if (!IS_CONCRETE((MVMObject *)a) || !IS_CONCRETE((MVMObject *)b)) { MVM_exception_throw_adhoc(tc, "equal_at needs concrete strings"); } agraphs = NUM_GRAPHS(a); bgraphs = NUM_GRAPHS(b); if (offset < 0) { offset += agraphs; if (offset < 0) offset = 0; /* XXX I think this is the right behavior here */ } if (agraphs - offset < bgraphs) return 0; return MVM_string_substrings_equal_nocheck(tc, a, offset, bgraphs, b, 0); }
static void late_bound_find_method_return(MVMThreadContext *tc, void *sr_data) { FindMethodSRData *fm = (FindMethodSRData *)sr_data; if (MVM_is_null(tc, fm->res->o) || !IS_CONCRETE(fm->res->o)) { MVMObject *obj = fm->obj; MVMString *name = fm->name; MVM_free(fm); die_over_missing_method(tc, obj, name); } else { MVM_free(fm); } }
/* Introspects the methods. */ static void methods(MVMThreadContext *tc, MVMCallsite *callsite, MVMRegister *args) { MVMObject *self, *type_obj, *method_table; MVMArgProcContext arg_ctx; arg_ctx.named_used = NULL; MVM_args_proc_init(tc, &arg_ctx, callsite, args); self = MVM_args_get_pos_obj(tc, &arg_ctx, 0, MVM_ARG_REQUIRED).arg.o; type_obj = MVM_args_get_pos_obj(tc, &arg_ctx, 1, MVM_ARG_REQUIRED).arg.o; MVM_args_proc_cleanup(tc, &arg_ctx); if (!self || !IS_CONCRETE(self) || REPR(self)->ID != MVM_REPR_ID_KnowHOWREPR) MVM_exception_throw_adhoc(tc, "KnowHOW methods must be called on object instance with REPR KnowHOWREPR"); method_table = ((MVMKnowHOWREPR *)self)->body.methods; MVM_args_set_result_obj(tc, method_table, MVM_RETURN_CURRENT_FRAME); }
/* 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); }
MVMString * MVM_string_repeat(MVMThreadContext *tc, MVMString *a, MVMint64 count) { MVMString *result; MVMint64 bkup_count = count; MVMStringIndex string_offset = 0, graphs = 0, rgraphs; if (!IS_CONCRETE((MVMObject *)a)) { MVM_exception_throw_adhoc(tc, "repeat needs a concrete string"); } if (count < 0) MVM_exception_throw_adhoc(tc, "repeat count (%lld) cannot be negative", count); if (count > (1<<30)) MVM_exception_throw_adhoc(tc, "repeat count > %lld arbitrarily unsupported...", (1<<30)); MVM_gc_root_temp_push(tc, (MVMCollectable **)&a); result = (MVMString *)REPR(a)->allocate(tc, STABLE(a)); MVM_gc_root_temp_pop(tc); if (IS_ONE_STRING_ROPE(a)) { string_offset = a->body.strands->string_offset; graphs = a->body.strands[1].graphs; a = a->body.strands->string; } else { graphs = NUM_GRAPHS(a); } rgraphs = graphs * count; /* XXX compute tradeoff here of space usage of the strands table vs real string */ if (rgraphs) { MVMStrand *strands = result->body.strands = calloc(sizeof(MVMStrand), count + 1); result->body.flags = MVM_STRING_TYPE_ROPE; while (count--) { strands[count].compare_offset = count * graphs; strands[count].string = a; strands[count].string_offset = string_offset; } strands[bkup_count].graphs = rgraphs; result->body.num_strands = bkup_count; _STRAND_DEPTH(result) = STRAND_DEPTH(a) + 1; } else { result->body.flags = MVM_STRING_TYPE_UINT8; /* leave graphs 0 and storage null */ } return result; }
/* =item C<static void add_to_cache(PARROT_INTERP, NQP_md_cache *cache, PMC *capture, INTVAL num_args)> Adds an entry to the multi-dispatch cache. =cut */ void add_to_cache(PARROT_INTERP, NQP_md_cache *cache, PMC *capture, INTVAL num_args, PMC *result) { INTVAL arg_tup[MD_CACHE_MAX_ARITY]; INTVAL i, entries, ins_type; struct Pcc_cell * pc_positionals; /* Make sure 6model type ID is set. */ if (!smo_id) smo_id = Parrot_pmc_get_type_str(interp, Parrot_str_new(interp, "SixModelObject", 0)); /* If it's zero arity, just stick it in that slot. */ if (num_args == 0) { cache->zero_arity = result; return; } /* If the cache is saturated, don't do anything (we could instead do a random * replacement). */ entries = cache->arity_caches[num_args - 1].num_entries; if (entries == MD_CACHE_MAX_ENTRIES) return; /* Create arg tuple. */ if (capture->vtable->base_type == enum_class_CallContext) GETATTR_CallContext_positionals(interp, capture, pc_positionals); else return; for (i = 0; i < num_args; i++) { if (pc_positionals[i].type == BIND_VAL_OBJ) { PMC *arg = pc_positionals[i].u.p; if (arg->vtable->base_type != smo_id) return; arg_tup[i] = STABLE(arg)->type_cache_id | (IS_CONCRETE(arg) ? 1 : 0); } else { arg_tup[i] = (pc_positionals[i].type << 1) | 1; } } /* If there's no entries yet, need to do some allocation. */ if (entries == 0) { cache->arity_caches[num_args - 1].type_ids = mem_sys_allocate(num_args * sizeof(INTVAL) * MD_CACHE_MAX_ENTRIES); cache->arity_caches[num_args - 1].results = mem_sys_allocate(sizeof(PMC *) * MD_CACHE_MAX_ENTRIES); } /* Add entry. */ ins_type = entries * num_args; for (i = 0; i < num_args; i++) cache->arity_caches[num_args - 1].type_ids[ins_type + i] = arg_tup[i]; cache->arity_caches[num_args - 1].results[entries] = result; cache->arity_caches[num_args - 1].num_entries = entries + 1; }
/* returns the codepoint (could be a negative synthetic) at a given index of the string */ MVMint64 MVM_string_get_codepoint_at(MVMThreadContext *tc, MVMString *a, MVMint64 index) { MVMStringIndex agraphs; if (!IS_CONCRETE((MVMObject *)a)) { MVM_exception_throw_adhoc(tc, "codepoint_at needs a concrete string"); } agraphs = NUM_GRAPHS(a); if (index < 0 || index >= agraphs) MVM_exception_throw_adhoc(tc, "Invalid string index: max %lld, got %lld", agraphs - 1, index); return (MVMint64)MVM_string_get_codepoint_at_nocheck(tc, a, index); }
/* Throws the specified exception object, taking the category from it. If * the handler resumes, the resumption result will be put into resume_result. * Leaves the interpreter in a state where it will next run the instruction of * the handler. If there is no handler, it will panic and exit with a backtrace. */ void MVM_exception_throwobj(MVMThreadContext *tc, MVMuint8 mode, MVMObject *exObj, MVMRegister *resume_result) { LocatedHandler lh; MVMException *ex; if (IS_CONCRETE(exObj) && REPR(exObj)->ID == MVM_REPR_ID_MVMException) ex = (MVMException *)exObj; else MVM_exception_throw_adhoc(tc, "Can only throw an exception object"); lh = search_for_handler_from(tc, tc->cur_frame, mode, ex->body.category); if (lh.frame == NULL) panic_unhandled_ex(tc, ex); run_handler(tc, lh, exObj); }
/* Gets the current value for an attribute. */ static PMC * get_attribute_boxed(PARROT_INTERP, STable *st, void *data, PMC *class_handle, STRING *name, INTVAL hint) { P6opaqueREPRData *repr_data = (P6opaqueREPRData *)st->REPR_data; INTVAL slot; /* Try the slot allocation first. */ slot = hint >= 0 && !(repr_data->mi) ? hint : try_get_slot(interp, repr_data, class_handle, name); if (slot >= 0) { if (!repr_data->flattened_stables[slot]) { PMC *result = get_pmc_at_offset(data, repr_data->attribute_offsets[slot]); if (result) { return result; } else { /* Maybe we know how to auto-viv it to a container. */ if (repr_data->auto_viv_values) { PMC *value = repr_data->auto_viv_values[slot]; if (value != NULL) { if (IS_CONCRETE(value)) { PMC *cloned = REPR(value)->allocate(interp, STABLE(value)); REPR(value)->copy_to(interp, STABLE(value), OBJECT_BODY(value), OBJECT_BODY(cloned)); PARROT_GC_WRITE_BARRIER(interp, cloned); set_pmc_at_offset(data, repr_data->attribute_offsets[slot], cloned); return cloned; } else { set_pmc_at_offset(data, repr_data->attribute_offsets[slot], value); return value; } } } return PMCNULL; } } else { /* Need to produce a boxed version of this attribute. */ STable *st = repr_data->flattened_stables[slot]; PMC *result = st->REPR->allocate(interp, st); st->REPR->copy_to(interp, st, (char *)data + repr_data->attribute_offsets[slot], OBJECT_BODY(result)); PARROT_GC_WRITE_BARRIER(interp, result); return result; } } /* Otherwise, complain that the attribute doesn't exist. */ no_such_attribute(interp, "get", class_handle, name); }
/* Returns a substring of the given string */ MVMString * MVM_string_substring(MVMThreadContext *tc, MVMString *a, MVMint64 start, MVMint64 length) { MVMString *result; MVMStrand *strands; MVMStringIndex agraphs = NUM_GRAPHS(a); if (start < 0) { start += agraphs; if (start < 0) start = 0; } if (!IS_CONCRETE((MVMObject *)a)) { MVM_exception_throw_adhoc(tc, "Substring needs a concrete string"); } if (start > agraphs) start = agraphs; if (length == -1) /* -1 signifies go to the end of the string */ length = agraphs - start; if (length < 0) MVM_exception_throw_adhoc(tc, "Substring length (%lld) cannot be negative", length); if (start + length > agraphs) length = agraphs - start; MVM_gc_root_temp_push(tc, (MVMCollectable **)&a); result = (MVMString *)REPR(a)->allocate(tc, STABLE(a)); MVM_gc_root_temp_pop(tc); strands = result->body.strands = calloc(sizeof(MVMStrand), 2); /* if we're substringing a substring, substring the same one */ if (IS_ONE_STRING_ROPE(a)) { strands->string_offset = (MVMStringIndex)start + a->body.strands->string_offset; strands->string = a->body.strands->string; } else { strands->string_offset = (MVMStringIndex)start; strands->string = a; } /* result->body.codes = 0; /* Populate this lazily. */ result->body.flags = MVM_STRING_TYPE_ROPE; result->body.num_strands = 1; strands[1].graphs = length; _STRAND_DEPTH(result) = STRAND_DEPTH(strands->string) + 1; return result; }
void MVM_string_print(MVMThreadContext *tc, MVMString *a) { MVMuint8 *utf8_encoded; MVMuint64 utf8_encoded_length; if (!IS_CONCRETE((MVMObject *)a)) { MVM_exception_throw_adhoc(tc, "print needs a concrete string"); } /* XXX send a buffer of substrings of size 100 or something? */ utf8_encoded = MVM_string_utf8_encode(tc, a, &utf8_encoded_length); fwrite(utf8_encoded, 1, utf8_encoded_length, stdout); free(utf8_encoded); }
/* 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; }
/* =item C<static PMC * find_in_cache(PARROT_INTERP, NQP_md_cache *cache, PMC *capture, INTVAL num_args)> Looks for an entry in the multi-dispatch cache. =cut */ static PMC * find_in_cache(PARROT_INTERP, NQP_md_cache *cache, PMC *capture, INTVAL num_args) { INTVAL arg_tup[MD_CACHE_MAX_ARITY]; INTVAL i, j, entries, t_pos; struct Pcc_cell * pc_positionals; /* If it's zero-arity, return result right off. */ if (num_args == 0) return cache->zero_arity; /* Create arg tuple. */ if (capture->vtable->base_type == enum_class_CallContext) GETATTR_CallContext_positionals(interp, capture, pc_positionals); else return NULL; for (i = 0; i < num_args; i++) { if (pc_positionals[i].type == BIND_VAL_OBJ) { PMC *arg = pc_positionals[i].u.p; if (arg->vtable->base_type != smo_id) return NULL; arg_tup[i] = STABLE(arg)->type_cache_id | (IS_CONCRETE(arg) ? 1 : 0); } else { arg_tup[i] = (pc_positionals[i].type << 1) | 1; } } /* Look through entries. */ entries = cache->arity_caches[num_args - 1].num_entries; t_pos = 0; for (i = 0; i < entries; i++) { INTVAL match = 1; for (j = 0; j < num_args; j++) { if (cache->arity_caches[num_args - 1].type_ids[t_pos + j] != arg_tup[j]) { match = 0; break; } } if (match) return cache->arity_caches[num_args - 1].results[i]; t_pos += num_args; } return NULL; }
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); }
/* Adds facts from knowing the exact value being put into an object local. */ static void object_facts(MVMThreadContext *tc, MVMSpeshGraph *g, MVMuint16 tgt_orig, MVMuint16 tgt_i, MVMObject *obj) { /* Ensure it's non-null. */ if (!obj) return; /* Set the value itself. */ g->facts[tgt_orig][tgt_i].value.o = obj; g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_KNOWN_VALUE; /* We also know the type. */ g->facts[tgt_orig][tgt_i].type = STABLE(obj)->WHAT; g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_KNOWN_TYPE; /* Set concreteness flags. */ if (IS_CONCRETE(obj)) g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_CONCRETE; else g->facts[tgt_orig][tgt_i].flags |= MVM_SPESH_FACT_TYPEOBJ; }
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"); } }
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; }