cfad47cfa3/tads3/vmerr.h

4b825dc642cb6eb9a060e54bf8d69288fbee4904cfad47cfa334b206c65f22086bcc5d63e6f70944
1
/* $Header: d:/cvsroot/tads/tads3/vmerr.h,v 1.3 1999/05/17 02:52:29 MJRoberts Exp $ */
2
3
/* 
4
 *   Copyright (c) 1998, 2002 Michael J. Roberts.  All Rights Reserved.
5
 *   
6
 *   Please see the accompanying license file, LICENSE.TXT, for information
7
 *   on using and copying this software.  
8
 */
9
/*
10
Name
11
  vmerr.h - VM exception handling
12
Function
13
  Defines error macros for try/throw exception handling.
14
15
  To throw an exception, use err_throw(exception_object), where the
16
  exception_object is an object describing the exception.  This object
17
  must be allocated with 'new'.  Control will immediately transfer to
18
  the nearest enclosing err_catch() block.
19
20
  Protected code is defined as shown below.  The blocks must occur
21
  in the order shown, but the err_catch and err_finally blocks are
22
  optional (although it would be pointless to have an err_try that
23
  omits both).
24
25
    err_try
26
    {
27
        // protected code
28
    }
29
    err_catch(exception_variable) // or just err_catch_disc
30
    {
31
        // Exception handler - this code is executed only if an
32
        // exception occurs in the protected block.
33
        //
34
        // If an exception is thrown here (with no nested exception
35
        // handler to catch it), the err_finally block will be executed
36
        // and then the exception will be thrown to the enclosing block.
37
    }
38
    err_finally
39
    {
40
        // Code that is executed regardless whether exception occurs
41
        // or not.  If no exception occurs, this code is executed as
42
        // soon as the protected block finishes.  If an exception
43
        // occurs, this code is executed, then the exception is
44
        // re-thrown.
45
        //
46
        // If an exception is thrown here, the finally block will
47
        // be aborted and the enclosing error handler will be activated.
48
        // Care should be taken to ensure that code within this block
49
        // is properly protected against exceptions if necessary.
50
    }
51
    err_end;
52
53
  err_catch automatically defines the given exception_variable as
54
  a local variable of type (CVmException *) in the scope of the
55
  err_catch block.  The referenced CVmException object is valid
56
  *only* within the err_catch block; after the err_catch block exits,
57
  the CVmException object will no longer be present.
58
59
  If you don't need to access the CVmException information, use
60
  err_catch_disc instead of err_catch - this has the same effect as
61
  err_catch, but does not declare a local variable to point to the
62
  exception object.
63
64
  To re-throw the exception being handled from an err_catch() block,
65
  use err_rethrow().
66
67
  IMPORTANT: control must NOT be transferred out of an err_try, err_catch,
68
  or err_finally block via break, goto, or return.  Actual execution of
69
  the err_end is required in order to properly unwind the error stack.
70
  The easiest way to leave one of these blocks prematurely, if necessary,
71
  is with a goto:
72
73
     err_try
74
     {
75
        // some code
76
77
        if (done_with_this_block)
78
            goto done;
79
80
        // more code
81
82
     done: ;
83
     }
84
     err_catch_disc
85
     {
86
         // etc...
87
     }
88
     err_end;
89
     
90
Notes
91
  
92
Modified
93
  10/20/98 MJRoberts  - Creation
94
*/
95
96
#ifndef VMERR_H
97
#define VMERR_H
98
99
#include <setjmp.h>
100
#include <stdarg.h>
101
102
#include "t3std.h"
103
#include "vmerrnum.h"
104
105
/* ------------------------------------------------------------------------ */
106
/*
107
 *   err_throw() Return Handling
108
 *   
109
 *   Some compilers (such as MSVC 2007) are capable of doing global
110
 *   optimizations that can detect functions that never return.  err_throw()
111
 *   is one such function: it uses longjmp() to jump out, so it never returns
112
 *   to its caller.
113
 *   
114
 *   Most compilers can't detect this automatically, and C++ doesn't have a
115
 *   standard way to declare a function that never returns.  So on most
116
 *   compilers, the compiler will assume that err_throw() returns, and thus
117
 *   will generate a warning if err_throw() isn't followed by some proper
118
 *   control flow statement.  For example, in a function with a return value,
119
 *   a code branch containing an err_throw() would still need a 'return
120
 *   <val>' statement - without such a statement, the compiler would generate
121
 *   an error about a branch without a value return.
122
 *   
123
 *   This creates a porting dilemma.  On compilers that can detect that
124
 *   err_throw() never returns, the presence of any statement in a code
125
 *   branch after an err_throw() will cause an "unreachable code" error.  For
126
 *   all other compilers, the *absence* of such code will often cause a
127
 *   different error ("missing return", etc).
128
 *   
129
 *   The only way I can see to deal with this is to use a compile-time
130
 *   #define to select which type of compiler we're using, and use this to
131
 *   insert or delete the proper dummy control flow statement after an
132
 *   err_throw().  So:
133
 *   
134
 *   --- INSTRUCTIONS TO BASE CODE DEVELOPERS ---
135
 *   
136
 *   - after each err_throw() call, if the code branch needs some kind of
137
 *   explicit termination (such as a "return val;" statement), code it with
138
 *   the AFTER_ERR_THROW() macro.  Since err_throw() never *actually*
139
 *   returns, these will be dummy statements that will never be reached, but
140
 *   the compiler might require their presence anyway because it doesn't know
141
 *   better.
142
 *   
143
 *   --- INSTRUCTIONS TO PORTERS ---
144
 *   
145
 *   - if your compiler CAN detect that err_throw() never returns, define
146
 *   COMPILER_DETECTS_THROW_NORETURN in your compiler command-line options;
147
 *   
148
 *   - otherwise, leave the symbol undefined.  
149
 */
