jscl
jscl copied to clipboard
HANDLER-BIND calls error handlers after the stack unwinds instead of before.
(defvar *x* nil)
(handler-bind ((error (lambda (exn) (throw 'x :caught!))))
(setf *x* (catch 'x (car 'x))))
That should swallow the error caused by (car 'x)
and assign :caught!
to *x*
. In JSCL, you can't throw from the handler-bind handler to the catch because the stack unwinds to outside of the catch before the lambda is called.
In Common Lisp, the handler-bind
lambdas are called before the stack unwinds, and they can halt further error handling by transferring control, such as with throw
or return-from
. The debugger is supposed to be called, also before unwinding the stack, if no handler-bind
handlers match.
The stack should only unwind when a control transfer happens, either from the debugger, or one of the handlers. The stack isn't supposed to spontaneously unwind itself because of an error like it does in every other language.
Thanks for the report @heegaiximephoomeeghahyaiseekh
In JSCL, you can't throw from the handler-bind handler to the catch because the stack unwinds to outside of the catch before the lambda is called.
If you replace (car 'x)
with (error "foo")
, then it works as you would expect. The condition system kind of works.. the problem is somewhere else.
The condition system was implemented relatively recently. Before we had it, errors were just regular Javascript exceptions. So there are still a lot of errors that are not using anything equivalent to the SIGNAL
or ERROR
function, so they can't be handled properly from CL.
I think this should be quite easily solvable. All compiler-generated code could use a jscl.throwError
function internally. To workaround the bootstrapping issue, we could define it like
var throwErrorHook;
jscl.throwError = function(msg){
if (throwErrorHook){
throwErrorHook(msg);
} else {
throw new Error(msg);
}
}
or similar, in prelude.js
. Then condition.lisp
could hook the condition system into throwErrorHook
.
If somebody would like to give it a try it would be great.
An exhaustive (but inefficient) solution would be to wrap every JavaScript operation in try/catch so that Lisp error handling can take over without the stack getting a chance to unwind.
Alternately, it might be worth it to go see what ABCL does about Java exceptions, since those would pose the same problem that you're running into here.