07011cc4f0
This replaces Runtime_RunMicrotasks with Runtime_PerformMicrotaskCheckpoint. RunMicrotasks forcibly runs Microtasks even when the microtasks are suppressed, and may causes nested Microtasks in a problematic way. E.g. that confuses v8::MicrotasksScope::IsRunningMicrotasks() and GetEnteredOrMicrotaskContext(). OTOH, PerformMicrotaskCheckpoint() doesn't run cause the failure as it respects the microtask suppressions. As all existing tests don't call RunMicrotasks() in the suppressed situation (like Promise.resolve().then(()=>{%RunMicrotasks();})), this change should not affect to these tests. Change-Id: Ib043a0cc8e482e022d375084d65ea98a6f54ef3d Reviewed-on: https://chromium-review.googlesource.com/c/1360095 Reviewed-by: Yang Guo <yangguo@chromium.org> Commit-Queue: Taiju Tsuiki <tzik@chromium.org> Cr-Commit-Position: refs/heads/master@{#58068}
766 lines
22 KiB
JavaScript
766 lines
22 KiB
JavaScript
// Copyright 2017 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
// Flags: --allow-natives-syntax
|
|
|
|
let testFailed = false;
|
|
let testFailure;
|
|
|
|
function assertThrowsAsync(run, errorType, message) {
|
|
var actual;
|
|
var hadValue = false;
|
|
var hadError = false;
|
|
var promise = run();
|
|
|
|
if (typeof promise !== "object" || typeof promise.then !== "function") {
|
|
throw new MjsUnitAssertionError(
|
|
"Expected " + run.toString() +
|
|
" to return a Promise, but it returned " + PrettyPrint(promise));
|
|
}
|
|
|
|
promise.then(function(value) { hadValue = true; actual = value; },
|
|
function(error) { hadError = true; actual = error; });
|
|
|
|
assertFalse(hadValue || hadError);
|
|
|
|
%PerformMicrotaskCheckpoint();
|
|
|
|
if (!hadError) {
|
|
throw new MjsUnitAssertionError(
|
|
"Expected " + run + "() to throw " + errorType.name +
|
|
", but did not throw.");
|
|
}
|
|
if (!(actual instanceof errorType))
|
|
throw new MjsUnitAssertionError(
|
|
"Expected " + run + "() to throw " + errorType.name +
|
|
", but threw '" + actual + "'");
|
|
if (message !== void 0 && actual.message !== message)
|
|
throw new MjsUnitAssertionError(
|
|
"Expected " + run + "() to throw '" + message + "', but threw '" +
|
|
actual.message + "'");
|
|
};
|
|
|
|
function resolveLater(value) {
|
|
return new Promise(function(resolve) {
|
|
Promise.resolve().then(function() {
|
|
resolve(value);
|
|
});
|
|
});
|
|
}
|
|
|
|
function rejectLater(value) {
|
|
return new Promise(function(resolve, reject) {
|
|
Promise.resolve().then(function() {
|
|
reject(value);
|
|
});
|
|
});
|
|
}
|
|
|
|
const kNext = 1;
|
|
const kThrow = 2;
|
|
const kReturn = 4;
|
|
const kNextThrows = kNext | 8;
|
|
const kReturnThrows = kReturn | 16;
|
|
const kThrowNormal = kThrow | 32;
|
|
const kNextUnchanged = kNext | 64;
|
|
const kReturnUnchanged = kReturn | 128;
|
|
const kThrowUnchanged = kThrow | 256;
|
|
function sync(array, features, log) {
|
|
// `log` is a required parameter
|
|
if (log === void 0) %AbortJS("`log` is undefined");
|
|
|
|
let i = 0;
|
|
let methods = {
|
|
next(sent) {
|
|
let done = i >= array.length;
|
|
let value = array[i];
|
|
log.push({ method: "next", sent, value, done });
|
|
if ((features & kNextThrows) === kNextThrows) throw sent;
|
|
if ((features & kNextUnchanged) === kNextUnchanged) return sent;
|
|
i++;
|
|
return { value, done };
|
|
},
|
|
throw(sent) {
|
|
let done = i >= array.length;
|
|
log.push({ method: "throw", sent, done });
|
|
if ((features & kThrowNormal) === kThrowNormal)
|
|
return { value: sent, done };
|
|
if ((features & kThrowUnchanged) === kThrowUnchanged) return sent;
|
|
throw sent;
|
|
},
|
|
return(sent) {
|
|
let done = true;
|
|
log.push({ method: "return", sent, done });
|
|
if ((features & kReturnThrows) === kReturnThrows) throw sent;
|
|
if ((features & kReturnUnchanged) === kReturnUnchanged) return sent;
|
|
return { value: sent, done };
|
|
}
|
|
};
|
|
return {
|
|
[Symbol.iterator]() { return this; },
|
|
next: (features & kNext) ? methods.next : undefined,
|
|
throw: (features & kThrow) ? methods.throw : undefined,
|
|
return: (features & kReturn) ? methods.return : undefined
|
|
};
|
|
}
|
|
|
|
class MyError extends Error {};
|
|
|
|
(async function AsyncFromSyncWithGenerator() {
|
|
function* gen() {
|
|
yield "sync value";
|
|
try {
|
|
yield new Promise(function(resolve) {
|
|
resolve("async value");
|
|
});
|
|
} catch (error) {
|
|
throw error;
|
|
}
|
|
assertUnreachable("generator is closed");
|
|
}
|
|
let iter = %CreateAsyncFromSyncIterator(gen());
|
|
|
|
// [Async-from-Sync Iterator] wraps sync iterator values in a Promise
|
|
let promise = iter.next();
|
|
assertInstanceof(promise, Promise);
|
|
let iter_result = await promise;
|
|
assertEquals({ value: "sync value", done: false }, iter_result);
|
|
|
|
// [Async-from-Sync Iterator] will wait for resolution of Promise values
|
|
promise = iter.next();
|
|
assertInstanceof(promise, Promise);
|
|
iter_result = await promise;
|
|
assertEquals({ value: "async value", done: false }, iter_result);
|
|
|
|
// [Async-from-Sync Iterator].throw delegates to .throw() method of sync
|
|
// iterator.
|
|
promise = iter.throw(new MyError("Error#1"));
|
|
assertInstanceof(promise, Promise);
|
|
try {
|
|
await promise;
|
|
assertUnreachable("promise should be rejected");
|
|
} catch (e) {
|
|
// If assertUnreachable failed, rethrow
|
|
if (e instanceof MjsUnitAssertionError) throw e;
|
|
assertInstanceof(e, MyError);
|
|
assertEquals("Error#1", e.message);
|
|
}
|
|
|
|
// Generator is closed, subsequent calls to .next() will not resume.
|
|
promise = iter.next("floof");
|
|
iter_result = await promise;
|
|
assertEquals({ value: undefined, done: true }, iter_result);
|
|
|
|
promise = iter.return("generator closed");
|
|
assertInstanceof(promise, Promise);
|
|
iter_result = await promise;
|
|
assertEquals({ value: "generator closed", done: true }, iter_result);
|
|
|
|
// .next(), .return() and .throw() delegate to sync iterator methods, without
|
|
// keeping track of the state of the generator.
|
|
promise = iter.next("unused");
|
|
assertInstanceof(promise, Promise);
|
|
iter_result = await promise;
|
|
assertEquals({ value: undefined, done: true }, iter_result);
|
|
|
|
promise = iter.throw(new MyError("Error#2"));
|
|
assertInstanceof(promise, Promise);
|
|
try {
|
|
await promise;
|
|
assertUnreachable("promise should be rejected");
|
|
} catch (e) {
|
|
// If assertUnreachable failed, rethrow
|
|
if (e instanceof MjsUnitAssertionError) throw e;
|
|
assertInstanceof(e, MyError);
|
|
assertEquals("Error#2", e.message);
|
|
}
|
|
|
|
promise = iter.return("return-after-completed");
|
|
assertInstanceof(promise, Promise);
|
|
iter_result = await promise;
|
|
assertEquals({ value: "return-after-completed", done: true }, iter_result);
|
|
})().catch(function(error) {
|
|
testFailed = true;
|
|
testFailure = error;
|
|
});
|
|
|
|
%PerformMicrotaskCheckpoint();
|
|
if (testFailed) {
|
|
throw testFailure;
|
|
}
|
|
|
|
|
|
(async function AsyncFromSyncOrderOfOperations() {
|
|
let log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(["sync-value"], 0, log));
|
|
|
|
try {
|
|
await iter.next();
|
|
assertUnreachable("Iterator.next() method is not optional");
|
|
} catch (e) {
|
|
assertInstanceof(e, TypeError);
|
|
assertEquals([], log);
|
|
}
|
|
|
|
log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(["sync-value"], kNext, log));
|
|
assertEquals({ value: "sync-value", done: false }, await iter.next("a"));
|
|
assertEquals([
|
|
{
|
|
method: "next",
|
|
sent: "a",
|
|
value: "sync-value",
|
|
done: false
|
|
}
|
|
], log);
|
|
|
|
log = [];
|
|
let asyncValue = resolveLater("async-value");
|
|
iter = %CreateAsyncFromSyncIterator(sync([asyncValue], kNext, log));
|
|
assertEquals({ value: "async-value", done: false }, await iter.next("b"));
|
|
assertEquals([
|
|
{
|
|
method: "next",
|
|
sent: "b",
|
|
value: asyncValue,
|
|
done: false
|
|
}
|
|
], log);
|
|
|
|
// If [sync_iterator].next() produces a rejected Promise or an exception is
|
|
// thrown, Promise is rejected with thrown/rejected value.
|
|
log = [];
|
|
asyncValue = rejectLater("Boo!");
|
|
iter = %CreateAsyncFromSyncIterator(sync([asyncValue], kNext, log));
|
|
try {
|
|
await iter.next('c');
|
|
assertUnreachable('Expected `iter.next(\'c\') to throw, but did not throw');
|
|
} catch (e) {
|
|
assertEquals("Boo!", e);
|
|
assertEquals([
|
|
{
|
|
method: 'next',
|
|
sent: 'c',
|
|
value: asyncValue,
|
|
done: false
|
|
}
|
|
], log);
|
|
}
|
|
|
|
log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNextThrows, log));
|
|
try {
|
|
await iter.next('Boo!');
|
|
assertUnreachable('Expected `iter.next(\'c\') to throw, but did not throw');
|
|
} catch (e) {
|
|
assertEquals("Boo!", e);
|
|
assertEquals([
|
|
{
|
|
method: 'next',
|
|
sent: 'Boo!',
|
|
value: 'sync-value',
|
|
done: false
|
|
}
|
|
], log);
|
|
}
|
|
|
|
|
|
// [Async-from-Sync Iterator].next() will be rejected with a TypeError if
|
|
// Type([sync_iterator].next()) is not Object.
|
|
log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNextUnchanged,
|
|
log));
|
|
try {
|
|
await iter.next('not-a-JSReceiver');
|
|
assertUnreachable('Expected `iter.next(\'not-a-JSReceiver\')` to ' +
|
|
'throw, but did not throw')
|
|
} catch (e) {
|
|
assertEquals(e.constructor, TypeError);
|
|
}
|
|
|
|
assertEquals([
|
|
{
|
|
method: 'next',
|
|
sent: 'not-a-JSReceiver',
|
|
value: 'sync-value',
|
|
done: false
|
|
}
|
|
], log);
|
|
|
|
// If [sync_iterator] does not have a .return() method, return a Promise
|
|
// resolved with the value `{ value: <<sent value>>, done: true }`.
|
|
log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(['sync-return'], kNext, log));
|
|
assertEquals({
|
|
value: 'd',
|
|
done: true
|
|
}, await iter.return('d'));
|
|
|
|
// [Async-from-Sync Iterator] merely delegates, and does not keep track of
|
|
// whether [sync_iterator] is completed or not.
|
|
assertEquals({
|
|
value: 'sync-return',
|
|
done: false
|
|
}, await iter.next('e'));
|
|
|
|
assertEquals([
|
|
{
|
|
method: 'next',
|
|
sent: 'e',
|
|
value: 'sync-return',
|
|
done: false
|
|
}
|
|
], log);
|
|
|
|
// If [sync_iterator] does have a .return() method, return a Promise
|
|
// fulfilled with the iterator result of [sync_iterator].return().
|
|
log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(['sync-return'],
|
|
kNext|kReturn, log));
|
|
assertEquals({
|
|
value: 'f',
|
|
done: true
|
|
}, await iter.return('f'));
|
|
|
|
// [Async-from-Sync Iterator] merely delegates, and does not keep track of
|
|
// whether [sync_iterator] is completed or not.
|
|
assertEquals({
|
|
value: 'sync-return',
|
|
done: false
|
|
}, await iter.next('g'));
|
|
|
|
assertEquals([
|
|
{
|
|
method: 'return',
|
|
sent: 'f',
|
|
done: true
|
|
},
|
|
{
|
|
method: 'next',
|
|
sent: 'g',
|
|
value: 'sync-return',
|
|
done: false
|
|
}
|
|
], log);
|
|
|
|
// If [sync_iterator].return() produces a rejected Promise or an exception is
|
|
// thrown, Promise is rejected with thrown/rejected value.
|
|
log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kReturnThrows,
|
|
log));
|
|
try {
|
|
await iter.return('Boo!!');
|
|
assertUnreachable('Expected `iter.return(\'Boo!!\')` to throw, but did ' +
|
|
'not throw');
|
|
} catch (e) {
|
|
assertEquals("Boo!!", e);
|
|
}
|
|
|
|
// [Async-from-Sync Iterator] merely delegates, and does not keep track of
|
|
// whether [sync_iterator] is completed or not.
|
|
assertEquals({ value: 'sync-value', done: false }, await iter.next('h'));
|
|
assertEquals([
|
|
{
|
|
method: 'return',
|
|
sent: 'Boo!!',
|
|
done: true
|
|
},
|
|
{
|
|
method: 'next',
|
|
sent: 'h',
|
|
value: 'sync-value',
|
|
done: false
|
|
}
|
|
], log);
|
|
|
|
|
|
log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kReturn, log));
|
|
|
|
let rejection = Promise.reject('Boo!!');
|
|
try {
|
|
await iter.return(rejection);
|
|
assertUnreachable('Expected `iter.return(Promise.reject(\'Boo!!\'))` to ' +
|
|
'throw, but did not throw');
|
|
} catch (e) {
|
|
assertEquals('Boo!!', e);
|
|
}
|
|
|
|
// [Async-from-Sync Iterator] merely delegates, and does not keep track of
|
|
// whether [sync_iterator] is completed or not.
|
|
assertEquals({ value: 'sync-value', done: false }, await iter.next('i'));
|
|
assertEquals([
|
|
{
|
|
method: 'return',
|
|
sent: rejection,
|
|
done: true
|
|
},
|
|
{
|
|
method: 'next',
|
|
sent: 'i',
|
|
value: 'sync-value',
|
|
done: false
|
|
}
|
|
], log);
|
|
|
|
// [Async-from-Sync Iterator].return() will be rejected with a TypeError if
|
|
// Type([sync_iterator].return()) is not Object.
|
|
log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(['sync-value'],
|
|
kNext|kReturnUnchanged, log));
|
|
try {
|
|
await iter.return('not-a-JSReceiver');
|
|
assertUnreachable('Expected `iter.return(\'not-a-JSReceiver\')` to ' +
|
|
'throw, but did not throw')
|
|
} catch (e) {
|
|
assertEquals(e.constructor, TypeError);
|
|
}
|
|
|
|
// [Async-from-Sync Iterator] merely delegates, and does not keep track of
|
|
// whether [sync_iterator] is completed or not.
|
|
assertEquals({ value: 'sync-value', done: false }, await iter.next('j'));
|
|
assertEquals([
|
|
{
|
|
method: 'return',
|
|
sent: 'not-a-JSReceiver',
|
|
done: true
|
|
},
|
|
{
|
|
method: 'next',
|
|
sent: 'j',
|
|
value: 'sync-value',
|
|
done: false
|
|
}
|
|
], log);
|
|
|
|
// If [sync_iterator] does not have a .throw method, return a Promise rejected
|
|
// with the sent value.
|
|
log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext, log));
|
|
try {
|
|
await iter.throw('Boo!!');
|
|
assertUnreachable('Expected iter.throw(\'Boo!!\') to throw, but did not ' +
|
|
'throw');
|
|
} catch (e) {
|
|
assertEquals('Boo!!', e);
|
|
}
|
|
|
|
// [Async-from-Sync Iterator] merely delegates, and does not keep track of
|
|
// whether [sync_iterator] is completed or not.
|
|
assertEquals({ value: 'sync-value', done: false }, await iter.next('k'));
|
|
assertEquals([
|
|
{
|
|
method: 'next',
|
|
sent: 'k',
|
|
value: 'sync-value',
|
|
done: false
|
|
}
|
|
], log);
|
|
|
|
|
|
log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kThrow, log));
|
|
try {
|
|
await iter.throw('Boo!!');
|
|
assertUnreachable('Expected iter.throw(\'Boo!!\') to throw, but did not ' +
|
|
'throw');
|
|
} catch (e) {
|
|
assertEquals('Boo!!', e);
|
|
}
|
|
|
|
// [Async-from-Sync Iterator] merely delegates, and does not keep track of
|
|
// whether [sync_iterator] is completed or not.
|
|
assertEquals({ value: 'sync-value', done: false }, await iter.next('l'));
|
|
assertEquals([
|
|
{
|
|
method: 'throw',
|
|
sent: 'Boo!!',
|
|
done: false
|
|
},
|
|
{
|
|
method: 'next',
|
|
sent: 'l',
|
|
value: 'sync-value',
|
|
done: false
|
|
}
|
|
], log);
|
|
|
|
// If [sync_iterator].throw() returns a resolved Promise or a Completion
|
|
// with [[Type]] "normal" or "return", return a resolved Promise
|
|
log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(['sync-value'], kNext|kThrowNormal,
|
|
log));
|
|
assertEquals({
|
|
value: 'Boo!!',
|
|
done: false
|
|
}, await iter.throw('Boo!!'));
|
|
|
|
// [Async-from-Sync Iterator] merely delegates, and does not keep track of
|
|
// whether [sync_iterator] is completed or not.
|
|
assertEquals({ value: 'sync-value', done: false }, await iter.next('m'));
|
|
assertEquals([
|
|
{
|
|
method: 'throw',
|
|
sent: 'Boo!!',
|
|
done: false
|
|
},
|
|
{
|
|
method: 'next',
|
|
sent: 'm',
|
|
value: 'sync-value',
|
|
done: false
|
|
}
|
|
], log);
|
|
|
|
// [Async-from-Sync Iterator].throw() will be rejected with a TypeError if
|
|
// Type([sync_iterator].throw()) is not Object.
|
|
log = [];
|
|
iter = %CreateAsyncFromSyncIterator(sync(['sync-value'],
|
|
kNext|kThrowUnchanged, log));
|
|
try {
|
|
await iter.throw('not-a-JSReceiver');
|
|
assertUnreachable('Expected `iter.throw(\'not-a-JSReceiver\')` to ' +
|
|
'throw, but did not throw')
|
|
} catch (e) {
|
|
assertEquals(e.constructor, TypeError);
|
|
}
|
|
|
|
// [Async-from-Sync Iterator] merely delegates, and does not keep track of
|
|
// whether [sync_iterator] is completed or not.
|
|
assertEquals({ value: 'sync-value', done: false }, await iter.next('n'));
|
|
assertEquals([
|
|
{
|
|
method: 'throw',
|
|
sent: 'not-a-JSReceiver',
|
|
done: false
|
|
},
|
|
{
|
|
method: 'next',
|
|
sent: 'n',
|
|
value: 'sync-value',
|
|
done: false
|
|
}
|
|
], log);
|
|
|
|
// Let nextValue be IteratorValue(nextResult).
|
|
// IfAbruptRejectPromise(nextValue, promiseCapability).)
|
|
iter = %CreateAsyncFromSyncIterator({
|
|
next() { return { get value() { throw "BadValue!" }, done: false }; }
|
|
});
|
|
try {
|
|
await iter.next();
|
|
assertUnreachable('Expected `iter.next()` to throw, but did not throw');
|
|
} catch (e) {
|
|
assertEquals('BadValue!', e);
|
|
}
|
|
|
|
// Let nextDone be IteratorComplete(nextResult).
|
|
// IfAbruptRejectPromise(nextDone, promiseCapability).
|
|
iter = %CreateAsyncFromSyncIterator({
|
|
next() { return { value: undefined, get done() { throw "BadValue!" } }; }
|
|
});
|
|
try {
|
|
await iter.next();
|
|
assertUnreachable('Expected `iter.next()` to throw, but did not throw');
|
|
} catch (e) {
|
|
assertEquals('BadValue!', e);
|
|
}
|
|
|
|
// IfAbruptRejectPromise(returnResult, promiseCapability).
|
|
// Let returnValue be IteratorValue(returnResult).
|
|
iter = %CreateAsyncFromSyncIterator({
|
|
return() { return { get value() { throw "BadValue!" }, done: false }; }
|
|
});
|
|
try {
|
|
await iter.return();
|
|
assertUnreachable('Expected `iter.return()` to throw, but did not throw');
|
|
} catch (e) {
|
|
assertEquals('BadValue!', e);
|
|
}
|
|
|
|
// IfAbruptRejectPromise(returnValue, promiseCapability).
|
|
// Let returnDone be IteratorComplete(returnResult).
|
|
iter = %CreateAsyncFromSyncIterator({
|
|
return() { return { value: undefined, get done() { throw "BadValue!" } }; }
|
|
});
|
|
try {
|
|
await iter.return();
|
|
assertUnreachable('Expected `iter.return()` to throw, but did not throw');
|
|
} catch (e) {
|
|
assertEquals('BadValue!', e);
|
|
}
|
|
|
|
// IfAbruptRejectPromise(throwResult, promiseCapability).
|
|
// Let throwValue be IteratorValue(throwResult).
|
|
iter = %CreateAsyncFromSyncIterator({
|
|
throw() { return { get value() { throw "BadValue!" }, done: false }; }
|
|
});
|
|
try {
|
|
await iter.throw();
|
|
assertUnreachable('Expected `iter.throw()` to throw, but did not throw');
|
|
} catch (e) {
|
|
assertEquals('BadValue!', e);
|
|
}
|
|
|
|
// IfAbruptRejectPromise(throwValue, promiseCapability).
|
|
// Let throwDone be IteratorComplete(throwResult).
|
|
iter = %CreateAsyncFromSyncIterator({
|
|
throw() { return { value: undefined, get done() { throw "BadValue!" } }; }
|
|
});
|
|
try {
|
|
await iter.throw();
|
|
assertUnreachable('Expected `iter.throw()` to throw, but did not throw');
|
|
} catch (e) {
|
|
assertEquals('BadValue!', e);
|
|
}
|
|
})().catch(function(error) {
|
|
testFailed = true;
|
|
testFailure = error;
|
|
});
|
|
|
|
%PerformMicrotaskCheckpoint();
|
|
if (testFailed) {
|
|
throw testFailure;
|
|
}
|
|
|
|
(function ExtractedAsyncFromSyncIteratorMethods() {
|
|
// TODO(ishell, caitp): Rewrite the test without using function.caller.
|
|
// According to ES#sec-built-in-function-objects all built-in functions
|
|
// must be strict. And ES#sec-forbidden-extensions states that the value of
|
|
// a function.caller must not be a strict function.
|
|
return;
|
|
// Async-from-Sync iterator methods can be extracted via function.caller.
|
|
// TODO(caitp): test extracted `throw` method using yield* in async generator.
|
|
let extractor = [0, 1, 2, 3, 4,5,6,7,8,9];
|
|
let extractedNext;
|
|
let extractedReturn;
|
|
|
|
extractor[Symbol.iterator] = function() {
|
|
let it = [][Symbol.iterator].call(extractor);
|
|
let origNext = it.next, origThrow = it.throw, origReturn = it.return;
|
|
function extractNext() {
|
|
extractedNext = extractNext.caller;
|
|
return origNext;
|
|
}
|
|
function extractReturn() {
|
|
extractedReturn = extractReturn.caller;
|
|
return origReturn;
|
|
}
|
|
Object.defineProperties(it, {
|
|
"next": { get: extractNext, configurable: true },
|
|
"return": { get: extractReturn, configurable: true }
|
|
});
|
|
return it;
|
|
};
|
|
|
|
async function f() {
|
|
let i;
|
|
let it = extractor[Symbol.iterator]();
|
|
for await (let x of it) break;
|
|
for await (let x of it) return "x";
|
|
}
|
|
|
|
// Cycle through `f` to extract iterator methods
|
|
f().catch(function() { %AbortJS("No error should have occurred"); });
|
|
%PerformMicrotaskCheckpoint();
|
|
|
|
assertEquals(typeof extractedNext, "function");
|
|
assertThrowsAsync(() => extractedNext.call(undefined), TypeError);
|
|
assertThrowsAsync(() => extractedNext.call(1), TypeError);
|
|
|
|
assertEquals(typeof extractedReturn, "function");
|
|
assertThrowsAsync(() => extractedReturn.call(undefined), TypeError);
|
|
assertThrowsAsync(() => extractedReturn.call(1), TypeError);
|
|
})();
|
|
|
|
(function AsyncFromSyncIteratorOrdering() {
|
|
let i = 0;
|
|
let log = [];
|
|
function r(value, done) {
|
|
let number = (++i);
|
|
return {
|
|
get value() {
|
|
log.push("get iterResult #" + number + ".value");
|
|
return {
|
|
get then() {
|
|
log.push("get nextValue#" + number + ".then");
|
|
return (r) => {
|
|
log.push("call nextValue#" + number + ".then");
|
|
r(value);
|
|
}
|
|
}
|
|
};
|
|
},
|
|
get done() {
|
|
log.push("get iterResult #" + number + ".done");
|
|
return done;
|
|
}
|
|
};
|
|
}
|
|
var results = [r("value1", false), r("value2", false), r("value3", true),
|
|
r("value4", false)];
|
|
|
|
var iter = {
|
|
get [Symbol.asyncIterator]() {
|
|
log.push("get syncIterable[@@asyncIterator]");
|
|
return null;
|
|
},
|
|
|
|
get [Symbol.iterator]() {
|
|
log.push("get syncIterable[@@iterator]");
|
|
return (...args) => {
|
|
log.push("call syncIterable[@@iterator](" + args.join(", ") + ")");
|
|
return this;
|
|
}
|
|
},
|
|
next_: 0,
|
|
get next() {
|
|
log.push("get syncIterable.next");
|
|
return (...args) => {
|
|
let i = this.next_++;
|
|
log.push("call syncIterable.next(" + args.join(", ") + ")");
|
|
return results[i];
|
|
}
|
|
}
|
|
};
|
|
|
|
async function iterate(iterable) {
|
|
log.push("before");
|
|
for await (let x of iterable) {
|
|
log.push("got value " + x);
|
|
}
|
|
log.push("after");
|
|
|
|
return log;
|
|
}
|
|
|
|
iterate(iter).then(log => {
|
|
assertEquals([
|
|
"before",
|
|
"get syncIterable[@@asyncIterator]",
|
|
"get syncIterable[@@iterator]",
|
|
"call syncIterable[@@iterator]()",
|
|
"get syncIterable.next",
|
|
"call syncIterable.next()",
|
|
"get iterResult #1.done",
|
|
"get iterResult #1.value",
|
|
"get nextValue#1.then",
|
|
"call nextValue#1.then",
|
|
"got value value1",
|
|
"call syncIterable.next()",
|
|
"get iterResult #2.done",
|
|
"get iterResult #2.value",
|
|
"get nextValue#2.then",
|
|
"call nextValue#2.then",
|
|
"got value value2",
|
|
"call syncIterable.next()",
|
|
"get iterResult #3.done",
|
|
"get iterResult #3.value",
|
|
"get nextValue#3.then",
|
|
"call nextValue#3.then",
|
|
"after"
|
|
], log)
|
|
}).catch(x => %AbortJS(String(x)));
|
|
})();
|