Merge pull request #2096 from anba/asynciter-fx

Test cases for async function and async generator edge-cases
This commit is contained in:
Leo Balter 2019-03-13 13:22:46 -04:00 committed by GitHub
commit 8e2a07d2e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 878 additions and 0 deletions

View File

@ -0,0 +1,80 @@
// Copyright (C) 2019 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-return-statement-runtime-semantics-evaluation
description: >
Return with an explicit return value awaits this value.
info: |
13.10.1 Runtime Semantics: Evaluation
ReturnStatement : return;
1. Return Completion { [[Type]]: return, [[Value]]: undefined, [[Target]]: empty }.
ReturnStatement : return Expression ;
1. Let exprRef be the result of evaluating Expression.
2. Let exprValue be ? GetValue(exprRef).
3. If ! GetGeneratorKind() is async, set exprValue to ? Await(exprValue).
4. Return Completion { [[Type]]: return, [[Value]]: exprValue, [[Target]]: empty }.
25.5.3.2 AsyncGeneratorStart ( generator, generatorBody )
...
5. Set the code evaluation state of genContext such that when evaluation is resumed for that
execution context the following steps will be performed:
a. Let result be the result of evaluating generatorBody.
...
e. If result is a normal completion, let resultValue be undefined.
...
g. Return ! AsyncGeneratorResolve(generator, resultValue, true).
includes: [compareArray.js]
flags: [async]
features: [async-iteration]
---*/
// 25.5.3.2, step 5.e: |generatorBody| execution ends with a normal completion.
async function* g1() {
// no return
}
// 13.10.1: No expression form means direct return.
async function* g2() {
return;
}
// 13.10.1: Explicit expression requires Await.
async function* g3() {
return undefined; // Return undefined via global value `undefined`.
}
// 13.10.1: Explicit expression requires Await.
async function* g4() {
return void 0; // Return undefined via void expression.
}
var expected = [
"tick 1",
"g1 ret",
"g2 ret",
"tick 2",
"g3 ret",
"g4 ret",
];
var actual = [];
Promise.resolve(0)
.then(() => actual.push("tick 1"))
.then(() => actual.push("tick 2"))
.then(() => {
assert.compareArray(actual, expected, "Ticks for implicit and explicit return undefined");
}).then($DONE, $DONE);
g1().next().then(v => actual.push("g1 ret"));
g2().next().then(v => actual.push("g2 ret"));
g3().next().then(v => actual.push("g3 ret"));
g4().next().then(v => actual.push("g4 ret"));

View File

@ -0,0 +1,104 @@
// Copyright (C) 2019 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-generator-function-definitions-runtime-semantics-evaluation
description: >
Return resumption value is awaited upon and hence is treated as a thenable.
info: |
14.4.14 Runtime Semantics: Evaluation
YieldExpression : yield AssignmentExpression
...
3. Let value be ? GetValue(exprRef).
4. If generatorKind is async, then return ? AsyncGeneratorYield(value).
...
25.5.3.7 AsyncGeneratorYield ( value )
...
5. Set value to ? Await(value).
...
8. Set the code evaluation state of genContext such that when evaluation is resumed with a
Completion resumptionValue the following steps will be performed:
...
b. Let awaited be Await(resumptionValue.[[Value]]).
...
e. Return Completion { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }.
...
6.2.3.1 Await
...
2. Let promise be ? PromiseResolve(%Promise%, « value »).
...
25.6.4.5.1 PromiseResolve ( C, x )
...
3. Let promiseCapability be ? NewPromiseCapability(C).
4. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »).
...
25.6.1.5 NewPromiseCapability ( C )
...
7. Let promise be ? Construct(C, « executor »).
...
25.6.3.1 Promise ( executor )
...
8. Let resolvingFunctions be CreateResolvingFunctions(promise).
...
25.6.1.3 CreateResolvingFunctions ( promise )
...
2. Let stepsResolve be the algorithm steps defined in Promise Resolve Functions (25.6.1.3.2).
3. Let resolve be CreateBuiltinFunction(stepsResolve, « [[Promise]], [[AlreadyResolved]] »).
...
25.6.1.3.2 Promise Resolve Functions
...
9. Let then be Get(resolution, "then").
...
includes: [compareArray.js]
flags: [async]
features: [async-iteration]
---*/
var expected = [
"start",
// `Await(value)` promise resolved.
"tick 1",
// "then" of `resumptionValue.[[Value]]` accessed.
"get then",
// `Await(resumptionValue.[[Value]])` promise resolved.
"tick 2",
];
var actual = [];
async function* f() {
actual.push("start");
yield 123;
actual.push("stop - never reached");
}
Promise.resolve(0)
.then(() => actual.push("tick 1"))
.then(() => actual.push("tick 2"))
.then(() => {
assert.compareArray(actual, expected, "Ticks for return with thenable getter");
}).then($DONE, $DONE);
var it = f();
// Start generator execution.
it.next();
// Stop generator execution.
it.return({
get then() {
actual.push("get then");
}
});

