Package: src/packages/exceptions.fdoc

Felix RTL Exception Handling.

key file
flx_continuation.hpp share/lib/rtl/flx_continuation.hpp
flx_continuation.cpp share/src/exceptions/flx_continuation.cpp
flx_eh.hpp share/lib/rtl/flx_eh.hpp
flx_eh.cpp share/src/exceptions/flx_eh.cpp
flx_exceptions.hpp share/lib/rtl/flx_exceptions.hpp
flx_exceptions.cpp share/src/exceptions/flx_exceptions.cpp
flx_exceptions_config.hpp share/lib/rtl/flx_exceptions_config.hpp
flx_exceptions.py $PWD/buildsystem/flx_exceptions.py
unix_flx_exceptions.fpc $PWD/src/config/unix/flx_exceptions.fpc
win_flx_exceptions.fpc $PWD/src/config/win/flx_exceptions.fpc

Continuation

Needed here in the exceptions library because it can be thrown as an exception.

//[flx_continuation.hpp]
#ifndef _FLX_CONTINUATION_HPP
#define _FLX_CONTINUATION_HPP
#include "flx_exceptions_config.hpp"
#include "flx_compiler_support_headers.hpp"

// ********************************************************
/// CONTINUATION.
// ********************************************************

namespace flx {namespace rtl {
struct FLX_EXCEPTIONS_EXTERN con_t ///< abstract base for mutable continuations
{
  FLX_PC_DECL               ///< interior program counter
  union svc_req_t *p_svc;           ///< pointer to service request

  con_t();                  ///< initialise pc, p_svc to 0
  virtual con_t *resume()=0;///< method to perform a computational step
  virtual ~con_t();
  con_t * _caller;          ///< callers continuation (return address)
};
}} //namespaces
#endif
//[flx_continuation.cpp]
#include "flx_continuation.hpp"
namespace flx { namespace rtl {
// ********************************************************
// con_t implementation
// ********************************************************

con_t::con_t() : pc(0), p_svc(0), _caller(0) {
#if FLX_DEBUG_CONT
 fprintf(stderr,"Constructing %p\n",this);
#endif
}
con_t::~con_t(){
#if FLX_DEBUG_CONT
  fprintf(stderr,"Destroying %p\n",this);
#endif
}
}} // namespaces

Exceptions

Felix programs can throw a number of exceptions representing errors. Such exceptions cannot be handled. The Felix RTL catches these exceptions, displays a diagnostic, and then terminates the program.

Exceptions thrown in a thread should also terminate the program, that is, the whole containing process and any child processes if the OS supports that.

These exceptions are for fatal errors only.

Internally Felix may throw continuations to unwind the machine stack so that a long jump (non-local goto) can be executed in a top level procedure.

Exception handling is, however, supported in one very special case: it is allowed to directly wrap a C++ primitive function with a try/catch/entry construction. General Felix code can NOT be wrapped because procedures do not use the machine stack to retain continuations (return addresses).

In general C++ style dynamic exception handling is unsafe and should not be used.

//[flx_exceptions.hpp]
#ifndef __FLX_EXCEPTIONS_HPP__
#define __FLX_EXCEPTIONS_HPP__
#include "flx_exceptions_config.hpp"
#include <string>

