// { dg-do run { xfail powerpc-ibm-aix* } }
// { dg-options "-flat_namespace" { target *-*-darwin[67]* } }
// Test __cxa_vec routines
// Copyright (C) 2000, 2005 Free Software Foundation, Inc.
// Contributed by Nathan Sidwell 7 Apr 2000 <nathan@nathan@codesourcery.com>

#if defined (__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100
#include <cxxabi.h>
#include <stdio.h>
#include <new>
#include <stdlib.h>
#include <setjmp.h>

static int ctor_count = 0;
static int dtor_count = 0;
static bool dtor_repeat = false;

// Allocate enough padding to hold an array cookie.
#ifdef __ARM_EABI__
#define padding 8
#else
#define padding (sizeof (std::size_t))
#endif

// our pseudo ctors and dtors
static abi::__cxa_cdtor_return_type ctor (void *x)
{
  if (!ctor_count)
    throw 1;
  ctor_count--;
#ifdef __ARM_EABI__
  return x;
#endif
}

static abi::__cxa_cdtor_return_type dtor (void *x)
{
  if (!dtor_count)
    {
      if (!dtor_repeat)
        dtor_count--;
      throw 1;
    }
  dtor_count--;
#ifdef __ARM_EABI__
  return x;
#endif
}

// track new and delete
static int blocks = 0;
void *operator new[] (std::size_t size) throw (std::bad_alloc)
{
  void *ptr = malloc (size);
  
  if (!ptr)
    throw std::bad_alloc ();
  blocks++;
  return ptr;
}

void operator delete[] (void *ptr) throw ()
{
  if (ptr)
    {
      free (ptr);
      blocks--;
    }
}
static jmp_buf jump;

// allocate and delete an array with no problems
void test0 ()
{
  static bool started = false;
  
  if (!started)
    {
      started = true;
      std::set_terminate (test0);
      
      ctor_count = dtor_count = 5;
      dtor_repeat = false;
      blocks = 0;
      
      try
        {
          void *ary = abi::__cxa_vec_new (5, 1, padding, ctor, dtor);
          abi::__cxa_vec_delete (ary, 1, padding, dtor);
          if (ctor_count || dtor_count || blocks)
            longjmp (jump, 1);
        }
      catch (...)
        {
          longjmp (jump, 2);
        }
    }
  else
    {
      longjmp (jump, 3);
    }
  return;
}

// allocate and delete an array with exception on ctor
void test1 ()
{
  static bool started = false;
  
  if (!started)
    {
      started = true;
      std::set_terminate (test1);
      
      ctor_count = dtor_count = 5;
      dtor_repeat = false;
      blocks = 0;
      
      ctor_count = 4;
      try
        {
          void *ary = abi::__cxa_vec_new (5, 1, padding, ctor, dtor);
          longjmp (jump, 1);
        }
      catch (...)
        {
          // we expect to get here
          if (ctor_count || dtor_count != 1 || blocks)
            longjmp (jump, 2);
        }
    }
  else
    {
      longjmp (jump, 3);
    }
  return;
}

// allocate and delete an array with exception on dtor
void test2 ()
{
  static bool started = false;
  
  if (!started)
    {
      started = true;
      std::set_terminate (test2);
      ctor_count = dtor_count = 5;
      dtor_repeat = false;
      blocks = 0;
      
      dtor_count = 3;
      try
        {
          void *ary = abi::__cxa_vec_new (5, 1, padding, ctor, dtor);
          abi::__cxa_vec_delete (ary, 1, padding, dtor);
          longjmp (jump, 1);
        }
      catch (...)
        {
          // we expect to get here
          if (ctor_count || dtor_count != -2u || blocks)
            longjmp (jump, 2);
        }
    }
  else
    {
      longjmp (jump, 3);
    }
  return;
}

// allocate an array with double exception on dtor
void test3 ()
{
  static bool started = false;
  
  if (!started)
    {
      started = true;
      std::set_terminate (test3);
      
      ctor_count = dtor_count = 5;
      dtor_repeat = false;
      blocks = 0;
  
      dtor_count = 3;
      dtor_repeat = true;
      try
        {
          void *ary = abi::__cxa_vec_new (5, 1, padding, ctor, dtor);
          abi::__cxa_vec_delete (ary, 1, padding, dtor);
          longjmp (jump, 1);
        }
      catch (...)
        {
          // we do not expect to get here
          longjmp (jump, 2);
        }
    }
  else
    {
      // we expect to get here (via terminate)
      if (ctor_count || dtor_count || blocks != 1)
        longjmp (jump, 3);
      longjmp (jump, -1);
    }
  return;
}

// allocate an array with exception on ctor and exception in cleanup
void test4 ()
{
  static bool started = false;
  
  if (!started)
    {
      started = true;
      std::set_terminate (test4);
      
      ctor_count = dtor_count = 5;
      dtor_repeat = false;
      blocks = 0;
  
      ctor_count = 3;
      dtor_count = 2;
      try
        {
          void *ary = abi::__cxa_vec_new (5, 1, padding, ctor, dtor);
          longjmp (jump, 1);
        }
      catch (...)
        {
          // we do not expect to get here
          longjmp (jump, 2);
        }
    }
  else
    {
      // we expect to get here (via terminate)
      if (ctor_count || dtor_count != -1u || blocks != 1)
        longjmp (jump, 3);
      longjmp (jump, -1);
    }
  return;
}

static void (*tests[])() =
{
  test0,
  test1,
  test2,
  test3,
  test4,
  NULL
};

int main ()
{
  int ix;
  int n;
  int errors = 0;
  
  for (ix = 0; tests[ix]; ix++)
    {
      if (n = setjmp (jump))
        {
          if (n > 0)
            {
              printf ("test %d failed %d\n", ix, n);
              errors++;
            }
        }
      else
        tests[ix] ();
    }
  return errors;
}

#else
int main ()
{
  return 0;
}
#endif
