예제 #1
0
MVMObject * snapshot_to_mvm_object(MVMThreadContext *tc, MVMHeapSnapshot *s) {
    MVMObject *snapshot = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_hash_type);
    MVM_repr_bind_key_o(tc, snapshot, vmstr(tc, "collectables"),
        collectables_str(tc, s));
    MVM_repr_bind_key_o(tc, snapshot, vmstr(tc, "references"),
        references_str(tc, s));
    return snapshot;
}
예제 #2
0
/* Dumps data from a single thread. */
static MVMObject * dump_thread_data(MVMThreadContext *tc, ProfDumpStrs *pds,
                                    const MVMProfileThreadData *ptd) {
    MVMObject *thread_hash = new_hash(tc);
    MVMObject *thread_gcs  = new_array(tc);
    MVMuint32  i;

    /* Add time. */
    MVM_repr_bind_key_o(tc, thread_hash, pds->total_time,
        box_i(tc, (ptd->end_time - ptd->start_time) / 1000));

    /* Add call graph. */
    if (ptd->call_graph)
        MVM_repr_bind_key_o(tc, thread_hash, pds->call_graph,
            dump_call_graph_node(tc, pds, ptd->call_graph));

    /* Add GCs. */
    for (i = 0; i < ptd->num_gcs; i++) {
        MVMObject *gc_hash = new_hash(tc);
        MVM_repr_bind_key_o(tc, gc_hash, pds->time,
            box_i(tc, ptd->gcs[i].time / 1000));
        MVM_repr_bind_key_o(tc, gc_hash, pds->full,
            box_i(tc, ptd->gcs[i].full));
        MVM_repr_bind_key_o(tc, gc_hash, pds->cleared_bytes,
            box_i(tc, ptd->gcs[i].cleared_bytes));
        MVM_repr_bind_key_o(tc, gc_hash, pds->retained_bytes,
            box_i(tc, ptd->gcs[i].retained_bytes));
        MVM_repr_bind_key_o(tc, gc_hash, pds->promoted_bytes,
            box_i(tc, ptd->gcs[i].promoted_bytes));
        MVM_repr_bind_key_o(tc, gc_hash, pds->gen2_roots,
            box_i(tc, ptd->gcs[i].num_gen2roots));
        MVM_repr_push_o(tc, thread_gcs, gc_hash);
    }
    MVM_repr_bind_key_o(tc, thread_hash, pds->gcs, thread_gcs);

    /* Add spesh time. */
    MVM_repr_bind_key_o(tc, thread_hash, pds->spesh_time,
        box_i(tc, ptd->spesh_time / 1000));

    return thread_hash;
}
예제 #3
0
MVMObject * collection_to_mvm_objects(MVMThreadContext *tc, MVMHeapSnapshotCollection *col) {
    MVMObject *results;

    /* Allocate in gen2, so as not to trigger GC. */
    MVM_gc_allocate_gen2_default_set(tc);

    /* Top-level results is a hash. */
    results = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_hash_type);
    MVM_repr_bind_key_o(tc, results, vmstr(tc, "strings"),
        string_heap_array(tc, col));
    MVM_repr_bind_key_o(tc, results, vmstr(tc, "types"),
        types_str(tc, col));
    MVM_repr_bind_key_o(tc, results, vmstr(tc, "static_frames"),
        static_frames_str(tc, col));
    MVM_repr_bind_key_o(tc, results, vmstr(tc, "snapshots"),
        snapshots_to_mvm_objects(tc, col));

    /* Switch off gen2 allocations now we're done. */
    MVM_gc_allocate_gen2_default_clear(tc);

    return results;
}
예제 #4
0
파일: procops.c 프로젝트: baby-gnu/MoarVM
MVMObject * MVM_proc_getenvhash(MVMThreadContext *tc) {
    MVMInstance * const instance = tc->instance;
    MVMObject   *       env_hash;

#ifdef _WIN32
    const MVMuint16 acp = GetACP(); /* We should get ACP at runtime. */
#endif
    MVMuint32     pos = 0;
    MVMString *needle = MVM_string_ascii_decode(tc, instance->VMString, STR_WITH_LEN("="));
    char      *env;

    MVM_gc_root_temp_push(tc, (MVMCollectable **)&needle);

    env_hash = MVM_repr_alloc_init(tc,  MVM_hll_current(tc)->slurpy_hash_type);
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&env_hash);

    while ((env = environ[pos++]) != NULL) {
#ifndef _WIN32
        MVMString    *str = MVM_string_utf8_c8_decode(tc, instance->VMString, env, strlen(env));
#else
        char * const _env = ANSIToUTF8(acp, env);
        MVMString    *str = MVM_string_utf8_c8_decode(tc, instance->VMString, _env, strlen(_env));
#endif

        MVMuint32 index = MVM_string_index(tc, str, needle, 0);

        MVMString *key, *val;
        MVMObject *box;

#ifdef _WIN32
        MVM_free(_env);
#endif
        MVM_gc_root_temp_push(tc, (MVMCollectable **)&str);

        key  = MVM_string_substring(tc, str, 0, index);
        MVM_gc_root_temp_push(tc, (MVMCollectable **)&key);

        val  = MVM_string_substring(tc, str, index + 1, -1);
        box  = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, val);
        MVM_repr_bind_key_o(tc, env_hash, key, box);

        MVM_gc_root_temp_pop_n(tc, 2);
    }

    MVM_gc_root_temp_pop_n(tc, 2);

    return env_hash;
}
예제 #5
0
파일: bootstrap.c 프로젝트: nanis/MoarVM
/* Adds a method. */
static void add_method(MVMThreadContext *tc, MVMCallsite *callsite, MVMRegister *args) {
    MVMObject *self, *method, *method_table;
    MVMString *name;

    /* Get arguments. */
    MVMArgProcContext arg_ctx; arg_ctx.named_used = NULL;
    MVM_args_proc_init(tc, &arg_ctx, callsite, args);
    MVM_args_checkarity(tc, &arg_ctx, 4, 4);
    self     = MVM_args_get_pos_obj(tc, &arg_ctx, 0, MVM_ARG_REQUIRED).arg.o;
    name     = MVM_args_get_pos_str(tc, &arg_ctx, 2, MVM_ARG_REQUIRED).arg.s;
    method   = MVM_args_get_pos_obj(tc, &arg_ctx, 3, 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");

    /* Add to method table. */
    method_table = ((MVMKnowHOWREPR *)self)->body.methods;
    MVM_repr_bind_key_o(tc, method_table, name, method);

    /* Return added method as result. */
    MVM_args_set_result_obj(tc, method, MVM_RETURN_CURRENT_FRAME);
}
예제 #6
0
/* 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;
}
예제 #7
0
/* Dumps a call graph node. */
static MVMObject * dump_call_graph_node(MVMThreadContext *tc, ProfDumpStrs *pds,
                                        const MVMProfileCallNode *pcn) {
    MVMObject *node_hash  = new_hash(tc);
    MVMuint32  i;

    /* Let's see if we're dealing with a native call or a regular moar call */
    if (pcn->sf) {
        /* Try to resolve the code filename and line number. */
        MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc,
            &(pcn->sf->body), 0);
        MVMint32 fshi = annot ? (MVMint32)annot->filename_string_heap_index : -1;

        /* Add name of code object. */
        MVM_repr_bind_key_o(tc, node_hash, pds->name,
            box_s(tc, pcn->sf->body.name));

        /* Add line number and file name. */
        if (fshi >= 0 && fshi < pcn->sf->body.cu->body.num_strings)
            MVM_repr_bind_key_o(tc, node_hash, pds->file,
                box_s(tc, MVM_cu_string(tc, pcn->sf->body.cu, fshi)));
        else if (pcn->sf->body.cu->body.filename)
            MVM_repr_bind_key_o(tc, node_hash, pds->file,
                box_s(tc, pcn->sf->body.cu->body.filename));
        else
            MVM_repr_bind_key_o(tc, node_hash, pds->file,
                box_s(tc, tc->instance->str_consts.empty));
        MVM_repr_bind_key_o(tc, node_hash, pds->line,
            box_i(tc, annot ? (MVMint32)annot->line_number : -1));
        MVM_free(annot);

        /* Use static frame memory address to get a unique ID. */
        MVM_repr_bind_key_o(tc, node_hash, pds->id,
            box_i(tc, (MVMint64)pcn->sf));
    } else {
        MVMString *function_name_string =
            MVM_string_utf8_c8_decode(tc, tc->instance->VMString,
                                      pcn->native_target_name, strlen(pcn->native_target_name));

        MVM_repr_bind_key_o(tc, node_hash, pds->name,
            box_s(tc, function_name_string));
        MVM_repr_bind_key_o(tc, node_hash, pds->file,
            box_s(tc, pds->native_lib));

        MVM_repr_bind_key_o(tc, node_hash, pds->line,
            box_i(tc, -2));

        /* Use the address of the name string as unique ID. a hack, but oh well. */
        MVM_repr_bind_key_o(tc, node_hash, pds->id,
            box_i(tc, (MVMint64)pcn->native_target_name));
    }

    /* Entry counts. */
    if (pcn->total_entries)
        MVM_repr_bind_key_o(tc, node_hash, pds->entries,
            box_i(tc, pcn->total_entries));
    if (pcn->specialized_entries)
        MVM_repr_bind_key_o(tc, node_hash, pds->spesh_entries,
            box_i(tc, pcn->specialized_entries));
    if (pcn->jit_entries)
        MVM_repr_bind_key_o(tc, node_hash, pds->jit_entries,
            box_i(tc, pcn->jit_entries));
    if (pcn->inlined_entries)
        MVM_repr_bind_key_o(tc, node_hash, pds->inlined_entries,
            box_i(tc, pcn->inlined_entries));

    /* Total (inclusive) time. */
    MVM_repr_bind_key_o(tc, node_hash, pds->inclusive_time,
        box_i(tc, pcn->total_time / 1000));

    /* OSR and deopt counts. */
    if (pcn->osr_count)
        MVM_repr_bind_key_o(tc, node_hash, pds->osr,
            box_i(tc, pcn->osr_count));
    if (pcn->deopt_one_count)
        MVM_repr_bind_key_o(tc, node_hash, pds->deopt_one,
            box_i(tc, pcn->deopt_one_count));
    if (pcn->deopt_all_count)
        MVM_repr_bind_key_o(tc, node_hash, pds->deopt_all,
            box_i(tc, pcn->deopt_all_count));

    /* Visit successors in the call graph, dumping them and working out the
     * exclusive time. */
    if (pcn->num_succ) {
        MVMObject *callees        = new_array(tc);
        MVMuint64  exclusive_time = pcn->total_time;
        for (i = 0; i < pcn->num_succ; i++) {
            MVM_repr_push_o(tc, callees,
                dump_call_graph_node(tc, pds, pcn->succ[i]));
            exclusive_time -= pcn->succ[i]->total_time;
        }
        MVM_repr_bind_key_o(tc, node_hash, pds->exclusive_time,
            box_i(tc, exclusive_time / 1000));
        MVM_repr_bind_key_o(tc, node_hash, pds->callees, callees);
    }
    else {
        MVM_repr_bind_key_o(tc, node_hash, pds->exclusive_time,
            box_i(tc, pcn->total_time / 1000));
    }

    if (pcn->num_alloc) {
        /* Emit allocations. */
        MVMObject *alloc_list = new_array(tc);
        MVM_repr_bind_key_o(tc, node_hash, pds->allocations, alloc_list);
        for (i = 0; i < pcn->num_alloc; i++) {
            MVMObject *alloc_info = new_hash(tc);
            MVMProfileAllocationCount *alloc = &pcn->alloc[i];

            MVMObject *type       = pcn->alloc[i].type;

            MVM_repr_bind_key_o(tc, alloc_info, pds->id, box_i(tc, (MVMint64)type));
            MVM_repr_bind_key_o(tc, alloc_info, pds->type, type);
            if (alloc->allocations_spesh)
                MVM_repr_bind_key_o(tc, alloc_info, pds->spesh,
                    box_i(tc, alloc->allocations_spesh));
            if (alloc->allocations_jit)
                MVM_repr_bind_key_o(tc, alloc_info, pds->jit,
                    box_i(tc, alloc->allocations_jit));
            MVM_repr_bind_key_o(tc, alloc_info, pds->count,
                box_i(tc, alloc->allocations_interp
                          + alloc->allocations_spesh
                          + alloc->allocations_jit));
            MVM_repr_push_o(tc, alloc_list, alloc_info);
        }
    }

    return node_hash;
}
예제 #8
0
파일: bootstrap.c 프로젝트: nanis/MoarVM
/* Composes the meta-object. */
static void compose(MVMThreadContext *tc, MVMCallsite *callsite, MVMRegister *args) {
    MVMObject *self, *type_obj, *method_table, *attributes, *BOOTArray, *BOOTHash,
              *repr_info_hash, *repr_info, *type_info, *attr_info_list, *parent_info;
    MVMuint64   num_attrs, i;
    MVMInstance *instance = tc->instance;

    /* Get arguments. */
    MVMArgProcContext arg_ctx; arg_ctx.named_used = NULL;
    MVM_args_proc_init(tc, &arg_ctx, callsite, args);
    MVM_args_checkarity(tc, &arg_ctx, 2, 2);
    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");

    /* Fill out STable. */
    method_table = ((MVMKnowHOWREPR *)self)->body.methods;
    MVM_ASSIGN_REF(tc, &(STABLE(type_obj)->header), STABLE(type_obj)->method_cache, method_table);
    STABLE(type_obj)->mode_flags              = MVM_METHOD_CACHE_AUTHORITATIVE;
    STABLE(type_obj)->type_check_cache_length = 1;
    STABLE(type_obj)->type_check_cache        = MVM_malloc(sizeof(MVMObject *));
    MVM_ASSIGN_REF(tc, &(STABLE(type_obj)->header), STABLE(type_obj)->type_check_cache[0], type_obj);
    attributes = ((MVMKnowHOWREPR *)self)->body.attributes;

    /* Next steps will allocate, so make sure we keep hold of the type
     * object and ourself. */
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&attributes);
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&type_obj);

    /* Use any attribute information to produce attribute protocol
     * data. The protocol consists of an array... */
    BOOTArray = instance->boot_types.BOOTArray;
    BOOTHash = instance->boot_types.BOOTHash;
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&BOOTArray);
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&BOOTHash);
    repr_info = REPR(BOOTArray)->allocate(tc, STABLE(BOOTArray));
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&repr_info);

    /* ...which contains an array per MRO entry (just us)... */
    type_info = REPR(BOOTArray)->allocate(tc, STABLE(BOOTArray));
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&type_info);
    MVM_repr_push_o(tc, repr_info, type_info);

    /* ...which in turn contains this type... */
    MVM_repr_push_o(tc, type_info, type_obj);

    /* ...then an array of hashes per attribute... */
    attr_info_list = REPR(BOOTArray)->allocate(tc, STABLE(BOOTArray));
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&attr_info_list);
    MVM_repr_push_o(tc, type_info, attr_info_list);
    num_attrs = REPR(attributes)->elems(tc, STABLE(attributes),
        attributes, OBJECT_BODY(attributes));
    for (i = 0; i < num_attrs; i++) {
        MVMObject *attr_info = REPR(BOOTHash)->allocate(tc, STABLE(BOOTHash));
        MVMKnowHOWAttributeREPR *attribute = (MVMKnowHOWAttributeREPR *)
            MVM_repr_at_pos_o(tc, attributes, i);
        MVMROOT(tc, attr_info, {
            MVMROOT(tc, attribute, {
                if (REPR((MVMObject *)attribute)->ID != MVM_REPR_ID_KnowHOWAttributeREPR)
                    MVM_exception_throw_adhoc(tc, "KnowHOW attributes must use KnowHOWAttributeREPR");

                MVM_repr_init(tc, attr_info);
                MVM_repr_bind_key_o(tc, attr_info, instance->str_consts.name, (MVMObject *)attribute->body.name);
                MVM_repr_bind_key_o(tc, attr_info, instance->str_consts.type, attribute->body.type);
                if (attribute->body.box_target) {
                    /* Merely having the key serves as a "yes". */
                    MVM_repr_bind_key_o(tc, attr_info, instance->str_consts.box_target, attr_info);
                }

                MVM_repr_push_o(tc, attr_info_list, attr_info);
            });
        });
예제 #9
0
파일: exceptions.c 프로젝트: danaj/MoarVM
/* Returns a list of hashes containing file, line, sub and annotations. */
MVMObject * MVM_exception_backtrace(MVMThreadContext *tc, MVMObject *ex_obj) {
    MVMFrame *cur_frame;
    MVMObject *arr = NULL, *annotations = NULL, *row = NULL, *value = NULL;
    MVMuint32 count = 0;
    MVMString *k_file = NULL, *k_line = NULL, *k_sub = NULL, *k_anno = NULL;

    if (IS_CONCRETE(ex_obj) && REPR(ex_obj)->ID == MVM_REPR_ID_MVMException)
        cur_frame = ((MVMException *)ex_obj)->body.origin;
    else
        MVM_exception_throw_adhoc(tc, "Op 'backtrace' needs an exception object");

    MVM_gc_root_temp_push(tc, (MVMCollectable **)&arr);
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&annotations);
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&row);
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&value);
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_file);
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_line);
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_sub);
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&k_anno);

    k_file = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "file");
    k_line = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "line");
    k_sub  = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "sub");
    k_anno = MVM_string_ascii_decode_nt(tc, tc->instance->VMString, "annotations");

    arr = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTArray);

    while (cur_frame != NULL) {
        MVMuint8             *cur_op = count ? cur_frame->return_address : cur_frame->throw_address;
        MVMuint32             offset = cur_op - cur_frame->effective_bytecode;
        MVMBytecodeAnnotation *annot = MVM_bytecode_resolve_annotation(tc, &cur_frame->static_info->body,
                                            offset > 0 ? offset - 1 : 0);
        MVMint32              fshi   = annot ? (MVMint32)annot->filename_string_heap_index : -1;
        char            *line_number = malloc(16);
        snprintf(line_number, 16, "%d", annot ? annot->line_number : 1);

        /* annotations hash will contain "file" and "line" */
        annotations = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTHash);

        /* file */
        if (fshi >= 0 && fshi < cur_frame->static_info->body.cu->body.num_strings)
            value = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type,
                        cur_frame->static_info->body.cu->body.strings[fshi]);
        else
            value = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type,
                        cur_frame->static_info->body.cu->body.filename);
        MVM_repr_bind_key_o(tc, annotations, k_file, value);

        /* line */
        value = (MVMObject *)MVM_string_ascii_decode_nt(tc, tc->instance->VMString, line_number);
        value = MVM_repr_box_str(tc, MVM_hll_current(tc)->str_box_type, (MVMString *)value);
        MVM_repr_bind_key_o(tc, annotations, k_line, value);
        free(line_number);

        /* row will contain "sub" and "annotations" */
        row = MVM_repr_alloc_init(tc, tc->instance->boot_types.BOOTHash);
        MVM_repr_bind_key_o(tc, row, k_sub, cur_frame->code_ref);
        MVM_repr_bind_key_o(tc, row, k_anno, annotations);

        MVM_repr_push_o(tc, arr, row);
        cur_frame = cur_frame->caller;
        while (cur_frame && cur_frame->static_info->body.is_thunk)
            cur_frame = cur_frame->caller;
        count++;
    }

    MVM_gc_root_temp_pop_n(tc, 8);

    return arr;
}