150
#ifdef COMPILER_DETECTS_THROW_NORETURN
151
#define AFTER_ERR_THROW(dummy)
152
#else
153
#define AFTER_ERR_THROW(dummy)   dummy
154
#endif
155
156
157
/* ------------------------------------------------------------------------ */
158
/*
159
 *   Error Message Definition structure 
160
 */
161
struct err_msg_t
162
{
163
    /* message number */
164
    int msgnum;
165
166
    /* concise message text */
167
    const char *short_msgtxt;
168
169
    /* verbose message text */
170
    const char *long_msgtxt;
171
172
#ifdef VMERR_BOOK_MSG
173
    const char *book_msgtxt;
174
#endif
175
};
176
177
/* VM error message array */
178
extern const err_msg_t *vm_messages;
179
extern size_t vm_message_count;
180
181
/* 
182
 *   VM error message array - English version.  This version of the
183
 *   messages is linked directly into the VM; at run time, we can attempt
184
 *   to replace this version with another language version obtained from
185
 *   an external file.  We link in the English version so that we will
186
 *   always have a valid set of messages even if the user doesn't have a
187
 *   message file installed.  
188
 */
189
extern const err_msg_t vm_messages_english[];
190
extern size_t vm_message_count_english;
191
192
/* external message file signature */
193
#define VM_MESSAGE_FILE_SIGNATURE "TADS3.Message.0001\n\r\032"
194
195
196
/*
197
 *   load an external message file - returns zero on success, non-zero on
198
 *   failure 
199
 */
200
int err_load_message_file(osfildef *fp,
201
                          const err_msg_t **arr, size_t *arr_size,
202
                          const err_msg_t *default_arr,
203
                          size_t default_arr_size);
204
205
/* load default message file */
206
#define err_load_vm_message_file(fp) \
207
    err_load_message_file((fp), &vm_messages, &vm_message_count, \
208
                          vm_messages_english, vm_message_count_english)
209
210
/* 
211
 *   check to see if an external message file has been loaded for the
212
 *   default VM message set 
213
 */
214
int err_is_message_file_loaded();
215
216
/* 
217
 *   delete messages previously loaded with err_load_message_file (this is
218
 *   called automatically by err_terminate, so clients generally will not
219
 *   need to call this directly) 
220
 */
221
void err_delete_message_array(const err_msg_t **arr, size_t *arr_size,
222
                              const err_msg_t *default_arr,
223
                              size_t default_arr_size);
224
225
226
/*
227
 *   Search an array of messages for a given message number.  The array
228
 *   must be sorted by message ID.  
229
 */
230
const char *err_get_msg(const err_msg_t *msg_array, size_t msg_count,
231
                        int msgnum, int verbose);
232
233
/*
234
 *   Format a message with the parameters contained in an exception
235
 *   object.  Suports the following format codes:
236
 *   
237
 *   %s - String.  Formats an ERR_TYPE_CHAR, ERR_TYPE_TEXTCHAR, or
238
 *   ERR_TYPE_TEXTCHAR_LEN value.
239
 *   
240
 *   %d, %u, %x - signed/unsigned decimal integer, hexadecimal integer.
241
 *   Formats an ERR_TYPE_INT value or an ERR_TYPE_ULONG value.
242
 *   Automatically uses the correct size for the argument.
243
 *   
244
 *   %% - Formats as a single percent sign.  
245
 */
246
void err_format_msg(char *outbuf, size_t outbuflen,
247
                    const char *msg, const struct CVmException *exc);
248
249
/* 
250
 *   exception ID - this identifies an error 
251
 */
