// File:       exception.c++
// Version:    1.00
// Author:     (c) Miles Sabin, 1996
// Purpose:    fake exception handling for Acorn CFront

// Change log:
//  11/10/96   v. 1.00
//  18/01/97   Added thread interface
//  29/01/97   Moved AutoDestory_base here.
//             Modified ExceptionState handling to reduce overhead
//               on a context switch when multi-threading.
//  16/02/97   Removed AutoDestroyer's dependency on __current_context.
//  22/02/97   Bug fix: rethrow behaviour not quite right.
//  25/02/97   Added release_exception().
//   8/03/97   Moved __uncaught_exception to separate translation unit.

#include "exception.h"

#include <stdio.h>
#include <stdlib.h>


// Implementation of exception

exception::exception()
  {}

exception::~exception()
  {}

const char* exception::what() const
  { return 0; }

RTTI_SCAFFOLDING_DEFN_0(exception)


// terminate handler

static void default_terminate_handler()
{
  abort();
}

static terminate_handler current_terminate_handler = default_terminate_handler;

terminate_handler set_terminate(terminate_handler new_handler)
{
  terminate_handler old_handler = current_terminate_handler;
  current_terminate_handler = new_handler;
  return old_handler;
}

void terminate()
{
  current_terminate_handler();
}


// uncaught_exception

bool uncaught_exception()
{
  return ExceptionContext::uncaught_exception();
}


// Implementation of ExceptionState

ExceptionState::ExceptionState()
  : current_context(0),
    current_exception(0),
    current_destroyer(0)
  {}


// Implementation of ExceptionContext

static ExceptionState* current_state = 0;

static ExceptionState* default_state_accessor()
  {
    if(current_state != 0)
      return current_state;

    current_state = new ExceptionState;
    return current_state;
  }

static ExceptionState* (*get_state)() = default_state_accessor;

void ExceptionContext::set_current_state_accessor(exception_state_accessor_t accessor)
{
  get_state = accessor;
}

ExceptionState* ExceptionContext::get_current_state()
{
  return get_state();
}

ExceptionContext::ExceptionContext()
  : uncaught_(true),
    exception_(0),
    next_(0)
  {
    ExceptionState* state = get_state();

    prev_ = state->current_context;
    state->current_context = this;
  }

ExceptionContext::~ExceptionContext()
  {
    ExceptionState* state = get_state();

    if(state->current_context == this)
      state->current_context = prev_;

    if(prev_->next_ == this)
      prev_->next_ = 0;

    delete_pending_exceptions();

    delete exception_;
  }

void ExceptionContext::rethrow_exception()
  {
    exception const* ex = exception_;
    exception_ = 0;

    throw_exception(0, ex);
  }

exception const* ExceptionContext::release_exception()
  {
    exception const* ex = exception_;
    exception_ = 0;

    return ex;
  }

void ExceptionContext::delete_pending_exceptions()
  {
    for(ExceptionContext* next_context = next_; next_context != 0; next_context = next_context->next_)
      delete next_context->exception_;

    next_ = 0;
  }

extern "C" void __uncaught_exception(exception const* current_exception, exception const* previous_exception);

void ExceptionContext::throw_exception(exception const* original, exception const* ex)
{
  if(original != 0)
    original->~exception();

  ExceptionState* state = get_state();
  ExceptionContext* active_context = state->current_context;

  if(ex == 0 || active_context == 0 || (active_context->uncaught_ && active_context->exception_ != 0))
  {
    exception const* active_exception = 0;

    if(active_context == 0)
      // no enclosing try block, so unwind to root
      AutoDestroyer_base::unwind(0);
    else
      // null exception or exception while dispatching exception
      active_exception = active_context->exception_;

    __uncaught_exception(ex, active_exception);
    terminate();
  }

  state->current_exception = ex;
  active_context->exception_ = ex;
  state->current_context = active_context->prev_;

  if(state->current_context != 0)
    state->current_context->next_ = active_context;

  active_context->delete_pending_exceptions();

  AutoDestroyer_base::unwind(active_context);

  longjmp(active_context->jmp_buf_, 1);
}

bool ExceptionContext::uncaught_exception()
{
  ExceptionState* state = get_state();
  return state->current_context != 0 && state->current_context->exception_ != 0;
}


// Implementation of AutoDestroyer_base

AutoDestroyer_base::AutoDestroyer_base()
  {
    ExceptionState* state = get_state();

    context_ = state->current_context;
    prev_ = state->current_destroyer;
    state->current_destroyer = this;
  }

AutoDestroyer_base::~AutoDestroyer_base()
  { get_state()->current_destroyer = prev_; }

void AutoDestroyer_base::unwind(ExceptionContext* c)
{
  ExceptionState* state = get_state();

  while(state->current_destroyer != 0 && state->current_destroyer->context_ == c)
  {
    state->current_destroyer->destroy();
    state->current_destroyer = state->current_destroyer->prev_;
  }
}
