ruby-changes:32394
From: nobu <ko1@a...>
Date: Tue, 31 Dec 2013 23:49:24 +0900 (JST)
Subject: [ruby-changes:32394] nobu:r44473 (trunk): eval.c: raise with cause
nobu 2013-12-31 23:49:16 +0900 (Tue, 31 Dec 2013) New Revision: 44473 http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=44473 Log: eval.c: raise with cause * eval.c (rb_f_raise): add cause: optional keyword argument. [ruby-core:58610] [Feature #8257] [EXPERIMENTAL] Modified files: trunk/ChangeLog trunk/eval.c trunk/test/ruby/test_exception.rb Index: ChangeLog =================================================================== --- ChangeLog (revision 44472) +++ ChangeLog (revision 44473) @@ -1,3 +1,8 @@ https://github.com/ruby/ruby/blob/trunk/ChangeLog#L1 +Tue Dec 31 23:49:07 2013 Nobuyoshi Nakada <nobu@r...> + + * eval.c (rb_f_raise): add cause: optional keyword argument. + [ruby-core:58610] [Feature #8257] [EXPERIMENTAL] + Tue Dec 31 21:44:17 2013 Akio Tajima <artonx@y...> * win32/Makefile.sub: remove HAVE_FSEEKO because fseeko removed from win32/win32.c Index: eval.c =================================================================== --- eval.c (revision 44472) +++ eval.c (revision 44473) @@ -20,7 +20,7 @@ https://github.com/ruby/ruby/blob/trunk/eval.c#L20 #include "vm_core.h" #include "probes_helper.h" -NORETURN(void rb_raise_jump(VALUE)); +NORETURN(void rb_raise_jump(VALUE, VALUE)); VALUE rb_eLocalJumpError; VALUE rb_eSysStackError; @@ -429,7 +429,7 @@ rb_frozen_class_p(VALUE klass) https://github.com/ruby/ruby/blob/trunk/eval.c#L429 } } -NORETURN(static void rb_longjmp(int, volatile VALUE)); +NORETURN(static void rb_longjmp(int, volatile VALUE, volatile VALUE)); static VALUE get_errinfo(void); static VALUE get_thread_errinfo(rb_thread_t *th); @@ -460,21 +460,27 @@ exc_setup_cause(VALUE exc, VALUE cause) https://github.com/ruby/ruby/blob/trunk/eval.c#L460 } static void -setup_exception(rb_thread_t *th, int tag, volatile VALUE mesg) +setup_exception(rb_thread_t *th, int tag, volatile VALUE mesg, VALUE cause) { VALUE at; VALUE e; const char *file; volatile int line = 0; + int nocause = 0; if (NIL_P(mesg)) { mesg = th->errinfo; if (INTERNAL_EXCEPTION_P(mesg)) JUMP_TAG(TAG_FATAL); + nocause = 1; } if (NIL_P(mesg)) { mesg = rb_exc_new(rb_eRuntimeError, 0, 0); + nocause = 0; } - exc_setup_cause(mesg, get_thread_errinfo(th)); + if (cause == Qundef) { + cause = nocause ? Qnil : get_thread_errinfo(th); + } + exc_setup_cause(mesg, cause); file = rb_sourcefile(); if (file) line = rb_sourceline(); @@ -548,10 +554,10 @@ setup_exception(rb_thread_t *th, int tag https://github.com/ruby/ruby/blob/trunk/eval.c#L554 } static void -rb_longjmp(int tag, volatile VALUE mesg) +rb_longjmp(int tag, volatile VALUE mesg, VALUE cause) { rb_thread_t *th = GET_THREAD(); - setup_exception(th, tag, mesg); + setup_exception(th, tag, mesg, cause); rb_thread_raised_clear(th); JUMP_TAG(tag); } @@ -564,7 +570,7 @@ rb_exc_raise(VALUE mesg) https://github.com/ruby/ruby/blob/trunk/eval.c#L570 if (!NIL_P(mesg)) { mesg = make_exception(1, &mesg, FALSE); } - rb_longjmp(TAG_RAISE, mesg); + rb_longjmp(TAG_RAISE, mesg, Qundef); } void @@ -573,7 +579,7 @@ rb_exc_fatal(VALUE mesg) https://github.com/ruby/ruby/blob/trunk/eval.c#L579 if (!NIL_P(mesg)) { mesg = make_exception(1, &mesg, FALSE); } - rb_longjmp(TAG_FATAL, mesg); + rb_longjmp(TAG_FATAL, mesg, Qnil); } void @@ -582,6 +588,31 @@ rb_interrupt(void) https://github.com/ruby/ruby/blob/trunk/eval.c#L588 rb_raise(rb_eInterrupt, "%s", ""); } +enum {raise_opt_cause, raise_max_opt}; + +static int +extract_raise_opts(int argc, VALUE *argv, VALUE *opts) +{ + int i; + if (argc > 0) { + VALUE opt = argv[argc-1]; + if (RB_TYPE_P(opt, T_HASH)) { + VALUE kw = rb_extract_keywords(&opt); + if (!opt) --argc; + if (kw) { + ID keywords[1]; + CONST_ID(keywords[0], "cause"); + rb_get_kwargs(kw, keywords, 0, 1, opts); + return argc; + } + } + } + for (i = 0; i < raise_max_opt; ++i) { + opts[i] = Qundef; + } + return argc; +} + /* * call-seq: * raise @@ -610,14 +641,20 @@ static VALUE https://github.com/ruby/ruby/blob/trunk/eval.c#L641 rb_f_raise(int argc, VALUE *argv) { VALUE err; + VALUE opts[raise_max_opt], *const cause = &opts[raise_opt_cause]; + + argc = extract_raise_opts(argc, argv, opts); if (argc == 0) { + if (*cause != Qundef) { + rb_raise(rb_eArgError, "only cause is given with no arguments"); + } err = get_errinfo(); if (!NIL_P(err)) { argc = 1; argv = &err; } } - rb_raise_jump(rb_make_exception(argc, argv)); + rb_raise_jump(rb_make_exception(argc, argv), *cause); UNREACHABLE; } @@ -680,7 +717,7 @@ rb_make_exception(int argc, VALUE *argv) https://github.com/ruby/ruby/blob/trunk/eval.c#L717 } void -rb_raise_jump(VALUE mesg) +rb_raise_jump(VALUE mesg, VALUE cause) { rb_thread_t *th = GET_THREAD(); rb_control_frame_t *cfp = th->cfp; @@ -691,7 +728,7 @@ rb_raise_jump(VALUE mesg) https://github.com/ruby/ruby/blob/trunk/eval.c#L728 th->cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(th->cfp); EXEC_EVENT_HOOK(th, RUBY_EVENT_C_RETURN, self, mid, klass, Qnil); - setup_exception(th, TAG_RAISE, mesg); + setup_exception(th, TAG_RAISE, mesg, cause); rb_thread_raised_clear(th); JUMP_TAG(TAG_RAISE); Index: test/ruby/test_exception.rb =================================================================== --- test/ruby/test_exception.rb (revision 44472) +++ test/ruby/test_exception.rb (revision 44473) @@ -517,4 +517,18 @@ end.join https://github.com/ruby/ruby/blob/trunk/test/ruby/test_exception.rb#L517 } assert_not_same(e, e.cause, "#{msg}: should not be recursive") end + + def test_raise_with_cause + msg = "[Feature #8257]" + cause = ArgumentError.new("foobar") + e = assert_raise(RuntimeError) {raise msg, cause: cause} + assert_same(cause, e.cause) + end + + def test_cause_with_no_arguments + cause = ArgumentError.new("foobar") + assert_raise_with_message(ArgumentError, /with no arguments/) do + raise cause: cause + end + end end -- ML: ruby-changes@q... Info: http://www.atdot.net/~ko1/quickml/