* Fix [Bug #19632]: Disable external iterator for frozen enumerator Currently, methods to manipulate an external iterator like `#next` and `#feed` can be called even if a receiver of an enumerator is frozen. However, these methods change the state of an external iterator in an enumerator. Therefore, it seems a BUG to me, and these methods should raise FrozenError if the receiver is frozen. This fixed the following methods to raise FrozenError if the receiver is frozen. - `Enumerator#next` - `Enumerator#next_values` - `Enumerator#peek` - `Enumerator#peek_values` - `Enumerator#feed` - `Enumerator#rewind` * Fix a typo in the document Thanks @Maumagnaguagno.
This commit is contained in:
parent
a6a67b0524
commit
3e64cf60b5
16
enumerator.c
16
enumerator.c
@ -85,12 +85,16 @@
|
|||||||
* puts e.next # => 3
|
* puts e.next # => 3
|
||||||
* puts e.next # raises StopIteration
|
* puts e.next # raises StopIteration
|
||||||
*
|
*
|
||||||
* +next+, +next_values+, +peek+ and +peek_values+ are the only methods
|
* +next+, +next_values+, +peek+, and +peek_values+ are the only methods
|
||||||
* which use external iteration (and Array#zip(Enumerable-not-Array) which uses +next+).
|
* which use external iteration (and Array#zip(Enumerable-not-Array) which uses +next+ internally).
|
||||||
*
|
*
|
||||||
* These methods do not affect other internal enumeration methods,
|
* These methods do not affect other internal enumeration methods,
|
||||||
* unless the underlying iteration method itself has side-effect, e.g. IO#each_line.
|
* unless the underlying iteration method itself has side-effect, e.g. IO#each_line.
|
||||||
*
|
*
|
||||||
|
* FrozenError will be raised if these methods are called against a frozen enumerator.
|
||||||
|
* Since +rewind+ and +feed+ also change state for external iteration,
|
||||||
|
* these methods may raise FrozenError too.
|
||||||
|
*
|
||||||
* External iteration differs *significantly* from internal iteration
|
* External iteration differs *significantly* from internal iteration
|
||||||
* due to using a Fiber:
|
* due to using a Fiber:
|
||||||
* - The Fiber adds some overhead compared to internal enumeration.
|
* - The Fiber adds some overhead compared to internal enumeration.
|
||||||
@ -869,6 +873,8 @@ enumerator_next_values(VALUE obj)
|
|||||||
struct enumerator *e = enumerator_ptr(obj);
|
struct enumerator *e = enumerator_ptr(obj);
|
||||||
VALUE vs;
|
VALUE vs;
|
||||||
|
|
||||||
|
rb_check_frozen(obj);
|
||||||
|
|
||||||
if (!UNDEF_P(e->lookahead)) {
|
if (!UNDEF_P(e->lookahead)) {
|
||||||
vs = e->lookahead;
|
vs = e->lookahead;
|
||||||
e->lookahead = Qundef;
|
e->lookahead = Qundef;
|
||||||
@ -930,6 +936,8 @@ enumerator_peek_values(VALUE obj)
|
|||||||
{
|
{
|
||||||
struct enumerator *e = enumerator_ptr(obj);
|
struct enumerator *e = enumerator_ptr(obj);
|
||||||
|
|
||||||
|
rb_check_frozen(obj);
|
||||||
|
|
||||||
if (UNDEF_P(e->lookahead)) {
|
if (UNDEF_P(e->lookahead)) {
|
||||||
e->lookahead = get_next_values(obj, e);
|
e->lookahead = get_next_values(obj, e);
|
||||||
}
|
}
|
||||||
@ -1054,6 +1062,8 @@ enumerator_feed(VALUE obj, VALUE v)
|
|||||||
{
|
{
|
||||||
struct enumerator *e = enumerator_ptr(obj);
|
struct enumerator *e = enumerator_ptr(obj);
|
||||||
|
|
||||||
|
rb_check_frozen(obj);
|
||||||
|
|
||||||
if (!UNDEF_P(e->feedvalue)) {
|
if (!UNDEF_P(e->feedvalue)) {
|
||||||
rb_raise(rb_eTypeError, "feed value already set");
|
rb_raise(rb_eTypeError, "feed value already set");
|
||||||
}
|
}
|
||||||
@ -1076,6 +1086,8 @@ enumerator_rewind(VALUE obj)
|
|||||||
{
|
{
|
||||||
struct enumerator *e = enumerator_ptr(obj);
|
struct enumerator *e = enumerator_ptr(obj);
|
||||||
|
|
||||||
|
rb_check_frozen(obj);
|
||||||
|
|
||||||
rb_check_funcall(e->obj, id_rewind, 0, 0);
|
rb_check_funcall(e->obj, id_rewind, 0, 0);
|
||||||
|
|
||||||
e->fib = 0;
|
e->fib = 0;
|
||||||
|
@ -1008,4 +1008,14 @@ class TestEnumerator < Test::Unit::TestCase
|
|||||||
Enumerator.product(1..3, foo: 1, bar: 2)
|
Enumerator.product(1..3, foo: 1, bar: 2)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_freeze
|
||||||
|
e = 3.times.freeze
|
||||||
|
assert_raise(FrozenError) { e.next }
|
||||||
|
assert_raise(FrozenError) { e.next_values }
|
||||||
|
assert_raise(FrozenError) { e.peek }
|
||||||
|
assert_raise(FrozenError) { e.peek_values }
|
||||||
|
assert_raise(FrozenError) { e.feed 1 }
|
||||||
|
assert_raise(FrozenError) { e.rewind }
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
x
Reference in New Issue
Block a user