Skip to content

Commit b090359

Browse files
committed
Add bug detection to Iterator flatMap method
1 parent 6cb9a08 commit b090359

File tree

6 files changed

+51
-4
lines changed

6 files changed

+51
-4
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
- Use `CreateDataProperty` / `CreateDataPropertyOrThrow` in some missed cases, [#1497](https://github.com/zloirock/core-js/issues/1497)
44
- Minor fix / optimization in the `RegExp` constructor (NCG and `dotAll`) polyfill
55
- Added some more workarounds for a Safari < 13 bug with silent ignore of non-writable array `.length`
6+
- Added detection of a Webkit [bug](https://bugs.webkit.org/show_bug.cgi?id=297532): `Iterator.prototype.flatMap` throws on iterator without `return` method
67
- Compat data improvements:
78
- [`Map` upsert proposal](https://github.com/tc39/proposal-upsert) features marked as [shipped in V8 ~ Chrome 145](https://issues.chromium.org/issues/434977728#comment4)
89
- [Joint iteration proposal](https://github.com/tc39/proposal-joint-iteration) features marked as [shipped in FF148](https://bugzilla.mozilla.org/show_bug.cgi?id=2003333#c8)
@@ -11,6 +12,7 @@
1112
- Added [Deno 2.6](https://github.com/denoland/deno/releases/tag/v2.6.0) compat data mapping
1213
- Added Opera Android [93](https://forums.opera.com/topic/87267/opera-for-android-93) and [94](https://forums.opera.com/topic/87678/opera-for-android-94) compat data mapping
1314
- Added Electron 41 compat data mapping
15+
- `Iterator.prototype.flatMap` marked as supported from Safari 26.2 and Bun 1.2.21 because of a [bug](https://bugs.webkit.org/show_bug.cgi?id=297532): throws on iterator without `return` method
1416

1517
### [3.47.0 - 2025.11.18](https://github.com/zloirock/core-js/releases/tag/v3.47.0)
1618
- Changes [v3.46.0...v3.47.0](https://github.com/zloirock/core-js/compare/v3.46.0...v3.47.0) (117 commits)

packages/core-js-compat/src/data.mjs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -747,11 +747,13 @@ export const data = {
747747
'es.iterator.flat-map': {
748748
// with changes related to the new iteration closing approach on early error
749749
// https://github.com/tc39/ecma262/pull/3467
750-
bun: '1.2.4', // '1.1.31',
750+
// throws on iterator without `return` method
751+
// https://bugs.webkit.org/show_bug.cgi?id=297532
752+
bun: '1.2.21', // '1.2.4', '1.1.31'
751753
chrome: '135', // '122',
752754
deno: '2.2.5', // '1.38.1',
753755
firefox: '141', // '131',
754-
safari: '26.0', // 18.4',
756+
safari: '26.2', // '26.0', '18.4
755757
},
756758
'es.iterator.for-each': {
757759
// with changes related to the new iteration closing approach on early error

packages/core-js/modules/es.iterator.flat-map.js

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,28 @@ var IS_PURE = require('../internals/is-pure');
1111
var iteratorHelperThrowsOnInvalidIterator = require('../internals/iterator-helper-throws-on-invalid-iterator');
1212
var iteratorHelperWithoutClosingOnEarlyError = require('../internals/iterator-helper-without-closing-on-early-error');
1313

14+
// Should not throw an error for an iterator without `return` method. Supported from Safari 26.2
15+
// https://bugs.webkit.org/show_bug.cgi?id=297532
16+
function throwsOnIteratorWithoutReturn() {
17+
try {
18+
// eslint-disable-next-line es/no-map, es/no-iterator, es/no-iterator-prototype-flatmap -- required for testing
19+
var it = Iterator.prototype.flatMap.call(new Map([[4, 5]]).entries(), function (v) { return v; });
20+
it.next();
21+
it['return']();
22+
} catch (error) {
23+
return true;
24+
}
25+
}
26+
1427
var FLAT_MAP_WITHOUT_THROWING_ON_INVALID_ITERATOR = !IS_PURE
1528
&& !iteratorHelperThrowsOnInvalidIterator('flatMap', function () { /* empty */ });
1629
var flatMapWithoutClosingOnEarlyError = !IS_PURE && !FLAT_MAP_WITHOUT_THROWING_ON_INVALID_ITERATOR
1730
&& iteratorHelperWithoutClosingOnEarlyError('flatMap', TypeError);
31+
var FLAT_MAP_THROWS_ON_ITERATOR_WITHOUT_RETURN = !IS_PURE && !FLAT_MAP_WITHOUT_THROWING_ON_INVALID_ITERATOR
32+
&& !flatMapWithoutClosingOnEarlyError && throwsOnIteratorWithoutReturn();
1833

19-
var FORCED = IS_PURE || FLAT_MAP_WITHOUT_THROWING_ON_INVALID_ITERATOR || flatMapWithoutClosingOnEarlyError;
34+
var FORCED = IS_PURE || FLAT_MAP_WITHOUT_THROWING_ON_INVALID_ITERATOR || flatMapWithoutClosingOnEarlyError
35+
|| FLAT_MAP_THROWS_ON_ITERATOR_WITHOUT_RETURN;
2036

2137
var IteratorProxy = createIteratorProxy(function () {
2238
var iterator = this.iterator;

tests/compat/tests.js

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -813,7 +813,17 @@ GLOBAL.tests = {
813813
'es.iterator.find': checkIteratorClosingOnEarlyError('find', TypeError),
814814
'es.iterator.flat-map': [
815815
iteratorHelperThrowsErrorOnInvalidIterator('flatMap', function () { /* empty */ }),
816-
checkIteratorClosingOnEarlyError('flatMap', TypeError)
816+
checkIteratorClosingOnEarlyError('flatMap', TypeError),
817+
// Should not throw an error for an iterator without `return` method. Supported from Safari 26.2
818+
// https://bugs.webkit.org/show_bug.cgi?id=297532
819+
function () {
820+
try {
821+
var it = new Map([[4, 5]]).entries().flatMap(function (v) { return v; });
822+
it.next();
823+
it['return']();
824+
return true;
825+
} catch (error) { /* empty */ }
826+
}
817827
],
818828
'es.iterator.for-each': checkIteratorClosingOnEarlyError('forEach', TypeError),
819829
'es.iterator.from': function () {

tests/unit-global/es.iterator.flat-map.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ QUnit.test('Iterator#flatMap', assert => {
2323
return [arg];
2424
}).toArray();
2525

26+
// Should not throw an error for an iterator without `return` method. Fixed in Safari 26.2
27+
// https://bugs.webkit.org/show_bug.cgi?id=297532
28+
assert.notThrows(() => {
29+
const iter = flatMap.call(new Map([[4, 5]]).entries(), v => v);
30+
iter.next();
31+
iter.return();
32+
}, 'iterator without `return` method');
33+
2634
if (STRICT) {
2735
assert.throws(() => flatMap.call(undefined, () => { /* empty */ }), TypeError);
2836
assert.throws(() => flatMap.call(null, () => { /* empty */ }), TypeError);

tests/unit-pure/es.iterator.flat-map.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { createIterator, createIterable } from '../helpers/helpers.js';
22
import { STRICT, STRICT_THIS } from '../helpers/constants.js';
33

44
import Iterator from 'core-js-pure/es/iterator';
5+
import Map from 'core-js-pure/es/map';
56

67
QUnit.test('Iterator#flatMap', assert => {
78
const { flatMap } = Iterator.prototype;
@@ -23,6 +24,14 @@ QUnit.test('Iterator#flatMap', assert => {
2324
return [arg];
2425
}).toArray();
2526

27+
// Should not throw an error for an iterator without `return` method. Fixed in Safari 26.2
28+
// https://bugs.webkit.org/show_bug.cgi?id=297532
29+
assert.notThrows(() => {
30+
const iter = flatMap.call(new Map([[4, 5]]).entries(), v => v);
31+
iter.next();
32+
iter.return();
33+
}, 'iterator without `return` method');
34+
2635
if (STRICT) {
2736
assert.throws(() => flatMap.call(undefined, () => { /* empty */ }), TypeError);
2837
assert.throws(() => flatMap.call(null, () => { /* empty */ }), TypeError);

0 commit comments

Comments
 (0)