Hi,
I'd like to add some sort of 'changed definitions' functionality to slime, i.e., be able to to evaluate or compile changed defs in the buffer or all buffers.
I'm thinking of modeling this after the changed definitions of Genera (from my new lisp machine :) or LispWorks.
I've looked at the similar functionality in ilisp (but you have to manually mark functions) and Franz's eli (which looks like it might be a good starting point).
Any particular hints or comments folks would like to make before I start?
Any internal functions that might be helpful?
Glenn Ehrlich glenn.ehrlich.lists@cox.net writes:
I'm thinking of modeling this after the changed definitions of Genera (from my new lisp machine :) or LispWorks.
How does it work on the Lisp Machine?
[BTW, was/is SLIME an acronym for Symbolics LIsp Machine Environment?]
Any particular hints or comments folks would like to make before I start?
I don't want to discourage you, but do you think this is worth to have? When I looked at this feature I thought "well, this has a certain hack value, but compiling the entire buffer doesn't take that long and is easier to understand and trivial to implement."
Helmut.
Helmut,
I've emailed you separately, but thanks for the comments. If no one else thinks it would be useful, I can easily be persuaded to work on something else. I've got a rough design floating around in my head that would use the existing definition support in swank plus some additional functionality from imenu. I looked at the way Franz eli does it and it's sort of similar to the way I was thinking of doing it. I don't think it would be too difficult to implement, but I would like to spend my time working on something that others would find useful. As I said to Helmut, I'm more in the mindset of "I'd like to contribute in ways folks will find useful", rather than "I've got to get feature X into slime".
On Wed, 16 Jun 2004 23:27:11 +0200, Helmut Eller wrote:
Glenn Ehrlich glenn.ehrlich.lists@cox.net writes:
I'm thinking of modeling this after the changed definitions of Genera (from my new lisp machine :) or LispWorks.
How does it work on the Lisp Machine?
The way it works in LispWorks is exactly the same way it works on the Lisp Machine, same keystroke bindings, too, I think. Basically, meta shift e evaluates all changed definitions in the current buffer (yes, I know that emacs doesn't recognize the shift modifier, but zmacs does) and meta shift c compiles the changed defintions in the current buffer. There's similar commands for doing the same for all buffers, but they aren't bound to any keystrokes. The definition of changed is roughly "edited since last sent to Lisp".
I speculated in my reply to Helmut that maybe development styles are different nowadays vs. back then on the LispM. For example, the default configuration creates an unlimited number of versioned files (indicated by a trailing number i.e., "foo.lisp.1", "foo.lisp.2", etc). I unconsciously save very frequently, which when done on a LispM creates a *ton* of versioned files. I speculate that maybe it was normal to have lots of unsaved buffers throughout the day and that you needed a convenient way of evaluating or compiling the changed stuff without going through all of your buffers individually.
[BTW, was/is SLIME an acronym for Symbolics LIsp Machine Environment?]
Any particular hints or comments folks would like to make before I start?
I don't want to discourage you, but do you think this is worth to have? When I looked at this feature I thought "well, this has a certain hack value, but compiling the entire buffer doesn't take that long and is easier to understand and trivial to implement."
After reading this, I do have to say that I agree.
Helmut.
Glenn Ehrlich glenn.ehrlich.lists@cox.net writes:
Helmut,
I've emailed you separately, but thanks for the comments. If no one else thinks it would be useful, I can easily be persuaded to work on something else. I've got a rough design floating around in my head that would use the existing definition support in swank plus some additional functionality from imenu. I looked at the way Franz eli does it and it's sort of similar to the way I was thinking of doing it. I don't think it would be too difficult to implement, but I would like to spend my time working on something that others would find useful. As I said to Helmut, I'm more in the mindset of "I'd like to contribute in ways folks will find useful", rather than "I've got to get feature X into slime".
Just in case people run out of ideas, here are some from my TODO/half baked ideas list:
- rename throw-to-toplevel as restart-toplevel
- unify compiler warnings buffer with xref buffer
- think about :lisp filename in compilation notes
- stepper
- better handling of segfaulted cmucl
- debugger-limit
- implement inspector history and use a "path" instead of a stack.
- better describe-function. With clickable text that brings you to the source of the function.
- better source location handling in sbcl, probably with sb-introspect.
- support for redirecting the output of C-x C-e to the current buffer; like Emacs built-in C-x C-e.
- support polling of the redirected input stream.
- multiple listners (low priority). This should be relatively easy for multi-threaded Lisps but is a bit strange for CLISP and CMUCL.
- better scrolling. Ideally similar to term-mode.
- implement sldb-show-source for Allegro; at least a bit.
- OpenMCL: Make M-. work for functions compiled with C-c C-c. Probably involves setting *loading-file-source-file* in swank-compile-string. Might be similar to the kludge used in swank-lispworks.lisp.
- CLISP: produce better backtraces; requires C hacking. Improve source location recording/defintion finding. Perhaps the easiest to fall back to TAGS.
- LispWorks: idea to improve sldb-show-source: first READ the toplevel form for the frame then find the location of a form that looks like a call to the callee (the next-frame). LispWorks graphical debugger is somehow able to highliht (/ 1 0) in the following form:
(defun foo () (print (/ 1 0)))
I wonder how they do it.
- implement restart-frame and return-from-frame in CMUCL. The major problem is that CMUCL uses 4 stacks to represent the dynamic environment -- Control Stack, Binding Stack, Eval Stack and Alien Stack (Non x86 have a Number Stack but no Alien stack.) -- but it isn't easy to reconstruct all stack pointers by the debugger. SBCL implements return-from-frame with some relatively inefficient mechanism: every function is rewritten as a (defun ... (...) (catch (make-symbol "SB-CATCH-TAG") ...)) and the debugger searches the tag (with string comparision) in that frame and throws to it. This approach isn't particularly appealing, because it is expensive to allocate a symbol for each call, it's expensive to save the dynamic environment on each call, it infers with tail call optimization, is not "pay as you go".
Relatively simple way to avoid allocating a symbol for each catch, is to use a special unwind function. All we need to do is to set the current-catch-block (or whatever) to the target catcher. Throwing will then execute all unwind blocks and skip all catchers event if they have the same catch tag.
I think it should be possible to reconstruct the dynamic environment if we safe the spill locations for stack pointers in the debugging info. Stack pointers are saved on the control stack in the following situations: catch, unwind-protect, all sort of NLXs, alien allocations, evaluations, and dynamic lets.
It seems that Allegro saves the dynamic binding stack pointer at function entry if the function binds a dynamic variable. This would be quite a robust way to reconstruct the dsp. I don't know all the details needed for an actual implementation, but I assume it would similar to the handling of nfp on the RISC ports.
Another problem are the restart entry points and the locations for the arguments for the to be restarted function. This might be very difficult.
- test the scalability of the xref fasl dumping. saving callers _and_ callees seems like a redundancy (especially since the compiler only sees callees). think about xref and compile-defun and compile-from-stream. Also think about the usefulness of precise callsite recording, probably only feasible for small projects. Should xref info be attached to function objects? A lot of the stuff is already there in particular callees (and callers can be reconstructed by memory/symbol-groveling). We could add a bitvector indicating which fdefn or symbol in the constant pool is a callee, a referenced/set/used variable ... How useful is this xref stuff anyway? Isn't grep good enough? The interesting/hard cases like indirect calls to function objects stored in tables aren't covered by xref. Should we record where a function escapes? Again, funcalling symbols will not be covered.
- Zmacs and ELI have a concept of "changed definitions". Sounds difficult to implement in the presence of toplevel macros and eval-when. perhaps we could deal with toplevel forms insteaod of definitions? or some shortcut like forms starting in the first column with "(def". Worth to bother? http://bitsavers.org/pdf/ti/explorer/2243192-0001A_Zmacs_Jun87.pdf
- enhance the disassembler so that we can find the code location of references to fdefn objects and use that as approximation for the call-site. [difficult]
The way it works in LispWorks is exactly the same way it works on the Lisp Machine, same keystroke bindings, too, I think. Basically, meta shift e evaluates all changed definitions in the current buffer (yes, I know that emacs doesn't recognize the shift modifier, but zmacs does) and meta shift c compiles the changed defintions in the current buffer. There's similar commands for doing the same for all buffers, but they aren't bound to any keystrokes. The definition of changed is roughly "edited since last sent to Lisp".
And what's the definition of "definition"?
Helmut.
Helmut Eller e9626484@stud3.tuwien.ac.at writes:
Just in case people run out of ideas, here are some from my TODO/half baked ideas list:
[...]
- implement sldb-show-source for Allegro; at least a bit.
I just committed a patch from Matthew Danish that does this with defun-granularity.
-Luke