summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--enum.c19
-rw-r--r--test/ruby/test_enum.rb16
2 files changed, 28 insertions, 7 deletions
diff --git a/enum.c b/enum.c
index 5c973e643d..362c706235 100644
--- a/enum.c
+++ b/enum.c
@@ -1263,16 +1263,24 @@ struct nmin_data {
const char *method;
};
+static VALUE
+cmpint_reenter_check(struct nmin_data *data, VALUE val)
+{
+ if (RBASIC(data->buf)->klass) {
+ rb_raise(rb_eRuntimeError, "%s reentered", data->method);
+ }
+ return val;
+}
+
static int
nmin_cmp(const void *ap, const void *bp, void *_data)
{
struct cmp_opt_data cmp_opt = { 0, 0 };
struct nmin_data *data = (struct nmin_data *)_data;
VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp;
- if (RBASIC(data->buf)->klass) {
- rb_raise(rb_eRuntimeError, "%s reentered", data->method);
- }
+#define rb_cmpint(cmp, a, b) rb_cmpint(cmpint_reenter_check(data, (cmp)), a, b)
return OPTIMIZED_CMP(a, b, cmp_opt);
+#undef rb_cmpint
}
static int
@@ -1281,13 +1289,10 @@ nmin_block_cmp(const void *ap, const void *bp, void *_data)
struct nmin_data *data = (struct nmin_data *)_data;
VALUE a = *(const VALUE *)ap, b = *(const VALUE *)bp;
VALUE cmp = rb_yield_values(2, a, b);
- if (RBASIC(data->buf)->klass) {
- rb_raise(rb_eRuntimeError, "%s reentered", data->method);
- }
+ cmpint_reenter_check(data, cmp);
return rb_cmpint(cmp, a, b);
}
-
static void
nmin_filter(struct nmin_data *data)
{
diff --git a/test/ruby/test_enum.rb b/test/ruby/test_enum.rb
index 57616791bb..991bda9b3f 100644
--- a/test/ruby/test_enum.rb
+++ b/test/ruby/test_enum.rb
@@ -604,6 +604,22 @@ class TestEnumerable < Test::Unit::TestCase
[o, o, o].sort_by {|x| x }
c.call
end
+
+ assert_raise_with_message(RuntimeError, /reentered/) do
+ i = 0
+ c = nil
+ o = Object.new
+ class << o; self; end.class_eval do
+ define_method(:<=>) do |x|
+ callcc {|c2| c ||= c2 }
+ i += 1
+ 0
+ end
+ end
+ [o, o].min(1)
+ assert_operator(i, :<=, 5, "infinite loop")
+ c.call
+ end
end
def test_reverse_each