diff --git a/ChangeLog b/ChangeLog index 71ca216da7..5ee3872c68 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Wed Dec 25 01:03:00 2013 Nobuyoshi Nakada + + * proc.c (bind_local_variables): allowing binding to list its + local variables. patch by Jack Danger Canty at [ruby-core:56543]. [Feature #8773] + Tue Dec 24 23:20:38 2013 Nobuyoshi Nakada * test/fileutils/fileasserts.rb (assert_ownership_user): new diff --git a/proc.c b/proc.c index 2630df15d0..9de48a053f 100644 --- a/proc.c +++ b/proc.c @@ -446,6 +446,58 @@ check_local_id(VALUE bindval, volatile VALUE *pname) return lid; } +/* + * call-seq: + * binding.local_variables -> Array + * + * Returns the +symbol+ names of the binding's local variables + * + * def foo + * a = 1 + * 2.times do |n| + * binding.local_variables #=> [:a, :n] + * end + * end + * + * This method is short version of the following code. + * + * binding.eval("local_variables") + * + */ +static VALUE +bind_local_variables(VALUE bindval) +{ + VALUE ary = rb_ary_new(); + + const rb_binding_t *bind; + const rb_env_t *env; + VALUE envval; + + GetBindingPtr(bindval, bind); + + envval = bind->env; + GetEnvPtr(envval, env); + + do { + const rb_iseq_t *iseq; + int i; + ID id; + iseq = env->block.iseq; + + for (i = 0; i < iseq->local_table_size; i++) { + id = iseq->local_table[i]; + if (id) { + const char *vname = rb_id2name(id); + if (vname) { + rb_ary_push(ary, ID2SYM(id)); + } + } + } + } while ((envval = env->prev_envval) != 0); + + return ary; +} + /* * call-seq: * binding.local_variable_get(symbol) -> obj @@ -2718,6 +2770,7 @@ Init_Binding(void) rb_define_method(rb_cBinding, "clone", binding_clone, 0); rb_define_method(rb_cBinding, "dup", binding_dup, 0); rb_define_method(rb_cBinding, "eval", bind_eval, -1); + rb_define_method(rb_cBinding, "local_variables", bind_local_variables, 0); rb_define_method(rb_cBinding, "local_variable_get", bind_local_variable_get, 1); rb_define_method(rb_cBinding, "local_variable_set", bind_local_variable_set, 2); rb_define_method(rb_cBinding, "local_variable_defined?", bind_local_variable_defined_p, 1); diff --git a/test/ruby/test_variable.rb b/test/ruby/test_variable.rb index 32b3d61573..321cd7e4c5 100644 --- a/test/ruby/test_variable.rb +++ b/test/ruby/test_variable.rb @@ -83,6 +83,17 @@ class TestVariable < Test::Unit::TestCase end.call end + def local_variables_of(bind) + this_should_not_be_in_bind = 2 + bind.local_variables + end + + def test_local_variables_from_other_method_binding + feature8773 = '[Feature #8773]' + x = 1 + assert_equal([:x], local_variables_of(binding), feature8773) + end + def test_global_variable_0 assert_in_out_err(["-e", "$0='t'*1000;print $0"], "", /\At+\z/, []) end