Navigation:  Programming Cookbook > Exception Handling >

Handling Exceptions

Previous pageReturn to chapter overviewNext page

Having caught an exception (hopefully one that was anticipated), one then needs to do something with it to recover from the exceptional condition. The handler block can contain any sequence of Smalltalk statements, and it is passed the exception instance as its argument when evaluated. Exceptions understand a 'handler response' protocolthat can be used to affect the execution state. The messages and their meanings are:

#exit(:). Depending on whether the exception is resumable or non-resumable this is the same as #resume(:) or #resume(:) respectively. This is the way to specify that execution should continue from the point where the exception was raised if possible, and if not from the statement after the handler block.
#outer. Evaluate the next enclosing exception handler, which may not return to the handler depending on whether it modifies the execution path itself. If there is no suitable outer handler, then the default action is performed for the exception, which will vary depending on the class of exception, and the SessionManager. It is probably only appropriate to use this message where one knows there is an outer handler, the behaviour of which one wishes to modify slightly.
#pass. Pass the exception to the next enclosing exception handler. This differs from #outer in that control does not return to the handler sending this message - it is the standard way to rethrow an exception, perhaps after having decided that one cannot handle it after all.
#resignalAs:. Raise a different class of exception in place of the current exception, as if the new class of exception had been raised in the first place.
#resume(:). If the exception is resumable, continue execution at the point immediately after where the exception was raised, with the argument (or nil) as the result of the expression that raised the exception. If the exception is not resumable, then an Error is raised to the effect that an attempt was made to resume a non-resumable exception.
#retry. The try-block associated with the handler (i.e. the receiver of the #on:do: to which it is the last argument) is re-evaluated. Of course it is pointless retrying if the same exception will be raised, and this is an easy way to create an infinite loop (though Ctrl+Break should get one out of trouble).
#retryUsing: Substitute the argument as the new try block, and #retry. This has particular application for operations that may have fast implementations for commonly used execution paths, but slower implementations for less common usages.
none - drop off the end of the handler block and accept the default behaviour, which is to always perform a #return: with the value of the handler block as the argument.

Note that some of these handler responses are quite powerful, and dangerous (especially #retry(:) and #retryUsing:), and should be used with caution.

The default behaviour if no handler response is specified is ALWAYS to continue execution with the statement immediately after the handler block (i.e. it is the same as if #return: had been explicitly specified), regardless of whether the exception is resumable or not, and is very much like the Java/C++ exception handling model in this regard.

In order to resume after a handled resumable exception, it is necessary to explicitly send #resume(:) (or #exit(:)) to the exception from the handler block.

Further exceptions may occur inside handler blocks, and these can be caught and handled too. Handler blocks are evaluated in the exception enviroment current when they were constructed, which in practice coincides with the behaviour one expects.

It is worth bearing in mind that when an exception handler block is being evaluated, that the stack has not yet been unwound (if it had then resumption would not be possible). The stack is only unwound when the handler completes by dropping off its end, or by some explicit handler response message being sent to the exception.