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.