I want to describe progress I've made with this problem. I'll try and make this email stand alone.
I've attached a 41 line Java program that calls into ABCL. The unusual thing it does is to first install a *debugger-hook* that throws a Java RuntimeException, not a Condition. (This voids my warranty. What follows cannot therefore be called an ABCL bug.)
The output of the program is 1, unless I compile one of the Lisp functions it defines (line 14), in which case it is 2.
Here is why. There are two definitions of progv. The first is in SpecialOperators.java. That is used for interpreted Lisp code. The other is generated by compiler-pass2.lisp, and is used for compiled Lisp code. The former has a finally clause that does:
LispThread.currentThread().resetSpecialBindings(mark)
My hypothesis is that the latter does not do the equivalent in a finally clause.
---- The situation for LET is similar to that for PROGV (as line 11 notes).
==== I had said before that using a debugger hook like this would take care of the first error in the process's life, but not subsequent ones, which would all land me in the debugger. Now we know the reason. debug.lisp's #'run-hook has a PROGV. Because debug.lisp is compiled, the "bad" PROGV gets used and *DEBUGGER-HOOK* remains rebound to NIL forever.
I've worked around the problem for my project. One workaround is to load (but not compile) #'run-hook and #'invoke-debugger again. But that's not enough. signal.lisp's #'error rebinds *current-error-depth* in a LET, and that doesn't get unbound. So after a few errors we cross the *maximum-error-depth*. To solve that, we must also load (but not compile) signal.lisp's #'error. That works.
There's a simpler equivalent solution, which is to just define an interpreted #'error function that delegates to the original one like this:
eval("(let ((error-orig #'error))" + " (handler-bind ((style-warning #'(lambda (c)" + " (declare (ignore c))" + " (muffle-warning))))" + " (defun error (&rest a)" + " (let ()" + " (apply error-orig a)))))");
The LET (interpreted, so the "good" version in SpecialOperators.java is used) takes care of resetting special bindings even though error-orig throws a RuntimeException.
This is still not ideal. If you have the following calls:
Java code -> Lisp code (function1) -> Lisp code (function2)
where function2 calls #'error, then any special variables that were rebound by function1 are _not_ reset to their original values. For me, for now, that's tolerable. I suppose there's also then a memory leak in the special bindings data structure. I'll need to watch out for this as well.
---- Perhaps most people use ABCL more to use Java from Lisp instead of vice-versa as I'm doing. If ABCL is not your top level and you disable the debugger in production, I'd love to hear from you about how you did it.
Vibhu