| /*************************************************************************** |
| * |
| * exception.cpp - test exercising [lib.support.exception] |
| * |
| * $Id$ |
| * |
| *************************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed |
| * with this work for additional information regarding copyright |
| * ownership. The ASF licenses this file to you under the Apache |
| * License, Version 2.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * permissions and limitations under the License. |
| * |
| * Copyright 2001-2007 Rogue Wave Software, Inc. |
| * |
| **************************************************************************/ |
| |
| #ifdef __SUNPRO_CC |
| // working around a SunPro/SunOS 5.8 bug (PR #26255) |
| # include <time.h> |
| #endif // __SUNPRO_CC |
| |
| #include <exception> // for bad_exception, exception |
| #include <ios> // for ios_base::failure |
| #include <new> // for bad_alloc |
| #include <stdexcept> // for exception classes |
| #include <typeinfo> // for bad_cast, bad_typeid |
| |
| #include <csignal> // for signal(), SIGABRT |
| #include <cstdio> // for size_t, sprintf() |
| #include <cstring> // for strcmp(), strlen() |
| |
| #include <rw/_error.h> // for _RWSTD_ERROR_XXX constants |
| |
| |
| #ifndef _RWSTD_NO_SETRLIMIT |
| # include <sys/resource.h> // for setrlimit() |
| #endif // _RWSTD_NO_SETRLIMIT |
| |
| #include <driver.h> |
| |
| |
| #ifdef _RWSTD_OS_LINUX |
| // use siglongjmp() and sigsetjmp() on Linux to avoid |
| // http://sourceware.org/bugzilla/show_bug.cgi?id=2351 |
| # include <setjmp.h> // for siglongjmp(), sigsetjmp() |
| |
| # define RW_JMP_BUF jmp_buf |
| # define RW_SETJMP(env) sigsetjmp (env, 0) |
| # define RW_LONGJMP(env, val) siglongjmp (env, val) |
| #else // if !defined (_RWSTD_OS_LINUX) |
| # include <csetjmp> // for longjmp(), setjmp() |
| |
| # define RW_JMP_BUF std::jmp_buf |
| # ifdef setjmp |
| # define RW_SETJMP(env) setjmp (env) |
| # else |
| # define RW_SETJMP(env) std::setjmp (env) |
| # endif |
| # define RW_LONGJMP(env, val) std::longjmp (env, val) |
| #endif // _RWSTD_OS_LINUX |
| |
| /**************************************************************************/ |
| |
| // check header <exception> synopsis |
| static void |
| test_synopsis () |
| { |
| // check header <exception> synopsis |
| rw_info (0, 0, __LINE__, "header <exception> synopsis"); |
| |
| // verify that classes are declared |
| std::exception *pex = (std::exception*)0; |
| |
| // verify that bad_exception publicly derives from exception |
| pex = (std::bad_exception*)0; |
| _RWSTD_UNUSED (pex); |
| |
| // check handler types |
| void (*phandler)() = (std::unexpected_handler)0; |
| phandler = (std::terminate_handler)0; |
| |
| // check unexpected and terminate |
| phandler = &std::unexpected; |
| phandler = &std::terminate; |
| _RWSTD_UNUSED (phandler); |
| |
| // check set_{unexpected,terminate} |
| std::unexpected_handler (*pset)(std::unexpected_handler) _PTR_THROWS (()); |
| |
| pset = &std::set_unexpected; |
| pset = &std::set_terminate; |
| _RWSTD_UNUSED (pset); |
| |
| // check uncaught_exception() |
| // see http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/lwg-defects.html#70 |
| // for exception specification details |
| bool (*pue)() _PTR_THROWS (()) = &std::uncaught_exception; |
| _RWSTD_UNUSED (pue); |
| } |
| |
| /**************************************************************************/ |
| |
| |
| // defined at global scope rather than as locals |
| // to work around an MSVC 6.0 bug (see PR #26305) |
| static bool dtor_virtual = false; |
| static bool what_virtual = false; |
| |
| // check the signatures of class exception and bad_exception members |
| static void |
| test_signatures () |
| { |
| // verify that a member function is accessible and has the appropriate |
| // signature, including return type and exception specification |
| #define MEMFUN(result, T, name, arg_list) do { \ |
| result (T::*pf) arg_list _PTR_THROWS (()) = &T::name; \ |
| _RWSTD_UNUSED (pf); \ |
| } while (0) |
| |
| rw_info (0, 0, __LINE__, "std::exception member function signatures"); |
| |
| // verify 18.6.1 [lib.exception] |
| |
| // verify that a public default and copy ctors exist |
| std::exception e1; |
| std::exception e2 (e1); |
| |
| MEMFUN (std::exception&, std::exception, operator=, |
| (const std::exception&)); |
| MEMFUN (const char*, std::exception, what, () const); |
| |
| struct test_exception_virtuals: std::exception { |
| |
| ~test_exception_virtuals () _THROWS (()) { |
| dtor_virtual = true; |
| } |
| const char* what () const _THROWS (()) { |
| what_virtual = true; |
| |
| // working around an MSVC 6.0 bug (PR #26330) |
| typedef std::exception Base; |
| return Base::what (); |
| } |
| }; |
| |
| std::exception *pe = new test_exception_virtuals; |
| |
| // verify that destructor and what() are virtual |
| pe->what (); |
| rw_assert (what_virtual, 0, __LINE__, |
| "std::exception::what() not virtual"); |
| |
| delete pe; |
| rw_assert (dtor_virtual, 0, __LINE__, |
| "std::exception::~exception() not virtual"); |
| |
| |
| rw_info (0, 0, __LINE__, "std::bad_exception member function signatures"); |
| |
| // verify 18.6.2.1 [lib.bad.exception] |
| // verify that a public default and copy ctors exist |
| std::bad_exception be1; |
| std::bad_exception be2 (be1); |
| |
| MEMFUN (std::bad_exception&, std::bad_exception, operator=, |
| (const std::bad_exception&)); |
| MEMFUN (const char*, std::bad_exception, what, () const); |
| |
| dtor_virtual = false; |
| what_virtual = false; |
| |
| struct test_bad_exception_virtuals: std::bad_exception { |
| |
| ~test_bad_exception_virtuals () _THROWS (()) { |
| dtor_virtual = true; |
| } |
| const char* what () const _THROWS (()) { |
| what_virtual = true; |
| |
| // working around an MSVC 6.0 bug (PR #26330) |
| typedef std::bad_exception Base; |
| return Base::what (); |
| } |
| }; |
| |
| pe = new test_bad_exception_virtuals; |
| |
| // verify that destructor and what() are virtual |
| pe->what (); |
| rw_assert (what_virtual, 0, __LINE__, |
| "std::bad_exception::what() not virtual"); |
| |
| delete pe; |
| rw_assert (dtor_virtual, 0, __LINE__, |
| "std::bad_exception::~bad_exception() not virtual"); |
| } |
| |
| /**************************************************************************/ |
| |
| // test globals (to make the accessible in signal and other handlers) |
| std::unexpected_handler puh; // previous unexpected_handler |
| std::terminate_handler pth; // previous terminate_handler |
| int expect_abort; // SIGABRT expected if 1 |
| int expect_terminate; // terminate expected if 1 |
| int expect_unexpected; // unexpected expected if 1 |
| int expect_throw_proc; // throw_proc expected if 1 |
| RW_JMP_BUF jmpenv; |
| |
| /**************************************************************************/ |
| |
| // called in response to abort() (called from std::terminate()) |
| extern "C" { |
| |
| static void |
| SIGABRT_handler (int signo) |
| { |
| rw_assert (1 == expect_abort && SIGABRT == signo, 0, __LINE__, |
| "SIGABRT unexpected"); |
| |
| // reestablish handler |
| std::signal (SIGABRT, SIGABRT_handler); |
| |
| expect_abort = -1; |
| |
| RW_LONGJMP (jmpenv, 1); |
| } |
| |
| } // extern "C" |
| |
| /**************************************************************************/ |
| |
| static void |
| test_terminate_handler () |
| { |
| rw_assert (1 == expect_terminate, 0, __LINE__, |
| "std::terminate() unexpected"); |
| |
| expect_terminate = -1; |
| |
| // establish a handler for SIGABRT (raised from abort()) |
| std::signal (SIGABRT, SIGABRT_handler); |
| |
| // invoke default terminate handler |
| pth (); |
| |
| // shouldn't be reached |
| rw_assert (false, 0, __LINE__, |
| "std::terminate() not called or returned"); |
| |
| RW_LONGJMP (jmpenv, -1); |
| } |
| |
| /**************************************************************************/ |
| |
| static void |
| test_unexpected_handler () |
| { |
| rw_assert (1 == expect_unexpected, 0, __LINE__, |
| "std::unexpected() unexpected"); |
| |
| expect_unexpected = -1; |
| |
| // establish a handler for SIGABRT (raised from abort()) |
| std::signal (SIGABRT, SIGABRT_handler); |
| |
| expect_abort = 1; |
| expect_terminate = 1; |
| |
| // throw an exception outside of any try block |
| // useless conditional used to prevent warnings |
| if (expect_abort) |
| throw 1; |
| |
| // shouldn't be reached |
| rw_assert (false, 0, __LINE__, |
| "std::terminate() not called or returned"); |
| |
| RW_LONGJMP (jmpenv, -1); |
| } |
| |
| /**************************************************************************/ |
| |
| // test the effects of 18.6 |
| static void |
| test_effects () |
| { |
| // verify 18.6.1, p8: what() returns an implementation-defined NTBS |
| std::exception e1; |
| const char *what = e1.what (); |
| rw_assert (what && 1 <= 1 + std::strlen (what), 0, __LINE__, |
| "std::exception::what() != 0"); |
| |
| std::exception e2 (e1); |
| what = e2.what (); |
| rw_assert (what && 1 <= 1 + std::strlen (what), 0, __LINE__, |
| "std::exception::what() != 0"); |
| |
| // verify 18.6.2.1, p5: what() returns an implementation-defined NTBS |
| std::bad_exception e3; |
| what = e3.what (); |
| rw_assert (what && 1 <= 1 + std::strlen (what), 0, __LINE__, |
| "std::bad_exception::what() != 0"); |
| |
| std::exception e4 (e3); |
| what = e4.what (); |
| rw_assert (what && 1 <= 1 + std::strlen (what), 0, __LINE__, |
| "std::bad_exception::what()"); |
| |
| #if !defined (_RWSTD_NO_EXCEPTIONS) \ |
| && !defined (_RWSTD_NO_EXCEPTION_SPECIFICATION) |
| |
| struct S { |
| static void foo () throw (double) { |
| throw 1; |
| } |
| }; |
| |
| rw_info (0, 0, __LINE__, "std::set_unexpected()"); |
| rw_info (0, 0, __LINE__, "std::set_terminate()"); |
| |
| // determine the address of the default |
| // handlers and replace them with our own |
| puh = std::set_unexpected (test_unexpected_handler); |
| pth = std::set_terminate (test_terminate_handler); |
| |
| // invoke a function that throws an exception |
| // that is not in its exception specification |
| expect_unexpected = 1; |
| |
| try { |
| S::foo (); |
| } |
| catch (...) { |
| rw_assert (0, 0, __LINE__, "incompatible exception propagated"); |
| } |
| |
| RW_LONGJMP (jmpenv, -1); |
| |
| #else |
| |
| // prevent failures due to functionality not implemented in compiler |
| expect_abort = -1; |
| expect_terminate = -1; |
| expect_unexpected = -1; |
| |
| #endif // _RWSTD_NO_EXCEPTIONS && ... |
| } |
| |
| /**************************************************************************/ |
| |
| struct UncaughtExceptionCheck { |
| bool *presult_; |
| |
| ~UncaughtExceptionCheck () { |
| *presult_ = std::uncaught_exception (); |
| } |
| }; |
| |
| static void |
| test_uncaught_exception () |
| { |
| rw_info (0, 0, __LINE__, "std::uncaught_exception()"); |
| |
| bool expect; |
| bool uncaught = std::uncaught_exception (); |
| |
| #if !defined (_RWSTD_NO_STD_UNCAUGHT_EXCEPTION) \ |
| || !defined (_RWSTD_NO_GLOBAL_UNCAUGHT_EXCEPTION) |
| |
| expect = false; |
| |
| #else |
| |
| # ifndef _RWSTD_NO_EXCEPTIONS |
| |
| expect = true; |
| |
| # else // if defined (_RWSTD_NO_EXCEPTIONS) |
| |
| expect = false; |
| |
| # endif // _RWSTD_NO_EXCEPTIONS) |
| |
| #endif |
| |
| rw_assert (uncaught == expect, 0, __LINE__, |
| "std::uncaught_exception() == %d, got %d", expect, uncaught); |
| |
| #ifndef _RWSTD_NO_EXCEPTIONS |
| |
| expect = true; |
| |
| try { |
| UncaughtExceptionCheck chk; |
| |
| chk.presult_ = &uncaught; |
| |
| throw 0; |
| } |
| catch (...) { |
| } |
| |
| #else // if defined (_RWSTD_NO_EXCEPTIONS) |
| |
| expect = false; |
| |
| { |
| UncaughtExceptionCheck chk; |
| |
| chk.presult_ = &uncaught; |
| } |
| |
| #endif // _RWSTD_NO_EXCEPTIONS |
| |
| rw_assert (uncaught == expect, 0, __LINE__, |
| "std::uncaught_exception() == %d, got %d", expect, uncaught); |
| } |
| |
| /**************************************************************************/ |
| |
| // original throw proc |
| static void (*const pthrow_proc_save)(int, char*) = _RW::__rw_throw_proc; |
| |
| // replaces the original throw proc |
| static void |
| test_throw_proc (int id, char *s) |
| { |
| rw_assert (id == expect_throw_proc, 0, __LINE__, |
| "throw_proc expected %d, got %d (\"%s\")", |
| expect_throw_proc, id, s); |
| |
| // do not delete[] s if returning to the lib |
| // must delete only if rethrowing or jumping from here |
| _RWSTD_UNUSED (s); |
| |
| // signal that we were successfully called and with what value |
| expect_throw_proc = -id; |
| } |
| |
| /**************************************************************************/ |
| |
| // exercise __rw::__rw_throw() and __rw::__rw_throw_proc() |
| static void |
| test_rw_throw () |
| { |
| #ifndef _RWSTD_NO_EXCEPTIONS |
| |
| rw_info (0, 0, __LINE__, "__rw::__rw_throw()"); |
| |
| // exception format strings |
| static const char* strings[] = { _RWSTD_ERROR_STRINGS }; |
| |
| // exception id's: strings [i + 1] corresponds to expect [i] |
| static const int expect[] = { |
| _RWSTD_ERROR_FIRST + 1, // _RWSTD_ERROR_EXCEPTION |
| _RWSTD_ERROR_FIRST + 2, // _RWSTD_ERROR_BAD_EXCEPTION |
| _RWSTD_ERROR_FIRST + 3, // _RWSTD_ERROR_BAD_ALLOC |
| _RWSTD_ERROR_FIRST + 4, // _RWSTD_ERROR_BAD_CAST |
| _RWSTD_ERROR_LOGIC_ERROR, |
| _RWSTD_ERROR_DOMAIN_ERROR, |
| _RWSTD_ERROR_INVALID_ARGUMENT, |
| _RWSTD_ERROR_LENGTH_ERROR, |
| _RWSTD_ERROR_OUT_OF_RANGE, |
| _RWSTD_ERROR_RUNTIME_ERROR, |
| _RWSTD_ERROR_RANGE_ERROR, |
| _RWSTD_ERROR_OVERFLOW_ERROR, |
| _RWSTD_ERROR_UNDERFLOW_ERROR, |
| |
| _RWSTD_ERROR_FAILBIT_SET, |
| _RWSTD_ERROR_BADBIT_SET, |
| _RWSTD_ERROR_EOFBIT_SET, |
| _RWSTD_ERROR_IOSTATE_BIT_SET |
| }; |
| |
| const char* const ex_names[] = { |
| "std::exception", |
| "std::bad_exception", |
| "std::bad_alloc", |
| "std::bad_cast", |
| "std::logic_error", |
| "std::domain_error", |
| "std::invalid_argument", |
| "std::length_error", |
| "std::out_of_range", |
| "std::runtime_error", |
| "std::range_error", |
| "std::overflow_error", |
| "std::underflow_error", |
| |
| "std::ios_base::failbit_set", |
| "std::ios_base::badbit_set", |
| "std::ios_base::eofbit_set", |
| "std::ios_base::failure" |
| }; |
| |
| const char empty[] = ""; |
| |
| // raise each exception by its id, check that |
| // an exception object of the correct type is thrown |
| for (unsigned i = 0; i != sizeof expect / sizeof *expect; ++i) { |
| |
| rw_info (0, 0, __LINE__, "%s", ex_names [i]); |
| |
| int caught = -1; |
| |
| // set up a new throw proc (prevent exceptions) |
| _RW::__rw_throw_proc = test_throw_proc; |
| |
| // no exception should be thrown at this point |
| expect_throw_proc = expect [i]; |
| |
| const char format[] = _RWSTD_FILE_LINE; |
| const char func[] = "void test_rwt_hrow (Test&)"; |
| |
| _RW::__rw_throw (expect [i], format, func, empty, empty, empty); |
| |
| rw_assert (expect [i] == -expect_throw_proc, 0, __LINE__, |
| "%d. throw_proc (%d, ...) not called", i, expect [i]); |
| |
| try { |
| // reestablish original throw proc |
| _RW::__rw_throw_proc = pthrow_proc_save; |
| |
| // expect an exception |
| _RW::__rw_throw (expect [i], format, func, empty, empty, empty); |
| |
| rw_assert (false, 0, __LINE__, |
| "%d. __rw::__rw_throw(%d, ...) returned", |
| i, expect [i]); |
| } |
| catch (std::domain_error &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "domain_error::what() != 0"); |
| caught = _RWSTD_ERROR_DOMAIN_ERROR; |
| } |
| catch (std::invalid_argument &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "invalid_argument::what() != 0"); |
| caught = _RWSTD_ERROR_INVALID_ARGUMENT; |
| } |
| catch (std::length_error &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "length_error::what() != 0"); |
| caught = _RWSTD_ERROR_LENGTH_ERROR; |
| } |
| catch (std::out_of_range &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "out_of_range::what() != 0"); |
| caught = _RWSTD_ERROR_OUT_OF_RANGE; |
| } |
| catch (std::range_error &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "range_error::what() != 0"); |
| caught = _RWSTD_ERROR_RANGE_ERROR; |
| } |
| catch (std::overflow_error &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "overflow_error::what() != 0"); |
| caught = _RWSTD_ERROR_OVERFLOW_ERROR; |
| } |
| catch (std::underflow_error &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "underflow_error::what() != 0"); |
| caught = _RWSTD_ERROR_UNDERFLOW_ERROR; |
| } |
| catch (std::logic_error &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "logic_error::what() != 0"); |
| caught = _RWSTD_ERROR_LOGIC_ERROR; |
| } |
| catch (std::runtime_error &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "runtime_error::what() != 0"); |
| caught = _RWSTD_ERROR_RUNTIME_ERROR; |
| } |
| catch (std::bad_alloc &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "bad_alloc::what() != 0"); |
| caught = _RWSTD_ERROR_FIRST + 3; // _RWSTD_BAD_ALLOC |
| } |
| catch (std::bad_exception &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "bad_exception::what() != 0"); |
| caught = _RWSTD_ERROR_FIRST + 2; // _RWSTD_ERROR_BAD_EXCEPTION |
| } |
| catch (std::bad_cast &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "bad_cast::what() != 0"); |
| caught = _RWSTD_ERROR_FIRST + 4; // _RWSTD_ERROR_BAD_CAST; |
| } |
| |
| #ifdef _RWSTD_ERROR_BAD_TYPEID |
| |
| catch (std::bad_typeid &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "bad_typeid::what() != 0"); |
| caught = _RWSTD_ERROR_BAD_TYPEID; |
| } |
| |
| #endif // _RWSTD_ERROR_BAD_TYPEID; |
| |
| #ifndef _RWSTD_NO_EXT_FAILURE |
| |
| catch (std::ios_base::failbit_set &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "ios_base::failbit_set::what() != 0"); |
| caught = _RWSTD_ERROR_FAILBIT_SET; |
| } |
| catch (std::ios_base::badbit_set &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "ios_base::badbit_set::what() != 0"); |
| caught = _RWSTD_ERROR_BADBIT_SET; |
| } |
| catch (std::ios_base::eofbit_set &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "ios_base::eofbit_set::what() != 0"); |
| caught = _RWSTD_ERROR_EOFBIT_SET; |
| } |
| |
| #endif // _RWSTD_NO_EXT_FAILURE |
| |
| catch (std::ios_base::failure &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "ios_base::failure::what() != 0"); |
| caught = _RWSTD_ERROR_IOSTATE_BIT_SET; |
| } |
| catch (std::exception &ex) { |
| rw_assert (!!ex.what (), 0, __LINE__, |
| "exception::what() != 0"); |
| caught = _RWSTD_ERROR_FIRST + 1; // _RWSTD_ERROR_EXCEPTION |
| } |
| catch (...) { |
| caught = -1; |
| } |
| |
| rw_assert (expect [i] == caught, 0, __LINE__, |
| "%d. expected %d, caught %d", i, expect [i], caught); |
| } |
| |
| |
| // reestablish original throw proc |
| _RW::__rw_throw_proc = pthrow_proc_save; |
| |
| char str [1024]; |
| char result [2 * sizeof str]; |
| |
| rw_info (0, 0, __LINE__, |
| "exercising throwing standard exception objects " |
| "constructed with string arguments up to %zu " |
| "characters long (not including context info)", |
| sizeof str); |
| |
| // exercise the ability or __rw_throw() to correctly format |
| // strings of arbitrary length, also verify that format string |
| // macros are used to format the what() strings as expected |
| for (unsigned j = 0; j != sizeof str - 1; ++j) { |
| |
| // exclude exception, bad_alloc, bad_cast, and bad_exception |
| // they are typically generated by the compiler and their |
| // what() strings are implementation-specific |
| unsigned en = j % ((sizeof expect / sizeof *expect) - 5); |
| |
| // null-terminate str |
| str [j] = '\0'; |
| |
| std::sprintf (result, strings [en + 5], __FILE__, str, |
| empty, empty, empty); |
| |
| try { |
| // expect an exception |
| _RW::__rw_throw (expect [en + 4], __FILE__, str, |
| empty, empty, empty); |
| |
| rw_assert (false, 0, __LINE__, |
| "%d. __rw::__rw_throw (%d, ...) returned", |
| j, expect [en + 4]); |
| } |
| catch (std::exception &e) { |
| rw_assert (e.what () && 0 == std::strcmp (e.what (), result), |
| 0, __LINE__, |
| "%u. \"%s\" != \"%s\" [%u]", |
| j, e.what (), result, en + 3); |
| } |
| |
| // append a decimal digit |
| str [j] = '0' + j % 10; |
| } |
| |
| #endif // _RWSTD_NO_EXCEPTIONS |
| } |
| |
| /**************************************************************************/ |
| |
| static void |
| throwing_unexpected_handler () |
| { |
| // throw an exception that can't otherwise be possibly thrown |
| // to induce std::bad_exception to be rethrown by the langauage |
| // runtime library |
| |
| struct PrivateStruct { }; |
| |
| // prevent assertions from the installed SIGABRT handler |
| // and terminate_handler in case they are invoked as a |
| // result of throwing the exception below |
| expect_terminate = 1; |
| expect_abort = 1; |
| |
| throw PrivateStruct (); |
| } |
| |
| /**************************************************************************/ |
| |
| enum RuntimeExceptionId { |
| E_bad_alloc, E_bad_cast, E_bad_exception, E_bad_typeid, E_error |
| }; |
| |
| |
| // helper classes used by the bad_cast test |
| // defined at namespace scope rather than at local scope |
| // to work around an Intel C++ 10.0 ICE (see STDCXX-475) |
| struct Base { virtual ~Base () { } }; |
| struct Derived1: Base { }; |
| struct Derived2: Base { }; |
| |
| |
| // induce the language runtime into throwing an exception |
| // returns e if exception cannot bee thrown |
| static RuntimeExceptionId |
| induce_exception (RuntimeExceptionId reid, const char *name) |
| { |
| const char *why = 0; |
| |
| switch (reid) { |
| |
| case E_bad_alloc: { //////////////////////////////////////////// |
| |
| #ifndef _RWSTD_NO_NEW_THROWS |
| |
| # ifndef _RWSTD_NO_SETRLIMIT |
| |
| # if !defined (__HP_aCC) |
| |
| // work around an HP aCC 5.xx (IPF) bug (PR #29014) |
| |
| // retrieve the current resource limits |
| struct rlimit rl = { 0, 0 }; |
| if (getrlimit (RLIMIT_DATA, &rl)) |
| return E_error; |
| |
| // set the soft limit, leave hard limit unchanged |
| rl.rlim_cur = 0; |
| rw_warn (0 == setrlimit (RLIMIT_DATA, &rl), 0, __LINE__, |
| "setrlimit (RLIMIT_DATA, ...) failed: %m"); |
| |
| try { |
| |
| # endif // __HP_aCC |
| # endif // _RWSTD_NO_SETRLIMIT |
| |
| // try to allocate a huge amount of memory to induce bad_alloc |
| const std::size_t huge_amount = _RWSTD_SIZE_MAX - 4096; |
| |
| ::operator new (huge_amount); |
| |
| # ifndef _RWSTD_NO_SETRLIMIT |
| # if !defined (__HP_aCC) |
| |
| } |
| catch (...) { |
| |
| // reset the soft limit back to the value of the hard limit |
| rl.rlim_cur = rl.rlim_max; |
| rw_warn (0 == setrlimit (RLIMIT_DATA, &rl), 0, __LINE__, |
| "setrlimit (RLIMIT_DATA, ...) failed: %m"); |
| |
| // rethrow bad_alloc |
| throw; |
| } |
| |
| # endif // __HP_aCC |
| |
| return E_error; |
| |
| # endif // _RWSTD_NO_SETRLIMIT |
| |
| #else // if defined (_RWSTD_NO_NEW_THROWS) |
| |
| why = "_RWSTD_NO_NEW_THROWS is #defined"; |
| |
| break; // unable to induce bad_alloc |
| |
| #endif // NO_NEW_THROWS |
| |
| } |
| |
| case E_bad_cast: { ///////////////////////////////////////////// |
| |
| #ifndef _RWSTD_NO_DYNAMIC_CAST |
| |
| Derived1 b; |
| Base &a = b; |
| |
| // induce bad_cast |
| dynamic_cast<Derived2&>(a); |
| |
| return E_error; |
| |
| #else // if defined (_RWSTD_NO_DYNAMIC_CAST) |
| |
| why = "_RWSTD_NO_DYNAMIC_CAST is #defined"; |
| |
| break; // unable to induce bad_cast |
| |
| #endif // _RWSTD_NO_DYNAMIC_CAST |
| } |
| |
| case E_bad_exception: { //////////////////////////////////////// |
| |
| #ifndef _RWSTD_NO_EXCEPTION_SPECIFICATION |
| |
| std::set_unexpected (throwing_unexpected_handler); |
| |
| struct S { |
| // induce bad_exception |
| S () throw (std::bad_exception) { throw 0; } |
| } s; |
| |
| _RWSTD_UNUSED (s); |
| return E_error; |
| |
| #else // if defined (_RWSTD_NO_EXCEPTION_SPECIFICATION) |
| |
| why = "_RWSTD_NO_EXCEPTION_SPECIFICATION is #defined"; |
| |
| break; // unable to induce bad_exception |
| |
| #endif // _RWSTD_NO_EXCEPTION_SPECIFICATION |
| |
| } |
| |
| case E_bad_typeid: { /////////////////////////////////////////// |
| |
| #if !defined (_RWSTD_NO_GLOBAL_BAD_TYPEID) \ |
| || !defined (_RWSTD_NO_STD_BAD_TYPEID) |
| |
| struct S { virtual ~S () { } } *s = 0; |
| |
| // induce bad_typeid |
| typeid (*s); |
| |
| return E_error; |
| |
| #else // if _RWSTD_NO_GLOBAL_BAD_TYPEID && _RWSTD_NO_STD_BAD_TYPEID |
| |
| why = "both _RWSTD_NO_GLOBAL_BAD_TYPEID and _RWSTD_NO_STD_BAD_TYPEID " |
| "are #defined"; |
| |
| break; // unable to induce bad_typeid |
| |
| #endif // NO_BAD_TYPEID |
| |
| } |
| |
| default: |
| break; |
| } |
| |
| rw_warn (0, 0, __LINE__, "unable to induce std::%s: %s\n", name, why); |
| |
| return reid; |
| } |
| |
| /**************************************************************************/ |
| |
| static const RuntimeExceptionId |
| rt_exceptions[] = { |
| E_bad_alloc, E_bad_cast, E_bad_exception, E_bad_typeid, |
| E_error |
| }; |
| |
| static const char* const |
| rt_exception_names[] = { |
| "bad_alloc", "bad_cast", "bad_exception", "bad_typeid" |
| }; |
| |
| static int |
| opt_rt_exception [E_error]; |
| |
| |
| static void |
| test_runtime () |
| { |
| #ifndef _RWSTD_NO_EXCEPTIONS |
| |
| rw_info (0, 0, __LINE__, "runtime support for exceptions"); |
| |
| |
| // using static to avoid gcc 3.x warning: variable |
| // might be clobbered by `longjmp' or `vfork' |
| |
| for (static unsigned i = 0; E_error != rt_exceptions [i]; ++i) { |
| |
| const RuntimeExceptionId ex_id = rt_exceptions [i]; |
| const char* const ex_name = rt_exception_names [i]; |
| |
| if (0 == rw_note (0 <= opt_rt_exception [i], |
| 0, __LINE__, |
| "std::%s test disabled", ex_name)) |
| continue; |
| |
| rw_info (0, 0, __LINE__, "std::%s", ex_name); |
| |
| static int ex0; |
| static int ex1; |
| |
| ex0 = ex1 = 0; |
| |
| try { |
| try { |
| // jump back here if the induced exception causes |
| // a call to terminate() and/or raises SIGABRT |
| if (0 == RW_SETJMP (jmpenv)) { |
| |
| // try to induce the standard exception |
| if (ex_id == induce_exception (ex_id, ex_name)) { |
| ex0 = -1; |
| ex1 = -1; |
| } |
| } |
| else { |
| rw_assert (false, 0, __LINE__, |
| "inducing std::%s caused a call " |
| "to std::terminate()", ex_name); |
| // prevent additional assertions |
| ex0 = -1; |
| } |
| } |
| catch (std::exception&) { |
| // so far so good, rethrow and try to catch again |
| ex0 = 0; |
| throw; |
| } |
| catch (...) { |
| // failure to catch a standard exception using std::exception |
| // most likely indication of class exception having been |
| // defined in a different namespace by the C++ Standard |
| // library than by the language support library (compiler |
| // runtime) |
| ex0 = -2; |
| throw; |
| } |
| } |
| catch (std::bad_alloc&) { ex1 = E_bad_alloc; } |
| catch (std::bad_cast&) { ex1 = E_bad_cast; } |
| catch (std::bad_exception&) { ex1 = E_bad_exception; } |
| catch (std::bad_typeid&) { ex1 = E_bad_typeid; } |
| catch (...) { ex1 = -2; } |
| |
| rw_assert (-1 == ex0 || 0 == ex0, 0, __LINE__, |
| "std::%s thrown, std::exception not caught", |
| ex_name); |
| |
| rw_assert (-1 == ex1 || rt_exceptions [i] == ex1, 0, __LINE__, |
| "std::%s thrown, unknown exception caught", |
| ex_name); |
| } |
| |
| #endif // _RWSTD_NO_EXCEPTIONS |
| } |
| |
| /**************************************************************************/ |
| |
| static int opt_synopsis; // for --<toggle>-synopis |
| static int opt_signatures; // for --<toggle>-signatures |
| static int opt_uncaught; // for --<toggle>-uncaught_exception |
| static int opt_effects; // for --<toggle>-effects |
| static int opt_rw_throw; // for --<toggle>-rw_throw |
| static int opt_runtime; // for --<toggle>-runtime |
| |
| static int |
| run_test (int, char**) |
| { |
| if (rw_note (0 <= opt_synopsis, 0, __LINE__, |
| "test of <exception> synopsis disabled")) |
| test_synopsis (); |
| |
| // check the signatures of class exception and bad_exception members |
| if (rw_note (0 <= opt_signatures, 0, __LINE__, |
| "test of function signatures disabled")) |
| test_signatures (); |
| |
| // exercise std::uncaught_exception() before running any other tests |
| // since some of them might affect the correct behavior of the function |
| // (if they violate such constraints as returning from a call to |
| // std::terminate()) |
| if (rw_note (0 <= opt_uncaught, 0, __LINE__, |
| "test of uncaught_exception() disabled")) |
| test_uncaught_exception (); |
| |
| // exercise __rw::__rw_throw() and __rw::__rw_throw_proc() |
| if (rw_note (0 <= opt_rw_throw, 0, __LINE__, |
| "test of __rw_throw() disabled")) |
| test_rw_throw (); |
| |
| // exercise the cooperation between the C++ standard library and |
| // the runtime support library when throwing standard exceptions |
| if (rw_note (0 <= opt_runtime, 0, __LINE__, |
| "test of runtime support disabled")) |
| test_runtime (); |
| |
| // exercise the effects last to defer potential problems |
| // due to the tests returning (jumping) out of the handlers |
| if (rw_note (0 <= opt_effects, 0, __LINE__, |
| "test of effects disabled")) { |
| // test the effects of 18.6 |
| if (0 == RW_SETJMP (jmpenv)) { |
| test_effects (); |
| } |
| |
| // verify that test worked as expected (each handler sets |
| // its own expect_xxx variable to -1 after it's been called) |
| rw_error (-1 == expect_abort, 0, __LINE__, |
| "abort() was called unexpectedly"); |
| |
| rw_error (-1 == expect_terminate, 0, __LINE__, |
| "terminate() was called unexpectedly"); |
| |
| rw_error (-1 == expect_unexpected, 0, __LINE__, |
| "unexpected() was called unexpectedly"); |
| } |
| |
| return 0; |
| } |
| |
| /**************************************************************************/ |
| |
| int main (int argc, char *argv[]) |
| { |
| return rw_test (argc, argv, __FILE__, |
| "lib.support.exception", |
| 0 /* no comment */, |
| run_test, |
| "|-synopsis~ " |
| "|-signatures~ " |
| "|-uncaught_exception~ " |
| "|-effects~ " |
| "|-rw_throw~ " |
| "|-runtime~ " |
| "|-bad_alloc~ " |
| "|-bad_cast~ " |
| "|-bad_exception~ " |
| "|-bad_typeid~", |
| &opt_synopsis, |
| &opt_signatures, |
| &opt_uncaught, |
| &opt_effects, |
| &opt_rw_throw, |
| &opt_runtime, |
| opt_rt_exception + E_bad_alloc, |
| opt_rt_exception + E_bad_cast, |
| opt_rt_exception + E_bad_exception, |
| opt_rt_exception + E_bad_typeid, |
| 0 /* sentinel */); |
| } |