summaryrefslogtreecommitdiff
path: root/vm_eval.c
diff options
context:
space:
mode:
Diffstat (limited to 'vm_eval.c')
-rw-r--r--vm_eval.c170
1 files changed, 96 insertions, 74 deletions
diff --git a/vm_eval.c b/vm_eval.c
index 25fa28d828..807f322854 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -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);