summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYusuke Endoh <[email protected]>2025-01-20 17:41:08 +0900
committerYusuke Endoh <[email protected]>2025-02-18 16:23:24 +0900
commit993fd96ce6bb763e08207bb3d53824d5d46d07a4 (patch)
tree1794e9e8411c5bfd139d0b730642c8678e9bf07f
parent6d75599a1aade9f8081d0691a9da1e62a5428e95 (diff)
reject numbered parameters from Binding#local_variables
Also, Binding#local_variable_get and #local_variable_set rejects an access to numbered parameters. [Bug #20965] [Bug #21049]
Notes
Notes: Merged: https://github.com/ruby/ruby/pull/12746
-rw-r--r--proc.c14
-rw-r--r--spec/ruby/language/numbered_parameters_spec.rb15
-rw-r--r--vm.c15
-rw-r--r--vm_core.h1
-rw-r--r--vm_eval.c24
5 files changed, 64 insertions, 5 deletions
diff --git a/proc.c b/proc.c
index e7e011a415..0436a9aaf8 100644
--- a/proc.c
+++ b/proc.c
@@ -499,6 +499,12 @@ bind_local_variables(VALUE bindval)
return rb_vm_env_local_variables(env);
}
+int
+rb_numparam_id_p(ID id)
+{
+ return (tNUMPARAM_1 << ID_SCOPE_SHIFT) <= id && id < ((tNUMPARAM_1 + 10) << ID_SCOPE_SHIFT);
+}
+
/*
* call-seq:
* binding.local_variable_get(symbol) -> obj
@@ -525,6 +531,10 @@ bind_local_variable_get(VALUE bindval, VALUE sym)
const rb_env_t *env;
if (!lid) goto undefined;
+ if (rb_numparam_id_p(lid)) {
+ rb_name_err_raise("numbered parameter '%1$s' is not a local variable",
+ bindval, ID2SYM(lid));
+ }
GetBindingPtr(bindval, bind);
@@ -574,6 +584,10 @@ bind_local_variable_set(VALUE bindval, VALUE sym, VALUE val)
const rb_env_t *env;
if (!lid) lid = rb_intern_str(sym);
+ if (rb_numparam_id_p(lid)) {
+ rb_name_err_raise("numbered parameter '%1$s' is not a local variable",
+ bindval, ID2SYM(lid));
+ }
GetBindingPtr(bindval, bind);
env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));
diff --git a/spec/ruby/language/numbered_parameters_spec.rb b/spec/ruby/language/numbered_parameters_spec.rb
index 06f9948c58..a3153d3cc6 100644
--- a/spec/ruby/language/numbered_parameters_spec.rb
+++ b/spec/ruby/language/numbered_parameters_spec.rb
@@ -90,9 +90,18 @@ describe "Numbered parameters" do
proc { _2 }.parameters.should == [[:opt, :_1], [:opt, :_2]]
end
- it "affects binding local variables" do
- -> { _1; binding.local_variables }.call("a").should == [:_1]
- -> { _2; binding.local_variables }.call("a", "b").should == [:_1, :_2]
+ ruby_version_is "".."3.4" do
+ it "affects binding local variables" do
+ -> { _1; binding.local_variables }.call("a").should == [:_1]
+ -> { _2; binding.local_variables }.call("a", "b").should == [:_1, :_2]
+ end
+ end
+
+ ruby_version_is "3.5" do
+ it "does not affect binding local variables" do
+ -> { _1; binding.local_variables }.call("a").should == []
+ -> { _2; binding.local_variables }.call("a", "b").should == []
+ end
end
it "does not work in methods" do
diff --git a/vm.c b/vm.c
index d4d57b363c..406e45c737 100644
--- a/vm.c
+++ b/vm.c
@@ -1107,6 +1107,21 @@ rb_vm_env_local_variables(const rb_env_t *env)
}
VALUE
+rb_vm_env_numbered_parameters(const rb_env_t *env)
+{
+ struct local_var_list vars;
+ local_var_list_init(&vars);
+ // if (VM_ENV_FLAGS(env->ep, VM_ENV_FLAG_ISOLATED)) break; // TODO: is this needed?
+ const rb_iseq_t *iseq = env->iseq;
+ unsigned int i;
+ if (!iseq) return 0;
+ for (i = 0; i < ISEQ_BODY(iseq)->local_table_size; i++) {
+ numparam_list_add(&vars, ISEQ_BODY(iseq)->local_table[i]);
+ }
+ return local_var_list_finish(&vars);
+}
+
+VALUE
rb_iseq_local_variables(const rb_iseq_t *iseq)
{
struct local_var_list vars;
diff --git a/vm_core.h b/vm_core.h
index f9b1d6758f..d9159f5ccf 100644
--- a/vm_core.h
+++ b/vm_core.h
@@ -1853,6 +1853,7 @@ rb_vm_make_lambda(const rb_execution_context_t *ec, const struct rb_captured_blo
VALUE rb_vm_make_binding(const rb_execution_context_t *ec, const rb_control_frame_t *src_cfp);
VALUE rb_vm_env_local_variables(const rb_env_t *env);
+VALUE rb_vm_env_numbered_parameters(const rb_env_t *env);
const rb_env_t *rb_vm_env_prev_env(const rb_env_t *env);
const VALUE *rb_binding_add_dynavars(VALUE bindval, rb_binding_t *bind, int dyncount, const ID *dynvars);
void rb_vm_inc_const_missing_count(void);
diff --git a/vm_eval.c b/vm_eval.c
index 74dfd40907..229dbf03b3 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -2661,11 +2661,31 @@ local_var_list_update(st_data_t *key, st_data_t *value, st_data_t arg, int exist
return ST_CONTINUE;
}
+extern int rb_numparam_id_p(ID id);
+
static void
local_var_list_add(const struct local_var_list *vars, ID lid)
{
- if (lid && rb_is_local_id(lid)) {
- /* should skip temporary variable */
+ /* should skip temporary variable */
+ if (!lid) return;
+ if (!rb_is_local_id(lid)) return;
+
+ /* should skip numbered parameters as well */
+ if (rb_numparam_id_p(lid)) return;
+
+ st_data_t idx = 0; /* tbl->num_entries */
+ rb_hash_stlike_update(vars->tbl, ID2SYM(lid), local_var_list_update, idx);
+}
+
+static void
+numparam_list_add(const struct local_var_list *vars, ID lid)
+{
+ /* should skip temporary variable */
+ if (!lid) return;
+ if (!rb_is_local_id(lid)) return;
+
+ /* should skip anything but numbered parameters */
+ if (rb_numparam_id_p(lid)) {
st_data_t idx = 0; /* tbl->num_entries */
rb_hash_stlike_update(vars->tbl, ID2SYM(lid), local_var_list_update, idx);
}