namespace flx { namespace rtl {
// ********************************************************
// Standard C++ Exceptions
// ********************************************************

struct FLX_EXCEPTIONS_EXTERN flx_exception_t;
struct FLX_EXCEPTIONS_EXTERN flx_out_of_memory_t;
struct FLX_EXCEPTIONS_EXTERN flx_exec_failure_t;
struct FLX_EXCEPTIONS_EXTERN flx_range_srcref_t;
struct FLX_EXCEPTIONS_EXTERN flx_match_failure_t;
struct FLX_EXCEPTIONS_EXTERN flx_assert_failure_t;
struct FLX_EXCEPTIONS_EXTERN flx_assert2_failure_t;
struct FLX_EXCEPTIONS_EXTERN flx_axiom_check_failure_t;
struct FLX_EXCEPTIONS_EXTERN flx_switch_failure_t;
struct FLX_EXCEPTIONS_EXTERN flx_dead_frame_failure_t;
struct FLX_EXCEPTIONS_EXTERN flx_dropthru_failure_t;
struct FLX_EXCEPTIONS_EXTERN flx_link_failure_t;

// ********************************************************
/// EXCEPTION: Felix exception base abstraction.
/// Mainly used to convert catches into subroutine
/// calls which then dispatch on RTTI manually.
// ********************************************************


struct FLX_EXCEPTIONS_EXTERN flx_exception_t {
  virtual ~flx_exception_t()=0;
};

// ********************************************************
/// EXCEPTION: Out of Memory.
/// Thrown when out of memory or memory bound exceeded.
// ********************************************************

struct FLX_EXCEPTIONS_EXTERN flx_out_of_memory_t : flx_exception_t {
  flx_out_of_memory_t();
  virtual ~flx_out_of_memory_t();
};

// ********************************************************
/// EXCEPTION: EXEC protocol failure.
/// Thrown when trying to run a dead procedure
// ********************************************************

struct FLX_EXCEPTIONS_EXTERN flx_exec_failure_t : flx_exception_t {
  ::std::string filename;  ///< dll filename
  ::std::string operation; ///< faulty operation
  ::std::string what;      ///< error description
  flx_exec_failure_t(::std::string f, ::std::string o, ::std::string w);
  virtual ~flx_exec_failure_t();
};

// ********************************************************
/// SOURCE REFERENCE: to track places in user source code.
// ********************************************************

struct FLX_EXCEPTIONS_EXTERN flx_range_srcref_t {
  char const *filename;  ///< source file name
  int startline;   ///< first line (1 origin)
  int startcol;    ///< first column (1 origin)
  int endline;     ///< last line
  int endcol;      ///< last column
  flx_range_srcref_t(char const *f,int sl, int sc, int el, int ec);
  flx_range_srcref_t();
};

// ********************************************************
/// EXCEPTION: HALT.
/// Thrown by halt command
// ********************************************************

struct FLX_EXCEPTIONS_EXTERN flx_halt_t : flx_exception_t {
  ::std::string reason;         ///< halt argument
  flx_range_srcref_t flx_loc; ///< location in Felix file
  char const *cxx_srcfile;          ///< C++ file name
  int cxx_srcline;            ///< C++ line number
  flx_halt_t(flx_range_srcref_t ff, char const *cf, int cl, ::std::string reason);
  virtual ~flx_halt_t();
};

// ********************************************************
/// EXCEPTION: MATCH failure.
/// Thrown when no match cases match the argument of a match,
/// regmatch, or reglex
// ********************************************************

struct FLX_EXCEPTIONS_EXTERN flx_match_failure_t : flx_exception_t {
  flx_range_srcref_t flx_loc; ///< location in Felix file
  char const *cxx_srcfile;          ///< C++ file name
  int cxx_srcline;            ///< C++ line number
  flx_match_failure_t(flx_range_srcref_t ff, char const *cf, int cl);
  virtual ~flx_match_failure_t();
};

// ********************************************************
/// EXCEPTION: DROPTHRU failure.
/// Thrown when function drops off end without returning value
// ********************************************************

struct FLX_EXCEPTIONS_EXTERN flx_dropthru_failure_t : flx_exception_t {
  flx_range_srcref_t flx_loc; ///< location in Felix file
  char const *cxx_srcfile;          ///< C++ file name
  int cxx_srcline;            ///< C++ line number
  flx_dropthru_failure_t(flx_range_srcref_t ff, char const *cf, int cl);
  virtual ~flx_dropthru_failure_t();
};

// ********************************************************
/// EXCEPTION: ASSERT failure.
/// Thrown when user assertion fails
// ********************************************************

struct FLX_EXCEPTIONS_EXTERN flx_assert_failure_t : flx_exception_t {
  flx_range_srcref_t flx_loc; ///< location in Felix file
  char const *cxx_srcfile;          ///< C++ file
  int cxx_srcline;            ///< __LINE__ macro
  flx_assert_failure_t(flx_range_srcref_t ff, char const *cf, int cl);
  virtual ~flx_assert_failure_t();
};

struct FLX_EXCEPTIONS_EXTERN flx_assert2_failure_t : flx_exception_t {
  flx_range_srcref_t flx_loc; ///< location in Felix file
  flx_range_srcref_t flx_loc2; ///< second location in Felix file
  char const *cxx_srcfile;          ///< C++ file
  int cxx_srcline;            ///< __LINE__ macro
  flx_assert2_failure_t(flx_range_srcref_t ff, flx_range_srcref_t ff2, char const *cf, int cl);
  virtual ~flx_assert2_failure_t();
};

struct FLX_EXCEPTIONS_EXTERN flx_axiom_check_failure_t : flx_exception_t {
  flx_range_srcref_t flx_loc; ///< location in Felix file
  flx_range_srcref_t flx_loc2; ///< second location in Felix file
  char const *cxx_srcfile;          ///< C++ file
  int cxx_srcline;            ///< __LINE__ macro
  flx_axiom_check_failure_t (flx_range_srcref_t ff, flx_range_srcref_t ff2, char const *cf, int cl);
  virtual ~flx_axiom_check_failure_t ();
};

// ********************************************************
/// EXCEPTION: RANGE failure.
/// Thrown when a range check fails
// ********************************************************

struct FLX_EXCEPTIONS_EXTERN flx_range_failure_t : flx_exception_t {
  long min; long v; long max;
  flx_range_srcref_t flx_loc; ///< location in Felix file
  char const *cxx_srcfile;          ///< C++ file
  int cxx_srcline;            ///< __LINE__ macro
  flx_range_failure_t(long,long,long,flx_range_srcref_t ff, char const *cf, int cl);
  virtual ~flx_range_failure_t();
};

FLX_EXCEPTIONS_EXTERN long range_check (long l, long x, long h, flx_range_srcref_t sref, char const *cf, int cl);
FLX_EXCEPTIONS_EXTERN void print_loc(FILE *ef,flx_range_srcref_t x,char const *cf, int cl);
FLX_EXCEPTIONS_EXTERN void print_cxxloc(FILE *ef,char const *cf, int cl);


// ********************************************************
/// EXCEPTION: SWITCH failure. this is a system failure!
// ********************************************************

struct FLX_EXCEPTIONS_EXTERN flx_switch_failure_t : flx_exception_t {
  char const *cxx_srcfile;          ///< C++ file
  int cxx_srcline;            ///< __LINE__ macro
  flx_switch_failure_t(char const *cf, int cl);
  virtual ~flx_switch_failure_t();
};


// ********************************************************
/// EXCEPTION: DEAD FRAME failure.
/// Thrown on attempt to resume already returned procedure frame.
// ********************************************************

struct FLX_EXCEPTIONS_EXTERN flx_dead_frame_failure_t : flx_exception_t {
  char const *cxx_srcfile;          ///< C++ file
  int cxx_srcline;            ///< __LINE__ macro
  flx_dead_frame_failure_t(char const *cf, int cl);
  virtual ~flx_dead_frame_failure_t();
};


// ********************************************************
/// EXCEPTION: DYNAMIC LINKAGE failure. this is a system failure!
// ********************************************************

struct FLX_EXCEPTIONS_EXTERN flx_link_failure_t : flx_exception_t {
  ::std::string filename;
  ::std::string operation;
  ::std::string what;
  flx_link_failure_t(::std::string f, ::std::string o, ::std::string w);
  flx_link_failure_t(); // unfortunately this one requires a default ctor.
  virtual ~flx_link_failure_t();
};

}}
#endif
//[flx_exceptions.cpp]
#include <stdio.h>

