From bc9d9090e42e3cb18149e2d6410c6cf6050f774c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Mon, 31 Oct 2022 09:38:41 +0000 Subject: [PATCH] Bug 1797724 - Part 5: Use consistent error messages when iterator method returns a non-object. r=mgaudet This ensures `Array.fromAsync` uses the same error message as `Array.from` and normal iteration (`for-of` and `for-await-of`). This let's us also remove the `GetIterator` self-hosted function. Also renamed `asyncIterator` to `iterator`, because the returned iterator isn't an async iterator. Depends on D160478 Differential Revision: https://phabricator.services.mozilla.com/D160479 --- js/src/builtin/Array.js | 18 ++++++-- js/src/builtin/Iterator.js | 55 ------------------------- js/src/tests/non262/Array/from_async.js | 41 ++++++++++++++++++ 3 files changed, 55 insertions(+), 59 deletions(-) diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 0a8d96394748..53d8aa47e08f 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -871,17 +871,27 @@ function ArrayFromAsync(asyncItems, mapfn = undefined, thisArg = undefined) { // Step 3.h. If usingAsyncIterator is not undefined, then if (usingAsyncIterator !== undefined) { // Step 3.h.i. Set iteratorRecord to ? GetIterator(asyncItems, async, usingAsyncIterator). - iteratorRecord = GetIterator(asyncItems, "async", usingAsyncIterator); + let iterator = callContentFunction(usingAsyncIterator, asyncItems); + + if (!IsObject(iterator)) { + ThrowTypeError(JSMSG_GET_ASYNC_ITER_RETURNED_PRIMITIVE); + } + + iteratorRecord = { iterator, nextMethod: iterator.next }; } else if (usingSyncIterator !== undefined) { // Step 3.i. Else if usingSyncIterator is not undefined, then // Step 3.i.i. Set iteratorRecord to ? CreateAsyncFromSyncIterator(GetIterator(asyncItems, sync, usingSyncIterator)). - let asyncIterator = GetIterator(asyncItems, "sync", usingSyncIterator); + let iterator = callContentFunction(usingSyncIterator, asyncItems); + + if (!IsObject(iterator)) { + ThrowTypeError(JSMSG_GET_ITER_RETURNED_PRIMITIVE); + } // SpiderMonkey's CreateAsyncFromSyncIterator doesn't return an iterator record // with named slots; so we need to create our own iterator record. let asyncFromSyncIteratorObject = CreateAsyncFromSyncIterator( - asyncIterator.iterator, - asyncIterator.nextMethod + iterator, + iterator.next ); iteratorRecord = { diff --git a/js/src/builtin/Iterator.js b/js/src/builtin/Iterator.js index d803069b2b33..80478b97aabb 100644 --- a/js/src/builtin/Iterator.js +++ b/js/src/builtin/Iterator.js @@ -43,61 +43,6 @@ function IteratorClose(iteratorRecord, value) { return value; } -/** - * ES2022 draft rev c5f683e61d5dce703650f1c90d2309c46f8c157a - * - * GetIterator ( obj [ , hint [ , method ] ] ) - * https://tc39.es/ecma262/#sec-getiterator - * - */ -function GetIterator(obj, hint = undefined, method = undefined) { - // Step 1. If hint is not present, set hint to sync. - if (hint === undefined) { - hint = "sync"; - } - - // Step 2. If method is not present, then - if (method === undefined) { - // Step 2.a. If hint is async, then - if (hint === "async") { - // Step 2.a.i. Set method to ? GetMethod(obj, @@asyncIterator). - method = GetMethod(obj, GetBuiltinSymbol("asyncIterator")); - // Step 2.a.ii. If method is undefined, then - if (method === undefined) { - // Step 2.a.ii.1. Let syncMethod be ? GetMethod(obj, @@iterator). - let syncMethod = GetMethod(obj, GetBuiltinSymbol("iterator")); - // Step 2.a.ii.2. Let syncIteratorRecord be ? GetIterator(obj, sync, syncMethod). - let syncIteratorRecord = GetIterator(obj, "sync", syncMethod); - // Step 2.a.ii.3. Return CreateAsyncFromSyncIterator(syncIteratorRecord). - // (SpiderMonkey extracts the contents of the iterator record for this call.) - return CreateAsyncFromSyncIterator( - syncIteratorRecord.iterator, - syncIteratorRecord.nextMethod - ); - } - } else { - // Step 2.b. Otherwise, set method to ? GetMethod(obj, @@iterator). - method = GetMethod(obj, GetBuiltinSymbol("iterator")); - } - } - - // Step 3. Let iterator be ? Call(method, obj). - let iterator = callContentFunction(method, obj); - // Step 4. If iterator is not an Object, throw a TypeError exception. - if (!IsObject(iterator)) { - ThrowTypeError(JSMSG_OBJECT_REQUIRED, iterator); - } - - // Step 5. Let nextMethod be ? GetV(iterator, "next"). - let nextMethod = iterator.next; - - // Step 6. Let iteratorRecord be the Iterator Record { [[Iterator]]: iterator, [[NextMethod]]: nextMethod, [[Done]]: false }. - let iteratorRecord = { iterator, nextMethod, done: false }; - - // Step 7. Return iteratorRecord. - return iteratorRecord; -} - /** * ES2022 draft rev c5f683e61d5dce703650f1c90d2309c46f8c157a * diff --git a/js/src/tests/non262/Array/from_async.js b/js/src/tests/non262/Array/from_async.js index 81fbcc690d6c..4cde0fc5072c 100644 --- a/js/src/tests/non262/Array/from_async.js +++ b/js/src/tests/non262/Array/from_async.js @@ -133,5 +133,46 @@ assertEq(done, true); })(); drainJobQueue(); + +(async function() { + var badSyncIterator = { + [Symbol.iterator](){ + return null; + } + }; + + var badAsyncIterator = { + [Symbol.asyncIterator](){ + return null; + } + }; + + async function errorMessage(fn) { + try { + await fn(); + } catch (e) { + return e.message; + } + throw new Error("missing error"); + } + + // Ensure Array.from and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(() => Array.from(badSyncIterator)); + var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); + assertEq(actual, expected); + + // Ensure for-of iteration and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(() => { for (var _ of badSyncIterator); }); + var actual = await errorMessage(() => Array.fromAsync(badSyncIterator)); + assertEq(actual, expected); + + // Ensure await for-of iteration and Array.fromAsync use consistent error reporting. + var expected = await errorMessage(async () => { for await (var _ of badAsyncIterator); }); + var actual = await errorMessage(() => Array.fromAsync(badAsyncIterator)); + assertEq(actual, expected); +})(); + +drainJobQueue(); + if (typeof reportCompare === 'function') reportCompare(true, true); \ No newline at end of file