In the CLRHack compiler, handler-bind is a primitive form used to register condition handlers in the dynamic environment. It operates by managing a thread-local list of active handler objects, ensuring that condition signaling follows the standard Common Lisp search and execution rules.
Handling of handler-bind
When the compiler processes a handler-bind form, it generates CIL code that performs the following steps:
- Capture Previous State: It calls
Lisp.HandlerControl::GetActiveHandlers()to retrieve the current list of active handlers and stores it in a frame-local variable. - Construct New List: For each binding, it evaluates the condition type and the handler function (which is
typically a closure). It instantiates a new
[LispBase]Lisp.Handlerobject and conses it onto the current handler list. - Install New State: It calls
Lisp.HandlerControl::SetActiveHandlers(new_list)to update the dynamic environment for the current thread. - Protected Execution: The body of the
handler-bindis wrapped in a CIL.tryblock. - Restoration: A
finallyblock is emitted that callsSetActiveHandlerswith the saved list. This ensures that handlers are properly uninstalled, regardless of whether the body completes normally, signals an error, or performs a non-local exit.
Lexical Non-Local Exits
Handlers in Common Lisp are executed in the dynamic environment of the signaller but have lexical access to the environment
where they were defined. In CLRHack, if a handler function performs a non-local exit (such as a throw or
return-from), the compiler utilizes its exception-based jump mechanism:
- If the exit is a
throw, it uses the standardCatchThrowExceptionmechanism. - If the exit is a
return-fromto a block outside the handler closure, the compiler identifies this as a non-local exit duringanalyze-environment. It compiles thereturn-frominto athrowof aBlockExitException, which is subsequently caught by thetry/catchframe established by the targetblock.
Handler Search
The handler search is performed at runtime by the signal or error functions. These functions retrieve
the active handlers list via HandlerControl.GetActiveHandlers() and iterate through them. For each handler, the
runtime checks if the signaled condition is of the type (or a subtype of the type) the handler was registered for. If a match is
found, the handler function is invoked. If the handler returns normally (declines), the search continues with the next applicable
handler.
Dynamic Tags
The handler-bind implementation itself relies on the dynamic state of the thread-local activeHandlers
list. However, when used in conjunction with handler-case, unique dynamic tags (typically fresh ListCell
objects) are generated. These tags are used as the "target" for the throw performed by the handler, ensuring that the
control flow returns exactly to the correct handler-case frame and doesn't conflict with other active handler or catch
frames.
handler-case as an Extension of handler-bind
In CLRHack, handler-case is not a primitive but a macro that expands into a combination of block,
catch, and handler-bind. It extends handler-bind by providing a mechanism to automatically
exit the signaling context and execute a specific branch of code based on the condition caught.
The implementation details of the expansion are as follows:
- Exit Block: The entire form is wrapped in a
blockwith a unique exit tag to allow the normal path to return immediately upon completion of the protected expression. - Dynamic Setup: A unique dynamic tag is created for the
catchframe. Local variables are established to store the captured condition and a unique ID identifying which clause was triggered. - The Binding: A
handler-bindis generated where each handler function is a closure that, when called:- Saves the signaled condition into the local
condition-var. - Sets the
id-varto a unique GENSYM representing that specific clause. - Performs a
throwto the dynamic tag.
- Saves the signaled condition into the local
- The Catch and Dispatch: A
catchblock surrounds the protected expression. If a handler performs thethrow, thecatchreturns, and acondstatement (the dispatcher) checks theid-var. It then executes the body of the matchinghandler-caseclause with the condition variable bound to the clause's parameter.
No comments:
Post a Comment