Skip to content

Commit dc0e812

Browse files
slowcheetahzloirock
authored andcommitted
Added detection for throwing on undefined initial parameter in Iterator.prototype.reduce
1 parent 45b6ad6 commit dc0e812

File tree

6 files changed

+34
-4
lines changed

6 files changed

+34
-4
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
- `Array.prototype.fromAsync` (due to the lack of async feature detection capability - temporarily, only in own core-js implementation)
2929
- `AsyncIterator.prototype.from`
3030
- `Iterator.prototype.toAsync`
31+
- Added detection for throwing on `undefined` initial parameter in `Iterator.prototype.reduce` (see [WebKit bug](https://bugs.webkit.org/show_bug.cgi?id=291651))
3132
- Compat data improvements:
3233
- [Explicit Resource Management](https://github.com/tc39/proposal-explicit-resource-management) features disabled (again) in V8 ~ Chromium 135 and re-added in 136
3334
- [`RegExp.escape`](https://github.com/tc39/proposal-regex-escaping) marked as [shipped from V8 ~ Chromium 136](https://issues.chromium.org/issues/353856236#comment17)

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

+3-1
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,9 @@ export const data = {
711711
'es.iterator.reduce': {
712712
// with changes related to the new iteration closing approach on early error
713713
// https://github.com/tc39/ecma262/pull/3467
714-
bun: '1.2.4', // '1.1.31',
714+
// due to bug that causes a throw when the initial parameter is `undefined`
715+
// https://bugs.webkit.org/show_bug.cgi?id=291651
716+
// bun: '1.2.4', // '1.1.31',
715717
chrome: '135', // '122',
716718
deno: '2.2.5', // '1.38.1',
717719
// firefox: '131',

packages/core-js/modules/es.iterator.reduce.js

+20-2
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,29 @@ var aCallable = require('../internals/a-callable');
1212
var $TypeError = TypeError;
1313
var Iterator = globalThis.Iterator;
1414
var nativeReduce = Iterator && Iterator.prototype && Iterator.prototype.reduce;
15-
var NATIVE_METHOD_WITHOUT_CLOSING_ON_EARLY_ERROR = nativeReduce && !checkIteratorClosingOnEarlyError(TypeError, nativeReduce, null);
15+
16+
// https://bugs.webkit.org/show_bug.cgi?id=291651
17+
var nativeMethodFailsOnUndefinedInitialParam = function () {
18+
try {
19+
// eslint-disable-next-line es/no-iterator-prototype-reduce, es/no-array-prototype-keys -- required for testing
20+
[].keys().reduce(function () { return false; }, undefined);
21+
return false;
22+
} catch (err) {
23+
return true;
24+
}
25+
};
26+
27+
var NATIVE_METHOD_WITHOUT_CLOSING_ON_EARLY_ERROR = false;
28+
var nativeMethodWithoutClosingOnEarlyError = function () {
29+
NATIVE_METHOD_WITHOUT_CLOSING_ON_EARLY_ERROR = !checkIteratorClosingOnEarlyError(TypeError, nativeReduce, null);
30+
return NATIVE_METHOD_WITHOUT_CLOSING_ON_EARLY_ERROR;
31+
};
32+
33+
var FORCED = !nativeReduce || nativeMethodFailsOnUndefinedInitialParam() || nativeMethodWithoutClosingOnEarlyError();
1634

1735
// `Iterator.prototype.reduce` method
1836
// https://tc39.es/ecma262/#sec-iterator.prototype.reduce
19-
$({ target: 'Iterator', proto: true, real: true, forced: NATIVE_METHOD_WITHOUT_CLOSING_ON_EARLY_ERROR }, {
37+
$({ target: 'Iterator', proto: true, real: true, forced: FORCED }, {
2038
reduce: function reduce(reducer /* , initialValue */) {
2139
anObject(this);
2240
try {

tests/compat/tests.js

+6-1
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,12 @@ GLOBAL.tests = {
744744
return Iterator.from;
745745
},
746746
'es.iterator.map': checkIteratorClosingOnEarlyError(TypeError, 'map', null),
747-
'es.iterator.reduce': checkIteratorClosingOnEarlyError(TypeError, 'reduce', null),
747+
'es.iterator.reduce': [checkIteratorClosingOnEarlyError(TypeError, 'reduce', null), function () {
748+
// fails on undefined initial parameter
749+
// https://bugs.webkit.org/show_bug.cgi?id=291651
750+
[].keys().reduce(function () { /* empty */ }, undefined);
751+
return true;
752+
}],
748753
'es.iterator.some': checkIteratorClosingOnEarlyError(TypeError, 'some', null),
749754
'es.iterator.take': checkIteratorClosingOnEarlyError(RangeError, 'take', -1),
750755
'es.iterator.to-array': function () {

tests/unit-global/es.iterator.reduce.js

+2
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,6 @@ QUnit.test('Iterator#reduce', assert => {
3232
const it = createIterator([1], { return() { this.closed = true; } });
3333
assert.throws(() => reduce.call(it, {}, 1), TypeError);
3434
assert.true(it.closed, "reduce doesn't close iterator on validation error");
35+
assert.notThrows(() => reduce.call(createIterator([]), () => false, undefined), 'fails on undefined initial parameter');
36+
assert.same(reduce.call(createIterator([]), () => false, undefined), undefined, 'incorrect result on undefined initial parameter');
3537
});

tests/unit-pure/es.iterator.reduce.js

+2
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,6 @@ QUnit.test('Iterator#reduce', assert => {
3232
const it = createIterator([1], { return() { this.closed = true; } });
3333
assert.throws(() => reduce.call(it, {}, 1), TypeError);
3434
assert.true(it.closed, "reduce doesn't close iterator on validation error");
35+
assert.notThrows(() => reduce.call(createIterator([]), () => false, undefined), 'fails on undefined initial parameter');
36+
assert.same(reduce.call(createIterator([]), () => false, undefined), undefined, 'incorrect result on undefined initial parameter');
3537
});

0 commit comments

Comments
 (0)