author | Claus Gittinger <cg@exept.de> |
Tue, 14 Nov 1995 20:02:34 +0100 | |
changeset 544 | d78012b20769 |
parent 530 | 07d0bce293c9 |
child 547 | 7772f34ce597 |
permissions | -rw-r--r-- |
1 | 1 |
" |
2 |
COPYRIGHT (c) 1993 by Claus Gittinger |
|
171 | 3 |
All Rights Reserved |
1 | 4 |
|
5 |
This software is furnished under a license and may be used |
|
6 |
only in accordance with the terms of that license and with the |
|
7 |
inclusion of the above copyright notice. This software may not |
|
8 |
be provided or otherwise made available to, or used by, any |
|
9 |
other person. No title to or ownership of the software is |
|
10 |
hereby transferred. |
|
11 |
" |
|
12 |
||
13 |
Object subclass:#Exception |
|
171 | 14 |
instanceVariableNames:'signal parameter errorString |
15 |
suspendedContext handlerContext |
|
362 | 16 |
rejected originator |
171 | 17 |
resumeBlock rejectBlock' |
18 |
classVariableNames:'EmergencyHandler RecursiveExceptionSignal' |
|
19 |
poolDictionaries:'' |
|
20 |
category:'Kernel-Exceptions' |
|
1 | 21 |
! |
22 |
||
12 | 23 |
!Exception class methodsFor:'documentation'! |
24 |
||
88 | 25 |
copyright |
26 |
" |
|
27 |
COPYRIGHT (c) 1993 by Claus Gittinger |
|
171 | 28 |
All Rights Reserved |
88 | 29 |
|
30 |
This software is furnished under a license and may be used |
|
31 |
only in accordance with the terms of that license and with the |
|
32 |
inclusion of the above copyright notice. This software may not |
|
33 |
be provided or otherwise made available to, or used by, any |
|
34 |
other person. No title to or ownership of the software is |
|
35 |
hereby transferred. |
|
36 |
" |
|
37 |
! |
|
38 |
||
39 |
version |
|
530
07d0bce293c9
uff - version methods changed to return stings
Claus Gittinger <cg@exept.de>
parents:
384
diff
changeset
|
40 |
^ '$Header: /cvs/stx/stx/libbasic/Exception.st,v 1.26 1995-11-11 15:22:22 cg Exp $' |
88 | 41 |
! |
42 |
||
12 | 43 |
documentation |
44 |
" |
|
68 | 45 |
Instances of Exception are passed to a Signal handling block as argument. |
46 |
The handler block may perform various actions by sending corresponding messages |
|
142 | 47 |
to the exception object. The following actions are possible: |
1 | 48 |
|
171 | 49 |
reject - dont handle this signal; |
50 |
another handler will be searched for, |
|
51 |
upper in the calling hierarchy |
|
1 | 52 |
|
171 | 53 |
proceed - return from the Signal>>raise, with nil as value |
142 | 54 |
|
171 | 55 |
proceedWith:val - same, but return val from Signal>>raise |
1 | 56 |
|
171 | 57 |
return - return from the Signal>>handle:do:, with nil as value |
142 | 58 |
|
362 | 59 |
returnWith:val - same, but return val from Signal>>handle:do: |
60 |
(this is also the handlers default, |
|
61 |
if it falls through; taking the handlerBlocks value |
|
62 |
as return value) |
|
1 | 63 |
|
171 | 64 |
restart - restart the Signal>>handle:do:, after repairing |
1 | 65 |
|
142 | 66 |
Via the Exception object, the handler can also query the state of execution: |
68 | 67 |
where the Signal was raised, where the handler is, the signal which caused |
142 | 68 |
the error and the errorString passed when the signal was raised. Also, an optional |
69 |
parameter can be passed - the use is signal specific.: |
|
44 | 70 |
|
68 | 71 |
instance variables: |
171 | 72 |
signal <Signal> the signal which caused the exception |
142 | 73 |
|
171 | 74 |
parameter <Object> a parameter (if any) which was passed when raising |
75 |
the signal (only if raised with #raiseWith:aParameter) |
|
142 | 76 |
|
171 | 77 |
errorString <String> an errorString |
78 |
(usually the signals own errorString, but sometimes |
|
79 |
changed explicitely in #raiseWith:errorString:) |
|
44 | 80 |
|
171 | 81 |
suspendedContext <Context> the context in which the raise occured |
142 | 82 |
|
171 | 83 |
handlerContext <Context> the context of the handler (if any) |
142 | 84 |
|
171 | 85 |
resumeBlock <Block> private to the exception; needed to perform resume action |
44 | 86 |
|
171 | 87 |
rejectBlock <Block> private to the exception; needed to perform reject action |
44 | 88 |
|
142 | 89 |
In case of an unhandled signal raise, Exceptions EmergenyHandler will be evaluated. |
68 | 90 |
The default emergeny handler will enter the debugger. |
142 | 91 |
|
68 | 92 |
For applications, which do not want Debuggers to come up, other handlers are |
93 |
possible. |
|
362 | 94 |
For example, to get the typical C++ behavior, use: |
171 | 95 |
Exception emergencyHandler:[:ex | Smalltalk exitWithCoreDump] |
68 | 96 |
|
97 |
Class variables: |
|
171 | 98 |
|
99 |
EmergencyHandler <Block> this block is evaluated, if no handler was defined |
|
100 |
for a signal (i.e. this one is responsible for the |
|
101 |
unhandled exception debugger). |
|
102 |
Having this being a block allows to globally catch |
|
103 |
these errors - even when no enclosing handler-scope |
|
104 |
around the erronous code exists. |
|
105 |
(as the catch/through does). |
|
106 |
||
107 |
RecursiveExceptionSignal |
|
108 |
<Signal> raised when within a handler for some signal, |
|
109 |
th same signal is raised again. |
|
12 | 110 |
" |
111 |
! ! |
|
3 | 112 |
|
171 | 113 |
!Exception class methodsFor:'initialization'! |
114 |
||
115 |
initialize |
|
116 |
"setup the signal used to handle unhandled signals" |
|
117 |
||
118 |
RecursiveExceptionSignal isNil ifTrue:[ |
|
302 | 119 |
RecursiveExceptionSignal := ErrorSignal newSignalMayProceed:false. |
171 | 120 |
RecursiveExceptionSignal nameClass:self message:#recursiveExceptionSignal. |
121 |
RecursiveExceptionSignal notifierString:'recursive signal raise in handler' |
|
122 |
] |
|
123 |
! ! |
|
124 |
||
345 | 125 |
!Exception class methodsFor:'Signal constants'! |
171 | 126 |
|
127 |
recursiveExceptionSignal |
|
128 |
"return the signal used to handle recursive signals in the handlers" |
|
129 |
||
130 |
^ RecursiveExceptionSignal |
|
131 |
! ! |
|
132 |
||
12 | 133 |
!Exception class methodsFor:'defaults'! |
134 |
||
135 |
emergencyHandler |
|
136 |
"return the handler used for unhandled exceptions" |
|
137 |
||
77 | 138 |
" |
139 |
set it up, when called the first time |
|
140 |
" |
|
141 |
EmergencyHandler isNil ifTrue:[ |
|
171 | 142 |
EmergencyHandler := [:ex | |
143 |
" |
|
144 |
sending it to the signal allows per-signal specific |
|
145 |
debuggers to be implemented in the future |
|
146 |
(for example, segv in primitive code could show things |
|
147 |
on the C-level ..) |
|
148 |
" |
|
149 |
(ex signal) enterDebuggerWith:ex message:(ex errorString). |
|
150 |
] |
|
77 | 151 |
]. |
152 |
||
12 | 153 |
^ EmergencyHandler |
154 |
! |
|
155 |
||
130 | 156 |
emergencyHandler:aOneArgBlock |
12 | 157 |
"set the handler used for unhandled exceptions" |
158 |
||
130 | 159 |
EmergencyHandler := aOneArgBlock |
362 | 160 |
|
161 |
"ST-80 behavior of first showing a notifier: |
|
162 |
(I prefer to get right into the debugger, though) |
|
163 |
||
164 |
Exception |
|
165 |
emergencyHandler: |
|
166 |
[:ex | self notify:ex errorString ] |
|
167 |
" |
|
168 |
||
169 |
"automatically aborting current operation, on error: |
|
170 |
(may be useful for end-user apps; make certain, |
|
171 |
you have abortSignal handlers at appropriate places) |
|
172 |
||
173 |
Exception |
|
174 |
emergencyHandler: |
|
175 |
[:ex | Object abortSignal raise. ex return. ] |
|
176 |
" |
|
177 |
||
178 |
"finally, traditional language system behavior; dump core ;-) |
|
179 |
||
180 |
Exception |
|
181 |
emergencyHandler: |
|
182 |
[:ex | Smalltalk exitWithCoreDump. ] |
|
183 |
" |
|
184 |
! ! |
|
185 |
||
186 |
!Exception class methodsFor:'instance creation'! |
|
187 |
||
188 |
signal:aSignal parameter:aParameter errorString:aString suspendedContext:sContext originator:origin |
|
189 |
"create a new instance and set the fields in preparation for a raise. |
|
190 |
- only to be sent from the signal when raising" |
|
191 |
||
192 |
^ (self new) |
|
193 |
signal:aSignal |
|
194 |
parameter:aParameter |
|
195 |
errorString:aString |
|
196 |
suspendedContext:sContext |
|
197 |
originator:origin. |
|
198 |
! |
|
199 |
||
200 |
signal:aSignal parameter:aParameter errorString:aString suspendedContext:sContext |
|
201 |
"create a new instance and set the fields in preparation for a raise. |
|
202 |
- only to be sent from the signal when raising" |
|
203 |
||
204 |
^ (self new) |
|
205 |
signal:aSignal |
|
206 |
parameter:aParameter |
|
207 |
errorString:aString |
|
208 |
suspendedContext:sContext |
|
209 |
originator:(sContext receiver). |
|
12 | 210 |
! ! |
1 | 211 |
|
212 |
!Exception methodsFor:'accessing'! |
|
213 |
||
214 |
signal |
|
215 |
"return the signal, that caused the exception" |
|
216 |
||
217 |
^ signal |
|
218 |
! |
|
219 |
||
44 | 220 |
parameter |
221 |
"return the parameter passsed with the signal raise |
|
222 |
(or nil, if there was none)" |
|
1 | 223 |
|
224 |
^ parameter |
|
225 |
! |
|
226 |
||
44 | 227 |
errorString |
228 |
"return the errorString passsed with the signal raise |
|
229 |
(or nil, if there was none)" |
|
230 |
||
231 |
^ errorString |
|
232 |
! |
|
233 |
||
362 | 234 |
originator |
235 |
"return the originator passsed with the signal raise |
|
236 |
(or nil, if there was none)" |
|
237 |
||
238 |
^ originator |
|
239 |
! |
|
240 |
||
44 | 241 |
handlerContext |
242 |
"return the context of the handler" |
|
243 |
||
244 |
^ handlerContext |
|
245 |
! |
|
246 |
||
1 | 247 |
suspendedContext |
44 | 248 |
"return the context in which the raise occured" |
249 |
||
250 |
^ suspendedContext |
|
68 | 251 |
! |
252 |
||
253 |
rejected |
|
362 | 254 |
"return true, if any other of the exceptions handlers has rejected |
255 |
Uncertain, if this is really interresting to anybody. |
|
256 |
This is only valid during handler execution. |
|
257 |
(i.e. an outer handler can find out, if any other handler has already |
|
258 |
rejected). |
|
259 |
" |
|
68 | 260 |
|
261 |
^ rejected |
|
362 | 262 |
! |
263 |
||
264 |
willProceed |
|
265 |
"return true, if the exception is proceedable" |
|
266 |
||
267 |
^ resumeBlock notNil |
|
1 | 268 |
! ! |
269 |
||
270 |
!Exception methodsFor:'setup'! |
|
271 |
||
362 | 272 |
signal:aSignal parameter:aParameter errorString:aString suspendedContext:sContext originator:origin |
44 | 273 |
"set the fields usable for inspection by the handler |
274 |
- only to be sent from the signal when raising" |
|
1 | 275 |
|
44 | 276 |
signal := aSignal. |
277 |
parameter := aParameter. |
|
278 |
errorString := aString. |
|
279 |
suspendedContext := sContext. |
|
362 | 280 |
originator := origin. |
171 | 281 |
! ! |
282 |
||
217 | 283 |
!Exception methodsFor:'raising'! |
1 | 284 |
|
171 | 285 |
raise |
362 | 286 |
"actually raise a nonproceedable exception. |
287 |
For now, same as #raiseRequest (always proceedable)." |
|
288 |
||
289 |
resumeBlock := [:value | ^ value]. |
|
290 |
^ self evaluateHandler |
|
291 |
! |
|
292 |
||
293 |
raiseRequest |
|
294 |
"actually raise a proceedable exception." |
|
1 | 295 |
|
171 | 296 |
resumeBlock := [:value | ^ value]. |
217 | 297 |
^ self evaluateHandler |
1 | 298 |
! |
299 |
||
171 | 300 |
evaluateHandler |
301 |
"search through the context-calling chain for a 'handle:do:'-context |
|
302 |
to the raising signal a parent of it or a SignalSet which includes |
|
303 |
the raising signal. |
|
304 |
If found, take the contexts 2nd argument (the handler) and evaluate |
|
305 |
it with the receiver exception as argument. |
|
217 | 306 |
If no handler is found, try per signal handler, or |
307 |
per process handler (if its the noHandlerSignal). |
|
308 |
Finally fall back to Exceptions emergencyHandler, which is always |
|
309 |
available and enters the debugger." |
|
171 | 310 |
|
367 | 311 |
|con block noHandlerSignal any msg sel activeHandler| |
171 | 312 |
|
313 |
con := thisContext sender. "the raise-context" |
|
314 |
con := con sender. "the signal raise context" |
|
315 |
con isRecursive ifTrue:[ |
|
316 |
" |
|
317 |
mhmh - an error while in a handler |
|
318 |
" |
|
319 |
((signal == RecursiveExceptionSignal) |
|
320 |
or:[RecursiveExceptionSignal isNil]) ifTrue:[ |
|
321 |
" |
|
322 |
... either while handling RecursiveExceptionSignal |
|
323 |
or at startup when RecursiveExceptionSignal is not yet |
|
324 |
created - |
|
325 |
- go immediately into the debugger. |
|
326 |
" |
|
327 |
^ self enterDebuggerWith:self |
|
328 |
message:'recursive signal raise' |
|
329 |
]. |
|
330 |
^ RecursiveExceptionSignal |
|
331 |
raiseRequestWith:self |
|
332 |
errorString:('recursive signal raise: ' , errorString) |
|
333 |
]. |
|
334 |
||
362 | 335 |
any := false. |
171 | 336 |
[con notNil] whileTrue:[ |
362 | 337 |
con isBlockContext ifFalse:[ |
367 | 338 |
sel := con selector. |
339 |
sel == #doCallHandler: ifTrue:[ |
|
340 |
activeHandler := con args at:1 |
|
341 |
]. |
|
342 |
||
343 |
((sel == #'handle:do:') |
|
344 |
or:[((sel == #'handle:from:do:') |
|
362 | 345 |
and:[(con args at:2) == originator])]) ifTrue:[ |
346 |
" |
|
347 |
if this is the Signal>>handle:do: context |
|
348 |
or a SignalSet>>handle:do: context with self in it, |
|
349 |
call the handler |
|
350 |
" |
|
351 |
(con receiver accepts:signal) ifTrue:[ |
|
352 |
"call the handler" |
|
171 | 353 |
|
367 | 354 |
activeHandler == (con args at:1) ifTrue:[ |
355 |
"/ 'skip active handler:' print. |
|
356 |
"/ con print. ' ' print. con receiver print. |
|
357 |
"/ ' for ' print. signal printNL |
|
358 |
] ifFalse:[ |
|
359 |
handlerContext := con. |
|
360 |
any := true. |
|
361 |
self doCallHandler:(con args at:1). |
|
1 | 362 |
|
367 | 363 |
"if the handler rejects, we arrive here" |
364 |
"continue search for another handler" |
|
365 |
] |
|
362 | 366 |
]. |
367 |
] |
|
171 | 368 |
]. |
369 |
con := con sender |
|
370 |
]. |
|
371 |
||
372 |
" |
|
373 |
we arrive here, if either no handler was found, or none of the |
|
374 |
handlers did a return (i.e. every handler rejected or fell through). |
|
375 |
" |
|
376 |
" |
|
377 |
try per signal handler |
|
378 |
" |
|
379 |
(block := signal handlerBlock) notNil ifTrue:[ |
|
380 |
^ block value:self. |
|
381 |
]. |
|
382 |
||
383 |
" |
|
384 |
if it is not the NoHandlerSignal, raise it ... |
|
213 | 385 |
passing the receiver as parameter. |
171 | 386 |
" |
387 |
signal ~~ (noHandlerSignal := Signal noHandlerSignal) ifTrue:[ |
|
388 |
noHandlerSignal notNil ifTrue:[ |
|
362 | 389 |
any ifTrue:[ |
390 |
msg := 'unhandled (rejected)' |
|
391 |
] ifFalse:[ |
|
392 |
msg := 'unhandled' |
|
393 |
]. |
|
171 | 394 |
^ noHandlerSignal |
395 |
raiseRequestWith:self |
|
362 | 396 |
errorString:(msg , ' exception: ' , errorString) |
255 | 397 |
in:self suspendedContext |
171 | 398 |
]. |
399 |
" |
|
400 |
mhmh - an error during early startup; noHandlerSignal is |
|
401 |
not yet defined. |
|
402 |
" |
|
403 |
^ MiniDebugger enterWithMessage:errorString |
|
404 |
]. |
|
405 |
||
406 |
" |
|
407 |
mhmh - smells like trouble - there is no handler and |
|
408 |
no per-signal handler block. |
|
409 |
Look for either a per-process emergencyHandlerBlock |
|
410 |
or the global emergencyHandler (from Exception) ... |
|
411 |
" |
|
412 |
Processor notNil ifTrue:[ |
|
413 |
"care for signal during startup (Processor not yet created)" |
|
414 |
block := Processor activeProcess emergencySignalHandler. |
|
415 |
]. |
|
416 |
block isNil ifTrue:[ |
|
417 |
block := Exception emergencyHandler |
|
418 |
]. |
|
419 |
block isNil ifTrue:[ |
|
420 |
"care for error during startup (Exception not yet initialized)" |
|
421 |
^ MiniDebugger enterWithMessage:errorString |
|
422 |
]. |
|
423 |
||
424 |
"... and call it" |
|
425 |
^ block value:self. |
|
1 | 426 |
! |
427 |
||
171 | 428 |
doCallHandler:aHandler |
429 |
"call the handler proper - needed an extra method |
|
430 |
to have a separate returnContext for the rejectBlock. |
|
431 |
(which is historical, and actually no longer needed)" |
|
1 | 432 |
|
362 | 433 |
|val| |
434 |
||
171 | 435 |
rejectBlock := [^ self]. "this will return on reject" |
362 | 436 |
val := aHandler value:self. |
437 |
" |
|
438 |
handler fall through - is just like a returnWith:blocks-value |
|
439 |
" |
|
440 |
self returnWith:val |
|
1 | 441 |
! ! |
442 |
||
443 |
!Exception methodsFor:'handler actions'! |
|
444 |
||
445 |
reject |
|
446 |
"handler decided not to handle this signal - |
|
447 |
system will look for another handler" |
|
448 |
||
68 | 449 |
rejected := true. |
1 | 450 |
rejectBlock value |
451 |
! |
|
452 |
||
453 |
resume |
|
454 |
"Continue after the raise - the raise returns nil" |
|
455 |
||
362 | 456 |
resumeBlock notNil ifTrue:[resumeBlock value:nil] |
1 | 457 |
! |
458 |
||
459 |
resumeWith:value |
|
460 |
"Continue after the raise - the raise returns value" |
|
461 |
||
362 | 462 |
resumeBlock notNil ifTrue:[resumeBlock value:value] |
1 | 463 |
! |
464 |
||
465 |
proceed |
|
466 |
"Continue after the raise - the raise returns nil" |
|
467 |
||
362 | 468 |
resumeBlock notNil ifTrue:[resumeBlock value:nil] |
1 | 469 |
! |
470 |
||
471 |
proceedWith:value |
|
472 |
"Continue after the raise - the raise returns value" |
|
473 |
||
362 | 474 |
resumeBlock notNil ifTrue:[resumeBlock value:value] |
1 | 475 |
! |
476 |
||
477 |
return |
|
478 |
"Continue after the handle:do: - the handle:do: returns nil" |
|
479 |
||
68 | 480 |
handlerContext unwind |
1 | 481 |
! |
482 |
||
483 |
returnWith:value |
|
484 |
"Continue after the handle:do: - the handle:do: returns value" |
|
485 |
||
68 | 486 |
handlerContext unwind:value |
1 | 487 |
! |
488 |
||
328 | 489 |
returnDoing:aBlock |
490 |
"Continue after the handle:do: - the handle:do: returns aBlock value" |
|
491 |
||
492 |
handlerContext unwindThenDo:aBlock |
|
493 |
! |
|
494 |
||
1 | 495 |
restart |
496 |
"restart the handle:do: - usually after some repair work is done |
|
497 |
in handler" |
|
498 |
||
249 | 499 |
handlerContext unwindAndRestart |
1 | 500 |
! ! |