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}
103 lines
2.8 KiB
JavaScript
103 lines
2.8 KiB
JavaScript
// Copyright 2016 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
|
|
|
|
function assertEqualsAsync(expected, run, msg) {
|
|
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 actual;
|
|
|
|
assertTrue(
|
|
hadValue, "Expected '" + run.toString() + "' to produce a value");
|
|
|
|
assertEquals(expected, actual, msg);
|
|
};
|
|
|
|
// Rename a function so that it can help omit things from stack trace.
|
|
function test(fn) {
|
|
return Object.defineProperty(fn, "name", {
|
|
enumerable: false,
|
|
configurable: true,
|
|
value: "@" + fn.name,
|
|
writable: false
|
|
});
|
|
}
|
|
|
|
function getStack(error) {
|
|
var stack = error.stack.split('\n').
|
|
filter(function(line) {
|
|
return /^\s*at @?[a-zA-Z0-9_]/.test(line);
|
|
}).
|
|
map(line =>
|
|
line.replace(/^\s*at (@?(?:new )?[a-zA-Z0-9_\.\[\]]+)(.*)/, "$1"));
|
|
|
|
// remove `Promise.then()` invocation by assertEqualsAsync()
|
|
if (stack[2] === "assertEqualsAsync") return [];
|
|
|
|
return stack.reverse();
|
|
}
|
|
|
|
var log = [];
|
|
class FakePromise extends Promise {
|
|
constructor(executor) {
|
|
var stack = getStack(new Error("Getting Callstack"));
|
|
if (stack.length) {
|
|
var first = -1;
|
|
for (var i = 0; i < stack.length; ++i) {
|
|
if (stack[i][0] === '@') {
|
|
first = i;
|
|
break;
|
|
}
|
|
}
|
|
while (first > 0) stack.shift(), --first;
|
|
if (stack.length) {
|
|
log.push("@@Species: [" + stack.join(" > ") + "]");
|
|
}
|
|
}
|
|
return new Promise(executor);
|
|
}
|
|
};
|
|
|
|
Object.defineProperty(Promise, Symbol.species, {
|
|
value: FakePromise,
|
|
configurable: true,
|
|
enumerable: false,
|
|
writable: false
|
|
});
|
|
|
|
// Internal `AsyncFunctionAwait` only --- no @@species invocations.
|
|
async function asyncFn() { return await "foo"; }
|
|
assertEqualsAsync("foo", test(function testInternalOnly() { return asyncFn(); },
|
|
"should not call Promise[@@Species]"));
|
|
assertEquals([], log);
|
|
|
|
log.length = 0;
|
|
assertEqualsAsync(
|
|
"foo",
|
|
test(function testThenOnReturnedPromise() {
|
|
return asyncFn().then(x => (log.push("Then: " + x), x));
|
|
}),
|
|
"should call Promise[@@Species] after non-internal Then");
|
|
assertEquals([
|
|
"@@Species: [@testThenOnReturnedPromise > Promise.then > new FakePromise]",
|
|
"Then: foo"
|
|
], log);
|