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 * Error Message Definition structure 108 */ 109 struct err_msg_t 110 { 111 /* message number */ 112 int msgnum; 113 114 /* concise message text */ 115 const char *short_msgtxt; 116 117 /* verbose message text */ 118 const char *long_msgtxt; 119 }; 120 121 /* VM error message array */ 122 extern const err_msg_t *vm_messages; 123 extern size_t vm_message_count; 124 125 /* 126 * VM error message array - English version. This version of the 127 * messages is linked directly into the VM; at run time, we can attempt 128 * to replace this version with another language version obtained from 129 * an external file. We link in the English version so that we will 130 * always have a valid set of messages even if the user doesn't have a 131 * message file installed. 132 */ 133 extern const err_msg_t vm_messages_english[]; 134 extern size_t vm_message_count_english; 135 136 /* external message file signature */ 137 #define VM_MESSAGE_FILE_SIGNATURE "TADS3.Message.0001\n\r\032" 138 139 140 /* 141 * load an external message file - returns zero on success, non-zero on 142 * failure 143 */ 144 int err_load_message_file(osfildef *fp, 145 const err_msg_t **arr, size_t *arr_size, 146 const err_msg_t *default_arr, 147 size_t default_arr_size); 148 149 /* load default message file */ 150 #define err_load_vm_message_file(fp) \ 151 err_load_message_file((fp), &vm_messages, &vm_message_count, \ 152 vm_messages_english, vm_message_count_english) 153 154 /* 155 * check to see if an external message file has been loaded for the 156 * default VM message set 157 */ 158 int err_is_message_file_loaded(); 159 160 /* 161 * delete messages previously loaded with err_load_message_file (this is 162 * called automatically by err_terminate, so clients generally will not 163 * need to call this directly) 164 */ 165 void err_delete_message_array(const err_msg_t **arr, size_t *arr_size, 166 const err_msg_t *default_arr, 167 size_t default_arr_size); 168 169 170 /* 171 * Search an array of messages for a given message number. The array 172 * must be sorted by message ID. 173 */ 174 const char *err_get_msg(const err_msg_t *msg_array, size_t msg_count, 175 int msgnum, int verbose); 176 177 /* 178 * Format a message with the parameters contained in an exception 179 * object. Suports the following format codes: 180 * 181 * %s - String. Formats an ERR_TYPE_CHAR, ERR_TYPE_TEXTCHAR, or 182 * ERR_TYPE_TEXTCHAR_LEN value. 183 * 184 * %d, %u, %x - signed/unsigned decimal integer, hexadecimal integer. 185 * Formats an ERR_TYPE_INT value or an ERR_TYPE_ULONG value. 186 * Automatically uses the correct size for the argument. 187 * 188 * %% - Formats as a single percent sign. 189 */ 190 void err_format_msg(char *outbuf, size_t outbuflen, 191 const char *msg, const struct CVmException *exc); 192 193 /* 194 * exception ID - this identifies an error 195 */ 196 typedef uint err_id_t; 197 198 /* 199 * Error parameter type codes 200 */ 201 enum err_param_type 202 { 203 /* parameter is a native 'int' value */ 204 ERR_TYPE_INT, 205 206 /* parameter is a native 'unsigned long' value */ 207 ERR_TYPE_ULONG, 208 209 /* parameter is a 'textchar_t *' value (null terminated) */ 210 ERR_TYPE_TEXTCHAR, 211 212 /* parameter is a 'char *' value (null terminated) */ 213 ERR_TYPE_CHAR, 214 215 /* 216 * parameter is a 'textchar_t *' value followed by a 'size_t' value 217 * giving the number of bytes in the string 218 */ 219 ERR_TYPE_TEXTCHAR_LEN, 220 221 /* parameter is a 'char *' value with a separate length */ 222 ERR_TYPE_CHAR_LEN 223 }; 224 225 /* 226 * Exception parameter 227 */ 228 struct CVmExcParam 229 { 230 /* type of this parameter */ 231 err_param_type type_; 232 233 /* value of the parameter */ 234 union 235 { 236 /* as an integer */ 237 int intval_; 238 239 /* as an unsigned long */ 240 unsigned long ulong_; 241 242 /* as a text string */ 243 const textchar_t *strval_; 244 245 /* as a char string */ 246 const char *charval_; 247 248 /* as char string with separate length counter */ 249 struct 250 { 251 const char *str_; 252 size_t len_; 253 } charlenval_; 254 } val_; 255 }; 256 257 /* 258 * Exception object 259 */ 260 struct CVmException 261 { 262 /* get the error code */ get_error_codeCVmException263 int get_error_code() const { return error_code_; } 264 265 /* get the number of parameters */ get_param_countCVmException266 int get_param_count() const { return param_count_; } 267 268 /* get the type of the nth parameter */ get_param_typeCVmException269 err_param_type get_param_type(int n) const { return params_[n].type_; } 270 271 /* get the nth parameter as an integer */ get_param_intCVmException272 int get_param_int(int n) const { return params_[n].val_.intval_; } 273 274 /* get the nth parameter as an unsigned long */ get_param_ulongCVmException275 unsigned long get_param_ulong(int n) const 276 { return params_[n].val_.ulong_; } 277 278 /* get the nth parameter as a string */ get_param_textCVmException279 const textchar_t *get_param_text(int n) const 280 { return params_[n].val_.strval_; } 281 282 /* get the nth parameter as a char string */ get_param_charCVmException283 const char *get_param_char(int n) const 284 { return params_[n].val_.charval_; } 285 286 /* get the nth parameter as a counted-length string */ get_param_char_lenCVmException287 const char *get_param_char_len(int n, size_t *len) const 288 { 289 /* set the length return */ 290 *len = params_[n].val_.charlenval_.len_; 291 292 /* return the string pointer */ 293 return params_[n].val_.charlenval_.str_; 294 } 295 296 297 /* set a parameter - null-terminated string value */ set_param_strCVmException298 void set_param_str(int n, const char *str) 299 { 300 params_[n].type_ = ERR_TYPE_CHAR; 301 params_[n].val_.charval_ = str; 302 } 303 304 /* set a parameter - counted-length string value */ set_param_strCVmException305 void set_param_str(int n, const char *str, size_t len) 306 { 307 params_[n].type_ = ERR_TYPE_CHAR_LEN; 308 params_[n].val_.charlenval_.str_ = str; 309 params_[n].val_.charlenval_.len_ = len; 310 } 311 312 /* set a parameter - integer value */ set_param_intCVmException313 void set_param_int(int n, int val) 314 { 315 params_[n].type_ = ERR_TYPE_INT; 316 params_[n].val_.intval_ = val; 317 } 318 319 /* previous exception in the exception stack */ 320 CVmException *prv_; 321 322 /* the error code */ 323 err_id_t error_code_; 324 325 /* number of parameters stored in the exception */ 326 int param_count_; 327 328 /* parameters (actual array size is given by param_cnt_) */ 329 CVmExcParam params_[1]; 330 }; 331 332 333 /* error states */ 334 enum err_state_t 335 { 336 /* no exception */ 337 ERR_STATE_OKAY = 0, 338 339 /* trying */ 340 ERR_STATE_TRYING = 1, 341 342 /* exception in progress, and has not been caught */ 343 ERR_STATE_EXCEPTION = 2, 344 345 /* exception has been caught */ 346 ERR_STATE_CAUGHT = 3, 347 348 /* error thrown while exception handler in progress */ 349 ERR_STATE_RETHROWN = 4 350 }; 351 352 /* 353 * Error frame item - allocated by err_try. This object is not 354 * manipulated directly by the client; this is handled automatically by 355 * the error macros. 356 */ 357 struct err_frame_t 358 { 359 /* enclosing error frame */ 360 err_frame_t *prv_; 361 362 /* jmpbuf for this handler */ 363 jmp_buf jmpbuf_; 364 365 /* current state */ 366 err_state_t state_; 367 368 /* 369 * Flag: processing the 'finally' clause. If an exception is thrown 370 * while we're processing this, we will not process the 'finally' 371 * clause again, nor will we execute the 'catch' clause. 372 */ 373 int in_finally_; 374 }; 375 376 /* 377 * Global error context structure 378 */ 379 struct err_context_t 380 { 381 /* current active error frame */ 382 err_frame_t *cur_frame_; 383 384 /* current exception in the exception stack */ 385 CVmException *cur_exc_; 386 387 /* next free byte of parameter stack */ 388 char *param_free_; 389 390 /* size of parameter stack */ 391 size_t param_stack_size_; 392 393 /* parameter stack - actual size given by param_stack_size */ 394 char param_stack_[1]; 395 }; 396 397 /* 398 * global static error context 399 */ 400 extern err_context_t *G_err; 401 402 /* reference count for error context */ 403 extern int G_err_refs; 404 405 /* 406 * Initialize the global error context. Should be called at program 407 * initialization. 'param_stack_size' is the size in bytes of the error 408 * parameter stack; this space is used to store all error parameters in 409 * err_throw_a() calls. Generally, only one or two errors are active at 410 * a given time, so it should be safe to make this three or four times 411 * sizeof(CVmException) plus sizeof(CVmExcParam) plus the total 412 * parameter sizes of the largest parameterized exception codes; if 413 * still in doubt, one or two kbytes should be adequate in any case. 414 */ 415 void err_init(size_t param_stack_size); 416 417 /* 418 * Delete the global error context. Should be called at program 419 * termination. 420 */ 421 void err_terminate(); 422 423 /* 424 * Throw an exception. The first form takes no parameters except the 425 * error code; the second form takes a parameter count followed by that 426 * number of parameters. Each parameter requires two arguments: the 427 * first is a type code of type err_param_type, and the second the 428 * value, whose interpretation depends on the type code. 429 */ 430 void err_throw(err_id_t error_code); 431 void err_throw_a(err_id_t error_code, int param_count, ...); 432 433 /* 434 * Rethrow the current exception. This is valid only in 'catch' blocks. 435 */ 436 void err_rethrow(); 437 438 /* pop the top exception from the exception stack */ 439 void err_pop_exc(); 440 441 /* 442 * Serious error - abort program 443 */ 444 void err_abort(const char *message); 445 446 /* 447 * determine the error stack depth 448 */ 449 int err_stack_depth(); 450 451 #define err_try \ 452 { \ 453 err_frame_t err_cur__; \ 454 err_cur__.prv_ = G_err->cur_frame_; \ 455 err_cur__.in_finally_ = FALSE; \ 456 G_err->cur_frame_ = &err_cur__; \ 457 if ((err_cur__.state_ = \ 458 (err_state_t)setjmp(err_cur__.jmpbuf_)) == 0) \ 459 { \ 460 err_cur__.state_ = ERR_STATE_TRYING; 461 462 #define err_catch(exc) \ 463 } \ 464 if (!err_cur__.in_finally_ && \ 465 err_cur__.state_ == ERR_STATE_EXCEPTION) \ 466 { \ 467 CVmException *exc = G_err->cur_exc_; \ 468 err_cur__.state_ = ERR_STATE_CAUGHT; 469 470 #define err_catch_disc \ 471 } \ 472 if (!err_cur__.in_finally_ && \ 473 err_cur__.state_ == ERR_STATE_EXCEPTION) \ 474 { \ 475 err_cur__.state_ = ERR_STATE_CAUGHT; 476 477 478 #define err_finally \ 479 } \ 480 if (!err_cur__.in_finally_) \ 481 { \ 482 err_cur__.in_finally_ = TRUE; 483 484 #define err_end \ 485 } \ 486 G_err->cur_frame_ = err_cur__.prv_; \ 487 if (err_cur__.state_ == ERR_STATE_EXCEPTION \ 488 || err_cur__.state_ == ERR_STATE_RETHROWN) \ 489 err_rethrow(); \ 490 if (err_cur__.state_ == ERR_STATE_CAUGHT) \ 491 err_pop_exc(); \ 492 } 493 494 #endif /* VMERR_H */ 495 496