252
typedef uint err_id_t;
253
254
/*
255
 *   Error parameter type codes 
256
 */
257
enum err_param_type
258
{
259
    /* parameter is a native 'int' value */
260
    ERR_TYPE_INT,
261
262
    /* parameter is a native 'unsigned long' value */
263
    ERR_TYPE_ULONG,
264
265
    /* parameter is a 'textchar_t *' value (null terminated) */
266
    ERR_TYPE_TEXTCHAR,
267
268
    /* parameter is a 'char *' value (null terminated) */
269
    ERR_TYPE_CHAR,
270
271
    /* 
272
     *   parameter is a 'textchar_t *' value followed by a 'size_t' value
273
     *   giving the number of bytes in the string 
274
     */
275
    ERR_TYPE_TEXTCHAR_LEN,
276
277
    /* parameter is a 'char *' value with a separate length */
278
    ERR_TYPE_CHAR_LEN
279
};
280
281
/*
282
 *   Exception parameter 
283
 */
284
struct CVmExcParam
285
{
286
    /* type of this parameter */
287
    err_param_type type_;
288
289
    /* value of the parameter */
290
    union
291
    {
292
        /* as an integer */
293
        int intval_;
294
295
        /* as an unsigned long */
296
        unsigned long ulong_;
297
298
        /* as a text string */
299
        const textchar_t *strval_;
300
301
        /* as a char string */
302
        const char *charval_;
303
304
        /* as char string with separate length counter */
305
        struct
306
        {
307
            const char *str_;
308
            size_t len_;
309
        } charlenval_;
310
    } val_;
311
};
312
313
/*
314
 *   Exception object 
315
 */
316
struct CVmException
317
{
318
    /* get the error code */
319
    int get_error_code() const { return error_code_; }
320
321
    /* get the number of parameters */
322
    int get_param_count() const { return param_count_; }
323
324
    /* get the type of the nth parameter */
325
    err_param_type get_param_type(int n) const { return params_[n].type_; }
326
327
    /* get the nth parameter as an integer */
328
    int get_param_int(int n) const { return params_[n].val_.intval_; }
329
330
    /* get the nth parameter as an unsigned long */
331
    unsigned long get_param_ulong(int n) const
332
        { return params_[n].val_.ulong_; }
333
334
    /* get the nth parameter as a string */
335
    const textchar_t *get_param_text(int n) const
336
        { return params_[n].val_.strval_; }
337
338
    /* get the nth parameter as a char string */
339
    const char *get_param_char(int n) const
340
        { return params_[n].val_.charval_; }
341
342
    /* get the nth parameter as a counted-length string */
343
    const char *get_param_char_len(int n, size_t *len) const
344
    {
345
        /* set the length return */
346
        *len = params_[n].val_.charlenval_.len_;
347
348
        /* return the string pointer */
349
        return params_[n].val_.charlenval_.str_;
350
    }
351
352
353
    /* set a parameter - null-terminated string value */
354
    void set_param_str(int n, const char *str)
355
    {
356
        params_[n].type_ = ERR_TYPE_CHAR;
357
        params_[n].val_.charval_ = str;
358
    }
359
360
    /* set a parameter - counted-length string value */
361
    void set_param_str(int n, const char *str, size_t len)
362
    {
363
        params_[n].type_ = ERR_TYPE_CHAR_LEN;
364
        params_[n].val_.charlenval_.str_ = str;
365
        params_[n].val_.charlenval_.len_ = len;
366
    }
367
368
    /* set a parameter - integer value */
369
    void set_param_int(int n, int val)
370
    {
371
        params_[n].type_ = ERR_TYPE_INT;
372
        params_[n].val_.intval_ = val;
373
    }
374
    
375
    /* previous exception in the exception stack */
376
    CVmException *prv_;
377
    
378
    /* the error code */
379
    err_id_t error_code_;
380
381
    /* number of parameters stored in the exception */
382
    int param_count_;
383
384
    /* parameters (actual array size is given by param_cnt_) */
385
    CVmExcParam params_[1];
386
};
387
388
389
/* error states */
390
enum err_state_t
391
{
392
    /* no exception */
393
    ERR_STATE_OKAY = 0,
394
395
    /* trying */
396
    ERR_STATE_TRYING = 1,
397
    
398
    /* exception in progress, and has not been caught */
399
    ERR_STATE_EXCEPTION = 2,
400
    
401
    /* exception has been caught */
402
    ERR_STATE_CAUGHT = 3,
403
404
    /* error thrown while exception handler in progress */
405
    ERR_STATE_RETHROWN = 4
406
};
407
408
/* 
409
 *   Error frame item - allocated by err_try.  This object is not
410
 *   manipulated directly by the client; this is handled automatically by
411
 *   the error macros. 
412
 */
