* 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
Родитель abd5ba5af2
Коммит 558b9191c0
7 изменённых файлов: 98 добавлений и 9 удалений

Просмотреть файл

@ -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.

3
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:

Просмотреть файл

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

Просмотреть файл

@ -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"\

Просмотреть файл

@ -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

Просмотреть файл

@ -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

Просмотреть файл

@ -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);
@ -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));
}
/*
* 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);
}