/******************************************************************************
 * $LastChangedDate: 2011-04-23 21:07:07 -0400 (Sat, 23 Apr 2011) $
 * @file
 * @author  Jim E. Brooks  http://www.palomino3d.org
 * @brief   Assertions.
 * @remarks
 * ASSERT() must be a NOP in release builds.
 * Lots of ASSERT() depend on DEBUG_CODE() expanding code.
 *//*
 * LEGAL:   COPYRIGHT (C) 2006 JIM E. BROOKS
 *          THIS SOURCE CODE IS RELEASED UNDER THE TERMS
 *          OF THE GNU GENERAL PUBLIC LICENSE VERSON 2 (GPL 2).
 ******************************************************************************/

#if DEBUG

#include <cstdlib>
#include <sstream>
#define BASE_ASSERT_CC 1
#include "base/module.hh"
#include "base/assert.hh"
#include "base/globals.hh"

namespace base {

/*****************************************************************************
 * Handler for when an assertion fails.
 *****************************************************************************/
AssertException::AssertException( const string& fileName, int fileLine )
{
    std::ostringstream ss;
    ss << "** ASSERTION FAILED **" << std::endl
       << "** " << fileName << ':' << fileLine << " **"; // << std::endl;
    mText = ss.str();
}

AssertException::~AssertException() throw()
{
}

namespace RESTRICT {

void
Assert( bool cond, const char* fileName, int fileLine )
{
#define DUMP_CORE {{ volatile long* p = 0; *p = 0xb00bb00b; }}

    if ( UX(not cond) )
    {
#if OS_FREEBSD
        // Kludge to break to gdb with call stack info on FreeBSD 6.2.
        if ( not gGlobals.mTesting ) { DUMP_CORE; }
#endif
#if DEBUG
        // gdb won't show a call-stack back-trace if an exception is thrown,
        // but will by calling abort().
        if ( gGlobals.mAssertAbort )
            abort();
#endif
        throw AssertException( fileName, fileLine );
    }
}

void
AssertUnlessExiting( bool cond, const char* fileName, int fileLine )
{
    if ( not gGlobals.mExiting )
    {
        Assert( cond, fileName, fileLine );
    }
}

} // namespace RESTRICT

/*****************************************************************************
 * ASSERT_PERSISTENT_OBJECT():
 * Assert that an object is in persistent storage (ie, not on call stack).
 *
 * ASSERT_STACK_OBJECT():
 * Assert that an object is a local object on call stack.
 *
 * Technically, these aren't portable.  But in fact, most OSs place
 * a grow-down stack at the top of the address space of a process.
 *
 * These are real functions, rather than macros, to induce a function call
 * to place the object and a known stack variable in separate stack frames.
 * Otherwise, if both were on the same stack frame, there's no way to predict
 * whether the compiler places one above or below the other.
 *****************************************************************************/
NO_INLINE void
ASSERT_STACK_OBJECT( const void* obj )
{
#if ! OS_CYGWIN

    // Use unsigned when comparing addresses.
    volatile int stackVar;
    ASSERT( reinterpret_cast<unsigned long>(obj) > reinterpret_cast<unsigned long>(&stackVar) );

#endif
}

NO_INLINE void
ASSERT_PERSISTENT_OBJECT( const void* obj )
{
#if 0  // malfunctions with threads
#if ! OS_CYGWIN

    volatile int stackVar;
    ASSERT( reinterpret_cast<unsigned long>(obj) < reinterpret_cast<unsigned long>(&stackVar) );

#endif
#endif // 0
}

} // namespace base

#endif // DEBUG
