include/SDL_assert.h
author David Ludwig <dludwig@pobox.com>
Sat, 29 Nov 2014 10:09:30 -0500
changeset 9247 eddb899239fe
parent 9019 23e35485dae3
child 9371 375799828431
permissions -rw-r--r--
WinRT: bug and data-integrity fixes for SDL_GetPrefPath()

This change does a few things, all with regards to the WinRT implementation of
SDL_GetPrefPath():

1. it fixes a bug whereby SDL_GetPrefPath() did not create the directory it
returned. On other SDL platforms, SDL_GetPrefPath() will create separate
directories for its 'org' and 'app' folders. Without this, attempts to create
files in the pref-path would fail, unless those directories were first created
by the app, or by some other library the app used. This change makes sure
that these directories get created, before SDL_GetPrefPath() returns to its
caller(s).


2. it defaults to having SDL_GetPrefPath() return a WinRT 'Local' folder
on all platforms. Previously, for Windows Store apps, it would have used a
different, 'Roaming' folder. Files in Roaming folders can be automatically,
and synchronized across multiple devices by Windows. This synchronization can
happen while the app runs, with new files being copied into a running app's
pref-path. Unless an app is specifically designed to handle this scenario,
there is a chance that save-data could be overwritten in unwanted or
unexpected ways.

The default is now to use a Local folder, which does not get synchronized, and
which is arguably a bit safer to use. Apps that wish to use Roaming folders
can do so by setting SDL_HINT_WINRT_PREF_PATH_ROOT to "roaming", however it
is recommended that one first read Microsoft's documentation for Roaming
files, a link to which is provided in README-winrt.md.

To preserve older pref-path selection behavior (found in SDL 2.0.3, as well as
many pre-2.0.4 versions of SDL from hg.libsdl.org), which uses a Roaming path
in Windows Store apps, and a Local path in Windows Phone, set
SDL_HINT_WINRT_PREF_PATH_ROOT to "old".

Please note that Roaming paths are not supported on Windows Phone 8.0, due to
limitations in the OS itself. Attempts to use this will fail.
(Windows Phone 8.1 does not have this limitation, however.)


3. It makes SDL_GetPrefPath(), when on Windows Phone 8.0, and when
SDL_HINT_WINRT_PREF_PATH_ROOT is set to "roaming", return NULL, rather than
silently defaulting to a Local path (then switching to a Roaming path if and
when the user upgraded to Windows Phone 8.1).
slouken@3647
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@8149
     3
  Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
slouken@3647
     4
slouken@5535
     5
  This software is provided 'as-is', without any express or implied
slouken@5535
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@5535
     7
  arising from the use of this software.
slouken@3647
     8
slouken@5535
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@5535
    10
  including commercial applications, and to alter it and redistribute it
slouken@5535
    11
  freely, subject to the following restrictions:
slouken@3647
    12
slouken@5535
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@5535
    14
     claim that you wrote the original software. If you use this software
slouken@5535
    15
     in a product, an acknowledgment in the product documentation would be
slouken@5535
    16
     appreciated but is not required.
slouken@5535
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@5535
    18
     misrepresented as being the original software.
slouken@5535
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@3647
    20
*/
slouken@3647
    21
slouken@3647
    22
#ifndef _SDL_assert_h
slouken@3647
    23
#define _SDL_assert_h
slouken@3647
    24
slouken@3655
    25
#include "SDL_config.h"
slouken@3655
    26
slouken@3655
    27
#include "begin_code.h"
slouken@3655
    28
/* Set up for C function definitions, even when using C++ */
slouken@3655
    29
#ifdef __cplusplus
slouken@3655
    30
