Structured exception-handling methods, apparatus, and computer program products6247169Abstract A structured software exception-handling mechanism and method provides an improved paradigm for managing program flow control after error detection without requiring compiler modifications. Selected code is wrapped in one or more exception handling constructs which can be nested. Exceptions are raised and handled in accordance with specified handlers or by a default handler outside the scope of specified exception code wrappers. Claims What is claimed is: Description COPYRIGHTS IN PATENT MATERIALS
int e;
void (*handler) ( int exception_code ) ;
ON_EXCEPTION(e , handler ) {
. . . protected code group one . . .
RAISE_EXCEPTION( v ) ; /* often invoked in
deeply nested calls */
. . . protected code group 2 . . .
}EX_END;
The first expression in the above pseudocode according to the present invention is a variable declaration "int e." The variable "e" is an argument of the ON_EXCEPTION macro. The function "handler" is another argument of the ON_EXCEPTION macro and depends on the integer variable "exception_code." Variable "e" is declared to be an integer variable. The expression "*handler" is a pointer to a function that returns no value. "ON_EXCEPTION" is a function of the integer variable "e" and the handler function. RAISE_EXCEPTION represents specific exception handling code according to the present invention, in accordance with the RAISE_EXCEPTION macro definition in Appendix A. FIG. 1a shows the general flow of a software code construct according to the present invention, for enveloping or wrapping a selected body of code, referred to as protected code. According to a method of the present invention, ON_EXCEPTION code is run 1. Then, a first portion of protected code is run 2. The protected code includes first and second groups of code, e.g., group one and group two. Next, particular RAISE_EXCEPTION code defined by integer "v" is run 3. Next, according to one embodiment of the preset invention, a second group of code which follows raising the exception is run 4. Finally, EX_END code is run 5. Additional embodiments are detailed below and particularly in connection with FIG. 4. The code shown in groups one and two is standard C code including first and second macro expressions "ON_EXCEPTION" and "EX_END." A well-known C preprocessor replaces the macro expressions with code provided in Appendix A. This invention also applies to other kinds of codes including but not limited to C++ or Java. The indicated first and second macro expressions mark the beginning and end of the protected code. Macro EX_END provides code complementary to ON_EXCEPTION to define the end of the software code within the protective exception handler construct in accordance with the present invention. FIG. 1b is a block diagram of a data processing system 15 on which software according to the present invention can be implemented. Data processing system 15 particularly includes an input/output (I/O) section 16a, a central processing unit (CPU) 16b, and a memory section 16c. CPU 16b receives information for data processing from memory section 16c and from input/output section 16a. Connected to input/output section 16a are a keyboard 15a, a monitor 15b, a storage unit 15c, and a drive 15d for operating a medium 15e encoded with a computer program and associated data 15f in accordance with the present invention. Storage unit 15c may be a mass storage unit for data such as a magnetic disk or optical storage device. Drive 15d can be a computer program product reader such as a floppy disk, optical drive, or a CD-ROM drive. Medium 15e can be a magnetic or optical medium for storing information readable by drive 15d. CPU 16b may run an operating system such as a Sun Microsystems Solaris operating system. The operating system can be loaded on memory 16c or storage unit 15c or on a combination thereof, to provide instructions for CPU operations. Software according to the present invention is written on medium 15e according to one embodiment of the present invention. The software is provided to storage unit 15c and/or memory section 16a by inserting medium 15e into drive 15d and installing the software in applicable memory locations of storage unit 15a and/or memory section 16a. FIG. 1c is a system for converting C code 18 into binary code 19" using a C preprocessor 19 and a compiler 19' according to the prior art. Compiler 15 is suitably configured according to well-known techniques to read selected C code according to the present invention which is provided by a user, into memory 16c for preprocessing with C preprocessor 19 and compiling with compiler 19' to produce binary code. FIG. 2 is a block diagram of an exception construct 20 according to one embodiment of the present invention. Exception construct 20 is an embedded-code-enclosing wrapper enveloping the lexical scope 21 of selected software code to be protected. In particular, exception construct 20 is placed around a selected section of software code within which an applicable exception may be raised. The wrapper of exception construct 20 includes an opening expression, "ON_EXCEPTION (e,handler) {"and a closing expression"}EX_END". The opening expression of exception construct 20 is inserted at the beginning of the lexical code scope 21 of the selected software. The closing expression of exception construct 20 is inserted at the end of the lexical code scope 21 of the selected software. The opening expression and the closing expression represent individual macros which are expanded according to one embodiment according to the macro definitions in Appendix A by action of C preprocessor 19 indicated in FIG. 1c. The braces of the opening expression and the closing expression enclose the selected code which is subject to protection according to exception construct 20 of the present invention. FIG. 3 is a diagram showing the scope of an exception construct 30 in accordance with an embodiment of the present invention, including both lexical code scope 31 and dynamic code scope 32. Lexical code scope 31 includes the code scope of a sequence of lines of code as read by a human reviewer including first RAISE_EXCEPTION code 32'. Dynamic code scope 32 includes the sequence of code as actually run during computer processing including both first RAISE_EXCEPTION code 32' and second RAISE_EXCEPTION code 32". Dynamic code scope 32 thus includes lexical code scope 31. The total effective scope of exception construct 30 is accordingly its lexical code scope 31 and the dynamic code scope 32 as bounded by an ON_EXCEPTION expression and its termination at an EX_END expression. The enclosed software code within this dynamic scope responds in predetermined fashion until the raising of a particular exception occurs. Raising an exception means invoking a particular exception handler as specified by a RAISE_EXCEPTION macro. The only restriction on protected code within the lexical or dynamic code scope is that the particular protected code cannot branch out of the established range of the ON_EXCEPTION block and that no called code within the protected block can execute a non-local goto, other than RAISE_EXCEPTION code, according to the present invention. This prevents transfer of control out of the code lexically or dynamically contained within exception construct 30. This restriction is, in practice, not a hindrance because it is in keeping with accepted good practice. Code enclosed within exception construct 30 of FIG. 3 is executed independent of its status of being wrapped within an ON_EXCEPTION block, according to the present invention. In particular, according to the present invention, when computer processing enters an ON_EXCEPTION construct covering selected computer code, the code is executed as though it were not within exception construct 30. Being within exception construct 30 means that determination is made whether an exception is raised within the selected code or within any code called by the selected code. If no exception is raised within the indicated code, then processing within the exception construct is continued and finally ended according to normal code processing of the wrapped code. If an exception is raised and can be handled by the most recently entered exception construct, then the exception handler identified by the exception construct will be invoked. On the other hand, if the exception raised cannot be handled by the most recently entered exception construct, the exception is reraised and a determination is made whether any enclosing active exception construct is available to handle the particular exception. If an active exception construct is available or if the exception raised can be handled by the most recently entered exception construct, then the handler is called with the specified exception code. If there is no active exception construct which can handle the exception, then an unexpected exception is reported according to file number and line number, and execution is terminated. In summary, if an exception is raised anywhere within an enclosed block or within any code called within a protected block, then further execution within the enclosed block will cease, unless the exception is handled by another ON_EXCEPTION block nested within the first ON_EXCEPTION block. The exception mechanism will ascertain whether the specific exception raised can in fact be handled by the handler designated by the ON_EXCEPTION block. If the exception can be handled by the designated handler, that handler is called by the exception code. Otherwise, the exception is automatically re-raised, giving an enclosing ON_EXCEPTION block the opportunity to handle the exception. If the exception does not correspond to kinds of exceptions which can be handled by any active ON_EXCEPTION block, then the default exception handler reports that an UNEXPECTED EXCEPTION occurred during execution at the particular place in the code identifying it by file and line of occurrence and exits. The exception mechanism according to the present invention particularly avoids certain unfortunate code redundancies prevalent in many state-of-the-art software programs. A pseudocode example of such code which is typically repeated in varied forms follows: if( . . . <bad thing> . . . ) { . . . <generate error messages> . . . <set error status values> . . . <cleanup program environment, e.g.: files, etc.> . . . exit(<error status value>); } Such code may reappear multiple times when an exception circumstance is encountered during a conventional program run. The conventional exception code often includes multiple unstructured variations. When such ad hoc code sequences proliferate excessively and unnecessarily in programs and libraries, the resulting code package becomes extremely bulky. The present invention accordingly provides consistency and compactness of a disciplined code structure for handling exceptions, according to the following paradigm: if( . . . <bad thing> . . . ) RAISE_EXCEPTION(VERIFICATION,BadThing); where this statement occurs somewhere within the dynamic scope of: ON_EXCEPTION(VERIFICATION,verify_class_excp_handler){ . . . }EX_END; where VERIFICATION is a class of exception, and BadThing is a constant or an expression representing a specific exception within the VERIFICATION class, or indicating multiple simultaneous exception conditions, such as in the form: (VerBad1.linevert split.VerBad2.linevert split. . . . .linevert split.VerBadN) where VerBad1, VerBad2, et seq. are specific exceptions within the verification class. Use of the indicated exception handling constructs permits elimination of code clutter caused by repetitive lines of ad hoc exception handling code This reduction in code clutter causes increased programmer productivity, in part because the programmer has less lines of code to write. The programmer further has fewer lines to read and scroll through during review or revision. It is further very convenient and practicable to write code with embedded exception handling constructs in accordance with the present invention. This reduces the temptation for a programmer to disregard potential error conditions and "bugs". The exception mechanism according to the present invention further simplifies software production by reporting the file and line of origin of each exception. This allows a programmer rapidly to reach problem code, without having to determine which of many program exits were taken. With possible repetitive exception occurrences in several libraries and software program locations, large numbers of such occurrences are possible, providing multiple potential exit points from a selected program. In some current software code, distinct exit numbers enable tracking the exit taken. According to an embodiment of the present invention, the handling and reporting of exceptions is subject to centralization in a single handler routine, substantially facilitating software coding, maintenance, and review. Further according to one embodiment of the present invention, the grouping of exception reporting and handling at selected central locations is accomplished. A handler according to one embodiment of the present invention includes the following lines:
verify_class_excp_handler(int exception_type) {
switch( exception_type ) {
case VerBad1: . . .
case VerBad2: . . .
. . .
}
}
The exception handler mechanism, according to one embodiment of the present invention, is invoked by executing a statement such as: RAISE_EXCEPTION (VERIFICATION, VerBad2), where the second argument to RAISE_EXCEPTION is a single value rather than a bit mask expression. In the case that the second argument is a bit mask, handler code according to the following embodiment is provided:
verify_class_excp-handler(int exception_set) {
int e;
for ( e = FIRST (VerifyExcps) ;
e<=LAST (VerifyExcps) ; e++ )
if ( member ( e, exception_set ) )
switch ( e ) {
case VerBad1: . . .
case VerBad2: . . .
. . .
}
}
Definitions of particular macros that implement the exception-handling mechanism at run-time, according to an embodiment of the present invention, are shown in Appendix A for a C preprocessor (CPP) which expands selected software code with macro declarations before compilation into machine language. These macros define the key words used to invoke the exception-handling mechanism in writing application programs. These definitions are contained in a header file which, according to the present invention, is named "exceptions.h" and is provided in Appendix A. It is the action of the C preprocessor in expanding these key words according to the definitions in exceptions.h that generates the C code that implements the operations implicit in the use of the exception-handling mechanism. The resulting C code is acceptable to the C compiler, or acceptable upon completion of substitution by the C preprocessor. Thus, implementation of the exception-handling mechanism is achieved with no modifications to the C compiler. The bodies of the CPP definitions of the key words of the exception-handling mechanism internally make use of two additional components described below. The C library primitives setjmp( ) and longjmp( ) are a primitive facility for non-local goto in C programs. These primitives may be implemented as functions, macros, or intrinsics recognizable by the compiler. The setjmp( ) primitive saves its stack and register environment in a data structure called a jmp_buf which occupies programmer supplied storage. These primitives are used by the exception-handling implementation, without the user being aware of the use of setjmp( ) and longjmp( ) in the implementation. A run-time stack of jmp_buf structures is referred to as the exception scope stack. This stack, like the use of setjmp( )/longjmp( ), is hidden from the user of the exception-handling mechanism. The stack discipline for maintaining jmp_buf structures provides the dynamic scoping behavior of the exception-handling mechanism, and permits nesting of those scopes. During the execution of a program using the exception-handling mechanism, entry to any ON_EXCEPTION construct includes a call to setjmp( ) and the subsequent pushing of the resulting jmp_buf structure on the exception scope stack. Exit from the exception construct includes popping the corresponding jmp_buf structure from the exception scope stack. Any RAISE_EXCEPTION operation entails a transfer of control to the environment specified in the dynamically innermost ON_EXCEPTION construct as its exception handling code. This transfer is achieved by popping the topmost jmp_buf structure from the exception scope stack and by performing a longjmp( ) using that jmp_buf as an argument. The process continues until an ON_EXCEPTION construct is encountered that can handle the particular exception value. If none can be found, an "UNHANDLED EXCEPTION" condition is reported. When an ON_EXCEPTION construct is entered 40 according to FIG. 4, a predetermined exception environment is established. If an exception is raised, a determination is made 42 whether the particular exception can be handled by the currently active exception environment. If the exception cannot be handled, the exception is re-raised 42a, and an iterative process determines whether there is any stacked exception environment that can handle the particular exception. If the exception can be handled, an applicable handler is called 43 to process the handling with specified exception code. Upon return from the exception handler, execution is resumed, according to one embodiment, with the first statement following the exception construct that handled the exception. If the exception cannot be handled by any exception environment, an unexpected exception is reported 44 according to file name and line number, an error is reported, and an exit from execution is made 44. The use of line numbers rather than exit numbers is preferred because exit numbers may be inadvertently duplicated, misleading the software engineer as to the location of a defect. Hours and days can be wasted in looking for an error at a location erroneously identified by a duplicate error number. In particular, according to the present invention, a block of code that is entered through ON_EXCEPTION is exited through EX_END. The code in this dynamic scope is protected from the occurrence of the designated exception. A restriction upon the protected code is that it not branch out of the ON_EXCEPTION block and that no called code executes a non-local goto, other then RAISE_EXCEPTION, which would effectively result in a transfer out of the code lexically contained within the ON_EXCEPTION . . . EX_END construct or out of the protected code in the dynamic scope of the construct. In the normal case, the enclosed code will be executed as though it were not wrapped in the exception wrapper. However, if any exception is raised within the enclosed block or within any code it calls, then execution of the enclosed block is interrupted (provided that the exception is not handled by another nested ON_EXCEPTION block). Then, the exception mechanism ascertains whether the specific exception raised can be handled by the handler designated by the ON_EXCEPTION block; if so, that handler is called with the specific exception code; otherwise, the exception is re-raised, giving an enclosing ON_EXCEPTION block the opportunity to handle the exception. If the exception does not correspond to that handled by any active ON_EXCEPTION block, then the default exception handler reports that an UNEXPECTED EXCEPTION occurred at a particular file and line and reports an error and exits. An additional embodiment of the exception handling construct according to the present invention is shown with reference to Appendix D. As shown in the appendix, exceptions are raised within both the lexical as well as within the dynamic scope of the exception handling construct. In particular, a first function call, func1, is made within the lexical scope of a first ON_EXCEPTION construct. Another function call, func2, is made within the scope of the first function call. Three exceptions are raised, one within the scope of the first exception handling construct, another within the first function code and outside the lexical but within the dynamic scope of the first exception handling construct, and a third within the second function code and within the dynamic scope of the first exception handling construct. Notably, the lexical scope of the first exception construct includes a second exception construct nested within it. In fact, the first function call is code encapsulated or wrapped within the second exception construct. Two kinds of exceptions are defined, EXC_SETUP and EXC_ARG; a third exception is undefined, EXC_PROGRAM. Thus, when the EXC_ARG exception is raised in func2, it is handled within the nested, inner exception construct ON_EXCEPTION_H (EXC_ARG, h). When EXC_PROGRAM is raised in func1, an attempt is made at handling the exception within the successive nested exception constructs, but unsuccessfully, because there is no corresponding construct which calls an associated handler. However, in accordance with an embodiment of the present invention, a default exception handling construct is provided in the preprocessable macro INIT_EXCEPTION. One embodiment of the INIT_EXCEPTION macro is expressed in both Appendix A and Appendix E. Its expression in Appendix A is in the form of a macro definition, and its expression in Appendix E is provided as part of an expanded version of an example of preprocessed code provided in Appendix D. The code shown in Appendix E is the result of running the example code of Appendix D through a "C" preprocessor. As shown in FIG. 1a and in Appendix D, ON_EXCEPTION constructs according to the present invention can be nested lexically and dynamically to enable their insertion within the scope of other ON_EXCEPTION constructs. Further, occurrences of RAISE_EXCEPTION can occur within either the dynamic or lexical scope of an ON_EXCEPTION, as shown in FIG. 3. The nearest or innermost layer ON_EXCEPTION which encloses a RAISE_EXCEPTION invocation will field the exception, followed successively by further enclosing ON_EXCEPTION constructs within which the nearest or innermost construct is nested. A topmost initialization of the exception handling mechanism is provided to catch otherwise unhandled exception conditions, as discussed above.
APPENDIX A
MACROS FOR EXCEPTION HANDLING
/*
*
y@ (#) exception.h 1.8 94/05/31 SMI; SunOS CMW
* Name: exception.h
*
* Description: Macros to implement exception handling control
structure
*/
#ifndef _exception_h
#define _exception_h
#include <setjmp.h>
/* exception classes: */
#define EXC_SETUP 1
#define EXC_OPERATION 2
#define EXC_VERIFY 3
#define EXC_LIB 4
#define EXC_TEST_PROG 5
/* SETUP class exception types */
#define SUP_INC_TEST_PARAMS 0x0001 /* incomplete test params */
#define SUP_CONDITION_CLASH 0x0002 /* test conditions clash */
#define SUP_CONDTIION_FAILURE 0x0004 /* can't establish
conditions */
#define SUP_ERROR_DURING_SETUP 0x0008 /* generic setup failure
*/
#define SUP_INVALID_SETUP_PARM 0x0010 /* generic setup failure
*/
/* OPERATION class exception types */
#define OPR_PRIV_BRACKET 0x0001 /* can't set or reset privs
*/
#define OPR_CANT_HAPPEN 0x0002 /* should NEVER happen, but
did*/
/* VERIFY class exception types */
#define VER_NOT_EXPECTED_ERROR 0x0001 /* actual err not exp
err
*/
#define VER_ERROR_NOT_EXPECTED 0x0002 /* no error was expected
*/
#define VER_SUCCESS_NOT_EXPECTED 0x0004 /* success when err expected
*/
#define VER_NOT_EXPECTED_RESULT 0x0008 /* generic result check
fail */
#define VER_ERROR_DURING_VERIFY 0x0010 /* generic can't verify
fail */
/* the following two VER_ provided for completeness, but usually not RAISED
*/
#define VER_ERROR_AS_EXPECTED 0x0020 /* actual err is exp err */
#define VER_SUCCESS_AS_EXPECTED 0x0040 /* no actual err and
none
exp */
#define VER_CANT_RESTORE_STATE 0x0080 /* can't reestablish
original */
#define VER_ERROR_DURING_CLEANUP 0x0100 /* generic can't cleanup
fail */
/* TEST PROGRAM class exception types */
#define TST_PRG_ERROR_DURING_AUX 0x0001 /* generic error during aux
fn */
#define TST_PRG_TEST_CASE_FAILURE 0x0002 /* generic test case fail */
#define EX_CLASS (e) ((e) & 0xFFFF)
#define EX_TYPE (e) ((e) >>16)
#define MAX_ENV_DEPTH 20
int exception_initialized;
int ex_envptr;
jmp_buf ex_envstack [MAX_ENV_DEPTH] ;
char *excp_file;
int excp_line;
int excp_errno;
void excp_gh ( /* int exception */ ) ;
#define INIT_EXCEPTION .backslash.
{ .backslash.
int v; .backslash.
if( v = setjmp (ex_envstack [ex_envptr = 0]) ) { .backslash.
excp_gh (v) ; .backslash.
errno = 0; .backslash.
tprintf (ERROR, "UNHANDLED EXCEPTION (last exception
reported) .backslash.n") ; .backslash.
exit (1) ; .backslash.
} else { .backslash.
ex_envptr++; .backslash.
exception_initialized = 1; .backslash.
} .backslash.
}
#define ON_EXCEPTION_H( e , handler ) .backslash.
for (;;) { .backslash.
int ex_val; .backslash.
if( ex_val = setjmp ( ex_envstack [ex_envptr++] ) ) { .backslash.
if( e == EX_CLASS (ex_val) ) { .backslash.
(*handler) (ex_val) ; .backslash.
break; .backslash.
}else .backslash.
longjmp(ex_envstack [--ex_envptr] ,ex_val) ; .backslash.
}else{
#define ON_EXCEPTION ( e ) .backslash.
for (;;) { .backslash.
int ex_val; .backslash.
if( !(ex_val=setjmp( ex_envstack[ex_envptr++] )) ) {
#define EX_HANDLER .backslash.
}else .backslash.
if( e != EX_CLASS(ex_val) ) .backslash.
longjmp(ex_envstack[--ex_envptr] ,ex_val); .backslash.
else { .backslash.
int ex_type = EX_TYPE(ex_val) ;
#define EX_END .backslash.
} .backslash.
ex_envptr--; break; .backslash.
}
#define RAISE_EXCEPTION ( c ) RAISE_EX_C( c )
#define RAISE_EX_C( c ) .backslash.
{ excp_file = _FILE_; excp_line = _LINE_; .backslash.
excp_errno = errno; .backslash.
longjmp( ex_envstack [--ex_envptr] , c ) ; }
#define RAISE_EX_CT( c, t ) .backslash.
{ excp_file = _FILE_; excp_line = _LINE_; .backslash.
excp_errno = errno; .backslash.
longjmp( ex_envstack [--ex_envptr] , (t<<16) .vertline. c ); }
#endif /* _exception_h */
|
Same subclass Same class Consider this |
||||||||||
