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