-
Notifications
You must be signed in to change notification settings - Fork 0
/
exception.h
258 lines (231 loc) · 7.74 KB
/
exception.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/*
* File: exception.h
* Version: 1.0
* Last modified on Sun Jul 24 10:26:20 1994 by eroberts
* -----------------------------------------------------
* The exception package provides a general exception
* handling mechanism for use with C that is portable
* across a variety of compilers and operating systems.
*/
#ifndef _exception_h
#define _exception_h
/*
* Overview:
* --------
* The exception package makes it possible for clients to
* specify a handler for an exceptional conditions in a
* syntactically readable way. As a client, your first step
* is to declare an exception condition name by declaring
* a variable of type exception, as in
*
* exception MyException;
*
* Normal visibility rules apply, so that you should declare
* the exception variable at the appropriate level. For
* example, if an exception is local to an implementation,
* it should be declared statically within that module. If
* an exception condition is shared by many modules, the
* exception variable should be declared in an interface
* and exported to all clients that need it. This package
* defines and exports the exception ErrorException, which
* is likely to be sufficient for many clients.
*
* The basic functionality of exceptions is that one piece
* of code can "raise" an exception so that it can then be
* "handled" by special code in a dynamically enclosing
* section of the program. Exceptions are raised by calling
* the pseudo-function raise with the exception name, as in
*
* raise(MyException);
*
* Exceptions are handled using the "try" statement
* (actually implemented using macros), which has the form:
*
* try {
* . . . statements in the body of the block . . .
* except(exception1)
* . . . statements to handle exception 1 . . .
* except(exception2)
* . . . statements to handle exception 2 . . .
* except(ANY)
* . . . statements to handle any exception . . .
* } endtry
*
* Any number of except clauses may appear (up to a
* maximum defined by the constant MaxExceptionsPerScope),
* and the ANY clause is optional.
*
* When the program encounters the "try" statement, the
* statements in the body are executed. If no exception
* conditions are raised during that execution, either
* in this block or by a function call nested inside
* this block, control passes to the end of the "try"
* statement when the last statement in the block is
* executed. If an exception is raised during the
* dynamic execution of the block, control immediately
* passes to the statements in the appropriate except
* clause. Only the statements in that clause are
* executed; no break statement is required to exit
* the block. If no handler for the raised exception
* appears anywhere in the control history, the program
* exits with an error.
*
* Examples of use:
*
* 1. Catching errors.
*
* The following code fragment traps calls to Error, so
* that the program does not quit but instead returns
* to the top-level read-and-execute loop.
*
* while (TRUE) {
* try {
* printf("> ");
* cmd = ReadCommand();
* ExecuteCommand(cmd);
* except(ErrorException)
* -- additional handling code, if any --
* } endtry
* }
*
* If either ReadCommand or ExecuteCommand calls Error,
* control will be passed back to the main loop, after
* executing any additional handler code. (The error
* message is printed by the Error routine, so it is
* often the case that no additional work is needed.)
*
* 2. Handling control-C
*
* The following code extends the example above so that
* typing ^C also returns to top-level.
*
* #include <signal.h>
*
* static exception ControlCException;
* static int errorCount = 0;
* static int ControlCHandler();
*
* main()
* {
* string cmd;
*
* signal(SIGINT, ControlCHandler);
* while (TRUE) {
* try {
* printf("> ");
* cmd = ReadCommand();
* ExecuteCommand(cmd);
* except(ControlCException);
* printf("^C\n");
* signal(SIGINT, ControlCHandler);
* except(ErrorException)
* errorCount++;
* } endtry
* }
* }
*
* static int ControlCHandler()
* {
* raise(ControlCException);
* }
*/
/*
* Actual interface specification
* ------------------------------
* Most of the implementation of the exception mechanism is
* actually done in the macros defined by this file.
* Clients should ordinarily be able to read the description
* above and ignore the detailed code below.
*/
#include <setjmp.h>
#include <string.h>
#include "genlib.h"
/* Define parameters and error status indicators */
#define MaxExceptionsPerScope 10
#define ETooManyExceptClauses 101
#define EUnhandledException 102
/* Codes to keep track of the state of the try handler */
#define ES_Initialize 0
#define ES_EvalBody 1
#define ES_Exception 2
/*
* Type: exception
* ---------------
* Exceptions are specified by their address, so that the
* actual structure does not matter. Strings are used here
* so that exporters of exceptions can store the exception
* name for the use of debuggers and other tools.
*/
typedef struct { string name; } exception;
/*
* Type: context_block
* -------------------
* This structure is used internally to maintain a chain of
* exception scopes on the control stack.
*/
typedef struct ctx_block {
jmp_buf jmp;
int nx;
exception *array[MaxExceptionsPerScope];
exception *id;
void *value;
string name;
struct ctx_block *link;
} context_block;
/* Declare the built-in exceptions */
extern exception ErrorException;
extern exception ANY;
/* Declare a global pointer to the context stack */
extern context_block *exceptionStack;
/*
* Function: RaiseException
* Usage: RaiseException(&e, name, value);
* ---------------------------------------
* This function is called by the raise macro and does the
* work necessary to raise the exception. See the exception.c file
* for details. Clients do not ordinarily call this directly.
*/
void RaiseException(exception *e, string name, void *value);
/*
* Function: HandlerExists
* Usage: if (HandlerExists(&e)) ...
* ---------------------------------
* Determines whether a handler exists for an exception in
* the dynamically enclosing scope. Intended only for use
* by special clients, such as the Error package.
*/
bool HandlerExists(exception *e);
/* Define the pseudo-functions for raise and try */
#define raise(e) RaiseException(&e, #e, NULL)
#define try \
{ \
jmp_buf _tmpbuf; \
context_block _ctx; \
volatile int _es; \
_es = ES_Initialize; \
_ctx.nx = 0; \
_ctx.link = exceptionStack; \
exceptionStack = (context_block *) &_ctx; \
if (setjmp(_tmpbuf) != 0) _es = ES_Exception; \
memcpy((void *) _ctx.jmp, (void *) _tmpbuf, sizeof(jmp_buf)); \
while (1) { \
if (_es == ES_EvalBody)
#define except(e) \
if (_es == ES_EvalBody) exceptionStack = _ctx.link; \
break; \
} \
if (_es == ES_Initialize) { \
if (_ctx.nx >= MaxExceptionsPerScope) \
exit(ETooManyExceptClauses); \
_ctx.array[_ctx.nx++] = &e; \
} else if (_ctx.id == &e || &e == &ANY) { \
exceptionStack = _ctx.link;
#define endtry \
if (_es != ES_Initialize) break; \
_es = ES_EvalBody; \
} \
}
#define GetExceptionName() _ctx.name
#define GetExceptionValue() _ctx.value
#define GetCurrentException() _ctx.id
#endif