View File

@ -0,0 +1,55 @@
// Copyright (C) 2019 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-createasyncfromsynciterator
description: >
Async-from-Sync Iterator instances are not accessible from user code.
info: |
25.1.4.1 CreateAsyncFromSyncIterator ( syncIteratorRecord )
1. Let asyncIterator be ! ObjectCreate(%AsyncFromSyncIteratorPrototype%, « [[SyncIteratorRecord]] »).
2. Set asyncIterator.[[SyncIteratorRecord]] to syncIteratorRecord.
3. Let nextMethod be ! Get(asyncIterator, "next").
4. Let iteratorRecord be Record { [[Iterator]]: asyncIterator, [[NextMethod]]: nextMethod, [[Done]]: false }.
5. Return iteratorRecord.
14.4.14 Runtime Semantics: Evaluation
YieldExpression : yield * AssignmentExpression
1. Let generatorKind be ! GetGeneratorKind().
...
4. Let iteratorRecord be ? GetIterator(value, generatorKind).
...
7.4.1 GetIterator ( obj [ , hint [ , method ] ] )
...
3. If method is not present, then
a. If hint is async, then
i. Set method to ? GetMethod(obj, @@asyncIterator).
ii. If method is undefined, then
1. Let syncMethod be ? GetMethod(obj, @@iterator).
2. Let syncIteratorRecord be ? GetIterator(obj, sync, syncMethod).
3. Return ? CreateAsyncFromSyncIterator(syncIteratorRecord).
...
flags: [async]
features: [async-iteration]
---*/
var AsyncIteratorPrototype = Object.getPrototypeOf(async function*(){}.constructor.prototype.prototype);
Object.defineProperty(AsyncIteratorPrototype, Symbol.iterator, {
get() {
throw new Error("@@iterator accessed");
}
});
Object.defineProperty(AsyncIteratorPrototype, Symbol.asyncIterator, {
get() {
throw new Error("@@asyncIterator accessed");
}
});
async function* g() {
yield* [];
}
g().next().then(() => $DONE(), $DONE);

View File

@ -0,0 +1,53 @@
// Copyright (C) 2019 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-generator-function-definitions-runtime-semantics-evaluation
description: >
Abrupt completion when calling IteratorValue is propagated when received.[[Type]] is normal.
info: |
14.4.14 Runtime Semantics: Evaluation
YieldExpression : yield* AssignmentExpression
...
7. Repeat,
a. If received.[[Type]] is normal, then
...
vi. If generatorKind is async, then set received to AsyncGeneratorYield(? IteratorValue(innerResult)).
...
flags: [async]
features: [async-iteration]
---*/
var token = {};
var asyncIter = {
[Symbol.asyncIterator]() {
return this;
},
next() {
return {
done: false,
get value() {
throw token;
}
};
}
};
async function* f() {
var thrown;
try {
yield* asyncIter;
} catch (e) {
thrown = e;
}
return thrown;
}
var iter = f();
iter.next().then(({value}) => {
assert.sameValue(value, token);
}).then($DONE, $DONE);

View File

