diff --git a/.eslintrc b/.eslintrc index 43d23b6..cf51282 100644 --- a/.eslintrc +++ b/.eslintrc @@ -21,6 +21,7 @@ "new-cap": [2, { "capIsNewExceptions": [ "Call", + "CreateDataPropertyOrThrow", "CreateIteratorFromClosure", "CreateIterResultObject", "GeneratorResume", @@ -29,18 +30,25 @@ "GeneratorValidate", "Get", "GetIntrinsic", + "GetIterator", "GetIteratorDirect", "GetIteratorFlattenable", "GetMethod", + "GetOptionsObject", + "IfAbruptCloseIterators", + "IsAccessorDescriptor", "IsArray", "IsCallable", + "IsDataDescriptor", "IteratorClose", "IteratorCloseAll", "IteratorStep", "IteratorStepValue", + "IteratorZip", "NormalCompletion", "OrdinaryHasInstance", "OrdinaryObjectCreate", + "ReturnCompletion", "StringToCodePoints", "ThrowCompletion", "ToBoolean", @@ -64,5 +72,19 @@ "import/no-extraneous-dependencies": [2, { "devDependencies": true }], }, }, + { + "files": "Iterator.zip*/implementation.js", + "rules": { + "complexity": "off", + "max-depth": "off", + }, + }, + { + "files": "aos/IteratorZip.js", + "rules": { + "max-depth": "off", + "max-params": "off", + }, + }, ], } diff --git a/Iterator.from/implementation.js b/Iterator.from/implementation.js index 4618971..6af9443 100644 --- a/Iterator.from/implementation.js +++ b/Iterator.from/implementation.js @@ -16,7 +16,7 @@ module.exports = function from(O) { throw new $TypeError('`Iterator.from` is not a constructor'); } - var iteratorRecord = GetIteratorFlattenable(O, 'iterate-strings'); // step 1 + var iteratorRecord = GetIteratorFlattenable(O, 'ITERATE-STRINGS'); // step 1 var hasInstance = OrdinaryHasInstance($Iterator, iteratorRecord['[[Iterator]]']); // step 2 diff --git a/Iterator.prototype.flatMap/implementation.js b/Iterator.prototype.flatMap/implementation.js index 7774b32..958849e 100644 --- a/Iterator.prototype.flatMap/implementation.js +++ b/Iterator.prototype.flatMap/implementation.js @@ -76,7 +76,7 @@ module.exports = function flatMap(mapper) { try { var mapped = Call(mapper, void undefined, [value, counter]); // step 5.b.iv // yield mapped // step 5.b.vi - innerIterator = GetIteratorFlattenable(mapped, 'reject-strings'); // step 5.b.vi + innerIterator = GetIteratorFlattenable(mapped, 'REJECT-STRINGS'); // step 5.b.vi } catch (e) { innerAlive = false; innerIterator = sentinel; diff --git a/Iterator.zip/auto.js b/Iterator.zip/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.zip/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.zip/implementation.js b/Iterator.zip/implementation.js new file mode 100644 index 0000000..6f5e045 --- /dev/null +++ b/Iterator.zip/implementation.js @@ -0,0 +1,122 @@ +'use strict'; + +var $TypeError = require('es-errors/type'); + +var Get = require('es-abstract/2024/Get'); +var GetIterator = require('es-abstract/2024/GetIterator'); +var GetIteratorFlattenable = require('../aos/GetIteratorFlattenable'); +var GetOptionsObject = require('../aos/GetOptionsObject'); +var IfAbruptCloseIterators = require('../aos/IfAbruptCloseIterators'); +var IteratorClose = require('es-abstract/2024/IteratorClose'); +var IteratorStepValue = require('es-abstract/2024/IteratorStepValue'); +var IteratorZip = require('../aos/IteratorZip'); +var NormalCompletion = require('es-abstract/2024/NormalCompletion'); +var ThrowCompletion = require('es-abstract/2024/ThrowCompletion'); +var Type = require('es-abstract/2024/Type'); + +module.exports = function zip(iterables) { + if (this instanceof zip) { + throw new $TypeError('`Iterator.zip` is not a constructor'); + } + + if (Type(iterables) !== 'Object') { + throw new $TypeError('`iterables` must be an Object'); // step 1 + } + + var options = GetOptionsObject(arguments.length > 1 ? arguments[1] : undefined); // step 2 + + var mode = Get(options, 'mode'); // step 3 + + if (typeof mode === 'undefined') { + mode = 'shortest'; // step 4 + } + + if (mode !== 'shortest' && mode !== 'longest' && mode !== 'strict') { + throw new $TypeError('`mode` must be one of "shortest", "longest", or "strict"'); // step 5 + } + + var paddingOption; // step 6 + + if (mode === 'longest') { + paddingOption = Get(options, 'padding'); // step 7 + if (typeof paddingOption !== 'undefined' && Type(paddingOption) !== 'Object') { + throw new $TypeError('`padding` option must be an Object'); // step 7.1 + } + } + + var iters = []; // step 8 + + var padding = []; // step 9 + + var inputIter = GetIterator(iterables, 'SYNC'); // step 10 + + var next; // = 'NOT-STARTED'; // step 11 + + while (!inputIter['[[Done]]']) { // step 12 + try { + next = IteratorStepValue(inputIter); // step 12.a + } catch (e) { + IfAbruptCloseIterators(ThrowCompletion(e), iters); // step 12.b + } + + if (!inputIter['[[Done]]']) { // step 12.c + var iter; + try { + iter = GetIteratorFlattenable(next, 'REJECT-STRINGS'); // step 12.c.i + } catch (e) { + IfAbruptCloseIterators(ThrowCompletion(e), [].concat(inputIter, iters)); // step 12.c.ii + } + iters[iters.length] = iter; // step 12.c.iii + } + } + + var iterCount = iters.length; // step 13 + + if (mode === 'longest') { // step 14 + if (typeof paddingOption === 'undefined') { // step 14.a + for (var i = 0; i < iterCount; i++) { // step 14.a.i + padding[padding.length] = void undefined; // step 14.a.i.1 + } + } else { // step 14.b + var paddingIter; + try { + paddingIter = GetIterator(paddingOption, 'SYNC'); // step 14.b.i + } catch (e) { + IfAbruptCloseIterators(ThrowCompletion(e), iters); // step 14.b.ii + } + var usingIterator = true; // step 14.b.iii + for (var j = 0; j < iterCount; j++) { // step 14.b.iv + if (usingIterator) { // step 14.b.iv.1 + try { + next = IteratorStepValue(paddingIter); // step 14.b.iv.1.a + } catch (e) { + IfAbruptCloseIterators(ThrowCompletion(e), iters); // step 14.b.iv.1.b + } + if (paddingIter['[[Done]]']) { // step 14.b.iv.1.c + usingIterator = false; // step 14.b.iv.1.c.i + } else { // step 14.b.iv.1.d + padding[padding.length] = next; // step 14.b.iv.1.d.i + } + } + if (!usingIterator) { + padding[padding.length] = void undefined; // step 14.b.iv.2 + } + } + + if (usingIterator) { // step 14.b.v + try { + IteratorClose(paddingIter, NormalCompletion(undefined)); // step 14.b.v.1 + } catch (e) { + IfAbruptCloseIterators(ThrowCompletion(e), iters); // step 14.b.v.2 + } + } + } + } + + // eslint-disable-next-line no-sequences + var finishResults = (0, function (results) { // step 15 + return results; // step 15.1 + }); + + return IteratorZip(iters, mode, padding, finishResults); // step 16 +}; diff --git a/Iterator.zip/index.js b/Iterator.zip/index.js new file mode 100644 index 0000000..76c4066 --- /dev/null +++ b/Iterator.zip/index.js @@ -0,0 +1,5 @@ +'use strict'; + +var getPolyfill = require('./polyfill'); + +module.exports = getPolyfill(); diff --git a/Iterator.zip/polyfill.js b/Iterator.zip/polyfill.js new file mode 100644 index 0000000..419d118 --- /dev/null +++ b/Iterator.zip/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +var $Iterator = require('../Iterator'); + +module.exports = function getPolyfill() { + return typeof $Iterator.zip === 'function' ? $Iterator.zip : implementation; +}; diff --git a/Iterator.zip/shim.js b/Iterator.zip/shim.js new file mode 100644 index 0000000..b9fff8d --- /dev/null +++ b/Iterator.zip/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var getPolyfill = require('./polyfill'); +var define = require('define-properties'); + +var getIteratorPolyfill = require('../Iterator/polyfill'); + +module.exports = function shimIteratorZip() { + var $Iterator = getIteratorPolyfill(); + var polyfill = getPolyfill(); + define( + $Iterator, + { zip: polyfill }, + { zip: function () { return $Iterator.zip !== polyfill; } } + ); + + return polyfill; +}; diff --git a/Iterator.zipKeyed/auto.js b/Iterator.zipKeyed/auto.js new file mode 100644 index 0000000..8ebf606 --- /dev/null +++ b/Iterator.zipKeyed/auto.js @@ -0,0 +1,3 @@ +'use strict'; + +require('./shim')(); diff --git a/Iterator.zipKeyed/implementation.js b/Iterator.zipKeyed/implementation.js new file mode 100644 index 0000000..b218f07 --- /dev/null +++ b/Iterator.zipKeyed/implementation.js @@ -0,0 +1,133 @@ +'use strict'; + +var $TypeError = require('es-errors/type'); + +var Call = require('es-abstract/2024/Call'); +var CreateDataPropertyOrThrow = require('es-abstract/2024/CreateDataPropertyOrThrow'); +var Get = require('es-abstract/2024/Get'); +var GetIteratorFlattenable = require('../aos/GetIteratorFlattenable'); +var GetOptionsObject = require('../aos/GetOptionsObject'); +var IfAbruptCloseIterators = require('../aos/IfAbruptCloseIterators'); +var IsAccessorDescriptor = require('es-abstract/2024/IsAccessorDescriptor'); +var IsDataDescriptor = require('es-abstract/2024/IsDataDescriptor'); +var IteratorZip = require('../aos/IteratorZip'); +var OrdinaryObjectCreate = require('es-abstract/2024/OrdinaryObjectCreate'); +var ThrowCompletion = require('es-abstract/2024/ThrowCompletion'); +var Type = require('es-abstract/2024/Type'); + +var forEach = require('es-abstract/helpers/forEach'); +var ownKeys = require('es-abstract/helpers/OwnPropertyKeys'); + +var gOPD = require('gopd'); + +module.exports = function zipKeyed(iterables) { + if (this instanceof zipKeyed) { + throw new $TypeError('`Iterator.zip` is not a constructor'); + } + + if (Type(iterables) !== 'Object') { + throw new $TypeError('`iterables` must be an Object'); // step 1 + } + + var options = GetOptionsObject(arguments.length > 1 ? arguments[1] : undefined); // step 2 + + var mode = Get(options, 'mode'); // step 3 + + if (typeof mode === 'undefined') { + mode = 'shortest'; // step 4 + } + + if (mode !== 'shortest' && mode !== 'longest' && mode !== 'strict') { + throw new $TypeError('`mode` must be one of "shortest", "longest", or "strict"'); // step 5 + } + + var paddingOption; // step 6 + + if (mode === 'longest') { + paddingOption = Get(options, 'padding'); // step 7 + if (typeof paddingOption !== 'undefined' && Type(paddingOption) !== 'Object') { + throw new $TypeError('`padding` option must be an Object'); // step 7.1 + } + } + + var iters = []; // step 8 + + var padding = []; // step 9 + + var allKeys = ownKeys(iterables); // step 10 + + var keys = []; // step 11 + + forEach(allKeys, function (key) { // step 12 + var desc; + try { + desc = gOPD(iterables, key); // step 12.a + } catch (e) { + IfAbruptCloseIterators(ThrowCompletion(e), iters); // step 12.b + } + + if (typeof desc !== 'undefined' && desc['[[Enumerable]]'] === true) { // step 12.c + var value; // step 12.c.i + if (IsDataDescriptor(desc)) { // step 12.c.ii + value = desc['[[Value]]']; // step 12.c.ii.1 + } else { + if (!IsAccessorDescriptor(desc)) { + throw new $TypeError('Assertion failed: IsAccessorDescriptor(desc) is not true'); // step 12.c.ii.1 + } + var getter = desc['[[Get]]']; // step 12.c.iii.2 + if (typeof getter !== 'undefined') { // step 12.c.iii.3 + var getterResult; + try { + getterResult = Call(getter, iterables); // step 12.c.iii.3.a + } catch (e) { + // step 12.c.iii.3.b + // 2. IfAbruptCloseIterators(e, iters). + } + value = getterResult; // step 12.c.iii.3.c + } + } + if (typeof value !== 'undefined') { // step 12.c.iv + keys[keys.length] = key; // step 12.c.iv.1 + var iter; + try { + iter = GetIteratorFlattenable(value, 'REJECT-STRINGS'); // step 12.c.iv.2 + } catch (e) { + // step 12.c.iv.3 + // 3. IfAbruptCloseIterators(e, iters). + } + iters[iters.length] = iter; // step 12.c.iv.4 + } + } + }); + + var iterCount = iters.length; // step 13 + + if (mode === 'longest') { // step 14 + if (typeof paddingOption === 'undefined') { // step 14.a + for (var j = 0; j < iterCount; j += 1) { // step 14.a.i + padding[padding.length] = void undefined; // step 14.a.i.1 + } + } else { // step 14.b + forEach(keys, function (key) { // step 14.b.i + var value; + try { + value = Get(paddingOption, key); // step 14.b.i.1 + } catch (e) { + IfAbruptCloseIterators(ThrowCompletion(e), iters); // step 14.b.i.2 + } + padding[padding.length] = value; // step 14.b.i.3 + }); + } + } + + // eslint-disable-next-line no-sequences + var finishResults = (0, function (results) { // step 15 + var obj = OrdinaryObjectCreate(null); // step 15.a + for (var i = 0; i < iterCount; i += 1) { // step 15.b + CreateDataPropertyOrThrow(obj, keys[i], results[i]); // step 15.b.i + } + return obj; // step 15.c + }); + + return IteratorZip(iters, mode, padding, finishResults); // step 16 +}; diff --git a/Iterator.zipKeyed/index.js b/Iterator.zipKeyed/index.js new file mode 100644 index 0000000..76c4066 --- /dev/null +++ b/Iterator.zipKeyed/index.js @@ -0,0 +1,5 @@ +'use strict'; + +var getPolyfill = require('./polyfill'); + +module.exports = getPolyfill(); diff --git a/Iterator.zipKeyed/polyfill.js b/Iterator.zipKeyed/polyfill.js new file mode 100644 index 0000000..13d934e --- /dev/null +++ b/Iterator.zipKeyed/polyfill.js @@ -0,0 +1,9 @@ +'use strict'; + +var implementation = require('./implementation'); + +var $Iterator = require('../Iterator'); + +module.exports = function getPolyfill() { + return typeof $Iterator.zipKeyed === 'function' ? $Iterator.zipKeyed : implementation; +}; diff --git a/Iterator.zipKeyed/shim.js b/Iterator.zipKeyed/shim.js new file mode 100644 index 0000000..1840998 --- /dev/null +++ b/Iterator.zipKeyed/shim.js @@ -0,0 +1,18 @@ +'use strict'; + +var getPolyfill = require('./polyfill'); +var define = require('define-properties'); + +var getIteratorPolyfill = require('../Iterator/polyfill'); + +module.exports = function shimIteratorZipKeyed() { + var $Iterator = getIteratorPolyfill(); + var polyfill = getPolyfill(); + define( + $Iterator, + { zipKeyed: polyfill }, + { zipKeyed: function () { return $Iterator.zipKeyed !== polyfill; } } + ); + + return polyfill; +}; diff --git a/IteratorHelperPrototype/index.js b/IteratorHelperPrototype/index.js index e3bda6e..8358652 100644 --- a/IteratorHelperPrototype/index.js +++ b/IteratorHelperPrototype/index.js @@ -5,12 +5,12 @@ var hasProto = require('has-proto')(); var iterProto = require('../Iterator.prototype/implementation'); var SLOT = require('internal-slot'); -var CompletionRecord = require('es-abstract/2024/CompletionRecord'); var CreateIterResultObject = require('es-abstract/2024/CreateIterResultObject'); var GeneratorResume = require('../aos/GeneratorResume'); var GeneratorResumeAbrupt = require('../aos/GeneratorResumeAbrupt'); var IteratorCloseAll = require('../aos/IteratorCloseAll'); var NormalCompletion = require('es-abstract/2024/NormalCompletion'); +var ReturnCompletion = require('../aos/ReturnCompletion'); var implementation; if (hasProto) { @@ -32,7 +32,7 @@ if (hasProto) { return CreateIterResultObject(void undefined, true); // step 4.d } - var C = new CompletionRecord('return', void undefined); // step 5 + var C = ReturnCompletion(void undefined); // step 5 return GeneratorResumeAbrupt(O, C, 'Iterator Helper'); // step 6 } @@ -47,7 +47,7 @@ if (hasProto) { return GeneratorResume(this, void undefined, 'Iterator Helper'); }; implementation['return'] = function () { - var C = new CompletionRecord('return', void undefined); // step 1 + var C = ReturnCompletion(void undefined); // step 1 return GeneratorResumeAbrupt(this, C, 'Iterator Helper'); }; } diff --git a/aos/GetIteratorFlattenable.js b/aos/GetIteratorFlattenable.js index 7857e89..6d18ab0 100644 --- a/aos/GetIteratorFlattenable.js +++ b/aos/GetIteratorFlattenable.js @@ -11,9 +11,15 @@ var Type = require('es-abstract/2024/Type'); var getIteratorMethod = require('es-abstract/helpers/getIteratorMethod'); +// https://tc39.es/proposal-iterator-helpers/#sec-getiteratorflattenable + module.exports = function GetIteratorFlattenable(obj, stringHandling) { + if (stringHandling !== 'REJECT-STRINGS' && stringHandling !== 'ITERATE-STRINGS') { + throw new $TypeError('Assertion failed: `stringHandling` must be "REJECT-STRINGS" or "ITERATE-STRINGS"'); + } + if (Type(obj) !== 'Object') { - if (stringHandling === 'reject-strings' || typeof obj !== 'string') { + if (stringHandling === 'REJECT-STRINGS' || typeof obj !== 'string') { throw new $TypeError('obj must be an Object'); // step 1.a } } diff --git a/aos/GetOptionsObject.js b/aos/GetOptionsObject.js new file mode 100644 index 0000000..4bc9d72 --- /dev/null +++ b/aos/GetOptionsObject.js @@ -0,0 +1,20 @@ +'use strict'; + +var $TypeError = require('es-errors/type'); + +// var OrdinaryObjectCreate = require('es-abstract/2024/OrdinaryObjectCreate'); +var Type = require('es-abstract/2024/Type'); + +// https://tc39.es/proposal-joint-iteration/#sec-getoptionsobject + +module.exports = function GetOptionsObject(options) { + if (typeof options === 'undefined') { // step 1 + // return OrdinaryObjectCreate(null); // step 1.a + return { __proto__: null }; // step 1.a + } + if (Type(options) === 'Object') { // step 2 + return options; // step 2.a + } + + throw new $TypeError('`options` must be an Object or undefined'); // step 3 +}; diff --git a/aos/IfAbruptCloseIterators.js b/aos/IfAbruptCloseIterators.js new file mode 100644 index 0000000..5cb8414 --- /dev/null +++ b/aos/IfAbruptCloseIterators.js @@ -0,0 +1,19 @@ +'use strict'; + +var $TypeError = require('es-errors/type'); + +var CompletionRecord = require('es-abstract/2024/CompletionRecord'); +var IteratorCloseAll = require('./IteratorCloseAll'); + +// https://tc39.es/proposal-joint-iteration/#sec-ifabruptcloseiterators + +module.exports = function IfAbruptCloseIterators(value, iteratorRecords) { + if (!(value instanceof CompletionRecord)) { + throw new $TypeError('Assertion failed: `value` must be a Completion Record'); // step 1 + } + if (value.type() === 'throw') { + return IteratorCloseAll(iteratorRecords, value); // step 2 + } + + return value['!'](); // step +}; diff --git a/aos/IteratorZip.js b/aos/IteratorZip.js new file mode 100644 index 0000000..493cf09 --- /dev/null +++ b/aos/IteratorZip.js @@ -0,0 +1,147 @@ +'use strict'; + +var $TypeError = require('es-errors/type'); + +var CreateIteratorFromClosure = require('./CreateIteratorFromClosure'); +var IteratorCloseAll = require('./IteratorCloseAll'); +var IteratorStep = require('es-abstract/2024/IteratorStep'); +var IteratorStepValue = require('es-abstract/2024/IteratorStepValue'); +var NormalCompletion = require('es-abstract/2024/NormalCompletion'); +var ThrowCompletion = require('es-abstract/2024/ThrowCompletion'); + +var isAbstractClosure = require('es-abstract/helpers/isAbstractClosure'); +var IsArray = require('es-abstract/helpers/IsArray'); +var isIteratorRecord = require('es-abstract/helpers/records/iterator-record'); +var some = require('es-abstract/helpers/some'); + +var callBound = require('call-bind/callBound'); + +var $indexOf = callBound('Array.prototype.indexOf'); +var $slice = callBound('Array.prototype.slice'); +var $splice = callBound('Array.prototype.splice'); + +var iterHelperProto = require('../IteratorHelperPrototype'); + +var SLOT = require('internal-slot'); + +// https://tc39.es/proposal-joint-iteration/#sec-IteratorZip + +module.exports = function IteratorZip(iters, mode, padding, finishResults) { + if (!IsArray(iters) || !some(iters, isIteratorRecord)) { + throw new $TypeError('`iters` must be a List of IteratorRecords'); + } + + if (mode !== 'shortest' && mode !== 'longest' && mode !== 'strict') { + throw new $TypeError('`mode` must be one of "shortest", "longest", or "strict"'); + } + + if (!IsArray(padding)) { + throw new $TypeError('`padding` must be a List'); + } + + if (!isAbstractClosure(finishResults)) { + throw new $TypeError('`finishResults` must be an Abstract Closure'); + } + + var iterCount = iters.length; // step 1 + + var openIters = $slice(iters); // step 2 + + var sentinel = {}; + var closure = function () { + if (iterCount === 0) { + // 1. If iterCount = 0, return ReturnCompletion(undefined). + return sentinel; // step 1 + } + // while (true) { + { // eslint-disable-line no-lone-blocks + var results = []; // step 3.b.i + if (openIters.length === 0) { + throw new $TypeError('Assertion failed: `openIters` is empty'); // step 3.b.ii + } + for (var i = 0; i < iterCount; ++i) { // step 3.b.iii + // for (var i = 0; i < iterCount; i += 1) { // step 3.b.iii + var result; + + var iter = iters[i]; + if (iter === null) { // step 3.b.iii.1 + if (mode !== 'longest') { + throw new $TypeError('Assertion failed: `mode` is not "longest"'); // step 3.b.iii.1.a + } + result = padding[i]; // step 3.b.iii.1.b + } else { // step 2 + try { + result = IteratorStepValue(iter); // step 3.b.iii.2.a, 3.b.iii.2.c + } catch (e) { // step 3.b.iii.2.b + $splice(openIters, $indexOf(openIters, iter), 1); // step 3.b.iii.2.b.i + return IteratorCloseAll(openIters, ThrowCompletion(e)); // step 3.b.iii.2.b.ii + } + if (iter['[[Done]]']) { // step 3.b.iii.2.d + $splice(openIters, $indexOf(openIters, iter), 1); // step 3.b.iii.2.d.i + if (mode === 'shortest') { // step 3.b.iii.2.d.ii + IteratorCloseAll(openIters, NormalCompletion(undefined)); // step 3.b.iii.2.d.ii.i + return sentinel; + } else if (mode === 'strict') { // step 3.b.iii.2.d.iii + if (i !== 0) { // step 3.b.iii.2.d.iii.i + return IteratorCloseAll( + openIters, + ThrowCompletion(new $TypeError('Assertion failed: `i` is not 0')) + ); // step 3.b.iii.2.d.iii.i.i + } + for (var k = 1; k < iterCount; k += 1) { // step 3.b.iii.2.d.iii.ii + if (iters[k] === null) { + throw new $TypeError('Assertion failed: `iters[k]` is `null`'); // step 3.b.iii.2.d.iii.ii.i + } + try { + result = IteratorStep(iters[k]); // step 3.b.iii.2.d.iii.ii.ii, 3.b.iii.2.d.iii.ii.iii.ii.iv + } catch (e) { // step 3.b.iii.2.d.iii.ii.iii + return IteratorCloseAll(openIters, ThrowCompletion(e)); // step 3.b.iii.2.d.iii.ii.iii.ii + } + + // if (open === false) { // step 3.b.iii.2.d.iii.ii.v + if (iters[k]['[[Done]]']) { // step 3.b.iii.2.d.iii.ii.v + $splice(openIters, $indexOf(openIters, iters[k]), 1); // step 3.b.iii.2.d.iii.ii.v.i + } else { // step 3.b.iii.2.d.iii.ii.vi + return IteratorCloseAll( + openIters, + ThrowCompletion(new $TypeError('Assertion failed: `open` is not `false`')) + ); // step 3.b.iii.2.d.iii.ii.vi.i + } + } + } else { // step 3.b.iii.2.d.iv + if (mode !== 'longest') { + throw new $TypeError('Assertion failed: `mode` is not "longest"'); // step 3.b.iii.2.d.iv.i + } + + if (openIters.length === 0) { + return sentinel; // ReturnCompletion(undefined); // step 3.b.iii.2.d.iv.ii + } + + // eslint-disable-next-line no-param-reassign + iters[i] = null; // step 3.b.iii.2.d.iv.iii + // i += 1; + + result = padding[i]; // step 3.b.iii.2.d.iv.iv + } + } + } + + results[results.length] = result; // step 3.b.iii.3 + + // 5. Let completion be Completion(Yield(results)). + // 6. If completion is an abrupt completion, then + // 1. Return ? IteratorCloseAll(openIters, completion). + } + } + + return finishResults(results); // step 3.b.iv + }; + SLOT.set(closure, '[[Sentinel]]', sentinel); // for the userland implementation + SLOT.set(closure, '[[CloseIfAbrupt]]', finishResults); // for the userland implementation + + var gen = CreateIteratorFromClosure(closure, 'Iterator Helper', iterHelperProto, ['[[UnderlyingIterators]]']); // step 4 + + SLOT.set(gen, '[[UnderlyingIterators]]', openIters); // step 5 + + return gen; // step 6 +}; diff --git a/aos/ReturnCompletion.js b/aos/ReturnCompletion.js new file mode 100644 index 0000000..eed495c --- /dev/null +++ b/aos/ReturnCompletion.js @@ -0,0 +1,9 @@ +'use strict'; + +var CompletionRecord = require('es-abstract/2024/CompletionRecord'); + +// https://tc39.es/ecma262/#sec-returncompletion + +module.exports = function ReturnCompletion(value) { + return new CompletionRecord('return', value); +}; diff --git a/index.json b/index.json index 9ac26be..392247a 100644 --- a/index.json +++ b/index.json @@ -2,6 +2,8 @@ "Iterator", "Iterator.concat", "Iterator.from", + "Iterator.zip", + "Iterator.zipKeyed", "Iterator.prototype", "Iterator.prototype.constructor", "Iterator.prototype.drop", diff --git a/package.json b/package.json index 6ff09ca..ee2cf44 100644 --- a/package.json +++ b/package.json @@ -27,6 +27,16 @@ "./Iterator.from/polyfill": "./Iterator.from/polyfill.js", "./Iterator.from/implementation": "./Iterator.from/implementation.js", "./Iterator.from/shim": "./Iterator.from/shim.js", + "./Iterator.zip": "./Iterator.zip/index.js", + "./Iterator.zip/auto": "./Iterator.zip/auto.js", + "./Iterator.zip/polyfill": "./Iterator.zip/polyfill.js", + "./Iterator.zip/implementation": "./Iterator.zip/implementation.js", + "./Iterator.zip/shim": "./Iterator.zip/shim.js", + "./Iterator.zipKeyed": "./Iterator.zipKeyed/index.js", + "./Iterator.zipKeyed/auto": "./Iterator.zipKeyed/auto.js", + "./Iterator.zipKeyed/polyfill": "./Iterator.zipKeyed/polyfill.js", + "./Iterator.zipKeyed/implementation": "./Iterator.zipKeyed/implementation.js", + "./Iterator.zipKeyed/shim": "./Iterator.zipKeyed/shim.js", "./Iterator.prototype.constructor": "./Iterator.prototype.constructor/index.js", "./Iterator.prototype.constructor/auto": "./Iterator.prototype.constructor/auto.js", "./Iterator.prototype.constructor/polyfill": "./Iterator.prototype.constructor/polyfill.js", @@ -144,6 +154,7 @@ "function-bind": "^1.1.2", "get-intrinsic": "^1.2.4", "globalthis": "^1.0.4", + "gopd": "^1.0.1", "has-property-descriptors": "^1.0.2", "has-proto": "^1.0.3", "has-symbols": "^1.0.3", diff --git a/shim.js b/shim.js index 077f751..931e87b 100644 --- a/shim.js +++ b/shim.js @@ -3,6 +3,8 @@ var shimIterator = require('./Iterator/shim'); var shimIteratorFrom = require('./Iterator.from/shim'); var shimIteratorConcat = require('./Iterator.concat/shim'); +var shimZip = require('./Iterator.zip/shim'); +var shimZipKeyed = require('./Iterator.zipKeyed/shim'); var shimIteratorProto = require('./Iterator.prototype/shim'); var shimIteratorCtor = require('./Iterator.prototype.constructor/shim'); var shimIteratorDrop = require('./Iterator.prototype.drop/shim'); @@ -19,9 +21,11 @@ var shimIteratorToArray = require('./Iterator.prototype.toArray/shim'); module.exports = function shimIteratorHelpers() { shimIterator(); + shimIteratorProto(); shimIteratorFrom(); shimIteratorConcat(); - shimIteratorProto(); + shimZip(); + shimZipKeyed(); shimIteratorCtor(); shimIteratorDrop(); shimIteratorEvery(); diff --git a/test/Iterator.zip.js b/test/Iterator.zip.js new file mode 100644 index 0000000..30ba4f0 --- /dev/null +++ b/test/Iterator.zip.js @@ -0,0 +1,125 @@ +'use strict'; + +var defineProperties = require('define-properties'); +var test = require('tape'); +var callBind = require('call-bind'); +var functionsHaveNames = require('functions-have-names')(); +var forEach = require('for-each'); +var debug = require('object-inspect'); +var v = require('es-value-fixtures'); +var hasSymbols = require('has-symbols/shams')(); +var mockProperty = require('mock-property'); + +var index = require('../Iterator.zip'); +var impl = require('../Iterator.zip/implementation'); +var from = require('../Iterator.from/polyfill')(); + +var isEnumerable = Object.prototype.propertyIsEnumerable; + +var testIterator = require('./helpers/testIterator'); + +module.exports = { + tests: function (zip, name, t) { + t['throws']( + function () { return new zip(); }, // eslint-disable-line new-cap + TypeError, + '`' + name + '` itself is not a constructor' + ); + t['throws']( + function () { return new zip({}); }, // eslint-disable-line new-cap + TypeError, + '`' + name + '` itself is not a constructor, with an argument' + ); + + forEach(v.primitives.concat(v.objects), function (nonIterator) { + t['throws']( + function () { zip(nonIterator, []); }, + TypeError, + debug(nonIterator) + ' is not an iterable Object' + ); + }); + + t.test('actual iteration', { skip: !hasSymbols }, function (st) { + forEach(v.nonFunctions, function (nonFunction) { + var badIterable = {}; + badIterable[Symbol.iterator] = nonFunction; + st['throws']( + function () { zip([[], badIterable, []]).next(); }, + TypeError, + debug(badIterable) + ' is not a function' + ); + }); + + forEach(v.strings, function (string) { + st['throws']( + function () { zip([string]); }, + TypeError, + 'non-objects are not considered iterable' + ); + }); + + var arrayIt = zip([[1, 2, 3]]); + st.equal(typeof arrayIt.next, 'function', 'has a `next` function'); + + st.test('real iterators', { skip: !hasSymbols }, function (s2t) { + var iter = [1, 2][Symbol.iterator](); + testIterator(zip([iter, [3, 4]]), [[1, 3], [2, 4]], s2t, 'array iterator + array yields combined results'); + + s2t.end(); + }); + + st.test('observability in a replaced String iterator', function (s2t) { + var originalStringIterator = String.prototype[Symbol.iterator]; + var observedType; + s2t.teardown(mockProperty(String.prototype, Symbol.iterator, { + get: function () { + 'use strict'; // eslint-disable-line strict, lines-around-directive + + observedType = typeof this; + return originalStringIterator; + } + })); + + zip([from('')]); + s2t.equal(observedType, 'string', 'string primitive -> primitive receiver in Symbol.iterator getter'); + zip([from(Object(''))]); + s2t.equal(observedType, 'object', 'boxed string -> boxed string in Symbol.iterator getter'); + + s2t.end(); + }); + + st.end(); + }); + }, + index: function () { + test('Iterator.zip: index', function (t) { + module.exports.tests(index, 'Iterator.zip', t); + + t.end(); + }); + }, + implementation: function () { + test('Iterator.zip: implementation', function (t) { + module.exports.tests(impl, 'Iterator.zip', t); + + t.end(); + }); + }, + shimmed: function () { + test('Iterator.zip: shimmed', function (t) { + t.test('Function name', { skip: !functionsHaveNames }, function (st) { + st.equal(Iterator.zip.name, 'zip', 'Iterator.zip has name "zip"'); + st.end(); + }); + + t.test('enumerability', { skip: !defineProperties.supportsDescriptors }, function (et) { + et.equal(false, isEnumerable.call(Iterator, 'zip'), 'Iterator.zip is not enumerable'); + et.end(); + }); + + module.exports.tests(callBind(Iterator.zip, Iterator), 'Iterator.zip', t); + + t.end(); + }); + } +};