cb08942d2d
Array spread syntax `[...obj]` is compiled to a special bytecode that tries to take fast-paths for values special kinds of `obj`s, including Set, Map, and Array iterator instances. But these fast-paths skip the side-effect checks of `Runtime.evaluate` and friends, and thus lead to surprises for developers. This CL alters the behavior to always call the `next()` builtins when the debugger is active to make sure we catch the side effects correctly. Fixed: chromium:1255896 Change-Id: If3fc48a119cfa791c4fde7b5c586acc22dd973e7 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3226329 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@{#77409}
93 lines
3.7 KiB
JavaScript
93 lines
3.7 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 someGlobalArray = [1, 2];
|
|
var someGlobalArrayIterator = someGlobalArray[Symbol.iterator]();
|
|
var someGlobalDate = new Date();
|
|
var someGlobalMap = new Map([[1, 2], [3, 4]]);
|
|
var someGlobalMapKeysIterator = someGlobalMap.keys();
|
|
var someGlobalMapValuesIterator = someGlobalMap.values();
|
|
var someGlobalSet = new Set([1, 2])
|
|
var someGlobalSetIterator = someGlobalSet.values();
|
|
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();
|
|
}
|
|
}
|
|
`, 0, 0, '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()');
|
|
},
|
|
|
|
async function testSpread() {
|
|
await check('[...someGlobalArray]');
|
|
await check('[...someGlobalArray.values()]');
|
|
await check('[...someGlobalArrayIterator]');
|
|
await check('[...someGlobalMap]');
|
|
await check('[...someGlobalMap.keys()]');
|
|
await check('[...someGlobalMap.values()]');
|
|
await check('[...someGlobalMapKeysIterator]');
|
|
await check('[...someGlobalMapValuesIterator]');
|
|
await check('[...someGlobalSet]');
|
|
await check('[...someGlobalSet.values()]');
|
|
await check('[...someGlobalSetIterator]');
|
|
}
|
|
]);
|