summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKoichi Sasada <ko1@atdot.net>2020-10-23 13:27:21 +0900
committerKoichi Sasada <ko1@atdot.net>2020-10-29 23:42:55 +0900
commit07c03bc30984a496558d9e830bc4fb2f8cfb1854 (patch)
tree961a2eaa656943974221bbdf8cae28a9116e5f37
parentbf951c763d00a4aee8f8c896d1a97c387fa8f30e (diff)
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.
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/3721
-rw-r--r--compile.c59
-rw-r--r--iseq.c39
-rw-r--r--iseq.h1
-rw-r--r--proc.c9
-rw-r--r--template/prelude.c.tmpl2
-rw-r--r--test/ruby/test_proc.rb51
-rw-r--r--vm.c111
-rw-r--r--vm_core.h19
-rw-r--r--vm_eval.c25
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; i<level; i++) {
+ VALUE val;
+ struct rb_id_table *ovs = iseq->body->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; i<level; i++) {
- iseq->body->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("<main>"),
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("<compiled>");
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("<main>"),
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);