mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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.
This commit is contained in:
parent
bf951c763d
commit
07c03bc309
Notes:
git
2020-10-29 23:43:27 +09:00
9 changed files with 264 additions and 52 deletions
59
compile.c
59
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,15 +1604,52 @@ 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)
|
||||
{
|
||||
// set access_outer_variables
|
||||
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++) {
|
||||
iseq->body->access_outer_variables = TRUE;
|
||||
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)
|
||||
{
|
||||
for (int i=0; i<level; i++) {
|
||||
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
|
||||
iseq_add_getlocal(rb_iseq_t *iseq, LINK_ANCHOR *const seq, int line, int idx, int level)
|
||||
{
|
||||
|
@ -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:{
|
||||
|
|
39
iseq.c
39
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;
|
||||
}
|
||||
|
|
1
iseq.h
1
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;
|
||||
|
|
9
proc.c
9
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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
111
vm.c
111
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;
|
||||
}
|
||||
|
||||
|
|
19
vm_core.h
19
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)
|
||||
|
|
25
vm_eval.c
25
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);
|
||||
|
||||
|
|
Loading…
Reference in a new issue