From 07c03bc30984a496558d9e830bc4fb2f8cfb1854 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Fri, 23 Oct 2020 13:27:21 +0900 Subject: check isolated Proc more strictly Isolated Proc prohibit to access outer local variables, but it was violated by binding and so on, so they should be error. --- compile.c | 59 ++++++++++++++++++++++--- iseq.c | 39 ++++++++++------- iseq.h | 1 + proc.c | 9 +++- template/prelude.c.tmpl | 2 +- test/ruby/test_proc.rb | 51 ++++++++++++++++++++++ vm.c | 111 +++++++++++++++++++++++++++++++++++++++++------- vm_core.h | 19 +++++---- vm_eval.c | 25 +++++++++-- 9 files changed, 264 insertions(+), 52 deletions(-) diff --git a/compile.c b/compile.c index 767e6c48e7..e0d4bc5455 100644 --- a/compile.c +++ b/compile.c @@ -1319,9 +1319,12 @@ new_child_iseq(rb_iseq_t *iseq, const NODE *const node, ast.line_count = -1; debugs("[new_child_iseq]> ---------------------------------------\n"); + int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth; ret_iseq = rb_iseq_new_with_opt(&ast, name, rb_iseq_path(iseq), rb_iseq_realpath(iseq), - INT2FIX(line_no), parent, type, ISEQ_COMPILE_DATA(iseq)->option); + INT2FIX(line_no), parent, + isolated_depth ? isolated_depth + 1 : 0, + type, ISEQ_COMPILE_DATA(iseq)->option); debugs("[new_child_iseq]< ---------------------------------------\n"); return ret_iseq; } @@ -1601,13 +1604,50 @@ iseq_block_param_id_p(const rb_iseq_t *iseq, ID id, int *pidx, int *plevel) } static void -check_access_outer_variables(const rb_iseq_t *iseq, int level) +access_outer_variables(const rb_iseq_t *iseq, int level, ID id, bool write) +{ + int isolated_depth = ISEQ_COMPILE_DATA(iseq)->isolated_depth; + + if (isolated_depth && level >= isolated_depth) { + if (id == rb_intern("yield")) { + COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not yield from isolated Proc", rb_id2name(id)); + } + else { + COMPILE_ERROR(iseq, ISEQ_LAST_LINE(iseq), "can not access variable `%s' from isolated Proc", rb_id2name(id)); + } + } + + for (int i=0; ibody->outer_variables; + + if (!ovs) { + ovs = iseq->body->outer_variables = rb_id_table_create(8); + } + + if (rb_id_table_lookup(iseq->body->outer_variables, id, &val)) { + if (write && !val) { + rb_id_table_insert(iseq->body->outer_variables, id, Qtrue); + } + } + else { + rb_id_table_insert(iseq->body->outer_variables, id, write ? Qtrue : Qfalse); + } + + iseq = iseq->body->parent_iseq; + } +} + +static ID +iseq_lvar_id(const rb_iseq_t *iseq, int idx, int level) { - // set access_outer_variables for (int i=0; ibody->access_outer_variables = TRUE; iseq = iseq->body->parent_iseq; } + + ID id = iseq->body->local_table[iseq->body->local_table_size - idx]; + // fprintf(stderr, "idx:%d level:%d ID:%s\n", idx, level, rb_id2name(id)); + return id; } static void @@ -1619,7 +1659,7 @@ iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, in else { ADD_INSN2(seq, line, getlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)); } - check_access_outer_variables(iseq, level); + if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qfalse); } static void @@ -1631,7 +1671,7 @@ iseq_add_setlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, in else { ADD_INSN2(seq, line, setlocal, INT2FIX((idx) + VM_ENV_DATA_SIZE - 1), INT2FIX(level)); } - check_access_outer_variables(iseq, level); + if (level > 0) access_outer_variables(iseq, level, iseq_lvar_id(iseq, idx, level), Qtrue); } @@ -8215,7 +8255,12 @@ iseq_compile_each0(rb_iseq_t *iseq, LINK_ANCHOR *const ret, const NODE *node, in ADD_INSN(ret, line, pop); } - iseq->body->access_outer_variables = TRUE; + int level = 0; + const rb_iseq_t *tmp_iseq = iseq; + for (; tmp_iseq != iseq->body->local_iseq; level++ ) { + tmp_iseq = tmp_iseq->body->parent_iseq; + } + if (level > 0) access_outer_variables(iseq, level, rb_intern("yield"), true); break; } case NODE_LVAR:{ diff --git a/iseq.c b/iseq.c index 85ced1212c..449d3385cc 100644 --- a/iseq.c +++ b/iseq.c @@ -130,6 +130,7 @@ rb_iseq_free(const rb_iseq_t *iseq) ruby_xfree((void *)body->param.keyword); } compile_data_free(ISEQ_COMPILE_DATA(iseq)); + if (body->outer_variables) rb_id_table_free(body->outer_variables); ruby_xfree(body); } @@ -575,8 +576,7 @@ new_arena(void) static VALUE prepare_iseq_build(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_code_location_t *code_location, const int node_id, - const rb_iseq_t *parent, enum iseq_type type, - const rb_compile_option_t *option) + const rb_iseq_t *parent, int isolated_depth, enum iseq_type type, const rb_compile_option_t *option) { VALUE coverage = Qfalse; VALUE err_info = Qnil; @@ -603,11 +603,11 @@ prepare_iseq_build(rb_iseq_t *iseq, ISEQ_COMPILE_DATA(iseq)->node.storage_head = ISEQ_COMPILE_DATA(iseq)->node.storage_current = new_arena(); ISEQ_COMPILE_DATA(iseq)->insn.storage_head = ISEQ_COMPILE_DATA(iseq)->insn.storage_current = new_arena(); + ISEQ_COMPILE_DATA(iseq)->isolated_depth = isolated_depth; ISEQ_COMPILE_DATA(iseq)->option = option; - ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = NULL; - ISEQ_COMPILE_DATA(iseq)->builtin_function_table = GET_VM()->builtin_function_table; + if (option->coverage_enabled) { VALUE coverages = rb_get_coverages(); @@ -794,8 +794,8 @@ rb_iseq_t * rb_iseq_new(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum iseq_type type) { - return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, type, - &COMPILE_OPTION_DEFAULT); + return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, + 0, type, &COMPILE_OPTION_DEFAULT); } rb_iseq_t * @@ -810,8 +810,8 @@ rb_iseq_new_top(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath } } - return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, ISEQ_TYPE_TOP, - &COMPILE_OPTION_DEFAULT); + return rb_iseq_new_with_opt(ast, name, path, realpath, INT2FIX(0), parent, 0, + ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT); } rb_iseq_t * @@ -819,7 +819,14 @@ rb_iseq_new_main(const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_ { return rb_iseq_new_with_opt(ast, rb_fstring_lit("
"), path, realpath, INT2FIX(0), - parent, ISEQ_TYPE_MAIN, &COMPILE_OPTION_DEFAULT); + parent, 0, ISEQ_TYPE_MAIN, &COMPILE_OPTION_DEFAULT); +} + +rb_iseq_t * +rb_iseq_new_eval(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth) +{ + return rb_iseq_new_with_opt(ast, name, path, realpath, first_lineno, + parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT); } static inline rb_iseq_t * @@ -838,8 +845,8 @@ iseq_translate(rb_iseq_t *iseq) rb_iseq_t * rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, - VALUE first_lineno, const rb_iseq_t *parent, - enum iseq_type type, const rb_compile_option_t *option) + VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth, + enum iseq_type type, const rb_compile_option_t *option) { const NODE *node = ast ? ast->root : 0; /* TODO: argument check */ @@ -854,7 +861,7 @@ rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE rea } if (ast && ast->compile_option) rb_iseq_make_compile_option(&new_opt, ast->compile_option); - prepare_iseq_build(iseq, name, path, realpath, first_lineno, node ? &node->nd_loc : NULL, node ? nd_node_id(node) : -1, parent, type, &new_opt); + prepare_iseq_build(iseq, name, path, realpath, first_lineno, node ? &node->nd_loc : NULL, node ? nd_node_id(node) : -1, parent, isolated_depth, type, &new_opt); rb_iseq_compile_node(iseq, node); finish_iseq_build(iseq); @@ -873,7 +880,7 @@ rb_iseq_new_with_callback( rb_iseq_t *iseq = iseq_alloc(); if (!option) option = &COMPILE_OPTION_DEFAULT; - prepare_iseq_build(iseq, name, path, realpath, first_lineno, NULL, -1, parent, type, option); + prepare_iseq_build(iseq, name, path, realpath, first_lineno, NULL, -1, parent, 0, type, option); rb_iseq_compile_callback(iseq, ifunc); finish_iseq_build(iseq); @@ -986,7 +993,7 @@ iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt) make_compile_option(&option, opt); option.peephole_optimization = FALSE; /* because peephole optimization can modify original iseq */ prepare_iseq_build(iseq, name, path, realpath, first_lineno, &tmp_loc, NUM2INT(node_id), - parent, (enum iseq_type)iseq_type, &option); + parent, 0, (enum iseq_type)iseq_type, &option); rb_iseq_build_from_ary(iseq, misc, locals, params, exception, body); @@ -1054,7 +1061,7 @@ rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, V else { INITIALIZED VALUE label = rb_fstring_lit(""); iseq = rb_iseq_new_with_opt(&ast->body, label, file, realpath, line, - 0, ISEQ_TYPE_TOP, &option); + NULL, 0, ISEQ_TYPE_TOP, &option); rb_ast_dispose(ast); } @@ -1310,7 +1317,7 @@ iseqw_s_compile_file(int argc, VALUE *argv, VALUE self) ret = iseqw_new(rb_iseq_new_with_opt(&ast->body, rb_fstring_lit("
"), file, rb_realpath_internal(Qnil, file, 1), - line, NULL, ISEQ_TYPE_TOP, &option)); + line, NULL, 0, ISEQ_TYPE_TOP, &option)); rb_ast_dispose(ast); return ret; } diff --git a/iseq.h b/iseq.h index 4e427f557c..bae534c71d 100644 --- a/iseq.h +++ b/iseq.h @@ -105,6 +105,7 @@ struct iseq_compile_data { int last_line; int label_no; int node_level; + int isolated_depth; unsigned int ci_index; const rb_compile_option_t *option; struct rb_id_table *ivar_cache_table; diff --git a/proc.c b/proc.c index 061c6c34a6..5c78fc10ed 100644 --- a/proc.c +++ b/proc.c @@ -424,7 +424,11 @@ get_local_variable_ptr(const rb_env_t **envp, ID lid) const rb_env_t *env = *envp; do { if (!VM_ENV_FLAGS(env->ep, VM_FRAME_FLAG_CFRAME)) { - const rb_iseq_t *iseq = env->iseq; + if (VM_ENV_FLAGS(env->ep, VM_ENV_FLAG_ISOLATED)) { + return NULL; + } + + const rb_iseq_t *iseq = env->iseq; unsigned int i; VM_ASSERT(rb_obj_is_iseq((VALUE)iseq)); @@ -3245,6 +3249,8 @@ proc_binding(VALUE self) GetProcPtr(self, proc); block = &proc->block; + if (proc->is_isolated) rb_raise(rb_eArgError, "Can't create Binding from isolated Proc"); + again: switch (vm_block_type(block)) { case block_type_iseq: @@ -4065,6 +4071,7 @@ Init_Proc(void) rb_define_method(rb_cProc, "source_location", rb_proc_location, 0); rb_define_method(rb_cProc, "parameters", rb_proc_parameters, 0); rb_define_method(rb_cProc, "ruby2_keywords", proc_ruby2_keywords, 0); + // rb_define_method(rb_cProc, "isolate", rb_proc_isolate, 0); is not accepted. /* Exceptions */ rb_eLocalJumpError = rb_define_class("LocalJumpError", rb_eStandardError); diff --git a/template/prelude.c.tmpl b/template/prelude.c.tmpl index 0ba6b0ba4b..eec8a32da9 100644 --- a/template/prelude.c.tmpl +++ b/template/prelude.c.tmpl @@ -202,7 +202,7 @@ prelude_eval(VALUE code, VALUE name, int line) rb_ast_t *ast = prelude_ast(name, code, line); rb_iseq_eval(rb_iseq_new_with_opt(&ast->body, name, name, Qnil, INT2FIX(line), - NULL, ISEQ_TYPE_TOP, &optimization)); + NULL, 0, ISEQ_TYPE_TOP, &optimization)); rb_ast_dispose(ast); } COMPILER_WARNING_POP diff --git a/test/ruby/test_proc.rb b/test/ruby/test_proc.rb index 0cc5d488c2..c1f17a003a 100644 --- a/test/ruby/test_proc.rb +++ b/test/ruby/test_proc.rb @@ -1538,6 +1538,57 @@ class TestProc < Test::Unit::TestCase assert_equal(42, Module.new { extend self def m1(&b) b end; def m2(); m1 { next 42 } end }.m2.call) end + + def test_isolate + assert_raise_with_message ArgumentError, /\(a\)/ do + a = :a + Proc.new{p a}.isolate.call + end + + assert_raise_with_message ArgumentError, /\(a\)/ do + a = :a + 1.times{ + Proc.new{p a}.isolate.call + } + end + + assert_raise_with_message ArgumentError, /yield/ do + Proc.new{yield}.isolate.call + end + + # binding + + :a.tap{|a| + :b.tap{|b| + Proc.new{ + :c.tap{|c| + assert_equal :c, eval('c') + + assert_raise_with_message SyntaxError, /\`a\'/ do + eval('p a') + end + + assert_raise_with_message SyntaxError, /\`b\'/ do + eval('p b') + end + + assert_raise_with_message SyntaxError, /can not yield from isolated Proc/ do + eval('p yield') + end + + assert_equal :c, binding.local_variable_get(:c) + + assert_raise_with_message NameError, /local variable \`a\' is not defined/ do + binding.local_variable_get(:a) + end + + assert_equal [:c], local_variables + assert_equal [:c], binding.local_variables + } + }.isolate.call + } + } + end if proc{}.respond_to? :isolate end class TestProcKeywords < Test::Unit::TestCase diff --git a/vm.c b/vm.c index 1f3d97b56d..27e39cf2ae 100644 --- a/vm.c +++ b/vm.c @@ -739,18 +739,17 @@ vm_make_env_each(const rb_execution_context_t * const ec, rb_control_frame_t *co if (!VM_ENV_LOCAL_P(ep)) { const VALUE *prev_ep = VM_ENV_PREV_EP(ep); + if (!VM_ENV_ESCAPED_P(prev_ep)) { + rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); - if (!VM_ENV_ESCAPED_P(prev_ep)) { - rb_control_frame_t *prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp); - - while (prev_cfp->ep != prev_ep) { - prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(prev_cfp); - VM_ASSERT(prev_cfp->ep != NULL); - } + while (prev_cfp->ep != prev_ep) { + prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(prev_cfp); + VM_ASSERT(prev_cfp->ep != NULL); + } - vm_make_env_each(ec, prev_cfp); - VM_FORCE_WRITE_SPECIAL_CONST(&ep[VM_ENV_DATA_INDEX_SPECVAL], VM_GUARDED_PREV_EP(prev_cfp->ep)); - } + vm_make_env_each(ec, prev_cfp); + VM_FORCE_WRITE_SPECIAL_CONST(&ep[VM_ENV_DATA_INDEX_SPECVAL], VM_GUARDED_PREV_EP(prev_cfp->ep)); + } } else { VALUE block_handler = VM_ENV_BLOCK_HANDLER(ep); @@ -836,7 +835,8 @@ rb_vm_env_prev_env(const rb_env_t *env) return NULL; } else { - return VM_ENV_ENVVAL_PTR(VM_ENV_PREV_EP(ep)); + const VALUE *prev_ep = VM_ENV_PREV_EP(ep); + return VM_ENV_ENVVAL_PTR(prev_ep); } } @@ -855,6 +855,7 @@ static void collect_local_variables_in_env(const rb_env_t *env, const struct local_var_list *vars) { do { + if (VM_ENV_FLAGS(env->ep, VM_ENV_FLAG_ISOLATED)) break; collect_local_variables_in_iseq(env->iseq, vars); } while ((env = rb_vm_env_prev_env(env)) != NULL); } @@ -963,17 +964,97 @@ rb_proc_dup(VALUE self) return procval; } +struct collect_outer_variable_name_data { + VALUE ary; + bool yield; +}; + +static enum rb_id_table_iterator_result +collect_outer_variable_names(ID id, VALUE val, void *ptr) +{ + struct collect_outer_variable_name_data *data = (struct collect_outer_variable_name_data *)ptr; + + if (id == rb_intern("yield")) { + data->yield = true; + } + else { + if (data->ary == Qfalse) data->ary = rb_ary_new(); + rb_ary_push(data->ary, rb_id2str(id)); + } + return ID_TABLE_CONTINUE; +} + +static const rb_env_t * +env_copy(const VALUE *src_ep) +{ + const rb_env_t *src_env = (rb_env_t *)VM_ENV_ENVVAL(src_ep); + VALUE *env_body = ZALLOC_N(VALUE, src_env->env_size); // fill with Qfalse + VALUE *ep = &env_body[src_env->env_size - 2]; + + VM_ASSERT(src_env->ep == src_ep); + + ep[VM_ENV_DATA_INDEX_ME_CREF] = src_ep[VM_ENV_DATA_INDEX_ME_CREF]; + ep[VM_ENV_DATA_INDEX_FLAGS] = src_ep[VM_ENV_DATA_INDEX_FLAGS] | VM_ENV_FLAG_ISOLATED; + + if (!VM_ENV_LOCAL_P(src_ep)) { + const VALUE *prev_ep = VM_ENV_PREV_EP(src_env->ep); + const rb_env_t *new_prev_env = env_copy(prev_ep); + ep[VM_ENV_DATA_INDEX_SPECVAL] = VM_GUARDED_PREV_EP(new_prev_env->ep); + } + else { + ep[VM_ENV_DATA_INDEX_SPECVAL] = VM_BLOCK_HANDLER_NONE; + } + return vm_env_new(ep, env_body, src_env->env_size, src_env->iseq); +} + +static void +proc_isolate_env(VALUE self, rb_proc_t *proc) +{ + const struct rb_captured_block *captured = &proc->block.as.captured; + const rb_env_t *env = env_copy(captured->ep); + *((const VALUE **)&proc->block.as.captured.ep) = env->ep; + RB_OBJ_WRITTEN(self, Qundef, env); +} + VALUE rb_proc_isolate_bang(VALUE self) { // check accesses const rb_iseq_t *iseq = vm_proc_iseq(self); - if (iseq && iseq->body->access_outer_variables) { - rb_raise(rb_eArgError, "can not isolate a Proc because it can accesses outer variables."); + + if (iseq) { + rb_proc_t *proc = (rb_proc_t *)RTYPEDDATA_DATA(self); + if (proc->block.type != block_type_iseq) rb_raise(rb_eRuntimeError, "not supported yet"); + + if (iseq->body->outer_variables) { + struct collect_outer_variable_name_data data = { + .ary = Qfalse, + .yield = false, + }; + + rb_id_table_foreach(iseq->body->outer_variables, collect_outer_variable_names, (void *)&data); + + if (data.ary != Qfalse) { + VALUE str = rb_ary_join(data.ary, rb_str_new2(", ")); + if (data.yield) { + rb_raise(rb_eArgError, "can not isolate a Proc because it accesses outer variables (%s) and uses `yield'.", + StringValueCStr(str)); + } + else { + rb_raise(rb_eArgError, "can not isolate a Proc because it accesses outer variables (%s).", + StringValueCStr(str)); + } + } + else { + VM_ASSERT(data.yield); + rb_raise(rb_eArgError, "can not isolate a Proc because it uses `yield'."); + } + } + + proc_isolate_env(self, proc); + proc->is_isolated = TRUE; } - rb_proc_t *proc = (rb_proc_t *)RTYPEDDATA_DATA(self); - proc->is_isolated = TRUE; return self; } diff --git a/vm_core.h b/vm_core.h index 02e777fb06..c4341b474c 100644 --- a/vm_core.h +++ b/vm_core.h @@ -418,7 +418,7 @@ struct rb_iseq_constant_body { char catch_except_p; /* If a frame of this ISeq may catch exception, set TRUE */ bool builtin_inline_p; // This ISeq's builtin func is safe to be inlined by MJIT - char access_outer_variables; + struct rb_id_table *outer_variables; #if USE_MJIT /* The following fields are MJIT related info. */ @@ -1017,11 +1017,13 @@ typedef enum { RUBY_SYMBOL_EXPORT_BEGIN /* node -> iseq */ -rb_iseq_t *rb_iseq_new (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum iseq_type); -rb_iseq_t *rb_iseq_new_top (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent); -rb_iseq_t *rb_iseq_new_main (const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent); -rb_iseq_t *rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, - const rb_iseq_t *parent, enum iseq_type, const rb_compile_option_t*); +rb_iseq_t *rb_iseq_new (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent, enum iseq_type); +rb_iseq_t *rb_iseq_new_top (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent); +rb_iseq_t *rb_iseq_new_main (const rb_ast_body_t *ast, VALUE path, VALUE realpath, const rb_iseq_t *parent); +rb_iseq_t *rb_iseq_new_eval (const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth); +rb_iseq_t *rb_iseq_new_with_opt(const rb_ast_body_t *ast, VALUE name, VALUE path, VALUE realpath, VALUE first_lineno, const rb_iseq_t *parent, int isolated_depth, + enum iseq_type, const rb_compile_option_t*); + struct iseq_link_anchor; struct rb_iseq_new_with_callback_callback_func { VALUE flags; @@ -1156,18 +1158,19 @@ enum { VM_FRAME_MAGIC_MASK = 0x7fff0001, /* frame flag */ - VM_FRAME_FLAG_PASSED = 0x0010, VM_FRAME_FLAG_FINISH = 0x0020, VM_FRAME_FLAG_BMETHOD = 0x0040, VM_FRAME_FLAG_CFRAME = 0x0080, VM_FRAME_FLAG_LAMBDA = 0x0100, VM_FRAME_FLAG_MODIFIED_BLOCK_PARAM = 0x0200, VM_FRAME_FLAG_CFRAME_KW = 0x0400, + VM_FRAME_FLAG_PASSED = 0x0800, /* env flag */ VM_ENV_FLAG_LOCAL = 0x0002, VM_ENV_FLAG_ESCAPED = 0x0004, - VM_ENV_FLAG_WB_REQUIRED = 0x0008 + VM_ENV_FLAG_WB_REQUIRED = 0x0008, + VM_ENV_FLAG_ISOLATED = 0x0010, }; #define VM_ENV_DATA_SIZE ( 3) diff --git a/vm_eval.c b/vm_eval.c index 3ada33e128..20117bb79d 100644 --- a/vm_eval.c +++ b/vm_eval.c @@ -1461,6 +1461,23 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind, VALUE realpath = Qnil; rb_iseq_t *iseq = NULL; rb_ast_t *ast; + int isolated_depth = 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++; + } + } if (!fname) { fname = rb_source_location(&line); @@ -1477,10 +1494,10 @@ eval_make_iseq(VALUE src, VALUE fname, int line, const rb_binding_t *bind, rb_parser_set_context(parser, parent, FALSE); ast = rb_parser_compile_string_path(parser, fname, src, line); if (ast->body.root) { - iseq = rb_iseq_new_with_opt(&ast->body, - parent->body->location.label, - fname, realpath, INT2FIX(line), - parent, ISEQ_TYPE_EVAL, NULL); + iseq = rb_iseq_new_eval(&ast->body, + parent->body->location.label, + fname, realpath, INT2FIX(line), + parent, isolated_depth); } rb_ast_dispose(ast); -- cgit v1.2.3