#include "flx_exceptions.hpp"

namespace flx { namespace rtl {
// ********************************************************
// standard exceptions -- implementation
// ********************************************************
flx_exception_t::~flx_exception_t(){}

flx_exec_failure_t::flx_exec_failure_t(::std::string f, ::std::string o, ::std::string w) :
  filename(f),
  operation(o),
  what(w)
{}

flx_out_of_memory_t::flx_out_of_memory_t(){}
flx_out_of_memory_t::~flx_out_of_memory_t(){}
flx_exec_failure_t::~flx_exec_failure_t(){}

flx_range_srcref_t::flx_range_srcref_t() :
    filename(""),startline(0),startcol(0),endline(0),endcol(0){}
flx_range_srcref_t::flx_range_srcref_t(char const *f,int sl, int sc, int el, int ec) :
    filename(f),startline(sl),startcol(sc),endline(el),endcol(ec){}

flx_halt_t::flx_halt_t(flx_range_srcref_t ff, char const *cf, int cl, ::std::string r) :
   reason(r), flx_loc(ff), cxx_srcfile(cf), cxx_srcline(cl) {}
flx_halt_t::~flx_halt_t(){}

flx_match_failure_t::flx_match_failure_t(flx_range_srcref_t ff, char const *cf, int cl) :
   flx_loc(ff), cxx_srcfile(cf), cxx_srcline(cl) {}
flx_match_failure_t::~flx_match_failure_t(){}

flx_dropthru_failure_t::flx_dropthru_failure_t(flx_range_srcref_t ff, char const *cf, int cl) :
   flx_loc(ff), cxx_srcfile(cf), cxx_srcline(cl) {}
flx_dropthru_failure_t::~flx_dropthru_failure_t(){}

flx_assert_failure_t::flx_assert_failure_t(flx_range_srcref_t ff, char const *cf, int cl) :
   flx_loc(ff), cxx_srcfile(cf), cxx_srcline(cl) {}
flx_assert_failure_t::~flx_assert_failure_t(){}

flx_assert2_failure_t::flx_assert2_failure_t(flx_range_srcref_t ff, flx_range_srcref_t ff2, char const *cf, int cl) :
   flx_loc(ff), flx_loc2(ff2), cxx_srcfile(cf), cxx_srcline(cl) {}
flx_assert2_failure_t::~flx_assert2_failure_t(){}

flx_axiom_check_failure_t::flx_axiom_check_failure_t(flx_range_srcref_t ff, flx_range_srcref_t ff2, char const *cf, int cl) :
   flx_loc(ff), flx_loc2(ff2), cxx_srcfile(cf), cxx_srcline(cl) {}
flx_axiom_check_failure_t::~flx_axiom_check_failure_t(){}

flx_range_failure_t::flx_range_failure_t(long l, long x, long h, flx_range_srcref_t ff, char const *cf, int cl) :
   min(l), v(x), max(h), flx_loc(ff), cxx_srcfile(cf), cxx_srcline(cl) {}
flx_range_failure_t::~flx_range_failure_t(){}

flx_switch_failure_t::~flx_switch_failure_t(){}
flx_switch_failure_t::flx_switch_failure_t (char const *cf, int cl) :
  cxx_srcfile(cf), cxx_srcline (cl) {}

flx_dead_frame_failure_t::~flx_dead_frame_failure_t(){}
flx_dead_frame_failure_t::flx_dead_frame_failure_t(char const *cf, int cl) :
  cxx_srcfile(cf), cxx_srcline (cl) {}


flx_link_failure_t::flx_link_failure_t(::std::string f, ::std::string o, ::std::string w) :
  filename(f),
  operation(o),
  what(w)
{}

flx_link_failure_t::~flx_link_failure_t(){}
flx_link_failure_t::flx_link_failure_t(){}


long range_check (long l, long x, long h, flx_range_srcref_t sref, char const *cf, int cl)
{
  if (x>=l && x<h) return x;
  throw flx::rtl::flx_range_failure_t (l,x,h,sref,cf,cl);
}

void print_cxxloc(FILE *ef,char const *cf, int cl)
{
  fprintf(ef,"C++ location  : %s %d\n", cf, cl);
}

void print_loc(FILE *ef,flx_range_srcref_t x,char const *cf, int cl)
{
  fprintf(ef,"Felix location: %s %d[%d]-%d[%d]\n",
    x.filename,
    x.startline,
    x.startcol,
    x.endline,
    x.endcol
  );
  fprintf(ef,"C++ location  : %s %d\n", cf, cl);
}

}}

