cfad47cfa3/tads3/vmerr.h
Commiter: Nikos Chantziaras
Author: Nikos Chantziaras
Revision: cfad47cfa3
File Size: 16 KB
(June 01, 2009 20:54 UTC) Almost 3 years ago
Initial commit.
/* $Header: d:/cvsroot/tads/tads3/vmerr.h,v 1.3 1999/05/17 02:52:29 MJRoberts Exp $ */
/*
* Copyright (c) 1998, 2002 Michael J. Roberts. All Rights Reserved.
*
* Please see the accompanying license file, LICENSE.TXT, for information
* on using and copying this software.
*/
/*
Name
vmerr.h - VM exception handling
Function
Defines error macros for try/throw exception handling.
To throw an exception, use err_throw(exception_object), where the
exception_object is an object describing the exception. This object
must be allocated with 'new'. Control will immediately transfer to
the nearest enclosing err_catch() block.
Protected code is defined as shown below. The blocks must occur
in the order shown, but the err_catch and err_finally blocks are
optional (although it would be pointless to have an err_try that
omits both).
err_try
{
// protected code
}
err_catch(exception_variable) // or just err_catch_disc
{
// Exception handler - this code is executed only if an
// exception occurs in the protected block.
//
// If an exception is thrown here (with no nested exception
// handler to catch it), the err_finally block will be executed
// and then the exception will be thrown to the enclosing block.
}
err_finally
{
// Code that is executed regardless whether exception occurs
// or not. If no exception occurs, this code is executed as
// soon as the protected block finishes. If an exception
// occurs, this code is executed, then the exception is
// re-thrown.
//
// If an exception is thrown here, the finally block will
// be aborted and the enclosing error handler will be activated.
// Care should be taken to ensure that code within this block
// is properly protected against exceptions if necessary.
}
err_end;
err_catch automatically defines the given exception_variable as
a local variable of type (CVmException *) in the scope of the
err_catch block. The referenced CVmException object is valid
*only* within the err_catch block; after the err_catch block exits,
the CVmException object will no longer be present.
If you don't need to access the CVmException information, use
err_catch_disc instead of err_catch - this has the same effect as
err_catch, but does not declare a local variable to point to the
exception object.
To re-throw the exception being handled from an err_catch() block,
use err_rethrow().
IMPORTANT: control must NOT be transferred out of an err_try, err_catch,
or err_finally block via break, goto, or return. Actual execution of
the err_end is required in order to properly unwind the error stack.
The easiest way to leave one of these blocks prematurely, if necessary,
is with a goto:
err_try
{
// some code
if (done_with_this_block)
goto done;
// more code
done: ;
}
err_catch_disc
{
// etc...
}
err_end;
Notes
Modified
10/20/98 MJRoberts - Creation
*/
#ifndef VMERR_H
#define VMERR_H
#include <setjmp.h>
#include <stdarg.h>
#include "t3std.h"
#include "vmerrnum.h"
/* ------------------------------------------------------------------------ */
/*
* err_throw() Return Handling
*
* Some compilers (such as MSVC 2007) are capable of doing global
* optimizations that can detect functions that never return. err_throw()
* is one such function: it uses longjmp() to jump out, so it never returns
* to its caller.
*
* Most compilers can't detect this automatically, and C++ doesn't have a
* standard way to declare a function that never returns. So on most
* compilers, the compiler will assume that err_throw() returns, and thus
* will generate a warning if err_throw() isn't followed by some proper
* control flow statement. For example, in a function with a return value,
* a code branch containing an err_throw() would still need a 'return
* <val>' statement - without such a statement, the compiler would generate
* an error about a branch without a value return.
*
* This creates a porting dilemma. On compilers that can detect that
* err_throw() never returns, the presence of any statement in a code
* branch after an err_throw() will cause an "unreachable code" error. For
* all other compilers, the *absence* of such code will often cause a
* different error ("missing return", etc).
*
* The only way I can see to deal with this is to use a compile-time
* #define to select which type of compiler we're using, and use this to
* insert or delete the proper dummy control flow statement after an
* err_throw(). So:
*
* --- INSTRUCTIONS TO BASE CODE DEVELOPERS ---
*
* - after each err_throw() call, if the code branch needs some kind of
* explicit termination (such as a "return val;" statement), code it with
* the AFTER_ERR_THROW() macro. Since err_throw() never *actually*
* returns, these will be dummy statements that will never be reached, but
* the compiler might require their presence anyway because it doesn't know
* better.
*
* --- INSTRUCTIONS TO PORTERS ---
*
* - if your compiler CAN detect that err_throw() never returns, define
* COMPILER_DETECTS_THROW_NORETURN in your compiler command-line options;
*
* - otherwise, leave the symbol undefined.
*/
#ifdef COMPILER_DETECTS_THROW_NORETURN
#define AFTER_ERR_THROW(dummy)
#else
#define AFTER_ERR_THROW(dummy) dummy
#endif
/* ------------------------------------------------------------------------ */
/*
* Error Message Definition structure
*/
struct err_msg_t
{
/* message number */
int msgnum;
/* concise message text */
const char *short_msgtxt;
/* verbose message text */
const char *long_msgtxt;
#ifdef VMERR_BOOK_MSG
const char *book_msgtxt;
#endif
};
/* VM error message array */
extern const err_msg_t *vm_messages;
extern size_t vm_message_count;
/*
* VM error message array - English version. This version of the
* messages is linked directly into the VM; at run time, we can attempt
* to replace this version with another language version obtained from
* an external file. We link in the English version so that we will
* always have a valid set of messages even if the user doesn't have a
* message file installed.
*/
extern const err_msg_t vm_messages_english[];
extern size_t vm_message_count_english;
/* external message file signature */
#define VM_MESSAGE_FILE_SIGNATURE "TADS3.Message.0001\n\r\032"
/*
* load an external message file - returns zero on success, non-zero on
* failure
*/
int err_load_message_file(osfildef *fp,
const err_msg_t **arr, size_t *arr_size,
const err_msg_t *default_arr,
size_t default_arr_size);
/* load default message file */
#define err_load_vm_message_file(fp) \
err_load_message_file((fp), &vm_messages, &vm_message_count, \
vm_messages_english, vm_message_count_english)
/*
* check to see if an external message file has been loaded for the
* default VM message set
*/
int err_is_message_file_loaded();
/*
* delete messages previously loaded with err_load_message_file (this is
* called automatically by err_terminate, so clients generally will not
* need to call this directly)
*/
void err_delete_message_array(const err_msg_t **arr, size_t *arr_size,
const err_msg_t *default_arr,
size_t default_arr_size);
/*
* Search an array of messages for a given message number. The array
* must be sorted by message ID.
*/
const char *err_get_msg(const err_msg_t *msg_array, size_t msg_count,
int msgnum, int verbose);
/*
* Format a message with the parameters contained in an exception
* object. Suports the following format codes:
*
* %s - String. Formats an ERR_TYPE_CHAR, ERR_TYPE_TEXTCHAR, or
* ERR_TYPE_TEXTCHAR_LEN value.
*
* %d, %u, %x - signed/unsigned decimal integer, hexadecimal integer.
* Formats an ERR_TYPE_INT value or an ERR_TYPE_ULONG value.
* Automatically uses the correct size for the argument.
*
* %% - Formats as a single percent sign.
*/
void err_format_msg(char *outbuf, size_t outbuflen,
const char *msg, const struct CVmException *exc);
/*
* exception ID - this identifies an error
*/
typedef uint err_id_t;
/*
* Error parameter type codes
*/
enum err_param_type
{
/* parameter is a native 'int' value */
ERR_TYPE_INT,
/* parameter is a native 'unsigned long' value */
ERR_TYPE_ULONG,
/* parameter is a 'textchar_t *' value (null terminated) */
ERR_TYPE_TEXTCHAR,
/* parameter is a 'char *' value (null terminated) */
ERR_TYPE_CHAR,
/*
* parameter is a 'textchar_t *' value followed by a 'size_t' value
* giving the number of bytes in the string
*/
ERR_TYPE_TEXTCHAR_LEN,
/* parameter is a 'char *' value with a separate length */
ERR_TYPE_CHAR_LEN
};
/*
* Exception parameter
*/
struct CVmExcParam
{
/* type of this parameter */
err_param_type type_;
/* value of the parameter */
union
{
/* as an integer */
int intval_;
/* as an unsigned long */
unsigned long ulong_;
/* as a text string */
const textchar_t *strval_;
/* as a char string */
const char *charval_;
/* as char string with separate length counter */
struct
{
const char *str_;
size_t len_;
} charlenval_;
} val_;
};
/*
* Exception object
*/
struct CVmException
{
/* get the error code */
int get_error_code() const { return error_code_; }
/* get the number of parameters */
int get_param_count() const { return param_count_; }
/* get the type of the nth parameter */
err_param_type get_param_type(int n) const { return params_[n].type_; }
/* get the nth parameter as an integer */
int get_param_int(int n) const { return params_[n].val_.intval_; }
/* get the nth parameter as an unsigned long */
unsigned long get_param_ulong(int n) const
{ return params_[n].val_.ulong_; }
/* get the nth parameter as a string */
const textchar_t *get_param_text(int n) const
{ return params_[n].val_.strval_; }
/* get the nth parameter as a char string */
const char *get_param_char(int n) const
{ return params_[n].val_.charval_; }
/* get the nth parameter as a counted-length string */
const char *get_param_char_len(int n, size_t *len) const
{
/* set the length return */
*len = params_[n].val_.charlenval_.len_;
/* return the string pointer */
return params_[n].val_.charlenval_.str_;
}
/* set a parameter - null-terminated string value */
void set_param_str(int n, const char *str)
{
params_[n].type_ = ERR_TYPE_CHAR;
params_[n].val_.charval_ = str;
}
/* set a parameter - counted-length string value */
void set_param_str(int n, const char *str, size_t len)
{
params_[n].type_ = ERR_TYPE_CHAR_LEN;
params_[n].val_.charlenval_.str_ = str;
params_[n].val_.charlenval_.len_ = len;
}
/* set a parameter - integer value */
void set_param_int(int n, int val)
{
params_[n].type_ = ERR_TYPE_INT;
params_[n].val_.intval_ = val;
}
/* previous exception in the exception stack */
CVmException *prv_;
/* the error code */
err_id_t error_code_;
/* number of parameters stored in the exception */
int param_count_;
/* parameters (actual array size is given by param_cnt_) */
CVmExcParam params_[1];
};
/* error states */
enum err_state_t
{
/* no exception */
ERR_STATE_OKAY = 0,
/* trying */
ERR_STATE_TRYING = 1,
/* exception in progress, and has not been caught */
ERR_STATE_EXCEPTION = 2,
/* exception has been caught */
ERR_STATE_CAUGHT = 3,
/* error thrown while exception handler in progress */
ERR_STATE_RETHROWN = 4
};
/*
* Error frame item - allocated by err_try. This object is not
* manipulated directly by the client; this is handled automatically by
* the error macros.
*/
struct err_frame_t
{
/* enclosing error frame */
err_frame_t *prv_;
/* jmpbuf for this handler */
jmp_buf jmpbuf_;
/* current state */
err_state_t state_;
/*
* Flag: processing the 'finally' clause. If an exception is thrown
* while we're processing this, we will not process the 'finally'
* clause again, nor will we execute the 'catch' clause.
*/
int in_finally_;
};
/*
* Global error context structure
*/
struct err_context_t
{
/* current active error frame */
err_frame_t *cur_frame_;
/* current exception in the exception stack */
CVmException *cur_exc_;
/* next free byte of parameter stack */
char *param_free_;
/* size of parameter stack */
size_t param_stack_size_;
/* parameter stack - actual size given by param_stack_size */
char param_stack_[1];
};
/*
* global static error context
*/
extern err_context_t *G_err;
/* reference count for error context */
extern int G_err_refs;
/*
* Initialize the global error context. Should be called at program
* initialization. 'param_stack_size' is the size in bytes of the error
* parameter stack; this space is used to store all error parameters in
* err_throw_a() calls. Generally, only one or two errors are active at
* a given time, so it should be safe to make this three or four times
* sizeof(CVmException) plus sizeof(CVmExcParam) plus the total
* parameter sizes of the largest parameterized exception codes; if
* still in doubt, one or two kbytes should be adequate in any case.
*/
void err_init(size_t param_stack_size);
/*
* Delete the global error context. Should be called at program
* termination.
*/
void err_terminate();
/*
* Throw an exception. The first form takes no parameters except the
* error code; the second form takes a parameter count followed by that
* number of parameters. Each parameter requires two arguments: the
* first is a type code of type err_param_type, and the second the
* value, whose interpretation depends on the type code.
*/
void err_throw(err_id_t error_code);
void err_throw_a(err_id_t error_code, int param_count, ...);
/*
* Rethrow the current exception. This is valid only in 'catch' blocks.
*/
void err_rethrow();
/* pop the top exception from the exception stack */
void err_pop_exc();
/*
* Serious error - abort program
*/
void err_abort(const char *message);
/*
* determine the error stack depth
*/
int err_stack_depth();
#define err_try \
{ \
err_frame_t err_cur__; \
err_cur__.prv_ = G_err->cur_frame_; \
err_cur__.in_finally_ = FALSE; \
G_err->cur_frame_ = &err_cur__; \
if ((err_cur__.state_ = \
(err_state_t)setjmp(err_cur__.jmpbuf_)) == 0) \
{ \
err_cur__.state_ = ERR_STATE_TRYING;
#define err_catch(exc) \
} \
if (!err_cur__.in_finally_ && \
err_cur__.state_ == ERR_STATE_EXCEPTION) \
{ \
CVmException *exc; \
exc = G_err->cur_exc_; \
err_cur__.state_ = ERR_STATE_CAUGHT;
#define err_catch_disc \
} \
if (!err_cur__.in_finally_ && \
err_cur__.state_ == ERR_STATE_EXCEPTION) \
{ \
err_cur__.state_ = ERR_STATE_CAUGHT;
#define err_finally \
} \
if (!err_cur__.in_finally_) \
{ \
err_cur__.in_finally_ = TRUE;
#define err_end \
} \
G_err->cur_frame_ = err_cur__.prv_; \
if (err_cur__.state_ == ERR_STATE_EXCEPTION \
|| err_cur__.state_ == ERR_STATE_RETHROWN) \
err_rethrow(); \
if (err_cur__.state_ == ERR_STATE_CAUGHT) \
err_pop_exc(); \
}
#endif /* VMERR_H */
|