@ -0,0 +1,63 @@
// Copyright (C) 2019 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-generator-function-definitions-runtime-semantics-evaluation
description: >
Abrupt completion when calling IteratorValue is propagated when received.[[Type]] is return.
info: |
14.4.14 Runtime Semantics: Evaluation
YieldExpression : yield* AssignmentExpression
...
7. Repeat,
...
c. Else,
i. Assert: received.[[Type]] is return.
...
ix. If generatorKind is async, then set received to AsyncGeneratorYield(? IteratorValue(innerReturnResult)).
...
flags: [async]
features: [async-iteration]
---*/
var token = {};
var asyncIter = {
[Symbol.asyncIterator]() {
return this;
},
next() {
return {
done: false,
value: undefined,
};
},
return() {
return {
done: false,
get value() {
throw token;
}
};
}
};
async function* f() {
var thrown;
try {
yield* asyncIter;
} catch (e) {
thrown = e;
}
return thrown;
}
var iter = f();
iter.next().then(() => {
iter.return().then(({value}) => {
assert.sameValue(value, token);
}).then($DONE, $DONE);
}).catch($DONE);

View File

@ -0,0 +1,145 @@
// Copyright (C) 2019 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-generator-function-definitions-runtime-semantics-evaluation
description: >
Return resumption value is awaited upon and hence is treated as a thenable.
info: |
14.4.14 Runtime Semantics: Evaluation
YieldExpression : yield* AssignmentExpression
...
7. Repeat,
a. If received.[[Type]] is normal, then
i. Let innerResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]],
« received.[[Value]] »).
ii. If generatorKind is async, then set innerResult to ? Await(innerResult).
...
vi. If generatorKind is async, then set received to AsyncGeneratorYield(? IteratorValue(innerResult)).
...
...
c. Else,
i. Assert: received.[[Type]] is return.
ii. Let return be ? GetMethod(iterator, "return").
iii. If return is undefined, then
1. If generatorKind is async, then set received.[[Value]] to ? Await(received.[[Value]]).
2. Return Completion(received).
...
25.5.3.7 AsyncGeneratorYield ( value )
...
5. Set value to ? Await(value).
...
8. Set the code evaluation state of genContext such that when evaluation is resumed with a
Completion resumptionValue the following steps will be performed:
...
b. Let awaited be Await(resumptionValue.[[Value]]).
...
e. Return Completion { [[Type]]: return, [[Value]]: awaited.[[Value]], [[Target]]: empty }.
...
6.2.3.1 Await
...
2. Let promise be ? PromiseResolve(%Promise%, « value »).
...
25.6.4.5.1 PromiseResolve ( C, x )
...
3. Let promiseCapability be ? NewPromiseCapability(C).
4. Perform ? Call(promiseCapability.[[Resolve]], undefined, « x »).
...
25.6.1.5 NewPromiseCapability ( C )
...
7. Let promise be ? Construct(C, « executor »).
...
25.6.3.1 Promise ( executor )
...
8. Let resolvingFunctions be CreateResolvingFunctions(promise).
...
25.6.1.3 CreateResolvingFunctions ( promise )
...
2. Let stepsResolve be the algorithm steps defined in Promise Resolve Functions (25.6.1.3.2).
3. Let resolve be CreateBuiltinFunction(stepsResolve, « [[Promise]], [[AlreadyResolved]] »).
...
25.6.1.3.2 Promise Resolve Functions
...
9. Let then be Get(resolution, "then").
...
includes: [compareArray.js]
flags: [async]
features: [async-iteration]
---*/
var expected = [
"start",
// `Await(innerResult)` promise resolved.
"tick 1",
// `Await(value)` promise resolved.
"tick 2",
// "then" of `resumptionValue.[[Value]]` accessed.
"get then",
// `Await(resumptionValue.[[Value]])` promise resolved.
"tick 3",
// Get iterator "return" method.
"get return",
// "then" of `received.[[Value]]` accessed.
"get then",
// `Await(received.[[Value]])` promise resolved.
"tick 4",
];
var actual = [];
var asyncIter = {
[Symbol.asyncIterator]() {
return this;
},
next() {
return {
done: false,
};
},
get return() {
actual.push("get return");
}
};
async function* f() {
actual.push("start");
yield* asyncIter;
actual.push("stop - never reached");
}
Promise.resolve(0)
.then(() => actual.push("tick 1"))
.then(() => actual.push("tick 2"))
.then(() => actual.push("tick 3"))
.then(() => actual.push("tick 4"))
.then(() => {
assert.compareArray(actual, expected, "Ticks for return with thenable getter");
}).then($DONE, $DONE);
var it = f();
// Start generator execution.
it.next();
// Stop generator execution.
it.return({
get then() {
actual.push("get then");
}
});

