chaiscript_engine.hpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710
  1. // This file is distributed under the BSD License.
  2. // See "license.txt" for details.
  3. // Copyright 2009-2012, Jonathan Turner ([email protected])
  4. // Copyright 2009-2017, Jason Turner ([email protected])
  5. // http://www.chaiscript.com
  6. // This is an open source non-commercial project. Dear PVS-Studio, please check it.
  7. // PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com
  8. #ifndef CHAISCRIPT_ENGINE_HPP_
  9. #define CHAISCRIPT_ENGINE_HPP_
  10. #include <cassert>
  11. #include <exception>
  12. #include <fstream>
  13. #include <functional>
  14. #include <map>
  15. #include <memory>
  16. #include <mutex>
  17. #include <set>
  18. #include <stdexcept>
  19. #include <vector>
  20. #include <cstring>
  21. #include "../chaiscript_defines.hpp"
  22. #include "../chaiscript_threading.hpp"
  23. #include "../dispatchkit/boxed_cast_helper.hpp"
  24. #include "../dispatchkit/boxed_value.hpp"
  25. #include "../dispatchkit/dispatchkit.hpp"
  26. #include "../dispatchkit/type_conversions.hpp"
  27. #include "../dispatchkit/proxy_functions.hpp"
  28. #include "chaiscript_common.hpp"
  29. #if defined(__linux__) || defined(__unix__) || defined(__APPLE__) || defined(__HAIKU__)
  30. #include <unistd.h>
  31. #endif
  32. #if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__)
  33. #include <dlfcn.h>
  34. #endif
  35. #if defined(CHAISCRIPT_NO_DYNLOAD)
  36. #include "chaiscript_unknown.hpp"
  37. #elif defined(CHAISCRIPT_WINDOWS)
  38. #include "chaiscript_windows.hpp"
  39. #elif _POSIX_VERSION
  40. #include "chaiscript_posix.hpp"
  41. #else
  42. #include "chaiscript_unknown.hpp"
  43. #endif
  44. #include "../dispatchkit/exception_specification.hpp"
  45. namespace chaiscript
  46. {
  47. namespace detail
  48. {
  49. typedef std::shared_ptr<Loadable_Module> Loadable_Module_Ptr;
  50. }
  51. /// \brief The main object that the ChaiScript user will use.
  52. class ChaiScript_Basic {
  53. mutable chaiscript::detail::threading::shared_mutex m_mutex;
  54. mutable chaiscript::detail::threading::recursive_mutex m_use_mutex;
  55. std::set<std::string> m_used_files;
  56. std::map<std::string, detail::Loadable_Module_Ptr> m_loaded_modules;
  57. std::set<std::string> m_active_loaded_modules;
  58. std::vector<std::string> m_module_paths;
  59. std::vector<std::string> m_use_paths;
  60. std::unique_ptr<parser::ChaiScript_Parser_Base> m_parser;
  61. chaiscript::detail::Dispatch_Engine m_engine;
  62. /// Evaluates the given string in by parsing it and running the results through the evaluator
  63. Boxed_Value do_eval(const std::string &t_input, const std::string &t_filename = "__EVAL__", bool /* t_internal*/ = false)
  64. {
  65. try {
  66. const auto p = m_parser->parse(t_input, t_filename);
  67. return p->eval(chaiscript::detail::Dispatch_State(m_engine));
  68. }
  69. catch (chaiscript::eval::detail::Return_Value &rv) {
  70. return rv.retval;
  71. }
  72. }
  73. /// Evaluates the given file and looks in the 'use' paths
  74. const Boxed_Value internal_eval_file(const std::string &t_filename) {
  75. for (const auto &path : m_use_paths)
  76. {
  77. try {
  78. const auto appendedpath = path + t_filename;
  79. return do_eval(load_file(appendedpath), appendedpath, true);
  80. } catch (const exception::file_not_found_error &) {
  81. // failed to load, try the next path
  82. } catch (const exception::eval_error &t_ee) {
  83. throw Boxed_Value(t_ee);
  84. }
  85. }
  86. // failed to load by any name
  87. throw exception::file_not_found_error(t_filename);
  88. }
  89. /// Evaluates the given string, used during eval() inside of a script
  90. const Boxed_Value internal_eval(const std::string &t_e) {
  91. try {
  92. return do_eval(t_e, "__EVAL__", true);
  93. } catch (const exception::eval_error &t_ee) {
  94. throw Boxed_Value(t_ee);
  95. }
  96. }
  97. /// Returns the current evaluation m_engine
  98. chaiscript::detail::Dispatch_Engine &get_eval_engine() {
  99. return m_engine;
  100. }
  101. /// Builds all the requirements for ChaiScript, including its evaluator and a run of its prelude.
  102. void build_eval_system(const ModulePtr &t_lib, const std::vector<Options> &t_opts) {
  103. if (t_lib)
  104. {
  105. add(t_lib);
  106. }
  107. m_engine.add(fun([this](){ m_engine.dump_system(); }), "dump_system");
  108. m_engine.add(fun([this](const Boxed_Value &t_bv){ m_engine.dump_object(t_bv); }), "dump_object");
  109. m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_type){ return m_engine.is_type(t_bv, t_type); }), "is_type");
  110. m_engine.add(fun([this](const Boxed_Value &t_bv){ return m_engine.type_name(t_bv); }), "type_name");
  111. m_engine.add(fun([this](const std::string &t_f){ return m_engine.function_exists(t_f); }), "function_exists");
  112. m_engine.add(fun([this](){ return m_engine.get_function_objects(); }), "get_functions");
  113. m_engine.add(fun([this](){ return m_engine.get_scripting_objects(); }), "get_objects");
  114. m_engine.add(
  115. dispatch::make_dynamic_proxy_function(
  116. [this](const std::vector<Boxed_Value> &t_params) {
  117. return m_engine.call_exists(t_params);
  118. })
  119. , "call_exists");
  120. m_engine.add(fun(
  121. [=](const dispatch::Proxy_Function_Base &t_fun, const std::vector<Boxed_Value> &t_params) -> Boxed_Value {
  122. Type_Conversions_State s(this->m_engine.conversions(), this->m_engine.conversions().conversion_saves());
  123. return t_fun(t_params, s);
  124. }), "call");
  125. m_engine.add(fun([this](const Type_Info &t_ti){ return m_engine.get_type_name(t_ti); }), "name");
  126. m_engine.add(fun([this](const std::string &t_type_name, bool t_throw){ return m_engine.get_type(t_type_name, t_throw); }), "type");
  127. m_engine.add(fun([this](const std::string &t_type_name){ return m_engine.get_type(t_type_name, true); }), "type");
  128. m_engine.add(fun(
  129. [=](const Type_Info &t_from, const Type_Info &t_to, const std::function<Boxed_Value (const Boxed_Value &)> &t_func) {
  130. m_engine.add(chaiscript::type_conversion(t_from, t_to, t_func));
  131. }
  132. ), "add_type_conversion");
  133. if (std::find(t_opts.begin(), t_opts.end(), Options::No_Load_Modules) == t_opts.end()
  134. && std::find(t_opts.begin(), t_opts.end(), Options::Load_Modules) != t_opts.end())
  135. {
  136. m_engine.add(fun([this](const std::string &t_module, const std::string &t_file){ return load_module(t_module, t_file); }), "load_module");
  137. m_engine.add(fun([this](const std::string &t_module){ return load_module(t_module); }), "load_module");
  138. }
  139. if (std::find(t_opts.begin(), t_opts.end(), Options::No_External_Scripts) == t_opts.end()
  140. && std::find(t_opts.begin(), t_opts.end(), Options::External_Scripts) != t_opts.end())
  141. {
  142. m_engine.add(fun([this](const std::string &t_file){ return use(t_file); }), "use");
  143. m_engine.add(fun([this](const std::string &t_file){ return internal_eval_file(t_file); }), "eval_file");
  144. }
  145. m_engine.add(fun([this](const std::string &t_str){ return internal_eval(t_str); }), "eval");
  146. m_engine.add(fun([this](const AST_Node &t_ast){ return eval(t_ast); }), "eval");
  147. m_engine.add(fun([this](const std::string &t_str, const bool t_dump){ return parse(t_str, t_dump); }), "parse");
  148. m_engine.add(fun([this](const std::string &t_str){ return parse(t_str); }), "parse");
  149. m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global_const(t_bv, t_name); }), "add_global_const");
  150. m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ add_global(t_bv, t_name); }), "add_global");
  151. m_engine.add(fun([this](const Boxed_Value &t_bv, const std::string &t_name){ set_global(t_bv, t_name); }), "set_global");
  152. }
  153. /// Helper function for loading a file
  154. static std::string load_file(const std::string &t_filename) {
  155. std::ifstream infile(t_filename.c_str(), std::ios::in | std::ios::ate | std::ios::binary );
  156. if (!infile.is_open()) {
  157. throw chaiscript::exception::file_not_found_error(t_filename);
  158. }
  159. const auto size = infile.tellg();
  160. infile.seekg(0, std::ios::beg);
  161. assert(size >= 0);
  162. if (size == std::streampos(0))
  163. {
  164. return std::string();
  165. } else {
  166. std::vector<char> v(static_cast<size_t>(size));
  167. infile.read(&v[0], size);
  168. return std::string(v.begin(), v.end());
  169. }
  170. }
  171. std::vector<std::string> ensure_minimum_path_vec(std::vector<std::string> paths)
  172. {
  173. if (paths.empty()) { return {""}; }
  174. else { return paths; }
  175. }
  176. public:
  177. /// \brief Constructor for ChaiScript
  178. /// \param[in] t_lib Standard library to apply to this ChaiScript instance
  179. /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module
  180. /// \param[in] t_usepaths Vector of paths to search when attempting to "use" an included ChaiScript file
  181. ChaiScript_Basic(const ModulePtr &t_lib,
  182. std::unique_ptr<parser::ChaiScript_Parser_Base> &&parser,
  183. std::vector<std::string> t_module_paths = {},
  184. std::vector<std::string> t_use_paths = {},
  185. const std::vector<chaiscript::Options> &t_opts = chaiscript::default_options())
  186. : m_module_paths(ensure_minimum_path_vec(std::move(t_module_paths))),
  187. m_use_paths(ensure_minimum_path_vec(std::move(t_use_paths))),
  188. m_parser(std::move(parser)),
  189. m_engine(*m_parser)
  190. {
  191. #if !defined(CHAISCRIPT_NO_DYNLOAD) && defined(_POSIX_VERSION) && !defined(__CYGWIN__)
  192. // If on Unix, add the path of the current executable to the module search path
  193. // as windows would do
  194. union cast_union
  195. {
  196. Boxed_Value (ChaiScript_Basic::*in_ptr)(const std::string&);
  197. void *out_ptr;
  198. };
  199. Dl_info rInfo;
  200. memset( &rInfo, 0, sizeof(rInfo) );
  201. cast_union u;
  202. u.in_ptr = &ChaiScript_Basic::use;
  203. if ( (dladdr(static_cast<void*>(u.out_ptr), &rInfo) != 0) && (rInfo.dli_fname != nullptr) ) {
  204. std::string dllpath(rInfo.dli_fname);
  205. const size_t lastslash = dllpath.rfind('/');
  206. if (lastslash != std::string::npos)
  207. {
  208. dllpath.erase(lastslash);
  209. }
  210. // Let's see if this is a link that we should expand
  211. std::vector<char> buf(2048);
  212. const auto pathlen = readlink(dllpath.c_str(), &buf.front(), buf.size());
  213. if (pathlen > 0 && static_cast<size_t>(pathlen) < buf.size())
  214. {
  215. dllpath = std::string(&buf.front(), static_cast<size_t>(pathlen));
  216. }
  217. m_module_paths.insert(m_module_paths.begin(), dllpath+"/");
  218. }
  219. #endif
  220. build_eval_system(t_lib, t_opts);
  221. }
  222. #ifndef CHAISCRIPT_NO_DYNLOAD
  223. /// \brief Constructor for ChaiScript.
  224. ///
  225. /// This version of the ChaiScript constructor attempts to find the stdlib module to load
  226. /// at runtime generates an error if it cannot be found.
  227. ///
  228. /// \param[in] t_modulepaths Vector of paths to search when attempting to load a binary module
  229. /// \param[in] t_usepaths Vector of paths to search when attempting to "use" an included ChaiScript file
  230. explicit ChaiScript_Basic(std::unique_ptr<parser::ChaiScript_Parser_Base> &&parser,
  231. std::vector<std::string> t_module_paths = {},
  232. std::vector<std::string> t_use_paths = {},
  233. const std::vector<chaiscript::Options> &t_opts = chaiscript::default_options())
  234. : ChaiScript_Basic({}, std::move(parser), t_module_paths, t_use_paths, t_opts)
  235. {
  236. try {
  237. // attempt to load the stdlib
  238. load_module("chaiscript_stdlib-" + Build_Info::version());
  239. } catch (const exception::load_module_error &t_err) {
  240. std::cout << "An error occured while trying to load the chaiscript standard library.\n"
  241. << "\n"
  242. << "You must either provide a standard library, or compile it in.\n"
  243. << "For an example of compiling the standard library in,\n"
  244. << "see: https://gist.github.com/lefticus/9456197\n"
  245. << "Compiling the stdlib in is the recommended and MOST SUPPORTED method.\n"
  246. << "\n"
  247. << "\n"
  248. << t_err.what();
  249. throw;
  250. }
  251. }
  252. #else // CHAISCRIPT_NO_DYNLOAD
  253. explicit ChaiScript_Basic(std::unique_ptr<parser::ChaiScript_Parser_Base> &&parser,
  254. std::vector<std::string> t_module_paths = {},
  255. std::vector<std::string> t_use_paths = {},
  256. const std::vector<chaiscript::Options> &t_opts = chaiscript::default_options()) = delete;
  257. #endif
  258. parser::ChaiScript_Parser_Base &get_parser()
  259. {
  260. return *m_parser;
  261. }
  262. const Boxed_Value eval(const AST_Node &t_ast)
  263. {
  264. try {
  265. return t_ast.eval(chaiscript::detail::Dispatch_State(m_engine));
  266. } catch (const exception::eval_error &t_ee) {
  267. throw Boxed_Value(t_ee);
  268. }
  269. }
  270. AST_NodePtr parse(const std::string &t_input, const bool t_debug_print = false)
  271. {
  272. auto ast = m_parser->parse(t_input, "PARSE");
  273. if (t_debug_print) {
  274. m_parser->debug_print(*ast);
  275. }
  276. return ast;
  277. }
  278. std::string get_type_name(const Type_Info &ti) const
  279. {
  280. return m_engine.get_type_name(ti);
  281. }
  282. template<typename T>
  283. std::string get_type_name() const
  284. {
  285. return get_type_name(user_type<T>());
  286. }
  287. /// \brief Loads and parses a file. If the file is already, it is not reloaded
  288. /// The use paths specified at ChaiScript construction time are searched for the
  289. /// requested file.
  290. ///
  291. /// \param[in] t_filename Filename to load and evaluate
  292. Boxed_Value use(const std::string &t_filename)
  293. {
  294. for (const auto &path : m_use_paths)
  295. {
  296. try {
  297. const auto appendedpath = path + t_filename;
  298. chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
  299. chaiscript::detail::threading::unique_lock<chaiscript::detail::threading::shared_mutex> l2(m_mutex);
  300. Boxed_Value retval;
  301. if (m_used_files.count(appendedpath) == 0)
  302. {
  303. l2.unlock();
  304. retval = eval_file(appendedpath);
  305. l2.lock();
  306. m_used_files.insert(appendedpath);
  307. }
  308. return retval; // return, we loaded it, or it was already loaded
  309. } catch (const exception::file_not_found_error &) {
  310. // failed to load, try the next path
  311. }
  312. }
  313. // failed to load by any name
  314. throw exception::file_not_found_error(t_filename);
  315. }
  316. /// \brief Adds a constant object that is available in all contexts and to all threads
  317. /// \param[in] t_bv Boxed_Value to add as a global
  318. /// \param[in] t_name Name of the value to add
  319. /// \throw chaiscript::exception::global_non_const If t_bv is not a constant object
  320. /// \sa Boxed_Value::is_const
  321. ChaiScript_Basic &add_global_const(const Boxed_Value &t_bv, const std::string &t_name)
  322. {
  323. Name_Validator::validate_object_name(t_name);
  324. m_engine.add_global_const(t_bv, t_name);
  325. return *this;
  326. }
  327. /// \brief Adds a mutable object that is available in all contexts and to all threads
  328. /// \param[in] t_bv Boxed_Value to add as a global
  329. /// \param[in] t_name Name of the value to add
  330. /// \warning The user is responsible for making sure the object is thread-safe if necessary
  331. /// ChaiScript is thread-safe but provides no threading locking mechanism to the script
  332. ChaiScript_Basic &add_global(const Boxed_Value &t_bv, const std::string &t_name)
  333. {
  334. Name_Validator::validate_object_name(t_name);
  335. m_engine.add_global(t_bv, t_name);
  336. return *this;
  337. }
  338. ChaiScript_Basic &set_global(const Boxed_Value &t_bv, const std::string &t_name)
  339. {
  340. Name_Validator::validate_object_name(t_name);
  341. m_engine.set_global(t_bv, t_name);
  342. return *this;
  343. }
  344. /// \brief Represents the current state of the ChaiScript system. State and be saved and restored
  345. /// \warning State object does not contain the user defined type conversions of the engine. They
  346. /// are left out due to performance considerations involved in tracking the state
  347. /// \sa ChaiScript::get_state
  348. /// \sa ChaiScript::set_state
  349. struct State
  350. {
  351. std::set<std::string> used_files;
  352. chaiscript::detail::Dispatch_Engine::State engine_state;
  353. std::set<std::string> active_loaded_modules;
  354. };
  355. /// \brief Returns a state object that represents the current state of the global system
  356. ///
  357. /// The global system includes the reserved words, global const objects, functions and types.
  358. /// local variables are thread specific and not included.
  359. ///
  360. /// \return Current state of the global system
  361. ///
  362. /// \b Example:
  363. ///
  364. /// \code
  365. /// chaiscript::ChaiScript chai;
  366. /// chaiscript::ChaiScript::State s = chai.get_state(); // represents bootstrapped initial state
  367. /// \endcode
  368. State get_state() const
  369. {
  370. chaiscript::detail::threading::lock_guard<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
  371. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l2(m_mutex);
  372. State s;
  373. s.used_files = m_used_files;
  374. s.engine_state = m_engine.get_state();
  375. s.active_loaded_modules = m_active_loaded_modules;
  376. return s;
  377. }
  378. /// \brief Sets the state of the system
  379. ///
  380. /// The global system includes the reserved words, global objects, functions and types.
  381. /// local variables are thread specific and not included.
  382. ///
  383. /// \param[in] t_state New state to set
  384. ///
  385. /// \b Example:
  386. /// \code
  387. /// chaiscript::ChaiScript chai;
  388. /// chaiscript::ChaiScript::State s = chai.get_state(); // get initial state
  389. /// chai.add(chaiscript::fun(&somefunction), "somefunction");
  390. /// chai.set_state(s); // restore initial state, which does not have the recently added "somefunction"
  391. /// \endcode
  392. void set_state(const State &t_state)
  393. {
  394. chaiscript::detail::threading::lock_guard<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
  395. chaiscript::detail::threading::shared_lock<chaiscript::detail::threading::shared_mutex> l2(m_mutex);
  396. m_used_files = t_state.used_files;
  397. m_active_loaded_modules = t_state.active_loaded_modules;
  398. m_engine.set_state(t_state.engine_state);
  399. }
  400. /// \returns All values in the local thread state, added through the add() function
  401. std::map<std::string, Boxed_Value> get_locals() const
  402. {
  403. return m_engine.get_locals();
  404. }
  405. /// \brief Sets all of the locals for the current thread state.
  406. ///
  407. /// \param[in] t_locals The map<name, value> set of variables to replace the current state with
  408. ///
  409. /// Any existing locals are removed and the given set of variables is added
  410. void set_locals(const std::map<std::string, Boxed_Value> &t_locals)
  411. {
  412. m_engine.set_locals(t_locals);
  413. }
  414. /// \brief Adds a type, function or object to ChaiScript. Objects are added to the local thread state.
  415. /// \param[in] t_t Item to add
  416. /// \param[in] t_name Name of item to add
  417. /// \returns Reference to current ChaiScript object
  418. ///
  419. /// \b Examples:
  420. /// \code
  421. /// chaiscript::ChaiScript chai;
  422. /// chai.add(chaiscript::user_type<MyClass>(), "MyClass"); // Add explicit type info (not strictly necessary)
  423. /// chai.add(chaiscript::fun(&MyClass::function), "function"); // Add a class method
  424. /// MyClass obj;
  425. /// chai.add(chaiscript::var(&obj), "obj"); // Add a pointer to a locally defined object
  426. /// \endcode
  427. ///
  428. /// \sa \ref adding_items
  429. template<typename T>
  430. ChaiScript_Basic &add(const T &t_t, const std::string &t_name)
  431. {
  432. Name_Validator::validate_object_name(t_name);
  433. m_engine.add(t_t, t_name);
  434. return *this;
  435. }
  436. /// \brief Add a new conversion for upcasting to a base class
  437. /// \sa chaiscript::base_class
  438. /// \param[in] d Base class / parent class
  439. ///
  440. /// \b Example:
  441. /// \code
  442. /// chaiscript::ChaiScript chai;
  443. /// chai.add(chaiscript::base_class<std::runtime_error, chaiscript::dispatch_error>());
  444. /// \endcode
  445. ChaiScript_Basic &add(const Type_Conversion &d)
  446. {
  447. m_engine.add(d);
  448. return *this;
  449. }
  450. /// \brief Adds all elements of a module to ChaiScript runtime
  451. /// \param[in] t_p The module to add.
  452. /// \sa chaiscript::Module
  453. ChaiScript_Basic &add(const ModulePtr &t_p)
  454. {
  455. t_p->apply(*this, this->get_eval_engine());
  456. return *this;
  457. }
  458. /// \brief Load a binary module from a dynamic library. Works on platforms that support
  459. /// dynamic libraries.
  460. /// \param[in] t_module_name Name of the module to load
  461. ///
  462. /// The module is searched for in the registered module path folders (chaiscript::ChaiScript::ChaiScript)
  463. /// and with standard prefixes and postfixes: ("lib"|"")\<t_module_name\>(".dll"|".so"|".bundle"|"").
  464. ///
  465. /// Once the file is located, the system looks for the symbol "create_chaiscript_module_\<t_module_name\>".
  466. /// If no file can be found matching the search criteria and containing the appropriate entry point
  467. /// (the symbol mentioned above), an exception is thrown.
  468. ///
  469. /// \throw chaiscript::exception::load_module_error In the event that no matching module can be found.
  470. std::string load_module(const std::string &t_module_name)
  471. {
  472. #ifdef CHAISCRIPT_NO_DYNLOAD
  473. (void)t_module_name; // -Wunused-parameter
  474. throw chaiscript::exception::load_module_error("Loadable module support was disabled (CHAISCRIPT_NO_DYNLOAD)");
  475. #else
  476. std::vector<exception::load_module_error> errors;
  477. std::string version_stripped_name = t_module_name;
  478. size_t version_pos = version_stripped_name.find("-" + Build_Info::version());
  479. if (version_pos != std::string::npos)
  480. {
  481. version_stripped_name.erase(version_pos);
  482. }
  483. std::vector<std::string> prefixes{"lib", "cyg", ""};
  484. std::vector<std::string> postfixes{".dll", ".so", ".bundle", ""};
  485. for (auto & elem : m_module_paths)
  486. {
  487. for (auto & prefix : prefixes)
  488. {
  489. for (auto & postfix : postfixes)
  490. {
  491. try {
  492. const auto name = elem + prefix + t_module_name + postfix;
  493. // std::cerr << "trying location: " << name << '\n';
  494. load_module(version_stripped_name, name);
  495. return name;
  496. } catch (const chaiscript::exception::load_module_error &e) {
  497. // std::cerr << "error: " << e.what() << '\n';
  498. errors.push_back(e);
  499. // Try next set
  500. }
  501. }
  502. }
  503. }
  504. throw chaiscript::exception::load_module_error(t_module_name, errors);
  505. #endif
  506. }
  507. /// \brief Load a binary module from a dynamic library. Works on platforms that support
  508. /// dynamic libraries.
  509. ///
  510. /// \param[in] t_module_name Module name to load
  511. /// \param[in] t_filename Ignore normal filename search process and use specific filename
  512. ///
  513. /// \sa ChaiScript::load_module(const std::string &t_module_name)
  514. void load_module(const std::string &t_module_name, const std::string &t_filename)
  515. {
  516. chaiscript::detail::threading::lock_guard<chaiscript::detail::threading::recursive_mutex> l(m_use_mutex);
  517. if (m_loaded_modules.count(t_module_name) == 0)
  518. {
  519. detail::Loadable_Module_Ptr lm(new detail::Loadable_Module(t_module_name, t_filename));
  520. m_loaded_modules[t_module_name] = lm;
  521. m_active_loaded_modules.insert(t_module_name);
  522. add(lm->m_moduleptr);
  523. } else if (m_active_loaded_modules.count(t_module_name) == 0) {
  524. m_active_loaded_modules.insert(t_module_name);
  525. add(m_loaded_modules[t_module_name]->m_moduleptr);
  526. }
  527. }
  528. /// \brief Evaluates a string. Equivalent to ChaiScript::eval.
  529. ///
  530. /// \param[in] t_script Script to execute
  531. /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
  532. ///
  533. /// \return result of the script execution
  534. ///
  535. /// \throw chaiscript::exception::eval_error In the case that evaluation fails.
  536. Boxed_Value operator()(const std::string &t_script, const Exception_Handler &t_handler = Exception_Handler())
  537. {
  538. return eval(t_script, t_handler);
  539. }
  540. /// \brief Evaluates a string and returns a typesafe result.
  541. ///
  542. /// \tparam T Type to extract from the result value of the script execution
  543. /// \param[in] t_input Script to execute
  544. /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
  545. /// \param[in] t_filename Optional filename to report to the user for where the error occured. Useful
  546. /// in special cases where you are loading a file internally instead of using eval_file
  547. ///
  548. /// \return result of the script execution
  549. ///
  550. /// \throw chaiscript::exception::eval_error In the case that evaluation fails.
  551. /// \throw chaiscript::exception::bad_boxed_cast In the case that evaluation succeeds but the result value cannot be converted
  552. /// to the requested type.
  553. template<typename T>
  554. T eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename="__EVAL__")
  555. {
  556. return m_engine.boxed_cast<T>(eval(t_input, t_handler, t_filename));
  557. }
  558. /// \brief casts an object while applying any Dynamic_Conversion available
  559. template<typename Type>
  560. decltype(auto) boxed_cast(const Boxed_Value &bv) const
  561. {
  562. return(m_engine.boxed_cast<Type>(bv));
  563. }
  564. /// \brief Evaluates a string.
  565. ///
  566. /// \param[in] t_input Script to execute
  567. /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
  568. /// \param[in] t_filename Optional filename to report to the user for where the error occurred. Useful
  569. /// in special cases where you are loading a file internally instead of using eval_file
  570. ///
  571. /// \return result of the script execution
  572. ///
  573. /// \throw exception::eval_error In the case that evaluation fails.
  574. Boxed_Value eval(const std::string &t_input, const Exception_Handler &t_handler = Exception_Handler(), const std::string &t_filename="__EVAL__")
  575. {
  576. try {
  577. return do_eval(t_input, t_filename);
  578. } catch (Boxed_Value &bv) {
  579. if (t_handler) {
  580. t_handler->handle(bv, m_engine);
  581. }
  582. throw;
  583. }
  584. }
  585. /// \brief Loads the file specified by filename, evaluates it, and returns the result.
  586. /// \param[in] t_filename File to load and parse.
  587. /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
  588. /// \return result of the script execution
  589. /// \throw chaiscript::exception::eval_error In the case that evaluation fails.
  590. Boxed_Value eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) {
  591. return eval(load_file(t_filename), t_handler, t_filename);
  592. }
  593. /// \brief Loads the file specified by filename, evaluates it, and returns the type safe result.
  594. /// \tparam T Type to extract from the result value of the script execution
  595. /// \param[in] t_filename File to load and parse.
  596. /// \param[in] t_handler Optional Exception_Handler used for automatic unboxing of script thrown exceptions
  597. /// \return result of the script execution
  598. /// \throw chaiscript::exception::eval_error In the case that evaluation fails.
  599. /// \throw chaiscript::exception::bad_boxed_cast In the case that evaluation succeeds but the result value cannot be converted
  600. /// to the requested type.
  601. template<typename T>
  602. T eval_file(const std::string &t_filename, const Exception_Handler &t_handler = Exception_Handler()) {
  603. return m_engine.boxed_cast<T>(eval_file(t_filename, t_handler));
  604. }
  605. };
  606. }
  607. #endif /* CHAISCRIPT_ENGINE_HPP_ */