//
// Copyright (C) 1991 Texas Instruments Incorporated.
//
// Permission is granted to any individual or institution to use, copy, modify,
// and distribute this software, provided that this complete copyright and
// permission notice is maintained, intact, in all copies and supporting
// documentation.
//
// Texas Instruments Incorporated provides this software "as is" without
// express or implied warranty.
//
// Created: MJF 07/05/89 -- Initial design and implementation.
// Changed: MJF 02/22/89 -- Added multiple exception type constructor
//
// This file contains  support  for  an  IGNORE_ERRORS macro which will  ignore
// exceptions which are raised while executing  a specified body of statements.
// A specified variable  will be set to  the address  of the exception  raised.
// and program control returns to statment after the IGNORE_ERRORS.
//
// IGNORE_ERRORS(excp_ptr, excp_type, body)  is a macro which  uses  the system
// functions, SETJMP and LONGJMP, and declares an instance of a special purpose
// exception handler, called JUMP_HANDLER.   If an exception  of type EXCP_TYPE
// is  raised while  executing   statements within  BODY,  then   the variable,
// EXCP_PTR, is  set to the  address  of this exception  object just raised and
// program  control  returns   to  statement following  IGNORE_ERRORS.   If  no
// exception is  raised while executing BODY,  then  EXCP_PTR is  set  to NULL.
// Note that  the statements within BODY should  not require  destructors to be
// invoked.  SETJMP/LONGJMP mechanism does not support this.
//
// The Jump_Handler class is derived from the  Excp_Handler class.  It contains
// a  slot, jmpbuf, which  is where  the  current environment is saved when the
// system  function, SETJMP,  is called in   the IGNORE_ERRORS macro.  It  also
// contains a slot for saving a pointer to  the  actual exception object raised
// while executing the statements within the braces of the IGNORE_ERRORS macro.
// It  has a new constructor which  takes  an exception handler function of two
// arguments (a pointer to the raised exception and  a pointer to the exception
// handler which is handling this exception).
//
// The  Jump_Handler function (which is used  by the  IGNORE_ERRORS macro  when
// declaring Jump_Handler) will  handle   the exception  by first    saving the
// pointer  to  the exception  raised in  the   Jump_Handler  object.  And then
// calling  the system function LONGJMP  passing  the  environment saved in the
// Jump_Handler object.
//
// The IGNORE_ERRORS  macro  first  creates  an  instance of   the Jump_Handler
// passing a pointer to the Jump_Handler function  just described and  the type
// of exception  object this handler will  handle.   The exception  type  is an
// optional third argument of the IGNROE_ERRORS  macro and its default value is
// Error.   After declaring an  instance  of Jump_Handler, IGNORE_ERRORS  calls
// SETJMP passing the  environment slot of the  Jump_Handler.  It then executes
// the body of statements within the braces of the IGNORE_ERRORS macro.   If an
// exception of the appropriate type is raised  while  executing the statements
// within body,  the  Jump_Handler (which was  established in this  macro) will
// handle the exception by invoking the  Jump_Handler function described above.
// When the Jump_Handler  function calls LONG_JMP, it  returns to the statement
// after the call of SETJMP.  The pointer to  the exception object  just raised
// was saved in the  Jump_Handler object and  is  set to the  exception pointer
// specified as the second  argument of IGNORE_ERRORS.   If no exception of the
// specified type is  raised while executing the  statements within  body,  the
// exception pointer is set to NULL.

// In the following example, IGNORE_ERRORS is used to detect that an error did
// actually occur while running a test program.
//
//   Vector<int> v2(4, 1);
//   Error* e;
//   IGNORE_ERRORS(v2[5], e);
//   TEST("v2[5]", e->is_type_of(SYM(Error)), TRUE);

#ifndef IGNORE_ERRORSH	// If Ignore_Error has not yet been defined,
#define IGNORE_ERRORSH	// indicate that Ignore_Error has been included.

#ifndef EXCEPTIONH	// If the Exception/Handler object has not been defined,
#include <cool/Exception.h>	// include the Excpetion header file.
#endif

