From 8de4f9abe9d8f1448e8c240ee484425f582de876 Mon Sep 17 00:00:00 2001 From: Denis Pushkarev Date: Mon, 30 Dec 2024 02:19:38 +0700 Subject: [PATCH] re-use `IteratorResult` objects when possible in `Iterator.concat` https://github.com/tc39/proposal-iterator-sequencing/issues/17 https://github.com/tc39/proposal-iterator-sequencing/pull/18 --- CHANGELOG.md | 2 ++ .../internals/iterator-create-proxy.js | 10 ++++---- .../core-js/modules/esnext.iterator.concat.js | 7 +++--- tests/unit-global/esnext.iterator.concat.js | 21 ++++++++++++++++ tests/unit-pure/esnext.iterator.concat.js | 24 ++++++++++++++++++- 5 files changed, 56 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 35b4d5efe802..00638bb4334e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - Added built-ins: - `Error.isError` - We have no bulletproof way to polyfill this method / check if the object is an error, so it's an enough naive implementation that is marked as `.sham` +- Added [`Iterator` sequencing stage 2.7 proposal](https://github.com/tc39/proposal-iterator-sequencing): + - Re-use `IteratorResult` objects when possible, [tc39/proposal-iterator-sequencing/17](https://github.com/tc39/proposal-iterator-sequencing/issues/17), [tc39/proposal-iterator-sequencing/18](https://github.com/tc39/proposal-iterator-sequencing/pull/18), December 2024 TC39 meeting - Optimized `DataView.prototype.{ getFloat16, setFloat16 }` performance, [#1379](https://github.com/zloirock/core-js/pull/1379), thanks [**@LeviPesin**](https://github.com/LeviPesin) - Dropped unneeded feature detection of non-standard `%TypedArray%.prototype.toSpliced` - Dropped possible re-usage of some non-standard / early stage features (like `Math.scale`) available on global diff --git a/packages/core-js/internals/iterator-create-proxy.js b/packages/core-js/internals/iterator-create-proxy.js index 8479be409a5f..a0f21028c103 100644 --- a/packages/core-js/internals/iterator-create-proxy.js +++ b/packages/core-js/internals/iterator-create-proxy.js @@ -22,12 +22,13 @@ var createIteratorProxyPrototype = function (IS_ITERATOR) { next: function next() { var state = getInternalState(this); // for simplification: - // for `%WrapForValidIteratorPrototype%.next` our `nextHandler` returns `IterResultObject` + // for `%WrapForValidIteratorPrototype%.next` or with `state.returnHandlerResult` our `nextHandler` returns `IterResultObject` // for `%IteratorHelperPrototype%.next` - just a value if (IS_ITERATOR) return state.nextHandler(); + if (state.done) return createIterResultObject(undefined, true); try { - var result = state.done ? undefined : state.nextHandler(); - return createIterResultObject(result, state.done); + var result = state.nextHandler(); + return state.returnHandlerResult ? result : createIterResultObject(result, state.done); } catch (error) { state.done = true; throw error; @@ -57,13 +58,14 @@ var IteratorHelperPrototype = createIteratorProxyPrototype(false); createNonEnumerableProperty(IteratorHelperPrototype, TO_STRING_TAG, 'Iterator Helper'); -module.exports = function (nextHandler, IS_ITERATOR) { +module.exports = function (nextHandler, IS_ITERATOR, RETURN_HANDLER_RESULT) { var IteratorProxy = function Iterator(record, state) { if (state) { state.iterator = record.iterator; state.next = record.next; } else state = record; state.type = IS_ITERATOR ? WRAP_FOR_VALID_ITERATOR : ITERATOR_HELPER; + state.returnHandlerResult = !!RETURN_HANDLER_RESULT; state.nextHandler = nextHandler; state.counter = 0; state.done = false; diff --git a/packages/core-js/modules/esnext.iterator.concat.js b/packages/core-js/modules/esnext.iterator.concat.js index 2a528f45dbc9..c59969dc1496 100644 --- a/packages/core-js/modules/esnext.iterator.concat.js +++ b/packages/core-js/modules/esnext.iterator.concat.js @@ -5,6 +5,7 @@ var aCallable = require('../internals/a-callable'); var anObject = require('../internals/an-object'); var getIteratorMethod = require('../internals/get-iterator-method'); var createIteratorProxy = require('../internals/iterator-create-proxy'); +var createIterResultObject = require('../internals/create-iter-result-object'); var $Array = Array; @@ -16,7 +17,7 @@ var IteratorProxy = createIteratorProxy(function () { var iterables = this.iterables; if (iterableIndex >= iterables.length) { this.done = true; - return; + return createIterResultObject(undefined, true); } var entry = iterables[iterableIndex]; this.iterables[iterableIndex] = null; @@ -29,9 +30,9 @@ var IteratorProxy = createIteratorProxy(function () { this.next = null; continue; } - return result.value; + return result; } -}); +}, false, true); // `Iterator.concat` method // https://github.com/tc39/proposal-iterator-sequencing diff --git a/tests/unit-global/esnext.iterator.concat.js b/tests/unit-global/esnext.iterator.concat.js index 7ee10c5fca15..e610859d55a2 100644 --- a/tests/unit-global/esnext.iterator.concat.js +++ b/tests/unit-global/esnext.iterator.concat.js @@ -62,6 +62,27 @@ QUnit.test('Iterator.concat', assert => { assert.deepEqual(iterator.return(), { done: true, value: undefined }, '.return with active inner iterator with return result'); assert.true(called, 'inner .return called'); + // https://github.com/tc39/proposal-iterator-sequencing/issues/17 + const oldIterResult = { + done: false, + value: 123, + }; + const testIterator = { + next() { + return oldIterResult; + }, + }; + const iterable = { + [Symbol.iterator]() { + return testIterator; + }, + }; + iterator = concat(iterable); + const iterResult = iterator.next(); + assert.same(iterResult.done, false); + assert.same(iterResult.value, 123); + assert.same(iterResult, oldIterResult); + assert.throws(() => concat(createIterator([1, 2, 3])), TypeError, 'non-iterable iterator #1'); assert.throws(() => concat([], createIterator([1, 2, 3])), TypeError, 'non-iterable iterator #2'); assert.throws(() => concat(''), TypeError, 'iterable non-object argument #1'); diff --git a/tests/unit-pure/esnext.iterator.concat.js b/tests/unit-pure/esnext.iterator.concat.js index 0777915e27c6..0a0d153fca77 100644 --- a/tests/unit-pure/esnext.iterator.concat.js +++ b/tests/unit-pure/esnext.iterator.concat.js @@ -1,7 +1,8 @@ import { createIterable, createIterator } from '../helpers/helpers.js'; import concat from 'core-js-pure/full/iterator/concat'; -import Iterator from 'core-js-pure/full/iterator'; +import Iterator from 'core-js-pure/es/iterator'; +import Symbol from 'core-js-pure/es/symbol'; import from from 'core-js-pure/es/array/from'; QUnit.test('Iterator.concat', assert => { @@ -60,6 +61,27 @@ QUnit.test('Iterator.concat', assert => { assert.deepEqual(iterator.return(), { done: true, value: undefined }, '.return with active inner iterator with return result'); assert.true(called, 'inner .return called'); + // https://github.com/tc39/proposal-iterator-sequencing/issues/17 + const oldIterResult = { + done: false, + value: 123, + }; + const testIterator = { + next() { + return oldIterResult; + }, + }; + const iterable = { + [Symbol.iterator]() { + return testIterator; + }, + }; + iterator = concat(iterable); + const iterResult = iterator.next(); + assert.same(iterResult.done, false); + assert.same(iterResult.value, 123); + assert.same(iterResult, oldIterResult); + assert.throws(() => concat(createIterator([1, 2, 3])), TypeError, 'non-iterable iterator #1'); assert.throws(() => concat([], createIterator([1, 2, 3])), TypeError, 'non-iterable iterator #2'); assert.throws(() => concat(''), TypeError, 'iterable non-object argument #1');