static void callback_handler(ffi_cif *cif, void *cb_result, void **cb_args, void *cb_data) {
    CallbackInvokeData cid;
    MVMint32 num_roots, i;
    MVMRegister res;
    MVMRegister *args;
    MVMNativeCallback *data = (MVMNativeCallback *)cb_data;
    void           **values = MVM_malloc(sizeof(void *) * (data->cs->arg_count ? data->cs->arg_count : 1));
    unsigned int interval_id;

    /* Locate the MoarVM thread this callback is being run on. */
    MVMThreadContext *tc = MVM_nativecall_find_thread_context(data->instance);

    /* Unblock GC if needed, so this thread can do work. */
    MVMint32 was_blocked = MVM_gc_is_thread_blocked(tc);
    if (was_blocked)
        MVM_gc_mark_thread_unblocked(tc);

    interval_id = MVM_telemetry_interval_start(tc, "nativecall callback handler");

    /* Build a callsite and arguments buffer. */
    args = MVM_malloc(data->num_types * sizeof(MVMRegister));
    num_roots = 0;
    for (i = 1; i < data->num_types; i++) {
        MVMObject *type     = data->types[i];
        MVMint16   typeinfo = data->typeinfos[i];
        switch (typeinfo & MVM_NATIVECALL_ARG_TYPE_MASK) {
            case MVM_NATIVECALL_ARG_CHAR:
                args[i - 1].i64 = *(signed char *)cb_args[i - 1];
                break;
            case MVM_NATIVECALL_ARG_SHORT:
                args[i - 1].i64 = *(signed short *)cb_args[i - 1];
                break;
            case MVM_NATIVECALL_ARG_INT:
                args[i - 1].i64 = *(signed int *)cb_args[i - 1];
                break;
            case MVM_NATIVECALL_ARG_LONG:
                args[i - 1].i64 = *(signed long *)cb_args[i - 1];
                break;
            case MVM_NATIVECALL_ARG_LONGLONG:
                args[i - 1].i64 = *(signed long long *)cb_args[i - 1];
                break;
            case MVM_NATIVECALL_ARG_FLOAT:
                args[i - 1].n64 = *(float *)cb_args[i - 1];
                break;
            case MVM_NATIVECALL_ARG_DOUBLE:
                args[i - 1].n64 = *(double *)cb_args[i - 1];
                break;
            case MVM_NATIVECALL_ARG_ASCIISTR:
            case MVM_NATIVECALL_ARG_UTF8STR:
            case MVM_NATIVECALL_ARG_UTF16STR:
                args[i - 1].o = MVM_nativecall_make_str(tc, type, typeinfo, *(char **)cb_args[i - 1]);
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
                break;
            case MVM_NATIVECALL_ARG_CSTRUCT:
                args[i - 1].o = MVM_nativecall_make_cstruct(tc, type, *(void **)cb_args[i - 1]);
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
                break;
            case MVM_NATIVECALL_ARG_CPPSTRUCT:
                args[i - 1].o = MVM_nativecall_make_cppstruct(tc, type, *(void **)cb_args[i - 1]);
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
                break;
            case MVM_NATIVECALL_ARG_CPOINTER:
                args[i - 1].o = MVM_nativecall_make_cpointer(tc, type, *(void **)cb_args[i - 1]);
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
                break;
            case MVM_NATIVECALL_ARG_CARRAY:
                args[i - 1].o = MVM_nativecall_make_carray(tc, type, *(void **)cb_args[i - 1]);
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
                break;
            case MVM_NATIVECALL_ARG_CUNION:
                args[i - 1].o = MVM_nativecall_make_cunion(tc, type, *(void **)cb_args[i - 1]);
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
                break;
            case MVM_NATIVECALL_ARG_CALLBACK:
                /* TODO: A callback -return- value means that we have a C method
                * that needs to be wrapped similarly to a is native(...) Perl 6
                * sub. */
                /* XXX do something with the function pointer: *(void **)cb_args[i - 1] */
                args[i - 1].o = type;
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
            case MVM_NATIVECALL_ARG_UCHAR:
                args[i - 1].i64 = *(unsigned char *)cb_args[i - 1];
                break;
            case MVM_NATIVECALL_ARG_USHORT:
                args[i - 1].i64 = *(unsigned short *)cb_args[i - 1];
                break;
            case MVM_NATIVECALL_ARG_UINT:
                args[i - 1].i64 = *(unsigned int *)cb_args[i - 1];
                break;
            case MVM_NATIVECALL_ARG_ULONG:
                args[i - 1].i64 = *(unsigned long *)cb_args[i - 1];
                break;
            case MVM_NATIVECALL_ARG_ULONGLONG:
                args[i - 1].i64 = *(unsigned long long *)cb_args[i - 1];
                break;
            default:
                MVM_telemetry_interval_stop(tc, interval_id, "nativecall callback handler failed");
                MVM_exception_throw_adhoc(tc,
                    "Internal error: unhandled libffi callback argument type");
        }
    }

    /* Call into a nested interpreter (since we already are in one). Need to
     * save a bunch of state around each side of this. */
    cid.invokee = data->target;
    cid.args    = args;
    cid.cs      = data->cs;
    {
        MVMuint8 **backup_interp_cur_op         = tc->interp_cur_op;
        MVMuint8 **backup_interp_bytecode_start = tc->interp_bytecode_start;
        MVMRegister **backup_interp_reg_base    = tc->interp_reg_base;
        MVMCompUnit **backup_interp_cu          = tc->interp_cu;
        MVMFrame *backup_cur_frame              = MVM_frame_force_to_heap(tc, tc->cur_frame);
        MVMFrame *backup_thread_entry_frame     = tc->thread_entry_frame;
        void **backup_jit_return_address        = tc->jit_return_address;
        tc->jit_return_address                  = NULL;

        MVMROOT2(tc, backup_cur_frame, backup_thread_entry_frame, {
            MVMuint32 backup_mark                   = MVM_gc_root_temp_mark(tc);
            jmp_buf backup_interp_jump;
            memcpy(backup_interp_jump, tc->interp_jump, sizeof(jmp_buf));

            tc->cur_frame->return_value = &res;
            tc->cur_frame->return_type  = MVM_RETURN_OBJ;
            MVM_interp_run(tc, callback_invoke, &cid);

            tc->interp_cur_op         = backup_interp_cur_op;
            tc->interp_bytecode_start = backup_interp_bytecode_start;
            tc->interp_reg_base       = backup_interp_reg_base;
            tc->interp_cu             = backup_interp_cu;
            tc->cur_frame             = backup_cur_frame;
            tc->current_frame_nr      = backup_cur_frame->sequence_nr;
            tc->thread_entry_frame    = backup_thread_entry_frame;
            tc->jit_return_address    = backup_jit_return_address;
            memcpy(tc->interp_jump, backup_interp_jump, sizeof(jmp_buf));
            MVM_gc_root_temp_mark_reset(tc, backup_mark);
        });
    }
Exemple #2
0
MVMint64 MVM_file_stat(MVMThreadContext *tc, MVMString *filename, MVMint64 status, MVMint32 use_lstat) {
    MVMint64 r = -1;

    switch (status) {

    case MVM_STAT_EXISTS:
        r = MVM_file_exists(tc, filename, use_lstat);
        break;

    case MVM_STAT_FILESIZE: {
        char * const a = MVM_string_utf8_encode_C_string(tc, filename);
        uv_fs_t req;

        if ((use_lstat
                ? uv_fs_lstat(tc->loop, &req, a, NULL)
                :  uv_fs_stat(tc->loop, &req, a, NULL)
            ) < 0) {
            MVM_free(a);
            MVM_exception_throw_adhoc(tc, "Failed to stat file: %s", uv_strerror(req.result));
        }
        MVM_free(a);

        r = req.statbuf.st_size;
        break;
    }

    case MVM_STAT_ISDIR:
        r = (file_info(tc, filename, use_lstat).st_mode & S_IFMT) == S_IFDIR;
        break;

    case MVM_STAT_ISREG:
        r = (file_info(tc, filename, use_lstat).st_mode & S_IFMT) == S_IFREG;
        break;

    case MVM_STAT_ISDEV: {
        const int mode = file_info(tc, filename, use_lstat).st_mode;
#ifdef _WIN32
        r = mode & S_IFMT == S_IFCHR;
#else
        r = (mode & S_IFMT) == S_IFCHR || (mode & S_IFMT) == S_IFBLK;
#endif
        break;
    }

    case MVM_STAT_CREATETIME:
        r = file_info(tc, filename, use_lstat).st_ctim.tv_sec;
        break;

    case MVM_STAT_ACCESSTIME:
        r = file_info(tc, filename, use_lstat).st_atim.tv_sec;
        break;

    case MVM_STAT_MODIFYTIME:
        r = file_info(tc, filename, use_lstat).st_mtim.tv_sec;
        break;

    case MVM_STAT_CHANGETIME:
        r = file_info(tc, filename, use_lstat).st_ctim.tv_sec;
        break;

    /*        case MVM_STAT_BACKUPTIME:         r = -1; break;  */

    case MVM_STAT_UID:
        r = file_info(tc, filename, use_lstat).st_uid;
        break;

    case MVM_STAT_GID:
        r = file_info(tc, filename, use_lstat).st_gid;
        break;

    case MVM_STAT_ISLNK:
        r = (file_info(tc, filename, 1).st_mode & S_IFMT) == S_IFLNK;
        break;

    case MVM_STAT_PLATFORM_DEV:
        r = file_info(tc, filename, use_lstat).st_dev;
        break;

    case MVM_STAT_PLATFORM_INODE:
        r = file_info(tc, filename, use_lstat).st_ino;
        break;

    case MVM_STAT_PLATFORM_MODE:
        r = file_info(tc, filename, use_lstat).st_mode;
        break;

    case MVM_STAT_PLATFORM_NLINKS:
        r = file_info(tc, filename, use_lstat).st_nlink;
        break;

    case MVM_STAT_PLATFORM_DEVTYPE:
        r = file_info(tc, filename, use_lstat).st_rdev;
        break;

    case MVM_STAT_PLATFORM_BLOCKSIZE:
        r = file_info(tc, filename, use_lstat).st_blksize;
        break;

    case MVM_STAT_PLATFORM_BLOCKS:
        r = file_info(tc, filename, use_lstat).st_blocks;
        break;

    default:
        break;
    }

    return r;
}
Exemple #3
0
/* Flushes the file handle. */
static void flush(MVMThreadContext *tc, MVMOSHandle *h){
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
    uv_fs_t req;
    if (uv_fs_fsync(tc->loop, &req, data->fd, NULL) < 0 )
        MVM_exception_throw_adhoc(tc, "Failed to flush filehandle: %s", uv_strerror(req.result));
}
Exemple #4
0
/* copy a file from one to another */
void MVM_file_copy(MVMThreadContext *tc, MVMString *src, MVMString * dest) {
    /* TODO: on Windows we can use the CopyFile API, which is probaly
       more efficient, not to mention easier to use. */
    uv_fs_t req;
    char * a, * b;
    uv_file in_fd = -1, out_fd = -1;
    MVMuint64 size, offset;

    a = MVM_string_utf8_c8_encode_C_string(tc, src);
    b = MVM_string_utf8_c8_encode_C_string(tc, dest);

    /* If the file cannot be stat(), there is little point in going any further. */
    if (uv_fs_stat(tc->loop, &req, a, NULL) < 0)
        goto failure;
    size = req.statbuf.st_size;

    in_fd = uv_fs_open(tc->loop, &req, (const char *)a, O_RDONLY, 0, NULL);
    if (in_fd < 0) {
        goto failure;
    }

    out_fd = uv_fs_open(tc->loop, &req, (const char *)b, O_WRONLY | O_CREAT | O_TRUNC, DEFAULT_MODE, NULL);
    if (out_fd < 0) {
        goto failure;
    }

    offset = 0;
    do {
        /* sendfile() traditionally takes offset as a pointer argument
         * used a both input and output. libuv deviates by making
         * offset an integer and returning the number of bytes
         * sent. So it is necessary to add these explicitly. */
        MVMint64 sent = uv_fs_sendfile(tc->loop, &req, out_fd, in_fd, offset, size - offset, NULL);
        if (sent < 0) {
            goto failure;
        }
        offset += sent;
    } while (offset < size);

    /* Cleanup */
    if(uv_fs_close(tc->loop, &req, in_fd, NULL) < 0) {
        goto failure;
    }
    in_fd = -1;

    if (uv_fs_close(tc->loop, &req, out_fd, NULL) < 0) {
        goto failure;
    }

    MVM_free(b);
    MVM_free(a);
    return;

 failure: {
        /* First get the error, since it may be overwritten further on. */
        const char * error = uv_strerror(req.result);
        /* Basic premise: dealing with all failure cases is hard.
         * So to simplify, a and b are allocated in all conditions.
         * Also to simplify, in_fd are nonnegative if open, negative
         * otherwise. */
        MVM_free(b);
        MVM_free(a);
        /* If any of these fail there is nothing
         * further to do, since we're already failing */
        if (in_fd >= 0)
            uv_fs_close(tc->loop, &req, in_fd, NULL);
        if (out_fd >= 0)
            uv_fs_close(tc->loop, &req, out_fd, NULL);
        /* This function only throws adhoc errors, so the message is for
         * progammer eyes only */
        MVM_exception_throw_adhoc(tc, "Failed to copy file: %s", error);
    }
}
Exemple #5
0
/* Sets the encoding used for string-based I/O. */
static void set_encoding(MVMThreadContext *tc, MVMOSHandle *h, MVMint64 encoding) {
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
    if (data->ds)
        MVM_exception_throw_adhoc(tc, "Too late to change handle encoding");
    data->encoding = encoding;
}
Exemple #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;
}
Exemple #7
0
/* Loads the extension op records. */
static MVMExtOpRecord * deserialize_extop_records(MVMThreadContext *tc, MVMCompUnit *cu, ReaderState *rs) {
    MVMExtOpRecord *extops;
    MVMuint32 num = rs->expected_extops;
    MVMuint8 *pos;
    MVMuint32 i;

    if (num == 0)
        return NULL;

    extops = MVM_calloc(num, sizeof *extops);

    pos = rs->extop_seg;
    for (i = 0; i < num; i++) {
        MVMuint32 name_idx;
        MVMuint16 operand_bytes = 0;
        MVMuint8 *operand_descriptor = extops[i].operand_descriptor;

        /* Read name string index. */
        ensure_can_read(tc, cu, rs, pos, 4);
        name_idx = read_int32(pos, 0);
        pos += 4;

        /* Lookup name string. */
        if (name_idx >= cu->body.num_strings) {
            cleanup_all(tc, rs);
            MVM_exception_throw_adhoc(tc,
                    "String heap index beyond end of string heap");
        }
        extops[i].name = cu->body.strings[name_idx];

        /* Read operand descriptor. */
        ensure_can_read(tc, cu, rs, pos, 8);
        memcpy(operand_descriptor, pos, 8);
        pos += 8;

        /* Validate operand descriptor.
         * TODO: Unify with validation in MVM_ext_register_extop? */
        {
            MVMuint8 j = 0;

            for(; j < 8; j++) {
                MVMuint8 flags = operand_descriptor[j];

                if (!flags)
                    break;

                switch (flags & MVM_operand_rw_mask) {
                    case MVM_operand_literal:
                        goto check_literal;

                    case MVM_operand_read_reg:
                    case MVM_operand_write_reg:
                        operand_bytes += 2;
                        goto check_reg;

                    case MVM_operand_read_lex:
                    case MVM_operand_write_lex:
                        operand_bytes += 4;
                        goto check_reg;

                    default:
                        goto fail;
                }

            check_literal:
                switch (flags & MVM_operand_type_mask) {
                    case MVM_operand_int8:
                        operand_bytes += 1;
                        continue;

                    case MVM_operand_int16:
                        operand_bytes += 2;
                        continue;

                    case MVM_operand_int32:
                        operand_bytes += 4;
                        continue;

                    case MVM_operand_int64:
                        operand_bytes += 8;
                        continue;

                    case MVM_operand_num32:
                        operand_bytes += 4;
                        continue;

                    case MVM_operand_num64:
                        operand_bytes += 8;
                        continue;

                    case MVM_operand_str:
                        operand_bytes += 2;
                        continue;

                    case MVM_operand_coderef:
                        operand_bytes += 2;
                        continue;

                    case MVM_operand_ins:
                    case MVM_operand_callsite:
                    default:
                        goto fail;
                }

            check_reg:
                switch (flags & MVM_operand_type_mask) {
                    case MVM_operand_int8:
                    case MVM_operand_int16:
                    case MVM_operand_int32:
                    case MVM_operand_int64:
                    case MVM_operand_num32:
                    case MVM_operand_num64:
                    case MVM_operand_str:
                    case MVM_operand_obj:
                    case MVM_operand_type_var:
                        continue;

                    default:
                        goto fail;
                }

            fail:
                cleanup_all(tc, rs);
                MVM_exception_throw_adhoc(tc, "Invalid operand descriptor");
            }
        }

        extops[i].operand_bytes = operand_bytes;
    }

    return extops;
}
Exemple #8
0
static void die_no_attrs(MVMThreadContext *tc, const char *repr_name, const char *debug_name) {
    MVM_exception_throw_adhoc(tc,
        "This representation (%s) does not support attribute storage (for type %s)", repr_name, debug_name);
}
Exemple #9
0
/* Gets a representation by ID. */
const MVMREPROps * MVM_repr_get_by_id(MVMThreadContext *tc, MVMuint32 id) {
    if (id >= tc->instance->num_reprs)
        MVM_exception_throw_adhoc(tc, "REPR lookup by invalid ID %" PRIu32, id);

    return tc->instance->repr_list[id]->repr;
}
Exemple #10
0
MVMObject * MVM_nativecall_invoke(MVMThreadContext *tc, MVMObject *res_type,
        MVMObject *site, MVMObject *args) {
    MVMObject  *result = NULL;
    char      **free_strs = NULL;
    void      **free_rws  = NULL;
    MVMint16    num_strs  = 0;
    MVMint16    num_rws   = 0;
    MVMint16    i;

    /* Get native call body, so we can locate the call info. Read out all we
     * shall need, since later we may allocate a result and and move it. */
    MVMNativeCallBody *body = MVM_nativecall_get_nc_body(tc, site);
    MVMint16  num_args    = body->num_args;
    MVMint16 *arg_types   = body->arg_types;
    MVMint16  ret_type    = body->ret_type;
    void     *entry_point = body->entry_point;

    /* Create and set up call VM. */
    DCCallVM *vm = dcNewCallVM(8192);
    dcMode(vm, body->convention);

    /* Process arguments. */
    for (i = 0; i < num_args; i++) {
        MVMObject *value = MVM_repr_at_pos_o(tc, args, i);
        switch (arg_types[i] & MVM_NATIVECALL_ARG_TYPE_MASK) {
            case MVM_NATIVECALL_ARG_CHAR:
                handle_arg("integer", cont_i, DCchar, i64, dcArgChar, MVM_nativecall_unmarshal_char);
                break;
            case MVM_NATIVECALL_ARG_SHORT:
                handle_arg("integer", cont_i, DCshort, i64, dcArgShort, MVM_nativecall_unmarshal_short);
                break;
            case MVM_NATIVECALL_ARG_INT:
                handle_arg("integer", cont_i, DCint, i64, dcArgInt, MVM_nativecall_unmarshal_int);
                break;
            case MVM_NATIVECALL_ARG_LONG:
                handle_arg("integer", cont_i, DClong, i64, dcArgLong, MVM_nativecall_unmarshal_long);
                break;
            case MVM_NATIVECALL_ARG_LONGLONG:
                handle_arg("integer", cont_i, DClonglong, i64, dcArgLongLong, MVM_nativecall_unmarshal_longlong);
                break;
            case MVM_NATIVECALL_ARG_FLOAT:
                handle_arg("number", cont_n, DCfloat, n64, dcArgFloat, MVM_nativecall_unmarshal_float);
                break;
            case MVM_NATIVECALL_ARG_DOUBLE:
                handle_arg("number", cont_n, DCdouble, n64, dcArgDouble, MVM_nativecall_unmarshal_double);
                break;
            case MVM_NATIVECALL_ARG_ASCIISTR:
            case MVM_NATIVECALL_ARG_UTF8STR:
            case MVM_NATIVECALL_ARG_UTF16STR:
                {
                    MVMint16 free = 0;
                    char *str = MVM_nativecall_unmarshal_string(tc, value, arg_types[i], &free);
                    if (free) {
                        if (!free_strs)
                            free_strs = (char**)MVM_malloc(num_args * sizeof(char *));
                        free_strs[num_strs] = str;
                        num_strs++;
                    }
                    dcArgPointer(vm, str);
                }
                break;
            case MVM_NATIVECALL_ARG_CSTRUCT:
                dcArgPointer(vm, MVM_nativecall_unmarshal_cstruct(tc, value));
                break;
            case MVM_NATIVECALL_ARG_CPOINTER:
                dcArgPointer(vm, MVM_nativecall_unmarshal_cpointer(tc, value));
                break;
            case MVM_NATIVECALL_ARG_CARRAY:
                dcArgPointer(vm, MVM_nativecall_unmarshal_carray(tc, value));
                break;
            case MVM_NATIVECALL_ARG_CUNION:
                dcArgPointer(vm, MVM_nativecall_unmarshal_cunion(tc, value));
                break;
            case MVM_NATIVECALL_ARG_VMARRAY:
                dcArgPointer(vm, MVM_nativecall_unmarshal_vmarray(tc, value));
                break;
            case MVM_NATIVECALL_ARG_CALLBACK:
                dcArgPointer(vm, unmarshal_callback(tc, value, body->arg_info[i]));
                break;
            case MVM_NATIVECALL_ARG_UCHAR:
                handle_arg("integer", cont_i, DCuchar, i64, dcArgChar, MVM_nativecall_unmarshal_uchar);
                break;
            case MVM_NATIVECALL_ARG_USHORT:
                handle_arg("integer", cont_i, DCushort, i64, dcArgShort, MVM_nativecall_unmarshal_ushort);
                break;
            case MVM_NATIVECALL_ARG_UINT:
                handle_arg("integer", cont_i, DCuint, i64, dcArgInt, MVM_nativecall_unmarshal_uint);
                break;
            case MVM_NATIVECALL_ARG_ULONG:
                handle_arg("integer", cont_i, DCulong, i64, dcArgLong, MVM_nativecall_unmarshal_ulong);
                break;
            case MVM_NATIVECALL_ARG_ULONGLONG:
                handle_arg("integer", cont_i, DCulonglong, i64, dcArgLongLong, MVM_nativecall_unmarshal_ulonglong);
                break;
            default:
                MVM_exception_throw_adhoc(tc, "Internal error: unhandled dyncall argument type");
        }
    }

    /* Call and process return values. */
    MVMROOT(tc, args, {
    MVMROOT(tc, res_type, {
        switch (ret_type & MVM_NATIVECALL_ARG_TYPE_MASK) {
            case MVM_NATIVECALL_ARG_VOID:
                dcCallVoid(vm, entry_point);
                result = res_type;
                break;
            case MVM_NATIVECALL_ARG_CHAR:
                result = MVM_nativecall_make_int(tc, res_type, dcCallChar(vm, entry_point));
                break;
            case MVM_NATIVECALL_ARG_SHORT:
                result = MVM_nativecall_make_int(tc, res_type, dcCallShort(vm, entry_point));
                break;
            case MVM_NATIVECALL_ARG_INT:
                result = MVM_nativecall_make_int(tc, res_type, dcCallInt(vm, entry_point));
                break;
            case MVM_NATIVECALL_ARG_LONG:
                result = MVM_nativecall_make_int(tc, res_type, dcCallLong(vm, entry_point));
                break;
            case MVM_NATIVECALL_ARG_LONGLONG:
                result = MVM_nativecall_make_int(tc, res_type, dcCallLongLong(vm, entry_point));
                break;
            case MVM_NATIVECALL_ARG_FLOAT:
                result = MVM_nativecall_make_num(tc, res_type, dcCallFloat(vm, entry_point));
                break;
            case MVM_NATIVECALL_ARG_DOUBLE:
                result = MVM_nativecall_make_num(tc, res_type, dcCallDouble(vm, entry_point));
                break;
            case MVM_NATIVECALL_ARG_ASCIISTR:
            case MVM_NATIVECALL_ARG_UTF8STR:
            case MVM_NATIVECALL_ARG_UTF16STR:
                result = MVM_nativecall_make_str(tc, res_type, body->ret_type,
                    (char *)dcCallPointer(vm, entry_point));
                break;
            case MVM_NATIVECALL_ARG_CSTRUCT:
                result = MVM_nativecall_make_cstruct(tc, res_type, dcCallPointer(vm, body->entry_point));
                break;
            case MVM_NATIVECALL_ARG_CPOINTER:
                result = MVM_nativecall_make_cpointer(tc, res_type, dcCallPointer(vm, body->entry_point));
                break;
            case MVM_NATIVECALL_ARG_CARRAY:
                result = MVM_nativecall_make_carray(tc, res_type, dcCallPointer(vm, body->entry_point));
                break;
            case MVM_NATIVECALL_ARG_CUNION:
                result = MVM_nativecall_make_cunion(tc, res_type, dcCallPointer(vm, body->entry_point));
                break;
            case MVM_NATIVECALL_ARG_CALLBACK:
                /* TODO: A callback -return- value means that we have a C method
                * that needs to be wrapped similarly to a is native(...) Perl 6
                * sub. */
                dcCallPointer(vm, body->entry_point);
                result = res_type;
                break;
            case MVM_NATIVECALL_ARG_UCHAR:
                result = MVM_nativecall_make_uint(tc, res_type, (DCuchar)dcCallChar(vm, entry_point));
                break;
            case MVM_NATIVECALL_ARG_USHORT:
                result = MVM_nativecall_make_uint(tc, res_type, (DCushort)dcCallShort(vm, entry_point));
                break;
            case MVM_NATIVECALL_ARG_UINT:
                result = MVM_nativecall_make_uint(tc, res_type, (DCuint)dcCallInt(vm, entry_point));
                break;
            case MVM_NATIVECALL_ARG_ULONG:
                result = MVM_nativecall_make_uint(tc, res_type, (DCulong)dcCallLong(vm, entry_point));
                break;
            case MVM_NATIVECALL_ARG_ULONGLONG:
                result = MVM_nativecall_make_uint(tc, res_type, (DCulonglong)dcCallLongLong(vm, entry_point));
                break;
            default:
                MVM_exception_throw_adhoc(tc, "Internal error: unhandled dyncall return type");
        }
    });
    });
Exemple #11
0
MVM_STATIC_INLINE MVMString * get_string_key(MVMThreadContext *tc, MVMObject *key) {
    if (!key || REPR(key)->ID != MVM_REPR_ID_MVMString || !IS_CONCRETE(key))
        MVM_exception_throw_adhoc(tc, "MVMHash representation requires MVMString keys");
    return (MVMString *)key;
}
Exemple #12
0
static char callback_handler(DCCallback *cb, DCArgs *cb_args, DCValue *cb_result, MVMNativeCallback *data) {
    CallbackInvokeData cid;
    MVMint32 num_roots, i;
    MVMRegister res;

    /* Build a callsite and arguments buffer. */
    MVMThreadContext *tc = data->tc;
    MVMRegister *args = MVM_malloc(data->num_types * sizeof(MVMRegister));
    num_roots = 0;
    for (i = 1; i < data->num_types; i++) {
        MVMObject *type     = data->types[i];
        MVMint16   typeinfo = data->typeinfos[i];
        switch (typeinfo & MVM_NATIVECALL_ARG_TYPE_MASK) {
            case MVM_NATIVECALL_ARG_CHAR:
                args[i - 1].i64 = dcbArgChar(cb_args);
                break;
            case MVM_NATIVECALL_ARG_SHORT:
                args[i - 1].i64 = dcbArgShort(cb_args);
                break;
            case MVM_NATIVECALL_ARG_INT:
                args[i - 1].i64 = dcbArgInt(cb_args);
                break;
            case MVM_NATIVECALL_ARG_LONG:
                args[i - 1].i64 = dcbArgLong(cb_args);
                break;
            case MVM_NATIVECALL_ARG_LONGLONG:
                args[i - 1].i64 = dcbArgLongLong(cb_args);
                break;
            case MVM_NATIVECALL_ARG_FLOAT:
                args[i - 1].n64 = dcbArgFloat(cb_args);
                break;
            case MVM_NATIVECALL_ARG_DOUBLE:
                args[i - 1].n64 = dcbArgDouble(cb_args);
                break;
            case MVM_NATIVECALL_ARG_ASCIISTR:
            case MVM_NATIVECALL_ARG_UTF8STR:
            case MVM_NATIVECALL_ARG_UTF16STR:
                args[i - 1].o = MVM_nativecall_make_str(tc, type, typeinfo,
                    (char *)dcbArgPointer(cb_args));
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
                break;
            case MVM_NATIVECALL_ARG_CSTRUCT:
                args[i - 1].o = MVM_nativecall_make_cstruct(tc, type,
                    dcbArgPointer(cb_args));
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
                break;
            case MVM_NATIVECALL_ARG_CPOINTER:
                args[i - 1].o = MVM_nativecall_make_cpointer(tc, type,
                    dcbArgPointer(cb_args));
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
                break;
            case MVM_NATIVECALL_ARG_CARRAY:
                args[i - 1].o = MVM_nativecall_make_carray(tc, type,
                    dcbArgPointer(cb_args));
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
                break;
            case MVM_NATIVECALL_ARG_CUNION:
                args[i - 1].o = MVM_nativecall_make_cunion(tc, type,
                    dcbArgPointer(cb_args));
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
                break;
            case MVM_NATIVECALL_ARG_CALLBACK:
                /* TODO: A callback -return- value means that we have a C method
                * that needs to be wrapped similarly to a is native(...) Perl 6
                * sub. */
                dcbArgPointer(cb_args);
                args[i - 1].o = type;
                MVM_gc_root_temp_push(tc, (MVMCollectable **)&(args[i - 1].o));
                num_roots++;
            case MVM_NATIVECALL_ARG_UCHAR:
                args[i - 1].i64 = dcbArgUChar(cb_args);
                break;
            case MVM_NATIVECALL_ARG_USHORT:
                args[i - 1].i64 = dcbArgUShort(cb_args);
                break;
            case MVM_NATIVECALL_ARG_UINT:
                args[i - 1].i64 = dcbArgUInt(cb_args);
                break;
            case MVM_NATIVECALL_ARG_ULONG:
                args[i - 1].i64 = dcbArgULong(cb_args);
                break;
            case MVM_NATIVECALL_ARG_ULONGLONG:
                args[i - 1].i64 = dcbArgULongLong(cb_args);
                break;
            default:
                MVM_exception_throw_adhoc(tc,
                    "Internal error: unhandled dyncall callback argument type");
        }
    }

    /* Call into a nested interpreter (since we already are in one). Need to
     * save a bunch of state around each side of this. */
    cid.invokee = data->target;
    cid.args    = args;
    cid.cs      = data->cs;
    {
        MVMuint8 **backup_interp_cur_op         = tc->interp_cur_op;
        MVMuint8 **backup_interp_bytecode_start = tc->interp_bytecode_start;
        MVMRegister **backup_interp_reg_base    = tc->interp_reg_base;
        MVMCompUnit **backup_interp_cu          = tc->interp_cu;
        MVMFrame *backup_cur_frame              = tc->cur_frame;
        MVMFrame *backup_thread_entry_frame     = tc->thread_entry_frame;
        MVMuint32 backup_mark                   = MVM_gc_root_temp_mark(tc);
        jmp_buf backup_interp_jump;
        memcpy(backup_interp_jump, tc->interp_jump, sizeof(jmp_buf));

        tc->cur_frame->return_value = &res;
        tc->cur_frame->return_type  = MVM_RETURN_OBJ;
        MVM_interp_run(tc, &callback_invoke, &cid);

        tc->interp_cur_op         = backup_interp_cur_op;
        tc->interp_bytecode_start = backup_interp_bytecode_start;
        tc->interp_reg_base       = backup_interp_reg_base;
        tc->interp_cu             = backup_interp_cu;
        tc->cur_frame             = backup_cur_frame;
        tc->thread_entry_frame    = backup_thread_entry_frame;
        memcpy(tc->interp_jump, backup_interp_jump, sizeof(jmp_buf));
        MVM_gc_root_temp_mark_reset(tc, backup_mark);
    }

    /* Handle return value. */
    if (res.o) {
        MVMContainerSpec const *contspec = STABLE(res.o)->container_spec;
        if (contspec && contspec->fetch_never_invokes)
            contspec->fetch(data->tc, res.o, &res);
    }
    switch (data->typeinfos[0] & MVM_NATIVECALL_ARG_TYPE_MASK) {
        case MVM_NATIVECALL_ARG_VOID:
            break;
        case MVM_NATIVECALL_ARG_CHAR:
            cb_result->c = MVM_nativecall_unmarshal_char(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_SHORT:
            cb_result->s = MVM_nativecall_unmarshal_short(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_INT:
            cb_result->i = MVM_nativecall_unmarshal_int(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_LONG:
            cb_result->j = MVM_nativecall_unmarshal_long(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_LONGLONG:
            cb_result->l = MVM_nativecall_unmarshal_longlong(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_FLOAT:
            cb_result->f = MVM_nativecall_unmarshal_float(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_DOUBLE:
            cb_result->d = MVM_nativecall_unmarshal_double(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_ASCIISTR:
        case MVM_NATIVECALL_ARG_UTF8STR:
        case MVM_NATIVECALL_ARG_UTF16STR:
            cb_result->Z = MVM_nativecall_unmarshal_string(data->tc, res.o, data->typeinfos[0], NULL);
            break;
        case MVM_NATIVECALL_ARG_CSTRUCT:
            cb_result->p = MVM_nativecall_unmarshal_cstruct(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_CPOINTER:
            cb_result->p = MVM_nativecall_unmarshal_cpointer(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_CARRAY:
            cb_result->p = MVM_nativecall_unmarshal_carray(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_CUNION:
            cb_result->p = MVM_nativecall_unmarshal_cunion(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_VMARRAY:
            cb_result->p = MVM_nativecall_unmarshal_vmarray(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_CALLBACK:
            cb_result->p = unmarshal_callback(data->tc, res.o, data->types[0]);
            break;
        case MVM_NATIVECALL_ARG_UCHAR:
            cb_result->c = MVM_nativecall_unmarshal_uchar(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_USHORT:
            cb_result->s = MVM_nativecall_unmarshal_ushort(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_UINT:
            cb_result->i = MVM_nativecall_unmarshal_uint(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_ULONG:
            cb_result->j = MVM_nativecall_unmarshal_ulong(data->tc, res.o);
            break;
        case MVM_NATIVECALL_ARG_ULONGLONG:
            cb_result->l = MVM_nativecall_unmarshal_ulonglong(data->tc, res.o);
            break;
        default:
            MVM_exception_throw_adhoc(data->tc,
                "Internal error: unhandled dyncall callback return type");
    }

    /* Clean up. */
    MVM_gc_root_temp_pop_n(tc, num_roots);
    MVM_free(args);

    /* Indicate what we're producing as a result. */
    return get_signature_char(data->typeinfos[0]);
}
Exemple #13
0
/* Cannot seek a TTY of named pipe (could fake the forward case, probably). */
void MVM_io_syncstream_seek(MVMThreadContext *tc, MVMOSHandle *h, MVMint64 offset, MVMint64 whence) {
    MVMIOSyncStreamData *data = (MVMIOSyncStreamData *)h->body.data;
    MVM_exception_throw_adhoc(tc, "Cannot seek this kind of handle");
}
Exemple #14
0
/* Cannot truncate a stream. */
void MVM_io_syncstream_truncate(MVMThreadContext *tc, MVMOSHandle *h, MVMint64 bytes) {
    MVM_exception_throw_adhoc(tc, "Cannot truncate this kind of handle");
}
Exemple #15
0
/* Generate bytecode from a spesh graph. */
MVMSpeshCode * MVM_spesh_codegen(MVMThreadContext *tc, MVMSpeshGraph *g) {
    MVMSpeshCode *res;
    MVMSpeshBB   *bb;
    MVMint32      i, hanlen;

    /* Initialize writer state. */
    SpeshWriterState *ws     = MVM_malloc(sizeof(SpeshWriterState));
    ws->bytecode_pos    = 0;
    ws->bytecode_alloc  = 1024;
    ws->bytecode        = MVM_malloc(ws->bytecode_alloc);
    ws->bb_offsets      = MVM_malloc(g->num_bbs * sizeof(MVMint32));
    ws->num_fixups      = 0;
    ws->alloc_fixups    = 64;
    ws->fixup_locations = MVM_malloc(ws->alloc_fixups * sizeof(MVMint32));
    ws->fixup_bbs       = MVM_malloc(ws->alloc_fixups * sizeof(MVMSpeshBB *));
    for (i = 0; i < g->num_bbs; i++)
        ws->bb_offsets[i] = -1;

    /* Create copy of handlers, and -1 all offsets so we can catch missing
     * updates. */
    hanlen = g->num_handlers * sizeof(MVMFrameHandler);
    if (hanlen) {
        ws->handlers = MVM_malloc(hanlen);
        memcpy(ws->handlers, g->handlers, hanlen);
        for (i = 0; i < g->sf->body.num_handlers; i++) {
            ws->handlers[i].start_offset = -1;
            ws->handlers[i].end_offset   = -1;
            ws->handlers[i].goto_offset  = -1;
        }
    }
    else {
        ws->handlers = NULL;
    }

    /* -1 all the deopt targets, so we'll easily catch those that don't get
     * mapped if we try to use them. Same for inlines. */
    for (i = 0; i < g->num_deopt_addrs; i++)
        g->deopt_addrs[i * 2 + 1] = -1;
    for (i = 0; i < g->num_inlines; i++) {
        g->inlines[i].start = -1;
        g->inlines[i].end = -1;
    }

    /* Write out each of the basic blocks, in linear order. Skip the first,
     * dummy, block. */
    bb = g->entry->linear_next;
    while (bb) {
        ws->bb_offsets[bb->idx] = ws->bytecode_pos;
        write_instructions(tc, g, ws, bb);
        bb = bb->linear_next;
    }

    /* Fixup labels we were too early for. */
    for (i = 0; i < ws->num_fixups; i++)
        *((MVMuint32 *)(ws->bytecode + ws->fixup_locations[i])) =
            ws->bb_offsets[ws->fixup_bbs[i]->idx];

    /* Ensure all handlers got fixed up. */
    for (i = 0; i < g->sf->body.num_handlers; i++) {
        if (ws->handlers[i].start_offset == -1 ||
            ws->handlers[i].end_offset   == -1 ||
            ws->handlers[i].goto_offset  == -1)
            MVM_exception_throw_adhoc(tc, "Spesh: failed to fix up handlers (%d, %d, %d)",
                (int)ws->handlers[i].start_offset,
                (int)ws->handlers[i].end_offset,
                (int)ws->handlers[i].goto_offset);
    }

    /* Ensure all inlines got fixed up. */
    for (i = 0; i < g->num_inlines; i++)
        if (g->inlines[i].start == -1 || g->inlines[i].end == -1)
            MVM_exception_throw_adhoc(tc, "Spesh: failed to fix up inline %d", i);

    /* Produce result data structure. */
    res                = MVM_malloc(sizeof(MVMSpeshCode));
    res->bytecode      = ws->bytecode;
    res->bytecode_size = ws->bytecode_pos;
    res->handlers      = ws->handlers;

    /* Cleanup. */
    MVM_free(ws->bb_offsets);
    MVM_free(ws->fixup_locations);
    MVM_free(ws->fixup_bbs);
    MVM_free(ws);

    return res;
}
Exemple #16
0
void MVM_REPR_DEFAULT_SET_STR(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMString *value) {
    MVM_exception_throw_adhoc(tc,
        "This representation (%s) cannot box a native string (for type %s)", st->REPR->name, MVM_6model_get_stable_debug_name(tc, st));
}
Exemple #17
0
/* Writes instructions within a basic block boundary. */
void write_instructions(MVMThreadContext *tc, MVMSpeshGraph *g, SpeshWriterState *ws, MVMSpeshBB *bb) {
    MVMSpeshIns *ins = bb->first_ins;
    while (ins) {
        MVMint32 i;

        /* Process any annotations. */
        MVMSpeshAnn *ann              = ins->annotations;
        MVMSpeshAnn *deopt_one_ann    = NULL;
        MVMSpeshAnn *deopt_all_ann    = NULL;
        MVMSpeshAnn *deopt_inline_ann = NULL;
        while (ann) {
            switch (ann->type) {
            case MVM_SPESH_ANN_FH_START:
                ws->handlers[ann->data.frame_handler_index].start_offset =
                    ws->bytecode_pos;
                break;
            case MVM_SPESH_ANN_FH_END:
                ws->handlers[ann->data.frame_handler_index].end_offset =
                    ws->bytecode_pos;
                break;
            case MVM_SPESH_ANN_FH_GOTO:
                ws->handlers[ann->data.frame_handler_index].goto_offset =
                    ws->bytecode_pos;
                break;
            case MVM_SPESH_ANN_DEOPT_ONE_INS:
                deopt_one_ann = ann;
                break;
            case MVM_SPESH_ANN_DEOPT_ALL_INS:
                deopt_all_ann = ann;
                break;
            case MVM_SPESH_ANN_INLINE_START:
                g->inlines[ann->data.inline_idx].start = ws->bytecode_pos;
                break;
            case MVM_SPESH_ANN_INLINE_END:
                g->inlines[ann->data.inline_idx].end = ws->bytecode_pos;
                break;
            case MVM_SPESH_ANN_DEOPT_INLINE:
                deopt_inline_ann = ann;
                break;
            case MVM_SPESH_ANN_DEOPT_OSR:
                g->deopt_addrs[2 * ann->data.deopt_idx + 1] = ws->bytecode_pos;
                break;
            }
            ann = ann->next;
        }

        if (ins->info->opcode != MVM_SSA_PHI) {
            /* Real instruction, not a phi. Emit opcode. */
            if (ins->info->opcode == (MVMuint16)-1) {
                /* Ext op; resolve. */
                MVMExtOpRecord *extops     = g->sf->body.cu->body.extops;
                MVMuint16       num_extops = g->sf->body.cu->body.num_extops;
                MVMint32        found      = 0;
                for (i = 0; i < num_extops; i++) {
                    if (extops[i].info == ins->info) {
                        write_int16(ws, MVM_OP_EXT_BASE + i);
                        found = 1;
                        break;
                    }
                }
                if (!found)
                    MVM_exception_throw_adhoc(tc, "Spesh: failed to resolve extop in code-gen");
            }
            else {
                /* Core op. */
                write_int16(ws, ins->info->opcode);
            }

            /* Write out operands. */
            for (i = 0; i < ins->info->num_operands; i++) {
                MVMuint8 flags = ins->info->operands[i];
                MVMuint8 rw    = flags & MVM_operand_rw_mask;
                switch (rw) {
                case MVM_operand_read_reg:
                case MVM_operand_write_reg:
                    write_int16(ws, ins->operands[i].reg.orig);
                    break;
                case MVM_operand_read_lex:
                case MVM_operand_write_lex:
                    write_int16(ws, ins->operands[i].lex.idx);
                    write_int16(ws, ins->operands[i].lex.outers);
                    break;
                case MVM_operand_literal: {
                    MVMuint8 type = flags & MVM_operand_type_mask;
                    switch (type) {
                    case MVM_operand_int8:
                        write_int8(ws, ins->operands[i].lit_i8);
                        break;
                    case MVM_operand_int16:
                        write_int16(ws, ins->operands[i].lit_i16);
                        break;
                    case MVM_operand_int32:
                        write_int32(ws, ins->operands[i].lit_i32);
                        break;
                    case MVM_operand_int64:
                        write_int64(ws, ins->operands[i].lit_i64);
                        break;
                    case MVM_operand_num32:
                        write_num32(ws, ins->operands[i].lit_n32);
                        break;
                    case MVM_operand_num64:
                        write_num64(ws, ins->operands[i].lit_n64);
                        break;
                    case MVM_operand_callsite:
                        write_int16(ws, ins->operands[i].callsite_idx);
                        break;
                    case MVM_operand_coderef:
                        write_int16(ws, ins->operands[i].coderef_idx);
                        break;
                    case MVM_operand_str:
                        write_int32(ws, ins->operands[i].lit_str_idx);
                        break;
                    case MVM_operand_ins: {
                        MVMint32 offset = ws->bb_offsets[ins->operands[i].ins_bb->idx];
                        if (offset >= 0) {
                            /* Already know where it is, so just write it. */
                            write_int32(ws, offset);
                        }
                        else {
                            /* Need to fix it up. */
                            if (ws->num_fixups == ws->alloc_fixups) {
                                ws->alloc_fixups *= 2;
                                ws->fixup_locations = MVM_realloc(ws->fixup_locations,
                                    ws->alloc_fixups * sizeof(MVMint32));
                                ws->fixup_bbs = MVM_realloc(ws->fixup_bbs,
                                    ws->alloc_fixups * sizeof(MVMSpeshBB *));
                            }
                            ws->fixup_locations[ws->num_fixups] = ws->bytecode_pos;
                            ws->fixup_bbs[ws->num_fixups]       = ins->operands[i].ins_bb;
                            write_int32(ws, 0);
                            ws->num_fixups++;
                        }
                        break;
                    }
                    case MVM_operand_spesh_slot:
                        write_int16(ws, ins->operands[i].lit_i16);
                        break;
                    default:
                        MVM_exception_throw_adhoc(tc,
                            "Spesh: unknown operand type %d in codegen (op %s)",
                            (int)type, ins->info->name);
                    }
                    }
                    break;
                default:
                    MVM_exception_throw_adhoc(tc, "Spesh: unknown operand type in codegen");
                }
            }
        }

        /* If there was a deopt point annotation, update table. */
        if (deopt_one_ann)
            g->deopt_addrs[2 * deopt_one_ann->data.deopt_idx + 1] = ws->bytecode_pos;
        if (deopt_all_ann)
            g->deopt_addrs[2 * deopt_all_ann->data.deopt_idx + 1] = ws->bytecode_pos;
        if (deopt_inline_ann)
            g->deopt_addrs[2 * deopt_inline_ann->data.deopt_idx + 1] = ws->bytecode_pos;

        ins = ins->next;
    }
}
Exemple #18
0
MVMuint64 MVM_REPR_DEFAULT_ELEMS(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) {
    MVM_exception_throw_adhoc(tc,
        "This representation (%s) does not support elems (for type %s)",
        st->REPR->name, MVM_6model_get_stable_debug_name(tc, st));
}
Exemple #19
0
/* Dissects the bytecode stream and hands back a reader pointing to the
 * various parts of it. */
static ReaderState * dissect_bytecode(MVMThreadContext *tc, MVMCompUnit *cu) {
    MVMCompUnitBody *cu_body = &cu->body;
    ReaderState *rs = NULL;
    MVMuint32 version, offset, size;

    /* Sanity checks. */
    if (cu_body->data_size < HEADER_SIZE)
        MVM_exception_throw_adhoc(tc, "Bytecode stream shorter than header");
    if (memcmp(cu_body->data_start, "MOARVM\r\n", 8) != 0)
        MVM_exception_throw_adhoc(tc, "Bytecode stream corrupt (missing magic string)");
    version = read_int32(cu_body->data_start, 8);
    if (version < MIN_BYTECODE_VERSION)
        MVM_exception_throw_adhoc(tc, "Bytecode stream version too low");
    if (version > MAX_BYTECODE_VERSION)
        MVM_exception_throw_adhoc(tc, "Bytecode stream version too high");

    /* Allocate reader state. */
    rs = MVM_malloc(sizeof(ReaderState));
    memset(rs, 0, sizeof(ReaderState));
    rs->version = version;
    cu->body.bytecode_version = version;

    /* Locate SC dependencies segment. */
    offset = read_int32(cu_body->data_start, SCDEP_HEADER_OFFSET);
    if (offset > cu_body->data_size) {
        cleanup_all(tc, rs);
        MVM_exception_throw_adhoc(tc, "Serialization contexts segment starts after end of stream");
    }
    rs->sc_seg       = cu_body->data_start + offset;
    rs->expected_scs = read_int32(cu_body->data_start, SCDEP_HEADER_OFFSET + 4);

    /* Locate extension ops segment. */
    offset = read_int32(cu_body->data_start, EXTOP_HEADER_OFFSET);
    if (offset > cu_body->data_size) {
        cleanup_all(tc, rs);
        MVM_exception_throw_adhoc(tc, "Extension ops segment starts after end of stream");
    }
    rs->extop_seg       = cu_body->data_start + offset;
    rs->expected_extops = read_int32(cu_body->data_start, EXTOP_HEADER_OFFSET + 4);

    /* Locate frames segment. */
    offset = read_int32(cu_body->data_start, FRAME_HEADER_OFFSET);
    if (offset > cu_body->data_size) {
        cleanup_all(tc, rs);
        MVM_exception_throw_adhoc(tc, "Frames segment starts after end of stream");
    }
    rs->frame_seg       = cu_body->data_start + offset;
    rs->expected_frames = read_int32(cu_body->data_start, FRAME_HEADER_OFFSET + 4);

    /* Locate callsites segment. */
    offset = read_int32(cu_body->data_start, CALLSITE_HEADER_OFFSET);
    if (offset > cu_body->data_size) {
        cleanup_all(tc, rs);
        MVM_exception_throw_adhoc(tc, "Callsites segment starts after end of stream");
    }
    rs->callsite_seg       = cu_body->data_start + offset;
    rs->expected_callsites = read_int32(cu_body->data_start, CALLSITE_HEADER_OFFSET + 4);

    /* Locate strings segment. */
    offset = read_int32(cu_body->data_start, STRING_HEADER_OFFSET);
    if (offset > cu_body->data_size) {
        cleanup_all(tc, rs);
        MVM_exception_throw_adhoc(tc, "Strings segment starts after end of stream");
    }
    rs->string_seg       = cu_body->data_start + offset;
    rs->expected_strings = read_int32(cu_body->data_start, STRING_HEADER_OFFSET + 4);

    /* Get SC data, if any. */
    offset = read_int32(cu_body->data_start, SCDATA_HEADER_OFFSET);
    size = read_int32(cu_body->data_start, SCDATA_HEADER_OFFSET + 4);
    if (offset > cu_body->data_size || offset + size > cu_body->data_size) {
        cleanup_all(tc, rs);
        MVM_exception_throw_adhoc(tc, "Serialized data segment overflows end of stream");
    }
    if (offset) {
        cu_body->serialized = cu_body->data_start + offset;
        cu_body->serialized_size = size;
    }

    /* Locate bytecode segment. */
    offset = read_int32(cu_body->data_start, BYTECODE_HEADER_OFFSET);
    size = read_int32(cu_body->data_start, BYTECODE_HEADER_OFFSET + 4);
    if (offset > cu_body->data_size || offset + size > cu_body->data_size) {
        cleanup_all(tc, rs);
        MVM_exception_throw_adhoc(tc, "Bytecode segment overflows end of stream");
    }
    rs->bytecode_seg  = cu_body->data_start + offset;
    rs->bytecode_size = size;

    /* Locate annotations segment. */
    offset = read_int32(cu_body->data_start, ANNOTATION_HEADER_OFFSET);
    size = read_int32(cu_body->data_start, ANNOTATION_HEADER_OFFSET + 4);
    if (offset > cu_body->data_size || offset + size > cu_body->data_size) {
        cleanup_all(tc, rs);
        MVM_exception_throw_adhoc(tc, "Annotation segment overflows end of stream");
    }
    rs->annotation_seg  = cu_body->data_start + offset;
    rs->annotation_size = size;

    /* Locate HLL name */
    rs->hll_str_idx = read_int32(cu_body->data_start, HLL_NAME_HEADER_OFFSET);

    /* Locate special frame indexes. Note, they are 0 for none, and the
     * index + 1 if there is one. */
    rs->main_frame = read_int32(cu_body->data_start, SPECIAL_FRAME_HEADER_OFFSET);
    rs->load_frame = read_int32(cu_body->data_start, SPECIAL_FRAME_HEADER_OFFSET + 4);
    rs->deserialize_frame = read_int32(cu_body->data_start, SPECIAL_FRAME_HEADER_OFFSET + 8);
    if (rs->main_frame > rs->expected_frames
            || rs->load_frame > rs->expected_frames
            || rs->deserialize_frame > rs->expected_frames) {
        MVM_exception_throw_adhoc(tc, "Special frame index out of bounds");
    }

    return rs;
}
Exemple #20
0
MVMuint64 MVM_REPR_DEFAULT_GET_UINT(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data) {
    MVM_exception_throw_adhoc(tc,
        "This representation (%s) cannot unbox to an unsigned native int (for type %s)", st->REPR->name, MVM_6model_get_stable_debug_name(tc, st));
}
Exemple #21
0
void MVM_coerce_istrue(MVMThreadContext *tc, MVMObject *obj, MVMRegister *res_reg,
        MVMuint8 *true_addr, MVMuint8 *false_addr, MVMuint8 flip) {
    MVMint64 result;
    if (MVM_is_null(tc, obj)) {
        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: {
                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;
                }
                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) || MVM_string_graphs(tc, 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_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;
    }
}
Exemple #22
0
void * MVM_REPR_DEFAULT_GET_BOXED_REF(MVMThreadContext *tc, MVMSTable *st, MVMObject *root, void *data, MVMuint32 repr_id) {
    MVM_exception_throw_adhoc(tc,
        "This representation (%s) cannot unbox to other types (for type %s)", st->REPR->name, MVM_6model_get_stable_debug_name(tc, st));
}
Exemple #23
0
/* Setting and clearing mutex to release on exception throw. */
void MVM_tc_set_ex_release_mutex(MVMThreadContext *tc, uv_mutex_t *mutex) {
    if (tc->ex_release_mutex)
        MVM_exception_throw_adhoc(tc, "Internal error: multiple ex_release_mutex");
    tc->ex_release_mutex = mutex;
}
Exemple #24
0
static void die_no_pos(MVMThreadContext *tc, const char *repr_name, const char *debug_name) {
    MVM_exception_throw_adhoc(tc,
        "This representation (%s) does not support positional access (for type %s)", repr_name, debug_name);
}
Exemple #25
0
/* Invocation protocol handler. */
static void invoke_handler(MVMThreadContext *tc, MVMObject *invokee, MVMCallsite *callsite, MVMRegister *args) {
    MVM_exception_throw_adhoc(tc, "Cannot invoke comp unit object");
}
Exemple #26
0
/* Default invoke function on STables; for non-invokable objects */
void MVM_6model_invoke_default(MVMThreadContext *tc, MVMObject *invokee, MVMCallsite *callsite, MVMRegister *args) {
    MVM_exception_throw_adhoc(tc, "non-invokable object is non-invokable");
}
Exemple #27
0
/* If a is composed of strands, create a descent record, initialize it, and
 * descend into the substring. If result is set when it returns, return. 
 * If a is a real string, grab some codepoints from a string, compare
 * with codepoints from the other buffer. If different, set result nonzero and
 * it will fast return. Otherwise, reset buffer indexes
 * to zero. Loop if `needed` for the current string is nonzero. */
static void compare_descend(MVMThreadContext *tc, MVMCompareDescentState *st,
        MVMuint8 *result, MVMCompareCursor *orig) {
    /* while we still need to compare some things */
    while (st->needed) {
        /* pick a tree from which to get more characters. */
        MVMCompareCursor *c = st->cursora->owns_buffer ? st->cursorb : st->cursora;
        
        if (*c->gaps) {
            (*c->gaps)--;
            if (c->isa) { st->cursora = c->parent; }
            else { st->cursorb = c->parent; }
            return;
        }
        
        /* get some characters, comparing as they're found */
        switch(STR_FLAGS(c->string)) {
            case MVM_STRING_TYPE_INT32: {
                descend_sized(int32s, uint8s, MVMCodepoint32, MVMCodepoint8, cp32, cp8)
            }
            case MVM_STRING_TYPE_UINT8: {
                descend_sized(uint8s, int32s, MVMCodepoint8, MVMCodepoint32, cp8, cp32)
            }
            case MVM_STRING_TYPE_ROPE: {
                MVMStrand *strand;
                MVMStringIndex next_compare_offset, child_length, child_idx, child_end_idx;
                if (c->owns_buffer) { /* first time we've seen this rope */
                    c->strand_idx = find_strand_index(c->string, c->string_idx);
                    c->owns_buffer = 0;
                }
                if (c->string_idx == c->end_idx) {
                    if (c->isa) { st->cursora = c->parent; }
                    else { st->cursorb = c->parent; }
                    if (orig == c)
                        return;
                    (*c->gaps)++;
                    continue;
                }
                strand = c->string->body.strands + c->strand_idx;
                child_idx = c->string_idx - strand->compare_offset;
                next_compare_offset = (strand + 1)->compare_offset;
                child_length = next_compare_offset - strand->compare_offset;
                child_length = child_length > c->end_idx - c->string_idx
                    ? c->end_idx - c->string_idx : child_length;
                child_end_idx = child_idx + child_length;
                c->string_idx += child_length;
                c->strand_idx++;
                {
                    MVMCompareCursor child = { strand->string, c, c->gaps, child_idx,
                        child_end_idx, 0, IS_ROPE(strand->string), c->isa };
                    if (c->isa) { st->cursora = &child; } else { st->cursorb = &child; }
                    compare_descend(tc, st, result, &child);
                }
                if (*result) return;
                continue;
            }
            default:
                MVM_exception_throw_adhoc(tc, "internal string corruption");
        }
    }
    *result = 2;
}
Exemple #28
0
MVMObject * MVM_thread_start(MVMThreadContext *tc, MVMObject *invokee, MVMObject *result_type) {
    int status;
    ThreadStart *ts;
    MVMObject *child_obj;

    /* Create a thread object to wrap it up in. */
    MVM_gc_root_temp_push(tc, (MVMCollectable **)&invokee);
    child_obj = REPR(result_type)->allocate(tc, STABLE(result_type));
    MVM_gc_root_temp_pop(tc);
    if (REPR(child_obj)->ID == MVM_REPR_ID_MVMThread) {
        MVMThread *child = (MVMThread *)child_obj;
        MVMThread * volatile *threads;

        /* Create a new thread context and set it up. */
        MVMThreadContext *child_tc = MVM_tc_create(tc->instance);
        child->body.tc = child_tc;
        MVM_ASSIGN_REF(tc, child, child->body.invokee, invokee);
        child_tc->thread_obj = child;
        child_tc->thread_id = MVM_incr(&tc->instance->next_user_thread_id);

        /* Create the thread. Note that we take a reference to the current frame,
         * since it must survive to be the dynamic scope of where the thread was
         * started, and there's no promises that the thread won't start before
         * the code creating the thread returns. The count is decremented when
         * the thread is done. */
        ts = malloc(sizeof(ThreadStart));
        ts->tc = child_tc;
        ts->caller = MVM_frame_inc_ref(tc, tc->cur_frame);
        ts->thread_obj = child_obj;

        /* push this to the *child* tc's temp roots. */
        MVM_gc_root_temp_push(child_tc, (MVMCollectable **)&ts->thread_obj);

        /* Signal to the GC we have a childbirth in progress. The GC
         * will null it for us. */
        MVM_gc_mark_thread_blocked(child_tc);
        MVM_ASSIGN_REF(tc, tc->thread_obj, tc->thread_obj->body.new_child, child);

        /* push to starting threads list */
        threads = &tc->instance->threads;
        do {
            MVMThread *curr = *threads;
            MVM_ASSIGN_REF(tc, child, child->body.next, curr);
        } while (MVM_casptr(threads, child->body.next, child) != child->body.next);


        status = uv_thread_create(&child->body.thread, &start_thread, ts);

        if (status < 0) {
            MVM_panic(MVM_exitcode_compunit, "Could not spawn thread: errorcode %d", status);
        }

        /* need to run the GC to clear our new_child field in case we try
         * try to launch another thread before the GC runs and before the
         * thread starts. */
        GC_SYNC_POINT(tc);
    }
    else {
        MVM_exception_throw_adhoc(tc,
            "Thread result type must have representation MVMThread");
    }

    return child_obj;
}
Exemple #29
0
/* Truncates the file handle. */
static void truncatefh(MVMThreadContext *tc, MVMOSHandle *h, MVMint64 bytes) {
    MVMIOFileData *data = (MVMIOFileData *)h->body.data;
    uv_fs_t req;
    if(uv_fs_ftruncate(tc->loop, &req, data->fd, bytes, NULL) < 0 )
        MVM_exception_throw_adhoc(tc, "Failed to truncate filehandle: %s", uv_strerror(req.result));
}
Exemple #30
0
MVMObject * MVM_radix(MVMThreadContext *tc, MVMint64 radix, MVMString *str, MVMint64 offset, MVMint64 flag) {
    MVMObject *result;
    MVMint64 zvalue = 0;
    MVMint64 zbase  = 1;
    MVMint64 chars  = MVM_string_graphs(tc, str);
    MVMint64 value  = zvalue;
    MVMint64 base   = zbase;
    MVMint64   pos  = -1;
    MVMuint16  neg  = 0;
    MVMint64   ch;

    if (radix > 36) {
        MVM_exception_throw_adhoc(tc, "Cannot convert radix of %"PRId64" (max 36)", radix);
    }

    ch = (offset < chars) ? MVM_string_get_grapheme_at_nocheck(tc, str, offset) : 0;
    if ((flag & 0x02) && (ch == '+' || ch == '-')) {
        neg = (ch == '-');
        offset++;
        ch = (offset < chars) ? MVM_string_get_grapheme_at_nocheck(tc, str, offset) : 0;
    }

    while (offset < chars) {
        if (ch >= '0' && ch <= '9') ch = ch - '0'; /* fast-path for ASCII 0..9 */
        else if (ch >= 'a' && ch <= 'z') ch = ch - 'a' + 10;
        else if (ch >= 'A' && ch <= 'Z') ch = ch - 'A' + 10;
        else if (ch >= 0xFF21 && ch <= 0xFF3A) ch = ch - 0xFF21 + 10; /* uppercase fullwidth */
        else if (ch >= 0xFF41 && ch <= 0xFF5A) ch = ch - 0xFF41 + 10; /* lowercase fullwidth */
        else if (ch > 0 && MVM_unicode_codepoint_has_property_value(tc, ch, MVM_UNICODE_PROPERTY_GENERAL_CATEGORY, 
                MVM_unicode_cname_to_property_value_code(tc, MVM_UNICODE_PROPERTY_GENERAL_CATEGORY, STR_WITH_LEN("Nd")))) {
            /* As of Unicode 6.0.0, we know that Nd category numerals are within
             * the range 0..9
             */

            /* the string returned for NUMERIC_VALUE contains a floating point
             * value, so atoi will stop on the . in the string. This is fine
             * though, since we'd have to truncate the float regardless.
             */
            ch = atoi(MVM_unicode_codepoint_get_property_cstr(tc, ch, MVM_UNICODE_PROPERTY_NUMERIC_VALUE));
        }
        else break;
        if (ch >= radix) break;
        zvalue = zvalue * radix + ch;
        zbase = zbase * radix;
        offset++; pos = offset;
        if (ch != 0 || !(flag & 0x04)) { value=zvalue; base=zbase; }
        if (offset >= chars) break;
        ch = MVM_string_get_grapheme_at_nocheck(tc, str, offset);
        if (ch != '_') continue;
        offset++;
        if (offset >= chars) break;
        ch = MVM_string_get_grapheme_at_nocheck(tc, str, offset);
    }

    if (neg || flag & 0x01) { value = -value; }

    /* initialize the object */
    result = MVM_repr_alloc_init(tc, MVM_hll_current(tc)->slurpy_array_type);
    MVMROOT(tc, result, {
        MVMObject *box_type = MVM_hll_current(tc)->int_box_type;
        MVMROOT(tc, box_type, {
            MVMObject *boxed = MVM_repr_box_int(tc, box_type, value);
            MVM_repr_push_o(tc, result, boxed);
            boxed = MVM_repr_box_int(tc, box_type, base);
            MVM_repr_push_o(tc, result, boxed);
            boxed = MVM_repr_box_int(tc, box_type, pos);
            MVM_repr_push_o(tc, result, boxed);
        });
    });