Commit e417b339 authored by Simon Zünd's avatar Simon Zünd Committed by V8 LUCI CQ

[debug] Report variables in TDZ as 'value unavailable'

Consider the function:

function foo() {
  debugger;
  let y = 1;
}

V8 will elide the hole initialization for 'y'. When we pause at the
debugger statement, then 'y' evaluates to 'undefined'.

This CL fixes this in the ScopeIterator. When we encounter local
variables with an `undefined` value we check the static scope
information if we are stopped *before* the variable's initializer.
If yes, then we are in the variable's TDZ and report
"value unavailable".

Drive-by: Mark `GetSourcePosition()` as `const` to make it available
in the visitor method.

R=bmeurer@chromium.org

Bug: chromium:1328681
Change-Id: I8b966eaa2af64a35a58095a744440851760921a0
Fixed: chromium:1303493
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3829539
Commit-Queue: Simon Zünd <szuend@chromium.org>
Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/main@{#82483}
parent 42ea6327
......@@ -716,7 +716,7 @@ void ScopeIterator::DebugPrint() {
}
#endif
int ScopeIterator::GetSourcePosition() {
int ScopeIterator::GetSourcePosition() const {
if (frame_inspector_) {
return frame_inspector_->GetSourcePosition();
} else {
......@@ -861,6 +861,14 @@ bool ScopeIterator::VisitLocals(const Visitor& visitor, Mode mode,
current_scope_->AsDeclarationScope()->arguments() == var) {
continue;
}
} else if (value->IsUndefined(isolate_) &&
GetSourcePosition() != kNoSourcePosition &&
GetSourcePosition() <= var->initializer_position()) {
// Variables that are `undefined` could also mean an elided hole
// write. We explicitly check the static scope information if we
// are currently stopped before the variable is actually initialized
// which means we are in the middle of that var's TDZ.
value = isolate_->factory()->the_hole_value();
}
}
break;
......
......@@ -135,7 +135,7 @@ class ScopeIterator {
void AdvanceContext();
void CollectLocalsFromCurrentScope();
int GetSourcePosition();
int GetSourcePosition() const;
void TryParseAndRetrieveScopes(ReparseStrategy strategy);
......
......@@ -5,3 +5,6 @@ variable y correctly reported as <value_unavailable>
Running test: testOptimizedOut
variable y correctly reported as <value_unavailable>
Running test: testUnusedValueInTdz
variable y correctly reported as <value_unavailable>
......@@ -33,6 +33,11 @@ function opt() {
return optimizedOut(1, true);
}
function unusedValInTdz() {
debugger;
let y = 1;
}
`);
Protocol.Debugger.onPaused(async ({params: {callFrames: [{scopeChain}]}}) => {
......@@ -78,4 +83,16 @@ InspectorTest.runAsyncTestSuite([
Protocol.Debugger.disable(),
]);
},
async function testUnusedValueInTdz() {
await Promise.all([
Protocol.Runtime.enable(),
Protocol.Debugger.enable(),
]);
await Protocol.Runtime.evaluate({expression: 'unusedValInTdz()'});
await Promise.all([
Protocol.Runtime.disable(),
Protocol.Debugger.disable(),
]);
},
]);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment