From c4d8f01d3d4a9c1694fe5737346975c00a23521d Mon Sep 17 00:00:00 2001 From: Philip Chimento Date: Wed, 23 Nov 2022 12:36:02 -0800 Subject: [PATCH] Array.fromAsync: Tests for mapfn and thisArg arguments - normal case with synchronous and asynchronous mapfn - a non-callable value is passed as mapfn - behaviour of various values of thisArg in strict and sloppy mode - mapfn result is awaited once per iteration - iterator is closed when mapfn throws --- .../Array/fromAsync/mapfn-async-arraylike.js | 35 +++++++++ .../fromAsync/mapfn-async-iterable-async.js | 35 +++++++++ .../fromAsync/mapfn-async-iterable-sync.js | 35 +++++++++ ...mapfn-async-throws-close-async-iterator.js | 40 ++++++++++ .../mapfn-async-throws-close-sync-iterator.js | 40 ++++++++++ .../Array/fromAsync/mapfn-async-throws.js | 23 ++++++ .../Array/fromAsync/mapfn-not-callable.js | 25 +++++++ ...mapfn-result-awaited-once-per-iteration.js | 47 ++++++++++++ .../Array/fromAsync/mapfn-sync-arraylike.js | 35 +++++++++ .../fromAsync/mapfn-sync-iterable-async.js | 33 ++++++++ .../fromAsync/mapfn-sync-iterable-sync.js | 33 ++++++++ .../mapfn-sync-throws-close-async-iterator.js | 39 ++++++++++ .../mapfn-sync-throws-close-sync-iterator.js | 39 ++++++++++ .../Array/fromAsync/mapfn-sync-throws.js | 22 ++++++ .../Array/fromAsync/thisarg-object.js | 21 ++++++ .../Array/fromAsync/thisarg-omitted-sloppy.js | 33 ++++++++ .../Array/fromAsync/thisarg-omitted-strict.js | 28 +++++++ .../fromAsync/thisarg-primitive-sloppy.js | 75 +++++++++++++++++++ .../fromAsync/thisarg-primitive-strict.js | 49 ++++++++++++ 19 files changed, 687 insertions(+) create mode 100644 test/built-ins/Array/fromAsync/mapfn-async-arraylike.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-async-iterable-async.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-async-iterable-sync.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-async-throws-close-async-iterator.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-async-throws-close-sync-iterator.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-async-throws.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-not-callable.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-result-awaited-once-per-iteration.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-sync-arraylike.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-sync-iterable-async.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-sync-iterable-sync.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-sync-throws-close-async-iterator.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-sync-throws-close-sync-iterator.js create mode 100644 test/built-ins/Array/fromAsync/mapfn-sync-throws.js create mode 100644 test/built-ins/Array/fromAsync/thisarg-object.js create mode 100644 test/built-ins/Array/fromAsync/thisarg-omitted-sloppy.js create mode 100644 test/built-ins/Array/fromAsync/thisarg-omitted-strict.js create mode 100644 test/built-ins/Array/fromAsync/thisarg-primitive-sloppy.js create mode 100644 test/built-ins/Array/fromAsync/thisarg-primitive-strict.js diff --git a/test/built-ins/Array/fromAsync/mapfn-async-arraylike.js b/test/built-ins/Array/fromAsync/mapfn-async-arraylike.js new file mode 100644 index 0000000000..7e032a2bdc --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-async-arraylike.js @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + An asynchronous mapping function is applied to each (awaited) item of an + arraylike. +info: | + 3.k.vii.4. If _mapping_ is *true*, then + a. Let _mappedValue_ be ? Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + b. Let _mappedValue_ be ? Await(_mappedValue_). + ... + 6. Perform ? CreateDataPropertyOrThrow(_A_, _Pk_, _mappedValue_). +flags: [async] +includes: [asyncHelpers.js, compareArray.js] +features: [Array.fromAsync] +---*/ + +const arrayLike = { + length: 4, + 0: 0, + 1: 2, + 2: Promise.resolve(4), + 3: 6, +}; + +async function asyncMap(val, ix) { + return Promise.resolve(val * ix); +} + +asyncTest(async () => { + const result = await Array.fromAsync(arrayLike, asyncMap); + assert.compareArray(result, [0, 2, 8, 18], "async mapfn should be applied to arraylike"); +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-async-iterable-async.js b/test/built-ins/Array/fromAsync/mapfn-async-iterable-async.js new file mode 100644 index 0000000000..24cf748b50 --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-async-iterable-async.js @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + An asynchronous mapping function is applied to each item yielded by an + asynchronous iterable. +info: | + 3.j.ii.6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + ... + c. Set _mappedValue_ to Await(_mappedValue_). + ... + ... + 8. Let _defineStatus_ be CreateDataPropertyOrThrow(_A_, _Pk_, _mappedValue_). +flags: [async] +includes: [asyncHelpers.js, compareArray.js] +features: [Array.fromAsync] +---*/ + +async function* asyncGen() { + for (let i = 0; i < 4; i++) { + yield Promise.resolve(i * 2); + } +} + +async function asyncMap(val, ix) { + return Promise.resolve(val * ix); +} + +asyncTest(async () => { + const result = await Array.fromAsync({ [Symbol.asyncIterator]: asyncGen }, asyncMap); + assert.compareArray(result, [0, 2, 8, 18], "async mapfn should be applied to async iterable"); +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-async-iterable-sync.js b/test/built-ins/Array/fromAsync/mapfn-async-iterable-sync.js new file mode 100644 index 0000000000..1211d7b8ee --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-async-iterable-sync.js @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + An asynchronous mapping function is applied to each item yielded by a + synchronous iterable. +info: | + 3.j.ii.6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + ... + c. Set _mappedValue_ to Await(_mappedValue_). + ... + ... + 8. Let _defineStatus_ be CreateDataPropertyOrThrow(_A_, _Pk_, _mappedValue_). +flags: [async] +includes: [asyncHelpers.js, compareArray.js] +features: [Array.fromAsync] +---*/ + +function* syncGen() { + for (let i = 0; i < 4; i++) { + yield i * 2; + } +} + +async function asyncMap(val, ix) { + return Promise.resolve(val * ix); +} + +asyncTest(async () => { + const result = await Array.fromAsync({ [Symbol.iterator]: syncGen }, asyncMap); + assert.compareArray(result, [0, 2, 8, 18], "async mapfn should be applied to sync iterable"); +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-async-throws-close-async-iterator.js b/test/built-ins/Array/fromAsync/mapfn-async-throws-close-async-iterator.js new file mode 100644 index 0000000000..fdb769fbb7 --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-async-throws-close-async-iterator.js @@ -0,0 +1,40 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + The iterator of an asynchronous iterable is closed when the asynchronous + mapping function throws. +info: | + 3.j.ii.6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + ... + c. Set _mappedValue_ to Await(_mappedValue_). + d. IfAbruptCloseAsyncIterator(_mappedValue_, _iteratorRecord_). +flags: [async] +includes: [asyncHelpers.js] +features: [Array.fromAsync] +---*/ + +let closed = false; +const iterator = { + next() { + return Promise.resolve({ value: 1, done: false }); + }, + return() { + closed = true; + return Promise.resolve({ done: true }); + }, + [Symbol.asyncIterator]() { + return this; + } +} + +asyncTest(async () => { + await assert.throwsAsync(Error, () => Array.fromAsync(iterator, async (val) => { + assert.sameValue(val, 1, "mapfn receives value from iterator"); + throw new Error("mapfn throws"); + }), "async mapfn rejecting should cause fromAsync to reject"); + assert(closed, "async mapfn rejecting should close iterator") +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-async-throws-close-sync-iterator.js b/test/built-ins/Array/fromAsync/mapfn-async-throws-close-sync-iterator.js new file mode 100644 index 0000000000..13b21d7a64 --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-async-throws-close-sync-iterator.js @@ -0,0 +1,40 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + The iterator of a synchronous iterable is closed when the asynchronous mapping + function throws. +info: | + 3.j.ii.6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + ... + c. Set _mappedValue_ to Await(_mappedValue_). + d. IfAbruptCloseAsyncIterator(_mappedValue_, _iteratorRecord_). +flags: [async] +includes: [asyncHelpers.js] +features: [Array.fromAsync] +---*/ + +let closed = false; +const iterator = { + next() { + return { value: 1, done: false }; + }, + return() { + closed = true; + return { done: true }; + }, + [Symbol.iterator]() { + return this; + } +} + +asyncTest(async () => { + await assert.throwsAsync(Error, () => Array.fromAsync(iterator, async (val) => { + assert.sameValue(val, 1, "mapfn receives value from iterator"); + throw new Error("mapfn throws"); + }), "async mapfn rejecting should cause fromAsync to reject"); + assert(closed, "async mapfn rejecting should close iterator") +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-async-throws.js b/test/built-ins/Array/fromAsync/mapfn-async-throws.js new file mode 100644 index 0000000000..8d148a356b --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-async-throws.js @@ -0,0 +1,23 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + The output promise rejects if the asynchronous mapping function rejects. +info: | + 3.j.ii.6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + ... + c. Set _mappedValue_ to Await(_mappedValue_). + d. IfAbruptCloseAsyncIterator(_mappedValue_, _iteratorRecord_). +flags: [async] +includes: [asyncHelpers.js] +features: [Array.fromAsync] +---*/ + +asyncTest(async () => { + await assert.throwsAsync(Test262Error, () => Array.fromAsync([1, 2, 3], async () => { + throw new Test262Error("mapfn throws"); + }), "async mapfn rejecting should cause fromAsync to reject"); +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-not-callable.js b/test/built-ins/Array/fromAsync/mapfn-not-callable.js new file mode 100644 index 0000000000..c3f8e2f9d2 --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-not-callable.js @@ -0,0 +1,25 @@ +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + A TypeError is thrown if the mapfn argument to Array.fromAsync is not callable +info: | + 3.a. If _mapfn_ is *undefined*, let _mapping_ be *false*. + b. Else, + i. If IsCallable(_mapfn_) is *false*, throw a *TypeError* exception. +flags: [async] +includes: [asyncHelpers.js] +features: [Array.fromAsync, BigInt, Symbol] +---*/ + +asyncTest(async () => { + await assert.throwsAsync(TypeError, () => Array.fromAsync([], null), "null mapfn"); + await assert.throwsAsync(TypeError, () => Array.fromAsync([], {}), "non-callable object mapfn"); + await assert.throwsAsync(TypeError, () => Array.fromAsync([], "String"), "string mapfn"); + await assert.throwsAsync(TypeError, () => Array.fromAsync([], true), "boolean mapfn"); + await assert.throwsAsync(TypeError, () => Array.fromAsync([], 3.1416), "number mapfn"); + await assert.throwsAsync(TypeError, () => Array.fromAsync([], 42n), "bigint mapfn"); + await assert.throwsAsync(TypeError, () => Array.fromAsync([], Symbol()), "symbol mapfn"); +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-result-awaited-once-per-iteration.js b/test/built-ins/Array/fromAsync/mapfn-result-awaited-once-per-iteration.js new file mode 100644 index 0000000000..0b3e60eff5 --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-result-awaited-once-per-iteration.js @@ -0,0 +1,47 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + The returned value from each invocation of the asynchronous mapping function + is awaited exactly once. +info: | + 3.j.ii.6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + ... + c. Set _mappedValue_ to Await(_mappedValue_). +flags: [async] +includes: [asyncHelpers.js, compareArray.js, temporalHelpers.js] +features: [Array.fromAsync] +---*/ + +const calls = []; +const expected = [ + "call mapping", + "get thenable_0.then", + "call thenable_0.then", + "call mapping", + "get thenable_1.then", + "call thenable_1.then", + "call mapping", + "get thenable_2.then", + "call thenable_2.then", +]; + +function mapping(val, ix) { + calls.push("call mapping"); + const thenableName = `thenable_${ix}`; + return TemporalHelpers.propertyBagObserver(calls, { + then(resolve, reject) { + calls.push(`call ${thenableName}.then`); + resolve(val * 2); + } + }, thenableName) +} + +asyncTest(async () => { + const result = await Array.fromAsync([1, 2, 3], mapping); + assert.compareArray(result, [2, 4, 6], "mapping function applied"); + assert.compareArray(calls, expected, "observable operations"); +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-sync-arraylike.js b/test/built-ins/Array/fromAsync/mapfn-sync-arraylike.js new file mode 100644 index 0000000000..2ebf437341 --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-sync-arraylike.js @@ -0,0 +1,35 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + A synchronous mapping function is applied to each (awaited) item of an + arraylike. +info: | + 3.k.vii.4. If _mapping_ is *true*, then + a. Let _mappedValue_ be ? Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + ... + ... + 6. Perform ? CreateDataPropertyOrThrow(_A_, _Pk_, _mappedValue_). +flags: [async] +includes: [asyncHelpers.js, compareArray.js] +features: [Array.fromAsync] +---*/ + +const arrayLike = { + length: 4, + 0: 0, + 1: 2, + 2: Promise.resolve(4), + 3: 6, +}; + +function syncMap(val, ix) { + return val * ix; +} + +asyncTest(async () => { + const result = await Array.fromAsync(arrayLike, syncMap); + assert.compareArray(result, [0, 2, 8, 18], "sync mapfn should be applied to arraylike"); +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-sync-iterable-async.js b/test/built-ins/Array/fromAsync/mapfn-sync-iterable-async.js new file mode 100644 index 0000000000..4ffe4cc84b --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-sync-iterable-async.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + A synchronous mapping function is applied to each item yielded by an + asynchronous iterable. +info: | + 3.j.ii.6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + ... + ... + 8. Let _defineStatus_ be CreateDataPropertyOrThrow(_A_, _Pk_, _mappedValue_). +flags: [async] +includes: [asyncHelpers.js, compareArray.js] +features: [Array.fromAsync] +---*/ + +async function* asyncGen() { + for (let i = 0; i < 4; i++) { + yield Promise.resolve(i * 2); + } +} + +function syncMap(val, ix) { + return val * ix; +} + +asyncTest(async () => { + const result = await Array.fromAsync({ [Symbol.asyncIterator]: asyncGen }, syncMap); + assert.compareArray(result, [0, 2, 8, 18], "sync mapfn should be applied to async iterable"); +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-sync-iterable-sync.js b/test/built-ins/Array/fromAsync/mapfn-sync-iterable-sync.js new file mode 100644 index 0000000000..b2a00ec2a2 --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-sync-iterable-sync.js @@ -0,0 +1,33 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + A synchronous mapping function is applied to each item yielded by a + synchronous iterable. +info: | + 3.j.ii.6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + ... + ... + 8. Let _defineStatus_ be CreateDataPropertyOrThrow(_A_, _Pk_, _mappedValue_). +flags: [async] +includes: [asyncHelpers.js, compareArray.js] +features: [Array.fromAsync] +---*/ + +function* syncGen() { + for (let i = 0; i < 4; i++) { + yield i * 2; + } +} + +function syncMap(val, ix) { + return val * ix; +} + +asyncTest(async () => { + const result = await Array.fromAsync({ [Symbol.iterator]: syncGen }, syncMap); + assert.compareArray(result, [0, 2, 8, 18], "sync mapfn should be applied to sync iterable"); +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-sync-throws-close-async-iterator.js b/test/built-ins/Array/fromAsync/mapfn-sync-throws-close-async-iterator.js new file mode 100644 index 0000000000..722a53bd36 --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-sync-throws-close-async-iterator.js @@ -0,0 +1,39 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + The iterator of an asynchronous iterable is closed when the synchronous + mapping function throws. +info: | + 3.j.ii.6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + b. IfAbruptCloseAsyncIterator(_mappedValue_, _iteratorRecord_). + ... +flags: [async] +includes: [asyncHelpers.js] +features: [Array.fromAsync] +---*/ + +let closed = false; +const iterator = { + next() { + return Promise.resolve({ value: 1, done: false }); + }, + return() { + closed = true; + return Promise.resolve({ done: true }); + }, + [Symbol.asyncIterator]() { + return this; + } +} + +asyncTest(async () => { + await assert.throwsAsync(Error, () => Array.fromAsync(iterator, (val) => { + assert.sameValue(val, 1, "mapfn receives value from iterator"); + throw new Error("mapfn throws"); + }), "sync mapfn throwing should cause fromAsync to reject"); + assert(closed, "sync mapfn throwing should close iterator") +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-sync-throws-close-sync-iterator.js b/test/built-ins/Array/fromAsync/mapfn-sync-throws-close-sync-iterator.js new file mode 100644 index 0000000000..15fea81c2d --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-sync-throws-close-sync-iterator.js @@ -0,0 +1,39 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + The iterator of a synchronous iterable is closed when the synchronous mapping + function throws. +info: | + 3.j.ii.6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + b. IfAbruptCloseAsyncIterator(_mappedValue_, _iteratorRecord_). + ... +flags: [async] +includes: [asyncHelpers.js] +features: [Array.fromAsync] +---*/ + +let closed = false; +const iterator = { + next() { + return { value: 1, done: false }; + }, + return() { + closed = true; + return { done: true }; + }, + [Symbol.iterator]() { + return this; + } +} + +asyncTest(async () => { + await assert.throwsAsync(Error, () => Array.fromAsync(iterator, (val) => { + assert.sameValue(val, 1, "mapfn receives value from iterator"); + throw new Error("mapfn throws"); + }), "sync mapfn throwing should cause fromAsync to reject"); + assert(closed, "sync mapfn throwing should close iterator") +}); diff --git a/test/built-ins/Array/fromAsync/mapfn-sync-throws.js b/test/built-ins/Array/fromAsync/mapfn-sync-throws.js new file mode 100644 index 0000000000..c3bc739ff5 --- /dev/null +++ b/test/built-ins/Array/fromAsync/mapfn-sync-throws.js @@ -0,0 +1,22 @@ +// Copyright (C) 2023 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + The output promise rejects if the synchronous mapping function throws. +info: | + 3.j.ii.6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + b. IfAbruptCloseAsyncIterator(_mappedValue_, _iteratorRecord_). + ... +flags: [async] +includes: [asyncHelpers.js] +features: [Array.fromAsync] +---*/ + +asyncTest(async () => { + await assert.throwsAsync(Test262Error, () => Array.fromAsync([1, 2, 3], () => { + throw new Test262Error("mapfn throws"); + }), "sync mapfn throwing should cause fromAsync to reject"); +}); diff --git a/test/built-ins/Array/fromAsync/thisarg-object.js b/test/built-ins/Array/fromAsync/thisarg-object.js new file mode 100644 index 0000000000..1f27785692 --- /dev/null +++ b/test/built-ins/Array/fromAsync/thisarg-object.js @@ -0,0 +1,21 @@ +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: If thisArg is an object, it's bound to mapfn as the this-value +info: | + 6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). +flags: [async] +includes: [asyncHelpers.js] +features: [Array.fromAsync] +---*/ + +asyncTest(async () => { + const myThisValue = {}; + + await Array.fromAsync([1, 2, 3], async function () { + assert.sameValue(this, myThisValue, "thisArg should be bound as the this-value of mapfn"); + }, myThisValue); +}); diff --git a/test/built-ins/Array/fromAsync/thisarg-omitted-sloppy.js b/test/built-ins/Array/fromAsync/thisarg-omitted-sloppy.js new file mode 100644 index 0000000000..b17ed5f339 --- /dev/null +++ b/test/built-ins/Array/fromAsync/thisarg-omitted-sloppy.js @@ -0,0 +1,33 @@ +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + If thisArg is omitted, mapfn is called with the global object as the + this-value in sloppy mode +info: | + 6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + + OrdinaryCallBindThis, when _F_.[[ThisMode]] is ~global~, where _F_ is the + function object: + 6. Else, + a. If _thisArgument_ is *undefined* or *null*, then + i. Let _globalEnv_ be _calleeRealm_.[[GlobalEnv]]. + ii. Assert: _globalEnv_ is a Global Environment Record. + iii. Let _thisValue_ be _globalEnv_.[[GlobalThisValue]]. +flags: [async, noStrict] +includes: [asyncHelpers.js] +features: [Array.fromAsync] +---*/ + +asyncTest(async () => { + await Array.fromAsync([1, 2, 3], async function () { + assert.sameValue( + this, + globalThis, + "the global should be bound as the this-value of mapfn when thisArg is omitted" + ); + }); +}); diff --git a/test/built-ins/Array/fromAsync/thisarg-omitted-strict.js b/test/built-ins/Array/fromAsync/thisarg-omitted-strict.js new file mode 100644 index 0000000000..3dead992e2 --- /dev/null +++ b/test/built-ins/Array/fromAsync/thisarg-omitted-strict.js @@ -0,0 +1,28 @@ +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + If thisArg is omitted, mapfn is called with undefined as the this-value in + strict mode +info: | + 6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + + In OrdinaryCallBindThis, _thisArgument_ is always bound as the this-value in + strict mode (_F_.[[ThisMode]] is ~strict~, where _F_ is the function object.) +flags: [async, onlyStrict] +includes: [asyncHelpers.js] +features: [Array.fromAsync] +---*/ + +asyncTest(async () => { + await Array.fromAsync([1, 2, 3], async function () { + assert.sameValue( + this, + undefined, + "undefined should be bound as the this-value of mapfn when thisArg is omitted" + ); + }); +}); diff --git a/test/built-ins/Array/fromAsync/thisarg-primitive-sloppy.js b/test/built-ins/Array/fromAsync/thisarg-primitive-sloppy.js new file mode 100644 index 0000000000..c459102a54 --- /dev/null +++ b/test/built-ins/Array/fromAsync/thisarg-primitive-sloppy.js @@ -0,0 +1,75 @@ +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + If thisArg is a primitive, mapfn is called with a wrapper this-value or the + global, according to the usual rules of sloppy mode +info: | + 6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + + OrdinaryCallBindThis, when _F_.[[ThisMode]] is ~global~, where _F_ is the + function object: + 6. Else, + a. If _thisArgument_ is *undefined* or *null*, then + i. Let _globalEnv_ be _calleeRealm_.[[GlobalEnv]]. + ii. Assert: _globalEnv_ is a Global Environment Record. + iii. Let _thisValue_ be _globalEnv_.[[GlobalThisValue]]. + b. Else, + i. Let _thisValue_ be ! ToObject(_thisArgument_). + ii. NOTE: ToObject produces wrapper objects using _calleeRealm_. +flags: [async, noStrict] +includes: [asyncHelpers.js] +features: [Array.fromAsync] +---*/ + +asyncTest(async () => { + await Array.fromAsync([1, 2, 3], async function () { + assert.sameValue( + this, + globalThis, + "the global should be bound as the this-value of mapfn when thisArg is undefined" + ); + }, undefined); + + await Array.fromAsync([1, 2, 3], async function () { + assert.sameValue( + this, + globalThis, + "the global should be bound as the this-value of mapfn when thisArg is null" + ); + }, null); + + await Array.fromAsync([1, 2, 3], async function () { + assert.notSameValue(this, "string", "string thisArg should not be bound as the this-value of mapfn"); + assert.sameValue(typeof this, "object", "a String wrapper object should be bound as the this-value of mapfn when thisArg is a string") + assert.sameValue(this.valueOf(), "string", "String wrapper object should have the same primitive value as thisArg"); + }, "string"); + + await Array.fromAsync([1, 2, 3], async function () { + assert.notSameValue(this, 3.1416, "number thisArg should be not bound as the this-value of mapfn"); + assert.sameValue(typeof this, "object", "a Number wrapper object should be bound as the this-value of mapfn when thisArg is a number") + assert.sameValue(this.valueOf(), 3.1416, "Number wrapper object should have the same primitive value as thisArg"); + }, 3.1416); + + await Array.fromAsync([1, 2, 3], async function () { + assert.notSameValue(this, 42n, "bigint thisArg should not be bound as the this-value of mapfn"); + assert.sameValue(typeof this, "object", "a BigInt wrapper object should be bound as the this-value of mapfn when thisArg is a bigint") + assert.sameValue(this.valueOf(), 42n, "BigInt wrapper object should have the same primitive value as thisArg"); + }, 42n); + + await Array.fromAsync([1, 2, 3], async function () { + assert.notSameValue(this, true, "boolean thisArg should not be bound as the this-value of mapfn"); + assert.sameValue(typeof this, "object", "a Boolean wrapper object should be bound as the this-value of mapfn when thisArg is a boolean") + assert.sameValue(this.valueOf(), true, "Boolean wrapper object should have the same primitive value as thisArg"); + }, true); + + const symbolThis = Symbol("symbol"); + await Array.fromAsync([1, 2, 3], async function () { + assert.notSameValue(this, symbolThis, "symbol thisArg should not be bound as the this-value of mapfn"); + assert.sameValue(typeof this, "object", "a Symbol wrapper object should be bound as the this-value of mapfn when thisArg is a symbol") + assert.sameValue(this.valueOf(), symbolThis, "Symbol wrapper object should have the same primitive value as thisArg"); + }, symbolThis); +}); diff --git a/test/built-ins/Array/fromAsync/thisarg-primitive-strict.js b/test/built-ins/Array/fromAsync/thisarg-primitive-strict.js new file mode 100644 index 0000000000..34118d1953 --- /dev/null +++ b/test/built-ins/Array/fromAsync/thisarg-primitive-strict.js @@ -0,0 +1,49 @@ +// Copyright (C) 2022 Igalia, S.L. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-array.fromasync +description: > + If thisArg is a primitive, mapfn is called with it as the this-value in strict + mode +info: | + 6. If _mapping_ is *true*, then + a. Let _mappedValue_ be Call(_mapfn_, _thisArg_, « _nextValue_, 𝔽(_k_) »). + + In OrdinaryCallBindThis, _thisArgument_ is always bound as the this-value in + strict mode (_F_.[[ThisMode]] is ~strict~, where _F_ is the function object.) +flags: [async, onlyStrict] +includes: [asyncHelpers.js] +features: [Array.fromAsync] +---*/ + +asyncTest(async () => { + await Array.fromAsync([1, 2, 3], async function () { + assert.sameValue(this, undefined, "undefined thisArg should be bound as the this-value of mapfn"); + }, undefined); + + await Array.fromAsync([1, 2, 3], async function () { + assert.sameValue(this, null, "null thisArg should be bound as the this-value of mapfn"); + }, null); + + await Array.fromAsync([1, 2, 3], async function () { + assert.sameValue(this, "string", "string thisArg should be bound as the this-value of mapfn"); + }, "string"); + + await Array.fromAsync([1, 2, 3], async function () { + assert.sameValue(this, 3.1416, "number thisArg should be bound as the this-value of mapfn"); + }, 3.1416); + + await Array.fromAsync([1, 2, 3], async function () { + assert.sameValue(this, 42n, "bigint thisArg should be bound as the this-value of mapfn"); + }, 42n); + + await Array.fromAsync([1, 2, 3], async function () { + assert.sameValue(this, true, "boolean thisArg should be bound as the this-value of mapfn"); + }, true); + + const symbolThis = Symbol("symbol"); + await Array.fromAsync([1, 2, 3], async function () { + assert.sameValue(this, symbolThis, "symbol thisArg should be bound as the this-value of mapfn"); + }, symbolThis); +});