diff options
Diffstat (limited to 'vm_eval.c')
| -rw-r--r-- | vm_eval.c | 302 |
1 files changed, 200 insertions, 102 deletions
@@ -1,6 +1,6 @@ /********************************************************************** - vm_eval.c - + vm_eval.c - Included into vm.c. $Author$ created at: Sat May 24 16:02:32 JST 2008 @@ -57,7 +57,7 @@ static inline VALUE vm_call0_cc(rb_execution_context_t *ec, VALUE recv, ID id, i VALUE rb_vm_call0(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE *argv, const rb_callable_method_entry_t *cme, int kw_splat) { - const struct rb_callcache cc = VM_CC_ON_STACK(Qfalse, vm_call_general, {{ 0 }}, cme); + const struct rb_callcache cc = VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, cme); return vm_call0_cc(ec, recv, id, argc, argv, &cc, kw_splat); } @@ -104,7 +104,7 @@ vm_call0_cc(rb_execution_context_t *ec, VALUE recv, ID id, int argc, const VALUE static VALUE vm_call0_cme(rb_execution_context_t *ec, struct rb_calling_info *calling, const VALUE *argv, const rb_callable_method_entry_t *cme) { - calling->cc = &VM_CC_ON_STACK(Qfalse, vm_call_general, {{ 0 }}, cme); + calling->cc = &VM_CC_ON_STACK(Qundef, vm_call_general, {{ 0 }}, cme); return vm_call0_body(ec, calling, argv); } @@ -381,7 +381,7 @@ stack_check(rb_execution_context_t *ec) if (!rb_ec_raised_p(ec, RAISED_STACKOVERFLOW) && rb_ec_stack_check(ec)) { rb_ec_raised_set(ec, RAISED_STACKOVERFLOW); - rb_ec_stack_overflow(ec, FALSE); + rb_ec_stack_overflow(ec, 0); } } @@ -400,9 +400,9 @@ static inline const rb_callable_method_entry_t *rb_search_method_entry(VALUE rec 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 VALUE -gccct_hash(VALUE klass, ID mid) +gccct_hash(VALUE klass, VALUE box_value, ID mid) { - return (klass >> 3) ^ (VALUE)mid; + return ((klass ^ box_value) >> 3) ^ (VALUE)mid; } NOINLINE(static const struct rb_callcache *gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, unsigned int index, const struct rb_callinfo * ci)); @@ -417,6 +417,9 @@ gccct_method_search_slowpath(rb_vm_t *vm, VALUE klass, unsigned int index, const vm_search_method_slowpath0(vm->self, &cd, klass); + if (UNLIKELY(!vm->global_cc_cache_table_used)) { + vm->global_cc_cache_table_used = true; + } return vm->global_cc_cache_table[index] = cd.cc; } @@ -447,7 +450,8 @@ scope_to_ci(call_type scope, ID mid, int argc, struct rb_callinfo *ci) static inline const struct rb_callcache * gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, const struct rb_callinfo *ci) { - VALUE klass; + VALUE klass, box_value; + const rb_box_t *box = rb_current_box(); if (!SPECIAL_CONST_P(recv)) { klass = RBASIC_CLASS(recv); @@ -457,8 +461,14 @@ gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, const struct klass = CLASS_OF(recv); } + if (BOX_USER_P(box)) { + box_value = box->box_object; + } + else { + box_value = 0; + } // search global method cache - unsigned int index = (unsigned int)(gccct_hash(klass, mid) % VM_GLOBAL_CC_CACHE_TABLE_SIZE); + unsigned int index = (unsigned int)(gccct_hash(klass, box_value, mid) % VM_GLOBAL_CC_CACHE_TABLE_SIZE); rb_vm_t *vm = rb_ec_vm_ptr(ec); const struct rb_callcache *cc = vm->global_cc_cache_table[index]; @@ -483,6 +493,18 @@ gccct_method_search(rb_execution_context_t *ec, VALUE recv, ID mid, const struct return gccct_method_search_slowpath(vm, klass, index, ci); } +VALUE +rb_gccct_clear_table(void) +{ + rb_vm_t *vm = GET_VM(); + if (vm->global_cc_cache_table_used) { + const struct rb_callcache **const table = vm->global_cc_cache_table; + MEMZERO(table, struct rb_callcache *, VM_GLOBAL_CC_CACHE_TABLE_SIZE); + vm->global_cc_cache_table_used = false; + } + return Qnil; +} + /** * @internal * calls the specified method. @@ -1361,6 +1383,17 @@ rb_yield(VALUE val) } } +VALUE +rb_ec_yield(rb_execution_context_t *ec, VALUE val) +{ + if (UNDEF_P(val)) { + return vm_yield(ec, 0, NULL, RB_NO_KEYWORDS); + } + else { + return vm_yield(ec, 1, &val, RB_NO_KEYWORDS); + } +} + #undef rb_yield_values VALUE rb_yield_values(int n, ...) @@ -1504,13 +1537,6 @@ rb_iterate_internal(VALUE (* it_proc)(VALUE), VALUE data1, GET_EC()); } -VALUE -rb_iterate(VALUE (* it_proc)(VALUE), VALUE data1, - rb_block_call_func_t bl_proc, VALUE data2) -{ - return rb_iterate_internal(it_proc, data1, bl_proc, data2); -} - struct iter_method_arg { VALUE obj; ID mid; @@ -1651,6 +1677,24 @@ get_eval_default_path(void) return eval_default_path; } +static inline int +compute_isolated_depth_from_ep(const VALUE *ep) +{ + int depth = 1; + while (1) { + if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ISOLATED)) return depth; + if (VM_ENV_LOCAL_P(ep)) return 0; + ep = VM_ENV_PREV_EP(ep); + depth++; + } +} + +static inline int +compute_isolated_depth_from_block(const struct rb_block *blk) +{ + return compute_isolated_depth_from_ep(vm_block_ep(blk)); +} + static const rb_iseq_t * pm_eval_make_iseq(VALUE src, VALUE fname, int line, const struct rb_block *base_block) @@ -1659,8 +1703,8 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, 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; + int isolated_depth = compute_isolated_depth_from_block(base_block); if (!fname) { fname = rb_source_location(&line); @@ -1674,16 +1718,17 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, coverage_enabled = 0; } - pm_parse_result_t result = { 0 }; - pm_options_line_set(&result.options, line); + pm_parse_result_t result; + pm_parse_result_init(&result); + 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 + // Count scopes, one for each parent iseq, plus one for our local scope int scopes_count = 0; do { scopes_count++; } while ((iseq = ISEQ_BODY(iseq)->parent_iseq)); - pm_options_scopes_init(&result.options, scopes_count + 1); + pm_options_scopes_init(result.options, scopes_count + 1); // Walk over the scope tree, adding known locals at the correct depths. The // scope array should be deepest -> shallowest. so lower indexes in the @@ -1705,19 +1750,19 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, VALUE iseq_value = (VALUE)iseq; int locals_count = ISEQ_BODY(iseq)->local_table_size; - pm_options_scope_t *options_scope = &result.options.scopes[scopes_count - scopes_index - 1]; + pm_options_scope_t *options_scope = pm_options_scope_mut(result.options, scopes_count - scopes_index - 1); pm_options_scope_init(options_scope, locals_count); uint8_t forwarding = PM_OPTIONS_SCOPE_FORWARDING_NONE; for (int local_index = 0; local_index < locals_count; local_index++) { - pm_string_t *scope_local = &options_scope->locals[local_index]; + pm_string_t *scope_local = pm_options_scope_local_mut(options_scope, local_index); ID local = ISEQ_BODY(iseq)->local_table[local_index]; if (rb_is_local_id(local)) { VALUE name_obj = rb_id2str(local); const char *name = RSTRING_PTR(name_obj); - size_t length = strlen(name); + size_t length = RSTRING_LEN(name_obj); // Explicitly skip numbered parameters. These should not be sent // into the eval. @@ -1736,22 +1781,26 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, /* We need to duplicate the string because the Ruby string may * be embedded so compaction could move the string and the pointer * will change. */ - char *name_dup = xmalloc(length + 1); - strlcpy(name_dup, name, length + 1); + char *name_dup = xmalloc(length); + MEMCPY(name_dup, name, char, length); RB_GC_GUARD(name_obj); pm_string_owned_init(scope_local, (uint8_t *) name_dup, length); - } else if (local == idMULT) { + } + else if (local == idMULT) { forwarding |= PM_OPTIONS_SCOPE_FORWARDING_POSITIONALS; pm_string_constant_init(scope_local, FORWARDING_POSITIONALS_STR, 1); - } else if (local == idPow) { + } + else if (local == idPow) { forwarding |= PM_OPTIONS_SCOPE_FORWARDING_KEYWORDS; pm_string_constant_init(scope_local, FORWARDING_KEYWORDS_STR, 1); - } else if (local == idAnd) { + } + else if (local == idAnd) { forwarding |= PM_OPTIONS_SCOPE_FORWARDING_BLOCK; pm_string_constant_init(scope_local, FORWARDING_BLOCK_STR, 1); - } else if (local == idDot3) { + } + else if (local == idDot3) { forwarding |= PM_OPTIONS_SCOPE_FORWARDING_ALL; pm_string_constant_init(scope_local, FORWARDING_ALL_STR, 1); } @@ -1768,7 +1817,7 @@ 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); + pm_options_scope_init(pm_options_scope_mut(result.options, scopes_count), 0); VALUE script_lines; VALUE error = pm_parse_string(&result, src, fname, ruby_vm_keep_script_lines ? &script_lines : NULL); @@ -1789,17 +1838,16 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, pm_scope_node_t *parent_scope = ruby_xcalloc(1, sizeof(pm_scope_node_t)); RUBY_ASSERT(parent_scope != NULL); - pm_options_scope_t *options_scope = &result.options.scopes[scopes_count - scopes_index - 1]; + const pm_options_scope_t *options_scope = pm_options_scope(result.options, scopes_count - scopes_index - 1); parent_scope->coverage_enabled = coverage_enabled; - parent_scope->parser = &result.parser; - parent_scope->index_lookup_table = st_init_numtable(); - + parent_scope->parser = result.parser; int locals_count = ISEQ_BODY(iseq)->local_table_size; + pm_index_lookup_table_init_heap(&parent_scope->index_lookup_table, (int) pm_parser_constants_size(result.parser)); parent_scope->local_table_for_iseq_size = locals_count; pm_constant_id_list_init(&parent_scope->locals); for (int local_index = 0; local_index < locals_count; local_index++) { - const pm_string_t *scope_local = &options_scope->locals[local_index]; + const pm_string_t *scope_local = pm_options_scope_local(options_scope, local_index); pm_constant_id_t constant_id = 0; const uint8_t *source = pm_string_source(scope_local); @@ -1821,18 +1869,18 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, constant_id = PM_CONSTANT_DOT3; break; default: - constant_id = pm_constant_pool_insert_constant(&result.parser.constant_pool, source, length); + constant_id = pm_parser_constant_find(result.parser, source, length); break; } } else { - constant_id = pm_constant_pool_insert_constant(&result.parser.constant_pool, source, length); + constant_id = pm_parser_constant_find(result.parser, source, length); } - st_insert(parent_scope->index_lookup_table, (st_data_t) constant_id, (st_data_t) local_index); + pm_index_lookup_table_insert(&parent_scope->index_lookup_table, constant_id, local_index); } - pm_constant_id_list_append(&parent_scope->locals, constant_id); + pm_constant_id_list_append(result.arena, &parent_scope->locals, constant_id); } node->previous = parent_scope; @@ -1850,14 +1898,13 @@ pm_eval_make_iseq(VALUE src, VALUE fname, int line, #undef FORWARDING_ALL_STR int error_state; - iseq = pm_iseq_new_eval(&result.node, name, fname, Qnil, line, parent, 0, &error_state); + iseq = pm_iseq_new_eval(&result.node, name, fname, Qnil, line, parent, isolated_depth, &error_state); pm_scope_node_t *prev = result.node.previous; while (prev) { pm_scope_node_t *next = prev->previous; - pm_constant_id_list_free(&prev->locals); pm_scope_node_destroy(prev); - ruby_xfree(prev); + SIZED_FREE(prev); prev = next; } @@ -1886,27 +1933,9 @@ eval_make_iseq(VALUE src, VALUE fname, int line, rb_iseq_t *iseq = NULL; VALUE ast_value; rb_ast_t *ast; - int isolated_depth = 0; - // Conditionally enable coverage depending on the current mode: int coverage_enabled = (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) != 0; - - { - int depth = 1; - const VALUE *ep = vm_block_ep(base_block); - - while (1) { - if (VM_ENV_FLAGS(ep, VM_ENV_FLAG_ISOLATED)) { - isolated_depth = depth; - break; - } - else if (VM_ENV_LOCAL_P(ep)) { - break; - } - ep = VM_ENV_PREV_EP(ep); - depth++; - } - } + int isolated_depth = compute_isolated_depth_from_block(base_block); if (!fname) { fname = rb_source_location(&line); @@ -1960,9 +1989,13 @@ eval_string_with_cref(VALUE self, VALUE src, rb_cref_t *cref, VALUE file, int li block.as.captured = *VM_CFP_TO_CAPTURED_BLOCK(cfp); block.as.captured.self = self; - block.as.captured.code.iseq = cfp->iseq; + block.as.captured.code.iseq = CFP_ISEQ(cfp); block.type = block_type_iseq; + // EP is not escaped to the heap here, but captured and reused by another frame. + // ZJIT's locals are incompatible with it unlike YJIT's, so invalidate the ISEQ for ZJIT. + rb_zjit_invalidate_no_ep_escape(CFP_ISEQ(cfp)); + iseq = eval_make_iseq(src, file, line, &block); if (!iseq) { rb_exc_raise(ec->errinfo); @@ -2001,21 +2034,38 @@ eval_string_with_scope(VALUE scope, VALUE src, VALUE file, int line) } /* - * call-seq: - * eval(string [, binding [, filename [,lineno]]]) -> obj + * call-seq: + * eval(string, binding = nil, filename = default_filename, lineno = 1) -> obj * - * Evaluates the Ruby expression(s) in <em>string</em>. If - * <em>binding</em> is given, which must be a Binding object, the - * evaluation is performed in its context. If the optional - * <em>filename</em> and <em>lineno</em> parameters are present, they - * will be used when reporting syntax errors. + * Evaluates the Ruby expression(s) in +string+. Returns the result of the last + * expression. * - * def get_binding(str) - * return binding - * end - * str = "hello" - * eval "str + ' Fred'" #=> "hello Fred" - * eval "str + ' Fred'", get_binding("bye") #=> "bye Fred" + * str = "Hello" + * eval("str + ' World'") # => "Hello World" + * + * If +binding+ is given, which must be a Binding object, the + * evaluation is performed in its context. Otherwise, the + * evaluation is performed in the context of the caller. + * + * def get_binding(str) = binding + * str = "Hello" + * eval("str + ' World'", get_binding("Bye")) # => "Bye World" + * + * If the optional +filename+ is given, it will be used as the + * filename of the evaluation (for <tt>__FILE__</tt> and errors). + * Otherwise, it will default to <tt>(eval at __FILE__:__LINE__)</tt> + * where <tt>__FILE__</tt> and <tt>__LINE__</tt> are the filename and + * line number of the caller, respectively. + * + * eval("puts __FILE__") # => "(eval at test.rb:1)" + * eval("puts __FILE__", nil, "foobar.rb") # => "foobar.rb" + * + * If the optional +lineno+ is given, it will be used as the + * line number of the evaluation (for <tt>__LINE__</tt> and errors). + * Otherwise, it will default to 1. + * + * eval("puts __LINE__") # => 1 + * eval("puts __LINE__", nil, "foobar.rb", 10) # => 10 */ VALUE @@ -2122,6 +2172,17 @@ rb_eval_string_wrap(const char *str, int *pstate) VALUE rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat) { + Check_Type(arg, T_ARRAY); + int argc = RARRAY_LENINT(arg); + const VALUE *argv = RARRAY_CONST_PTR(arg); + VALUE val = rb_eval_cmd_call_kw(cmd, argc, argv, kw_splat); + RB_GC_GUARD(arg); + return val; +} + +VALUE +rb_eval_cmd_call_kw(VALUE cmd, int argc, const VALUE *argv, int kw_splat) +{ enum ruby_tag_type state; volatile VALUE val = Qnil; /* OK */ rb_execution_context_t * volatile ec = GET_EC(); @@ -2129,8 +2190,7 @@ rb_eval_cmd_kw(VALUE cmd, VALUE arg, int kw_splat) EC_PUSH_TAG(ec); if ((state = EC_EXEC_TAG()) == TAG_NONE) { if (!RB_TYPE_P(cmd, T_STRING)) { - val = rb_funcallv_kw(cmd, idCall, RARRAY_LENINT(arg), - RARRAY_CONST_PTR(arg), kw_splat); + val = rb_funcallv_kw(cmd, idCall, argc, argv, kw_splat); } else { val = eval_string_with_cref(rb_vm_top_self(), cmd, NULL, 0, 0); @@ -2332,29 +2392,66 @@ rb_obj_instance_exec(int argc, const VALUE *argv, VALUE self) /* * call-seq: - * mod.class_eval(string [, filename [, lineno]]) -> obj - * mod.class_eval {|mod| block } -> obj - * mod.module_eval(string [, filename [, lineno]]) -> obj - * mod.module_eval {|mod| block } -> obj + * class_eval(string, filename = nil, lineno = 1) -> obj + * class_eval { |mod| ... } -> obj + * module_eval(string, filename = nil, lineno = 1) -> obj + * module_eval { |mod| ... } -> obj * - * Evaluates the string or block in the context of _mod_, except that when - * a block is given, constant/class variable lookup is not affected. This - * can be used to add methods to a class. <code>module_eval</code> returns - * the result of evaluating its argument. The optional _filename_ and - * _lineno_ parameters set the text for error messages. + * Evaluates the +string+ or block in the context of +self+. + * Returns the result of the last expression. * - * class Thing - * end - * a = %q{def hello() "Hello there!" end} - * Thing.module_eval(a) - * puts Thing.new.hello() - * Thing.module_eval("invalid code", "dummy", 123) + * When +string+ is given, evaluates the given string in the + * context of +self+: * - * <em>produces:</em> + * class Foo; end * - * Hello there! - * dummy:123:in `module_eval': undefined local variable - * or method `code' for Thing:Class + * Foo.module_eval("def greeting = puts 'hello'") + * + * Foo.new.greeting # => "hello" + * + * If the optional +filename+ is given, it will be used as the + * filename of the evaluation (for <tt>__FILE__</tt> and errors). + * Otherwise, it will default to <tt>(eval at __FILE__:__LINE__)</tt> + * where <tt>__FILE__</tt> and <tt>__LINE__</tt> are the filename and + * line number of the caller, respectively: + * + * class Foo; end + * + * Foo.module_eval("puts __FILE__") # => "(eval at ../test.rb:3)" + * Foo.module_eval("puts __FILE__", "foobar.rb") # => "foobar.rb" + * + * If the optional +lineno+ is given, it will be used as the + * line number of the evaluation (for <tt>__LINE__</tt> and errors). + * Otherwise, it will default to 1: + * + * class Foo; end + * + * Foo.module_eval("puts __LINE__") # => 1 + * Foo.module_eval("puts __FILE__", nil, 10) # => 10 + * + * When a block is given, evaluates the block in the context + * of +self+: + * + * class Foo; end + * + * Foo.module_eval do + * def greeting = puts "hello" + * end + * + * Foo.new.greeting + * + * However, constant and class variable lookup differs between + * +string+ and block. When +string+ is given, contant and class + * variables are looked up in the context of +self+. When a block + * is given, the context of the lookup is not changed: + * + * class Foo + * GREETING = "hello" + * end + * + * Foo.module_eval("puts GREETING") # => "hello" + * + * Foo.module_eval { puts GREETING } # => NameError: uninitialized constant GREETING */ static VALUE @@ -2714,9 +2811,9 @@ rb_f_local_variables(VALUE _) local_var_list_init(&vars); while (cfp) { - if (cfp->iseq) { - for (i = 0; i < ISEQ_BODY(cfp->iseq)->local_table_size; i++) { - local_var_list_add(&vars, ISEQ_BODY(cfp->iseq)->local_table[i]); + if (CFP_ISEQ(cfp)) { + for (i = 0; i < ISEQ_BODY(CFP_ISEQ(cfp))->local_table_size; i++) { + local_var_list_add(&vars, ISEQ_BODY(CFP_ISEQ(cfp))->local_table[i]); } } if (!VM_ENV_LOCAL_P(cfp->ep)) { @@ -2790,10 +2887,11 @@ rb_current_realfilepath(void) rb_control_frame_t *cfp = ec->cfp; cfp = vm_get_ruby_level_caller_cfp(ec, RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)); if (cfp != NULL) { - VALUE path = rb_iseq_realpath(cfp->iseq); + const rb_iseq_t *iseq = CFP_ISEQ(cfp); + VALUE path = rb_iseq_realpath(iseq); if (RTEST(path)) return path; // eval context - path = rb_iseq_path(cfp->iseq); + path = rb_iseq_path(iseq); if (path == eval_default_path) { return Qnil; } @@ -2819,7 +2917,7 @@ struct vm_ifunc * rb_current_ifunc(void) { // Search VM_FRAME_MAGIC_IFUNC to see ifunc imemos put on the iseq field. - VALUE ifunc = (VALUE)GET_EC()->cfp->iseq; + VALUE ifunc = (VALUE)CFP_ISEQ(GET_EC()->cfp); RUBY_ASSERT_ALWAYS(imemo_type_p(ifunc, imemo_ifunc)); return (struct vm_ifunc *)ifunc; } |
