Permalink
Show file tree
Hide file tree
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
[JSC] Implement array-from-async
https://bugs.webkit.org/show_bug.cgi?id=245260 rdar://problem/100303653 Reviewed by Alexey Shvayka. This patch implements stage-3 Array.fromAsync[1] feature. The goal of this feature is providing async iteration version of Array.from. Array.from's concept. const arr = []; for (const v of iterable) { arr.push(v); } Array.fromAsync's concept. const arr = []; for await (const v of asyncIterable) { arr.push(v); } The complicated part of this change is that, when using `async function` in builtin JS, it automatically generates internal promise, which we would like to avoid here. In the future, we would like to remove internal promise completely, but for now, we workaround this restriction by the convention that, when the builtin JS function's name starts with `defaultAsync`, then we use normal promise instead of internal promise. [1]: https://github.com/tc39/proposal-array-from-async * JSTests/stress/array-from-async-basic.js: Added. (shouldBe): (shouldBeArray): (async test.async shouldBeArray): (async test): (test.catch): * JSTests/stress/array-from-async-map-promise.js: Added. (shouldBe): (shouldBeArray): (async test.): (async test.async shouldBeArray): (async test): (test.catch): * JSTests/stress/array-from-async-map-this.js: Added. (shouldBe): (shouldBeArray): (async test.): (async test.async shouldBeArray): (async test): (test.catch): * JSTests/stress/array-from-async-map.js: Added. (shouldBe): (shouldBeArray): (async test.): (async test.async shouldBeArray): (async test): (test.catch): * Source/JavaScriptCore/builtins/ArrayConstructor.js: (from.wrapper.iterator): (from): (wrapper.asyncIterator): (linkTimeConstant.async defaultAsyncFromAsyncIterator): (linkTimeConstant.async defaultAsyncFromAsyncArrayLike): (fromAsync): * Source/JavaScriptCore/bytecompiler/BytecodeGenerator.cpp: (JSC::BytecodeGenerator::BytecodeGenerator): * Source/JavaScriptCore/runtime/ArrayConstructor.cpp: (JSC::ArrayConstructor::finishCreation): * Source/JavaScriptCore/runtime/OptionsList.h: Canonical link: https://commits.webkit.org/257177@main
- Loading branch information
1 parent
4467fe2
commit 464a308595c09e6151402adbb8e4e94018a115bd
Showing
10 changed files
with
464 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,147 @@ | ||
//@ requireOptions("--useArrayFromAsync=1") | ||
|
||
function shouldBe(actual, expected) { | ||
if (actual !== expected) | ||
throw new Error('bad value: ' + actual); | ||
} | ||
|
||
function shouldBeArray(actual, expected) { | ||
shouldBe(actual.length, expected.length); | ||
for (var i = 0; i < expected.length; ++i) { | ||
try { | ||
shouldBe(actual[i], expected[i]); | ||
} catch(e) { | ||
print(JSON.stringify(actual)); | ||
throw e; | ||
} | ||
} | ||
} | ||
|
||
shouldBe(Array.fromAsync.length, 1); | ||
|
||
async function test() | ||
{ | ||
// sync iterator | ||
{ | ||
let result = await Array.fromAsync([0, 1, 2, 3, 4, 5]); | ||
shouldBeArray(result, [0, 1, 2, 3, 4, 5]); | ||
} | ||
{ | ||
let result = await Array.fromAsync(function* generator() { | ||
for (var i = 0; i < 6; ++i) | ||
yield i; | ||
}()); | ||
shouldBeArray(result, [0, 1, 2, 3, 4, 5]); | ||
} | ||
|
||
// async iterator | ||
{ | ||
let result = await Array.fromAsync(async function* generator() { | ||
for (var i = 0; i < 6; ++i) | ||
yield i; | ||
}()); | ||
shouldBeArray(result, [0, 1, 2, 3, 4, 5]); | ||
} | ||
|
||
// array-like | ||
{ | ||
let result = await Array.fromAsync({ | ||
[0]: 0, | ||
[1]: 1, | ||
[2]: 2, | ||
[3]: 3, | ||
[4]: 4, | ||
[5]: 5, | ||
length: 6, | ||
}); | ||
shouldBeArray(result, [0, 1, 2, 3, 4, 5]); | ||
} | ||
|
||
try { | ||
await Array.fromAsync(null); | ||
} catch (error) { | ||
shouldBe(String(error), `TypeError: null is not an object`); | ||
} | ||
|
||
try { | ||
await Array.fromAsync(undefined); | ||
} catch (error) { | ||
shouldBe(String(error), `TypeError: undefined is not an object`); | ||
} | ||
|
||
try { | ||
await Array.fromAsync({ [Symbol.asyncIterator]: 42 }); | ||
} catch (error) { | ||
shouldBe(String(error), `TypeError: Array.fromAsync requires that the property of the first argument, items[Symbol.asyncIterator], when exists, be a function`); | ||
} | ||
|
||
try { | ||
await Array.fromAsync({ [Symbol.iterator]: 42 }); | ||
} catch (error) { | ||
shouldBe(String(error), `TypeError: Array.fromAsync requires that the property of the first argument, items[Symbol.iterator], when exists, be a function`); | ||
} | ||
|
||
// array-like, asyncIterator is ignored. | ||
{ | ||
let result = await Array.fromAsync({ | ||
[Symbol.asyncIterator]: null, | ||
[0]: 0, | ||
[1]: 1, | ||
[2]: 2, | ||
[3]: 3, | ||
[4]: 4, | ||
[5]: 5, | ||
length: 6, | ||
}); | ||
shouldBeArray(result, [0, 1, 2, 3, 4, 5]); | ||
} | ||
{ | ||
let result = await Array.fromAsync({ | ||
[Symbol.asyncIterator]: undefined, | ||
[0]: 0, | ||
[1]: 1, | ||
[2]: 2, | ||
[3]: 3, | ||
[4]: 4, | ||
[5]: 5, | ||
length: 6, | ||
}); | ||
shouldBeArray(result, [0, 1, 2, 3, 4, 5]); | ||
} | ||
|
||
// array-like, iterator is ignored. | ||
{ | ||
let result = await Array.fromAsync({ | ||
[Symbol.iterator]: null, | ||
[0]: 0, | ||
[1]: 1, | ||
[2]: 2, | ||
[3]: 3, | ||
[4]: 4, | ||
[5]: 5, | ||
length: 6, | ||
}); | ||
shouldBeArray(result, [0, 1, 2, 3, 4, 5]); | ||
} | ||
{ | ||
let result = await Array.fromAsync({ | ||
[Symbol.iterator]: undefined, | ||
[0]: 0, | ||
[1]: 1, | ||
[2]: 2, | ||
[3]: 3, | ||
[4]: 4, | ||
[5]: 5, | ||
length: 6, | ||
}); | ||
shouldBeArray(result, [0, 1, 2, 3, 4, 5]); | ||
} | ||
} | ||
|
||
test().catch(function (error) { | ||
print("FAIL"); | ||
print(String(error)); | ||
print(String(error.stack)); | ||
$vm.abort() | ||
}); | ||
drainMicrotasks(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
//@ requireOptions("--useArrayFromAsync=1") | ||
|
||
function shouldBe(actual, expected) { | ||
if (actual !== expected) | ||
throw new Error('bad value: ' + actual); | ||
} | ||
|
||
function shouldBeArray(actual, expected) { | ||
shouldBe(actual.length, expected.length); | ||
for (var i = 0; i < expected.length; ++i) { | ||
try { | ||
shouldBe(actual[i], expected[i]); | ||
} catch(e) { | ||
print(JSON.stringify(actual)); | ||
throw e; | ||
} | ||
} | ||
} | ||
|
||
shouldBe(Array.fromAsync.length, 1); | ||
|
||
async function test() | ||
{ | ||
// sync iterator | ||
{ | ||
let result = await Array.fromAsync([0, 1, 2, 3, 4, 5], function (value) { return Promise.resolve(value * value) }); | ||
shouldBeArray(result, [0, 1, 4, 9, 16, 25]); | ||
} | ||
{ | ||
let result = await Array.fromAsync(function* generator() { | ||
for (var i = 0; i < 6; ++i) | ||
yield i; | ||
}(), function (value) { return Promise.resolve(value * value) }); | ||
shouldBeArray(result, [0, 1, 4, 9, 16, 25]); | ||
} | ||
|
||
// async iterator | ||
{ | ||
let result = await Array.fromAsync(async function* generator() { | ||
for (var i = 0; i < 6; ++i) | ||
yield i; | ||
}(), function (value) { return Promise.resolve(value * value) }); | ||
shouldBeArray(result, [0, 1, 4, 9, 16, 25]); | ||
} | ||
|
||
// array-like | ||
{ | ||
let result = await Array.fromAsync({ | ||
[0]: 0, | ||
[1]: 1, | ||
[2]: 2, | ||
[3]: 3, | ||
[4]: 4, | ||
[5]: 5, | ||
length: 6, | ||
}, function (value) { return Promise.resolve(value * value) }); | ||
shouldBeArray(result, [0, 1, 4, 9, 16, 25]); | ||
} | ||
} | ||
|
||
test().catch(function (error) { | ||
print("FAIL"); | ||
print(String(error)); | ||
print(String(error.stack)); | ||
$vm.abort() | ||
}); | ||
drainMicrotasks(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
//@ requireOptions("--useArrayFromAsync=1") | ||
|
||
function shouldBe(actual, expected) { | ||
if (actual !== expected) | ||
throw new Error('bad value: ' + actual); | ||
} | ||
|
||
function shouldBeArray(actual, expected) { | ||
shouldBe(actual.length, expected.length); | ||
for (var i = 0; i < expected.length; ++i) { | ||
try { | ||
shouldBe(actual[i], expected[i]); | ||
} catch(e) { | ||
print(JSON.stringify(actual)); | ||
throw e; | ||
} | ||
} | ||
} | ||
|
||
shouldBe(Array.fromAsync.length, 1); | ||
|
||
async function test() | ||
{ | ||
// sync iterator | ||
{ | ||
let result = await Array.fromAsync([0, 1, 2, 3, 4, 5], function (value) { return value * value + this }, 42); | ||
shouldBeArray(result, [42, 43, 46, 51, 58, 67]); | ||
} | ||
{ | ||
let result = await Array.fromAsync(function* generator() { | ||
for (var i = 0; i < 6; ++i) | ||
yield i; | ||
}(), function (value) { return value * value + this }, 42); | ||
shouldBeArray(result, [42, 43, 46, 51, 58, 67]); | ||
} | ||
|
||
// async iterator | ||
{ | ||
let result = await Array.fromAsync(async function* generator() { | ||
for (var i = 0; i < 6; ++i) | ||
yield i; | ||
}(), function (value) { return value * value + this }, 42); | ||
shouldBeArray(result, [42, 43, 46, 51, 58, 67]); | ||
} | ||
|
||
// array-like | ||
{ | ||
let result = await Array.fromAsync({ | ||
[0]: 0, | ||
[1]: 1, | ||
[2]: 2, | ||
[3]: 3, | ||
[4]: 4, | ||
[5]: 5, | ||
length: 6, | ||
}, function (value) { return value * value + this }, 42); | ||
shouldBeArray(result, [42, 43, 46, 51, 58, 67]); | ||
} | ||
} | ||
|
||
test().catch(function (error) { | ||
print("FAIL"); | ||
print(String(error)); | ||
print(String(error.stack)); | ||
$vm.abort() | ||
}, 42); | ||
drainMicrotasks(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
//@ requireOptions("--useArrayFromAsync=1") | ||
|
||
function shouldBe(actual, expected) { | ||
if (actual !== expected) | ||
throw new Error('bad value: ' + actual); | ||
} | ||
|
||
function shouldBeArray(actual, expected) { | ||
shouldBe(actual.length, expected.length); | ||
for (var i = 0; i < expected.length; ++i) { | ||
try { | ||
shouldBe(actual[i], expected[i]); | ||
} catch(e) { | ||
print(JSON.stringify(actual)); | ||
throw e; | ||
} | ||
} | ||
} | ||
|
||
shouldBe(Array.fromAsync.length, 1); | ||
|
||
async function test() | ||
{ | ||
// sync iterator | ||
{ | ||
let result = await Array.fromAsync([0, 1, 2, 3, 4, 5], function (value) { return value * value }); | ||
shouldBeArray(result, [0, 1, 4, 9, 16, 25]); | ||
} | ||
{ | ||
let result = await Array.fromAsync(function* generator() { | ||
for (var i = 0; i < 6; ++i) | ||
yield i; | ||
}(), function (value) { return value * value }); | ||
shouldBeArray(result, [0, 1, 4, 9, 16, 25]); | ||
} | ||
|
||
// async iterator | ||
{ | ||
let result = await Array.fromAsync(async function* generator() { | ||
for (var i = 0; i < 6; ++i) | ||
yield i; | ||
}(), function (value) { return value * value }); | ||
shouldBeArray(result, [0, 1, 4, 9, 16, 25]); | ||
} | ||
|
||
// array-like | ||
{ | ||
let result = await Array.fromAsync({ | ||
[0]: 0, | ||
[1]: 1, | ||
[2]: 2, | ||
[3]: 3, | ||
[4]: 4, | ||
[5]: 5, | ||
length: 6, | ||
}, function (value) { return value * value }); | ||
shouldBeArray(result, [0, 1, 4, 9, 16, 25]); | ||
} | ||
} | ||
|
||
test().catch(function (error) { | ||
print("FAIL"); | ||
print(String(error)); | ||
print(String(error.stack)); | ||
$vm.abort() | ||
}); | ||
drainMicrotasks(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.