From e65ead741ba252833cddab021e0688c3bb2270ce Mon Sep 17 00:00:00 2001 From: Kevin Gibbons Date: Thu, 12 Mar 2026 10:41:27 -0700 Subject: [PATCH 1/3] close receiver on argument validation failure --- spec.emu | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/spec.emu b/spec.emu index cc1cfb5..c42bf43 100644 --- a/spec.emu +++ b/spec.emu @@ -14,14 +14,19 @@ copyright: false 1. Let _O_ be the *this* value. 1. If _O_ is not an Object, throw a *TypeError* exception. + 1. Let _iterated_ be the Iterator Record { [[Iterator]]: _O_, [[NextMethod]]: *undefined*, [[Done]]: *false* }. 1. If _skippedElements_ is *undefined*, then 1. Let _toSkip_ be 0. 1. Else, - 1. If _skippedElements_ is not one of *+∞𝔽*, *-∞𝔽*, or an integral Number, throw a *TypeError* exception. + 1. If _skippedElements_ is not one of *+∞𝔽*, *-∞𝔽*, or an integral Number, then + 1. Let _error_ be ThrowCompletion(a newly created *TypeError* object). + 1. Return ? IteratorClose(_iterated_, _error_). 1. Let _toSkip_ be the extended mathematical value of _skippedElements_. - 1. If _toSkip_ < 0, throw a *RangeError* exception. + 1. If _toSkip_ < 0, then + 1. Let _error_ be ThrowCompletion(a newly created *RangeError* object). + 1. Return ? IteratorClose(_iterated_, _error_). 1. Let _skipped_ be 0. - 1. Let _iterated_ be ? GetIteratorDirect(_O_). + 1. Set _iterated_ to ? GetIteratorDirect(_O_). 1. Repeat, 1. Let _value_ be ? IteratorStepValue(_iterated_). 1. If _value_ is ~done~, return *false*. From b5632dafbd05eee733a82f4abbe381b9b687800a Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Thu, 12 Mar 2026 14:56:31 -0400 Subject: [PATCH 2/3] add test and update implementation --- src/index.ts | 9 ++++++++- test/index.mjs | 46 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/src/index.ts b/src/index.ts index 4a7e4d2..b660783 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,20 +2,27 @@ function includes(this: IterableIterator, searchElement: T, skippedElement let toSkip = 0; if (skippedElements !== undefined) { if (!(skippedElements === 2e308 || skippedElements === -2e308 || typeof skippedElements === 'number' && Math.trunc(skippedElements) === skippedElements)) { + this.return?.(); throw new TypeError; } toSkip = skippedElements as number; } if (toSkip < 0) { + this.return?.(); throw new RangeError; } let skipped = 0; - for (let e of this) { + let next = (Date.bind).call(this.next, this); + let iteratorResult = next(); + while (!iteratorResult.done) { + let e = iteratorResult.value; if (skipped < toSkip) { ++skipped; } else if ([e].includes(searchElement)) { + this.return?.() return true; } + iteratorResult = next(); } return false; } diff --git a/test/index.mjs b/test/index.mjs index 733f654..ab64043 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -56,7 +56,7 @@ test('zeroes', async t => { assert.equal(negative.values().includes(-0), true); }); -test('closes iterator', async t => { +test('closes iterator after succeeding', async t => { let closed = false; let i = 0; let iter = { @@ -75,6 +75,50 @@ test('closes iterator', async t => { assert.equal(closed, true); }); +test('closes iterator after invalid second parameter', async t => { + { + let closed = false; + let i = 0; + let iter = { + __proto__: Iterator.prototype, + next() { + ++i; + return { value: i, done: false }; + }, + return() { + closed = true; + return { value: undefined, done: true }; + }, + }; + + assert.throws(() => { + iter.includes(null, -2); + }, RangeError); + assert.equal(closed, true); + } + + { + let closed = false; + let i = 0; + let iter = { + __proto__: Iterator.prototype, + next() { + ++i; + return { value: i, done: false }; + }, + return() { + closed = true; + return { value: undefined, done: true }; + }, + }; + + assert.throws(() => { + iter.includes(null, 'a string'); + }, TypeError); + assert.equal(closed, true); + } +}); + test('skipped elements', async t => { await test('negative integral', async t => { assert.throws(() => { From d4e4a750c5531dc3e0dee896d7c62a080a8cbc20 Mon Sep 17 00:00:00 2001 From: Michael Ficarra Date: Thu, 12 Mar 2026 15:01:34 -0400 Subject: [PATCH 3/3] swallow errors from .return() --- src/index.ts | 6 +++--- test/index.mjs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/index.ts b/src/index.ts index b660783..996d6c7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,13 +2,13 @@ function includes(this: IterableIterator, searchElement: T, skippedElement let toSkip = 0; if (skippedElements !== undefined) { if (!(skippedElements === 2e308 || skippedElements === -2e308 || typeof skippedElements === 'number' && Math.trunc(skippedElements) === skippedElements)) { - this.return?.(); + try { this.return?.(); } catch {} throw new TypeError; } toSkip = skippedElements as number; } if (toSkip < 0) { - this.return?.(); + try { this.return?.(); } catch {} throw new RangeError; } let skipped = 0; @@ -19,7 +19,7 @@ function includes(this: IterableIterator, searchElement: T, skippedElement if (skipped < toSkip) { ++skipped; } else if ([e].includes(searchElement)) { - this.return?.() + try { this.return?.(); } catch {} return true; } iteratorResult = next(); diff --git a/test/index.mjs b/test/index.mjs index ab64043..a21372d 100644 --- a/test/index.mjs +++ b/test/index.mjs @@ -67,7 +67,7 @@ test('closes iterator after succeeding', async t => { }, return() { closed = true; - return { value: undefined, done: true }; + throw new Error; }, }; @@ -87,7 +87,7 @@ test('closes iterator after invalid second parameter', async t => { }, return() { closed = true; - return { value: undefined, done: true }; + throw new Error; }, }; @@ -108,7 +108,7 @@ test('closes iterator after invalid second parameter', async t => { }, return() { closed = true; - return { value: undefined, done: true }; + throw new Error; }, };