diff --git a/.gitattributes b/.gitattributes index 2b1d4b01..53bff5c3 100644 --- a/.gitattributes +++ b/.gitattributes @@ -279,7 +279,6 @@ 2018/DeletePropertyOrThrow.js spackled linguist-generated=true 2018/FromPropertyDescriptor.js spackled linguist-generated=true 2018/Get.js spackled linguist-generated=true -2018/GetIterator.js spackled linguist-generated=true 2018/GetMethod.js spackled linguist-generated=true 2018/GetOwnPropertyKeys.js spackled linguist-generated=true 2018/GetPrototypeFromConstructor.js spackled linguist-generated=true @@ -302,7 +301,6 @@ 2018/IsPromise.js spackled linguist-generated=true 2018/IsPropertyKey.js spackled linguist-generated=true 2018/IsRegExp.js spackled linguist-generated=true -2018/IterableToList.js spackled linguist-generated=true 2018/IteratorClose.js spackled linguist-generated=true 2018/IteratorComplete.js spackled linguist-generated=true 2018/IteratorNext.js spackled linguist-generated=true @@ -519,6 +517,7 @@ 2020/EnumerableOwnPropertyNames.js spackled linguist-generated=true 2020/FromPropertyDescriptor.js spackled linguist-generated=true 2020/Get.js spackled linguist-generated=true +2020/GetIterator.js spackled linguist-generated=true 2020/GetMethod.js spackled linguist-generated=true 2020/GetOwnPropertyKeys.js spackled linguist-generated=true 2020/GetPrototypeFromConstructor.js spackled linguist-generated=true @@ -543,6 +542,7 @@ 2020/IsPropertyKey.js spackled linguist-generated=true 2020/IsRegExp.js spackled linguist-generated=true 2020/IsStringPrefix.js spackled linguist-generated=true +2020/IterableToList.js spackled linguist-generated=true 2020/IteratorClose.js spackled linguist-generated=true 2020/IteratorComplete.js spackled linguist-generated=true 2020/IteratorNext.js spackled linguist-generated=true diff --git a/2018/GetIterator.js b/2018/GetIterator.js index 7beddacc..952859b1 100644 --- a/2018/GetIterator.js +++ b/2018/GetIterator.js @@ -3,6 +3,10 @@ var GetIntrinsic = require('../GetIntrinsic'); var $TypeError = GetIntrinsic('%TypeError%'); +var $asyncIterator = GetIntrinsic('%Symbol.asyncIterator%', true); + +var inspect = require('object-inspect'); +var hasSymbols = require('has-symbols')(); var getIteratorMethod = require('../helpers/getIteratorMethod'); var AdvanceStringIndex = require('./AdvanceStringIndex'); @@ -11,25 +15,48 @@ var GetMethod = require('./GetMethod'); var IsArray = require('./IsArray'); var Type = require('./Type'); -// https://ecma-international.org/ecma-262/6.0/#sec-getiterator - -module.exports = function GetIterator(obj, method) { - var actualMethod = method; +// https://ecma-international.org/ecma-262/9.0/#sec-getiterator +module.exports = function GetIterator(obj, hint, method) { + var actualHint = hint; if (arguments.length < 2) { - actualMethod = getIteratorMethod( - { + actualHint = 'sync'; + } + if (actualHint !== 'sync' && actualHint !== 'async') { + throw new $TypeError("Assertion failed: `hint` must be one of 'sync' or 'async', got " + inspect(hint)); + } + var actualMethod = method; + if (arguments.length < 3) { + if (actualHint === 'async') { + if (hasSymbols && $asyncIterator) { + actualMethod = GetMethod(obj, $asyncIterator); + } + if (actualMethod === undefined) { + throw new $TypeError("async from sync iterators aren't currently supported"); + } + } else { + actualMethod = getIteratorMethod({ AdvanceStringIndex: AdvanceStringIndex, GetMethod: GetMethod, IsArray: IsArray, Type: Type - }, - obj - ); + }, obj); + } } + var iterator = Call(actualMethod, obj); if (Type(iterator) !== 'Object') { throw new $TypeError('iterator must return an object'); } return iterator; + + // TODO: This should return an IteratorRecord + /* + var nextMethod = GetV(iterator, 'next'); + return { + '[[Iterator]]': iterator, + '[[NextMethod]]': nextMethod, + '[[Done]]': false + }; + */ }; diff --git a/2018/IterableToList.js b/2018/IterableToList.js index 0b8cdcfe..b31623cb 100644 --- a/2018/IterableToList.js +++ b/2018/IterableToList.js @@ -7,10 +7,10 @@ var GetIterator = require('./GetIterator'); var IteratorStep = require('./IteratorStep'); var IteratorValue = require('./IteratorValue'); -// https://www.ecma-international.org/ecma-262/8.0/#sec-iterabletolist +// https://www.ecma-international.org/ecma-262/9.0/#sec-iterabletolist module.exports = function IterableToList(items, method) { - var iterator = GetIterator(items, method); + var iterator = GetIterator(items, 'sync', method); var values = []; var next = true; while (next) { diff --git a/2019/GetIterator.js b/2019/GetIterator.js index 7beddacc..952859b1 100644 --- a/2019/GetIterator.js +++ b/2019/GetIterator.js @@ -3,6 +3,10 @@ var GetIntrinsic = require('../GetIntrinsic'); var $TypeError = GetIntrinsic('%TypeError%'); +var $asyncIterator = GetIntrinsic('%Symbol.asyncIterator%', true); + +var inspect = require('object-inspect'); +var hasSymbols = require('has-symbols')(); var getIteratorMethod = require('../helpers/getIteratorMethod'); var AdvanceStringIndex = require('./AdvanceStringIndex'); @@ -11,25 +15,48 @@ var GetMethod = require('./GetMethod'); var IsArray = require('./IsArray'); var Type = require('./Type'); -// https://ecma-international.org/ecma-262/6.0/#sec-getiterator - -module.exports = function GetIterator(obj, method) { - var actualMethod = method; +// https://ecma-international.org/ecma-262/9.0/#sec-getiterator +module.exports = function GetIterator(obj, hint, method) { + var actualHint = hint; if (arguments.length < 2) { - actualMethod = getIteratorMethod( - { + actualHint = 'sync'; + } + if (actualHint !== 'sync' && actualHint !== 'async') { + throw new $TypeError("Assertion failed: `hint` must be one of 'sync' or 'async', got " + inspect(hint)); + } + var actualMethod = method; + if (arguments.length < 3) { + if (actualHint === 'async') { + if (hasSymbols && $asyncIterator) { + actualMethod = GetMethod(obj, $asyncIterator); + } + if (actualMethod === undefined) { + throw new $TypeError("async from sync iterators aren't currently supported"); + } + } else { + actualMethod = getIteratorMethod({ AdvanceStringIndex: AdvanceStringIndex, GetMethod: GetMethod, IsArray: IsArray, Type: Type - }, - obj - ); + }, obj); + } } + var iterator = Call(actualMethod, obj); if (Type(iterator) !== 'Object') { throw new $TypeError('iterator must return an object'); } return iterator; + + // TODO: This should return an IteratorRecord + /* + var nextMethod = GetV(iterator, 'next'); + return { + '[[Iterator]]': iterator, + '[[NextMethod]]': nextMethod, + '[[Done]]': false + }; + */ }; diff --git a/2019/IterableToList.js b/2019/IterableToList.js index 0b8cdcfe..b31623cb 100644 --- a/2019/IterableToList.js +++ b/2019/IterableToList.js @@ -7,10 +7,10 @@ var GetIterator = require('./GetIterator'); var IteratorStep = require('./IteratorStep'); var IteratorValue = require('./IteratorValue'); -// https://www.ecma-international.org/ecma-262/8.0/#sec-iterabletolist +// https://www.ecma-international.org/ecma-262/9.0/#sec-iterabletolist module.exports = function IterableToList(items, method) { - var iterator = GetIterator(items, method); + var iterator = GetIterator(items, 'sync', method); var values = []; var next = true; while (next) { diff --git a/2020/GetIterator.js b/2020/GetIterator.js index e7548b38..952859b1 100644 --- a/2020/GetIterator.js +++ b/2020/GetIterator.js @@ -24,7 +24,6 @@ module.exports = function GetIterator(obj, hint, method) { if (actualHint !== 'sync' && actualHint !== 'async') { throw new $TypeError("Assertion failed: `hint` must be one of 'sync' or 'async', got " + inspect(hint)); } - var actualMethod = method; if (arguments.length < 3) { if (actualHint === 'async') { @@ -35,17 +34,15 @@ module.exports = function GetIterator(obj, hint, method) { throw new $TypeError("async from sync iterators aren't currently supported"); } } else { - actualMethod = getIteratorMethod( - { - AdvanceStringIndex: AdvanceStringIndex, - GetMethod: GetMethod, - IsArray: IsArray, - Type: Type - }, - obj - ); + actualMethod = getIteratorMethod({ + AdvanceStringIndex: AdvanceStringIndex, + GetMethod: GetMethod, + IsArray: IsArray, + Type: Type + }, obj); } } + var iterator = Call(actualMethod, obj); if (Type(iterator) !== 'Object') { throw new $TypeError('iterator must return an object'); diff --git a/test/tests.js b/test/tests.js index 185d13e3..67dfcc15 100644 --- a/test/tests.js +++ b/test/tests.js @@ -4000,6 +4000,56 @@ var es2018 = function ES2018(ES, ops, expectedMissing, skips) { t.end(); }); + test('GetIterator', function (t) { + try { + ES.GetIterator({}, null); + } catch (e) { + t.ok(e.message.indexOf("Assertion failed: `hint` must be one of 'sync' or 'async'" >= 0)); + } + + var arr = [1, 2]; + testIterator(t, ES.GetIterator(arr), arr); + + testIterator(t, ES.GetIterator('abc'), 'abc'.split('')); + + t.test('Symbol.iterator', { skip: !v.hasSymbols }, function (st) { + var m = new Map(); + m.set(1, 'a'); + m.set(2, 'b'); + + testIterator(st, ES.GetIterator(m), [[1, 'a'], [2, 'b']]); + + st.end(); + }); + + t.test('Symbol.asyncIterator', { skip: !v.hasSymbols || !Symbol.asyncIterator }, function (st) { + try { + ES.GetIterator(arr, 'async'); + } catch (e) { + st.ok(e.message.indexOf("async from sync iterators aren't currently supported") >= 0); + } + + /** @type {AsyncIterator} */ + var it = { + next: function () { + return Promise.resolve({ + done: true + }); + } + }; + var obj = {}; + obj[Symbol.asyncIterator] = function () { + return it; + }; + + st.equal(ES.GetIterator(obj, 'async'), it); + + st.end(); + }); + + t.end(); + }); + test('PromiseResolve', function (t) { t.test('Promises unsupported', { skip: typeof Promise === 'function' }, function (st) { st['throws'](