ruby-changes:36352
From: nobu <ko1@a...>
Date: Sat, 15 Nov 2014 16:28:28 +0900 (JST)
Subject: [ruby-changes:36352] nobu:r48433 (trunk): vm_eval.c: UncaughtThrowError
nobu 2014-11-15 16:28:08 +0900 (Sat, 15 Nov 2014) New Revision: 48433 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=revision&revision=48433 Log: vm_eval.c: UncaughtThrowError * vm_eval.c (rb_throw_obj): throw UncaughtThrowError instead of ArgumentError. [Feature #10480] Modified files: trunk/ChangeLog trunk/NEWS trunk/error.c trunk/test/lib/test/unit/assertions.rb trunk/test/ruby/test_exception.rb trunk/test/ruby/test_fiber.rb trunk/vm_eval.c Index: ChangeLog =================================================================== --- ChangeLog (revision 48432) +++ ChangeLog (revision 48433) @@ -1,3 +1,8 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Sat Nov 15 16:28:05 2014 Nobuyoshi Nakada <nobu@r...> + + * vm_eval.c (rb_throw_obj): throw UncaughtThrowError instead of + ArgumentError. [Feature #10480] + Sat Nov 15 14:13:38 2014 Tanaka Akira <akr@f...> * tool/update-deps: Extend to fix dependencies. Index: vm_eval.c =================================================================== --- vm_eval.c (revision 48432) +++ vm_eval.c (revision 48433) @@ -24,6 +24,8 @@ static VALUE vm_exec(rb_thread_t *th); https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L24 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 https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1728 } /* + * 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) https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L1827 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) https://github.com/ruby/ruby/blob/trunk/vm_eval.c#L2127 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); } Index: error.c =================================================================== --- error.c (revision 48432) +++ error.c (revision 48433) @@ -1798,6 +1798,7 @@ syserr_eqq(VALUE self, VALUE exc) https://github.com/ruby/ruby/blob/trunk/error.c#L1798 * * Interrupt * * StandardError -- default for +rescue+ * * ArgumentError + * * UncaughtThrowError * * EncodingError * * FiberError * * IOError Index: NEWS =================================================================== --- NEWS (revision 48432) +++ NEWS (revision 48433) @@ -67,6 +67,9 @@ with all sufficient information, see the https://github.com/ruby/ruby/blob/trunk/NEWS#L67 * 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: Index: test/ruby/test_fiber.rb =================================================================== --- test/ruby/test_fiber.rb (revision 48432) +++ test/ruby/test_fiber.rb (revision 48433) @@ -117,7 +117,7 @@ class TestFiber < Test::Unit::TestCase https://github.com/ruby/ruby/blob/trunk/test/ruby/test_fiber.rb#L117 end def test_throw - assert_raise(ArgumentError){ + assert_raise(UncaughtThrowError){ Fiber.new do throw :a end.resume Index: test/ruby/test_exception.rb =================================================================== --- test/ruby/test_exception.rb (revision 48432) +++ test/ruby/test_exception.rb (revision 48433) @@ -147,7 +147,7 @@ class TestException < Test::Unit::TestCa https://github.com/ruby/ruby/blob/trunk/test/ruby/test_exception.rb#L147 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::TestCa https://github.com/ruby/ruby/blob/trunk/test/ruby/test_exception.rb#L157 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 Index: test/lib/test/unit/assertions.rb =================================================================== --- test/lib/test/unit/assertions.rb (revision 48432) +++ test/lib/test/unit/assertions.rb (revision 48433) @@ -223,8 +223,8 @@ module Test https://github.com/ruby/ruby/blob/trunk/test/lib/test/unit/assertions.rb#L223 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"\ -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/