Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Async-from-sync test the iterator closes when .throw() is undefined #3976

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 15 additions & 15 deletions test/built-ins/AsyncFromSyncIteratorPrototype/throw/throw-null.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,12 @@ info: |
%AsyncFromSyncIteratorPrototype%.throw ( value )

[...]
5. Let throw be GetMethod(syncIterator, "throw").
6. Let throw be GetMethod(syncIterator, "throw").
[...]
7. If throw is undefined, then
a. Perform ! Call(promiseCapability.[[Reject]], undefined, « value »).
b. Return promiseCapability.[[Promise]].
8. If throw is undefined, then
...
g. Perform ! Call(promiseCapability.[[Reject]], undefined, « a newly created TypeError object »).
h. Return promiseCapability.[[Promise]].

GetMethod ( V, P )

Expand All @@ -23,6 +24,7 @@ info: |
3. If func is either undefined or null, return undefined.
flags: [async]
features: [async-iteration]
includes: [asyncHelpers.js]
---*/

var throwGets = 0;
Expand All @@ -46,14 +48,12 @@ async function* asyncGenerator() {
var asyncIterator = asyncGenerator();
var thrownError = { name: "err" };

asyncIterator.next().then(function() {
return asyncIterator.throw(thrownError);
}).then(function(result) {
throw new Test262Error("Promise should be rejected, got: " + result.value);
}, function(err) {
assert.sameValue(err, thrownError);
return asyncIterator.next().then(function(result) {
assert.sameValue(result.value, undefined);
assert.sameValue(result.done, true);
});
}).then($DONE, $DONE);
asyncTest(async function () {
await assert.throwsAsync(TypeError, async () => {
await asyncIterator.next();
return asyncIterator.throw(thrownError);
}, "Promise should be rejected");
const result = await asyncIterator.next();
assert(result.done, "the iterator is completed");
assert.sameValue(result.value, undefined, "value is undefined");
})
Original file line number Diff line number Diff line change
@@ -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");
})

Original file line number Diff line number Diff line change
@@ -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");
})
Original file line number Diff line number Diff line change
@@ -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");
})
Original file line number Diff line number Diff line change
@@ -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");
})
Loading