Handling Exceptions

These exception handlers are called with standard C++ exceptions or Felix exceptions, decoded as best as possible, an error message printed, and the program terminated.

Note that at the time of writing, exception decoding does not work when using clang 3.3 and the exception is thrown across a DLL boundary. This is a bug in clang handling dynamic_casts across DLL boundaries. Gcc does not have this bug.

//[flx_eh.hpp]
#ifndef __FLX_EH_H__
#define __FLX_EH_H__
#include "flx_rtl_config.hpp"
#include "flx_exceptions.hpp"

namespace flx { namespace rtl {
int FLX_EXCEPTIONS_EXTERN std_exception_handler (::std::exception const *e);
int FLX_EXCEPTIONS_EXTERN flx_exception_handler (::flx::rtl::flx_exception_t const *e);
}}

#endif
//[flx_eh.cpp]
#include <stdio.h>
#include "flx_exceptions.hpp"
#include "flx_eh.hpp"
using namespace ::flx::rtl;


int ::flx::rtl::std_exception_handler (::std::exception const *e)
{
  fprintf(stderr,"C++ STANDARD EXCEPTION %s\n",e->what());
  return 4;
}

int ::flx::rtl::flx_exception_handler (flx_exception_t const *e)
{
fprintf(stderr, "Felix exception handler\n");
  if (flx_halt_t const *x = dynamic_cast<flx_halt_t const*>(e))
  {
    fprintf(stderr,"Halt: %s \n",x->reason.data());
    print_loc(stderr,x->flx_loc,x->cxx_srcfile, x->cxx_srcline);
    return 3;
  }
  if (flx_link_failure_t const *x = dynamic_cast<flx_link_failure_t const*>(e))
  {
    fprintf(stderr,"Dynamic linkage error\n");
    fprintf(stderr,"filename: %s\n",x->filename.data());
    fprintf(stderr,"operation: %s\n",x->operation.data());
    fprintf(stderr,"what: %s\n",x->what.data());
    return 3;
  }
  else
  if (flx_exec_failure_t const *x = dynamic_cast<flx_exec_failure_t const*>(e))
  {
    fprintf(stderr,"Execution error\n");
    fprintf(stderr,"filename: %s\n",x->filename.data());
    fprintf(stderr,"operation: %s\n",x->operation.data());
    fprintf(stderr,"what: %s\n",x->what.data());
    return 3;
  }
  else
  if (flx_assert_failure_t const *x = dynamic_cast<flx_assert_failure_t const*>(e))
  {
    fprintf(stderr,"Assertion Failure\n");
    print_loc(stderr,x->flx_loc,x->cxx_srcfile, x->cxx_srcline);
    return 3;
  }
  else
  if (flx_assert2_failure_t const *x = dynamic_cast<flx_assert2_failure_t const*>(e))
  {
    fprintf(stderr,"Assertion2 Failure\n");
    print_loc(stderr,x->flx_loc,x->cxx_srcfile, x->cxx_srcline);
    print_loc(stderr,x->flx_loc2,x->cxx_srcfile, x->cxx_srcline);
    return 3;
  }
  if (flx_axiom_check_failure_t const *x = dynamic_cast<flx_axiom_check_failure_t const*>(e))
  {
    fprintf(stderr,"Axiom Check Failure\n");
    print_loc(stderr,x->flx_loc,x->cxx_srcfile, x->cxx_srcline);
    print_loc(stderr,x->flx_loc2,x->cxx_srcfile, x->cxx_srcline);
    return 3;
  }
  else
  if (flx_match_failure_t const *x = dynamic_cast<flx_match_failure_t const*>(e))
  {
    fprintf(stderr,"Match Failure\n");
    print_loc(stderr,x->flx_loc,x->cxx_srcfile, x->cxx_srcline);
    return 3;
  }
  else
  if (flx_switch_failure_t const *x = dynamic_cast<flx_switch_failure_t const*>(e))
  {
    fprintf(stderr,"Attempt to switch to non-existant case\n");
    print_cxxloc(stderr,x->cxx_srcfile, x->cxx_srcline);
    return 3;
  }
  if (flx_dead_frame_failure_t const *x = dynamic_cast<flx_dead_frame_failure_t const*>(e))
  {
    fprintf(stderr,"Attempt to resume non-live procedure frame\n");
    print_cxxloc(stderr,x->cxx_srcfile, x->cxx_srcline);
    return 3;
  }
  else
  if (flx_dropthru_failure_t const *x = dynamic_cast<flx_dropthru_failure_t const*>(e))
  {
    fprintf(stderr,"Function Drops Off End Failure\n");
    print_loc(stderr,x->flx_loc,x->cxx_srcfile, x->cxx_srcline);
    return 3;
  }
  else
  if (flx_range_failure_t const *x = dynamic_cast<flx_range_failure_t const*>(e))
  {
    fprintf(stderr,"Range Check Failure %ld <= %ld < %ld\n",x->min, x->v,x->max);
    print_loc(stderr,x->flx_loc,x->cxx_srcfile, x->cxx_srcline);
    return 3;
  }
  else
  if (dynamic_cast<flx_out_of_memory_t const*>(e))
  {
    fprintf(stderr,"Felix Out of Malloc or Specified Max allocation Exceeded");
    return 3;
  }
  else
  {
    fprintf(stderr,"Unknown Felix EXCEPTION!\n");
    return 5;
  }
}
//[flx_exceptions_config.hpp]
#ifndef __FLX_EXCEPTIONS_CONFIG_H__
#define __FLX_EXCEPTIONS_CONFIG_H__
#include "flx_rtl_config.hpp"
#ifdef BUILD_FLX_EXCEPTIONS
#define FLX_EXCEPTIONS_EXTERN FLX_EXPORT
#else
#define FLX_EXCEPTIONS_EXTERN FLX_IMPORT
#endif
#endif
//[unix_flx_exceptions.fpc]
Name: flx_exceptions
Description: Felix exceptions
provides_dlib: -lflx_exceptions_dynamic
provides_slib: -lflx_exceptions_static
library: flx_exceptions
macros: BUILD_FLX_EXCEPTIONS
includes: '"flx_exceptions.hpp"'
srcdir: src/exceptions
src: .*\.cpp
//[win_flx_exceptions.fpc]
Name: flx
Description: Felix exceptions
provides_dlib: /DEFAULTLIB:flx_exceptions_dynamic
provides_slib: /DEFAULTLIB:flx_exceptions_static
library: flx_exceptions
macros: BUILD_FLX_EXCEPTIONS
includes: '"flx_exceptions.hpp"'
srcdir: src/exceptions
src: .*\.cpp
#[flx_exceptions.py]
import fbuild
from fbuild.path import Path
from fbuild.record import Record
from fbuild.builders.file import copy

import buildsystem

# ------------------------------------------------------------------------------

def build_runtime(phase):
    print('[fbuild] [rtl] build exceptions')
    path = Path(phase.ctx.buildroot/'share'/'src/exceptions')

    srcs = [
     path / 'flx_continuation.cpp',
     path / 'flx_exceptions.cpp',
     path / 'flx_eh.cpp',
     ]
    includes = [phase.ctx.buildroot / 'host/lib/rtl', phase.ctx.buildroot / 'share/lib/rtl']
    macros = ['BUILD_FLX_EXCEPTIONS']

    dst = 'host/lib/rtl/flx_exceptions'
    return Record(
        static=buildsystem.build_cxx_static_lib(phase, dst, srcs,
            includes=includes,
            macros=macros),
        shared=buildsystem.build_cxx_shared_lib(phase, dst, srcs,
            includes=includes,
            macros=macros))