'From Squeak 1.2 of June 29, 1997 on 4 August 1997 at 2:39:25 am'!"Change Set: trivial-exceptions Date: 4 August 1997 Author: Ian Piumarta Simple (1 class, 7 methods) but versatile (aborting [in two ways], resuming, reraising, etc...) exception handling for Squeak -- with unwind protection for block evaluation thrown in for free. Plus the obligatory bunch of examples: look in Exception class."! Object subclass: #Exception instanceVariableNames: 'handlerContext ' classVariableNames: '' poolDictionaries: '' category: 'System-Exceptions'! Exception subclass: #TestExceptionA instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'System-Exceptions'! Exception subclass: #TestExceptionB instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'System-Exceptions'! Exception subclass: #TestExceptionC instanceVariableNames: '' classVariableNames: '' poolDictionaries: '' category: 'System-Exceptions'! !BlockContext methodsFor: 'evaluating' stamp: 'ikp 8/4/97 01:55'! valueNowOrOnUnwindDo: unwindBlock | result | ^Exception handle: [ :exception | unwindBlock value. ^exception reraise ] "non-local return protects against resuming handlers" do: [ result _ self value. unwindBlock value. ^result ]! ! !BlockContext methodsFor: 'evaluating' stamp: 'ikp 8/4/97 01:56'! valueOnUnwindDo: unwindBlock ^Exception handle: [ :exception | unwindBlock value. ^exception reraise ] "non-local return protects against resuming handlers" do: [ self value ]! ! !Exception reorganize! ('raising' raise reraise) ('returning' return:) ('private' findHandlerContextFrom: raiseFrom:) ! !Exception methodsFor: 'raising' stamp: 'ikp 8/4/97 01:34'! raise "Raise an exception of the receiver's type." ^self raiseFrom: thisContext sender! ! !Exception methodsFor: 'raising' stamp: 'ikp 8/4/97 01:46'! reraise "The current handler is not prepared to deal with the situation: find the next handler and activate it." handlerContext == nil ifTrue: [self error: 'this exception has not yet been raised']. ^self raiseFrom: handlerContext sender! ! !Exception methodsFor: 'returning' stamp: 'ikp 8/4/97 02:26'! return: result "Abort the exception handler, returning result as the value of the activity block" thisContext swapSender: handlerContext. ^result! ! !Exception methodsFor: 'private' stamp: 'ikp 8/4/97 02:24'! findHandlerContextFrom: aContext "Search from aContext for a handler prepared to handle the receiver. Answer the handler's context." | context targetMethod | context _ aContext. targetMethod _ Exception class compiledMethodAt: #handle:do:. [[context ~~ nil and: [context method ~~ targetMethod]] whileTrue: [context _ context sender]. context == nil or: [context receiver isPreparedToHandle: self]] whileFalse: [context == nil ifFalse: [context _ context sender]]. context == nil ifTrue: [self error: 'unhandled exception']. ^context! ! !Exception methodsFor: 'private' stamp: 'ikp 8/4/97 01:34'! raiseFrom: aContext "Raise the receiver in the nearest handler context to aContext (inclusive)." | handler | handlerContext _ self findHandlerContextFrom: aContext. handler _ handlerContext at: 1. (handler isKindOf: BlockContext) ifFalse: [self error: 'cannot find handler']. ^handler value: self! ! !Exception class reorganize! ('exception handling' handle:do:) ('private' isPreparedToHandle:) ('examples' example1 example2 example3 example4 exampleInheritance exampleUnwind) ! !Exception class methodsFor: 'exception handling' stamp: 'ikp 8/2/97 21:14'! handle: handlerBlock do: activityBlock "Evaluate activityBlock in a context where an exception of this class (or a subclass) can be raised and handled by handlerBlock" ^activityBlock value! ! !Exception class methodsFor: 'private' stamp: 'ikp 8/4/97 01:45'! isPreparedToHandle: anException "Answer wether this class is prepared to handle anException" ^anException isKindOf: self! ! !Exception class methodsFor: 'examples' stamp: 'ikp 8/4/97 02:10'! example1 "Exception example1" | value | value _ Exception handle: [ :exception | Transcript cr; show: 'Exception raised!!'. exception return: 42] do: [Transcript cr; show: 'not raising an exception...'. 43]. Transcript cr; show: 'value is ' , value printString.! ! !Exception class methodsFor: 'examples' stamp: 'ikp 8/4/97 02:10'! example2 "Exception example2" | value | value _ Exception handle: [ :exception | Transcript cr; show: 'Exception raised!!'. exception return: 42] do: [Transcript cr; show: 'about to raise exception...'. Exception new raise. Transcript cr; show: 'this should never happen!!'. 43]. Transcript cr; show: 'value is ' , value printString.! ! !Exception class methodsFor: 'examples' stamp: 'ikp 8/4/97 02:12'! example3 "Use print it, rather than do it, on:" "Exception example3" "Note: this is *supposed* to escape from the example3 method during the handler, avoiding the printing of the result in the Transcript altogether." | value | value _ Exception handle: [ :exception | Transcript cr; show: 'Exception raised!!'. ^42] "escape to this method's sender" do: [Transcript cr; show: 'about to raise exception...'. Exception new raise. Transcript cr; show: 'this should never happen!!'. 43]. Transcript cr; show: 'value is ' , value printString.! ! !Exception class methodsFor: 'examples' stamp: 'ikp 8/4/97 02:38'! example4 "Exception example4" | value raiseValue | value _ Exception handle: [ :exception | Transcript cr; show: 'Exception raised!!'. 42] do: [Transcript cr; show: 'about to raise exception...'. raiseValue _ Exception new raise. Transcript cr; show: 'raise returned ', raiseValue printString. 43]. Transcript cr; show: 'value is ' , value printString.! ! !Exception class methodsFor: 'examples' stamp: 'ikp 8/4/97 02:25'! exampleInheritance "Exception exampleInheritance" Exception handle: [ :exception | Transcript cr; show: 'Exception handling ', exception printString.] "resume" do: [Transcript cr; show: 'about to raise exception A with generic handler...'. TestExceptionA new raise]. Exception handle: [ :exception | Transcript cr; show: 'Exception handling ', exception printString.] "resume" do: [TestExceptionA handle: [ :exceptionA | Transcript cr; show: 'TestExceptionA handling ', exceptionA printString.] "resume" do: [TestExceptionB handle: [ :exceptionB | Transcript cr; show: 'TestExceptionB handling ', exceptionB printString.] "resume" do: [ Exception new raise. TestExceptionA new raise. TestExceptionB new raise. TestExceptionC new raise.]]]! ! !Exception class methodsFor: 'examples' stamp: 'ikp 8/4/97 02:05'! exampleUnwind "Exception exampleUnwind" | value | value _ [Transcript cr; show: 'case 1: valueOnUnwind, no exception'. 101] valueOnUnwindDo: [Transcript cr; show: 'unwinding 1...']. Transcript cr; show: 'value is ' , value printString. value _ Exception handle: [ :exception | nil ] "ignore the reraised exception from the unwind handler" do: [[Transcript cr; show: 'case 2: valueOnUnwind, exception'. Exception new raise. Transcript cr; show: 'after exception (THIS NEVER HAPPENS!!)'. 102] valueOnUnwindDo: [Transcript cr; show: 'unwinding 2...']]. Transcript cr; show: 'value is ' , value printString. value _ [Transcript cr; show: 'case 3: valueNowOrOnUnwind, no exception'. 103] valueNowOrOnUnwindDo: [Transcript cr; show: 'unwinding 3...']. Transcript cr; show: 'value is ' , value printString. value _ Exception handle: [ :exception | nil ] "ignore the reraised exception from the unwind handler" do: [[Transcript cr; show: 'case 4: valueNowOrOnUnwind, exception'. Exception new raise. Transcript cr; show: 'after exception (THIS NEVER HAPPENS!!)'. 104] valueOnUnwindDo: [Transcript cr; show: 'unwinding 4...']]. Transcript cr; show: 'value is ' , value printString. ! !