diff options
| -rw-r--r-- | include/mruby.h | 1 | ||||
| -rw-r--r-- | mrbgems/mruby-enum-ext/mrblib/enum.rb | 68 | ||||
| -rw-r--r-- | mrbgems/mruby-enum-ext/test/enum.rb | 22 | ||||
| -rw-r--r-- | mrbgems/mruby-fiber/src/fiber.c | 77 | ||||
| -rw-r--r-- | mrbgems/mruby-fiber/test/fiber.rb | 92 | ||||
| -rw-r--r-- | mrbgems/mruby-range-ext/src/range.c | 6 | ||||
| -rw-r--r-- | mrbgems/mruby-sprintf/src/sprintf.c | 2 | ||||
| -rw-r--r-- | mrblib/hash.rb | 2 | ||||
| -rw-r--r-- | src/class.c | 3 | ||||
| -rw-r--r-- | src/string.c | 4 | ||||
| -rw-r--r-- | src/vm.c | 2 |
11 files changed, 247 insertions, 32 deletions
diff --git a/include/mruby.h b/include/mruby.h index 3e0e796d2..5de47ae84 100644 --- a/include/mruby.h +++ b/include/mruby.h @@ -72,6 +72,7 @@ enum mrb_fiber_state { MRB_FIBER_RUNNING, MRB_FIBER_RESUMING, MRB_FIBER_SUSPENDED, + MRB_FIBER_TRANSFERRED, MRB_FIBER_TERMINATED, }; diff --git a/mrbgems/mruby-enum-ext/mrblib/enum.rb b/mrbgems/mruby-enum-ext/mrblib/enum.rb index d0e125850..1849ff4cd 100644 --- a/mrbgems/mruby-enum-ext/mrblib/enum.rb +++ b/mrbgems/mruby-enum-ext/mrblib/enum.rb @@ -267,4 +267,72 @@ module Enumerable ary end alias collect_concat flat_map + + ## + # call-seq: + # enum.max_by {|obj| block } -> obj + # enum.max_by -> an_enumerator + # + # Returns the object in <i>enum</i> that gives the maximum + # value from the given block. + # + # If no block is given, an enumerator is returned instead. + # + # %w[albatross dog horse].max_by {|x| x.length } #=> "albatross" + + def max_by(&block) + return to_enum :max_by unless block_given? + + first = true + max = nil + max_cmp = nil + + self.each do |*val| + if first + max = val.__svalue + max_cmp = block.call(*val) + first = false + else + if (cmp = block.call(*val)) > max_cmp + max = val.__svalue + max_cmp = cmp + end + end + end + max + end + + ## + # call-seq: + # enum.min_by {|obj| block } -> obj + # enum.min_by -> an_enumerator + # + # Returns the object in <i>enum</i> that gives the minimum + # value from the given block. + # + # If no block is given, an enumerator is returned instead. + # + # %w[albatross dog horse].min_by {|x| x.length } #=> "dog" + + def min_by(&block) + return to_enum :min_by unless block_given? + + first = true + min = nil + min_cmp = nil + + self.each do |*val| + if first + min = val.__svalue + min_cmp = block.call(*val) + first = false + else + if (cmp = block.call(*val)) < min_cmp + min = val.__svalue + min_cmp = cmp + end + end + end + min + end end diff --git a/mrbgems/mruby-enum-ext/test/enum.rb b/mrbgems/mruby-enum-ext/test/enum.rb index 9523b497e..ba0851582 100644 --- a/mrbgems/mruby-enum-ext/test/enum.rb +++ b/mrbgems/mruby-enum-ext/test/enum.rb @@ -47,11 +47,21 @@ assert("Enumerable#sort_by") do end assert("Enumerable#first") do - a = [1, 2, 3] + a = Object.new + a.extend Enumerable + def a.each + yield 1 + yield 2 + yield 3 + end assert_equal 1, a.first assert_equal [1, 2], a.first(2) assert_equal [1, 2, 3], a.first(10) - assert_nil [].first + a = Object.new + a.extend Enumerable + def a.each + end + assert_nil a.first end assert("Enumerable#count") do @@ -66,3 +76,11 @@ assert("Enumerable#flat_map") do assert_equal [1, -1, 2, -2, 3, -3, 4, -4], [1, 2, 3, 4].flat_map { |e| [e, -e] } assert_equal [1, 2, 100, 3, 4, 100], [[1, 2], [3, 4]].flat_map { |e| e + [100] } end + +assert("Enumerable#max_by") do + assert_equal "albatross", %w[albatross dog horse].max_by { |x| x.length } +end + +assert("Enumerable#min_by") do + assert_equal "dog", %w[albatross dog horse].min_by { |x| x.length } +end diff --git a/mrbgems/mruby-fiber/src/fiber.c b/mrbgems/mruby-fiber/src/fiber.c index 433b11693..457f50d41 100644 --- a/mrbgems/mruby-fiber/src/fiber.c +++ b/mrbgems/mruby-fiber/src/fiber.c @@ -126,7 +126,7 @@ fiber_check(mrb_state *mrb, mrb_value fib) } static mrb_value -fiber_result(mrb_state *mrb, mrb_value *a, int len) +fiber_result(mrb_state *mrb, const mrb_value *a, int len) { if (len == 0) return mrb_nil_value(); if (len == 1) return a[0]; @@ -136,27 +136,10 @@ fiber_result(mrb_state *mrb, mrb_value *a, int len) /* mark return from context modifying method */ #define MARK_CONTEXT_MODIFY(c) (c)->ci->target_class = NULL -/* - * call-seq: - * fiber.resume(args, ...) -> obj - * - * Resumes the fiber from the point at which the last <code>Fiber.yield</code> - * was called, or starts running it if it is the first call to - * <code>resume</code>. Arguments passed to resume will be the value of - * the <code>Fiber.yield</code> expression or will be passed as block - * parameters to the fiber's block if this is the first <code>resume</code>. - * - * Alternatively, when resume is called it evaluates to the arguments passed - * to the next <code>Fiber.yield</code> statement inside the fiber's block - * or to the block value if it runs to completion without any - * <code>Fiber.yield</code> - */ static mrb_value -fiber_resume(mrb_state *mrb, mrb_value self) +fiber_switch(mrb_state *mrb, mrb_value self, int len, const mrb_value *a, mrb_bool resume) { struct mrb_context *c = fiber_check(mrb, self); - mrb_value *a; - int len; mrb_callinfo *ci; for (ci = c->ci; ci >= c->cibase; ci--) { @@ -164,6 +147,9 @@ fiber_resume(mrb_state *mrb, mrb_value self) mrb_raise(mrb, E_FIBER_ERROR, "can't cross C function boundary"); } } + if (resume && c->status == MRB_FIBER_TRANSFERRED) { + mrb_raise(mrb, E_FIBER_ERROR, "resuming transfered fiber"); + } if (c->status == MRB_FIBER_RUNNING || c->status == MRB_FIBER_RESUMING) { mrb_raise(mrb, E_FIBER_ERROR, "double resume"); } @@ -171,7 +157,8 @@ fiber_resume(mrb_state *mrb, mrb_value self) mrb_raise(mrb, E_FIBER_ERROR, "resuming dead fiber"); } mrb_get_args(mrb, "*", &a, &len); - mrb->c->status = MRB_FIBER_RESUMING; + mrb->c->status = resume ? MRB_FIBER_RESUMING : MRB_FIBER_TRANSFERRED; + c->prev = resume ? mrb->c : (c->prev ? c->prev : mrb->root_c); if (c->status == MRB_FIBER_CREATED) { mrb_value *b = c->stack+1; mrb_value *e = b + len; @@ -180,7 +167,6 @@ fiber_resume(mrb_state *mrb, mrb_value self) *b++ = *a++; } c->cibase->argc = len; - c->prev = mrb->c; if (c->prev->fib) mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib); mrb_write_barrier(mrb, (struct RBasic*)c->fib); @@ -191,7 +177,6 @@ fiber_resume(mrb_state *mrb, mrb_value self) return c->ci->proc->env->stack[0]; } MARK_CONTEXT_MODIFY(c); - c->prev = mrb->c; if (c->prev->fib) mrb_field_write_barrier(mrb, (struct RBasic*)c->fib, (struct RBasic*)c->prev->fib); mrb_write_barrier(mrb, (struct RBasic*)c->fib); @@ -202,6 +187,30 @@ fiber_resume(mrb_state *mrb, mrb_value self) /* * call-seq: + * fiber.resume(args, ...) -> obj + * + * Resumes the fiber from the point at which the last <code>Fiber.yield</code> + * was called, or starts running it if it is the first call to + * <code>resume</code>. Arguments passed to resume will be the value of + * the <code>Fiber.yield</code> expression or will be passed as block + * parameters to the fiber's block if this is the first <code>resume</code>. + * + * Alternatively, when resume is called it evaluates to the arguments passed + * to the next <code>Fiber.yield</code> statement inside the fiber's block + * or to the block value if it runs to completion without any + * <code>Fiber.yield</code> + */ +static mrb_value +fiber_resume(mrb_state *mrb, mrb_value self) +{ + mrb_value *a; + int len; + mrb_get_args(mrb, "*", &a, &len); + return fiber_switch(mrb, self, len, a, TRUE); +} + +/* + * call-seq: * fiber.alive? -> true or false * * Returns true if the fiber can still be resumed. After finishing @@ -226,6 +235,29 @@ fiber_eq(mrb_state *mrb, mrb_value self) return mrb_bool_value(fiber_ptr(self) == fiber_ptr(other)); } +static mrb_value +fiber_transfer(mrb_state *mrb, mrb_value self) +{ + struct mrb_context *c = fiber_check(mrb, self); + mrb_value* a; + int len; + + mrb_get_args(mrb, "*", &a, &len); + + if (c == mrb->root_c) { + mrb->c->status = MRB_FIBER_TRANSFERRED; + mrb->c = c; + c->status = MRB_FIBER_RUNNING; + MARK_CONTEXT_MODIFY(c); + return fiber_result(mrb, a, len); + } + + if (c == mrb->c) { + return fiber_result(mrb, a, len); + } + + return fiber_switch(mrb, self, len, a, FALSE); +} mrb_value mrb_fiber_yield(mrb_state *mrb, int len, mrb_value *a) @@ -300,6 +332,7 @@ mrb_mruby_fiber_gem_init(mrb_state* mrb) mrb_define_method(mrb, c, "initialize", fiber_init, MRB_ARGS_NONE()); mrb_define_method(mrb, c, "resume", fiber_resume, MRB_ARGS_ANY()); + mrb_define_method(mrb, c, "transfer", fiber_transfer, MRB_ARGS_ANY()); mrb_define_method(mrb, c, "alive?", fiber_alive_p, MRB_ARGS_NONE()); mrb_define_method(mrb, c, "==", fiber_eq, MRB_ARGS_REQ(1)); diff --git a/mrbgems/mruby-fiber/test/fiber.rb b/mrbgems/mruby-fiber/test/fiber.rb index e10878bf4..c2bae2259 100644 --- a/mrbgems/mruby-fiber/test/fiber.rb +++ b/mrbgems/mruby-fiber/test/fiber.rb @@ -8,6 +8,27 @@ assert('Fiber#resume') { f.resume(2) } +assert('Fiber#transfer') do + f2 = nil + f1 = Fiber.new do |v| + Fiber.yield v + f2.transfer + end + f2 = Fiber.new do + f1.transfer(1) + f1.transfer(1) + Fiber.yield 2 + end + assert_equal 1, f2.resume + assert_raise(FiberError) { f2.resume } + assert_equal 2, f2.transfer + assert_raise(FiberError) { f1.resume } + f1.transfer + f2.resume + assert_false f1.alive? + assert_false f2.alive? +end + assert('Fiber#alive?') { f = Fiber.new{ Fiber.yield } f.resume @@ -114,3 +135,74 @@ end assert('Fiber without block') do assert_raise(ArgumentError) { Fiber.new } end + + +assert('Transfer to self.') do + result = [] + f = Fiber.new { result << :start; f.transfer; result << :end } + f.transfer + assert_equal [:start, :end], result + + result = [] + f = Fiber.new { result << :start; f.transfer; result << :end } + f.resume + assert_equal [:start, :end], result +end + +assert('Resume transferred fiber') do + f = Fiber.new { + assert_raise(FiberError) { f.resume } + } + f.transfer +end + +assert('Root fiber transfer.') do + result = nil + root = Fiber.current + f = Fiber.new { + result = :ok + root.transfer + } + f.resume + assert_true f.alive? + assert_equal :ok, result +end + +assert('Break nested fiber with root fiber transfer') do + root = Fiber.current + + result = nil + f2 = nil + f1 = Fiber.new { + Fiber.yield f2.resume + result = :f1 + } + f2 = Fiber.new { + result = :to_root + root.transfer :from_f2 + result = :f2 + } + assert_equal :from_f2, f1.resume + assert_equal :to_root, result + assert_equal :f2, f2.transfer + assert_equal :f2, result + assert_false f2.alive? + assert_equal :f1, f1.resume + assert_equal :f1, result + assert_false f1.alive? +end + +assert('CRuby Fiber#transfer test.') do + ary = [] + f2 = nil + f1 = Fiber.new{ + ary << f2.transfer(:foo) + :ok + } + f2 = Fiber.new{ + ary << f1.transfer(:baz) + :ng + } + assert_equal :ok, f1.transfer + assert_equal [:baz], ary +end diff --git a/mrbgems/mruby-range-ext/src/range.c b/mrbgems/mruby-range-ext/src/range.c index c980ecffd..9fbfd431f 100644 --- a/mrbgems/mruby-range-ext/src/range.c +++ b/mrbgems/mruby-range-ext/src/range.c @@ -78,16 +78,16 @@ mrb_range_cover(mrb_state *mrb, mrb_value range) static mrb_value mrb_range_first(mrb_state *mrb, mrb_value range) { - mrb_value num; + mrb_int num; mrb_value array; struct RRange *r = mrb_range_ptr(range); - if (mrb_get_args(mrb, "|o", &num) == 0) { + if (mrb_get_args(mrb, "|i", &num) == 0) { return r->edges->beg; } array = mrb_funcall(mrb, range, "to_a", 0); - return mrb_funcall(mrb, array, "first", 1, mrb_to_int(mrb, num)); + return mrb_funcall(mrb, array, "first", 1, mrb_fixnum_value(num)); } /* diff --git a/mrbgems/mruby-sprintf/src/sprintf.c b/mrbgems/mruby-sprintf/src/sprintf.c index 90ca913d5..4867ceb5e 100644 --- a/mrbgems/mruby-sprintf/src/sprintf.c +++ b/mrbgems/mruby-sprintf/src/sprintf.c @@ -843,7 +843,7 @@ retry: else { val = mrb_fixnum_to_str(mrb, mrb_fixnum_value(v), base); } - v = mrb_fixnum(mrb_str_to_inum(mrb, val, 10, 0/*Qfalse*/)); + v = mrb_fixnum(mrb_str_to_inum(mrb, val, 10, FALSE)); } if (sign) { char c = *p; diff --git a/mrblib/hash.rb b/mrblib/hash.rb index 20dd67091..0423f7edc 100644 --- a/mrblib/hash.rb +++ b/mrblib/hash.rb @@ -185,7 +185,7 @@ class Hash end def __update(h) - h.each{|k,v| self[k] = v} + h.each_key{|k| self[k] = h[v]} self end end diff --git a/src/class.c b/src/class.c index 30d376648..e73a28c56 100644 --- a/src/class.c +++ b/src/class.c @@ -612,6 +612,9 @@ mrb_get_args(mrb_state *mrb, const char *format, ...) *p = (mrb_int)f; } break; + case MRB_TT_STRING: + mrb_raise(mrb, E_TYPE_ERROR, "String can't be coerced into int"); + break; default: *p = mrb_fixnum(mrb_Integer(mrb, *sp)); break; diff --git a/src/string.c b/src/string.c index 9d6e6a04b..1b82802f9 100644 --- a/src/string.c +++ b/src/string.c @@ -2152,7 +2152,7 @@ mrb_str_to_i(mrb_state *mrb, mrb_value self) if (base < 0) { mrb_raisef(mrb, E_ARGUMENT_ERROR, "illegal radix %S", mrb_fixnum_value(base)); } - return mrb_str_to_inum(mrb, self, base, 0/*Qfalse*/); + return mrb_str_to_inum(mrb, self, base, FALSE); } double @@ -2262,7 +2262,7 @@ mrb_str_to_dbl(mrb_state *mrb, mrb_value str, mrb_bool badcheck) static mrb_value mrb_str_to_f(mrb_state *mrb, mrb_value self) { - return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, 0/*Qfalse*/)); + return mrb_float_value(mrb, mrb_str_to_dbl(mrb, self, FALSE)); } /* 15.2.10.5.40 */ @@ -1424,7 +1424,7 @@ RETRY_TRY_BLOCK: goto L_RAISE; } if (mrb->c->prev->ci == mrb->c->prev->cibase) { - mrb_value exc = mrb_exc_new_str_lit(mrb, E_RUNTIME_ERROR, "double resume"); + mrb_value exc = mrb_exc_new_str_lit(mrb, E_FIBER_ERROR, "double resume"); mrb->exc = mrb_obj_ptr(exc); goto L_RAISE; } |
