vm_eval.c: UncaughtThrowError

* vm_eval.c (rb_throw_obj): throw UncaughtThrowError instead of
  ArgumentError.  [Feature #10480]

git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@48433 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
nobu 2014-11-15 07:28:08 +00:00
parent abd5ba5af2
commit 558b9191c0
7 changed files with 98 additions and 9 deletions

View File

@ -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> Sat Nov 15 14:13:38 2014 Tanaka Akira <akr@fsij.org>
* tool/update-deps: Extend to fix dependencies. * tool/update-deps: Extend to fix dependencies.

3
NEWS
View File

@ -67,6 +67,9 @@ with all sufficient information, see the ChangeLog file.
* Kernel * Kernel
* New methods: * New methods:
* Kernel#itself * Kernel#itself
* Improvements
* Kernel#throw raises UncaughtThrowError, subclass of ArgumentError when
there is no corresponding catch block, instead of ArgumentError.
* Process * Process
* Extended method: * Extended method:

View File

@ -1798,6 +1798,7 @@ syserr_eqq(VALUE self, VALUE exc)
* * Interrupt * * Interrupt
* * StandardError -- default for +rescue+ * * StandardError -- default for +rescue+
* * ArgumentError * * ArgumentError
* * UncaughtThrowError
* * EncodingError * * EncodingError
* * FiberError * * FiberError
* * IOError * * IOError

View File

@ -223,8 +223,8 @@ module Test
ret = catch(tag) do ret = catch(tag) do
begin begin
yield(tag) yield(tag)
rescue ArgumentError => e rescue UncaughtThrowError => e
raise unless thrown = e.message[/\Auncaught throw (.+)\z/m, 1] thrown = e.tag
end end
msg = message(msg) { msg = message(msg) {
"Expected #{mu_pp(tag)} to have been thrown"\ "Expected #{mu_pp(tag)} to have been thrown"\

View File

@ -147,7 +147,7 @@ class TestException < Test::Unit::TestCase
end end
def test_catch_throw_noarg def test_catch_throw_noarg
assert_nothing_raised(ArgumentError) { assert_nothing_raised(UncaughtThrowError) {
result = catch {|obj| result = catch {|obj|
throw obj, :ok throw obj, :ok
assert(false, "should not reach here") assert(false, "should not reach here")
@ -157,13 +157,18 @@ class TestException < Test::Unit::TestCase
end end
def test_uncaught_throw def test_uncaught_throw
assert_raise_with_message(ArgumentError, /uncaught throw/) { tag = nil
e = assert_raise_with_message(UncaughtThrowError, /uncaught throw/) {
catch("foo") {|obj| 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(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 end
def test_catch_throw_in_require def test_catch_throw_in_require

View File

@ -117,7 +117,7 @@ class TestFiber < Test::Unit::TestCase
end end
def test_throw def test_throw
assert_raise(ArgumentError){ assert_raise(UncaughtThrowError){
Fiber.new do Fiber.new do
throw :a throw :a
end.resume end.resume

View File

@ -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 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 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 */ /* vm_backtrace.c */
VALUE rb_vm_backtrace_str_ary(rb_thread_t *th, int lev, int n); VALUE rb_vm_backtrace_str_ary(rb_thread_t *th, int lev, int n);
@ -1725,12 +1727,76 @@ rb_mod_module_exec(int argc, const VALUE *argv, VALUE mod)
return yield_under(mod, mod, rb_ary_new4(argc, argv)); return yield_under(mod, mod, rb_ary_new4(argc, argv));
} }
/*
* 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: * call-seq:
* throw(tag [, obj]) * throw(tag [, obj])
* *
* Transfers control to the end of the active +catch+ block * 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 * is no +catch+ block for the _tag_. The optional second
* parameter supplies a return value for the +catch+ block, * parameter supplies a return value for the +catch+ block,
* which otherwise defaults to +nil+. For examples, see * which otherwise defaults to +nil+. For examples, see
@ -1761,8 +1827,11 @@ rb_throw_obj(VALUE tag, VALUE value)
tt = tt->prev; tt = tt->prev;
} }
if (!tt) { if (!tt) {
VALUE desc = rb_inspect(tag); VALUE desc[3];
rb_raise(rb_eArgError, "uncaught throw %"PRIsVALUE, desc); 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); 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, "class_exec", rb_mod_module_exec, -1);
rb_define_method(rb_cModule, "module_eval", rb_mod_module_eval, -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_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);
} }