This summer, Mark and I met in person. We discussed several things, we
wanted to improve on ABCL. One of the most important things to improve on
ABCL, or so it seems to us, is to be able to inspect - given high enough
debug settings - the locals in the current stackframe.
Since I have had a good idea how to do this for a long time now, I'm writing
down how I want to do it, making it work for both compiled and interpreted
functions.
The basic requirements are simple: while walking the stack, being able to
focus on any single frame and retrieving the locals from it, as well as
their values for inspection, of course. Being able to STEP code isn't a
requirement - although that may be subject to another change later.
My idea is really actually very simple. We already have an interpreter which
creates Environment objects to record the lexical variables and their values
(as well as temporary "specialness"). The missing bit is the link between
the environment and the stack frame. Note that within the scope of a single
stack frame, multiple environments may be cycled through, one after another.
This typically happens with new binding blocks such as DO/DO*, LET/LET*,
M-V-B, etc.
Since this information is all readily available, I'm envisioning a method on
LispThread which sets the environment of the current stack frame. Then, when
the debugger is invoked, the environment object of the stack frame to be
inspected can be retrieved and the bindings referred to it can be walked to
print the state of the lexical environment.
The impact of this change is relatively limited: everywhere we find a "new
Environment", we want to review if adding the environment to the stackframe
is applicable.
So far so good. Now about compiled code. It doesn't have Environment
objects, because the environment is hard coded into the compiled functions.
If we were to step away from that concept, we might as well completely stop
compiling the program. So now what?
I see a middle road: values reachable through an Environment are stored in a
linked list of Binding objects. We could create these Binding objects,
adding them to the environment, but still keeping a reference to them. Then,
our performance would only suffer a single additional level of indirection.
The thing to be figured out would be how to 'model' closures. The
environment model works well with closures in interpreted mode, but with
this 'faked' environment it works less well. One solution would be to add
the closure variables to the Environment upon function entry. That might
cost some performance, but would require no access to the outer Environment.
Other solutions - which I haven't evaluated yet - might involve the array of
ClosureBindings, which is a way to carry information from one closure to
another.
Comments?
Bye,
Erik.