View File

@ -0,0 +1,64 @@
// Copyright (C) 2019 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-generator-function-definitions-runtime-semantics-evaluation
description: >
Abrupt completion when calling IteratorValue is propagated when received.[[Type]] is throw.
info: |
14.4.14 Runtime Semantics: Evaluation
YieldExpression : yield* AssignmentExpression
...
7. Repeat,
...
b. Else if received.[[Type]] is throw, then
...
ii. If throw is not undefined, then
...
7. If generatorKind is async, then set received to AsyncGeneratorYield(? IteratorValue(innerResult)).
...
flags: [async]
features: [async-iteration]
---*/
var token = {};
var asyncIter = {
[Symbol.asyncIterator]() {
return this;
},
next() {
return {
done: false,
value: undefined,
};
},
throw() {
return {
done: false,
get value() {
throw token;
}
};
}
};
async function* f() {
var thrown;
try {
yield* asyncIter;
} catch (e) {
thrown = e;
}
return thrown;
}
var iter = f();
iter.next().then(() => {
iter.throw().then(({value}) => {
assert.sameValue(value, token);
}).then($DONE, $DONE);
}).catch($DONE);

View File

@ -0,0 +1,54 @@
// Copyright (C) 2019 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-asyncfromsynciteratorcontinuation
description: >
Reject promise when PromiseResolve in AsyncFromSyncIteratorContinuation throws.
info: |
25.1.4.4 AsyncFromSyncIteratorContinuation ( result, promiseCapability )
...
5. Let valueWrapper be PromiseResolve(%Promise%, « value »).
6. IfAbruptRejectPromise(valueWrapper, promiseCapability).
...
includes: [compareArray.js]
flags: [async]
features: [async-iteration]
---*/
var expected = [
"start",
// `valueWrapper` promise rejected.
"tick 1",
// `Await(nextResult)` in 13.7.5.13 done.
"tick 2",
// catch handler executed.
"catch",
];
var actual = [];
async function f() {
var p = Promise.resolve(0);
Object.defineProperty(p, "constructor", {
get() {
throw new Error();
}
});
actual.push("start");
for await (var x of [p]);
actual.push("never reached");
}
Promise.resolve(0)
.then(() => actual.push("tick 1"))
.then(() => actual.push("tick 2"))
.then(() => {
assert.compareArray(actual, expected);
}).then($DONE, $DONE);
f().catch(() => actual.push("catch"));

View File

@ -0,0 +1,91 @@
// Copyright (C) 2019 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset
description: >
Ensure the number of ticks and Promise constructor lookups is correct with custom async iterator.
info: |
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind,
lhsKind, labelSet [ , iteratorKind ] )
25.6.4.5.1 PromiseResolve
6.2.3.1 Await
includes: [compareArray.js]
flags: [async]
features: [async-iteration]
---*/
// The expected event log.
var expected = [
// Before entering loop.
"pre",
// Await
// -> PromiseResolve
"constructor",
// Await promise resolved.
"tick 1",
// In loop body.
"loop",
// Await
// -> PromiseResolve
"constructor",
// Await promise resolved
"tick 2",
// After exiting loop.
"post",
];
// The actual event log.
var actual = [];
// Custom async iterator returning the result of the synchronous iterator wrapped in a Promise.
function toAsyncIterator(iterable) {
return {
[Symbol.asyncIterator]() {
var iter = iterable[Symbol.iterator]();
return {
next() {
return Promise.resolve(iter.next());
}
};
}
};
}
// Test function using for-await with a single, already resolved Promise.
async function f() {
var p = Promise.resolve(0);
actual.push("pre");
for await (var x of toAsyncIterator([p])) {
actual.push("loop");
}
actual.push("post");
}
// Count the number of ticks needed to complete the loop and compare the actual log.
Promise.resolve(0)
.then(() => actual.push("tick 1"))
.then(() => actual.push("tick 2"))
.then(() => {
assert.compareArray(actual, expected, "Ticks and constructor lookups");
}).then($DONE, $DONE);
// Redefine `Promise.constructor` in order to intercept "constructor" lookups from PromiseResolve.
// (Perform last so that the lookups from SpeciesConstructor in `then` aren't logged.)
Object.defineProperty(Promise.prototype, "constructor", {
get() {
actual.push("constructor");
return Promise;
},
configurable: true,
});
// Start the asynchronous function.
f();

