From 4de4db0037399faf7e181a17e11fdd1e5aa7d852 Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia Date: Wed, 24 Jan 2024 00:43:03 +0100 Subject: [PATCH 01/28] Add base for CLI refactor. --- source/cli/metacallcli/CMakeLists.txt | 5 + .../include/metacallcli/application.hpp | 168 +-- source/cli/metacallcli/source/application.cpp | 1103 ++++++----------- source/cli/plugins/CMakeLists.txt | 1 + .../include/cli_core_plugin/cli_core_plugin.h | 2 +- .../source/cli_core_plugin.cpp | 24 +- .../cli_core_plugin/source/metacall.json | 10 +- .../plugins/cli_repl_plugin/CMakeLists.txt | 53 + .../cli_repl_plugin/source/cli_repl_plugin.js | 76 ++ .../cli_repl_plugin/source/metacall.json | 7 + .../plugins/cli_repl_plugin/source/parser.js | 64 + .../plugins/cli_repl_plugin/source/test.js | 46 + .../plugin_extension/plugin_extension.h | 2 +- .../source/plugin_extension.cpp | 12 +- source/loader/include/loader/loader.h | 2 +- source/loader/source/loader.c | 4 +- .../ext_loader/source/ext_loader_impl.cpp | 6 +- .../node_loader/bootstrap/lib/bootstrap.js | 2 +- .../node_loader/source/node_loader_impl.cpp | 10 +- .../source/node_loader_trampoline.cpp | 4 +- source/metacall/include/metacall/metacall.h | 60 +- source/metacall/source/metacall.c | 9 +- .../include/plugin/plugin_interface.hpp | 6 +- .../backtrace_plugin/backtrace_plugin.h | 2 +- .../source/backtrace_plugin.cpp | 3 +- .../include/sandbox_plugin/sandbox_plugin.h | 2 +- .../sandbox_plugin/source/sandbox_plugin.cpp | 4 +- .../include/sum_extension/sum_extension.h | 2 +- .../sum_extension/source/sum_extension.cpp | 9 +- source/tests/CMakeLists.txt | 1 + .../source/metacall_cli_core_plugin_test.cpp | 7 +- .../source/metacall_python_test.cpp | 6 + .../CMakeLists.txt | 140 +++ .../source/main.cpp | 28 + .../metacall_python_without_env_vars_test.cpp | 49 + 35 files changed, 993 insertions(+), 936 deletions(-) create mode 100644 source/cli/plugins/cli_repl_plugin/CMakeLists.txt create mode 100644 source/cli/plugins/cli_repl_plugin/source/cli_repl_plugin.js create mode 100644 source/cli/plugins/cli_repl_plugin/source/metacall.json create mode 100644 source/cli/plugins/cli_repl_plugin/source/parser.js create mode 100644 source/cli/plugins/cli_repl_plugin/source/test.js create mode 100644 source/tests/metacall_python_without_env_vars_test/CMakeLists.txt create mode 100644 source/tests/metacall_python_without_env_vars_test/source/main.cpp create mode 100644 source/tests/metacall_python_without_env_vars_test/source/metacall_python_without_env_vars_test.cpp diff --git a/source/cli/metacallcli/CMakeLists.txt b/source/cli/metacallcli/CMakeLists.txt index 93308ed55..c0e4555e2 100644 --- a/source/cli/metacallcli/CMakeLists.txt +++ b/source/cli/metacallcli/CMakeLists.txt @@ -173,6 +173,11 @@ add_loader_dependencies(${target} ts_loader ) +add_dependencies(${target} + cli_repl_plugin + cli_core_plugin +) + # # Deployment # diff --git a/source/cli/metacallcli/include/metacallcli/application.hpp b/source/cli/metacallcli/include/metacallcli/application.hpp index f9426096c..93f639c9e 100644 --- a/source/cli/metacallcli/include/metacallcli/application.hpp +++ b/source/cli/metacallcli/include/metacallcli/application.hpp @@ -41,10 +41,6 @@ class application; class application { public: - /* -- Public Type Definitions -- */ - - typedef bool (*command_callback)(application &, tokenizer &); - /* -- Public Methods -- */ /** @@ -65,51 +61,6 @@ class application */ ~application(void); - /** - * @brief - * Application script loader - * - * @param[in] tag - * Loader tag reference - * - * @param[in] script - * Reference to script name - * - * @return - * Return true on success, false otherwhise - */ - bool load(const std::string &tag, const std::string &script); - - /** - * @brief - * Application script loader from memory - * - * @param[in] tag - * Loader tag reference - * - * @param[in] script - * Script code - * - * @return - * Return true on success, false otherwhise - */ - bool load_from_memory(const std::string &tag, const std::string &script); - - /** - * @brief - * Application script clearer - * - * @param[in] tag - * Loader tag reference - * - * @param[in] script - * Reference to script name - * - * @return - * Return true on success, false otherwhise - */ - bool clear(const std::string &tag, const std::string &script); - /** * @brief * Application main entry point @@ -122,36 +73,6 @@ class application */ void shutdown(void); - /** - * @brief - * Debug command line string - * - * @param[in] key - * Name of the command line option - * - * @param[in] t - * Tokenizer wrapper of input command - */ - void command_debug(const std::string &key, const tokenizer &t); - - /** - * @brief - * Show inspect information - * - * @param[in] str - * Serialized inspect data - * - * @param[in] size - * Size in bytes of str string - * - * @param[in] size - * Size in bytes of str string - * - * @param[in] allocator - * Pointer to the allocator to be used in deserialization - */ - void command_inspect(const char *str, size_t size, void *allocator); - /** * @brief * Create a new value from arguments with parser @p @@ -164,94 +85,61 @@ class application */ void *argument_parse(parser_parameter &p); +protected: + /* -- Protected Methods -- */ + /** * @brief - * Adapts metacallv from string @name and vector @args + * Load all plugins from a subfolder @path * - * @param[in] name - * String object of function name + * @param[in] path + * Subpath where the plugins are located * - * @param[in] args - * Vector pointing to arguments + * @param[out] handle + * Pointer to the handle containing of the loaded scripts * * @return - * Return a new value instanced if argument was correct with the result of the call + * Return true if the load was successful, false otherwise + * */ - void *metacallv_adaptor(const std::string &name, const std::vector &args); + bool load_path(const char *path, void **handle); /** * @brief - * Adapts metacallfs from string @name and array string @args + * Print a value @v in a readable form on error (throwable) * - * @param[in] name - * String object of function name + * @param[in] v + * Value to be printed * - * @param[in] args - * String representing an array to be deserialized - * - * @param[in] allocator - * Pointer to the allocator to be used in deserialization - * - * @return - * Return a new value instanced if argument was correct with the result of the call */ - void *metacallfs_adaptor(const std::string &name, const std::string &args, void *allocator); + void print(void *v); /** * @brief - * Adapts metacallfs_await from string @name and array string @args + * Invoke a function and print the result on error * - * @param[in] name - * String object of function name + * @param[in] func + * Command name matching the function registered from the plugins * * @param[in] args - * String representing an array to be deserialized + * Function arguments as values * - * @param[in] allocator - * Pointer to the allocator to be used in deserialization + * @param[in] size + * Size of arguments * - * @return - * Return a new value instanced if argument was correct with the result of the call */ - void *metacallfs_await_adaptor(const std::string &name, const std::string &args, void *allocator); - -protected: - /* -- Protected Definitions -- */ - - static const size_t arguments_str_size; - - /* -- Protected Methods -- */ + void invoke(const char *func, void *args[], size_t size); /** * @brief * Execute a command with string parameters * - * @param[in out] t + * @param[inout] t * Tokenizer wrapper of input command */ void execute(tokenizer &t); - /** - * @brief - * Defines a new command with a callback handler - * - * @param[in] key - * Name of the command line option - * - * @param[in] command_cb - * Handler will be raised on @key command entered - */ - void define(const char *key, command_callback command_cb); - private: - /* -- Private Type Definitions -- */ - - typedef std::vector arg_list; - - typedef std::vector script_list; - - typedef std::unordered_map command_table; - /* -- Private Class Definition -- */ class parameter_iterator @@ -300,13 +188,9 @@ class application /* -- Private Member Data -- */ - bool exit_condition; /**< Condition for main loop */ - void *plugin_cli_handle; /**< Handle containing all loaded plugins */ - arg_list arguments; /**< Vector containing a list of arguments */ - script_list scripts; /**< Vector containing a list of script names */ - command_table commands; /**< Hash table from command strings to command handlers */ - std::mutex await_mutex; /**< Mutex for blocking the REPL until await is resolved */ - std::condition_variable await_cond; /**< Condition to be fired once await method is resolved or rejected */ + void *plugin_cli_handle; /**< Handle containing all loaded plugins for CLI */ + void *plugin_repl_handle; /**< Handle containing all loaded plugins for REPL */ + std::vector arguments; /**< Vector containing a list of arguments */ }; } /* namespace metacallcli */ diff --git a/source/cli/metacallcli/source/application.cpp b/source/cli/metacallcli/source/application.cpp index 0bf9f9e67..b6a3d2702 100644 --- a/source/cli/metacallcli/source/application.cpp +++ b/source/cli/metacallcli/source/application.cpp @@ -30,22 +30,11 @@ namespace fs = std::experimental::filesystem; #include #include -/* TODO: Windows special characters not working properly */ -/* Set UTF-16 mode for stdout in Windows for the lambda character */ -/* -#if defined(WIN32) || defined(_WIN32) -# include -# include -# include -#endif -*/ - -#include - /* -- Namespace Declarations -- */ using namespace metacallcli; +<<<<<<< Updated upstream /* -- Private Methods -- */ bool command_cb_help(application & /*app*/, tokenizer & /*t*/) @@ -335,585 +324,373 @@ bool command_cb_call(application &app, tokenizer &t) std::string args = "["; t.delimit(param_delimiters); + == == == = + /* -- Member Data -- */ +>>>>>>> Stashed changes + + static bool exit_condition = true; - ++it; + /* -- Methods -- */ - if (it != t.end()) + application::parameter_iterator::parameter_iterator(application & app) : + app(app) { - args += *it; } - args += "]"; - - void *result = app.metacallfs_adaptor(func_name, args, allocator); - - if (result != NULL) + application::parameter_iterator::~parameter_iterator() { - size_t size = 0; - - char *value_str = metacall_serialize(metacall_serial(), result, &size, allocator); - - std::cout << value_str << std::endl; - - metacall_allocator_free(allocator, value_str); - - metacall_value_destroy(result); } - else + + void application::parameter_iterator::operator()(const char *parameter) { - std::cout << "(null)" << std::endl; + std::string script(parameter); + + /* List of file extensions mapped into loader tags */ + static std::unordered_map extension_to_tag = { + /* Mock Loader */ + { "mock", "mock" }, + /* Python Loader */ + { "py", "py" }, + /* Ruby Loader */ + { "rb", "rb" }, + /* C# Loader */ + { "cs", "cs" }, + { "dll", "cs" }, + { "vb", "cs" }, + /* Cobol Loader */ + { "cob", "cob" }, + { "cbl", "cob" }, + { "cpy", "cob" }, + /* NodeJS Loader */ + { "js", "node" }, + { "node", "node" }, + /* TypeScript Loader */ + { "ts", "ts" }, + { "jsx", "ts" }, + { "tsx", "ts" }, + /* WASM Loader */ + { "wasm", "wasm" }, + { "wat", "wasm" }, + /* Rust Loader */ + { "rs", "rs" }, + /* C Loader */ + { "c", "c" }, + { "h", "c" }, + /* Java Loader */ + { "java", "java" }, + { "jar", "java" }, + /* RPC Loader */ + { "rpc", "rpc" } + + // /* Extension Loader */ + // { "so", "ext" }, + // { "dylib", "ext" }, + // { "dll", "ext" }, + + /* Note: By default js extension uses NodeJS loader instead of JavaScript V8 */ + /* Probably in the future we can differenciate between them, but it is not trivial */ + }; + + const std::string tag = extension_to_tag[script.substr(script.find_last_of(".") + 1)]; + const std::string safe_tag = tag != "" ? tag : "file"; /* Use File Loader if the tag is not found */ + + /* Load the script */ + void *args[2] = { + metacall_value_create_string(safe_tag.c_str(), safe_tag.length()), + metacall_value_create_string(script.c_str(), script.length()) + }; + + app.invoke("load", args, 2); + app.shutdown(); } - metacall_allocator_destroy(allocator); - return true; - } - - else - { - std::cout << std::endl - << "Could not find function `" << func_name << "`" << std::endl; - std::cout << "Make sure it is loaded" << std::endl - << std::endl; - - metacall_allocator_destroy(allocator); - return false; - } - } - - return false; -} - -bool command_cb_await(application &app, tokenizer &t) -{ - const std::string func_delimiters(" \n\t\r\v\f(,)"); - - tokenizer::iterator it = t.begin(); - - /* Set custom function delimiters */ - t.delimit(func_delimiters); - - /* Skip command key */ - ++it; - - /* Parse function call */ - if (it != t.end()) - { - struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; - - void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); - - std::string func_name(*it); - - const std::string param_delimiters("()"); - - /* Convert arguments into an array */ - std::string args = "["; - - t.delimit(param_delimiters); - - ++it; - - if (it != t.end()) - { - args += *it; - } - - args += "]"; - - /* - void * result = app.metacallfv_await_adaptor(func_name, args); - */ - - void *result = app.metacallfs_await_adaptor(func_name, args, allocator); - - if (result != NULL) - { - size_t size = 0; - - char *value_str = metacall_serialize(metacall_serial(), result, &size, allocator); - - std::cout << value_str << std::endl; - - metacall_allocator_free(allocator, value_str); - - metacall_value_destroy(result); - } - else - { - std::cout << "(null)" << std::endl; - } - - metacall_allocator_destroy(allocator); - - return true; - } - - return false; -} - -bool command_cb_load(application &app, tokenizer &t) -{ - tokenizer::iterator it = t.begin(); - - parser p(it); - - std::string loader_tag; - - ++it; - - if (p.is()) - { - loader_tag = *it; - } - - std::string loaders[] = { - "mock", "py", "node", "rb", "cs", "cob", "ts", "js", "file", "wasm", "rs", "c" - }; - - /* Check if invalid loader tag */ - if (std::find(std::begin(loaders), std::end(loaders), loader_tag) == std::end(loaders)) - { - return false; - } - - do - { - ++it; - - } while (it != t.end() && p.is() && app.load(loader_tag, *it)); - - return true; -} - -bool command_cb_clear(application &app, tokenizer &t) -{ - tokenizer::iterator it = t.begin(); - - parser p(it); - - std::string loader_tag; - - ++it; - - if (p.is()) - { - loader_tag = *it; - } - - do - { - ++it; - - } while (it != t.end() && p.is() && app.clear(loader_tag, *it)); - - return true; -} - -bool command_cb_exit(application &app, tokenizer & /*t*/) -{ - std::cout << "Exiting ..." << std::endl; - - app.shutdown(); - - return true; -} - -/* -- Methods -- */ - -application::parameter_iterator::parameter_iterator(application &app) : - app(app) -{ -} - -application::parameter_iterator::~parameter_iterator() -{ -} - -void application::parameter_iterator::operator()(const char *parameter) -{ - std::string script(parameter); - - /* List of file extensions mapped into loader tags */ - static std::unordered_map extension_to_tag = { - /* Mock Loader */ - { "mock", "mock" }, - /* Python Loader */ - { "py", "py" }, - /* Ruby Loader */ - { "rb", "rb" }, - /* C# Loader */ - { "cs", "cs" }, - { "dll", "cs" }, - { "vb", "cs" }, - /* Cobol Loader */ - { "cob", "cob" }, - { "cbl", "cob" }, - { "cpy", "cob" }, - /* NodeJS Loader */ - { "js", "node" }, - { "node", "node" }, - /* TypeScript Loader */ - { "ts", "ts" }, - { "jsx", "ts" }, - { "tsx", "ts" }, - /* Rust Loader */ - { "rs", "rs" }, - /* C Loader */ - { "c", "c" }, - { "h", "c" } - - /* Note: By default js extension uses NodeJS loader instead of JavaScript V8 */ - /* Probably in the future we can differenciate between them, but it is not trivial */ - }; - - const std::string tag = extension_to_tag[script.substr(script.find_last_of(".") + 1)]; - const std::string safeTag = tag != "" ? tag : "file"; /* Use File Loader if the tag is not found */ - - app.load(safeTag, script); - app.shutdown(); -} - -bool application::load(const std::string &tag, const std::string &script) -{ - const char *load_scripts[] = { - script.c_str() - }; - - if (metacall_load_from_file(tag.c_str(), load_scripts, sizeof(load_scripts) / sizeof(load_scripts[0]), NULL) != 0) - { - std::cout << "Script (" << script << ") load error in loader (" << tag << ")" << std::endl; - - return false; - } - - scripts.push_back(script); - - std::cout << "Script (" << script << ") loaded correctly" << std::endl; - - return true; -} - -bool application::load_from_memory(const std::string &tag, const std::string &script) -{ - if (metacall_load_from_memory(tag.c_str(), script.c_str(), script.size() * sizeof(std::string::value_type), NULL) != 0) - { - std::cout << "Script '" << script << "' eval error in loader (" << tag << ")" << std::endl; - - return false; - } - - std::cout << "Script '" << script << "' evaluated correctly" << std::endl; - - return true; -} - -bool application::clear(const std::string &tag, const std::string &script) -{ - void *handle = metacall_handle(tag.c_str(), script.c_str()); - - if (handle == NULL) - { - std::cout << "Script (" << script << ") not found in loader (" << tag << ")" << std::endl; - - return false; - } - - if (metacall_clear(handle) != 0) - { - std::cout << "Script (" << script << ") clear error in loader (" << tag << ")" << std::endl; - - return false; - } - - script_list::iterator it = std::find(scripts.begin(), scripts.end(), script); - - if (it != scripts.end()) - { - scripts.erase(it); - } - - std::cout << "Script (" << script << ") removed correctly" << std::endl; - - return true; -} - -application::application(int argc, char *argv[]) : - exit_condition(false), plugin_cli_handle(NULL) -{ - /* Set locale */ - setlocale(LC_CTYPE, "C"); - -/* TODO: Windows special characters not working properly */ -#if 0 - #if defined(WIN32) || defined(_WIN32) - { - // SetConsoleOutputCP(CP_UTF16); - - /* Set UTF-16 mode for stdout in Windows for the lambda character */ - _setmode(_fileno(stdout), _O_U16TEXT); - - /* Enable buffering to prevent VS from chopping up UTF-16 byte sequences */ - setvbuf(stdout, nullptr, _IOFBF, 1000); - } - #endif -#endif - - /* Initialize MetaCall */ - if (metacall_initialize() != 0) - { - /* Exit from application */ - shutdown(); - } - - /* Initialize MetaCall arguments */ - metacall_initialize_args(argc, argv); - - /* Print MetaCall information */ - metacall_print_info(); - - /* TODO: This has been updated, review it: */ - /* Parse program arguments if any (e.g metacall (0) a.py (1) b.js (2) c.rb (3)) */ - if (argc > 1) - { - parameter_iterator param_it(*this); - - /* TODO: This has been refactored in order to pass the arguments to the runtimes */ - /* Using argv + 2 by now, but this should be deleted in a near future or review the implementation */ - - /* Parse program parameters */ - std::for_each(&argv[1], argv + /*argc*/ 2, param_it); - } - - /* Get core plugin path and handle in order to load cli plugins */ - const char *plugin_path = metacall_plugin_path(); - void *plugin_extension_handle = metacall_plugin_extension(); - - if (plugin_path != NULL && plugin_extension_handle != NULL) - { - /* Define the cli plugin path as string (core plugin path plus cli) */ - fs::path plugin_cli_path(plugin_path); - plugin_cli_path /= "cli"; - std::string plugin_cli_path_str(plugin_cli_path.string()); - - /* Load cli plugins into plugin cli handle */ - void *args[] = { - metacall_value_create_string(plugin_cli_path_str.c_str(), plugin_cli_path_str.length()), - metacall_value_create_ptr(&plugin_cli_handle) - }; - - void *ret = metacallhv_s(plugin_extension_handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); - - if (ret == NULL || (ret != NULL && metacall_value_to_int(ret) != 0)) - { - std::cout << "Failed to load CLI plugins from folder: " << plugin_cli_path_str << std::endl; - } - - metacall_value_destroy(args[0]); - metacall_value_destroy(args[1]); - metacall_value_destroy(ret); - } - - /* Define available commands */ - define("help", &command_cb_help); - - define("debug", &command_cb_debug); - - define("inspect", &command_cb_inspect); - - define("eval", &command_cb_eval); - - define("call", &command_cb_call); - - define("await", &command_cb_await); - - define("load", &command_cb_load); - - define("clear", &command_cb_clear); - - define("exit", &command_cb_exit); -} - -application::~application() -{ - if (metacall_destroy() != 0) - { - std::cout << "Error while destroying MetaCall." << std::endl; - } -} - -void application::run() -{ - /* Show welcome message */ - if (exit_condition != true) - { - std::cout << "Welcome to Tijuana, tequila, sexo & marijuana." << std::endl; - } - - while (exit_condition != true) - { - std::string input; - -/* Show prompt line */ -#if defined(WIN32) || defined(_WIN32) - /* TODO: Windows special characters not working properly */ - /* std::cout << L'\u03BB' << ' '; */ - std::cout << "> "; -#else - std::cout << "\u03BB "; -#endif - - /* Get whole line */ - std::getline(std::cin, input); - - if (std::cin.eof() || std::cin.fail() || std::cin.bad()) - { - shutdown(); - return; - } - - /* Check for valid data */ - if (input.length() > 0) - { - /* Create tokenizer from input string */ - tokenizer t(input); - - /* Execute the command */ - execute(t); - } - } -} - -void application::shutdown() -{ - exit_condition = true; -} - -void application::command_debug(const std::string &key, const tokenizer &t) -{ - std::cout << "{" << std::endl - << "\tkey : " << key << "," << std::endl - << "\targuments :" << std::endl; - - for (tokenizer::iterator it = t.begin(); it != t.end(); ++it) - { - std::cout << "\t\t[" << it.position() << "] " << *it << std::endl; - } - - std::cout << "}" << std::endl; -} - -void value_array_for_each(void *v, const std::function &lambda) -{ - void **v_array = static_cast(metacall_value_to_array(v)); - size_t count = metacall_value_count(v); - - std::for_each(v_array, v_array + count, lambda); -} - -void value_map_for_each(void *v, const std::function &lambda) -{ - void **v_map = static_cast(metacall_value_to_map(v)); - size_t count = metacall_value_count(v); - - std::for_each(v_map, v_map + count, [&lambda](void *element) { - void **v_element = metacall_value_to_array(element); - lambda(metacall_value_to_string(v_element[0]), v_element[1]); - }); -} - -void application::command_inspect(const char *str, size_t size, void *allocator) -{ - void *v = metacall_deserialize(metacall_serial(), str, size, allocator); - - if (v == NULL) - { - std::cout << "Invalid deserialization" << std::endl; - - return; - } - - /* Print run-times */ - value_map_for_each(v, [](const char *key, void *modules) { - std::cout << "runtime " << key; - - if (metacall_value_count(modules) == 0) - { - std::cout << std::endl; - return; - } - - std::cout << " {" << std::endl; - - /* Print scripts */ - value_array_for_each(modules, [](void *module) { - /* Get module name */ - void **v_module_map = static_cast(metacall_value_to_map(module)); - void **v_module_name_tuple = metacall_value_to_array(v_module_map[0]); - const char *name = metacall_value_to_string(v_module_name_tuple[1]); - - std::cout << "\tmodule " << name << " { " << std::endl; - - /* Get module functions */ - void **v_module_scope_tuple = metacall_value_to_array(v_module_map[1]); - void **v_scope_map = metacall_value_to_map(v_module_scope_tuple[1]); - void **v_scope_funcs_tuple = metacall_value_to_array(v_scope_map[1]); - - if (metacall_value_count(v_scope_funcs_tuple[1]) != 0) + application::application(int argc, char *argv[]) : + plugin_cli_handle(NULL), plugin_repl_handle(NULL) { - value_array_for_each(v_scope_funcs_tuple[1], [](void *func) { - /* Get function name */ - void **v_func_map = static_cast(metacall_value_to_map(func)); - void **v_func_tuple = metacall_value_to_array(v_func_map[0]); - const char *func_name = metacall_value_to_string(v_func_tuple[1]); + /* Initialize MetaCall */ + if (metacall_initialize() != 0) + { + /* Exit from application */ + return; + } + + /* Initialize MetaCall arguments */ + metacall_initialize_args(argc, argv); + + /* Print MetaCall information */ + metacall_print_info(); + + /* TODO: This has been updated, review it: */ + /* Parse program arguments if any (e.g metacall (0) a.py (1) b.js (2) c.rb (3)) */ + if (argc > 1) + { + parameter_iterator param_it(*this); + + /* TODO: This has been refactored in order to pass the arguments to the runtimes */ + /* Using argv + 2 by now, but this should be deleted in a near future or review the implementation */ + + /* Parse program parameters */ + std::for_each(&argv[1], argv + /*argc*/ 2, param_it); + + return; + } + + /* Initialize REPL plugins */ + if (!load_path("repl", &plugin_repl_handle)) + { + /* Do not enter into the main loop */ + exit_condition = true; + plugin_repl_handle = NULL; + return; + } + + /* Initialize CLI plugins */ + if (load_path("cli", &plugin_cli_handle)) + { + /* Register exit function */ + auto exit = [](size_t argc, void *args[], void *data) -> void * { + (void)args; + (void)data; + + /* Validate function parameters */ + if (argc != 0) + { + std::cout << "Invalid number of arguments passed to exit, expected 0, received: " << argc << std::endl; + } - std::cout << "\t\tfunction " << func_name << "("; + std::cout << "Exiting ..." << std::endl; - /* Get function signature */ - void **v_signature_tuple = metacall_value_to_array(v_func_map[1]); - void **v_args_map = metacall_value_to_map(v_signature_tuple[1]); - void **v_args_tuple = metacall_value_to_array(v_args_map[1]); - void **v_args_array = metacall_value_to_array(v_args_tuple[1]); + /* Exit from main loop */ + exit_condition = true; - size_t iterator = 0, count = metacall_value_count(v_args_tuple[1]); + return NULL; + }; - value_array_for_each(v_args_array, [&iterator, &count](void *arg) { - void **v_arg_map = metacall_value_to_map(arg); - void **v_arg_name_tupla = metacall_value_to_array(v_arg_map[0]); - std::string parameter_name(metacall_value_to_string(v_arg_name_tupla[1])); + int result = metacall_register_loaderv(metacall_loader("ext"), plugin_cli_handle, "exit", exit, METACALL_INVALID, 0, NULL); - if (parameter_name.empty()) - { - parameter_name += "arg"; - parameter_name += std::to_string(iterator); - } + if (result != 0) + { + std::cout << "Exit function was not registered properly, return code: " << result << std::endl; + } + else + { + /* Start the main loop */ + exit_condition = false; + } + } + } - std::cout << parameter_name; + application::~application() + { + int result = metacall_destroy(); - if (iterator + 1 < count) - { - std::cout << ", "; - } + if (result != 0) + { + std::cout << "Error while destroying MetaCall, exit code: " << result << std::endl; + } + } - ++iterator; - }); + bool application::load_path(const char *path, void **handle) + { + /* Get core plugin path and handle in order to load cli plugins */ + const char *plugin_path = metacall_plugin_path(); + void *plugin_extension_handle = metacall_plugin_extension(); + + if (plugin_path == NULL || plugin_extension_handle == NULL) + { + return NULL; + } + + /* Define the cli plugin path as string (core plugin path plus the subpath) */ + fs::path plugin_cli_path(plugin_path); + plugin_cli_path /= path; + std::string plugin_cli_path_str(plugin_cli_path.string()); + + /* Load cli plugins into plugin cli handle */ + void *args[] = { + metacall_value_create_string(plugin_cli_path_str.c_str(), plugin_cli_path_str.length()), + metacall_value_create_ptr(handle) + }; + + void *ret = metacallhv_s(plugin_extension_handle, "plugin_load_from_path", args, sizeof(args) / sizeof(args[0])); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + + if (ret == NULL) + { + std::cout << "Failed to load CLI plugins from folder: " << plugin_cli_path_str << std::endl; + return false; + } + + if (metacall_value_id(ret) == METACALL_INT && metacall_value_to_int(ret) != 0) + { + std::cout << "Failed to load CLI plugins from folder '" + << plugin_cli_path_str << "' with result: " + << metacall_value_to_int(ret) << std::endl; + + metacall_value_destroy(ret); + return false; + } + + metacall_value_destroy(ret); + return true; + } - std::cout << ")" << std::endl; - }); + void application::run() + { + void *evaluate_func = metacall_handle_function(plugin_repl_handle, "evaluate"); + + while (exit_condition != true) + { + std::mutex await_mutex; + std::condition_variable await_cond; + std::unique_lock lock(await_mutex); + + struct await_data_type + { + void *v; + std::mutex &mutex; + std::condition_variable &cond; + bool exit_condition; + }; + + struct await_data_type await_data = { NULL, await_mutex, await_cond, false }; + + void *future = metacallfv_await_s( + evaluate_func, metacall_null_args, 0, + [](void *result, void *ctx) -> void * { + struct await_data_type *await_data = static_cast(ctx); + std::unique_lock lock(await_data->mutex); + /* Value must be always copied, it gets deleted after the scope */ + await_data->v = metacall_value_copy(result); + await_data->cond.notify_one(); + return NULL; + }, + [](void *result, void *ctx) -> void * { + struct await_data_type *await_data = static_cast(ctx); + std::unique_lock lock(await_data->mutex); + /* Value must be always copied, it gets deleted after the scope */ + await_data->v = metacall_value_copy(result); + await_data->exit_condition = true; + await_data->cond.notify_one(); + return NULL; + }, + static_cast(&await_data)); + + await_cond.wait(lock); + + /* Unused */ + metacall_value_destroy(future); + + /* Check if the loop was rejected */ + if (await_data.exit_condition) + { + exit_condition = await_data.exit_condition; + } + else + { + void **results = metacall_value_to_array(await_data.v); + + // TODO: Execute command + + /* Print the data */ + // print(results[0]); + + // if result => exception; then use first parameter + // otherwise use second parameter + void *args[2] = { + metacall_value_create_null(), + metacall_value_create_int(3) + }; + + /* Invoke next iteration of the REPL */ + void *ret = metacallfv_s(metacall_value_to_function(results[1]), args, 2); + + metacall_value_destroy(args[0]); + metacall_value_destroy(args[1]); + metacall_value_destroy(ret); + } + + metacall_value_destroy(await_data.v); + } + + /* Close REPL */ + if (plugin_repl_handle != NULL) + { + void *ret = metacallhv_s(plugin_repl_handle, "close", metacall_null_args, 0); + + metacall_value_destroy(ret); + } } - std::cout << "\t}" << std::endl; - }); + void application::shutdown() + { + exit_condition = true; + } - std::cout << "}" << std::endl; - }); + void application::print(void *v) + { + if (v == NULL) + { + std::cout << "null" << std::endl; + } + else + { + if (metacall_value_id(v) == METACALL_THROWABLE) + { + // TODO: Implement print for throwable + std::cout << "TODO: Throwable" << std::endl; + } + else + { + size_t size = 0; + struct metacall_allocator_std_type std_ctx = { &std::malloc, &std::realloc, &std::free }; + void *allocator = metacall_allocator_create(METACALL_ALLOCATOR_STD, (void *)&std_ctx); + char *value_str = metacall_serialize(metacall_serial(), v, &size, allocator); + + std::cout << value_str << std::endl; + + metacall_allocator_free(allocator, value_str); + } + + metacall_value_destroy(v); + } + } - metacall_value_destroy(v); -} + void application::invoke(const char *func, void *args[], size_t size) + { + void *ret = metacallhv_s(plugin_cli_handle, func, args, size); + + if (metacall_value_id(ret) == METACALL_INT) + { + int result = metacall_value_to_int(ret); + + if (result != 0) + { + std::cout << "Failed to execute '" << func << "' command, return code: " << result; + } + + metacall_value_destroy(ret); + } + else + { + print(ret); + } + + for (size_t it = 0; it < size; ++it) + { + metacall_value_destroy(args[it]); + } + } -void application::execute(tokenizer &t) -{ - tokenizer::iterator it = t.begin(); + void application::execute(tokenizer & t) + { + tokenizer::iterator it = t.begin(); + const std::string func = *it; + // TODO + /* command_callback cb = commands[*it]; if (cb == nullptr) @@ -937,143 +714,61 @@ void application::execute(tokenizer &t) return; } -} - -void application::define(const char *key, application::command_callback command_cb) -{ - std::string cmd(key); - - commands[cmd] = command_cb; -} - -void *application::argument_parse(parser_parameter &p) -{ - if (p.is()) - { - bool b = p.to(); - - boolean bo = static_cast(b); - - return metacall_value_create_bool(bo); - } - else if (p.is()) - { - char c = p.to(); - - return metacall_value_create_char(c); - } - else if (p.is()) - { - int i = p.to(); - - return metacall_value_create_int(i); - } - else if (p.is()) - { - long l = p.to(); - - return metacall_value_create_long(l); - } - else if (p.is()) - { - float f = p.to(); - - return metacall_value_create_float(f); - } - else if (p.is()) - { - double d = p.to(); - - return metacall_value_create_double(d); - } - else if (p.is()) - { - void *ptr = p.to(); - - return metacall_value_create_ptr(ptr); - } - else if (p.is()) - { - std::string str = p.to(); - - return metacall_value_create_string(str.c_str(), str.length()); - } - - return NULL; -} - -void *application::metacallv_adaptor(const std::string &name, const std::vector &args) -{ - void **args_ptr = new void *[args.size()]; - - void *result = NULL; - - if (args_ptr != nullptr) - { - size_t iterator = 0; - - std::for_each(args.begin(), args.end(), [&iterator, args_ptr](void *v) { - args_ptr[iterator] = v; - - ++iterator; - }); - - result = metacallv(name.c_str(), args_ptr); - - delete[] args_ptr; - } - - return result; -} - -void *application::metacallfs_adaptor(const std::string &name, const std::string &args, void *allocator) -{ - void *func = metacall_function(name.c_str()); - - return metacallfs(func, args.c_str(), args.length() + 1, allocator); -} - -void *application::metacallfs_await_adaptor(const std::string &name, const std::string &args, void *allocator) -{ - void *func = metacall_function(name.c_str()); -#if 0 - return metacallfs_await(func, args.c_str(), args.length() + 1, allocator, NULL, NULL, NULL); -#endif - - std::unique_lock lock(this->await_mutex); - - struct await_data_type - { - void *v; - application *app; - }; + */ + } - struct await_data_type data = { NULL, this }; - - void *future = metacallfs_await( - func, args.c_str(), args.length() + 1, allocator, - [](void *result, void *ctx) -> void * { - struct await_data_type *await_data = static_cast(ctx); - std::unique_lock lock(await_data->app->await_mutex); - /* Value must be always copied, it gets deteled after the scope */ - await_data->v = metacall_value_copy(result); - await_data->app->await_cond.notify_one(); - return NULL; - }, - [](void *result, void *ctx) -> void * { - struct await_data_type *await_data = static_cast(ctx); - std::unique_lock lock(await_data->app->await_mutex); - /* Value must be always copied, it gets deteled after the scope */ - await_data->v = metacall_value_copy(result); - await_data->app->await_cond.notify_one(); - return NULL; - }, - static_cast(&data)); - - this->await_cond.wait(lock); - - /* Unused */ - metacall_value_destroy(future); - - return data.v; -} + void *application::argument_parse(parser_parameter & p) + { + if (p.is()) + { + bool b = p.to(); + + boolean bo = static_cast(b); + + return metacall_value_create_bool(bo); + } + else if (p.is()) + { + char c = p.to(); + + return metacall_value_create_char(c); + } + else if (p.is()) + { + int i = p.to(); + + return metacall_value_create_int(i); + } + else if (p.is()) + { + long l = p.to(); + + return metacall_value_create_long(l); + } + else if (p.is()) + { + float f = p.to(); + + return metacall_value_create_float(f); + } + else if (p.is()) + { + double d = p.to(); + + return metacall_value_create_double(d); + } + else if (p.is()) + { + void *ptr = p.to(); + + return metacall_value_create_ptr(ptr); + } + else if (p.is()) + { + std::string str = p.to(); + + return metacall_value_create_string(str.c_str(), str.length()); + } + + return NULL; + } diff --git a/source/cli/plugins/CMakeLists.txt b/source/cli/plugins/CMakeLists.txt index ecea7da94..fa020ca0a 100644 --- a/source/cli/plugins/CMakeLists.txt +++ b/source/cli/plugins/CMakeLists.txt @@ -5,3 +5,4 @@ endif() # Extension sub-projects add_subdirectory(cli_core_plugin) +add_subdirectory(cli_repl_plugin) diff --git a/source/cli/plugins/cli_core_plugin/include/cli_core_plugin/cli_core_plugin.h b/source/cli/plugins/cli_core_plugin/include/cli_core_plugin/cli_core_plugin.h index f1bd7a08d..e27865d26 100644 --- a/source/cli/plugins/cli_core_plugin/include/cli_core_plugin/cli_core_plugin.h +++ b/source/cli/plugins/cli_core_plugin/include/cli_core_plugin/cli_core_plugin.h @@ -29,7 +29,7 @@ extern "C" { #endif -CLI_CORE_PLUGIN_API int cli_core_plugin(void *loader, void *handle, void *context); +CLI_CORE_PLUGIN_API int cli_core_plugin(void *loader, void *handle); DYNLINK_SYMBOL_EXPORT(cli_core_plugin); diff --git a/source/cli/plugins/cli_core_plugin/source/cli_core_plugin.cpp b/source/cli/plugins/cli_core_plugin/source/cli_core_plugin.cpp index dd5f4a671..47734bb38 100644 --- a/source/cli/plugins/cli_core_plugin/source/cli_core_plugin.cpp +++ b/source/cli/plugins/cli_core_plugin/source/cli_core_plugin.cpp @@ -42,7 +42,6 @@ #define COPYRIGHT_ERROR "Failed to show the copyright" #define HELP_ERROR "Failed to show the help" #define DEBUG_ERROR "Failed to debug the command" -#define EXIT_ERROR "Failed to destroy MetaCall" #define COPYRIGHT_PRINT() \ do \ @@ -670,26 +669,12 @@ void *debug(size_t argc, void *args[], void *data) return metacall_value_create_string(result.c_str(), result.length()); } -void *exit(size_t argc, void *args[], void *data) +int cli_core_plugin(void *loader, void *handle) { - /* Validate function parameters */ - EXTENSION_FUNCTION_CHECK(EXIT_ERROR); - - std::cout << "Exiting ..." << std::endl; - - int result = metacall_destroy(); - - if (result != 0) - { - std::cout << EXIT_ERROR " with error: " << result << std::endl; - } + // TODO: Eliminar array en load, usar siempre string + // TODO: Eliminar comando debug - return NULL; -} - -int cli_core_plugin(void *loader, void *handle, void *context) -{ - (void)handle; + // Hacer esto ^, o encontrar una forma de tokenizar mejor acorde a los tipos (no creo q valga la pena) EXTENSION_FUNCTION(METACALL_INT, load, METACALL_STRING, METACALL_ARRAY); EXTENSION_FUNCTION(METACALL_INT, inspect); @@ -700,7 +685,6 @@ int cli_core_plugin(void *loader, void *handle, void *context) EXTENSION_FUNCTION(METACALL_INT, copyright); EXTENSION_FUNCTION(METACALL_INT, help); EXTENSION_FUNCTION(METACALL_STRING, debug, METACALL_ARRAY); - EXTENSION_FUNCTION(METACALL_INVALID, exit); return 0; } diff --git a/source/cli/plugins/cli_core_plugin/source/metacall.json b/source/cli/plugins/cli_core_plugin/source/metacall.json index d15c9bc59..8def5f2e3 100644 --- a/source/cli/plugins/cli_core_plugin/source/metacall.json +++ b/source/cli/plugins/cli_core_plugin/source/metacall.json @@ -1,7 +1,7 @@ { - "language_id": "ext", - "path": ".", - "scripts": [ - "cli_core_plugin" - ] + "language_id": "ext", + "path": ".", + "scripts": [ + "cli_core_plugin" + ] } diff --git a/source/cli/plugins/cli_repl_plugin/CMakeLists.txt b/source/cli/plugins/cli_repl_plugin/CMakeLists.txt new file mode 100644 index 000000000..537aeb902 --- /dev/null +++ b/source/cli/plugins/cli_repl_plugin/CMakeLists.txt @@ -0,0 +1,53 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_EXT OR NOT OPTION_BUILD_EXTENSIONS) + return() +endif() + +# +# Plugin name and options +# + +# Target name +set(target cli_repl_plugin) + +# Exit here if required dependencies are not met +message(STATUS "Plugin ${target}") + +# +# Source +# + +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +# +# Destination +# + +set(PLUGIN_OUTPUT_DIRECTORY "${PROJECT_OUTPUT_DIR}/plugins/repl/${target}") + +# +# Project Target +# + +add_custom_target(${target} ALL + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_COMMAND} -E copy ${source_path}/metacall.json ${PLUGIN_OUTPUT_DIRECTORY}/metacall.json + COMMAND ${CMAKE_COMMAND} -E copy ${source_path}/cli_repl_plugin.js ${PLUGIN_OUTPUT_DIRECTORY}/cli_repl_plugin.js +) + +# +# Target Properties +# + +set_target_properties(${target} + PROPERTIES + FOLDER "${IDE_FOLDER}" +) + +# +# Dependencies +# + +add_dependencies(${target} + plugin_extension +) diff --git a/source/cli/plugins/cli_repl_plugin/source/cli_repl_plugin.js b/source/cli/plugins/cli_repl_plugin/source/cli_repl_plugin.js new file mode 100644 index 000000000..52270388a --- /dev/null +++ b/source/cli/plugins/cli_repl_plugin/source/cli_repl_plugin.js @@ -0,0 +1,76 @@ +const vm = require('vm'); +const r = require('repl'); +const { command_register, command_parse, command_complete } = require('./parser') + +// Method for generating a continuous chain of promises for allowing the repl +// evaluation to be resolved from outside (in the CLI main loop) +const new_repl_promise = () => { + let promise_resolve = null; + let promise_reject = null; + const promise = new Promise(function(resolve, reject) { + promise_resolve = resolve; + promise_reject = reject; + }); + + const wait = async () => { + // Await to the REPL promise to be resolved from the evaluator + const p = await promise; + + // Reset the global promise from the REPL + repl_promise = new_repl_promise(); + return p + }; + + return { resolve: promise_resolve, reject: promise_reject, wait }; +} + +let repl_promise = new_repl_promise(); + +// Show welcome message +console.log('Welcome to Tijuana, tequila, sexo & marijuana.'); + +// Start REPL +const repl = r.start({ + prompt: '\u03BB ', + useGlobal: false, + ignoreUndefined: true, + preview: true, + eval: evaluator, +}); + +function evaluator(cmd, context, file, cb) { + // TODO: Implement parser for cmd + repl_promise.resolve([command_parse(cmd.trim()), cb]); +} + +// Complete function (hook it in order to allow inline autocompletion) +const _completer = repl.completer.bind(repl); +repl.completer = function(line, cb) { + // Hook the completer callback in order to inject our own completion results + const wrap = (err, result) => { + // TODO: Generate autocompletion array + cb(err, [['call'], line]); + }; + _completer(line, wrap); +} + +// Clear context and commands +repl.context = vm.createContext({}); +repl.commands = {}; + +// On close event reject the repl promise +repl.on('close', () => { + repl_promise.reject(); +}); + +module.exports = { + evaluate: async () => { + return await repl_promise.wait(); + }, + command_register, + close: () => { + repl.close(); + } +} + +setTimeout(() => repl.close(), 5000); diff --git a/source/cli/plugins/cli_repl_plugin/source/metacall.json b/source/cli/plugins/cli_repl_plugin/source/metacall.json new file mode 100644 index 000000000..961e0ef7c --- /dev/null +++ b/source/cli/plugins/cli_repl_plugin/source/metacall.json @@ -0,0 +1,7 @@ +{ + "language_id": "node", + "path": ".", + "scripts": [ + "cli_repl_plugin.js" + ] +} diff --git a/source/cli/plugins/cli_repl_plugin/source/parser.js b/source/cli/plugins/cli_repl_plugin/source/parser.js new file mode 100644 index 000000000..c5738c823 --- /dev/null +++ b/source/cli/plugins/cli_repl_plugin/source/parser.js @@ -0,0 +1,64 @@ +const command_parser_map = {}; + +function command_parse(cmd) { + original_command = cmd.toString(); + current = cmd.indexOf(' '); + key = cmd.substring(0, current); + cmd = cmd.substring(++current); + + const skip_whitespaces = (str, position) => { + while (str[position] === ' ') { + ++position; + } + + return position; + } + + const command = command_parser_map[key]; + const tokens = []; + + for (let param_count = 0; param_count < command.length; ++param_count) { + current = skip_whitespaces(current); + next = cmd.indexOf(' ') + param = cmd.substring(current, next); + + switch (command.types[param_count]) { + case 'METACALL_STRING': { + if (!command.regexes[param_count].test(param)) { + return null; + } + tokens.push(param); + break; + } + + case 'METACALL_ARRAY': { + const array = []; + if (!command.regexes[param_count].test(param)) { + return null; + } + array.push(param); + + do { + + } + break; + } + + default: + console.error(`Failed to parse command '${original_command}', type ${command.types[param_count]} is not implemented`) + return null; + } + + cmd = cmd.substring(next); + } + + return tokens; +} + +module.exports = { + command_register: (cmd, regexes) => { + command_parser_map[cmd] = regexes.map(re => new RegExp(re)); + }, + command_complete: () => {}, + command_parse, +}; diff --git a/source/cli/plugins/cli_repl_plugin/source/test.js b/source/cli/plugins/cli_repl_plugin/source/test.js new file mode 100644 index 000000000..76685616f --- /dev/null +++ b/source/cli/plugins/cli_repl_plugin/source/test.js @@ -0,0 +1,46 @@ +const assert = require('assert').strict; +const { command_register, command_parse } = require('./parser'); + +const loaders = "^(mock|py|node|rb|cs|cob|ts|js|file|wasm|rs|c|rpc|ext|java)"; +const path = "((?:[^\/]*\/)*)(.*)"; +const func = "^[a-zA-Z0-9_]+\(.+\)$"; +const anychar = ".+"; +const parser_map = { + load: { + regexes: [loaders, path], + types: ['METACALL_STRING', 'METACALL_ARRAY'], + }, + inspect: { + regexes: [], + types: [], + }, + eval: { + regexes: [loaders, anychar], /* Match any char except by newline */ + types: ['METACALL_STRING', 'METACALL_STRING'], + }, + call: { + regexes: [func], /* Match any function call like: func_abc_3423("asd", 3434, 0.2) */ + types: ['METACALL_STRING', 'METACALL_STRING'], + }, + clear: { + regexes: [loaders, anychar], /* Match any char except by newline */ + types: ['METACALL_STRING', 'METACALL_STRING'], + }, + copyright: { + regexes: [], + types: [], + }, + help: { + regexes: [], + types: [], + }, + debug: { + regexes: [anychar], + types: ['METACALL_ARRAY'], + }, +}; + +// Register commands +Object.keys(parser_map).forEach(key => command_register(key, parser_map[key].regexes, parser_map[key].types)); + +console.log(command_parse('call asdf(234, 34, 2, 3, 4, 5)')); diff --git a/source/extensions/plugin_extension/include/plugin_extension/plugin_extension.h b/source/extensions/plugin_extension/include/plugin_extension/plugin_extension.h index 89b0ed3b1..ef5d86dc5 100644 --- a/source/extensions/plugin_extension/include/plugin_extension/plugin_extension.h +++ b/source/extensions/plugin_extension/include/plugin_extension/plugin_extension.h @@ -29,7 +29,7 @@ extern "C" { #endif -PLUGIN_EXTENSION_API int plugin_extension(void *loader, void *handle, void *context); +PLUGIN_EXTENSION_API int plugin_extension(void *loader, void *handle); DYNLINK_SYMBOL_EXPORT(plugin_extension); diff --git a/source/extensions/plugin_extension/source/plugin_extension.cpp b/source/extensions/plugin_extension/source/plugin_extension.cpp index 942e9f367..8596f9533 100644 --- a/source/extensions/plugin_extension/source/plugin_extension.cpp +++ b/source/extensions/plugin_extension/source/plugin_extension.cpp @@ -20,8 +20,7 @@ #include -#include -#include +#include #if defined __has_include #if __has_include() @@ -123,9 +122,10 @@ void *plugin_load_from_path(size_t argc, void *args[], void *data) return metacall_value_create_int(0); } -int plugin_extension(void *loader, void *handle, void *context) +int plugin_extension(void *loader, void *handle) { - enum metacall_value_id arg_types[] = { METACALL_STRING, METACALL_PTR }; - (void)handle; - return metacall_register_loaderv(loader, context, "plugin_load_from_path", plugin_load_from_path, METACALL_INT, sizeof(arg_types) / sizeof(arg_types[0]), arg_types); + /* Register function */ + EXTENSION_FUNCTION(METACALL_INT, plugin_load_from_path, METACALL_STRING, METACALL_PTR); + + return 0; } diff --git a/source/loader/include/loader/loader.h b/source/loader/include/loader/loader.h index 69c20cde8..52fe51f02 100644 --- a/source/loader/include/loader/loader.h +++ b/source/loader/include/loader/loader.h @@ -59,7 +59,7 @@ LOADER_API int loader_is_initialized(const loader_tag tag); LOADER_API int loader_register(const char *name, loader_register_invoke invoke, function *func, type_id return_type, size_t arg_size, type_id args_type_id[]); -LOADER_API int loader_register_impl(void *impl, void *ctx, const char *name, loader_register_invoke invoke, type_id return_type, size_t arg_size, type_id args_type_id[]); +LOADER_API int loader_register_impl(void *impl, void *handle, const char *name, loader_register_invoke invoke, type_id return_type, size_t arg_size, type_id args_type_id[]); LOADER_API const char *loader_library_path(void); diff --git a/source/loader/source/loader.c b/source/loader/source/loader.c index 549ce728b..1507835f7 100644 --- a/source/loader/source/loader.c +++ b/source/loader/source/loader.c @@ -220,9 +220,9 @@ int loader_register(const char *name, loader_register_invoke invoke, function *f return loader_host_register(plugin_impl_type(manager_impl->host, loader_impl), NULL, name, invoke, func, return_type, arg_size, args_type_id); } -int loader_register_impl(void *impl, void *ctx, const char *name, loader_register_invoke invoke, type_id return_type, size_t arg_size, type_id args_type_id[]) +int loader_register_impl(void *impl, void *handle, const char *name, loader_register_invoke invoke, type_id return_type, size_t arg_size, type_id args_type_id[]) { - return loader_host_register((loader_impl)impl, (context)ctx, name, invoke, NULL, return_type, arg_size, args_type_id); + return loader_host_register((loader_impl)impl, loader_impl_handle_context(handle), name, invoke, NULL, return_type, arg_size, args_type_id); } plugin loader_get_impl_plugin(const loader_tag tag) diff --git a/source/loaders/ext_loader/source/ext_loader_impl.cpp b/source/loaders/ext_loader/source/ext_loader_impl.cpp index befb2429b..39d9db653 100644 --- a/source/loaders/ext_loader/source/ext_loader_impl.cpp +++ b/source/loaders/ext_loader/source/ext_loader_impl.cpp @@ -77,7 +77,7 @@ typedef struct loader_impl_ext_handle_type union loader_impl_function_cast { void *ptr; - int (*fn)(void *, void *, void *); + int (*fn)(void *, void *); }; static dynlink ext_loader_impl_load_from_file_dynlink(const char *path, const char *library_name); @@ -339,13 +339,15 @@ int ext_loader_impl_discover(loader_impl impl, loader_handle handle, context ctx { loader_impl_ext_handle ext_handle = static_cast(handle); + (void)ctx; /* We have opted by pass the handle instead of the context of the handle */ + for (auto ext : ext_handle->extensions) { loader_impl_function_cast function_cast; function_cast.ptr = static_cast(ext.addr); - if (function_cast.fn(impl, loader_impl_handle_container_of(impl, handle), ctx) != 0) + if (function_cast.fn(impl, loader_impl_handle_container_of(impl, handle)) != 0) { return 1; } diff --git a/source/loaders/node_loader/bootstrap/lib/bootstrap.js b/source/loaders/node_loader/bootstrap/lib/bootstrap.js index 8bff2497b..6ee515544 100644 --- a/source/loaders/node_loader/bootstrap/lib/bootstrap.js +++ b/source/loaders/node_loader/bootstrap/lib/bootstrap.js @@ -39,7 +39,7 @@ function node_loader_trampoline_initialize(loader_library_path) { } } - console.log('NodeJS Warning: MetaCall could not be preloaded'); + // console.log('NodeJS Warning: MetaCall could not be preloaded'); } function node_loader_trampoline_is_callable(value) { diff --git a/source/loaders/node_loader/source/node_loader_impl.cpp b/source/loaders/node_loader/source/node_loader_impl.cpp index e73f44676..092b9fbb1 100644 --- a/source/loaders/node_loader/source/node_loader_impl.cpp +++ b/source/loaders/node_loader/source/node_loader_impl.cpp @@ -2293,7 +2293,7 @@ void node_loader_impl_func_await_safe(napi_env env, loader_impl_async_func_await node_loader_impl_exception(env, status); - /* Delete references references to wrapped objects */ + /* Delete references to wrapped objects */ status = napi_delete_reference(env, trampoline_ref); node_loader_impl_exception(env, status); @@ -5062,6 +5062,8 @@ int64_t node_loader_impl_user_async_handles_count(loader_impl_node node_impl) int64_t active_handles = node_loader_impl_async_handles_count(node_impl); int64_t extra_active_handles = node_impl->extra_active_handles.load(); + /* TODO: Uncomment for debugging handles */ + /* #if (!defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG) || defined(__DEBUG) || defined(__DEBUG__)) int64_t closing = node_loader_impl_async_closing_handles_count(node_impl); @@ -5073,16 +5075,22 @@ int64_t node_loader_impl_user_async_handles_count(loader_impl_node node_impl) (int64_t)node_impl->thread_loop->active_reqs.count, closing); #endif +*/ return active_handles - node_impl->base_active_handles - extra_active_handles; } void node_loader_impl_print_handles(loader_impl_node node_impl) { + (void)node_impl; + + /* TODO: Uncomment for debugging handles */ + /* printf("Number of active handles: %" PRId64 "\n", node_loader_impl_async_handles_count(node_impl)); printf("Number of user active handles: %" PRId64 "\n", node_loader_impl_user_async_handles_count(node_impl)); uv_print_active_handles(node_impl->thread_loop, stdout); fflush(stdout); + */ } napi_value node_loader_impl_async_destroy_safe(napi_env env, napi_callback_info info) diff --git a/source/loaders/node_loader/source/node_loader_trampoline.cpp b/source/loaders/node_loader/source/node_loader_trampoline.cpp index f31512616..184fbbcda 100644 --- a/source/loaders/node_loader/source/node_loader_trampoline.cpp +++ b/source/loaders/node_loader/source/node_loader_trampoline.cpp @@ -169,7 +169,7 @@ napi_value node_loader_trampoline_resolve(napi_env env, napi_callback_info info) /* Unwrap trampoline pointer */ void *result; - status = napi_unwrap(env, args[0], &result); + status = napi_remove_wrap(env, args[0], &result); node_loader_impl_exception(env, status); @@ -226,7 +226,7 @@ napi_value node_loader_trampoline_reject(napi_env env, napi_callback_info info) /* Unwrap trampoline pointer */ void *result; - status = napi_unwrap(env, args[0], &result); + status = napi_remove_wrap(env, args[0], &result); node_loader_impl_exception(env, status); diff --git a/source/metacall/include/metacall/metacall.h b/source/metacall/include/metacall/metacall.h index f069d444b..1b130c40b 100644 --- a/source/metacall/include/metacall/metacall.h +++ b/source/metacall/include/metacall/metacall.h @@ -742,32 +742,44 @@ METACALL_API void *metacallfms(void *func, const char *buffer, size_t size, void */ METACALL_API int metacall_register(const char *name, void *(*invoke)(size_t, void *[], void *), void **func, enum metacall_value_id return_type, size_t size, ...); +/** + * @brief + * Register a function by name @name and arguments @types + * + * @param[in] name + * Name of the function (if it is NULL, function is not registered into host scope) + * + * @param[in] invoke + * Pointer to function invoke interface (argc, argv, data) + * + * @param[out] func + * Will set the pointer to the function if the parameter is not null + * + * @param[in] return_type + * Type of return value + * + * @param[in] size + * Number of function arguments + * + * @param[in] types + * List of parameter types + * + * @return + * Pointer to value containing the result of the call + */ +METACALL_API int metacall_registerv(const char *name, void *(*invoke)(size_t, void *[], void *), void **func, enum metacall_value_id return_type, size_t size, enum metacall_value_id types[]); + /** * @brief -* Register a function by name @name and arguments @types -* -* @param[in] name -* Name of the function (if it is NULL, function is not registered into host scope) -* -* @param[in] invoke -* Pointer to function invoke interface (argc, argv, data) +* Obtain the loader instance by @tag * -* @param[out] func -* Will set the pointer to the function if the parameter is not null -* -* @param[in] return_type -* Type of return value -* -* @param[in] size -* Number of function arguments -* -* @param[in] types -* List of parameter types +* @param[in] tag +* Tag in which the loader is identified, normally it is the extension of the script * * @return -* Pointer to value containing the result of the call +* Pointer the loader by @tag */ -METACALL_API int metacall_registerv(const char *name, void *(*invoke)(size_t, void *[], void *), void **func, enum metacall_value_id return_type, size_t size, enum metacall_value_id types[]); +METACALL_API void *metacall_loader(const char *tag); /** * @brief @@ -776,8 +788,8 @@ METACALL_API int metacall_registerv(const char *name, void *(*invoke)(size_t, vo * @param[in] loader * Opaque pointer to the loader in which you want to register the function (this allows to register the function into a different loader than the host) * -* @param[in] context -* Opaque pointer to the context in which you want to register the function (if it is NULL, it will be defined on the global scope of the loader) +* @param[in] handle +* Opaque pointer to the handle in which you want to register the function (if it is NULL, it will be defined on the global scope of the loader) * * @param[in] name * Name of the function (if it is NULL, function is not registered into host scope) @@ -795,9 +807,9 @@ METACALL_API int metacall_registerv(const char *name, void *(*invoke)(size_t, vo * List of parameter types * * @return -* Pointer to value containing the result of the call +* Zero if the function was registered properly, distinct from zero otherwise */ -METACALL_API int metacall_register_loaderv(void *loader, void *context, const char *name, void *(*invoke)(size_t, void *[], void *), enum metacall_value_id return_type, size_t size, enum metacall_value_id types[]); +METACALL_API int metacall_register_loaderv(void *loader, void *handle, const char *name, void *(*invoke)(size_t, void *[], void *), enum metacall_value_id return_type, size_t size, enum metacall_value_id types[]); /** * @brief diff --git a/source/metacall/source/metacall.c b/source/metacall/source/metacall.c index 701375304..f2486411a 100644 --- a/source/metacall/source/metacall.c +++ b/source/metacall/source/metacall.c @@ -1493,9 +1493,14 @@ int metacall_registerv(const char *name, void *(*invoke)(size_t, void *[], void return loader_register(name, (loader_register_invoke)invoke, (function *)func, (type_id)return_type, size, (type_id *)types); } -int metacall_register_loaderv(void *loader, void *context, const char *name, void *(*invoke)(size_t, void *[], void *), enum metacall_value_id return_type, size_t size, enum metacall_value_id types[]) +void *metacall_loader(const char *tag) { - return loader_register_impl(loader, context, name, (loader_register_invoke)invoke, (type_id)return_type, size, (type_id *)types); + return loader_get_impl(tag); +} + +int metacall_register_loaderv(void *loader, void *handle, const char *name, void *(*invoke)(size_t, void *[], void *), enum metacall_value_id return_type, size_t size, enum metacall_value_id types[]) +{ + return loader_register_impl(loader, handle, name, (loader_register_invoke)invoke, (type_id)return_type, size, (type_id *)types); } void *metacall_await(const char *name, void *args[], void *(*resolve_callback)(void *, void *), void *(*reject_callback)(void *, void *), void *data) diff --git a/source/plugin/include/plugin/plugin_interface.hpp b/source/plugin/include/plugin/plugin_interface.hpp index a539ad258..c3719abfb 100644 --- a/source/plugin/include/plugin/plugin_interface.hpp +++ b/source/plugin/include/plugin/plugin_interface.hpp @@ -38,7 +38,7 @@ #define EXTENSION_FUNCTION_IMPL_VOID(ret, name) \ do \ { \ - if (metacall_register_loaderv(loader, context, PREPROCESSOR_STRINGIFY(name), name, ret, 0, NULL) != 0) \ + if (metacall_register_loaderv(loader, handle, PREPROCESSOR_STRINGIFY(name), name, ret, 0, NULL) != 0) \ { \ log_write("metacall", LOG_LEVEL_ERROR, "Failed to register function: " PREPROCESSOR_STRINGIFY(name)); \ return 1; \ @@ -49,7 +49,7 @@ do \ { \ enum metacall_value_id arg_types[] = { __VA_ARGS__ }; \ - if (metacall_register_loaderv(loader, context, PREPROCESSOR_STRINGIFY(name), name, ret, PREPROCESSOR_ARGS_COUNT(__VA_ARGS__), arg_types) != 0) \ + if (metacall_register_loaderv(loader, handle, PREPROCESSOR_STRINGIFY(name), name, ret, PREPROCESSOR_ARGS_COUNT(__VA_ARGS__), arg_types) != 0) \ { \ log_write("metacall", LOG_LEVEL_ERROR, "Failed to register function: " PREPROCESSOR_STRINGIFY(name)); \ return 1; \ @@ -61,11 +61,9 @@ EXTENSION_FUNCTION_IMPL_VOID(ret, name), \ EXTENSION_FUNCTION_IMPL(ret, name, __VA_ARGS__)) -/* TODO: Move the log_write outside into the CLI or similar */ #define EXTENSION_FUNCTION_THROW(error) \ do \ { \ - /* log_write("metacall", LOG_LEVEL_ERROR, error); */ \ exception ex = exception_create_const(error, "PluginException", 0, ""); \ throwable th = throwable_create(value_create_exception(ex)); \ return value_create_throwable(th); \ diff --git a/source/plugins/backtrace_plugin/include/backtrace_plugin/backtrace_plugin.h b/source/plugins/backtrace_plugin/include/backtrace_plugin/backtrace_plugin.h index a0630cb95..5269713f9 100644 --- a/source/plugins/backtrace_plugin/include/backtrace_plugin/backtrace_plugin.h +++ b/source/plugins/backtrace_plugin/include/backtrace_plugin/backtrace_plugin.h @@ -29,7 +29,7 @@ extern "C" { #endif -BACKTRACE_PLUGIN_API int backtrace_plugin(void *loader, void *handle, void *context); +BACKTRACE_PLUGIN_API int backtrace_plugin(void *loader, void *handle); DYNLINK_SYMBOL_EXPORT(backtrace_plugin); diff --git a/source/plugins/backtrace_plugin/source/backtrace_plugin.cpp b/source/plugins/backtrace_plugin/source/backtrace_plugin.cpp index ca3611ed7..c3e826fd3 100644 --- a/source/plugins/backtrace_plugin/source/backtrace_plugin.cpp +++ b/source/plugins/backtrace_plugin/source/backtrace_plugin.cpp @@ -26,11 +26,10 @@ static backward::SignalHandling signal_handling; -int backtrace_plugin(void *loader, void *handle, void *context) +int backtrace_plugin(void *loader, void *handle) { (void)loader; (void)handle; - (void)context; if (signal_handling.loaded() == false) { diff --git a/source/plugins/sandbox_plugin/include/sandbox_plugin/sandbox_plugin.h b/source/plugins/sandbox_plugin/include/sandbox_plugin/sandbox_plugin.h index 670d69683..12d8c2caf 100644 --- a/source/plugins/sandbox_plugin/include/sandbox_plugin/sandbox_plugin.h +++ b/source/plugins/sandbox_plugin/include/sandbox_plugin/sandbox_plugin.h @@ -29,7 +29,7 @@ extern "C" { #endif -SANDBOX_PLUGIN_API int sandbox_plugin(void *loader, void *handle, void *context); +SANDBOX_PLUGIN_API int sandbox_plugin(void *loader, void *handle); DYNLINK_SYMBOL_EXPORT(sandbox_plugin); diff --git a/source/plugins/sandbox_plugin/source/sandbox_plugin.cpp b/source/plugins/sandbox_plugin/source/sandbox_plugin.cpp index 481942a03..e503d55a1 100644 --- a/source/plugins/sandbox_plugin/source/sandbox_plugin.cpp +++ b/source/plugins/sandbox_plugin/source/sandbox_plugin.cpp @@ -100,10 +100,8 @@ static int sandbox_plugin_post_fork_callback(metacall_pid id, void *data) #endif /* METACALL_FORK_SAFE */ #endif -int sandbox_plugin(void *loader, void *handle, void *context) +int sandbox_plugin(void *loader, void *handle) { - (void)handle; - EXTENSION_FUNCTION(METACALL_PTR, sandbox_initialize); EXTENSION_FUNCTION(METACALL_INT, sandbox_uname, METACALL_PTR, METACALL_BOOL); EXTENSION_FUNCTION(METACALL_INT, sandbox_destroy, METACALL_PTR); diff --git a/source/scripts/extension/sum_extension/include/sum_extension/sum_extension.h b/source/scripts/extension/sum_extension/include/sum_extension/sum_extension.h index 06585a3b7..4dd93bc04 100644 --- a/source/scripts/extension/sum_extension/include/sum_extension/sum_extension.h +++ b/source/scripts/extension/sum_extension/include/sum_extension/sum_extension.h @@ -29,7 +29,7 @@ extern "C" { #endif -SUM_EXTENSION_API int sum_extension(void *loader, void *handle, void *context); +SUM_EXTENSION_API int sum_extension(void *loader, void *handle); DYNLINK_SYMBOL_EXPORT(sum_extension); diff --git a/source/scripts/extension/sum_extension/source/sum_extension.cpp b/source/scripts/extension/sum_extension/source/sum_extension.cpp index aaa02ed2e..b091a4662 100644 --- a/source/scripts/extension/sum_extension/source/sum_extension.cpp +++ b/source/scripts/extension/sum_extension/source/sum_extension.cpp @@ -34,9 +34,10 @@ void *sum(size_t argc, void *args[], void *data) return metacall_value_create_long(result); } -int sum_extension(void *loader, void *handle, void *context) +int sum_extension(void *loader, void *handle) { - enum metacall_value_id arg_types[] = { METACALL_LONG, METACALL_LONG }; - (void)handle; - return metacall_register_loaderv(loader, context, "sum", sum, METACALL_LONG, sizeof(arg_types) / sizeof(arg_types[0]), arg_types); + /* Register function */ + EXTENSION_FUNCTION(METACALL_LONG, sum, METACALL_LONG, METACALL_LONG); + + return 0; } diff --git a/source/tests/CMakeLists.txt b/source/tests/CMakeLists.txt index 34502c1f7..00dca8939 100644 --- a/source/tests/CMakeLists.txt +++ b/source/tests/CMakeLists.txt @@ -186,6 +186,7 @@ add_subdirectory(metacall_python_async_test) # TODO: add_subdirectory(metacall_python_await_test) add_subdirectory(metacall_python_exception_test) # TODO: add_subdirectory(metacall_python_node_await_test) +add_subdirectory(metacall_python_without_env_vars_test) add_subdirectory(metacall_map_test) add_subdirectory(metacall_map_await_test) add_subdirectory(metacall_initialize_test) diff --git a/source/tests/metacall_cli_core_plugin_test/source/metacall_cli_core_plugin_test.cpp b/source/tests/metacall_cli_core_plugin_test/source/metacall_cli_core_plugin_test.cpp index 8803cc934..ec178460c 100644 --- a/source/tests/metacall_cli_core_plugin_test/source/metacall_cli_core_plugin_test.cpp +++ b/source/tests/metacall_cli_core_plugin_test/source/metacall_cli_core_plugin_test.cpp @@ -386,10 +386,5 @@ TEST_F(metacall_cli_core_plugin_test, DefaultConstructor) metacall_allocator_destroy(allocator); - /* Test destroy */ - { - void *ret = metacallhv_s(handle, "exit", metacall_null_args, 0); - - EXPECT_EQ((void *)NULL, (void *)ret); - } + EXPECT_EQ((int)0, (int)metacall_destroy()); } diff --git a/source/tests/metacall_python_test/source/metacall_python_test.cpp b/source/tests/metacall_python_test/source/metacall_python_test.cpp index 4346ed544..97a6bb3ef 100644 --- a/source/tests/metacall_python_test/source/metacall_python_test.cpp +++ b/source/tests/metacall_python_test/source/metacall_python_test.cpp @@ -47,6 +47,12 @@ TEST_F(metacall_python_test, DefaultConstructor) }; EXPECT_EQ((int)0, (int)metacall_load_from_file("py", py_scripts, sizeof(py_scripts) / sizeof(py_scripts[0]), NULL)); + + const char *py_scripts_fail[] = { + "thismoduledoesnotexistaaaa", // Non-existent Module load + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("py", py_scripts_fail, sizeof(py_scripts_fail) / sizeof(py_scripts_fail[0]), NULL)); } #endif /* OPTION_BUILD_LOADERS_PY */ diff --git a/source/tests/metacall_python_without_env_vars_test/CMakeLists.txt b/source/tests/metacall_python_without_env_vars_test/CMakeLists.txt new file mode 100644 index 000000000..d09f51080 --- /dev/null +++ b/source/tests/metacall_python_without_env_vars_test/CMakeLists.txt @@ -0,0 +1,140 @@ +# Check if this loader is enabled +if(NOT OPTION_BUILD_LOADERS OR NOT OPTION_BUILD_LOADERS_PY) + return() +endif() + +# +# Executable name and options +# + +# Target name +set(target metacall-python-without-env-vars-test) +message(STATUS "Test ${target}") + +# +# Compiler warnings +# + +include(Warnings) + +# +# Compiler security +# + +include(SecurityFlags) + +# +# Sources +# + +set(include_path "${CMAKE_CURRENT_SOURCE_DIR}/include/${target}") +set(source_path "${CMAKE_CURRENT_SOURCE_DIR}/source") + +set(sources + ${source_path}/main.cpp + ${source_path}/metacall_python_without_env_vars_test.cpp +) + +# Group source files +set(header_group "Header Files (API)") +set(source_group "Source Files") +source_group_by_path(${include_path} "\\\\.h$|\\\\.hpp$" + ${header_group} ${headers}) +source_group_by_path(${source_path} "\\\\.cpp$|\\\\.c$|\\\\.h$|\\\\.hpp$" + ${source_group} ${sources}) + +# +# Create executable +# + +# Build executable +add_executable(${target} + ${sources} +) + +# Create namespaced alias +add_executable(${META_PROJECT_NAME}::${target} ALIAS ${target}) + +# +# Project options +# + +set_target_properties(${target} + PROPERTIES + ${DEFAULT_PROJECT_OPTIONS} + FOLDER "${IDE_FOLDER}" +) + +# +# Include directories +# + +target_include_directories(${target} + PRIVATE + ${DEFAULT_INCLUDE_DIRECTORIES} + ${PROJECT_BINARY_DIR}/source/include +) + +# +# Libraries +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LIBRARIES} + + GTest + + ${META_PROJECT_NAME}::metacall +) + +# +# Compile definitions +# + +target_compile_definitions(${target} + PRIVATE + ${DEFAULT_COMPILE_DEFINITIONS} +) + +# +# Compile options +# + +target_compile_options(${target} + PRIVATE + ${DEFAULT_COMPILE_OPTIONS} +) + +# +# Linker options +# + +target_link_libraries(${target} + PRIVATE + ${DEFAULT_LINKER_OPTIONS} +) + +# +# Define test +# + +add_test(NAME ${target} + COMMAND $ +) + +# +# Define dependencies +# + +add_dependencies(${target} + py_loader +) + +# +# Define test properties +# + +set_property(TEST ${target} + PROPERTY LABELS ${target} +) diff --git a/source/tests/metacall_python_without_env_vars_test/source/main.cpp b/source/tests/metacall_python_without_env_vars_test/source/main.cpp new file mode 100644 index 000000000..3f4f3377f --- /dev/null +++ b/source/tests/metacall_python_without_env_vars_test/source/main.cpp @@ -0,0 +1,28 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2024 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +int main(int argc, char *argv[]) +{ + ::testing::InitGoogleTest(&argc, argv); + + return RUN_ALL_TESTS(); +} diff --git a/source/tests/metacall_python_without_env_vars_test/source/metacall_python_without_env_vars_test.cpp b/source/tests/metacall_python_without_env_vars_test/source/metacall_python_without_env_vars_test.cpp new file mode 100644 index 000000000..7a0c6c118 --- /dev/null +++ b/source/tests/metacall_python_without_env_vars_test/source/metacall_python_without_env_vars_test.cpp @@ -0,0 +1,49 @@ +/* + * Loader Library by Parra Studios + * A plugin for loading python code at run-time into a process. + * + * Copyright (C) 2016 - 2024 Vicente Eduardo Ferrer Garcia + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +#include + +#include +#include + +class metacall_python_without_env_vars_test : public testing::Test +{ +protected: +}; + +TEST_F(metacall_python_without_env_vars_test, DefaultConstructor) +{ + metacall_print_info(); + + ASSERT_EQ((int)0, (int)metacall_initialize()); + +/* Python */ +#if defined(OPTION_BUILD_LOADERS_PY) + { + const char *py_scripts_fail[] = { + "thismoduledoesnotexistaaaa", // Non-existent Module load + }; + + EXPECT_EQ((int)1, (int)metacall_load_from_file("py", py_scripts_fail, sizeof(py_scripts_fail) / sizeof(py_scripts_fail[0]), NULL)); + } +#endif /* OPTION_BUILD_LOADERS_PY */ + + EXPECT_EQ((int)0, (int)metacall_destroy()); +} From cac441701b7b96f75a9405a9126c458b0ac1ea30 Mon Sep 17 00:00:00 2001 From: Vicente Eduardo Ferrer Garcia Date: Mon, 5 Feb 2024 23:26:56 +0100 Subject: [PATCH 02/28] Implement the whole parser for the cli repl. --- source/cli/metacallcli/source/application.cpp | 994 +++++++----------- .../source/cli_core_plugin.cpp | 5 - .../plugins/cli_repl_plugin/CMakeLists.txt | 23 + .../source/cli_core_command.js | 50 + .../cli_repl_plugin/source/cli_repl_plugin.js | 67 +- .../plugins/cli_repl_plugin/source/parser.js | 94 +- .../plugins/cli_repl_plugin/source/test.js | 122 ++- .../sum_extension/source/sum_extension.cpp | 8 +- 8 files changed, 621 insertions(+), 742 deletions(-) create mode 100644 source/cli/plugins/cli_repl_plugin/source/cli_core_command.js diff --git a/source/cli/metacallcli/source/application.cpp b/source/cli/metacallcli/source/application.cpp index b6a3d2702..1c17f4594 100644 --- a/source/cli/metacallcli/source/application.cpp +++ b/source/cli/metacallcli/source/application.cpp @@ -34,663 +34,373 @@ namespace fs = std::experimental::filesystem; using namespace metacallcli; -<<<<<<< Updated upstream -/* -- Private Methods -- */ +static bool exit_condition = true; -bool command_cb_help(application & /*app*/, tokenizer & /*t*/) +/* -- Methods -- */ + +application::parameter_iterator::parameter_iterator(application &app) : + app(app) { - std::cout << "MetaCall Command Line Interface by Parra Studios" << std::endl; - std::cout << "Copyright (C) 2016 - 2024 Vicente Eduardo Ferrer Garcia " << std::endl; - std::cout << std::endl - << "A command line interface for MetaCall Core" << std::endl; - - /* Command list */ - std::cout << std::endl - << "Command list:" << std::endl - << std::endl; - - /* Load command */ - std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; - std::cout << "\t│ Load a script from file into MetaCall │" << std::endl; - std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; - std::cout << "\t│ Usage: │" << std::endl; - std::cout << "\t│ load ... │" << std::endl; - std::cout << "\t│ : identifier to the type of script │" << std::endl; - std::cout << "\t│ options : │" << std::endl; - std::cout << "\t│ mock - Mock (for testing purposes) │" << std::endl; - std::cout << "\t│ py - Python │" << std::endl; - std::cout << "\t│ node - NodeJS │" << std::endl; - std::cout << "\t│ rb - Ruby │" << std::endl; - std::cout << "\t│ cs - C# NetCore │" << std::endl; - std::cout << "\t│ cob - Cobol │" << std::endl; - std::cout << "\t│ ts - TypeScript │" << std::endl; - std::cout << "\t│ js - V8 JavaScript Engine │" << std::endl; - std::cout << "\t│ file - Files (for handling file systems) │" << std::endl; - std::cout << "\t│ ... : relative or absolute path to the script(s) │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Example: │" << std::endl; - std::cout << "\t│ load node concat.js │" << std::endl; - std::cout << "\t│ load py example.py │" << std::endl; - std::cout << "\t│ load rb hello.rb │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Result: │" << std::endl; - std::cout << "\t│ Script (concat.js) loaded correctly │" << std::endl; - std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl - << std::endl; - - /* Inspect command */ - std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; - std::cout << "\t│ Show all runtimes, modules and functions (with their signature) loaded into MetaCall │" << std::endl; - std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; - std::cout << "\t│ Usage: │" << std::endl; - std::cout << "\t│ inspect │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Example: │" << std::endl; - std::cout << "\t│ inspect │" << std::endl; - std::cout << "\t│ │" << std::endl; - std::cout << "\t│ Result: │" << std::endl; - std::cout << "\t│ runtime node { │" << std::endl; - std::cout << "\t│ module concat { │" << std::endl; - std::cout << "\t│ function concat(left, right) │" << std::endl; - std::cout << "\t│ } │" << std::endl; - std::cout << "\t│ } │" << std::endl; - std::cout << "\t└────────────────────────────────────────────────────────────────────────────────────────┘" << std::endl - << std::endl; - - /* Eval command */ - std::cout << "\t┌────────────────────────────────────────────────────────────────────────────────────────┐" << std::endl; - std::cout << "\t│ Evaluate a code snippet with the specified runtime tag │" << std::endl; - std::cout << "\t│────────────────────────────────────────────────────────────────────────────────────────│" << std::endl; - std::cout << "\t│ Usage: │" << std::endl; - std::cout << "\t│ eval