One principle of Debugger
's design is that its behavior should not be affected
by the garbage collector. Like the rest of JavaScript, the programmer should be
able to assume that objects have infinite lifetimes, and that the garbage
collector recycles objects only when doing so would have no visible effect on
the execution of the program. (Methods like findScripts
and findObjects
that
scan the heap are exceptions; their behavior is sensitive to the garbage
collector's activity—and cause plenty of trouble because of it.)
There is also a complementary principle, which is that Debugger
should not
impede the garbage collector's work more than necessary. For example, if the
presence of a Debugger
could have no observable effect on the future of the
program, the garbage collector should be able to recycle it. The same applies to
subsidiary objects like Debugger.Frame
: if their hooks will never fire, and
their referents are gone for good, then they should be recycleable.
These rules give Debugger
hooks unusual significance to the garbage collector.
If a Debugger
has hooks that could run in the future, they could have visible
effects. It follows, then, the the garbage collector must never recycle a
Debugger
whose hooks might run, even if the Debugger
is not reachable by
JavaScript until they do.
As part of the outer loop of its iterate-to-closure algorithm, the garbage
collector calls js::Debugger
's static markIteratively
method. This searches
all realms to find debuggees, and searches their debuggers for those that have
live hooks, according to the Debugger::hasAnyLiveHooks
method. That method
checks for hooks not only on the Debugger
itself, but also for
Debugger.Frame
s and breakpoints, which may have hooks of their own.
Generators are interesting to support in the Debugger API because they introduce
stack frames whose lifetimes are not stack-like: without generators, once a
frame leaves the stack, the Debugger has no reason to retain it. But a
generator's stack frame may be resumed in the future, and Debugger is expected
to produce the same Debugger.Frame
for it if it does.
This means that there are live Debugger.Frame
objects that are not present in
the Debugger
's usual table mapping abstract frame pointers to
Debugger.Frame
s. Instead, there must be an additional table mapping
GeneratorObject
s to Debugger.Frame
s, which can refer to the frames even when
they are not on the stack. The hasAnyLiveHooks
function must scan this table
for frames with live hooks, just as it does the usual frame table, and mark the
Debugger
if any are found.
- SpiderMonkey must iterate correctly to find all objects reachable through Debugger weakmaps.
- Suppose a GeneratorObject G1 is rooted. It has a D.F F1 with an onStep handler that refers to another GeneratorObject G2. That has a D.F F2 with an onStep handler. GC must not drop F2, and its handler should run.