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); }); }
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; }
/* 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)); }
/* 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); } }
/* 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; }
/* 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; }
/* 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; }
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); }
/* 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; }
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"); } }); });
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; }
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]); }
/* 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"); }
/* 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"); }
/* 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; }
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)); }
/* 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; } }
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)); }
/* 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; }
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)); }
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; } }
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)); }
/* 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; }
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); }
/* 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"); }
/* 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"); }
/* 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; }
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; }
/* 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)); }
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); }); });