diff --git a/CMakeLists.txt b/CMakeLists.txt index c270ce1..888f234 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,8 +17,12 @@ if (Boost_FOUND) include_directories(${Boost_INCLUDE_DIRS}) add_executable(chaiscript_eval src/main.cpp) #add_executable(dispatchkit_test contrib/test/dispatchkit_test.cpp) - target_link_libraries(chaiscript_eval ${Boost_LIBRARIES}) + target_link_libraries(chaiscript_eval dl ${Boost_LIBRARIES}) + + add_library(test MODULE src/test_module.cpp) + install(TARGETS chaiscript_eval DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/bin) + else(Boost_FOUND) message(FATAL_ERROR "Can not find Boost") endif(Boost_FOUND) diff --git a/include/chaiscript/chaiscript.hpp b/include/chaiscript/chaiscript.hpp index 0f1678b..467042b 100644 --- a/include/chaiscript/chaiscript.hpp +++ b/include/chaiscript/chaiscript.hpp @@ -23,6 +23,8 @@ #include "dispatchkit/function_call.hpp" namespace chaiscript { + typedef ModulePtr (*Create_Module_Func)(); + /** * Types of AST nodes available to the parser and eval */ diff --git a/include/chaiscript/chaiscript_threading.hpp b/include/chaiscript/chaiscript_threading.hpp index e00d82e..d898b4f 100644 --- a/include/chaiscript/chaiscript_threading.hpp +++ b/include/chaiscript/chaiscript_threading.hpp @@ -18,6 +18,11 @@ namespace chaiscript class Thread_Storage { public: + ~Thread_Storage() + { + m_thread_storage.reset(); + } + inline T *operator->() const { if (!m_thread_storage.get()) diff --git a/include/chaiscript/dispatchkit/boxed_value.hpp b/include/chaiscript/dispatchkit/boxed_value.hpp index 337f68a..f6340a7 100644 --- a/include/chaiscript/dispatchkit/boxed_value.hpp +++ b/include/chaiscript/dispatchkit/boxed_value.hpp @@ -218,14 +218,16 @@ namespace chaiscript * Drop objects from the cache where there is only one (ie, our) * reference to it, so it may be destructed */ - void cull() + void cull(bool force = false) { + ++m_cullcount; - if (m_cullcount % 10 != 0) + if (force || m_cullcount % 10 != 0) { return; } + std::map::iterator itr = m_ptrs.begin(); while (itr != m_ptrs.end()) @@ -279,11 +281,16 @@ namespace chaiscript /** * Return a reference to the static global Object_Cache */ - Object_Cache &get_object_cache() + static Object_Cache &get_object_cache() { static chaiscript::threading::Thread_Storage oc; return *oc; - } + } + + static void clear_cache() + { + get_object_cache().m_ptrs.clear(); + } /** * copy the values stored in rhs.m_data to m_data diff --git a/include/chaiscript/dispatchkit/dispatchkit.hpp b/include/chaiscript/dispatchkit/dispatchkit.hpp index 8ab919a..8e7d607 100644 --- a/include/chaiscript/dispatchkit/dispatchkit.hpp +++ b/include/chaiscript/dispatchkit/dispatchkit.hpp @@ -167,6 +167,11 @@ namespace chaiscript stack.get<1>().push_back(Scope()); } + ~Dispatch_Engine() + { + Boxed_Value::clear_cache(); + } + /** * Add a new named Proxy_Function to the system */ diff --git a/include/chaiscript/language/chaiscript_engine.hpp b/include/chaiscript/language/chaiscript_engine.hpp index 51f02e9..12d23cb 100644 --- a/include/chaiscript/language/chaiscript_engine.hpp +++ b/include/chaiscript/language/chaiscript_engine.hpp @@ -9,24 +9,102 @@ #include #include - +#include #include "chaiscript_prelude.hpp" #include "chaiscript_parser.hpp" namespace chaiscript { + struct load_module_error : std::runtime_error + { + load_module_error(const std::string &reason) throw() + : std::runtime_error(reason) + { + } + + virtual ~load_module_error() throw() + { + } + }; + +#ifdef _POSIX_VERSION + struct Loadable_Module + { + struct DLModule + { + DLModule(const std::string &t_filename) + : m_data(dlopen(t_filename.c_str(), RTLD_NOW)) + { + if (!m_data) + { + throw load_module_error(dlerror()); + } + } + + ~DLModule() + { + dlclose(m_data); + } + + void *m_data; + }; + + template + struct DLSym + { + DLSym(DLModule &t_mod, const std::string &t_symbol) + : m_symbol(reinterpret_cast(dlsym(t_mod.m_data, t_symbol.c_str()))) + { + } + + T m_symbol; + }; + + Loadable_Module(const std::string &t_module_name, const std::string &t_filename) + : m_dlmodule(t_filename), m_func(m_dlmodule, "create_chaiscript_module_" + t_module_name) + { + } + + ModulePtr get() + { + return m_func.m_symbol(); + } + + DLModule m_dlmodule; + DLSym m_func; + }; +#else + struct Loadable_Module + { + Loadable_Module(const std::string &t_module_name, const std::string &t_filename) + { + throw load_module_error("Loadable module support not available for your platform"); + } + + ModulePtr get() + { + throw load_module_error("Loadable module support not available for your platform"); + } + }; + +#endif + + typedef boost::shared_ptr Loadable_Module_Ptr; + + template class ChaiScript_System { - Eval_Engine engine; - - std::set loaded_files; - #ifndef CHAISCRIPT_NO_THREADS mutable boost::shared_mutex mutex; mutable boost::recursive_mutex use_mutex; #endif + std::set loaded_files; + std::map loaded_modules; + + Eval_Engine engine; + /** * Evaluates the given string in by parsing it and running the results through the evaluator @@ -127,6 +205,57 @@ namespace chaiscript return *this; } + /** + * Load a dynamic library containing a chaiscript module + */ + void load_module(const std::string &t_module_name) + { + std::vector prefixes; + prefixes.push_back("lib"); + prefixes.push_back(""); + + std::vector postfixes; + postfixes.push_back(".dll"); + postfixes.push_back(".so"); + postfixes.push_back(""); + + for (size_t i = 0; i < prefixes.size(); ++i) + { + for (size_t j = 0; j < postfixes.size(); ++j) + { + try { + std::string name = prefixes[i] + t_module_name + postfixes[j]; + load_module(t_module_name, name); + return; + } catch (const load_module_error &) { + // Try next set + } + } + } + + throw load_module_error("Unable to find module: " + t_module_name); + } + + /** + * Load a dynamic library and provide the file name to load it from + */ + void load_module(const std::string &t_module_name, const std::string &t_filename) + { +#ifndef CHAISCRIPT_NO_THREADS + boost::lock_guard l(use_mutex); +#endif + + if (loaded_modules.count(t_module_name) == 0) + { + Loadable_Module_Ptr lm(new Loadable_Module(t_module_name, t_filename)); + loaded_modules[t_module_name] = lm; + add(lm->get()); + } else { + engine.sync_cache(); + } + } + + /** * Helper for calling script code as if it were native C++ code * example: @@ -222,6 +351,17 @@ namespace chaiscript engine.add(fun(boost::function(boost::bind(&Eval_Engine::function_exists, boost::ref(engine), _1))), "function_exists"); + + engine.add(fun(boost::function( + boost::bind(static_cast::*)(const std::string&)>( + &ChaiScript_System::load_module), boost::ref(*this), _1))), + "load_module"); + + engine.add(fun(boost::function( + boost::bind(static_cast::*)(const std::string&, const std::string&)>( + &ChaiScript_System::load_module), boost::ref(*this), _1, _2))), + "load_module"); + engine.add(vector_type >("Vector")); engine.add(string_type("string")); engine.add(map_type >("Map")); diff --git a/src/main.cpp b/src/main.cpp index 9c1fbc5..8daba2c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -23,6 +23,7 @@ int main(int argc, char *argv[]) { chai.add(chaiscript::bootstrap::list_type >("List")); + if (argc < 2) { std::cout << "eval> "; std::getline(std::cin, input); diff --git a/src/test_module.cpp b/src/test_module.cpp new file mode 100644 index 0000000..f561fd0 --- /dev/null +++ b/src/test_module.cpp @@ -0,0 +1,19 @@ + +#include +#include + +std::string hello_world() +{ + return "Hello World"; +} + +extern "C" +{ + chaiscript::ModulePtr create_chaiscript_module_test() + { + chaiscript::ModulePtr m(new chaiscript::Module()); + + m->add(chaiscript::fun(hello_world), "hello_world"); + return m; + } +} diff --git a/unittests/load_module.chai b/unittests/load_module.chai new file mode 100644 index 0000000..7f1ebbc --- /dev/null +++ b/unittests/load_module.chai @@ -0,0 +1,2 @@ +load_module("test", "./libtest.so") +print(hello_world()) diff --git a/unittests/load_module.txt b/unittests/load_module.txt new file mode 100644 index 0000000..557db03 --- /dev/null +++ b/unittests/load_module.txt @@ -0,0 +1 @@ +Hello World