[inspector] Add disabled tests for 'Restart frame' 2/2
Doc: https://bit.ly/revive-restart-frame Context: https://crrev.com/c/3582395 (whole feature) This CL adds the second batch of inspector tests for the upcoming "Restart frame" feature. Landing the tests upfront allows us to better discuss the proposed API as well as think early about corner cases we should test. The tests check for the functionality of `Debugger.restartFrame`, as well as the newly added parameter `canBeRestarted` in the `Debugger.paused` event. Bug: chromium:1303521 Change-Id: Ie9dda100cdc5217a4e4cc2f0cf7019a33d124120 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3585947 Reviewed-by: Benedikt Meurer <bmeurer@chromium.org> Reviewed-by: Kim-Anh Tran <kimanh@chromium.org> Commit-Queue: Simon Zünd <szuend@chromium.org> Cr-Commit-Position: refs/heads/main@{#80037}
This commit is contained in:
parent
80f9d34685
commit
f6cb798753
@ -0,0 +1,17 @@
|
||||
Checks that requesting to restart a non-existant frame fails cleanly
|
||||
Paused at (after evaluation):
|
||||
|
||||
(function foo() { #debugger; })();
|
||||
|
||||
|
||||
Pause stack:
|
||||
foo:1 (canBeRestarted = true)
|
||||
|
||||
Attempting to restart frame with non-existent index 2
|
||||
{
|
||||
error : {
|
||||
code : -32000
|
||||
message : Restarting frame failed
|
||||
}
|
||||
id : <messageId>
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
utils.load('test/inspector/debugger/restart-frame/restart-frame-test.js');
|
||||
|
||||
const {session, Protocol} =
|
||||
InspectorTest.start('Checks that requesting to restart a non-existant frame fails cleanly');
|
||||
|
||||
session.setupScriptMap();
|
||||
|
||||
(async () => {
|
||||
await Protocol.Debugger.enable();
|
||||
|
||||
await RestartFrameTest.evaluateAndWaitForPause(`
|
||||
(function foo() { debugger; })();
|
||||
`);
|
||||
|
||||
InspectorTest.log('Attempting to restart frame with non-existent index 2');
|
||||
InspectorTest.logMessage(
|
||||
await Protocol.Debugger.restartFrame({ callFrameId: '1.1.2', mode: 'StepInto' }));
|
||||
InspectorTest.completeTest();
|
||||
})();
|
@ -0,0 +1,51 @@
|
||||
Checks that restart frame fails for generator or async functions.
|
||||
|
||||
Check that an async function cannot be restarted.
|
||||
Paused at (after evaluation):
|
||||
(async function asyncFn() {
|
||||
#debugger;
|
||||
})();
|
||||
|
||||
Pause stack:
|
||||
asyncFn:2 (canBeRestarted = false)
|
||||
|
||||
Restarting function "asyncFn" ...
|
||||
Failed to restart function "asyncFn":
|
||||
{
|
||||
code : -32000
|
||||
message : Restarting frame failed
|
||||
}
|
||||
|
||||
Check that a generator function cannot be restarted.
|
||||
Paused at (after evaluation):
|
||||
yield 10;
|
||||
#debugger;
|
||||
yield 20;
|
||||
|
||||
Pause stack:
|
||||
generatorFn:3 (canBeRestarted = false)
|
||||
|
||||
Restarting function "generatorFn" ...
|
||||
Failed to restart function "generatorFn":
|
||||
{
|
||||
code : -32000
|
||||
message : Restarting frame failed
|
||||
}
|
||||
|
||||
Check that a function cannot be restarted when a generator function is on the stack above
|
||||
Paused at (after evaluation):
|
||||
|
||||
function breaker() { #debugger; }
|
||||
function* genFn() {
|
||||
|
||||
Pause stack:
|
||||
breaker:1 (canBeRestarted = true)
|
||||
genFn:4 (canBeRestarted = false)
|
||||
bar:10 (canBeRestarted = false)
|
||||
|
||||
Restarting function "bar" ...
|
||||
Failed to restart function "bar":
|
||||
{
|
||||
code : -32000
|
||||
message : Restarting frame failed
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
utils.load('test/inspector/debugger/restart-frame/restart-frame-test.js');
|
||||
|
||||
const {session, contextGroup, Protocol} =
|
||||
InspectorTest.start('Checks that restart frame fails for generator or async functions.');
|
||||
|
||||
session.setupScriptMap();
|
||||
|
||||
async function testCase(description, snippet, restartFrameIndex) {
|
||||
InspectorTest.log('');
|
||||
InspectorTest.log(description);
|
||||
|
||||
const { callFrames, evaluatePromise } = await RestartFrameTest.evaluateAndWaitForPause(snippet);
|
||||
// These are negative tests where the following call is expected to fail.
|
||||
await RestartFrameTest.restartFrameAndWaitForPause(callFrames, restartFrameIndex);
|
||||
|
||||
// All snippets are written so a single resume is enough.
|
||||
Protocol.Debugger.resume();
|
||||
await evaluatePromise;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await Protocol.Debugger.enable();
|
||||
|
||||
await testCase('Check that an async function cannot be restarted.', `
|
||||
(async function asyncFn() {
|
||||
debugger;
|
||||
})();
|
||||
`, 0);
|
||||
|
||||
await testCase('Check that a generator function cannot be restarted.', `
|
||||
function* generatorFn() {
|
||||
yield 10;
|
||||
debugger;
|
||||
yield 20;
|
||||
}
|
||||
const gen1 = generatorFn();
|
||||
gen1.next();
|
||||
gen1.next();
|
||||
`, 0);
|
||||
|
||||
await testCase('Check that a function cannot be restarted when a generator function is on the stack above', `
|
||||
function breaker() { debugger; }
|
||||
function* genFn() {
|
||||
yield 10;
|
||||
breaker();
|
||||
yield 20;
|
||||
}
|
||||
const gen2 = genFn();
|
||||
function bar() { // We want to restart bar.
|
||||
gen2.next();
|
||||
gen2.next();
|
||||
}
|
||||
bar();
|
||||
`, 2);
|
||||
|
||||
InspectorTest.completeTest();
|
||||
})();
|
@ -0,0 +1,16 @@
|
||||
Checks that restart frame fails when embedder frames would be unwound
|
||||
Paused at (after evaluation):
|
||||
function breaker() {
|
||||
#debugger;
|
||||
}
|
||||
|
||||
Pause stack:
|
||||
breaker:2 (canBeRestarted = true)
|
||||
entrypoint:5 (canBeRestarted = false)
|
||||
|
||||
Restarting function "entrypoint" ...
|
||||
Failed to restart function "entrypoint":
|
||||
{
|
||||
code : -32000
|
||||
message : Restarting frame failed
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
utils.load('test/inspector/debugger/restart-frame/restart-frame-test.js');
|
||||
|
||||
const {session, contextGroup, Protocol} =
|
||||
InspectorTest.start('Checks that restart frame fails when embedder frames would be unwound');
|
||||
|
||||
session.setupScriptMap();
|
||||
|
||||
contextGroup.addScript(`
|
||||
function breaker() {
|
||||
debugger;
|
||||
}
|
||||
function entrypoint() {
|
||||
inspector.callbackForTests(breaker);
|
||||
}
|
||||
`, 0, 0, 'test.js');
|
||||
|
||||
(async () => {
|
||||
await Protocol.Debugger.enable();
|
||||
|
||||
const { callFrames } = await RestartFrameTest.evaluateAndWaitForPause('entrypoint()');
|
||||
|
||||
// Restart the `entrypoint` frame. Inbetween is the C++ API method `callbackForTests`.
|
||||
const restartFrameIndex = 1; // 0 is `breaker`, 1 is `entrypoint`.
|
||||
await RestartFrameTest.restartFrameAndWaitForPause(callFrames, restartFrameIndex);
|
||||
|
||||
InspectorTest.completeTest();
|
||||
})();
|
@ -0,0 +1,20 @@
|
||||
Checks that restarting an inlined frame works.
|
||||
Optimization status for function "h"? optimized
|
||||
Paused at (after evaluation):
|
||||
console.log('f');
|
||||
return a + b;# // Breakpoint is set here.
|
||||
}
|
||||
|
||||
Pause stack:
|
||||
f:3 (canBeRestarted = true)
|
||||
g:8 (canBeRestarted = true)
|
||||
h:13 (canBeRestarted = true)
|
||||
|
||||
Optimization status for function "h" after we paused? optimized
|
||||
Restarting function "g" ...
|
||||
Paused at (after restart):
|
||||
function g(a, b) { // We want to restart 'g'.
|
||||
#console.log('g');
|
||||
return 2 + f(a, b);
|
||||
|
||||
Called functions: h,g,f,g,f
|
@ -0,0 +1,73 @@
|
||||
// Copyright 2022 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
|
||||
|
||||
utils.load('test/inspector/debugger/restart-frame/restart-frame-test.js');
|
||||
|
||||
const {session, contextGroup, Protocol} =
|
||||
InspectorTest.start('Checks that restarting an inlined frame works.');
|
||||
|
||||
session.setupScriptMap();
|
||||
|
||||
contextGroup.addScript(`
|
||||
function f(a, b) {
|
||||
console.log('f');
|
||||
return a + b; // Breakpoint is set here.
|
||||
}
|
||||
|
||||
function g(a, b) { // We want to restart 'g'.
|
||||
console.log('g');
|
||||
return 2 + f(a, b);
|
||||
}
|
||||
|
||||
function h() {
|
||||
console.log('h');
|
||||
return 1 + g(3, 2);
|
||||
}
|
||||
|
||||
function isOptimized(f) {
|
||||
return (%GetOptimizationStatus(f) & 16) ? "optimized" : "unoptimized";
|
||||
}
|
||||
|
||||
%NeverOptimizeFunction(f); // Inline 'g' into 'h', but never 'f'.
|
||||
|
||||
%PrepareFunctionForOptimization(h);
|
||||
for (let i = 0; i < 10; ++i) h();
|
||||
%OptimizeFunctionOnNextCall(h);
|
||||
h();
|
||||
`, 0, 0, 'test.js');
|
||||
|
||||
(async () => {
|
||||
await Protocol.Debugger.enable();
|
||||
await Protocol.Runtime.enable();
|
||||
|
||||
const consoleMessages = [];
|
||||
Protocol.Runtime.onConsoleAPICalled(({ params }) => {
|
||||
consoleMessages.push(params.args[0].value);
|
||||
});
|
||||
|
||||
let { result: { result: { value } } } = await Protocol.Runtime.evaluate({ expression: 'isOptimized(h)' });
|
||||
InspectorTest.log(`Optimization status for function "h"? ${value}`);
|
||||
|
||||
await Protocol.Debugger.setBreakpointByUrl({ url: 'test.js', lineNumber: 4 });
|
||||
|
||||
const { callFrames, evaluatePromise } = await RestartFrameTest.evaluateAndWaitForPause('h()');
|
||||
|
||||
({ result: { result: { value } } } = await Protocol.Runtime.evaluate({ expression: 'isOptimized(h)' }));
|
||||
InspectorTest.log(`Optimization status for function "h" after we paused? ${value}`);
|
||||
|
||||
await RestartFrameTest.restartFrameAndWaitForPause(callFrames, 1);
|
||||
|
||||
// Restarting the frame means we hit the breakpoint a second time.
|
||||
Protocol.Debugger.resume();
|
||||
await Protocol.Debugger.oncePaused();
|
||||
await Protocol.Debugger.resume();
|
||||
|
||||
await evaluatePromise;
|
||||
|
||||
InspectorTest.log(`Called functions: ${consoleMessages.join()}`);
|
||||
|
||||
InspectorTest.completeTest();
|
||||
})();
|
@ -0,0 +1,139 @@
|
||||
Checks that restarting every frame on the stack works.
|
||||
|
||||
---- Restarting at frame index 0 ----
|
||||
Paused at (after evaluation):
|
||||
console.log('A');
|
||||
#debugger;
|
||||
return 'Magic value';
|
||||
|
||||
Pause stack:
|
||||
A:3 (canBeRestarted = true)
|
||||
B:8 (canBeRestarted = true)
|
||||
C:13 (canBeRestarted = true)
|
||||
D:18 (canBeRestarted = true)
|
||||
E:22 (canBeRestarted = true)
|
||||
F:26 (canBeRestarted = true)
|
||||
|
||||
Restarting function "A" ...
|
||||
Paused at (after restart):
|
||||
function A() {
|
||||
#console.log('A');
|
||||
debugger;
|
||||
|
||||
Evaluating to: Magic value
|
||||
Called functions: F,E,D,C,B,A,A
|
||||
|
||||
---- Restarting at frame index 1 ----
|
||||
Paused at (after evaluation):
|
||||
console.log('A');
|
||||
#debugger;
|
||||
return 'Magic value';
|
||||
|
||||
Pause stack:
|
||||
A:3 (canBeRestarted = true)
|
||||
B:8 (canBeRestarted = true)
|
||||
C:13 (canBeRestarted = true)
|
||||
D:18 (canBeRestarted = true)
|
||||
E:22 (canBeRestarted = true)
|
||||
F:26 (canBeRestarted = true)
|
||||
|
||||
Restarting function "B" ...
|
||||
Paused at (after restart):
|
||||
function B(param1, param2) {
|
||||
#console.log('B');
|
||||
return A();
|
||||
|
||||
Evaluating to: Magic value
|
||||
Called functions: F,E,D,C,B,A,B,A
|
||||
|
||||
---- Restarting at frame index 2 ----
|
||||
Paused at (after evaluation):
|
||||
console.log('A');
|
||||
#debugger;
|
||||
return 'Magic value';
|
||||
|
||||
Pause stack:
|
||||
A:3 (canBeRestarted = true)
|
||||
B:8 (canBeRestarted = true)
|
||||
C:13 (canBeRestarted = true)
|
||||
D:18 (canBeRestarted = true)
|
||||
E:22 (canBeRestarted = true)
|
||||
F:26 (canBeRestarted = true)
|
||||
|
||||
Restarting function "C" ...
|
||||
Paused at (after restart):
|
||||
function C() {
|
||||
#console.log('C');
|
||||
// Function call with argument adapter is intentional.
|
||||
|
||||
Evaluating to: Magic value
|
||||
Called functions: F,E,D,C,B,A,C,B,A
|
||||
|
||||
---- Restarting at frame index 3 ----
|
||||
Paused at (after evaluation):
|
||||
console.log('A');
|
||||
#debugger;
|
||||
return 'Magic value';
|
||||
|
||||
Pause stack:
|
||||
A:3 (canBeRestarted = true)
|
||||
B:8 (canBeRestarted = true)
|
||||
C:13 (canBeRestarted = true)
|
||||
D:18 (canBeRestarted = true)
|
||||
E:22 (canBeRestarted = true)
|
||||
F:26 (canBeRestarted = true)
|
||||
|
||||
Restarting function "D" ...
|
||||
Paused at (after restart):
|
||||
function D() {
|
||||
#console.log('D');
|
||||
// Function call with argument adapter is intentional.
|
||||
|
||||
Evaluating to: Magic value
|
||||
Called functions: F,E,D,C,B,A,D,C,B,A
|
||||
|
||||
---- Restarting at frame index 4 ----
|
||||
Paused at (after evaluation):
|
||||
console.log('A');
|
||||
#debugger;
|
||||
return 'Magic value';
|
||||
|
||||
Pause stack:
|
||||
A:3 (canBeRestarted = true)
|
||||
B:8 (canBeRestarted = true)
|
||||
C:13 (canBeRestarted = true)
|
||||
D:18 (canBeRestarted = true)
|
||||
E:22 (canBeRestarted = true)
|
||||
F:26 (canBeRestarted = true)
|
||||
|
||||
Restarting function "E" ...
|
||||
Paused at (after restart):
|
||||
function E() {
|
||||
#console.log('E');
|
||||
return D();
|
||||
|
||||
Evaluating to: Magic value
|
||||
Called functions: F,E,D,C,B,A,E,D,C,B,A
|
||||
|
||||
---- Restarting at frame index 5 ----
|
||||
Paused at (after evaluation):
|
||||
console.log('A');
|
||||
#debugger;
|
||||
return 'Magic value';
|
||||
|
||||
Pause stack:
|
||||
A:3 (canBeRestarted = true)
|
||||
B:8 (canBeRestarted = true)
|
||||
C:13 (canBeRestarted = true)
|
||||
D:18 (canBeRestarted = true)
|
||||
E:22 (canBeRestarted = true)
|
||||
F:26 (canBeRestarted = true)
|
||||
|
||||
Restarting function "F" ...
|
||||
Paused at (after restart):
|
||||
function F() {
|
||||
#console.log('F');
|
||||
return E();
|
||||
|
||||
Evaluating to: Magic value
|
||||
Called functions: F,E,D,C,B,A,F,E,D,C,B,A
|
@ -0,0 +1,71 @@
|
||||
// Copyright 2022 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.
|
||||
|
||||
utils.load('test/inspector/debugger/restart-frame/restart-frame-test.js');
|
||||
|
||||
const {session, contextGroup, Protocol} =
|
||||
InspectorTest.start('Checks that restarting every frame on the stack works.');
|
||||
|
||||
session.setupScriptMap();
|
||||
|
||||
contextGroup.addScript(`
|
||||
function A() {
|
||||
console.log('A');
|
||||
debugger;
|
||||
return 'Magic value';
|
||||
}
|
||||
function B(param1, param2) {
|
||||
console.log('B');
|
||||
return A();
|
||||
}
|
||||
function C() {
|
||||
console.log('C');
|
||||
// Function call with argument adapter is intentional.
|
||||
return B();
|
||||
}
|
||||
function D() {
|
||||
console.log('D');
|
||||
// Function call with argument adapter is intentional.
|
||||
return C(42, 'foo');
|
||||
}
|
||||
function E() {
|
||||
console.log('E');
|
||||
return D();
|
||||
}
|
||||
function F() {
|
||||
console.log('F');
|
||||
return E();
|
||||
}
|
||||
`, 0, 0, 'test.js');
|
||||
|
||||
async function testCase(frameIndex) {
|
||||
const { callFrames, evaluatePromise } = await RestartFrameTest.evaluateAndWaitForPause('F()');
|
||||
await RestartFrameTest.restartFrameAndWaitForPause(callFrames, frameIndex);
|
||||
|
||||
Protocol.Debugger.resume(); // Resuming hits the 'debugger' stmt again.
|
||||
await Protocol.Debugger.oncePaused();
|
||||
await Protocol.Debugger.resume();
|
||||
|
||||
const { result: { result: { value } } } = await evaluatePromise;
|
||||
InspectorTest.log(`Evaluating to: ${value}`);
|
||||
}
|
||||
|
||||
(async () => {
|
||||
await Protocol.Debugger.enable();
|
||||
await Protocol.Runtime.enable();
|
||||
|
||||
const consoleMessages = [];
|
||||
Protocol.Runtime.onConsoleAPICalled(({ params }) => {
|
||||
consoleMessages.push(params.args[0].value);
|
||||
});
|
||||
|
||||
for (let index = 0; index < 6; ++index) {
|
||||
InspectorTest.log(`\n---- Restarting at frame index ${index} ----`);
|
||||
await testCase(index);
|
||||
InspectorTest.log(`Called functions: ${consoleMessages.join()}`);
|
||||
consoleMessages.splice(0);
|
||||
}
|
||||
|
||||
InspectorTest.completeTest();
|
||||
})();
|
@ -510,6 +510,9 @@ class InspectorExtension : public InspectorIsolateData::SetupGlobalTask {
|
||||
inspector->Set(isolate, "newExceptionWithMetaData",
|
||||
v8::FunctionTemplate::New(
|
||||
isolate, &InspectorExtension::newExceptionWithMetaData));
|
||||
inspector->Set(isolate, "callbackForTests",
|
||||
v8::FunctionTemplate::New(
|
||||
isolate, &InspectorExtension::CallbackForTests));
|
||||
global->Set(isolate, "inspector", inspector);
|
||||
}
|
||||
|
||||
@ -771,6 +774,21 @@ class InspectorExtension : public InspectorIsolateData::SetupGlobalTask {
|
||||
args[2].As<v8::String>()));
|
||||
args.GetReturnValue().Set(error);
|
||||
}
|
||||
|
||||
static void CallbackForTests(
|
||||
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
||||
if (args.Length() != 1 || !args[0]->IsFunction()) {
|
||||
FATAL("Internal error: callbackForTests(function).");
|
||||
}
|
||||
|
||||
v8::Isolate* isolate = args.GetIsolate();
|
||||
v8::Local<v8::Context> context = isolate->GetCurrentContext();
|
||||
|
||||
v8::Local<v8::Function> callback = v8::Local<v8::Function>::Cast(args[0]);
|
||||
v8::MaybeLocal<v8::Value> result =
|
||||
callback->Call(context, v8::Undefined(isolate), 0, nullptr);
|
||||
args.GetReturnValue().Set(result.ToLocalChecked());
|
||||
}
|
||||
};
|
||||
|
||||
int InspectorTestMain(int argc, char* argv[]) {
|
||||
|
@ -328,10 +328,15 @@
|
||||
'debugger/regression-424142': [SKIP],
|
||||
'debugger/remove-breakpoint-at-breakpoint': [SKIP],
|
||||
'debugger/resource-name-to-url': [SKIP],
|
||||
'debugger/restart-frame/fails-for-non-existant-index': [SKIP],
|
||||
'debugger/restart-frame/fails-for-resumables': [SKIP],
|
||||
'debugger/restart-frame/fails-with-embedder-frames': [SKIP],
|
||||
'debugger/restart-frame/fails-without-mode-param': [SKIP],
|
||||
'debugger/restart-frame/restart-inlined-frame': [SKIP],
|
||||
'debugger/restart-frame/restart-top-frame-debugger-stmt': [SKIP],
|
||||
'debugger/restart-frame/restart-top-frame-local-variables': [SKIP],
|
||||
'debugger/restart-frame/restart-top-frame-with-breakpoint': [SKIP],
|
||||
'debugger/restart-frame/restart-various-frames': [SKIP],
|
||||
'debugger/restore-breakpoint': [SKIP],
|
||||
'debugger/return-break-locations': [SKIP],
|
||||
'debugger/scope-skip-variables-with-empty-name': [SKIP],
|
||||
@ -534,4 +539,10 @@
|
||||
'runtime/evaluate-without-side-effects-i18n': [SKIP],
|
||||
}], # no_i18n == True
|
||||
|
||||
##############################################################################
|
||||
['lite_mode or variant in (nooptimization, jitless, assert_types)', {
|
||||
# Test relies on TurboFan being enabled.
|
||||
'debugger/restart-frame/restart-inlined-frame': [SKIP],
|
||||
}], # lite_mode or variant in (nooptimization, jitless, assert_types)
|
||||
|
||||
]
|
||||
|
Loading…
Reference in New Issue
Block a user