Add support for loadable modules on POSIX systems
This commit is contained in:
@@ -17,8 +17,12 @@ if (Boost_FOUND)
|
|||||||
include_directories(${Boost_INCLUDE_DIRS})
|
include_directories(${Boost_INCLUDE_DIRS})
|
||||||
add_executable(chaiscript_eval src/main.cpp)
|
add_executable(chaiscript_eval src/main.cpp)
|
||||||
#add_executable(dispatchkit_test contrib/test/dispatchkit_test.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)
|
install(TARGETS chaiscript_eval DESTINATION ${CMAKE_CURRENT_SOURCE_DIR}/bin)
|
||||||
|
|
||||||
else(Boost_FOUND)
|
else(Boost_FOUND)
|
||||||
message(FATAL_ERROR "Can not find Boost")
|
message(FATAL_ERROR "Can not find Boost")
|
||||||
endif(Boost_FOUND)
|
endif(Boost_FOUND)
|
||||||
|
@@ -23,6 +23,8 @@
|
|||||||
#include "dispatchkit/function_call.hpp"
|
#include "dispatchkit/function_call.hpp"
|
||||||
namespace chaiscript
|
namespace chaiscript
|
||||||
{
|
{
|
||||||
|
typedef ModulePtr (*Create_Module_Func)();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types of AST nodes available to the parser and eval
|
* Types of AST nodes available to the parser and eval
|
||||||
*/
|
*/
|
||||||
|
@@ -18,6 +18,11 @@ namespace chaiscript
|
|||||||
class Thread_Storage
|
class Thread_Storage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
~Thread_Storage()
|
||||||
|
{
|
||||||
|
m_thread_storage.reset();
|
||||||
|
}
|
||||||
|
|
||||||
inline T *operator->() const
|
inline T *operator->() const
|
||||||
{
|
{
|
||||||
if (!m_thread_storage.get())
|
if (!m_thread_storage.get())
|
||||||
|
@@ -218,14 +218,16 @@ namespace chaiscript
|
|||||||
* Drop objects from the cache where there is only one (ie, our)
|
* Drop objects from the cache where there is only one (ie, our)
|
||||||
* reference to it, so it may be destructed
|
* reference to it, so it may be destructed
|
||||||
*/
|
*/
|
||||||
void cull()
|
void cull(bool force = false)
|
||||||
{
|
{
|
||||||
|
|
||||||
++m_cullcount;
|
++m_cullcount;
|
||||||
if (m_cullcount % 10 != 0)
|
if (force || m_cullcount % 10 != 0)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
std::map<const void *, Data >::iterator itr = m_ptrs.begin();
|
std::map<const void *, Data >::iterator itr = m_ptrs.begin();
|
||||||
|
|
||||||
while (itr != m_ptrs.end())
|
while (itr != m_ptrs.end())
|
||||||
@@ -279,12 +281,17 @@ namespace chaiscript
|
|||||||
/**
|
/**
|
||||||
* Return a reference to the static global Object_Cache
|
* 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<Object_Cache> oc;
|
static chaiscript::threading::Thread_Storage<Object_Cache> oc;
|
||||||
return *oc;
|
return *oc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void clear_cache()
|
||||||
|
{
|
||||||
|
get_object_cache().m_ptrs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* copy the values stored in rhs.m_data to m_data
|
* copy the values stored in rhs.m_data to m_data
|
||||||
* m_data pointers are not shared in this case
|
* m_data pointers are not shared in this case
|
||||||
|
@@ -167,6 +167,11 @@ namespace chaiscript
|
|||||||
stack.get<1>().push_back(Scope());
|
stack.get<1>().push_back(Scope());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~Dispatch_Engine()
|
||||||
|
{
|
||||||
|
Boxed_Value::clear_cache();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new named Proxy_Function to the system
|
* Add a new named Proxy_Function to the system
|
||||||
*/
|
*/
|
||||||
|
@@ -9,24 +9,102 @@
|
|||||||
|
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
#include <dlfcn.h>
|
||||||
|
|
||||||
#include "chaiscript_prelude.hpp"
|
#include "chaiscript_prelude.hpp"
|
||||||
#include "chaiscript_parser.hpp"
|
#include "chaiscript_parser.hpp"
|
||||||
|
|
||||||
namespace chaiscript
|
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<typename T>
|
||||||
|
struct DLSym
|
||||||
|
{
|
||||||
|
DLSym(DLModule &t_mod, const std::string &t_symbol)
|
||||||
|
: m_symbol(reinterpret_cast<T>(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<Create_Module_Func> 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> Loadable_Module_Ptr;
|
||||||
|
|
||||||
|
|
||||||
template <typename Eval_Engine>
|
template <typename Eval_Engine>
|
||||||
class ChaiScript_System {
|
class ChaiScript_System {
|
||||||
Eval_Engine engine;
|
|
||||||
|
|
||||||
std::set<std::string> loaded_files;
|
|
||||||
|
|
||||||
#ifndef CHAISCRIPT_NO_THREADS
|
#ifndef CHAISCRIPT_NO_THREADS
|
||||||
mutable boost::shared_mutex mutex;
|
mutable boost::shared_mutex mutex;
|
||||||
mutable boost::recursive_mutex use_mutex;
|
mutable boost::recursive_mutex use_mutex;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
std::set<std::string> loaded_files;
|
||||||
|
std::map<std::string, Loadable_Module_Ptr> loaded_modules;
|
||||||
|
|
||||||
|
Eval_Engine engine;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluates the given string in by parsing it and running the results through the evaluator
|
* Evaluates the given string in by parsing it and running the results through the evaluator
|
||||||
@@ -127,6 +205,57 @@ namespace chaiscript
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Load a dynamic library containing a chaiscript module
|
||||||
|
*/
|
||||||
|
void load_module(const std::string &t_module_name)
|
||||||
|
{
|
||||||
|
std::vector<std::string> prefixes;
|
||||||
|
prefixes.push_back("lib");
|
||||||
|
prefixes.push_back("");
|
||||||
|
|
||||||
|
std::vector<std::string> 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<boost::recursive_mutex> 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
|
* Helper for calling script code as if it were native C++ code
|
||||||
* example:
|
* example:
|
||||||
@@ -222,6 +351,17 @@ namespace chaiscript
|
|||||||
engine.add(fun(boost::function<bool (const std::string &)>(boost::bind(&Eval_Engine::function_exists, boost::ref(engine), _1))),
|
engine.add(fun(boost::function<bool (const std::string &)>(boost::bind(&Eval_Engine::function_exists, boost::ref(engine), _1))),
|
||||||
"function_exists");
|
"function_exists");
|
||||||
|
|
||||||
|
|
||||||
|
engine.add(fun(boost::function<void (const std::string &)>(
|
||||||
|
boost::bind(static_cast<void (ChaiScript_System<Eval_Engine>::*)(const std::string&)>(
|
||||||
|
&ChaiScript_System<Eval_Engine>::load_module), boost::ref(*this), _1))),
|
||||||
|
"load_module");
|
||||||
|
|
||||||
|
engine.add(fun(boost::function<void (const std::string &, const std::string &)>(
|
||||||
|
boost::bind(static_cast<void (ChaiScript_System<Eval_Engine>::*)(const std::string&, const std::string&)>(
|
||||||
|
&ChaiScript_System<Eval_Engine>::load_module), boost::ref(*this), _1, _2))),
|
||||||
|
"load_module");
|
||||||
|
|
||||||
engine.add(vector_type<std::vector<Boxed_Value> >("Vector"));
|
engine.add(vector_type<std::vector<Boxed_Value> >("Vector"));
|
||||||
engine.add(string_type<std::string>("string"));
|
engine.add(string_type<std::string>("string"));
|
||||||
engine.add(map_type<std::map<std::string, Boxed_Value> >("Map"));
|
engine.add(map_type<std::map<std::string, Boxed_Value> >("Map"));
|
||||||
|
@@ -23,6 +23,7 @@ int main(int argc, char *argv[]) {
|
|||||||
|
|
||||||
chai.add(chaiscript::bootstrap::list_type<std::list<chaiscript::Boxed_Value> >("List"));
|
chai.add(chaiscript::bootstrap::list_type<std::list<chaiscript::Boxed_Value> >("List"));
|
||||||
|
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 2) {
|
||||||
std::cout << "eval> ";
|
std::cout << "eval> ";
|
||||||
std::getline(std::cin, input);
|
std::getline(std::cin, input);
|
||||||
|
19
src/test_module.cpp
Normal file
19
src/test_module.cpp
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
|
||||||
|
#include <chaiscript/chaiscript.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
2
unittests/load_module.chai
Normal file
2
unittests/load_module.chai
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
load_module("test", "./libtest.so")
|
||||||
|
print(hello_world())
|
1
unittests/load_module.txt
Normal file
1
unittests/load_module.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Hello World
|
Reference in New Issue
Block a user