Context and scope chain mismatch when debugging for-of scopes. |
|||||
Issue description
Consider following test case:
var Debug = debug.Debug;
Debug.setListener(() => {});
function repro() {
for (let [a, b = eval("debugger")] of [[0]]) {
function f() { return a };
}
}
repro();
To be run with test/mjsunit/mjsunit.js test/debugger/test-api.js --enable-inspector --allow-natives-syntax
We crash with
#
# Fatal error in ../../src/debug/debug-scopes.cc, line 276
# Debug check failed: context_->IsFunctionContext() || !scope_info->HasContext().
#
When run with --print-scopes, we get
function repro () { // (0x564a82491fd8) (70, 160)
// scope calls 'eval'
// inner scope calls 'eval'
// will be compiled
// 18 stack slots
// 8 heap slots
// temporary vars:
TEMPORARY .for; // (0x564a82492fa0) local[2]
TEMPORARY .0x564a82493020; // (0x564a82493020) local[3]
TEMPORARY .0x564a82493100; // (0x564a82493100) local[4]
TEMPORARY .0x564a824931d0; // (0x564a824931d0) local[5]
TEMPORARY .0x564a82493260; // (0x564a82493260) local[6]
TEMPORARY .0x564a82493290; // (0x564a82493290) local[7]
TEMPORARY .0x564a824932c0; // (0x564a824932c0) local[8]
TEMPORARY .0x564a824cb770; // (0x564a824cb770) local[9]
TEMPORARY .0x564a824cbc48; // (0x564a824cbc48) local[10]
TEMPORARY .0x564a824cc120; // (0x564a824cc120) local[11]
TEMPORARY .iterator; // (0x564a824cc7e0) local[12]
TEMPORARY .result; // (0x564a824cc810) local[13]
TEMPORARY .0x564a824cc840; // (0x564a824cc840) local[14]
TEMPORARY .0x564a824ccc28; // (0x564a824ccc28) local[15]
TEMPORARY .0x564a824cd0a8; // (0x564a824cd0a8) local[16]
TEMPORARY .0x564a824cd568; // (0x564a824cd568) local[17]
// local vars:
VAR arguments; // (0x564a824cdda8) context[7]
VAR f; // (0x564a824cdd78) context[6]
CONST .new.target; // (0x564a824921c0) context[5]
VAR this; // (0x564a82492190) context[4]
block { // (0x564a824924c8) (81, 158)
// is hidden
// scope calls 'eval'
// inner scope calls 'eval'
// 6 heap slots
// local vars:
LET b; // (0x564a824cdc58) context[5]
LET a; // (0x564a824cdbe8) context[4]
catch { // (0x564a824cd8f0) (-1, -1)
// is hidden
// 5 heap slots
// local vars:
VAR .catch; // (0x564a824cda38) context[4], never assigned
}
catch { // (0x564a824cd3c8) (-1, -1)
// is hidden
// 5 heap slots
// local vars:
VAR .catch; // (0x564a824cd510) context[4], never assigned
}
block { // (0x564a82492918) (120, 158)
// 5 heap slots
// local vars:
LET b; // (0x564a824cb8d0) local[1], never assigned, hole initialization elided
LET a; // (0x564a824939b8) context[4], forced context allocation, never assigned
catch { // (0x564a824cc4c0) (-1, -1)
// is hidden
// 5 heap slots
// local vars:
VAR .catch; // (0x564a824cc608) context[4], never assigned
}
catch { // (0x564a824cbf80) (-1, -1)
// is hidden
// 5 heap slots
// local vars:
VAR .catch; // (0x564a824cc0c8) context[4], never assigned
}
block { // (0x564a82492b08) (122, 158)
// local vars:
LET f; // (0x564a82492e58) local[0], never assigned
function f () { // (0x564a82492c78) (138, 153)
// lazily parsed
// 4 heap slots
}
}
}
}
}
Note that the assignment to 'b' is happens in the second block scope. This is in accordance of the comment in parser.cc
// Rewrite a for-in/of statement of the form
//
// for (let/const/var x in/of e) b
//
// into
//
// {
// var temp;
// for (temp in/of e) {
// let/const/var x = temp;
// b;
// }
// let x; // for TDZ
// }
However, at the point of the debugger statement, on the second-to-topmost frame, we have the source position right at the assignment to 'b'. The source position is 94, but we actually are in the second block scope with the source range (120, 158).
The debugger inspects the scopes by parsing the function, and finding the scope chain via source position. The expectation is that the scope chain matches the current context chain. This is not the case since the context chain is [function context]->[block context]->[block context], but the scope chain we found based on source position is [function scope]->[block scope].
The reason for this is that the assignment belongs to the second block scope, but is outside the source position range attributed to that block scope.
Adam, do you have any opinion on how to fix this? This is essentially confirmed your suspicion that we need several source ranges per scope to satisfy the debugger.
,
Aug 8 2017
I don't have any easy suggestions: the spec effectively does what our desugaring does. See https://tc39.github.io/ecma262/#sec-for-in-and-for-of-statements-runtime-semantics-labelledevaluation for the details (the bits including "ForDeclaration").
,
Sep 21 2017
,
Nov 16 2017
Going to take a look at fixing this in the naive way: attaching multiple source ranges to such scopes.
,
Nov 20 2017
Does this perhaps also solve https://bugs.chromium.org/p/chromium/issues/detail?id=774318 ?
,
Nov 20 2017
I think it would at least provide a basis for solving that issue, marking as such. Also, I have a work-in-progress up at https://chromium-review.googlesource.com/c/v8/v8/+/775538, which I'd appreciate feedback on. |
|||||
►
Sign in to add a comment |
|||||
Comment 1 by yangguo@chromium.org
, Aug 8 2017