v8/test/inspector/debugger/async-stack-for-promise.js

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

260 lines
5.7 KiB
JavaScript
Raw Normal View History

// 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.
let {session, contextGroup, Protocol} = InspectorTest.start('Checks that async chains for promises are correct.');
contextGroup.addScript(`
function foo1() {
debugger;
}
function foo2() {
debugger;
}
function promise() {
var resolve;
var p1 = new Promise(r => resolve = r);
var p2 = p1.then(foo1);
resolve();
return p2;
}
function promiseResolvedBySetTimeout() {
var resolve;
var p1 = new Promise(r => resolve = r);
var p2 = p1.then(foo1);
setTimeout(resolve, 0);
return p2;
}
function promiseAll() {
var resolve1;
var resolve2;
var p1 = new Promise(resolve => resolve1 = resolve);
var p2 = new Promise(resolve => resolve2 = resolve);
var p3 = Promise.all([ p1, p2 ]).then(foo1);
resolve1();
resolve2();
return p3;
}
function promiseAllReverseOrder() {
var resolve1;
var resolve2;
var p1 = new Promise(resolve => resolve1 = resolve);
var p2 = new Promise(resolve => resolve2 = resolve);
var p3 = Promise.all([ p1, p2 ]).then(foo1);
resolve2();
resolve1();
return p3;
}
function promiseRace() {
var resolve1;
var resolve2;
var p1 = new Promise(resolve => resolve1 = resolve);
var p2 = new Promise(resolve => resolve2 = resolve);
var p3 = Promise.race([ p1, p2 ]).then(foo1);
resolve1();
resolve2();
return p3;
}
function twoChainedCallbacks() {
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
var p2 = p1.then(foo1).then(foo2);
resolve1();
return p2;
}
function promiseResolve() {
return Promise.resolve().then(foo1).then(foo2);
}
function thenableJobResolvedInSetTimeout() {
function thenableJob() {
var resolve2;
var p2 = new Promise(resolve => resolve2 = resolve);
setTimeout(resolve2, 0);
return p2;
}
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
var p3 = p1.then(() => thenableJob()).then(foo1);
resolve1();
return p3;
}
function thenableJobResolvedInSetTimeoutWithStack() {
function thenableJob() {
function inner() {
resolve2();
}
var resolve2;
var p2 = new Promise(resolve => resolve2 = resolve);
setTimeout(inner, 0);
return p2;
}
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
var p3 = p1.then(() => thenableJob()).then(foo1);
resolve1();
return p3;
}
function thenableJobResolvedByPromise() {
function thenableJob() {
var resolve2;
var p2 = new Promise(resolve => resolve2 = resolve);
Promise.resolve().then(resolve2);
return p2;
}
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
var p3 = p1.then(() => thenableJob()).then(foo1);
resolve1();
return p3;
}
function thenableJobResolvedByPromiseWithStack() {
function thenableJob() {
function inner() {
resolve2();
}
var resolve2;
var p2 = new Promise(resolve => resolve2 = resolve);
Promise.resolve().then(inner);
return p2;
}
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
var p3 = p1.then(() => thenableJob()).then(foo1);
resolve1();
return p3;
}
function lateThenCallback() {
var resolve1;
var p1 = new Promise(resolve => resolve1 = resolve);
resolve1();
return p1.then(foo1);
}
function complex() {
var testResolve;
var testPromise = new Promise(resolve => testResolve = resolve);
function foo1() {
function inner1() {
debugger;
}
inner1();
}
function foo2() {
var resolve20;
function inner2() {
resolve20();
}
var p20 = new Promise(resolve => resolve20 = resolve);
Promise.resolve().then(inner2);
return p20;
}
function foo3() {
var resolve17;
function inner3() {
resolve17();
}
var p17 = new Promise(resolve => resolve17 = resolve);
setTimeout(inner3, 0);
return p17;
}
function foo4() {
function inner4() {
return;
}
return inner4();
}
function foo5() {
return Promise.all([ Promise.resolve(), Promise.resolve() ])
.then(() => 42);
}
function foo6() {
return Promise.race([ Promise.resolve(), Promise.resolve()])
.then(() => 42);
}
var p = Promise.resolve()
.then(foo6)
.then(foo5)
.then(foo4)
.then(foo3)
.then(foo2)
.then(foo1);
setTimeout(() => {
p.then(() => {
p.then(() => {
debugger;
testResolve();
})
})
}, 0)
return testPromise;
}
function reject() {
return Promise.reject().catch(foo1);
}
[inspector] reworked async instrumentation for promises Old instrumentation was designed to collect promise creation stack and promise scheduled stack together. In DevTools for last 6 months we show only creation stack for promises. We got strong support from users for new model. Now we can drop support for scheduled stacks and simplify implementation. New promise instrumentation is straightforward: - we send kDebugPromiseThen when promise is created by .then call, - we send kDebugPromiseCatch when promise is created by .catch call, - we send kDebugWillHandle before chained callback and kDebugDidHandle after chained callback, - and we send separate kDebugAsyncFunctionPromiseCreated for internal promise inside async await function. Advantages: - we reduce amount of captured stacks (we do not capture stack for promise that constructed not by .then or .catch), - we can consider async task related to .then and .catch as one shot since chained callback is executed once, - on V8 side we can implement required instrumentation using only promise hooks, Disadvantage: - see await-promise test, sometimes scheduled stack was useful since we add catch handler in native code, Implementation details: - on kInit promise hook we need to figure out why promise was created. We analyze builtin functions until first user defined function on current stack. If there is kAsyncFunctionPromiseCreate function then we send kDebugAsyncFunctionPromiseCreated event. If there is kPromiseThen or kPromiseCatch then only if this function is bottom builtin function we send corresponded event to inspector. We need it because Promise.all internally calls .then and in this case we have Promise.all and Promise.then on stack at the same time and we do not need to report this internally created promise to inspector. Bug: chromium:778796 Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel Change-Id: I53f47ce8c5c4a9897655c3396c249ea59529ae47 Reviewed-on: https://chromium-review.googlesource.com/765208 Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Dmitry Gozman <dgozman@chromium.org> Cr-Commit-Position: refs/heads/master@{#49553}
2017-11-21 15:42:43 +00:00
function finally1() {
return Promise.reject().finally(foo1);
}
function finally2() {
return Promise.resolve().finally(foo1);
}
//# sourceURL=test.js`, 7, 26);
session.setupScriptMap();
Protocol.Debugger.onPaused(message => {
session.logCallFrames(message.params.callFrames);
session.logAsyncStackTrace(message.params.asyncStackTrace);
InspectorTest.log('');
Protocol.Debugger.resume();
});
Protocol.Debugger.enable();
Protocol.Debugger.setAsyncCallStackDepth({ maxDepth: 128 });
var testList = [
[inspector] reworked async instrumentation for promises Old instrumentation was designed to collect promise creation stack and promise scheduled stack together. In DevTools for last 6 months we show only creation stack for promises. We got strong support from users for new model. Now we can drop support for scheduled stacks and simplify implementation. New promise instrumentation is straightforward: - we send kDebugPromiseThen when promise is created by .then call, - we send kDebugPromiseCatch when promise is created by .catch call, - we send kDebugWillHandle before chained callback and kDebugDidHandle after chained callback, - and we send separate kDebugAsyncFunctionPromiseCreated for internal promise inside async await function. Advantages: - we reduce amount of captured stacks (we do not capture stack for promise that constructed not by .then or .catch), - we can consider async task related to .then and .catch as one shot since chained callback is executed once, - on V8 side we can implement required instrumentation using only promise hooks, Disadvantage: - see await-promise test, sometimes scheduled stack was useful since we add catch handler in native code, Implementation details: - on kInit promise hook we need to figure out why promise was created. We analyze builtin functions until first user defined function on current stack. If there is kAsyncFunctionPromiseCreate function then we send kDebugAsyncFunctionPromiseCreated event. If there is kPromiseThen or kPromiseCatch then only if this function is bottom builtin function we send corresponded event to inspector. We need it because Promise.all internally calls .then and in this case we have Promise.all and Promise.then on stack at the same time and we do not need to report this internally created promise to inspector. Bug: chromium:778796 Cq-Include-Trybots: master.tryserver.blink:linux_trusty_blink_rel Change-Id: I53f47ce8c5c4a9897655c3396c249ea59529ae47 Reviewed-on: https://chromium-review.googlesource.com/765208 Commit-Queue: Aleksey Kozyatinskiy <kozyatinskiy@chromium.org> Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org> Reviewed-by: Yang Guo <yangguo@chromium.org> Reviewed-by: Dmitry Gozman <dgozman@chromium.org> Cr-Commit-Position: refs/heads/master@{#49553}
2017-11-21 15:42:43 +00:00
'promise', 'promiseResolvedBySetTimeout', 'promiseAll',
'promiseAllReverseOrder', 'promiseRace', 'twoChainedCallbacks',
'promiseResolve', 'thenableJobResolvedInSetTimeout',
'thenableJobResolvedInSetTimeoutWithStack', 'thenableJobResolvedByPromise',
'thenableJobResolvedByPromiseWithStack', 'lateThenCallback', 'complex',
'reject', 'finally1', 'finally2'
]
InspectorTest.runTestSuite(testList.map(name => {
return eval(`
(function test${capitalize(name)}(next) {
Protocol.Runtime.evaluate({ expression: \`${name}()
//# sourceURL=test${capitalize(name)}.js\`, awaitPromise: true})
.then(next);
})
`);
}));
function capitalize(string) {
return string.charAt(0).toUpperCase() + string.slice(1);
}