summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog5
-rw-r--r--ext/-test-/iter/break.c15
-rw-r--r--ext/-test-/iter/extconf.rb1
-rw-r--r--include/ruby/ruby.h1
-rw-r--r--test/-ext-/iter/test_iter_break.rb9
-rw-r--r--vm.c15
-rw-r--r--vm_eval.c1
7 files changed, 43 insertions, 4 deletions
diff --git a/ChangeLog b/ChangeLog
index 65d1cea4ac..0eff2b79a2 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,8 @@
+Tue Jan 24 14:20:42 2012 Nobuyoshi Nakada <nobu@ruby-lang.org>
+
+ * vm.c (rb_iter_break_value): new function to break a block with
+ the value. [ruby-dev:45132] [Feature #5895]
+
Tue Jan 24 12:58:41 2012 Yukihiro Matsumoto <matz@ruby-lang.org>
* object.c (rb_Hash): add Kernel#Hash conversion method like
diff --git a/ext/-test-/iter/break.c b/ext/-test-/iter/break.c
new file mode 100644
index 0000000000..78c7da6491
--- /dev/null
+++ b/ext/-test-/iter/break.c
@@ -0,0 +1,15 @@
+#include <ruby.h>
+
+static VALUE
+iter_break_value(VALUE self, VALUE val)
+{
+ rb_iter_break_value(val);
+ return self; /* not reached */
+}
+
+void
+Init_break(void)
+{
+ VALUE breakable = rb_define_module_under(rb_define_module("Bug"), "Breakable");
+ rb_define_module_function(breakable, "iter_break", iter_break_value, 1);
+}
diff --git a/ext/-test-/iter/extconf.rb b/ext/-test-/iter/extconf.rb
new file mode 100644
index 0000000000..695b5e9f6d
--- /dev/null
+++ b/ext/-test-/iter/extconf.rb
@@ -0,0 +1 @@
+create_makefile("-test-/iter/break")
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index 189f2fc8e1..ef58a7d8b0 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -1185,6 +1185,7 @@ NORETURN(void rb_bug_errno(const char*, int));
NORETURN(void rb_sys_fail(const char*));
NORETURN(void rb_mod_sys_fail(VALUE, const char*));
NORETURN(void rb_iter_break(void));
+NORETURN(void rb_iter_break_value(VALUE));
NORETURN(void rb_exit(int));
NORETURN(void rb_notimplement(void));
VALUE rb_syserr_new(int, const char *);
diff --git a/test/-ext-/iter/test_iter_break.rb b/test/-ext-/iter/test_iter_break.rb
new file mode 100644
index 0000000000..03797a897d
--- /dev/null
+++ b/test/-ext-/iter/test_iter_break.rb
@@ -0,0 +1,9 @@
+require 'test/unit'
+require '-test-/iter/break'
+
+class TestIterBreak < Test::Unit::TestCase
+ def test_iter_break
+ feature5895 = '[ruby-dev:45132]'
+ assert_equal(42, 1.times{Bug::Breakable.iter_break(42)}, feature5895)
+ end
+end
diff --git a/vm.c b/vm.c
index 4e713d3bf3..86217098cb 100644
--- a/vm.c
+++ b/vm.c
@@ -987,23 +987,29 @@ rb_vm_jump_tag_but_local_jump(int state, VALUE val)
JUMP_TAG(state);
}
-NORETURN(static void vm_iter_break(rb_thread_t *th));
+NORETURN(static void vm_iter_break(rb_thread_t *th, VALUE val));
static void
-vm_iter_break(rb_thread_t *th)
+vm_iter_break(rb_thread_t *th, VALUE val)
{
rb_control_frame_t *cfp = th->cfp;
VALUE *dfp = GC_GUARDED_PTR_REF(*cfp->dfp);
th->state = TAG_BREAK;
- th->errinfo = (VALUE)NEW_THROW_OBJECT(Qnil, (VALUE)dfp, TAG_BREAK);
+ th->errinfo = (VALUE)NEW_THROW_OBJECT(val, (VALUE)dfp, TAG_BREAK);
TH_JUMP_TAG(th, TAG_BREAK);
}
void
rb_iter_break(void)
{
- vm_iter_break(GET_THREAD());
+ vm_iter_break(GET_THREAD(), Qnil);
+}
+
+void
+rb_iter_break_value(VALUE val)
+{
+ vm_iter_break(GET_THREAD(), val);
}
/* optimization: redefine management */
@@ -1352,6 +1358,7 @@ vm_exec(rb_thread_t *th)
#endif
}
th->errinfo = Qnil;
+ th->state = 0;
goto vm_loop_start;
}
}
diff --git a/vm_eval.c b/vm_eval.c
index 7b37b1002f..0ee81797ec 100644
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -906,6 +906,7 @@ rb_iterate(VALUE (* it_proc) (VALUE), VALUE data1,
state = 0;
th->state = 0;
th->errinfo = Qnil;
+ retval = GET_THROWOBJ_VAL(err);
/* check skipped frame */
while (th->cfp != cfp) {