//
// Cfront 2.0 removes the declaration of all unreferenced functions
// On the Sun4, /usr/include/machine/setjmp.h" spits out the following:
// #pragma unknown_control_flow(sigsetjmp, setjmp, _setjmp)
// The compiler complains when sigsetjmp, setjmp, and _setjmp aren't
// declared.  To fix this, we reference these functions here.
// This is tricky, because we can't reference the functions until
// they're declared inside setjmp.h (i.e. until it's too late).
// Because of this, we define a unknown_control_flow macro
// which causes #pragma unknown_control_flow to turn into a junk
// #pragma defmacro, which we delete afterwards.  We are then
// free to reference the functions, and do a correct
// #pragma unknown_control_flow
// All this wouldn't be necessary if Cfront2.0 didn't optimize so much.
//
#if defined __cplusplus && defined(sparc)
#define unknown_control_flow(a,b,c) defmacro ___foo_flow parmtype
#include <setjmp.h>   // for setjmp/longjmp
#undef unknown_control_flow
#undef ___foo_flow
static void* Jump_Junk[] = {sigsetjmp, setjmp, _setjmp};
#pragma unknown_control_flow(sigsetjmp, setjmp, _setjmp)
#elif defined(DOS)
extern "C" {
#include <setjmp.h>   // for setjmp/longjmp
}
#else
#include <setjmp.h>   // for setjmp/longjmp
#endif

// *********************************************************************
// Jump Handler class
// *********************************************************************

typedef void (*Jump_Handler_Function) (Exception*, Excp_Handler*);

class Jump_Handler : public Excp_Handler {
public:
   jmp_buf env;			// the saved environment 
   Exception* raised_excp;      // the exception while running IGNORE_ERRORS
   Jump_Handler();
   Jump_Handler(Jump_Handler_Function, Excp_Type);
   Jump_Handler(Jump_Handler_Function, int, Excp_Type, ...);
   ~Jump_Handler();
   virtual Boolean invoke_handler(Exception*);   
};

extern void ignore_errors_handler(Exception*, Excp_Handler*);

// *********************************************************************
// IGNORE_ERRORS macro
// *********************************************************************

// IGNORE_ERRORS(excp_ptr, excp_type=Error, REST: group_names, BODY: body)
// 
//   where "excp_ptr" is an pointer to an Exception object.
//         "excp_type" is the type of Exception object (ie, Error, Fatal)
//         "group_names" are Symbol* names (ie, SYM(Vector_Error)
//
//   Note that "excp_type" is only optional if no "group_names" are specified.
//   That is, "excp_type" must be specified if "group_names" are specified.
//  
//   If an Exception is raised while evaluating "body" and it
//   is the "desired" type (see below on what this means), then
//   all other exception handlers and default handlers will be ignored and
//   "excp_ptr" is set to the address of the Exception object just raised.
//   If no Exception was detected, "excp_ptr" is set to NULL.
//
//   If "group_names" are specified then these are the "desired" exception
//   types to be ignored.  If "group_names" are not specified, then
//   "excp_type" is the "desired" exception type to be ignored.
//   If no "group_names" and no "excp_type" are specified then "Error"
//   is the "desired" exception type to be ignored.
//   
//   Note:  Does not save return value of last statement in "body".
//          Catches exceptions raised via RAISE() and STOP() only.
//  

MACRO IGNORE_ERRORS(excp_ptr, excp_type=Error, REST: group_names=count,  
                                               BODY: body)
{
  {
#if count
    Jump_Handler jmp_eh(ignore_errors_handler, count, group_names);
#else
    Jump_Handler jmp_eh(ignore_errors_handler, SYM(excp_type));
#endif
    if (setjmp(jmp_eh.env))  // save environment in jump buffer of Jump_Handler
      // If here, an exception was raised (via RAISE() or STOP())
      // while evaluating Body
      // and Jump_Handler handled the exception.
      excp_ptr = (excp_type*) jmp_eh.raised_excp;
    else {
      // evaluate 
      body 
	// if here, no exception of type Excp_Type is raised,
	// set to NULL Exception ptr
	excp_ptr = (excp_type*) 0;
    }
  }
}
         
#endif				// End #ifdef of IGNORE_ERRORSH

