diff --git a/.travis.yml b/.travis.yml index 8fb7cca..a53e346 100644 --- a/.travis.yml +++ b/.travis.yml @@ -14,13 +14,14 @@ env: before_install: - export CXX="g++-$GCC_VER" CC="gcc-$GCC_VER" GCOV="gcov-$GCC_VER" - if [ "$GCC_VER" = "4.8" ]; then export COVERAGE=1 CPPCHECK=1; fi + - if [ ${COVERAGE} = 1 ]; then export FUZZY_CMD="-D RUN_FUZZY_TESTS:BOOL=TRUE"; fi - sudo pip install cpp-coveralls - sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y - sudo apt-get update - sudo apt-get install -qq g++-$GCC_VER script: - - if [ ${COVERITY_SCAN_BRANCH} != 1 ]; then cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug . ; fi + - if [ ${COVERITY_SCAN_BRANCH} != 1 ]; then cmake -D ENABLE_COVERAGE:BOOL=TRUE -D CMAKE_BUILD_TYPE:STRING=Debug $FUZZY_CMD . ; fi - if [ ${COVERITY_SCAN_BRANCH} != 1 ]; then make -j2 ; fi - make test - if [ ${COVERAGE} = 1 ]; then bash <(curl -s https://raw.githubusercontent.com/codecov/codecov-bash/master/codecov) -x $GCOV -a "-s `pwd`" ; fi diff --git a/CMakeLists.txt b/CMakeLists.txt index fb0af87..59c33a3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,7 @@ option(MULTITHREAD_SUPPORT_ENABLED "Multithreaded Support Enabled" TRUE) option(BUILD_MODULES "Build Extra Modules (stl)" TRUE) option(BUILD_SAMPLES "Build Samples Folder" FALSE) +option(RUN_FUZZY_TESTS "Run tests generated by AFL" FALSE) option(USE_STD_MAKE_SHARED "Use std::make_shared instead of chaiscript::make_shared" FALSE) mark_as_advanced(USE_STD_MAKE_SHARED) @@ -269,9 +270,51 @@ if(BUILD_MODULES) endif() file(GLOB UNIT_TESTS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR}/unittests/ ${CMAKE_CURRENT_SOURCE_DIR}/unittests/*.chai ${CMAKE_CURRENT_SOURCE_DIR}/unittests/3.x/*.chai) - list(SORT UNIT_TESTS) + +if (RUN_FUZZY_TESTS) + + file(MAKE_DIRECTORY "${CMAKE_BINARY_DIR}/unittests") + + execute_process( + COMMAND ${CMAKE_COMMAND} -E tar xjf ${CMAKE_CURRENT_SOURCE_DIR}/unittests/fuzzy_tests-2015-07-16.tar.bz2 + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/unittests + ) + + + file(GLOB FUZZY_CRASH_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/id*) + list(SORT FUZZY_CRASH_TESTS) + + file(GLOB FUZZY_EXCEPTION_TESTS RELATIVE ${CMAKE_BINARY_DIR}/unittests/ ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/exceptions/id*) + list(SORT FUZZY_EXCEPTION_TESTS) + + + foreach(filename ${FUZZY_CRASH_TESTS}) + message(STATUS "Adding test ${filename}") + add_test(${filename} chai "-e" ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/crashes/unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename}) + endforeach() + + set_property(TEST ${FUZZY_CRASH_TESTS} + PROPERTY ENVIRONMENT + "CHAI_USE_PATH=${CMAKE_BINARY_DIR}/unittests/" + "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" + ) + + foreach(filename ${FUZZY_EXCEPTION_TESTS}) + message(STATUS "Adding test ${filename}") + add_test(${filename} chai "--exception" ${CMAKE_BINARY_DIR}/unittests/fuzzy_tests/exceptions/unit_test.inc ${CMAKE_BINARY_DIR}/unittests/${filename}) + endforeach() + + set_property(TEST ${FUZZY_EXCEPTION_TESTS} + PROPERTY ENVIRONMENT + "CHAI_USE_PATH=${CMAKE_CURRENT_SOURCE_DIR}/unittests/" + "CHAI_MODULE_PATH=${CMAKE_CURRENT_BINARY_DIR}/" + ) + +endif() + + if(BUILD_TESTING) # Add catch tests macro diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index d832db1..1624e02 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -304,13 +304,17 @@ namespace chaiscript /// the remaining parameters are the args to bind into the result static Boxed_Value bind_function(const std::vector ¶ms) { - if (params.size() < 2) - { - throw exception::arity_error(static_cast(params.size()), 2); + if (params.empty()) { + throw exception::arity_error(0, 1); } Const_Proxy_Function f = boxed_cast(params[0]); + if (f->get_arity() != -1 && size_t(f->get_arity()) != params.size() - 1) + { + throw exception::arity_error(static_cast(params.size()), f->get_arity()); + } + return Boxed_Value(Const_Proxy_Function(std::make_shared(std::move(f), std::vector(params.begin() + 1, params.end())))); } diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 6f704b6..3369879 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -22,6 +22,7 @@ #include "../chaiscript_defines.hpp" #include "../chaiscript_threading.hpp" +#include "bad_boxed_cast.hpp" #include "boxed_cast.hpp" #include "boxed_cast_helper.hpp" #include "boxed_value.hpp" @@ -873,7 +874,14 @@ namespace chaiscript std::vector remaining_params{l_params.begin() + l_num_params, l_params.end()}; Boxed_Value bv = dispatch::dispatch(l_funs, attr_params, l_conversions); if (!remaining_params.empty() || bv.get_type_info().bare_equal(user_type())) { - return (*boxed_cast(bv))(remaining_params, l_conversions); + auto func = boxed_cast>(bv); + try { + return (*func)(remaining_params, l_conversions); + } catch (const chaiscript::exception::bad_boxed_cast &) { + } catch (const chaiscript::exception::arity_error &) { + } catch (const chaiscript::exception::guard_error &) { + } + throw chaiscript::exception::dispatch_error(remaining_params, std::vector{func}); } else { return bv; } diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index c2d4045..1eee060 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -149,7 +149,11 @@ namespace chaiscript Boxed_Value operator()(const std::vector ¶ms, const chaiscript::Type_Conversions &t_conversions) const { - return do_call(params, t_conversions); + if (m_arity < 0 || size_t(m_arity) == params.size()) { + return do_call(params, t_conversions); + } else { + throw exception::arity_error(static_cast(params.size()), m_arity); + } } /// Returns a vector containing all of the types of the parameters the function returns/takes @@ -420,18 +424,12 @@ namespace chaiscript protected: virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE { - if (m_arity < 0 || params.size() == size_t(m_arity)) + if (call_match(params, t_conversions) && test_guard(params, t_conversions)) { - if (call_match(params, t_conversions) && test_guard(params, t_conversions)) - { - return m_f(params); - } else { - throw exception::guard_error(); - } - + return m_f(params); } else { - throw exception::arity_error(static_cast(params.size()), m_arity); - } + throw exception::guard_error(); + } } private: @@ -728,19 +726,14 @@ namespace chaiscript protected: virtual Boxed_Value do_call(const std::vector ¶ms, const Type_Conversions &t_conversions) const CHAISCRIPT_OVERRIDE { - if (params.size() == 1) + const Boxed_Value &bv = params[0]; + if (bv.is_const()) { - const Boxed_Value &bv = params[0]; - if (bv.is_const()) - { - const Class *o = boxed_cast(bv, &t_conversions); - return detail::Handle_Return::type>::handle(o->*m_attr); - } else { - Class *o = boxed_cast(bv, &t_conversions); - return detail::Handle_Return::type>::handle(o->*m_attr); - } + const Class *o = boxed_cast(bv, &t_conversions); + return detail::Handle_Return::type>::handle(o->*m_attr); } else { - throw exception::arity_error(static_cast(params.size()), 1); + Class *o = boxed_cast(bv, &t_conversions); + return detail::Handle_Return::type>::handle(o->*m_attr); } } @@ -787,6 +780,9 @@ namespace chaiscript const Type_Conversions &t_conversions) { const std::vector &types = t_func->get_param_types(); + + if (t_func->get_arity() == -1) return false; + assert(plist.size() == types.size() - 1); return std::mismatch(plist.begin(), plist.end(), diff --git a/include/chaiscript/dispatchkit/type_info.hpp b/include/chaiscript/dispatchkit/type_info.hpp index 62dc081..0d5c5f3 100644 --- a/include/chaiscript/dispatchkit/type_info.hpp +++ b/include/chaiscript/dispatchkit/type_info.hpp @@ -29,7 +29,7 @@ namespace chaiscript class Type_Info { public: - CHAISCRIPT_CONSTEXPR Type_Info(bool t_is_const, bool t_is_reference, bool t_is_pointer, bool t_is_void, + CHAISCRIPT_CONSTEXPR Type_Info(bool t_is_const, bool t_is_reference, bool t_is_pointer, bool t_is_void, bool t_is_arithmetic, const std::type_info *t_ti, const std::type_info *t_bare_ti) : m_type_info(t_ti), m_bare_type_info(t_bare_ti), m_is_const(t_is_const), m_is_reference(t_is_reference), m_is_pointer(t_is_pointer), @@ -38,7 +38,7 @@ namespace chaiscript { } - CHAISCRIPT_CONSTEXPR Type_Info() + CHAISCRIPT_CONSTEXPR Type_Info() : m_type_info(nullptr), m_bare_type_info(nullptr), m_is_const(false), m_is_reference(false), m_is_pointer(false), m_is_void(false), m_is_arithmetic(false), @@ -134,14 +134,14 @@ namespace chaiscript { typedef T type; - static Type_Info get() + static Type_Info get() { return Type_Info(std::is_const::type>::type>::value, std::is_reference::value, std::is_pointer::value, std::is_void::value, (std::is_arithmetic::value || std::is_arithmetic::type>::value) - && !std::is_same::type, bool>::value, - &typeid(T), + && !std::is_same::type>::type, bool>::value, + &typeid(T), &typeid(typename Bare_Type::type)); } }; @@ -151,11 +151,11 @@ namespace chaiscript { typedef T type; - static Type_Info get() + static Type_Info get() { return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, std::is_void::value, - std::is_arithmetic::value && !std::is_same::type, bool>::value, + std::is_arithmetic::value && !std::is_same::type>::type, bool>::value, &typeid(std::shared_ptr ), &typeid(typename Bare_Type::type)); } @@ -166,11 +166,11 @@ namespace chaiscript { typedef T type; - static Type_Info get() + static Type_Info get() { return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, std::is_void::value, - std::is_arithmetic::value && !std::is_same::type, bool>::value, + std::is_arithmetic::value && !std::is_same::type>::type, bool>::value, &typeid(const std::shared_ptr &), &typeid(typename Bare_Type::type)); } @@ -181,11 +181,11 @@ namespace chaiscript { typedef T type; - static Type_Info get() + static Type_Info get() { return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, std::is_void::value, - std::is_arithmetic::value && !std::is_same::type, bool>::value, + std::is_arithmetic::value && !std::is_same::type>::type, bool>::value, &typeid(std::reference_wrapper ), &typeid(typename Bare_Type::type)); } @@ -196,11 +196,11 @@ namespace chaiscript { typedef T type; - static Type_Info get() + static Type_Info get() { return Type_Info(std::is_const::value, std::is_reference::value, std::is_pointer::value, std::is_void::value, - std::is_arithmetic::value && !std::is_same::type, bool>::value, + std::is_arithmetic::value && !std::is_same::type>::type, bool>::value, &typeid(const std::reference_wrapper &), &typeid(typename Bare_Type::type)); } diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 8498f88..daf8dd9 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -276,10 +276,13 @@ namespace chaiscript template static std::string format_location(const T &t) { - std::ostringstream oss; - oss << "(" << t->filename() << " " << t->start().line << ", " << t->start().column << ")"; - - return oss.str(); + if (t) { + std::ostringstream oss; + oss << "(" << t->filename() << " " << t->start().line << ", " << t->start().column << ")"; + return oss.str(); + } else { + return "(internal)"; + } } diff --git a/include/chaiscript/language/chaiscript_eval.hpp b/include/chaiscript/language/chaiscript_eval.hpp index 9d5d8c9..00ce4b1 100644 --- a/include/chaiscript/language/chaiscript_eval.hpp +++ b/include/chaiscript/language/chaiscript_eval.hpp @@ -1117,11 +1117,22 @@ namespace chaiscript AST_Node(std::move(t_ast_node_text), AST_Node_Type::File, std::move(t_loc), std::move(t_children)) { } virtual ~File_AST_Node() {} virtual Boxed_Value eval_internal(chaiscript::detail::Dispatch_Engine &t_ss) const CHAISCRIPT_OVERRIDE { - const auto num_children = children.size(); - for (size_t i = 0; i < num_children-1; ++i) { - children[i]->eval(t_ss); + try { + const auto num_children = children.size(); + + if (num_children > 0) { + for (size_t i = 0; i < num_children-1; ++i) { + children[i]->eval(t_ss); + } + return children.back()->eval(t_ss); + } else { + return Boxed_Value(); + } + } catch (const detail::Continue_Loop &) { + throw exception::eval_error("Unexpected `continue` statement outside of a loop"); + } catch (const detail::Break_Loop &) { + throw exception::eval_error("Unexpected `break` statement outside of a loop"); } - return children.back()->eval(t_ss); } }; @@ -1158,7 +1169,7 @@ namespace chaiscript try { // short circuit arithmetic operations - if (m_oper != Operators::invalid && bv.get_type_info().is_arithmetic()) + if (m_oper != Operators::invalid && m_oper != Operators::bitwise_and && bv.get_type_info().is_arithmetic()) { return Boxed_Number::do_oper(m_oper, bv); } else { diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index e39b632..ddc484f 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -194,6 +194,7 @@ namespace chaiscript /// Returns the front-most AST node AST_NodePtr ast() const { + if (m_match_stack.empty()) throw exception::eval_error("Attempted to access AST of failed parse."); return m_match_stack.front(); } @@ -261,7 +262,7 @@ namespace chaiscript } AST_NodePtr optimized_ast(bool t_optimize_blocks = false, bool t_optimize_returns = true) { - AST_NodePtr p = m_match_stack.front(); + AST_NodePtr p = ast(); //Note, optimize_blocks is currently broken; it breaks stack management if (t_optimize_blocks) { optimize_blocks(p); } if (t_optimize_returns) { optimize_returns(p); } @@ -1208,37 +1209,32 @@ namespace chaiscript } /// Reads an end-of-line group from input, without skipping initial whitespace - bool Eol_() { + bool Eol_(const bool t_eos = false) { bool retval = false; if (has_more_input() && (Symbol_("\r\n") || Char_('\n'))) { retval = true; ++m_line; m_col = 1; - } else if (has_more_input() && Char_(';')) { + } else if (has_more_input() && !t_eos && Char_(';')) { retval = true; } return retval; } - /// Reads (and potentially captures) an end-of-line group from input - bool Eol(const bool t_capture = false) { + /// Reads until the end of the current statement + bool Eos() { SkipWS(); - if (!t_capture) { - return Eol_(); - } else { - const auto start = m_input_pos; - const auto prev_col = m_col; - const auto prev_line = m_line; - if (Eol_()) { - m_match_stack.push_back(make_node(std::string(start, m_input_pos), prev_line, prev_col)); - return true; - } else { - return false; - } - } + return Eol_(true); + } + + /// Reads (and potentially captures) an end-of-line group from input + bool Eol() { + SkipWS(); + + return Eol_(); } /// Reads a comma-separated list of values from input. Id's only, no types allowed @@ -1441,7 +1437,7 @@ namespace chaiscript } } - while (Eol()) {} + while (Eos()) {} if (Char(':')) { if (!Operator()) { diff --git a/src/main.cpp b/src/main.cpp index 67725f0..372adf7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -297,6 +297,8 @@ int main(int argc, char *argv[]) chai.add(chaiscript::fun(&get_eval_error), "get_eval_error"); chai.add(chaiscript::fun(&now), "now"); + bool eval_error_ok = false; + bool boxed_exception_ok = false; for (int i = 0; i < argc; ++i) { if ( i == 0 && argc > 1 ) { @@ -327,6 +329,12 @@ int main(int argc, char *argv[]) arg = "print(version())" ; } else if ( arg == "-h" || arg == "--help" ) { arg = "help(-1)"; + } else if ( arg == "-e" || arg == "--evalerrorok" ) { + eval_error_ok = true; + continue; + } else if ( arg == "--exception" ) { + boxed_exception_ok = true; + continue; } else if ( arg == "-i" || arg == "--interactive" ) { mode = eInteractive ; } else if ( arg.find('-') == 0 ) { @@ -352,12 +360,23 @@ int main(int argc, char *argv[]) catch (const chaiscript::exception::eval_error &ee) { std::cout << ee.pretty_print(); std::cout << '\n'; - return EXIT_FAILURE; + + if (!eval_error_ok) { + return EXIT_FAILURE; + } } - catch (std::exception &e) { - std::cout << e.what() << '\n'; - return EXIT_FAILURE; + catch (const chaiscript::Boxed_Value &e) { + std::cout << "Unhandled exception thrown of type " << e.get_type_info().name() << '\n'; + + if (!boxed_exception_ok) { + return EXIT_FAILURE; + } } + +// catch (std::exception &e) { +// std::cout << e.what() << '\n'; +// return EXIT_FAILURE; +// } } return EXIT_SUCCESS; diff --git a/unittests/fuzzy_tests-2015-07-16.tar.bz2 b/unittests/fuzzy_tests-2015-07-16.tar.bz2 new file mode 100644 index 0000000..d150daa Binary files /dev/null and b/unittests/fuzzy_tests-2015-07-16.tar.bz2 differ diff --git a/unittests/type_info_test.cpp b/unittests/type_info_test.cpp index 5e16581..9b9fd92 100644 --- a/unittests/type_info_test.cpp +++ b/unittests/type_info_test.cpp @@ -5,33 +5,58 @@ #include #include -void test_type(const chaiscript::Type_Info &ti, bool t_is_const, bool t_is_pointer, bool t_is_reference, bool t_is_void, - bool t_is_undef) +#ifdef CHAISCRIPT_MSVC +#pragma warning(push) +#pragma warning(disable : 4190 4640 28251 4702 6330) +#endif + +#ifdef __GNUC__ +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wshadow" +#pragma GCC diagnostic ignored "-Wold-style-cast" +#endif + +#ifdef __llvm__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wreturn-type-c-linkage" +#pragma clang diagnostic ignored "-Wold-style-cast" +#pragma clang diagnostic ignored "-Wexit-time-destructors" +#pragma clang diagnostic ignored "-Wfloat-equal" +#pragma clang diagnostic ignored "-Wunreachable-code" +#endif + + + + +#define CATCH_CONFIG_MAIN + +#include "catch.hpp" + + +TEST_CASE("Type_Info objects generate expected results") { - if (ti.is_const() == t_is_const - && ti.is_pointer() == t_is_pointer - && ti.is_reference() == t_is_reference - && ti.is_void() == t_is_void - && ti.is_undef() == t_is_undef) + const auto test_type = [](const chaiscript::Type_Info &ti, bool t_is_const, bool t_is_pointer, bool t_is_reference, bool t_is_void, + bool t_is_undef, bool t_is_arithmetic) { - return; - } else { - exit(EXIT_FAILURE); - } -} + CHECK(ti.is_const() == t_is_const); + CHECK(ti.is_pointer() == t_is_pointer); + CHECK(ti.is_reference() == t_is_reference); + CHECK(ti.is_void() == t_is_void); + CHECK(ti.is_undef() == t_is_undef); + CHECK(ti.is_arithmetic() == t_is_arithmetic); + }; - -int main() -{ - test_type(chaiscript::user_type(), false, false, false, true, false); - test_type(chaiscript::user_type(), true, false, false, false, false); - test_type(chaiscript::user_type(), true, false, true, false, false); - test_type(chaiscript::user_type(), false, false, false, false, false); - test_type(chaiscript::user_type(), false, true, false, false, false); - test_type(chaiscript::user_type(), true, true, false, false, false); - test_type(chaiscript::Type_Info(), false, false, false, false, true); + SECTION("void") { test_type(chaiscript::user_type(), false, false, false, true, false, false); } + SECTION("const int") { test_type(chaiscript::user_type(), true, false, false, false, false, true); } + SECTION("const int &") { test_type(chaiscript::user_type(), true, false, true, false, false, true); } + SECTION("int") { test_type(chaiscript::user_type(), false, false, false, false, false, true); } + SECTION("int *") { test_type(chaiscript::user_type(), false, true, false, false, false, false); } + SECTION("const int *") { test_type(chaiscript::user_type(), true, true, false, false, false, false); } + SECTION("const bool &") { test_type(chaiscript::user_type(), true, false, true, false, false, false); } + SECTION("default") { test_type(chaiscript::Type_Info(), false, false, false, false, true, false); } std::cout << "Size of Type_Info " << sizeof(chaiscript::Type_Info) << '\n'; - - return EXIT_SUCCESS; } + + +