mirror of https://github.com/tc39/test262.git
Async-from-sync test the iterator closes when .throw() is undefined (#3976)
* Adjust existing tests to match new spec for async-from-sync iterators Testing normative changes of ecma626 PR 2600 * Test that async-from-sync iterator closes when throw is undefined. These test paths from newly added call to IteratorClose in step 7.c of %AsyncFromSyncIteratorPrototype%.throw as per normative changes of ecma626 PR 2600 * Apply suggestions from code review Co-authored-by: Jordan Harband <ljharb@gmail.com> * Refactoring tests to use the Async Helpers. Addresses review comments - https://github.com/tc39/test262/pull/3976/files#r1451071569 - https://github.com/tc39/test262/pull/3976/files#r1451075479 --------- Co-authored-by: Jordan Harband <ljharb@gmail.com>
This commit is contained in:
parent
97a2e443e1
commit
2c58671789
|
@ -10,11 +10,12 @@ info: |
|
||||||
%AsyncFromSyncIteratorPrototype%.throw ( value )
|
%AsyncFromSyncIteratorPrototype%.throw ( value )
|
||||||
|
|
||||||
[...]
|
[...]
|
||||||
5. Let throw be GetMethod(syncIterator, "throw").
|
6. Let throw be GetMethod(syncIterator, "throw").
|
||||||
[...]
|
[...]
|
||||||
7. If throw is undefined, then
|
8. If throw is undefined, then
|
||||||
a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »).
|
...
|
||||||
b. Return promiseCapability.[[Promise]].
|
g. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||||
|
h. Return promiseCapability.[[Promise]].
|
||||||
|
|
||||||
GetMethod ( V, P )
|
GetMethod ( V, P )
|
||||||
|
|
||||||
|
@ -23,6 +24,7 @@ info: |
|
||||||
3. If func is either undefined or null, return undefined.
|
3. If func is either undefined or null, return undefined.
|
||||||
flags: [async]
|
flags: [async]
|
||||||
features: [async-iteration]
|
features: [async-iteration]
|
||||||
|
includes: [asyncHelpers.js]
|
||||||
---*/
|
---*/
|
||||||
|
|
||||||
var throwGets = 0;
|
var throwGets = 0;
|
||||||
|
@ -46,14 +48,12 @@ async function* asyncGenerator() {
|
||||||
var asyncIterator = asyncGenerator();
|
var asyncIterator = asyncGenerator();
|
||||||
var thrownError = { name: "err" };
|
var thrownError = { name: "err" };
|
||||||
|
|
||||||
asyncIterator.next().then(function() {
|
asyncTest(async function () {
|
||||||
return asyncIterator.throw(thrownError);
|
await assert.throwsAsync(TypeError, async () => {
|
||||||
}).then(function(result) {
|
await asyncIterator.next();
|
||||||
throw new Test262Error("Promise should be rejected, got: " + result.value);
|
return asyncIterator.throw(thrownError);
|
||||||
}, function(err) {
|
}, "Promise should be rejected");
|
||||||
assert.sameValue(err, thrownError);
|
const result = await asyncIterator.next();
|
||||||
return asyncIterator.next().then(function(result) {
|
assert(result.done, "the iterator is completed");
|
||||||
assert.sameValue(result.value, undefined);
|
assert.sameValue(result.value, undefined, "value is undefined");
|
||||||
assert.sameValue(result.done, true);
|
})
|
||||||
});
|
|
||||||
}).then($DONE, $DONE);
|
|
||||||
|
|
|
@ -0,0 +1,73 @@
|
||||||
|
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-%asyncfromsynciteratorprototype%.throw
|
||||||
|
description: >
|
||||||
|
If syncIterator's "throw" method is `undefined`,
|
||||||
|
and its "return" method returns `undefined`,
|
||||||
|
the iterator will close returning the `undefined` value,
|
||||||
|
which will be ignored and instead a rejected Promise with a new TypeError is returned.
|
||||||
|
info: |
|
||||||
|
%AsyncFromSyncIteratorPrototype%.throw ( value )
|
||||||
|
...
|
||||||
|
2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
||||||
|
...
|
||||||
|
5. Let return be GetMethod(syncIterator, "throw").
|
||||||
|
6. IfAbruptRejectPromise(throw, promiseCapability).
|
||||||
|
7. If throw is undefined, then
|
||||||
|
a. NOTE: If syncIterator does not have a throw method, close it to give it a chance to clean up before we reject the capability.
|
||||||
|
b. Let closeCompletion be Completion { [[Type]]: normal, [[Value]]: empty, [[Target]]: empty }.
|
||||||
|
c. Set result to IteratorClose(syncIteratorRecord, closeCompletion).
|
||||||
|
...
|
||||||
|
g. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||||
|
h. Return promiseCapability.[[Promise]].
|
||||||
|
...
|
||||||
|
|
||||||
|
IteratorClose ( iterator, completion )
|
||||||
|
...
|
||||||
|
2. Let iterator be iteratorRecord.[[Iterator]].
|
||||||
|
3. Let innerResult be Completion(GetMethod(iterator, "return")).
|
||||||
|
4. If innerResult.[[Type]] is normal, then
|
||||||
|
a. Let return be innerResult.[[Value]].
|
||||||
|
b. If return is undefined, return ? completion.
|
||||||
|
...
|
||||||
|
|
||||||
|
flags: [async]
|
||||||
|
features: [async-iteration]
|
||||||
|
includes: [asyncHelpers.js]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
var returnCount = 0;
|
||||||
|
|
||||||
|
const obj = {
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return {
|
||||||
|
next() {
|
||||||
|
return {value: 1, done: false};
|
||||||
|
},
|
||||||
|
get return() {
|
||||||
|
returnCount += 1;
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function* wrapper() {
|
||||||
|
yield* obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
var iter = wrapper();
|
||||||
|
|
||||||
|
asyncTest(async function () {
|
||||||
|
await assert.throwsAsync(TypeError, async () => {
|
||||||
|
await iter.next();
|
||||||
|
return iter.throw();
|
||||||
|
}, "Promise should be rejected");
|
||||||
|
assert.sameValue(returnCount, 1, 'iterator closed properly');
|
||||||
|
const result = await iter.next();
|
||||||
|
assert(result.done, "the iterator is completed");
|
||||||
|
assert.sameValue(result.value, undefined, "value is undefined");
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,77 @@
|
||||||
|
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-%asyncfromsynciteratorprototype%.throw
|
||||||
|
description: throw() will close the iterator and return rejected promise if sync `throw` undefined
|
||||||
|
info: |
|
||||||
|
%AsyncFromSyncIteratorPrototype%.throw ( value )
|
||||||
|
...
|
||||||
|
2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
||||||
|
...
|
||||||
|
5. Let return be GetMethod(syncIterator, "throw").
|
||||||
|
6. IfAbruptRejectPromise(throw, promiseCapability).
|
||||||
|
7. If throw is undefined, then
|
||||||
|
a. NOTE: If syncIterator does not have a throw method, close it to give it a chance to clean up before we reject the capability.
|
||||||
|
b. Let closeCompletion be Completion { [[Type]]: normal, [[Value]]: empty, [[Target]]: empty }.
|
||||||
|
c. Set result to IteratorClose(syncIteratorRecord, closeCompletion).
|
||||||
|
d. IfAbruptRejectPromise(result, promiseCapability).
|
||||||
|
e. NOTE: The next step throws a TypeError to indicate that there was a protocol violation: syncIterator does not have a throw method.
|
||||||
|
f. NOTE: If closing syncIterator does not throw then the result of that operation is ignored, even if it yields a rejected promise.
|
||||||
|
g. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||||
|
h. Return promiseCapability.[[Promise]].
|
||||||
|
|
||||||
|
IteratorClose ( iterator, completion )
|
||||||
|
...
|
||||||
|
2. Let iterator be iteratorRecord.[[Iterator]].
|
||||||
|
3. Let innerResult be Completion(GetMethod(iterator, "return")).
|
||||||
|
...
|
||||||
|
6. If innerResult.[[Type]] is throw, return ? innerResult.
|
||||||
|
...
|
||||||
|
|
||||||
|
IfAbruptRejectPromise ( value, capability )
|
||||||
|
1. Assert: value is a Completion Record.
|
||||||
|
2. If value is an abrupt completion, then
|
||||||
|
a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
|
||||||
|
b. Return capability.[[Promise]].
|
||||||
|
...
|
||||||
|
|
||||||
|
flags: [async]
|
||||||
|
features: [async-iteration]
|
||||||
|
includes: [asyncHelpers.js]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
var returnCount = 0;
|
||||||
|
function CatchError() {}
|
||||||
|
var thrownError = new CatchError();
|
||||||
|
|
||||||
|
const obj = {
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return {
|
||||||
|
next() {
|
||||||
|
return {value: 1, done: false};
|
||||||
|
},
|
||||||
|
get return() {
|
||||||
|
returnCount += 1;
|
||||||
|
throw thrownError;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function* wrapper() {
|
||||||
|
yield* obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
var iter = wrapper();
|
||||||
|
|
||||||
|
asyncTest(async function () {
|
||||||
|
await assert.throwsAsync(CatchError, async () => {
|
||||||
|
await iter.next();
|
||||||
|
return iter.throw();
|
||||||
|
}, "Promise should be rejected");
|
||||||
|
assert.sameValue(returnCount, 1, 'iterator closed properly');
|
||||||
|
const result = await iter.next();
|
||||||
|
assert(result.done, "the iterator is completed");
|
||||||
|
assert.sameValue(result.value, undefined, "value is undefined");
|
||||||
|
})
|
|
@ -0,0 +1,80 @@
|
||||||
|
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-%asyncfromsynciteratorprototype%.throw
|
||||||
|
description: >
|
||||||
|
If syncIterator's "throw" method is `undefined`,
|
||||||
|
and its "return" method returns `undefined`,
|
||||||
|
the iterator will close returning the `undefined` value,
|
||||||
|
which will be ignored and instead a rejected Promise with a new TypeError is returned.
|
||||||
|
info: |
|
||||||
|
%AsyncFromSyncIteratorPrototype%.throw ( value )
|
||||||
|
...
|
||||||
|
2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
||||||
|
...
|
||||||
|
5. Let return be GetMethod(syncIterator, "throw").
|
||||||
|
6. IfAbruptRejectPromise(throw, promiseCapability).
|
||||||
|
7. If throw is undefined, then
|
||||||
|
a. NOTE: If syncIterator does not have a throw method, close it to give it a chance to clean up before we reject the capability.
|
||||||
|
b. Let closeCompletion be Completion { [[Type]]: normal, [[Value]]: empty, [[Target]]: empty }.
|
||||||
|
c. Set result to IteratorClose(syncIteratorRecord, closeCompletion).
|
||||||
|
d. IfAbruptRejectPromise(result, promiseCapability).
|
||||||
|
...
|
||||||
|
|
||||||
|
IteratorClose ( iterator, completion )
|
||||||
|
...
|
||||||
|
2. Let iterator be iteratorRecord.[[Iterator]].
|
||||||
|
3. Let innerResult be Completion(GetMethod(iterator, "return")).
|
||||||
|
4. If innerResult.[[Type]] is normal, then
|
||||||
|
a. Let return be innerResult.[[Value]].
|
||||||
|
...
|
||||||
|
c. Set innerResult to Completion(Call(return, iterator)).
|
||||||
|
...
|
||||||
|
7. If innerResult.[[Value]] is not an Object, throw a TypeError exception.
|
||||||
|
...
|
||||||
|
|
||||||
|
IfAbruptRejectPromise ( value, capability )
|
||||||
|
1. Assert: value is a Completion Record.
|
||||||
|
2. If value is an abrupt completion, then
|
||||||
|
a. Perform ? Call(capability.[[Reject]], undefined, « value.[[Value]] »).
|
||||||
|
b. Return capability.[[Promise]].
|
||||||
|
...
|
||||||
|
|
||||||
|
flags: [async]
|
||||||
|
features: [async-iteration]
|
||||||
|
includes: [asyncHelpers.js]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
var returnCount = 0;
|
||||||
|
|
||||||
|
const obj = {
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return {
|
||||||
|
next() {
|
||||||
|
return {value: 1, done: false};
|
||||||
|
},
|
||||||
|
return() {
|
||||||
|
returnCount += 1;
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function* wrapper() {
|
||||||
|
yield* obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
var iter = wrapper();
|
||||||
|
|
||||||
|
asyncTest(async function () {
|
||||||
|
await assert.throwsAsync(TypeError, async () => {
|
||||||
|
await iter.next();
|
||||||
|
return iter.throw();
|
||||||
|
}, "Promise should be rejected");
|
||||||
|
assert.sameValue(returnCount, 1, 'iterator closed properly');
|
||||||
|
const result = await iter.next();
|
||||||
|
assert(result.done, "the iterator is completed");
|
||||||
|
assert.sameValue(result.value, undefined, "value is undefined");
|
||||||
|
})
|
|
@ -0,0 +1,74 @@
|
||||||
|
// Copyright (C) 2023 Igalia, S.L. All rights reserved.
|
||||||
|
// This code is governed by the BSD license found in the LICENSE file.
|
||||||
|
|
||||||
|
/*---
|
||||||
|
esid: sec-%asyncfromsynciteratorprototype%.throw
|
||||||
|
description: >
|
||||||
|
If syncIterator's "throw" method is `undefined`,
|
||||||
|
and its "return" method returns `undefined`,
|
||||||
|
the iterator will close returning the `undefined` value,
|
||||||
|
which will be ignored and instead a rejected Promise with a new TypeError is returned.
|
||||||
|
info: |
|
||||||
|
%AsyncFromSyncIteratorPrototype%.throw ( value )
|
||||||
|
...
|
||||||
|
2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
||||||
|
...
|
||||||
|
5. Let return be GetMethod(syncIterator, "throw").
|
||||||
|
...
|
||||||
|
7. If throw is undefined, then
|
||||||
|
a. NOTE: If syncIterator does not have a throw method, close it to give it a chance to clean up before we reject the capability.
|
||||||
|
b. Let closeCompletion be Completion { [[Type]]: normal, [[Value]]: empty, [[Target]]: empty }.
|
||||||
|
c. Set result to IteratorClose(syncIteratorRecord, closeCompletion).
|
||||||
|
...
|
||||||
|
g. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||||
|
h. Return promiseCapability.[[Promise]].
|
||||||
|
...
|
||||||
|
|
||||||
|
IteratorClose ( iterator, completion )
|
||||||
|
...
|
||||||
|
2. Let iterator be iteratorRecord.[[Iterator]].
|
||||||
|
3. Let innerResult be Completion(GetMethod(iterator, "return")).
|
||||||
|
4. If innerResult.[[Type]] is normal, then
|
||||||
|
a. Let return be innerResult.[[Value]].
|
||||||
|
...
|
||||||
|
c. Set innerResult to Completion(Call(return, iterator)).
|
||||||
|
...
|
||||||
|
8. Return ? completion.
|
||||||
|
|
||||||
|
flags: [async]
|
||||||
|
features: [async-iteration]
|
||||||
|
includes: [asyncHelpers.js]
|
||||||
|
---*/
|
||||||
|
|
||||||
|
var returnCount = 0;
|
||||||
|
|
||||||
|
const obj = {
|
||||||
|
[Symbol.iterator]() {
|
||||||
|
return {
|
||||||
|
next() {
|
||||||
|
return {value: 1, done: false};
|
||||||
|
},
|
||||||
|
return() {
|
||||||
|
returnCount += 1;
|
||||||
|
return {value: 2, done: true};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function* wrapper() {
|
||||||
|
yield* obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
var iter = wrapper();
|
||||||
|
|
||||||
|
asyncTest(async function () {
|
||||||
|
await assert.throwsAsync(TypeError, async () => {
|
||||||
|
await iter.next();
|
||||||
|
return iter.throw();
|
||||||
|
}, "Promise should be rejected");
|
||||||
|
assert.sameValue(returnCount, 1, 'iterator closed properly');
|
||||||
|
const result = await iter.next();
|
||||||
|
assert(result.done, "the iterator is completed");
|
||||||
|
assert.sameValue(result.value, undefined, "value is undefined");
|
||||||
|
})
|
|
@ -9,14 +9,16 @@ info: |
|
||||||
...
|
...
|
||||||
2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
2. Let promiseCapability be ! NewPromiseCapability(%Promise%).
|
||||||
...
|
...
|
||||||
5. Let return be GetMethod(syncIterator, "throw").
|
6. Let throw be GetMethod(syncIterator, "throw").
|
||||||
6. IfAbruptRejectPromise(throw, promiseCapability).
|
[...]
|
||||||
7. If throw is undefined, then
|
8. If throw is undefined, then
|
||||||
a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »).
|
...
|
||||||
b. Return promiseCapability.[[Promise]].
|
g. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
|
||||||
|
h. Return promiseCapability.[[Promise]].
|
||||||
|
|
||||||
flags: [async]
|
flags: [async]
|
||||||
features: [async-iteration]
|
features: [async-iteration]
|
||||||
|
includes: [asyncHelpers.js]
|
||||||
---*/
|
---*/
|
||||||
|
|
||||||
var obj = {
|
var obj = {
|
||||||
|
@ -35,19 +37,12 @@ async function* asyncg() {
|
||||||
|
|
||||||
var iter = asyncg();
|
var iter = asyncg();
|
||||||
|
|
||||||
iter.next().then(function(result) {
|
asyncTest(async function () {
|
||||||
iter.throw().then(
|
await assert.throwsAsync(TypeError, async () => {
|
||||||
function (result) {
|
await iter.next();
|
||||||
throw new Test262Error("Promise should be rejected, got: " + result.value);
|
return iter.throw();
|
||||||
},
|
}, "Promise should be rejected");
|
||||||
function (err) {
|
const result = await iter.next();
|
||||||
assert.sameValue(err, undefined, "Promise should be rejected with undefined");
|
assert(result.done, "the iterator is completed");
|
||||||
|
assert.sameValue(result.value, undefined, "value is undefined");
|
||||||
iter.next().then(({ done, value }) => {
|
})
|
||||||
assert.sameValue(done, true, 'the iterator is completed');
|
|
||||||
assert.sameValue(value, undefined, 'value is undefined');
|
|
||||||
}).then($DONE, $DONE);
|
|
||||||
}
|
|
||||||
).catch($DONE);
|
|
||||||
|
|
||||||
}).catch($DONE);
|
|
||||||
|
|
Loading…
Reference in New Issue