[前][次][番号順一覧][スレッド一覧]

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/

[前][次][番号順一覧][スレッド一覧]