Introduce an assertion mechanism for the Subversion libraries, that enables assertion failures to be trapped and handled by the application. This commit adds the new macros and functions but does not use them. * subversion/include/svn_error_codes.h (SVN_ERR_ASSERT_CATEGORY_START, SVN_ERR_ASSERTION_FAIL): New macros. * subversion/include/svn_error.h (SVN_ERR_MALFUNCTION, SVN_ERR_ASSERT): New macros. (svn_error__internal_err, svn_error_set_malfunc_handler, svn_error_raise_on_malfunc, svn_error_abort_on_malfunc): New functions. (svn_error_malfunc_cb_t): New type. * subversion/libsvn_subr/error.c (svn_error_raise_on_malfunc, svn_error_abort_on_malfunc, svn_error_set_malfunc_handler, svn_error__internal_err): New functions. (malfunc_cb): New variable. Index: subversion/include/svn_error_codes.h =================================================================== --- subversion/include/svn_error_codes.h (revision 31761) +++ subversion/include/svn_error_codes.h (working copy) @@ -142,6 +142,8 @@ extern "C" { + (21 * SVN_ERR_CATEGORY_SIZE)) #define SVN_ERR_RA_SERF_CATEGORY_START (APR_OS_START_USERERR \ + (22 * SVN_ERR_CATEGORY_SIZE)) +#define SVN_ERR_ASSERT_CATEGORY_START (APR_OS_START_USERERR \ + + (23 * SVN_ERR_CATEGORY_SIZE)) #endif /* DOXYGEN_SHOULD_SKIP_THIS */ @@ -1221,6 +1223,12 @@ SVN_ERROR_START SVN_ERR_CL_CATEGORY_START + 10, "No external merge tool available") + /* internal errors such as assertion failures */ + + SVN_ERRDEF(SVN_ERR_ASSERTION_FAIL, + SVN_ERR_ASSERT_CATEGORY_START + 0, + "Assertion failure") + SVN_ERROR_END Index: subversion/include/svn_error.h =================================================================== --- subversion/include/svn_error.h (revision 31761) +++ subversion/include/svn_error.h (working copy) @@ -295,6 +295,93 @@ void svn_handle_warning(FILE *stream, sv err->apr_err == SVN_ERR_RA_NOT_LOCKED || \ err->apr_err == SVN_ERR_FS_LOCK_EXPIRED) +/** Raise an error that indicates an internal malfunction. + * + * Cause the function using this macro to return an error object that + * describes the location of the failure, or such other behaviour as may have + * been specified by a call to svn_error_set_assertion_handler(). + * + * This macro is logically equivalent to an assertion that always fails, and + * should be used where the program reaches a state that cannot possibly be + * reached unless there is a bug in the program. + * + * @since New in 1.6. + */ +#define SVN_ERR_MALFUNCTION() \ + SVN_ERR(svn_error__internal_err(__FILE__, __LINE__, NULL)) + +/** Assert that some condition is true: raise an error if it is not. + * + * If the expression @a expr is not true, cause the function using this + * macro to return an error object that describes the failure, or such + * other behaviour as may have been specified by a call to + * svn_error_set_assertion_handler(). + * + * This should be used to check conditions that cannot possibly be false + * unless there is a bug in the program. + * + * @since New in 1.6. + */ +#define SVN_ERR_ASSERT(expr) \ + do { \ + if (!(expr)) \ + SVN_ERR(svn_error__internal_err(__FILE__, __LINE__, #expr)); \ + } while (0) + +/** Helper function for the macros that report internal errors. */ +svn_error_t * +svn_error__internal_err(const char *file, int line, const char *expr); + +/** A type of function that handles an assertion failure or other internal + * malfunction detected within the Subversion libraries. + * + * The error occurred in the source file @a file at line @a line, and was an + * assertion failure of the expression @a expr, or, if @a expr is null, an + * unconditional error. + * + * A function of this type must do one of: + * - Return an error object describing the error. + * - Allow the program to continue, by returning SVN_NO_ERROR. + * - Never return. + * + * The function may alter its behaviour according to compile-time + * and run-time and even interactive conditions. + * + * @since New in 1.6. + */ +typedef svn_error_t *(*svn_error_malfunc_cb_t) + (const char *file, int line, const char *expr); + +/** Cause subsequent internal errors to be handled by @a func. + * Return the handler that was previously in effect. + * + * @a func may not be null. + * + * @note This function must be called in a single-threaded context. + * + * @since New in 1.6. + */ +svn_error_malfunc_cb_t +svn_error_set_malfunc_handler(svn_error_malfunc_cb_t func); + +/** Handle an internal error by returning an error object describing it. + * + * This function implements @c svn_error_assertion_func_t. + * + * @since New in 1.6. + */ +svn_error_t * +svn_error_raise_on_malfunc(const char *file, int line, const char *expr); + +/** Handle an internal error by printing a message to stderr and aborting. + * + * This function implements @c svn_error_assertion_func_t. + * + * @since New in 1.6. + */ +svn_error_t * +svn_error_abort_on_malfunc(const char *file, int line, const char *expr); + #ifdef __cplusplus } Index: subversion/libsvn_subr/error.c =================================================================== --- subversion/libsvn_subr/error.c (revision 31761) +++ subversion/libsvn_subr/error.c (working copy) @@ -482,3 +482,46 @@ svn_strerror(apr_status_t statcode, char return apr_strerror(statcode, buf, bufsize); } + +svn_error_t * +svn_error_raise_on_malfunc(const char *file, int line, const char *expr) +{ + if (expr != NULL) + return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, + _("Assertion failed in file '%s' line %d: '%s'"), + file, line, expr); + else + return svn_error_createf(SVN_ERR_ASSERTION_FAIL, NULL, + _("Internal malfunction in file '%s' line %d"), + file, line); +} + +svn_error_t * +svn_error_abort_on_malfunc(const char *file, int line, const char *expr) +{ + if (expr != NULL) + fprintf(stderr, _("Assertion failed in file '%s' line %d: '%s'"), + file, line, expr); + else + fprintf(stderr, _("Internal malfunction in file '%s' line %d"), + file, line); + abort(); +} + +/* The current handler for reporting internal errors (malfunctions). */ +static svn_error_malfunc_cb_t malfunc_cb = svn_error_raise_on_malfunc; + +svn_error_malfunc_cb_t +svn_error_set_malfunc_handler(svn_error_malfunc_cb_t func) +{ + svn_error_malfunc_cb_t old_malfunc_cb = malfunc_cb; + + malfunc_cb = func; + return old_malfunc_cb; +} + +svn_error_t * +svn_error__internal_err(const char *file, int line, const char *expr) +{ + return malfunc_cb(file, line, expr); +}