Currently this package consists of two “frameworks”:
It is intended to be a shared resource between DDC and dart2js.
TODO
This is supposed to work in a few steps:
In the above, the helper assumes
And then performs via 2) runD8AndStep
3) checkD8Steps
4) (done in above step)
The test files themselves contain information about where to stop, which breakpoints to expect etc. They contain this information in comments inlined in the code as in /*key*/
where key
can be one of the below.
Not context sensitive
These comments can be anywhere in the file and their position does not matter.
Context sensitive
These comments should be placed at the wanted position: The line and possibly column position of the comment matters. They refer to the next non-whitespace position in the source.
i
th stop (1-indexed).i
th stop (1-indexed). Only check the line number.i
and j
(1-indexed). Note that from
can also be the special value 0
meaning from the very first stop. For example nbb:0:1
means not before first expected stop.i
th stop (1-indexed).Note that in an ideal world bc:{i}
would not be unnecessary: Stopping at a line and stepping should generally be enough. Because of the current behavior of d8 though, for instance
baz(foo(), bar())
will stop at baz
, go into foo
, stop at bar
, go into bar
and stop at baz
. From a Dart perspective we would instead expect it to stop at foo
, go into foo
, stop at bar
, go into bar
and stop a baz
. Having bc:{i} allows us to force this behavior as d8 can actually stop at foo
too.
All of these annotations are removed before compiling to js and the expected output thus refers to the unannotated code.
When the test confirms that the debugger stopped at the expected locations it allows for additional breakpoints before, between and after the expected breakpoints.
By calling checkD8Steps
with the debug
parameter set to true
one can get information like this dumped to standard out:
Stop #1 test.main = function() { | main() { try { | try { let value = /*STOP*/"world"; | var value = /*STOP*/"world"; dart.throw(dart.str`Hello, ${value}`); | // Comment } catch (e) { | throw "Hello, $value"; Stop #2 try { | var value = "world"; let value = "world"; | // Comment /*STOP*/dart.throw(dart.str`Hello, ${value}`); | /*STOP*/throw "Hello, $value"; } catch (e) { | } let st = dart.stackTrace(e); | // Comment Stop #3 dart.throw(dart.str`Hello, ${value}`); | } } catch (e) { | // Comment let st = /*STOP*/dart.stackTrace(e); | catch (e, /*STOP*/st) { { | print(e); core.print(e); | print(st); [...]
This can for instance be useful in combination with /*fail*/
when adding new tests to see all the places where the debugger stopped.
Some of the logic comes from https://github.com/ChromeDevTools/devtools-frontend/, for instance see https://github.com/ChromeDevTools/devtools-frontend/blob/fa18d70a995f06cb73365b2e5b8ae974cf60bd3a/front_end/sources/JavaScriptSourceFrame.js#L1520-L1523 for how a line breakpoint is resolved: Basically the line asked to break on in user code (e.g. in dart code) is asked for first and last javascript positions; these are then used to get possible breakpoints in that part. If there are none it tries the next line (etc for a number of lines). Once it finds something (in javascript positions) it converts that to user code position (e.g. in dart code), normalizes it by converting to javascript position and back to user code position again, then converts to javascript position and sets the breakpoint. This is to some extend mimicked here when setting a line break (though not a “column break”).