413
struct err_frame_t
414
{
415
    /* enclosing error frame */
416
    err_frame_t *prv_;
417
418
    /* jmpbuf for this handler */
419
    jmp_buf      jmpbuf_;
420
421
    /* current state */
422
    err_state_t  state_;
423
424
    /* 
425
     *   Flag: processing the 'finally' clause.  If an exception is thrown
426
     *   while we're processing this, we will not process the 'finally'
427
     *   clause again, nor will we execute the 'catch' clause. 
428
     */
429
    int          in_finally_;
430
};
431
432
/*
433
 *   Global error context structure
434
 */
435
struct err_context_t
436
{
437
    /* current active error frame */
438
    err_frame_t *cur_frame_;
439
440
    /* current exception in the exception stack */
441
    CVmException *cur_exc_;
442
443
    /* next free byte of parameter stack */
444
    char *param_free_;
445
446
    /* size of parameter stack */
447
    size_t param_stack_size_;
448
449
    /* parameter stack - actual size given by param_stack_size */
450
    char param_stack_[1];
451
};
452
453
/* 
454
 *   global static error context 
455
 */
456
extern err_context_t *G_err;
457
458
/* reference count for error context */
459
extern int G_err_refs;
460
461
/*
462
 *   Initialize the global error context.  Should be called at program
463
 *   initialization.  'param_stack_size' is the size in bytes of the error
464
 *   parameter stack; this space is used to store all error parameters in
465
 *   err_throw_a() calls.  Generally, only one or two errors are active at
466
 *   a given time, so it should be safe to make this three or four times
467
 *   sizeof(CVmException) plus sizeof(CVmExcParam) plus the total
468
 *   parameter sizes of the largest parameterized exception codes; if
469
 *   still in doubt, one or two kbytes should be adequate in any case.  
470
 */
471
void err_init(size_t param_stack_size);
472
473
/*
474
 *   Delete the global error context.  Should be called at program
475
 *   termination. 
476
 */
477
void err_terminate();
478
479
/*
480
 *   Throw an exception.  The first form takes no parameters except the
481
 *   error code; the second form takes a parameter count followed by that
482
 *   number of parameters.  Each parameter requires two arguments: the
483
 *   first is a type code of type err_param_type, and the second the
484
 *   value, whose interpretation depends on the type code.  
485
 */
486
void err_throw(err_id_t error_code);
487
void err_throw_a(err_id_t error_code, int param_count, ...);
488
489
/*
490
 *   Rethrow the current exception.  This is valid only in 'catch' blocks.
491
 */
492
void err_rethrow();
493
494
/* pop the top exception from the exception stack */
495
void err_pop_exc();
496
497
/*
498
 *   Serious error - abort program 
499
 */
500
void err_abort(const char *message);
501
502
/* 
503
 *   determine the error stack depth 
504
 */
505
int err_stack_depth();
506
507
#define err_try \
508
    { \
509
        err_frame_t err_cur__; \
510
        err_cur__.prv_ = G_err->cur_frame_; \
511
        err_cur__.in_finally_ = FALSE; \
512
        G_err->cur_frame_ = &err_cur__; \
513
        if ((err_cur__.state_ = \
514
            (err_state_t)setjmp(err_cur__.jmpbuf_)) == 0) \
515
        { \
516
            err_cur__.state_ = ERR_STATE_TRYING;
517
518
#define err_catch(exc) \
519
        } \
520
        if (!err_cur__.in_finally_ && \
521
            err_cur__.state_ == ERR_STATE_EXCEPTION) \
522
        { \
523
            CVmException *exc; \
524
            exc = G_err->cur_exc_; \
525
            err_cur__.state_ = ERR_STATE_CAUGHT;
526
527
#define err_catch_disc \
528
        } \
529
        if (!err_cur__.in_finally_ && \
530
            err_cur__.state_ == ERR_STATE_EXCEPTION) \
531
        { \
532
           err_cur__.state_ = ERR_STATE_CAUGHT;
533
534
535
#define err_finally \
536
        } \
537
        if (!err_cur__.in_finally_) \
538
        { \
539
            err_cur__.in_finally_ = TRUE;
540
541
#define err_end \
542
        } \
543
        G_err->cur_frame_ = err_cur__.prv_; \
544
        if (err_cur__.state_ == ERR_STATE_EXCEPTION \
545
            || err_cur__.state_ == ERR_STATE_RETHROWN) \
546
            err_rethrow(); \
547
        if (err_cur__.state_ == ERR_STATE_CAUGHT) \
548
            err_pop_exc(); \
549
    }
550
551
#endif /* VMERR_H */
552