extern "C" {
slouken@3655
    31
#endif
slouken@3655
    32
slouken@3647
    33
#ifndef SDL_ASSERT_LEVEL
slouken@3654
    34
#ifdef SDL_DEFAULT_ASSERT_LEVEL
slouken@3654
    35
#define SDL_ASSERT_LEVEL SDL_DEFAULT_ASSERT_LEVEL
slouken@3654
    36
#elif defined(_DEBUG) || defined(DEBUG) || \
slouken@3654
    37
      (defined(__GNUC__) && !defined(__OPTIMIZE__))
slouken@3653
    38
#define SDL_ASSERT_LEVEL 2
slouken@3653
    39
#else
slouken@3653
    40
#define SDL_ASSERT_LEVEL 1
slouken@3647
    41
#endif
slouken@3653
    42
#endif /* SDL_ASSERT_LEVEL */
slouken@3647
    43
slouken@3647
    44
/*
slouken@3655
    45
These are macros and not first class functions so that the debugger breaks
slouken@3655
    46
on the assertion line and not in some random guts of SDL, and so each
icculus@3665
    47
assert can have unique static variables associated with it.
slouken@3655
    48
*/
slouken@3655
    49
icculus@6430
    50
#if defined(_MSC_VER)
slouken@5491
    51
/* Don't include intrin.h here because it contains C++ code */
icculus@6430
    52
    extern void __cdecl __debugbreak(void);
slouken@5011
    53
    #define SDL_TriggerBreakpoint() __debugbreak()
gabomdq@8833
    54
#elif (!defined(__NACL__) && defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__)))
slouken@3655
    55
    #define SDL_TriggerBreakpoint() __asm__ __volatile__ ( "int $3\n\t" )
slouken@3655
    56
#elif defined(HAVE_SIGNAL_H)
slouken@3655
    57
    #include <signal.h>
slouken@3655
    58
    #define SDL_TriggerBreakpoint() raise(SIGTRAP)
slouken@3655
    59
#else
slouken@3655
    60
    /* How do we trigger breakpoints on this platform? */
slouken@3655
    61
    #define SDL_TriggerBreakpoint()
slouken@3655
    62
#endif
slouken@3655
    63
icculus@5552
    64
#if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 supports __func__ as a standard. */
slouken@3655
    65
#   define SDL_FUNCTION __func__
slouken@3655
    66
#elif ((__GNUC__ >= 2) || defined(_MSC_VER))
slouken@3655
    67
#   define SDL_FUNCTION __FUNCTION__
slouken@3655
    68
#else
slouken@3655
    69
#   define SDL_FUNCTION "???"
slouken@3655
    70
#endif
slouken@3655
    71
#define SDL_FILE    __FILE__
slouken@3655
    72
#define SDL_LINE    __LINE__
slouken@3655
    73
slouken@3655
    74
/*
slouken@3647
    75
sizeof (x) makes the compiler still parse the expression even without
slouken@3647
    76
assertions enabled, so the code is always checked at compile time, but
slouken@3647
    77
doesn't actually generate code for it, so there are no side effects or
slouken@3647
    78
expensive checks at run time, just the constant size of what x WOULD be,
slouken@3647
    79
which presumably gets optimized out as unused.
slouken@3647
    80
This also solves the problem of...
slouken@3647
    81
slouken@3647
    82
    int somevalue = blah();
slouken@3647
    83
    SDL_assert(somevalue == 1);
slouken@3647
    84
slouken@3647
    85
...which would cause compiles to complain that somevalue is unused if we
slouken@3647
    86
disable assertions.
slouken@3647
    87
*/
slouken@3647
    88
icculus@7736
    89
#ifdef _MSC_VER  /* stupid /W4 warnings. */
icculus@7736
    90
#define SDL_NULL_WHILE_LOOP_CONDITION (-1 == __LINE__)
icculus@7736
    91
#else
icculus@7736
    92
#define SDL_NULL_WHILE_LOOP_CONDITION (0)
icculus@7736
    93
#endif
icculus@7736
    94
slouken@3647
    95
#define SDL_disabled_assert(condition) \
icculus@7736
    96
    do { (void) sizeof ((condition)); } while (SDL_NULL_WHILE_LOOP_CONDITION)
slouken@3647
    97
slouken@3647
    98
typedef enum
slouken@3647
    99
{
slouken@3647
   100
    SDL_ASSERTION_RETRY,  /**< Retry the assert immediately. */
slouken@3647
   101
    SDL_ASSERTION_BREAK,  /**< Make the debugger trigger a breakpoint. */
slouken@3647
   102
    SDL_ASSERTION_ABORT,  /**< Terminate the program. */
slouken@3647
   103
    SDL_ASSERTION_IGNORE,  /**< Ignore the assert. */
icculus@5625
   104
    SDL_ASSERTION_ALWAYS_IGNORE  /**< Ignore the assert from now on. */
slouken@3647
   105
} SDL_assert_state;
slouken@3647
   106
slouken@3647
   107
typedef struct SDL_assert_data
slouken@3647
   108
{
slouken@3647
   109
    int always_ignore;
slouken@3647
   110
    unsigned int trigger_count;
slouken@3647
   111
    const char *condition;
slouken@3647
   112
    const char *filename;
slouken@3647
   113
    int linenum;
slouken@3647
   114
    const char *function;
icculus@3670
   115
    const struct SDL_assert_data *next;
slouken@3647
   116
} SDL_assert_data;
slouken@3647
   117
icculus@6761
   118
#if (SDL_ASSERT_LEVEL > 0)
icculus@6761
   119
icculus@3668
   120
/* Never call this directly. Use the SDL_assert* macros. */
slouken@3655
   121
extern DECLSPEC SDL_assert_state SDLCALL SDL_ReportAssertion(SDL_assert_data *,
slouken@3655
   122
                                                             const char *,
icculus@8189
   123
                                                             const char *, int)
icculus@8190
   124
#if defined(__clang__)
icculus@8190
   125
#if __has_feature(attribute_analyzer_noreturn)
icculus@8189
   126
/* this tells Clang's static analysis that we're a custom assert function,
icculus@8189
   127
   and that the analyzer should assume the condition was always true past this
icculus@8189
   128
   SDL_assert test. */
icculus@8189
   129
   __attribute__((analyzer_noreturn))
icculus@8189
   130
#endif
icculus@8190
   131
#endif
icculus@8189
   132
;
slouken@3647
   133
slouken@3647
   134
/* the do {} while(0) avoids dangling else problems:
slouken@3647
   135
    if (x) SDL_assert(y); else blah();
slouken@3647
   136
       ... without the do/while, the "else" could attach to this macro's "if".
slouken@3647
   137
   We try to handle just the minimum we need here in a macro...the loop,
slouken@3647
   138
   the static vars, and break points. The heavy lifting is handled in
slouken@3647
   139
   SDL_ReportAssertion(), in SDL_assert.c.
slouken@3647
   140
*/
slouken@3647
   141
#define SDL_enabled_assert(condition) \
slouken@3647
   142
    do { \
slouken@3647
   143
        while ( !(condition) ) { \
icculus@9019
   144
            static struct SDL_assert_data sdl_assert_data = { \
slouken@3655
   145
                0, 0, #condition, 0, 0, 0, 0 \
slouken@3647
   146
            }; \
icculus@9019
   147
            const SDL_assert_state sdl_assert_state = SDL_ReportAssertion(&sdl_assert_data, SDL_FUNCTION, SDL_FILE, SDL_LINE); \
icculus@9019
   148
            if (sdl_assert_state == SDL_ASSERTION_RETRY) { \
slouken@3647
   149
                continue; /* go again. */ \
icculus@9019
   150
            } else if (sdl_assert_state == SDL_ASSERTION_BREAK) { \
slouken@3647
   151
                SDL_TriggerBreakpoint(); \
slouken@3647
   152
            } \
slouken@3647
   153
            break; /* not retrying. */ \
slouken@3647
   154
        } \
icculus@7736
   155
    } while (SDL_NULL_WHILE_LOOP_CONDITION)
slouken@3647
   156
slouken@3647
   157
#endif  /* enabled assertions support code */
slouken@3647
   158
slouken@3647
   159
/* Enable various levels of assertions. */
slouken@3647
   160
#if SDL_ASSERT_LEVEL == 0   /* assertions disabled */
slouken@3647
   161
#   define SDL_assert(condition) SDL_disabled_assert(condition)
slouken@3647
   162
#   define SDL_assert_release(condition) SDL_disabled_assert(condition)
slouken@3647
   163
#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
slouken@3647
   164
#elif SDL_ASSERT_LEVEL == 1  /* release settings. */
slouken@3652
   165
#   define SDL_assert(condition) SDL_disabled_assert(condition)
slouken@3647
   166
#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
slouken@3652
   167
#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
slouken@3647
   168
#elif SDL_ASSERT_LEVEL == 2  /* normal settings. */
slouken@3647
   169
#   define SDL_assert(condition) SDL_enabled_assert(condition)
slouken@3647
   170
#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
slouken@3647
   171
#   define SDL_assert_paranoid(condition) SDL_disabled_assert(condition)
slouken@3647
   172
#elif SDL_ASSERT_LEVEL == 3  /* paranoid settings. */
slouken@3647
   173
#   define SDL_assert(condition) SDL_enabled_assert(condition)
slouken@3647
   174
#   define SDL_assert_release(condition) SDL_enabled_assert(condition)
slouken@3647
   175
#   define SDL_assert_paranoid(condition) SDL_enabled_assert(condition)
slouken@3647
   176
#else
slouken@3655
   177
#   error Unknown assertion level.
slouken@3647
   178
#endif
slouken@3647
   179
icculus@7664
   180
/* this assertion is never disabled at any level. */
icculus@7664
   181
#define SDL_assert_always(condition) SDL_enabled_assert(condition)
icculus@7664
   182
icculus@3670
   183
icculus@3670
   184
typedef SDL_assert_state (SDLCALL *SDL_AssertionHandler)(
slouken@5538
   185
                                 const SDL_assert_data* data, void* userdata);
icculus@3670
   186
icculus@3670
   187
/**
icculus@3670
   188
 *  \brief Set an application-defined assertion handler.
icculus@3670
   189
 *
icculus@3670
   190
 *  This allows an app to show its own assertion UI and/or force the
icculus@3670
   191
 *  response to an assertion failure. If the app doesn't provide this, SDL
icculus@3670
   192
 *  will try to do the right thing, popping up a system-specific GUI dialog,
icculus@3670
   193
 *  and probably minimizing any fullscreen windows.
icculus@3670
   194
 *
icculus@3670
   195
 *  This callback may fire from any thread, but it runs wrapped in a mutex, so
icculus@3670
   196
 *  it will only fire from one thread at a time.
icculus@3670
   197
 *
icculus@3670
   198
 *  Setting the callback to NULL restores SDL's original internal handler.
icculus@3670
   199
 *
icculus@3670
   200
 *  This callback is NOT reset to SDL's internal handler upon SDL_Quit()!
icculus@3670
   201
 *
icculus@3670
   202
 *  \return SDL_assert_state value of how to handle the assertion failure.
slouken@7191
   203
 *
icculus@3670
   204
 *  \param handler Callback function, called when an assertion fails.
icculus@3670
   205
 *  \param userdata A pointer passed to the callback as-is.
icculus@3670
   206
 */
icculus@3670
   207
extern DECLSPEC void SDLCALL SDL_SetAssertionHandler(
icculus@3670
   208
                                            SDL_AssertionHandler handler,
icculus@3670
   209
                                            void *userdata);
icculus@3670
   210
icculus@3670
   211
/**
icculus@8167
   212
 *  \brief Get the default assertion handler.
icculus@8167
   213
 *
icculus@8167
   214
 *  This returns the function pointer that is called by default when an
icculus@8167
   215
 *   assertion is triggered. This is an internal function provided by SDL,
icculus@8167
   216
 *   that is used for assertions when SDL_SetAssertionHandler() hasn't been
icculus@8167
   217
 *   used to provide a different function.
icculus@8167
   218
 *
icculus@8167
   219
 *  \return The default SDL_AssertionHandler that is called when an assert triggers.
icculus@8167
   220
 */
icculus@8167
   221
extern DECLSPEC SDL_AssertionHandler SDLCALL SDL_GetDefaultAssertionHandler(void);
icculus@8167
   222
icculus@8167
   223
/**
icculus@8167
   224
 *  \brief Get the current assertion handler.
icculus@8167
   225
 *
icculus@8167
   226
 *  This returns the function pointer that is called when an assertion is
icculus@8167
   227
 *   triggered. This is either the value last passed to
icculus@8167
   228
 *   SDL_SetAssertionHandler(), or if no application-specified function is
icculus@8167
   229
 *   set, is equivalent to calling SDL_GetDefaultAssertionHandler().
icculus@8167
   230
 *
icculus@8167
   231
 *   \param puserdata Pointer to a void*, which will store the "userdata"
icculus@8167
   232
 *                    pointer that was passed to SDL_SetAssertionHandler().
icculus@8167
   233
 *                    This value will always be NULL for the default handler.
icculus@8167
   234
 *                    If you don't care about this data, it is safe to pass
icculus@8167
   235
 *                    a NULL pointer to this function to ignore it.
icculus@8167
   236
 *  \return The SDL_AssertionHandler that is called when an assert triggers.
icculus@8167
   237
 */
icculus@8167
   238
extern DECLSPEC SDL_AssertionHandler SDLCALL SDL_GetAssertionHandler(void **puserdata);
icculus@8167
   239
icculus@8167
   240
/**
icculus@3670
   241
 *  \brief Get a list of all assertion failures.
icculus@3670
   242
 *
icculus@3670
   243
 *  Get all assertions triggered since last call to SDL_ResetAssertionReport(),
icculus@3670
   244
 *  or the start of the program.
icculus@3670
   245
 *
icculus@3670
   246
 *  The proper way to examine this data looks something like this:
icculus@3670
   247
 *
icculus@3670
   248
 *  <code>
icculus@3670
   249
 *  const SDL_assert_data *item = SDL_GetAssertionReport();
icculus@5541
   250
 *  while (item) {
icculus@3670
   251
 *      printf("'%s', %s (%s:%d), triggered %u times, always ignore: %s.\n",
icculus@3670
   252
 *             item->condition, item->function, item->filename,
icculus@3670
   253
 *             item->linenum, item->trigger_count,
icculus@3670
   254
 *             item->always_ignore ? "yes" : "no");
icculus@3670
   255
 *      item = item->next;
icculus@3670
   256
 *  }
icculus@3670
   257
 *  </code>
icculus@3670
   258
 *
icculus@5541
   259
 *  \return List of all assertions.
icculus@3670
   260
 *  \sa SDL_ResetAssertionReport
icculus@3670
   261
 */
icculus@3670
   262
extern DECLSPEC const SDL_assert_data * SDLCALL SDL_GetAssertionReport(void);
icculus@3670
   263
icculus@3670
   264
/**
icculus@3670
   265
 *  \brief Reset the list of all assertion failures.
icculus@3670
   266
 *
icculus@3670
   267
 *  Reset list of all assertions triggered.
icculus@3670
   268
 *
icculus@3670
   269
 *  \sa SDL_GetAssertionReport
icculus@3670
   270
 */
icculus@3670
   271
extern DECLSPEC void SDLCALL SDL_ResetAssertionReport(void);
icculus@3670
   272
slouken@3655
   273
/* Ends C function definitions when using C++ */
slouken@3655
   274
#ifdef __cplusplus
slouken@3655
   275
}
slouken@3655
   276
#endif
slouken@3655
   277
#include "close_code.h"
slouken@3655
   278
slouken@3647
   279
#endif /* _SDL_assert_h */
slouken@3647
   280
slouken@3647
   281
/* vi: set ts=4 sw=4 expandtab: */