v8/test/inspector/debugger/side-effect-free-debug-evaluate.js
Benedikt Meurer 0195a5eb49 [inspector] Consistently treat promise rejections as side-effecting.
Previously we'd treat %_AsyncFunctionReject (and %AsyncFunctionReject)
as side-effect free (in async functions), but that's not correct, since
promise rejections have side-effects (at the very least triggering the
unhandled promise rejection machinery in the browser).

This required a minor refactoring as previously we'd classify functions
as side-effecting or not depending on whether they contain any calls to
side-effecting intrinsics, no matter whether this call is actually
executed or not. That would break REPL mode however if we'd generally
treat all async functions with %_AsyncFunctionReject intrinsic calls as
side-effecting, so instead of performing the intrinsic checks ahead of
time, we now perform the test at execution time.

Before: https://imgur.com/5BvJP9d.png
After: https://imgur.com/10FanNr.png
Fixed: chromium:1249275
Change-Id: Ib06f945ba21f1e06ee9b13a1363fad342464fd9a
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3197712
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Simon Zünd <szuend@chromium.org>
Auto-Submit: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Simon Zünd <szuend@chromium.org>
Cr-Commit-Position: refs/heads/main@{#77183}
2021-10-01 07:10:34 +00:00

72 lines
2.8 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.
let {session, contextGroup, Protocol} = InspectorTest.start('Tests side-effect-free evaluation');
contextGroup.addScript(`
var someGlobalDate = new Date();
function testFunction()
{
var o = 0;
function f() { return 1; }
function g() { o = 2; return o; }
f,g;
debugger;
}
async function testAsyncFunction(action) {
switch (action) {
case "resolve": return 1;
case "reject": throw new Error();
}
}
//# sourceURL=foo.js`);
const check = async (expression) => {
const {result:{exceptionDetails}} = await Protocol.Runtime.evaluate({expression, throwOnSideEffect: true});
InspectorTest.log(expression + ' : ' + (exceptionDetails ? 'throws' : 'ok'));
};
InspectorTest.runAsyncTestSuite([
async function basicTest() {
Protocol.Debugger.enable();
Protocol.Runtime.evaluate({ 'expression': 'setTimeout(testFunction, 0)' });
const {params:{callFrames:[{callFrameId: topFrameId}]}} = await Protocol.Debugger.oncePaused();
InspectorTest.log('Paused on "debugger;"');
const {result:{result:{value: fResult}}} = await Protocol.Debugger.evaluateOnCallFrame({ callFrameId: topFrameId, expression: 'f()' });
InspectorTest.log('f() returns ' + fResult);
const {result:{result:{value: gResult}}} = await Protocol.Debugger.evaluateOnCallFrame({ callFrameId: topFrameId, expression: 'g()' });
InspectorTest.log('g() returns ' + gResult);
const {result:{result:{value: fResultSideEffect}}} = await Protocol.Debugger.evaluateOnCallFrame({ callFrameId: topFrameId, expression: 'f()', throwOnSideEffect: true});
InspectorTest.log('f() returns ' + fResultSideEffect);
const {result:{result:{className}}} = await Protocol.Debugger.evaluateOnCallFrame({ callFrameId: topFrameId, expression: 'g()', throwOnSideEffect: true});
InspectorTest.log('g() throws ' + className);
},
async function testAsyncFunctions() {
await check('testAsyncFunction("resolve")');
await check('testAsyncFunction("reject")');
},
async function testDate() {
// setters are only ok on temporary objects
await check('someGlobalDate.setDate(10)');
await check('new Date().setDate(10)');
await check('someGlobalDate.setFullYear(1991)');
await check('new Date().setFullYear(1991)');
await check('someGlobalDate.setHours(0)');
await check('new Date().setHours(0)');
// getters are ok on any Date
await check('someGlobalDate.getDate()');
await check('new Date().getDate()');
await check('someGlobalDate.getFullYear()');
await check('new Date().getFullYear()');
await check('someGlobalDate.getHours()');
await check('new Date().getHours()');
},
async function testPromiseReject() {
await check('Promise.reject()');
}
]);