diff --git a/.travis.yml b/.travis.yml index 36d565c..c827a46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -18,10 +18,14 @@ script: - find CMakeFiles/ -name "*.gc*" -exec mv {} gcov/ \; - $GCOV -d -o gcov gcov/*.gcda - coveralls -n -E ".*\.cpp" +after_script: + - contrib/codeanalysis/runcppcheck.sh notifications: - recipients: - - jason@emptycrate.com email: + recipients: + - jason@emptycrate.com on_success: always on_failure: always - +env: + global: + secure: LCUAKUCRtFp2ak81nVLR+jx0C9+Drwx1OR4VzuvH+HNGWFdUZmAIV3R84euDqFC5cUhYYipaeMbiSOJUHE4MNlL58eQZryED6KSL7k7SgxOLpFSspMvuMjIYZLlBWpBneCR/EMDilu+zXEnASfVUMPuLmtY1GAyfSoZboqFProc= diff --git a/CMakeLists.txt b/CMakeLists.txt index 54e95fc..a4ec047 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -18,6 +18,18 @@ endif() option(BUILD_MODULES "Build Extra Modules (stl, reflection)" TRUE) option(BUILD_SAMPLES "Build Samples Folder" FALSE) +set(EXTRA_LINKER_FLAGS "") + +if (CMAKE_COMPILER_IS_GNUCC) + option(ENABLE_COVERAGE "Enable Coverage Reporting in GCC" FALSE) + + if (ENABLE_COVERAGE) + add_definitions(--coverage -O0) + SET(EXTRA_LINKER_FLAGS ${EXTRA_LINKER_FLAGS} "--coverage") + endif() + +endif() + list(APPEND CPACK_SOURCE_IGNORE_FILES "${CMAKE_CURRENT_BINARY_DIR}") list(APPEND CPACK_SOURCE_IGNORE_FILES "\\\\.svn") list(APPEND CPACK_SOURCE_IGNORE_FILES "\\\\.git") @@ -30,7 +42,7 @@ set(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/description.txt" set(CPACK_PACKAGE_VERSION_MAJOR 5) set(CPACK_PACKAGE_VERSION_MINOR 3) -set(CPACK_PACKAGE_VERSION_PATCH 0) +set(CPACK_PACKAGE_VERSION_PATCH 1) set(CPACK_PACKAGE_EXECUTABLES "chai;ChaiScript Eval") set(CPACK_PACKAGE_VENDOR "ChaiScript.com") diff --git a/contrib/codeanalysis/runcppcheck.sh b/contrib/codeanalysis/runcppcheck.sh new file mode 100755 index 0000000..a7eacd8 --- /dev/null +++ b/contrib/codeanalysis/runcppcheck.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +pushd .. +wget http://sourceforge.net/projects/cppcheck/files/cppcheck/1.64/cppcheck-1.64.tar.bz2 +tar -xvf cppcheck-1.64.tar.bz2 +cd cppcheck-1.64 +make -j2 +popd +../cppcheck-1.64/cppcheck --enable=all --inconclusive -I include --inline-suppr --std=c++11 --platform=unix64 src/main.cpp src/chai*.cpp --template ' - __{severity}__: [{file}:{line}](../blob/TRAVIS_COMMIT/{file}#L{line}) {message} ({id})' 2>output +sed -i "s/TRAVIS_COMMIT/${TRAVIS_COMMIT}/g" output +echo -n '{ "body": " ' > output.json +echo -n `awk '{printf "%s\\\\n", $0;}' output` >> output.json +echo -n '"}' >> output.json +if [ "${TRAVIS_PULL_REQUEST}" = "false" ]; then curl -H "Authorization: token ${TOKEN}" --request POST --data @output.json https://api.github.com/repos/ChaiScript/ChaiScript/commits/${TRAVIS_COMMIT}/comments; else curl -H "Authorization: token ${TOKEN}" --request POST --data @output.json https://api.github.com/repos/ChaiScript/ChaiScript/issues/${TRAVIS_PULL_REQUEST}/comments; fi + + diff --git a/include/chaiscript/dispatchkit/bootstrap.hpp b/include/chaiscript/dispatchkit/bootstrap.hpp index d42baa3..3bb5cfd 100644 --- a/include/chaiscript/dispatchkit/bootstrap.hpp +++ b/include/chaiscript/dispatchkit/bootstrap.hpp @@ -304,14 +304,6 @@ namespace chaiscript static void throw_exception(const Boxed_Value &bv) { throw bv; } - - static std::shared_ptr bootstrap2( - std::shared_ptr e - = std::shared_ptr (new chaiscript::detail::Dispatch_Engine())) - { - e->add(user_type(), "void"); - return e; - } static std::string what(const std::exception &e) { diff --git a/include/chaiscript/dispatchkit/boxed_cast.hpp b/include/chaiscript/dispatchkit/boxed_cast.hpp index a603da5..c153c99 100644 --- a/include/chaiscript/dispatchkit/boxed_cast.hpp +++ b/include/chaiscript/dispatchkit/boxed_cast.hpp @@ -79,11 +79,18 @@ namespace chaiscript if (std::is_polymorphic::type>::value && t_conversions) { try { + // std::cout << "trying an up conversion " << typeid(Type).name() << std::endl; // We will not catch any bad_boxed_dynamic_cast that is thrown, let the user get it // either way, we are not responsible if it doesn't work return detail::Cast_Helper::cast(t_conversions->boxed_dynamic_cast(bv), t_conversions); - } catch (const chaiscript::detail::exception::bad_any_cast &) { - throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); + } catch (...) { + try { + // std::cout << "trying a down conversion " << typeid(Type).name() << std::endl; + // try going the other way - down the inheritance graph + return detail::Cast_Helper::cast(t_conversions->boxed_dynamic_down_cast(bv), t_conversions); + } catch (const chaiscript::detail::exception::bad_any_cast &) { + throw exception::bad_boxed_cast(bv.get_type_info(), typeid(Type)); + } } } else { // If it's not polymorphic, just throw the error, don't waste the time on the diff --git a/include/chaiscript/dispatchkit/boxed_number.hpp b/include/chaiscript/dispatchkit/boxed_number.hpp index c78545d..92fda88 100644 --- a/include/chaiscript/dispatchkit/boxed_number.hpp +++ b/include/chaiscript/dispatchkit/boxed_number.hpp @@ -307,7 +307,7 @@ namespace chaiscript } template - std::string to_string_aux(const Boxed_Value &v) const + static std::string to_string_aux(const Boxed_Value &v) { std::ostringstream oss; oss << *static_cast(v.get_const_ptr()); @@ -520,7 +520,7 @@ namespace chaiscript return oper(Operators::assign_bitwise_and, this->bv, t_rhs.bv); } - void validate_boxed_number(const Boxed_Value &v) + static void validate_boxed_number(const Boxed_Value &v) { const Type_Info &inp_ = v.get_type_info(); if (inp_ == typeid(bool)) diff --git a/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp b/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp index 1fd461b..ac6f947 100644 --- a/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp +++ b/include/chaiscript/dispatchkit/dynamic_cast_conversion.hpp @@ -48,12 +48,13 @@ namespace chaiscript { public: virtual Boxed_Value convert(const Boxed_Value &derived) const = 0; + virtual Boxed_Value convert_down(const Boxed_Value &base) const = 0; - const Type_Info &base() + const Type_Info &base() const { return m_base; } - const Type_Info &derived() + const Type_Info &derived() const { return m_derived; } @@ -72,6 +73,57 @@ namespace chaiscript }; + template + class Dynamic_Caster + { + public: + static Boxed_Value cast(const Boxed_Value &t_from) + { + if (t_from.get_type_info().bare_equal(user_type())) + { + if (t_from.is_pointer()) + { + // Dynamic cast out the contained boxed value, which we know is the type we want + if (t_from.is_const()) + { + std::shared_ptr data + = std::dynamic_pointer_cast(detail::Cast_Helper >::cast(t_from, nullptr)); + if (!data) + { + throw std::bad_cast(); + } + + return Boxed_Value(data); + } else { + std::shared_ptr data + = std::dynamic_pointer_cast(detail::Cast_Helper >::cast(t_from, nullptr)); + + if (!data) + { + throw std::bad_cast(); + } + + return Boxed_Value(data); + } + } else { + // Pull the reference out of the contained boxed value, which we know is the type we want + if (t_from.is_const()) + { + const From &d = detail::Cast_Helper::cast(t_from, 0); + const To &data = dynamic_cast(d); + return Boxed_Value(std::cref(data)); + } else { + From &d = detail::Cast_Helper::cast(t_from, 0); + To &data = dynamic_cast(d); + return Boxed_Value(std::ref(data)); + } + } + } else { + throw chaiscript::exception::bad_boxed_dynamic_cast(t_from.get_type_info(), typeid(To), "Unknown dynamic_cast_conversion"); + } + } + }; + template class Dynamic_Conversion_Impl : public Dynamic_Conversion { @@ -81,50 +133,14 @@ namespace chaiscript { } + virtual Boxed_Value convert_down(const Boxed_Value &t_base) const + { + return Dynamic_Caster::cast(t_base); + } + virtual Boxed_Value convert(const Boxed_Value &t_derived) const { - if (t_derived.get_type_info().bare_equal(user_type())) - { - if (t_derived.is_pointer()) - { - // Dynamic cast out the contained boxed value, which we know is the type we want - if (t_derived.is_const()) - { - std::shared_ptr data - = std::dynamic_pointer_cast(detail::Cast_Helper >::cast(t_derived, nullptr)); - if (!data) - { - throw std::bad_cast(); - } - - return Boxed_Value(data); - } else { - std::shared_ptr data - = std::dynamic_pointer_cast(detail::Cast_Helper >::cast(t_derived, nullptr)); - - if (!data) - { - throw std::bad_cast(); - } - - return Boxed_Value(data); - } - } else { - // Pull the reference out of the contained boxed value, which we know is the type we want - if (t_derived.is_const()) - { - const Derived &d = detail::Cast_Helper::cast(t_derived, 0); - const Base &data = dynamic_cast(d); - return Boxed_Value(std::cref(data)); - } else { - Derived &d = detail::Cast_Helper::cast(t_derived, 0); - Base &data = dynamic_cast(d); - return Boxed_Value(std::ref(data)); - } - } - } else { - throw chaiscript::exception::bad_boxed_dynamic_cast(t_derived.get_type_info(), typeid(Base), "Unknown dynamic_cast_conversion"); - } + return Dynamic_Caster::cast(t_derived); } }; } @@ -155,7 +171,7 @@ namespace chaiscript bool dynamic_cast_converts(const Type_Info &base, const Type_Info &derived) const { - return has_conversion(base, derived); + return has_conversion(base, derived) || has_conversion(derived, base); } template @@ -170,6 +186,19 @@ namespace chaiscript } } + template + Boxed_Value boxed_dynamic_down_cast(const Boxed_Value &base) const + { + try { + return get_conversion(base.get_type_info(), user_type())->convert_down(base); + } catch (const std::out_of_range &) { + throw exception::bad_boxed_dynamic_cast(base.get_type_info(), typeid(Derived), "No known conversion"); + } catch (const std::bad_cast &) { + throw exception::bad_boxed_dynamic_cast(base.get_type_info(), typeid(Derived), "Unable to perform dynamic_cast operation"); + } + } + + bool has_conversion(const Type_Info &base, const Type_Info &derived) const { chaiscript::detail::threading::shared_lock l(m_mutex); @@ -242,8 +271,6 @@ namespace chaiscript /// chai.add(chaiscript::base_class()); /// \endcode /// - /// \todo Move share static type registration code into a mechanism that allows it to be properly - /// shared by all modules template Dynamic_Cast_Conversion base_class() { diff --git a/include/chaiscript/dispatchkit/dynamic_object.hpp b/include/chaiscript/dispatchkit/dynamic_object.hpp index 487505c..fe4108b 100644 --- a/include/chaiscript/dispatchkit/dynamic_object.hpp +++ b/include/chaiscript/dispatchkit/dynamic_object.hpp @@ -30,7 +30,7 @@ namespace chaiscript return m_attrs[t_attr_name]; } - std::map get_attrs() + std::map get_attrs() const { return m_attrs; } diff --git a/include/chaiscript/dispatchkit/function_call_detail.hpp b/include/chaiscript/dispatchkit/function_call_detail.hpp index 579ae67..40cfe86 100644 --- a/include/chaiscript/dispatchkit/function_call_detail.hpp +++ b/include/chaiscript/dispatchkit/function_call_detail.hpp @@ -17,12 +17,11 @@ namespace chaiscript { namespace detail { - /** * Internal helper class for handling the return * value of a build_function_caller */ - template + template struct Function_Caller_Ret { static Ret call(const std::vector &t_funcs, @@ -32,11 +31,25 @@ namespace chaiscript } }; + /** + * Specialization for arithmetic return types + */ + template + struct Function_Caller_Ret + { + static Ret call(const std::vector &t_funcs, + const std::vector ¶ms, const Dynamic_Cast_Conversions &t_conversions) + { + return Boxed_Number(dispatch::dispatch(t_funcs, params, t_conversions)).get_as(); + } + }; + + /** * Specialization for void return types */ template<> - struct Function_Caller_Ret + struct Function_Caller_Ret { static void call(const std::vector &t_funcs, const std::vector ¶ms, const Dynamic_Cast_Conversions &t_conversions) @@ -59,11 +72,11 @@ namespace chaiscript Ret operator()(Param...param) { - return Function_Caller_Ret::call(m_funcs, { -(std::is_reference::value&&!(std::is_same::type>::type>::value))?Boxed_Value(std::ref(param)):Boxed_Value(param)... - }, m_conversions - - ); + return Function_Caller_Ret::value>::call(m_funcs, { + (std::is_reference::value&&!(std::is_same::type>::type>::value))?Boxed_Value(std::ref(param)):Boxed_Value(param)... + }, m_conversions + + ); } diff --git a/include/chaiscript/dispatchkit/proxy_functions.hpp b/include/chaiscript/dispatchkit/proxy_functions.hpp index d5de855..f2e5767 100644 --- a/include/chaiscript/dispatchkit/proxy_functions.hpp +++ b/include/chaiscript/dispatchkit/proxy_functions.hpp @@ -23,7 +23,7 @@ namespace chaiscript class Boxed_Number; struct AST_Node; - typedef std::shared_ptr AST_NodePtr; + typedef std::shared_ptr AST_NodePtr; namespace dispatch { diff --git a/include/chaiscript/language/chaiscript_common.hpp b/include/chaiscript/language/chaiscript_common.hpp index 0572906..a628d9b 100644 --- a/include/chaiscript/language/chaiscript_common.hpp +++ b/include/chaiscript/language/chaiscript_common.hpp @@ -54,7 +54,7 @@ namespace chaiscript }; /// \brief Typedef for pointers to AST_Node objects. Used in building of the AST_Node tree - typedef std::shared_ptr AST_NodePtr; + typedef std::shared_ptr AST_NodePtr; /// \brief Classes which may be thrown during error cases when ChaiScript is executing. @@ -435,7 +435,7 @@ namespace chaiscript return eval_internal(t_e); } catch (exception::eval_error &ee) { ee.call_stack.push_back(shared_from_this()); - throw ee; + throw; } } diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 58d48f0..ae5990f 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -742,7 +742,7 @@ namespace chaiscript if (t_handler) { t_handler->handle(bv, m_engine); } - throw bv; + throw; } } @@ -768,7 +768,7 @@ namespace chaiscript if (t_handler) { t_handler->handle(bv, m_engine); } - throw bv; + throw; } } @@ -798,7 +798,7 @@ namespace chaiscript if (t_handler) { t_handler->handle(bv, m_engine); } - throw bv; + throw; } } @@ -814,7 +814,7 @@ namespace chaiscript if (t_handler) { t_handler->handle(bv, m_engine); } - throw bv; + throw; } } @@ -834,7 +834,7 @@ namespace chaiscript if (t_handler) { t_handler->handle(bv, m_engine); } - throw bv; + throw; } } }; diff --git a/include/chaiscript/language/chaiscript_parser.hpp b/include/chaiscript/language/chaiscript_parser.hpp index 0b30cc4..71334d5 100644 --- a/include/chaiscript/language/chaiscript_parser.hpp +++ b/include/chaiscript/language/chaiscript_parser.hpp @@ -56,7 +56,8 @@ namespace chaiscript public: ChaiScript_Parser() - : m_multiline_comment_begin("/*"), + : m_line(-1), m_col(-1), + m_multiline_comment_begin("/*"), m_multiline_comment_end("*/"), m_singleline_comment("//") { @@ -803,10 +804,9 @@ namespace chaiscript */ bool Quoted_String_() { bool retval = false; - char prev_char = 0; if (has_more_input() && (*m_input_pos == '\"')) { retval = true; - prev_char = *m_input_pos; + char prev_char = *m_input_pos; ++m_input_pos; ++m_col; @@ -980,10 +980,9 @@ namespace chaiscript */ bool Single_Quoted_String_() { bool retval = false; - char prev_char = 0; if (has_more_input() && (*m_input_pos == '\'')) { retval = true; - prev_char = *m_input_pos; + char prev_char = *m_input_pos; ++m_input_pos; ++m_col; @@ -1356,7 +1355,6 @@ namespace chaiscript bool Def() { bool retval = false; bool is_annotated = false; - bool is_method = false; AST_NodePtr annotation; if (Annotation()) { @@ -1375,6 +1373,8 @@ namespace chaiscript throw exception::eval_error("Missing function name in definition", File_Position(m_line, m_col), *m_filename); } + bool is_method = false; + if (Symbol("::", false)) { //We're now a method is_method = true; diff --git a/src/test_module.cpp b/src/test_module.cpp index 144e8d4..83886f0 100644 --- a/src/test_module.cpp +++ b/src/test_module.cpp @@ -11,6 +11,8 @@ class TestBaseType virtual ~TestBaseType() {} virtual int func() { return 0; } + int base_only_func() { return -9; } + const TestBaseType &constMe() const { return *this; } int val; @@ -35,11 +37,28 @@ class TestDerivedType : public TestBaseType public: virtual ~TestDerivedType() {} virtual int func() { return 1; } + int derived_only_func() { return 19; } private: TestDerivedType &operator=(const TestDerivedType &); }; +class TestMoreDerivedType : public TestDerivedType +{ + public: + virtual ~TestMoreDerivedType() {} +}; + +std::shared_ptr derived_type_factory() +{ + return std::shared_ptr(new TestDerivedType()); +} + +std::shared_ptr more_derived_type_factory() +{ + return std::shared_ptr(new TestMoreDerivedType()); +} + std::string hello_world() { return "Hello World"; @@ -70,6 +89,7 @@ CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_test_mo m->add(chaiscript::user_type(), "TestBaseType"); m->add(chaiscript::user_type(), "TestDerivedType"); + m->add(chaiscript::user_type(), "TestMoreDerivedType"); m->add(chaiscript::constructor(), "TestBaseType"); // m->add(chaiscript::constructor(), "TestBaseType"); @@ -79,11 +99,23 @@ CHAISCRIPT_MODULE_EXPORT chaiscript::ModulePtr create_chaiscript_module_test_mo m->add(chaiscript::constructor(), "TestDerivedType"); m->add(chaiscript::constructor(), "TestDerivedType"); + m->add(chaiscript::constructor(), "TestMoreDerivedType"); + m->add(chaiscript::constructor(), "TestMoreDerivedType"); + + /// \todo automatic chaining of base classes? m->add(chaiscript::base_class()); + m->add(chaiscript::base_class()); + m->add(chaiscript::base_class()); + + m->add(chaiscript::fun(&TestDerivedType::derived_only_func), "derived_only_func"); + + m->add(chaiscript::fun(&derived_type_factory), "derived_type_factory"); + m->add(chaiscript::fun(&more_derived_type_factory), "more_derived_type_factory"); m->add(chaiscript::fun(&TestBaseType::func), "func"); m->add(chaiscript::fun(&TestBaseType::val), "val"); m->add(chaiscript::fun(&TestBaseType::const_val), "const_val"); + m->add(chaiscript::fun(&TestBaseType::base_only_func), "base_only_func"); m->add(chaiscript::fun(&get_new_int), "get_new_int"); diff --git a/unittests/arithmetic_conversions_test.cpp b/unittests/arithmetic_conversions_test.cpp index 255a02a..903da3d 100644 --- a/unittests/arithmetic_conversions_test.cpp +++ b/unittests/arithmetic_conversions_test.cpp @@ -18,6 +18,11 @@ void f3(double) { } +void f_func_return(const std::function &f) +{ + // test the ability to return an unsigned with auto conversion + f(4); +} int main() { @@ -29,6 +34,8 @@ int main() chai.add(chaiscript::fun(&f1), "f3"); chai.add(chaiscript::fun(&f4), "f3"); + chai.add(chaiscript::fun(&f_func_return), "func_return"); + // no overloads chai.eval("f1(0)"); chai.eval("f1(0l)"); @@ -46,7 +53,12 @@ int main() // 1 non-arithmetic overload chai.eval("f2(1.0)"); - // this is the one call we expect to fail + // various options for returning with conversions from chaiscript + chai.eval("func_return(fun(x) { return 5u; })"); + chai.eval("func_return(fun(x) { return 5; })"); + chai.eval("func_return(fun(x) { return 5.0f; })"); + + // this is the one call we expect to fail, ambiguous overloads try { chai.eval("f2(1.0l)"); } catch (const std::exception &) { diff --git a/unittests/inheritance.chai b/unittests/inheritance.chai index e1c33af..0ffd4c0 100644 --- a/unittests/inheritance.chai +++ b/unittests/inheritance.chai @@ -15,3 +15,21 @@ assert_equal(15, t.const_val); t.val = 23; assert_equal(23, t.val) +// test_derived_factory returns a TestDerivedType contained +// in a shared_ptr. This is testing our ability +// to detect that and do the down casting for the user automatically +// at runtime + +assert_equal(t.derived_only_func(), 19); + +var d := derived_type_factory(); +assert_equal(d.derived_only_func(), 19); + +var t2 = TestMoreDerivedType(); +assert_equal(t2.derived_only_func(), 19); +assert_equal(t2.base_only_func(), -9); + +var md := more_derived_type_factory(); +assert_equal(md.derived_only_func(), 19); +assert_equal(md.base_only_func(), -9); +