Skip to content

Commit 993fd96

Browse files
committed
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]
1 parent 6d75599 commit 993fd96

File tree

5 files changed

+64
-5
lines changed

5 files changed

+64
-5
lines changed

proc.c

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -499,6 +499,12 @@ bind_local_variables(VALUE bindval)
499499
return rb_vm_env_local_variables(env);
500500
}
501501

502+
int
503+
rb_numparam_id_p(ID id)
504+
{
505+
return (tNUMPARAM_1 << ID_SCOPE_SHIFT) <= id && id < ((tNUMPARAM_1 + 10) << ID_SCOPE_SHIFT);
506+
}
507+
502508
/*
503509
* call-seq:
504510
* binding.local_variable_get(symbol) -> obj
@@ -525,6 +531,10 @@ bind_local_variable_get(VALUE bindval, VALUE sym)
525531
const rb_env_t *env;
526532

527533
if (!lid) goto undefined;
534+
if (rb_numparam_id_p(lid)) {
535+
rb_name_err_raise("numbered parameter '%1$s' is not a local variable",
536+
bindval, ID2SYM(lid));
537+
}
528538

529539
GetBindingPtr(bindval, bind);
530540

@@ -574,6 +584,10 @@ bind_local_variable_set(VALUE bindval, VALUE sym, VALUE val)
574584
const rb_env_t *env;
575585

576586
if (!lid) lid = rb_intern_str(sym);
587+
if (rb_numparam_id_p(lid)) {
588+
rb_name_err_raise("numbered parameter '%1$s' is not a local variable",
589+
bindval, ID2SYM(lid));
590+
}
577591

578592
GetBindingPtr(bindval, bind);
579593
env = VM_ENV_ENVVAL_PTR(vm_block_ep(&bind->block));

spec/ruby/language/numbered_parameters_spec.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,18 @@
9090
proc { _2 }.parameters.should == [[:opt, :_1], [:opt, :_2]]
9191
end
9292

93-
it "affects binding local variables" do
94-
-> { _1; binding.local_variables }.call("a").should == [:_1]
95-
-> { _2; binding.local_variables }.call("a", "b").should == [:_1, :_2]
93+
ruby_version_is "".."3.4" do
94+
it "affects binding local variables" do
95+
-> { _1; binding.local_variables }.call("a").should == [:_1]
96+
-> { _2; binding.local_variables }.call("a", "b").should == [:_1, :_2]
97+
end
98+
end
99+
100+
ruby_version_is "3.5" do
101+
it "does not affect binding local variables" do
102+
-> { _1; binding.local_variables }.call("a").should == []
103+
-> { _2; binding.local_variables }.call("a", "b").should == []
104+
end
96105
end
97106

98107
it "does not work in methods" do

vm.c

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,6 +1106,21 @@ rb_vm_env_local_variables(const rb_env_t *env)
11061106
return local_var_list_finish(&vars);
11071107
}
11081108

1109+
VALUE
1110+
rb_vm_env_numbered_parameters(const rb_env_t *env)
1111+
{
1112+
struct local_var_list vars;
1113+
local_var_list_init(&vars);
1114+
// if (VM_ENV_FLAGS(env->ep, VM_ENV_FLAG_ISOLATED)) break; // TODO: is this needed?
1115+
const rb_iseq_t *iseq = env->iseq;
1116+
unsigned int i;
1117+
if (!iseq) return 0;
1118+
for (i = 0; i < ISEQ_BODY(iseq)->local_table_size; i++) {
1119+
numparam_list_add(&vars, ISEQ_BODY(iseq)->local_table[i]);
1120+
}
1121+
return local_var_list_finish(&vars);
1122+
}
1123+
11091124
VALUE
11101125
rb_iseq_local_variables(const rb_iseq_t *iseq)
11111126
{

vm_core.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1853,6 +1853,7 @@ rb_vm_make_lambda(const rb_execution_context_t *ec, const struct rb_captured_blo
18531853

18541854
VALUE rb_vm_make_binding(const rb_execution_context_t *ec, const rb_control_frame_t *src_cfp);
18551855
VALUE rb_vm_env_local_variables(const rb_env_t *env);
1856+
VALUE rb_vm_env_numbered_parameters(const rb_env_t *env);
18561857
const rb_env_t *rb_vm_env_prev_env(const rb_env_t *env);
18571858
const VALUE *rb_binding_add_dynavars(VALUE bindval, rb_binding_t *bind, int dyncount, const ID *dynvars);
18581859
void rb_vm_inc_const_missing_count(void);

vm_eval.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2661,11 +2661,31 @@ local_var_list_update(st_data_t *key, st_data_t *value, st_data_t arg, int exist
26612661
return ST_CONTINUE;
26622662
}
26632663

2664+
extern int rb_numparam_id_p(ID id);
2665+
26642666
static void
26652667
local_var_list_add(const struct local_var_list *vars, ID lid)
26662668
{
2667-
if (lid && rb_is_local_id(lid)) {
2668-
/* should skip temporary variable */
2669+
/* should skip temporary variable */
2670+
if (!lid) return;
2671+
if (!rb_is_local_id(lid)) return;
2672+
2673+
/* should skip numbered parameters as well */
2674+
if (rb_numparam_id_p(lid)) return;
2675+
2676+
st_data_t idx = 0; /* tbl->num_entries */
2677+
rb_hash_stlike_update(vars->tbl, ID2SYM(lid), local_var_list_update, idx);
2678+
}
2679+
2680+
static void
2681+
numparam_list_add(const struct local_var_list *vars, ID lid)
2682+
{
2683+
/* should skip temporary variable */
2684+
if (!lid) return;
2685+
if (!rb_is_local_id(lid)) return;
2686+
2687+
/* should skip anything but numbered parameters */
2688+
if (rb_numparam_id_p(lid)) {
26692689
st_data_t idx = 0; /* tbl->num_entries */
26702690
rb_hash_stlike_update(vars->tbl, ID2SYM(lid), local_var_list_update, idx);
26712691
}

0 commit comments

Comments
 (0)