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]
This commit is contained in:
Yusuke Endoh 2025-01-20 17:41:08 +09:00
parent 6d75599a1a
commit 993fd96ce6
Notes: git 2025-02-18 07:23:41 +00:00
5 changed files with 64 additions and 5 deletions

14
proc.c
View File

@ -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));

View File

@ -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

15
vm.c
View File

@ -1106,6 +1106,21 @@ rb_vm_env_local_variables(const rb_env_t *env)
return local_var_list_finish(&vars);
}
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)
{

View File

@ -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);

View File

@ -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);
}