summaryrefslogtreecommitdiff
path: root/vm.c
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 /vm.c
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
Diffstat (limited to 'vm.c')
-rw-r--r--vm.c111
1 files changed, 96 insertions, 15 deletions
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;
}