View File

@ -0,0 +1,77 @@
// Copyright (C) 2019 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset
description: >
Ensure the number of ticks and Promise constructor lookups is correct with custom async iterator.
info: |
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind,
lhsKind, labelSet [ , iteratorKind ] )
6.2.3.1 Await
includes: [compareArray.js]
flags: [async]
features: [async-iteration]
---*/
// The expected event log.
var expected = [
// Before entering loop.
"pre",
// Await promise resolved.
"tick 1",
// In loop body.
"loop",
// Await promise resolved
"tick 2",
// After exiting loop.
"post",
];
// The actual event log.
var actual = [];
// Custom async iterator directly using a synchronous iterator.
function toAsyncIterator(iterable) {
return {
[Symbol.asyncIterator]() {
return iterable[Symbol.iterator]();
}
};
}
// Test function using for-await with a single, already resolved Promise.
async function f() {
var p = Promise.resolve(0);
actual.push("pre");
for await (var x of toAsyncIterator([p])) {
actual.push("loop");
}
actual.push("post");
}
// Count the number of ticks needed to complete the loop and compare the actual log.
Promise.resolve(0)
.then(() => actual.push("tick 1"))
.then(() => actual.push("tick 2"))
.then(() => {
assert.compareArray(actual, expected, "Ticks and constructor lookups");
}).then($DONE, $DONE);
// Redefine `Promise.constructor` in order to intercept "constructor" lookups from PromiseResolve.
// (Perform last so that the lookups from SpeciesConstructor in `then` aren't logged.)
Object.defineProperty(Promise.prototype, "constructor", {
get() {
actual.push("constructor");
return Promise;
},
configurable: true,
});
// Start the asynchronous function.
f();

View File

@ -0,0 +1,92 @@
// Copyright (C) 2019 André Bargull. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
esid: sec-runtime-semantics-forin-div-ofbodyevaluation-lhs-stmt-iterator-lhskind-labelset
description: >
Ensure the number of ticks and Promise constructor lookups is correct with a Async-from-Sync iterator.
info: |
13.7.5.13 Runtime Semantics: ForIn/OfBodyEvaluation ( lhs, stmt, iteratorRecord, iterationKind,
lhsKind, labelSet [ , iteratorKind ] )
25.1.4.2.1 %AsyncFromSyncIteratorPrototype%.next
25.1.4.4 AsyncFromSyncIteratorContinuation
25.6.4.5.1 PromiseResolve
6.2.3.1 Await
includes: [compareArray.js]
flags: [async]
features: [async-iteration]
---*/
// The expected event log.
var expected = [
// Before entering loop.
"pre",
// %AsyncFromSyncIteratorPrototype%.next
// -> AsyncFromSyncIteratorContinuation
// -> PromiseResolve
"constructor",
// Await
// -> PromiseResolve
"constructor",
// Async-from-Sync Iterator promise resolved.
"tick 1",
// Await promise resolved.
"tick 2",
// In loop body.
"loop",
// Await
// -> PromiseResolve
"constructor",
// Async-from-Sync Iterator promise resolved.
"tick 3",
// Await promise resolved
"tick 4",
// After exiting loop.
"post",
];
// The actual event log.
var actual = [];
// Test function using for-await with a single, already resolved Promise.
async function f() {
var p = Promise.resolve(0);
actual.push("pre");
for await (var x of [p]) {
actual.push("loop");
}
actual.push("post");
}
// Count the number of ticks needed to complete the loop and compare the actual log.
Promise.resolve(0)
.then(() => actual.push("tick 1"))
.then(() => actual.push("tick 2"))
.then(() => actual.push("tick 3"))
.then(() => actual.push("tick 4"))
.then(() => {
assert.compareArray(actual, expected, "Ticks and constructor lookups");
}).then($DONE, $DONE);
// Redefine `Promise.constructor` in order to intercept "constructor" lookups from PromiseResolve.
// (Perform last so that the lookups from SpeciesConstructor in `then` aren't logged.)
Object.defineProperty(Promise.prototype, "constructor", {
get() {
actual.push("constructor");
return Promise;
},
configurable: true,
});
// Start the asynchronous function.
f();