summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--include/mruby.h1
-rw-r--r--mrbgems/mruby-enum-ext/mrblib/enum.rb68
-rw-r--r--mrbgems/mruby-enum-ext/test/enum.rb22
-rw-r--r--mrbgems/mruby-fiber/src/fiber.c77
-rw-r--r--mrbgems/mruby-fiber/test/fiber.rb92
-rw-r--r--mrbgems/mruby-range-ext/src/range.c6
-rw-r--r--mrbgems/mruby-sprintf/src/sprintf.c2
-rw-r--r--mrblib/hash.rb2
-rw-r--r--src/class.c3
-rw-r--r--src/string.c4
-rw-r--r--src/vm.c2
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 */
diff --git a/src/vm.c b/src/vm.c
index ffdbb6182..8504cc34c 100644
--- a/src/vm.c
+++ b/src/vm.c
@@ -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;
}