summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authornobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-12-08 05:27:10 +0000
committernobu <nobu@b2dd03c8-39d4-4d8f-98ff-823fe69b080e>2015-12-08 05:27:10 +0000
commit506b25aabf66c54cffe478958f8672f7fe6fa669 (patch)
treee495e3702055d35e72535494ac9bef541ca5a81c
parentcb3b463a50dee9d4e3d174dc9b1d7c8181678cd4 (diff)
error.c: name_err_local_variables
* error.c (name_err_local_variables): new method NameError#local_variables for internal use only. [Feature #11777] git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52942 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
-rw-r--r--ChangeLog6
-rw-r--r--error.c40
-rw-r--r--iseq.c6
-rw-r--r--iseq.h3
-rw-r--r--test/ruby/test_exception.rb10
-rw-r--r--vm.c11
6 files changed, 75 insertions, 1 deletions
diff --git a/ChangeLog b/ChangeLog
index 66b74295422..acf88edc08f 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,9 @@
+Tue Dec 8 14:27:07 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * error.c (name_err_local_variables): new method
+ NameError#local_variables for internal use only.
+ [Feature #11777]
+
Tue Dec 8 14:20:38 2015 Nobuyoshi Nakada <nobu@ruby-lang.org>
* marshal.c (w_objivar): skip internal instance variables in
diff --git a/error.c b/error.c
index 816d704a1eb..5606612bb55 100644
--- a/error.c
+++ b/error.c
@@ -35,6 +35,9 @@
#define WEXITSTATUS(status) (status)
#endif
+VALUE rb_iseqw_local_variables(VALUE iseqval);
+VALUE rb_iseqw_new(const rb_iseq_t *);
+
VALUE rb_eEAGAIN;
VALUE rb_eEWOULDBLOCK;
VALUE rb_eEINPROGRESS;
@@ -660,7 +663,7 @@ static VALUE rb_eNOERROR;
static ID id_new, id_cause, id_message, id_backtrace;
static ID id_name, id_args, id_Errno, id_errno, id_i_path;
-static ID id_receiver;
+static ID id_receiver, id_iseq, id_local_variables;
extern ID ruby_static_id_status;
#define id_bt idBt
#define id_bt_locations idBt_locations
@@ -1102,10 +1105,18 @@ static VALUE
name_err_initialize(int argc, VALUE *argv, VALUE self)
{
VALUE name;
+ VALUE iseqw = Qnil;
name = (argc > 1) ? argv[--argc] : Qnil;
rb_call_super(argc, argv);
rb_ivar_set(self, id_name, name);
+ {
+ rb_thread_t *th = GET_THREAD();
+ rb_control_frame_t *cfp =
+ rb_vm_get_ruby_level_next_cfp(th, RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp));
+ if (cfp) iseqw = rb_iseqw_new(cfp->iseq);
+ }
+ rb_ivar_set(self, id_iseq, iseqw);
return self;
}
@@ -1123,6 +1134,30 @@ name_err_name(VALUE self)
}
/*
+ * call-seq:
+ * name_error.local_variables -> array
+ *
+ * Return a list of the local variable names defined where this
+ * NameError exception was raised.
+ *
+ * Internal use only.
+ */
+
+static VALUE
+name_err_local_variables(VALUE self)
+{
+ VALUE vars = rb_attr_get(self, id_local_variables);
+
+ if (NIL_P(vars)) {
+ VALUE iseqw = rb_attr_get(self, id_iseq);
+ if (!NIL_P(iseqw)) vars = rb_iseqw_local_variables(iseqw);
+ if (NIL_P(vars)) vars = rb_ary_new();
+ rb_ivar_set(self, id_local_variables, vars);
+ }
+ return vars;
+}
+
+/*
* call-seq:
* NoMethodError.new(msg, name [, args]) -> no_method_error
*
@@ -1942,6 +1977,7 @@ Init_Exception(void)
rb_define_method(rb_eNameError, "initialize", name_err_initialize, -1);
rb_define_method(rb_eNameError, "name", name_err_name, 0);
rb_define_method(rb_eNameError, "receiver", name_err_receiver, 0);
+ rb_define_method(rb_eNameError, "local_variables", name_err_local_variables, 0);
rb_cNameErrorMesg = rb_define_class_under(rb_eNameError, "message", rb_cData);
rb_define_method(rb_cNameErrorMesg, "==", name_err_mesg_equal, 1);
rb_define_method(rb_cNameErrorMesg, "to_str", name_err_mesg_to_str, 0);
@@ -1974,9 +2010,11 @@ Init_Exception(void)
id_name = rb_intern_const("name");
id_args = rb_intern_const("args");
id_receiver = rb_intern_const("receiver");
+ id_local_variables = rb_intern_const("local_variables");
id_Errno = rb_intern_const("Errno");
id_errno = rb_intern_const("errno");
id_i_path = rb_intern_const("@path");
+ id_iseq = rb_make_internal_id();
}
void
diff --git a/iseq.c b/iseq.c
index 17bfee49812..84d2d146893 100644
--- a/iseq.c
+++ b/iseq.c
@@ -2319,6 +2319,12 @@ rb_iseqw_line_trace_specify(VALUE iseqval, VALUE pos, VALUE set)
return data.prev == 1 ? Qtrue : Qfalse;
}
+VALUE
+rb_iseqw_local_variables(VALUE iseqval)
+{
+ return rb_iseq_local_variables(iseqw_check(iseqval));
+}
+
/*
* Document-class: RubyVM::InstructionSequence
*
diff --git a/iseq.h b/iseq.h
index 2b18d568933..c55119d01e0 100644
--- a/iseq.h
+++ b/iseq.h
@@ -214,6 +214,9 @@ enum defined_type {
VALUE rb_iseq_defined_string(enum defined_type type);
void rb_iseq_make_compile_option(struct rb_compile_option_struct *option, VALUE opt);
+/* vm.c */
+VALUE rb_iseq_local_variables(const rb_iseq_t *iseq);
+
RUBY_SYMBOL_EXPORT_END
#endif /* RUBY_ISEQ_H */
diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb
index fd58ed5933d..707a99f8059 100644
--- a/test/ruby/test_exception.rb
+++ b/test/ruby/test_exception.rb
@@ -688,6 +688,16 @@ end.join
assert_equal(:foo, e.name)
assert_equal([1, 2], e.args)
assert_same(obj, e.receiver)
+ def obj.test(a, b=nil, *c, &d)
+ e = a
+ 1.times {|f| g = foo}
+ end
+ e = assert_raise(NameError) {
+ obj.test(3)
+ }
+ assert_equal(:foo, e.name)
+ assert_same(obj, e.receiver)
+ assert_equal(%i[a b c d e f g], e.local_variables.sort)
end
def test_output_string_encoding
diff --git a/vm.c b/vm.c
index be9c7b2f934..d6c8f1b6221 100644
--- a/vm.c
+++ b/vm.c
@@ -755,6 +755,17 @@ rb_vm_env_local_variables(const rb_env_t *env)
return local_var_list_finish(&vars);
}
+VALUE
+rb_iseq_local_variables(const rb_iseq_t *iseq)
+{
+ struct local_var_list vars;
+ local_var_list_init(&vars);
+ while (collect_local_variables_in_iseq(iseq, &vars)) {
+ iseq = iseq->body->parent_iseq;
+ }
+ return local_var_list_finish(&vars);
+}
+
/* Proc */
static inline VALUE