mirror of
https://github.com/ruby/ruby.git
synced 2026-10-28 16:36:20 +08:00
Fix escaped blocks in Enumerable#uniq
Keep the state for block-based `Enumerable#uniq` alive after it returns,
as `each` can retain and later invoke the internal block, making the
stack-allocated memo introduced in 29c669bb56 invalid.
This commit is contained in:
1 parent
ea2836f7ec
commit
34d2105de0
2 files changed
+17
-16
Notes:
git
2026-09-24 10:22:52 +00:00
No files matched your search
@@ -4878,19 +4878,11 @@ uniq_func(RB_BLOCK_CALL_FUNC_ARGLIST(i, set))
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
struct uniq_iter_memo {
|
||||
VALUE set;
|
||||
VALUE ary;
|
||||
};
|
||||
|
||||
static VALUE
|
||||
uniq_iter(RB_BLOCK_CALL_FUNC_ARGLIST(i, memo_))
|
||||
uniq_iter(RB_BLOCK_CALL_FUNC_ARGLIST(i, hash))
|
||||
{
|
||||
struct uniq_iter_memo *memo = (struct uniq_iter_memo *)memo_;
|
||||
ENUM_WANT_SVALUE();
|
||||
if (rb_set_add_no_check(memo->set, rb_yield_values2(argc, argv))) {
|
||||
rb_ary_push(memo->ary, i);
|
||||
}
|
||||
rb_hash_add_new_element(hash, rb_yield_values2(argc, argv), i);
|
||||
return Qnil;
|
||||
}
|
||||
|
||||
@@ -4918,18 +4910,18 @@ uniq_iter(RB_BLOCK_CALL_FUNC_ARGLIST(i, memo_))
|
||||
static VALUE
|
||||
enum_uniq(VALUE obj)
|
||||
{
|
||||
VALUE ret;
|
||||
if (rb_block_given_p()) {
|
||||
struct uniq_iter_memo memo;
|
||||
memo.set = rb_obj_hide(rb_set_new());
|
||||
memo.ary = rb_ary_new();
|
||||
rb_block_call(obj, id_each, 0, 0, uniq_iter, (VALUE)&memo);
|
||||
return memo.ary;
|
||||
VALUE hash = rb_obj_hide(rb_hash_new());
|
||||
rb_block_call(obj, id_each, 0, 0, uniq_iter, hash);
|
||||
ret = rb_hash_values(hash);
|
||||
}
|
||||
else {
|
||||
VALUE set = rb_obj_hide(rb_set_new());
|
||||
rb_block_call(obj, id_each, 0, 0, uniq_func, set);
|
||||
return rb_set_to_a(set);
|
||||
ret = rb_set_to_a(set);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static VALUE
|
||||
|
||||
@@ -1295,6 +1295,15 @@ class TestEnumerable < Test::Unit::TestCase
|
||||
olympics.uniq{|k,v| v})
|
||||
assert_equal([1, 2, 3, 4, 5, 10], (1..100).uniq{|x| (x**2) % 10 }.first(6))
|
||||
assert_equal([1, [1, 2]], Foo.new.to_enum.uniq)
|
||||
|
||||
b = Struct.new(:block) do
|
||||
def each(&blk)
|
||||
self.block = blk
|
||||
nil
|
||||
end
|
||||
end.new
|
||||
b.uniq {|x| x}
|
||||
assert_nil(b.block.call(:foo))
|
||||
end
|
||||
|
||||
def test_compact
|
||||
|
||||
Reference in new issue
Block a user