I'm trying to get the debugger working again. It got broken back when the console was moved to usermode, and so its getting a rewrite to become more userspace itself.
When you ask for a BACKTRACE, it numbers the function frames in the stack. The numbers are a user convenience, so you can say things about which frame you want to inspect...or where you want to resume running from. debug 3 means switch the binding context of the console to the frame you saw numbered 3 in the backtrace, for instance, so it binds words that are function parameters there when you type in the console.
In a single-threaded world--with a routine like BACKTRACE written in usermode--it has to take into account its own stack frames. Presumably you don't want to see those, nor do you want other debug routines that try to use the numbers to face an issue of "contamination" from their own stack levels if they (like the aforementioned debug) try to turn those numbers back into FRAME! values on the user's behalf as part of their implementation.
A user-friendly approach is to count frames backward from the last breakpoint. So long as all the routines which use BACKTRACE's notion of numbering agree on that convention, they don't have to worry about their own stack frames intervening.
But this raises a curious question: when is a breakpoint on the stack? Let's say I want to make some kind of logging breakpoint, and I use ADAPT to do it:
logging-breakpoint: adapt 'breakpoint [ write/append %breakpoint-log.txt unspaced [ "breakpoint hit at" now newline ] ]
(Note: you'd more likely want to HIJACK the regular BREAKPOINT with this adaptation than to call it via the LOGGING-BREAKPOINT name explicitly, but for the sake of this argument let's say you just call it directly under this new name.)
When I use LOGGING-BREAKPOINT it logs the time, and falls through to breakpoint as the underlying "phase" of the function. But will BACKTRACE see BREAKPOINT on the stack? Technically it is running BREAKPOINT's body at that moment... it's morally equivalent to:
equivalent-breakpoint: func [return: ] [ write/append %breakpoint-log.txt unspaced [ "breakpoint hit at" now newline ] breakpoint ]
This particular issue could be checked by another property...asking if a frame itself is
paused?. But it raises a good question. What do you mean when you ask if a particular function is on the stack, in the face of compositions?
Mechanically, there is something called a "phase". So when you call LOGGING-BREAKPOINT it is originally on the stack in the phase of its own adapted body...e.g. its phase is the FUNCTION! of :LOGGING-BREAKPOINT. But when it falls off the end of the adaptation and continues running BREAKPOINT in the same FRAME!, it will be in the :BREAKPOINT phase.
So this raises the question of what you get back when you ask for the FUNCTION-OF a FRAME!. Should the answer be invariant (the top-level function of the frame, whose invocation began it) or should it change depending on how far you've gotten in the execution? Should there be a PHASE-OF which tells you the phase a frame is in, or should it be considered a "black box" and none of your business?