diff options
Diffstat (limited to 'vm_eval.c')
-rw-r--r-- | vm_eval.c | 170 |
1 files changed, 96 insertions, 74 deletions
@@ -29,15 +29,6 @@ static VALUE rb_eUncaughtThrow; static ID id_result, id_tag, id_value; #define id_mesg idMesg -typedef enum call_type { - CALL_PUBLIC, - CALL_FCALL, - CALL_VCALL, - CALL_PUBLIC_KW, - CALL_FCALL_KW, - CALL_TYPE_MAX -} call_type; - static VALUE send_internal(int argc, const VALUE *argv, VALUE recv, call_type scope); static VALUE vm_call0_body(rb_execution_context_t* ec, struct rb_calling_info *calling, const VALUE *argv); @@ -224,7 +215,12 @@ vm_call0_body(rb_execution_context_t *ec, struct rb_calling_info *calling, const *reg_cfp->sp++ = argv[i]; } - vm_call_iseq_setup(ec, reg_cfp, calling); + if (ISEQ_BODY(def_iseq_ptr(vm_cc_cme(cc)->def))->param.flags.forwardable) { + vm_call_iseq_fwd_setup(ec, reg_cfp, calling); + } + else { + vm_call_iseq_setup(ec, reg_cfp, calling); + } VM_ENV_FLAGS_SET(ec->cfp->ep, VM_FRAME_FLAG_FINISH); return vm_exec(ec); // CHECK_INTS in this function } @@ -403,71 +399,53 @@ NORETURN(static void uncallable_object(VALUE recv, ID mid)); static inline const rb_callable_method_entry_t *rb_search_method_entry(VALUE recv, ID mid); static inline enum method_missing_reason rb_method_call_status(rb_execution_context_t *ec, const rb_callable_method_entry_t *me, call_type scope, VALUE self); -static const struct rb_callcache * -cc_new(VALUE klass, ID mid, int argc, const rb_callable_method_entry_t *cme) -{ - const struct rb_callcache *cc = NULL; - - RB_VM_LOCK_ENTER(); - { - struct rb_class_cc_entries *ccs; - struct rb_id_table *cc_tbl = RCLASS_CC_TBL(klass); - VALUE ccs_data; - - if (rb_id_table_lookup(cc_tbl, mid, &ccs_data)) { - // ok - ccs = (struct rb_class_cc_entries *)ccs_data; - } - else { - ccs = vm_ccs_create(klass, cc_tbl, mid, cme); - } - - for (int i=0; i<ccs->len; i++) { - cc = ccs->entries[i].cc; - if (vm_cc_cme(cc) == cme) { - break; - } - cc = NULL; - } - - if (cc == NULL) { - const struct rb_callinfo *ci = vm_ci_new(mid, 0, argc, NULL); // TODO: proper ci - cc = vm_cc_new(klass, cme, vm_call_general, cc_type_normal); - METHOD_ENTRY_CACHED_SET((struct rb_callable_method_entry_struct *)cme); - vm_ccs_push(klass, ccs, ci, cc); - } - } - RB_VM_LOCK_LEAVE(); - - return cc; -} - static VALUE gccct_hash(VALUE klass, ID mid) { return (klass >> 3) ^ (VALUE)mid; } -NOINLINE(static const struct rb_callcache *gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, ID mid, int argc, unsigned int index)); +NOINLINE(static const struct rb_callcache *gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, unsigned int index, const struct rb_callinfo * ci)); static const struct rb_callcache * -gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, ID mid, int argc, unsigned int index) +gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, unsigned int index, const struct rb_callinfo *ci) { - const rb_callable_method_entry_t *cme = rb_callable_method_entry(klass, mid); - const struct rb_callcache *cc; + struct rb_call_data cd = { + .ci = ci, + .cc = NULL + }; - if (cme != NULL) { - cc = cc_new(klass, mid, argc, cme); - } - else { - cc = NULL; - } + vm_search_method_slowpath0(vm->self, &cd, klass); - return vm->global_cc_cache_table[index] = cc; + return vm->global_cc_cache_table[index] = cd.cc; +} + +static void +scope_to_ci(call_type scope, ID mid, int argc, struct rb_callinfo *ci) +{ + int flags = 0; + + switch(scope) { + case CALL_PUBLIC: + break; + case CALL_FCALL: + flags |= VM_CALL_FCALL; + break; + case CALL_VCALL: + flags |= VM_CALL_VCALL; + break; + case CALL_PUBLIC_KW: + flags |= VM_CALL_KWARG; + break; + case CALL_FCALL_KW: + flags |= (VM_CALL_KWARG | VM_CALL_FCALL); + break; + } + *ci = VM_CI_ON_STACK(mid, flags, argc, NULL); } static inline const struct rb_callcache * -gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc) +gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, const struct rb_callinfo *ci) { VALUE klass; @@ -502,7 +480,7 @@ gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, int argc) } RB_DEBUG_COUNTER_INC(gccct_miss); - return gccct_method_search_slowpath(vm, klass, mid, argc, index); + return gccct_method_search_slowpath(vm, klass, index, ci); } /** @@ -543,7 +521,10 @@ rb_call0(rb_execution_context_t *ec, break; } - const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, argc); + struct rb_callinfo ci; + scope_to_ci(scope, mid, argc, &ci); + + const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, &ci); if (scope == CALL_PUBLIC) { RB_DEBUG_COUNTER_INC(call0_public); @@ -745,13 +726,6 @@ rb_check_funcall_with_hook_kw(VALUE recv, ID mid, int argc, const VALUE *argv, return rb_vm_call_kw(ec, recv, mid, argc, argv, me, kw_splat); } -VALUE -rb_check_funcall_with_hook(VALUE recv, ID mid, int argc, const VALUE *argv, - rb_check_funcall_hook *hook, VALUE arg) -{ - return rb_check_funcall_with_hook_kw(recv, mid, argc, argv, hook, arg, RB_NO_KEYWORDS); -} - const char * rb_type_str(enum ruby_value_type type) { @@ -1060,7 +1034,11 @@ static inline VALUE rb_funcallv_scope(VALUE recv, ID mid, int argc, const VALUE *argv, call_type scope) { rb_execution_context_t *ec = GET_EC(); - const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, argc); + + struct rb_callinfo ci; + scope_to_ci(scope, mid, argc, &ci); + + const struct rb_callcache *cc = gccct_method_search(ec, recv, mid, &ci); VALUE self = ec->cfp->self; if (LIKELY(cc) && @@ -1573,6 +1551,37 @@ rb_block_call_kw(VALUE obj, ID mid, int argc, const VALUE * argv, return rb_iterate_internal(iterate_method, (VALUE)&arg, bl_proc, data2); } +/* + * A flexible variant of rb_block_call and rb_block_call_kw. + * This function accepts flags: + * + * RB_NO_KEYWORDS, RB_PASS_KEYWORDS, RB_PASS_CALLED_KEYWORDS: + * Works as the same as rb_block_call_kw. + * + * RB_BLOCK_NO_USE_PACKED_ARGS: + * The given block ("bl_proc") does not use "yielded_arg" of rb_block_call_func_t. + * Instead, the block accesses the yielded arguments via "argc" and "argv". + * This flag allows the called method to yield arguments without allocating an Array. + */ +VALUE +rb_block_call2(VALUE obj, ID mid, int argc, const VALUE *argv, + rb_block_call_func_t bl_proc, VALUE data2, long flags) +{ + struct iter_method_arg arg; + + arg.obj = obj; + arg.mid = mid; + arg.argc = argc; + arg.argv = argv; + arg.kw_splat = flags & 1; + + struct vm_ifunc *ifunc = rb_vm_ifunc_proc_new(bl_proc, (void *)data2); + if (flags & RB_BLOCK_NO_USE_PACKED_ARGS) + ifunc->flags |= IFUNC_YIELD_OPTIMIZABLE; + + return rb_iterate0(iterate_method, (VALUE)&arg, ifunc, GET_EC()); +} + VALUE rb_lambda_call(VALUE obj, ID mid, int argc, const VALUE *argv, rb_block_call_func_t bl_proc, int min_argc, int max_argc, @@ -1649,6 +1658,10 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, const rb_iseq_t *const parent = vm_block_iseq(base_block); const rb_iseq_t *iseq = parent; VALUE name = rb_fstring_lit("<compiled>"); + + // Conditionally enable coverage depending on the current mode: + int coverage_enabled = ((rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) != 0) ? 1 : 0; + if (!fname) { fname = rb_source_location(&line); } @@ -1658,10 +1671,12 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, } else { fname = get_eval_default_path(); + coverage_enabled = 0; } pm_parse_result_t result = { 0 }; pm_options_line_set(&result.options, line); + result.node.coverage_enabled = coverage_enabled; // Cout scopes, one for each parent iseq, plus one for our local scope int scopes_count = 0; @@ -1704,7 +1719,9 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, // Add our empty local scope at the very end of the array for our eval // scope's locals. pm_options_scope_init(&result.options.scopes[scopes_count], 0); - VALUE error = pm_parse_string(&result, src, fname); + + VALUE script_lines; + VALUE error = pm_parse_string(&result, src, fname, ruby_vm_keep_script_lines ? &script_lines : NULL); // If the parse failed, clean up and raise. if (error != Qnil) { @@ -1723,6 +1740,7 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, RUBY_ASSERT(parent_scope != NULL); pm_options_scope_t *options_scope = &result.options.scopes[scopes_count - scopes_index - 1]; + parent_scope->coverage_enabled = coverage_enabled; parent_scope->parser = &result.parser; parent_scope->index_lookup_table = st_init_numtable(); @@ -1773,6 +1791,7 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const VALUE parser = rb_parser_new(); const rb_iseq_t *const parent = vm_block_iseq(base_block); rb_iseq_t *iseq = NULL; + VALUE ast_value; rb_ast_t *ast; int isolated_depth = 0; @@ -1810,10 +1829,13 @@ eval_make_iseq(VALUE src, VALUE fname, int line, rb_parser_set_context(parser, parent, FALSE); if (ruby_vm_keep_script_lines) rb_parser_set_script_lines(parser); - ast = rb_parser_compile_string_path(parser, fname, src, line); + ast_value = rb_parser_compile_string_path(parser, fname, src, line); + + ast = rb_ruby_ast_data_get(ast_value); + if (ast->body.root) { ast->body.coverage_enabled = coverage_enabled; - iseq = rb_iseq_new_eval(&ast->body, + iseq = rb_iseq_new_eval(ast_value, ISEQ_BODY(parent)->location.label, fname, Qnil, line, parent, isolated_depth); |