summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--NEWS3
-rw-r--r--error.c1
-rw-r--r--test/lib/test/unit/assertions.rb4
-rw-r--r--test/ruby/test_exception.rb11
-rw-r--r--test/ruby/test_fiber.rb2
-rw-r--r--vm_eval.c81
7 files changed, 98 insertions, 9 deletions
diff --git a/ChangeLog b/ChangeLog
index 7c5a40b8c9..9bfc33ce37 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Sat Nov 15 16:28:05 2014 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * vm_eval.c (rb_throw_obj): throw UncaughtThrowError instead of
+ ArgumentError. [Feature #10480]
+
Sat Nov 15 14:13:38 2014 Tanaka Akira <akr@fsij.org>
* tool/update-deps: Extend to fix dependencies.
diff --git a/NEWS b/NEWS
index a01adc0beb..8c7d1ea9af 100644
--- a/NEWS
+++ b/NEWS
@@ -67,6 +67,9 @@ with all sufficient information, see the ChangeLog file.
* Kernel
* New methods:
* Kernel#itself
+ * Improvements
+ * Kernel#throw raises UncaughtThrowError, subclass of ArgumentError when
+ there is no corresponding catch block, instead of ArgumentError.
* Process
* Extended method:
diff --git a/error.c b/error.c
index 2f01b5797b..bf4d59aaca 100644
--- a/error.c
+++ b/error.c
@@ -1798,6 +1798,7 @@ syserr_eqq(VALUE self, VALUE exc)
* * Interrupt
* * StandardError -- default for +rescue+
* * ArgumentError
+ * * UncaughtThrowError
* * EncodingError
* * FiberError
* * IOError
diff --git a/test/lib/test/unit/assertions.rb b/test/lib/test/unit/assertions.rb
index 4ba13a81e0..727c54c9d5 100644
--- a/test/lib/test/unit/assertions.rb
+++ b/test/lib/test/unit/assertions.rb
@@ -223,8 +223,8 @@ module Test
ret = catch(tag) do
begin
yield(tag)
- rescue ArgumentError => e
- raise unless thrown = e.message[/\Auncaught throw (.+)\z/m, 1]
+ rescue UncaughtThrowError => e
+ thrown = e.tag
end
msg = message(msg) {
"Expected #{mu_pp(tag)} to have been thrown"\
diff --git a/test/ruby/test_exception.rb b/test/ruby/test_exception.rb
index 4659def1ea..99cacc21f9 100644
--- a/test/ruby/test_exception.rb
+++ b/test/ruby/test_exception.rb
@@ -147,7 +147,7 @@ class TestException < Test::Unit::TestCase
end
def test_catch_throw_noarg
- assert_nothing_raised(ArgumentError) {
+ assert_nothing_raised(UncaughtThrowError) {
result = catch {|obj|
throw obj, :ok
assert(false, "should not reach here")
@@ -157,13 +157,18 @@ class TestException < Test::Unit::TestCase
end
def test_uncaught_throw
- assert_raise_with_message(ArgumentError, /uncaught throw/) {
+ tag = nil
+ e = assert_raise_with_message(UncaughtThrowError, /uncaught throw/) {
catch("foo") {|obj|
- throw obj.dup, :ok
+ tag = obj.dup
+ throw tag, :ok
assert(false, "should not reach here")
}
assert(false, "should not reach here")
}
+ assert_not_nil(tag)
+ assert_same(tag, e.tag)
+ assert_equal(:ok, e.value)
end
def test_catch_throw_in_require
diff --git a/test/ruby/test_fiber.rb b/test/ruby/test_fiber.rb
index 4acfb139e0..7b5ce8190f 100644
--- a/test/ruby/test_fiber.rb
+++ b/test/ruby/test_fiber.rb
@@ -117,7 +117,7 @@ class TestFiber < Test::Unit::TestCase
end
def test_throw
- assert_raise(ArgumentError){
+ assert_raise(UncaughtThrowError){
Fiber.new do
throw :a
end.resume
diff --git a/vm_eval.c b/vm_eval.c
index ded7f75eb5..3d2c7fe33a 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -24,6 +24,8 @@ static VALUE vm_exec(rb_thread_t *th);
static void vm_set_eval_stack(rb_thread_t * th, VALUE iseqval, const NODE *cref, rb_block_t *base_block);
static int vm_collect_local_variables_in_heap(rb_thread_t *th, const VALUE *dfp, const struct local_var_list *vars);
+static VALUE rb_eUncaughtThrow;
+
/* vm_backtrace.c */
VALUE rb_vm_backtrace_str_ary(rb_thread_t *th, int lev, int n);
@@ -1726,11 +1728,75 @@ rb_mod_module_exec(int argc, const VALUE *argv, VALUE mod)
}
/*
+ * Document-class: UncaughtThrowError
+ *
+ * Raised when +throw+ is called with a _tag_ which does not have
+ * corresponding +catch+ block.
+ *
+ * throw "foo", "bar"
+ *
+ * <em>raises the exception:</em>
+ *
+ * UncaughtThrowError: uncaught throw "foo"
+ */
+
+static VALUE
+uncaught_throw_init(int argc, const VALUE *argv, VALUE exc)
+{
+ rb_check_arity(argc, 2, UNLIMITED_ARGUMENTS);
+ rb_call_super(argc - 2, argv + 2);
+ rb_iv_set(exc, "tag", argv[0]);
+ rb_iv_set(exc, "value", argv[1]);
+ return exc;
+}
+
+/*
+ * call-seq:
+ * uncaught_throw.tag -> obj
+ *
+ * Return the tag object which was called for.
+ */
+
+static VALUE
+uncaught_throw_tag(VALUE exc)
+{
+ return rb_iv_get(exc, "tag");
+}
+
+/*
+ * call-seq:
+ * uncaught_throw.value -> obj
+ *
+ * Return the return value which was called for.
+ */
+
+static VALUE
+uncaught_throw_value(VALUE exc)
+{
+ return rb_iv_get(exc, "value");
+}
+
+/*
+ * call-seq:
+ * uncaught_throw.to_s -> string
+ *
+ * Returns formatted message with the inspected tag.
+ */
+
+static VALUE
+uncaught_throw_to_s(VALUE exc)
+{
+ VALUE mesg = rb_attr_get(exc, rb_intern("mesg"));
+ VALUE tag = uncaught_throw_tag(exc);
+ return rb_str_format(1, &tag, mesg);
+}
+
+/*
* call-seq:
* throw(tag [, obj])
*
* Transfers control to the end of the active +catch+ block
- * waiting for _tag_. Raises +ArgumentError+ if there
+ * waiting for _tag_. Raises +UncaughtThrowError+ if there
* is no +catch+ block for the _tag_. The optional second
* parameter supplies a return value for the +catch+ block,
* which otherwise defaults to +nil+. For examples, see
@@ -1761,8 +1827,11 @@ rb_throw_obj(VALUE tag, VALUE value)
tt = tt->prev;
}
if (!tt) {
- VALUE desc = rb_inspect(tag);
- rb_raise(rb_eArgError, "uncaught throw %"PRIsVALUE, desc);
+ VALUE desc[3];
+ desc[0] = tag;
+ desc[1] = value;
+ desc[2] = rb_str_new_cstr("uncaught throw %p");
+ rb_exc_raise(rb_class_new_instance(numberof(desc), desc, rb_eUncaughtThrow));
}
th->errinfo = NEW_THROW_OBJECT(tag, 0, TAG_THROW);
@@ -2058,4 +2127,10 @@ Init_vm_eval(void)
rb_define_method(rb_cModule, "class_exec", rb_mod_module_exec, -1);
rb_define_method(rb_cModule, "module_eval", rb_mod_module_eval, -1);
rb_define_method(rb_cModule, "class_eval", rb_mod_module_eval, -1);
+
+ rb_eUncaughtThrow = rb_define_class("UncaughtThrowError", rb_eArgError);
+ rb_define_method(rb_eUncaughtThrow, "initialize", uncaught_throw_init, -1);
+ rb_define_method(rb_eUncaughtThrow, "tag", uncaught_throw_tag, 0);
+ rb_define_method(rb_eUncaughtThrow, "value", uncaught_throw_value, 0);
+ rb_define_method(rb_eUncaughtThrow, "to_s", uncaught_throw_to_s, 0);
}