diff options
author | Yusuke Endoh <[email protected]> | 2025-01-20 17:41:08 +0900 |
---|---|---|
committer | Yusuke Endoh <[email protected]> | 2025-02-18 16:23:24 +0900 |
commit | 993fd96ce6bb763e08207bb3d53824d5d46d07a4 (patch) | |
tree | 1794e9e8411c5bfd139d0b730642c8678e9bf07f | |
parent | 6d75599a1aade9f8081d0691a9da1e62a5428e95 (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.c | 14 | ||||
-rw-r--r-- | spec/ruby/language/numbered_parameters_spec.rb | 15 | ||||
-rw-r--r-- | vm.c | 15 | ||||
-rw-r--r-- | vm_core.h | 1 | ||||
-rw-r--r-- | vm_eval.c | 24 |
5 files changed, 64 insertions, 5 deletions
@@ -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 @@ -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; @@ -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); @@ -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); } |