Unified · Split
| @@ -195,14 +195,12 @@ struct exec_options · 194 unmodified lines | ||
| 195 | 195 | , enabled_debugger{ false } |
| 196 | 196 | , quiet_start{ false } |
| 197 | 197 | , print_help{ false } |
| 198 | − , sys_thread_count{ vm_t::default_system_thread_count } | |
| 199 | − , thread_quanta{ vm_t::default_thread_quanta } | |
| 198 | + , vm_config{} | |
| 200 | 199 | { } |
| 201 | 200 | |
| 202 | 201 | std::vector<char *> vm_args; |
| 203 | 202 | |
| 204 | − uint32_t sys_thread_count; | |
| 205 | − uint32_t thread_quanta; | |
| 203 | + vm_config_t vm_config; | |
| 206 | 204 | |
| 207 | 205 | bool print_help; |
| 208 | 206 | bool quiet_start; |
| @@ -220,7 +218,7 @@ void print_banner(const exec_options &options) · 11 unmodified lines | ||
| 220 | 218 | std::cout |
| 221 | 219 | << "\n----------------\n" |
| 222 | 220 | << "Debugger enabled: " << std::boolalpha << options.enabled_debugger << "\n" |
| 223 | − << "System thread usage: " << options.sys_thread_count << "\n"; | |
| 221 | + << "System thread usage: " << options.vm_config.sys_thread_pool_size << "\n"; | |
| 224 | 222 | |
| 225 | 223 | if (options.enabled_debugger) |
| 226 | 224 | std::cout << "\n" << options.debugger << "\n"; |
| @@ -274,14 +272,6 @@ void log_callback(const debug::component_trace_t origin, const debug::log_level_ · 47 unmodified lines | ||
| 274 | 272 | std::cout << ss.str(); |
| 275 | 273 | } |
| 276 | 274 | |
| 277 | −void enable_logging(debug::component_trace_t c) | |
| 278 | −{ | |
| 279 | − debug::set_component_tracing(c, true); | |
| 280 | − | |
| 281 | − // Constantly setting the callback | |
| 282 | − debug::set_logging_callback(log_callback); | |
| 283 | −} | |
| 284 | − | |
| 285 | 275 | void process_arg(char* arg, std::function<char *()> next, exec_options &options) |
| 286 | 276 | { |
| 287 | 277 | assert(arg != nullptr && next != nullptr); |
| @@ -326,28 +316,30 @@ void process_arg(char* arg, std::function<char *()> next, exec_options &options) · 38 unmodified lines | ||
| 326 | 316 | if (sys_threads <= 0 || 4 < sys_threads) |
| 327 | 317 | throw arg_exception_t{ "Invalid system thread value", sys_threads_str }; |
| 328 | 318 | |
| 329 | − options.sys_thread_count = sys_threads; | |
| 319 | + options.vm_config.sys_thread_pool_size = sys_threads; | |
| 330 | 320 | } |
| 331 | 321 | break; |
| 332 | 322 | |
| 333 | 323 | case 'l': |
| 324 | + // Set the callback | |
| 325 | + debug::set_logging_callback(log_callback); | |
| 334 | 326 | for (auto i = std::size_t{ 2 }; i < arg_len; ++i) |
| 335 | 327 | { |
| 336 | 328 | switch (arg[i]) |
| 337 | 329 | { |
| 338 | − case 's': enable_logging(debug::component_trace_t::scheduler); | |
| 330 | + case 's': debug::set_component_tracing(debug::component_trace_t::scheduler, true); | |
| 339 | 331 | break; |
| 340 | − case 'S': enable_logging(debug::component_trace_t::stack); | |
| 332 | + case 'S': debug::set_component_tracing(debug::component_trace_t::stack, true); | |
| 341 | 333 | break; |
| 342 | − case 't': enable_logging(debug::component_trace_t::thread); | |
| 334 | + case 't': debug::set_component_tracing(debug::component_trace_t::thread, true); | |
| 343 | 335 | break; |
| 344 | − case 'T': enable_logging(debug::component_trace_t::tool); | |
| 336 | + case 'T': debug::set_component_tracing(debug::component_trace_t::tool, true); | |
| 345 | 337 | break; |
| 346 | − case 'e': enable_logging(debug::component_trace_t::exception); | |
| 338 | + case 'e': debug::set_component_tracing(debug::component_trace_t::exception, true); | |
| 347 | 339 | break; |
| 348 | − case 'g': enable_logging(debug::component_trace_t::garbage_collector); | |
| 340 | + case 'g': debug::set_component_tracing(debug::component_trace_t::garbage_collector, true); | |
| 349 | 341 | break; |
| 350 | − case 'm': enable_logging(debug::component_trace_t::memory); | |
| 342 | + case 'm': debug::set_component_tracing(debug::component_trace_t::memory, true); | |
| 351 | 343 | break; |
| 352 | 344 | } |
| 353 | 345 | } |
| @@ -402,7 +394,7 @@ int main(int argc, char* argv[]) · 48 unmodified lines | ||
| 402 | 394 | return EXIT_SUCCESS; |
| 403 | 395 | } |
| 404 | 396 | |
| 405 | − vm_t vm{ options.sys_thread_count, options.thread_quanta }; | |
| 397 | + vm_t vm{ std::move(options.vm_config) }; | |
| 406 | 398 | |
| 407 | 399 | if (options.enabled_debugger) |
| 408 | 400 | vm.load_tool(std::make_shared<debugger>(options.debugger)); |
| @@ -7,18 +7,17 @@ · 6 unmodified lines | ||
| 7 | 7 | #ifndef _DISVM_SRC_INCLUDE_DISVM_H_ |
| 8 | 8 | #define _DISVM_SRC_INCLUDE_DISVM_H_ |
| 9 | 9 | |
| 10 | −#include "runtime.h" | |
| 10 | +#include <array> | |
| 11 | 11 | #include <iosfwd> |
| 12 | 12 | #include <memory> |
| 13 | 13 | #include <functional> |
| 14 | 14 | #include <mutex> |
| 15 | 15 | #include <forward_list> |
| 16 | +#include "runtime.h" | |
| 17 | +#include "exceptions.h" | |
| 16 | 18 | |
| 17 | 19 | namespace disvm |
| 18 | 20 | { |
| 19 | − // Read in the module at the supplied path | |
| 20 | − std::unique_ptr<runtime::vm_module_t> read_module(const char *path); | |
| 21 | − | |
| 22 | 21 | // Read in a module from the supplied stream |
| 23 | 22 | std::unique_ptr<runtime::vm_module_t> read_module(std::istream &data); |
| 24 | 23 | |
| @@ -51,20 +50,37 @@ namespace disvm · 26 unmodified lines | ||
| 51 | 50 | // Callback for enumerating loaded modules - return 'false' to stop enumeration, otherwise 'true' |
| 52 | 51 | using loaded_vm_module_callback_t = std::function<bool(const loaded_vm_module_t&)>; |
| 53 | 52 | |
| 53 | + // Configuration for the created VM | |
| 54 | + class vm_config_t final | |
| 55 | + { | |
| 56 | + public: | |
| 57 | + vm_config_t(); | |
| 58 | + vm_config_t(vm_config_t &&); | |
| 59 | + | |
| 60 | + // Settings for the default scheduler. Only used if create_scheduler is not set. | |
| 61 | + // Both of these values are initialized to valid default values on construction. | |
| 62 | + uint32_t sys_thread_pool_size; | |
| 63 | + uint32_t thread_quanta; | |
| 64 | + | |
| 65 | + create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler; | |
| 66 | + create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc; | |
| 67 | + | |
| 68 | + // Prefix paths used by the default module resolver if the path supplied by | |
| 69 | + // the user isn't found. Note these will be concatenated with the supplied | |
| 70 | + // path, not other manipulation will be performed. | |
| 71 | + std::vector<std::string> probing_paths; | |
| 72 | + std::vector<std::unique_ptr<runtime::vm_module_resolver_t>> additional_resolvers; | |
| 73 | + }; | |
| 74 | + | |
| 54 | 75 | // Virtual machine |
| 55 | 76 | class vm_t final |
| 56 | 77 | { |
| 57 | 78 | public: // static |
| 58 | 79 | static const uint32_t root_vm_thread_id; |
| 59 | − static const uint32_t default_system_thread_count; | |
| 60 | − static const uint32_t default_thread_quanta; | |
| 61 | 80 | |
| 62 | 81 | public: |
| 63 | − explicit vm_t( | |
| 64 | − create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler = nullptr, | |
| 65 | − create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc = nullptr); | |
| 66 | − | |
| 67 | − explicit vm_t(uint32_t thread_pool_size, uint32_t thread_quanta); | |
| 82 | + explicit vm_t(); | |
| 83 | + explicit vm_t(vm_config_t config); | |
| 68 | 84 | |
| 69 | 85 | ~vm_t(); |
| 70 | 86 | |
| @@ -92,7 +108,7 @@ namespace disvm · 21 unmodified lines | ||
| 92 | 108 | // Enumerate all loaded modules - this can include references to modules that |
| 93 | 109 | // have been unloaded due to no longer being used. If the module instance is null, the |
| 94 | 110 | // module is no longer loaded. |
| 95 | − // Note this will only enumerate modules that have been loaded by a call to vm_t::load_module. | |
| 111 | + // Note this will only enumerate modules that have been loaded via a path. | |
| 96 | 112 | void enum_loaded_modules(loaded_vm_module_callback_t callback) const; |
| 97 | 113 | |
| 98 | 114 | // Access the scheduler control for this VM. |
| @@ -118,10 +134,17 @@ namespace disvm · 19 unmodified lines | ||
| 118 | 134 | mutable std::mutex _modules_lock; |
| 119 | 135 | loaded_modules_t _modules; |
| 120 | 136 | |
| 137 | + std::vector<std::unique_ptr<runtime::vm_module_resolver_t>> _module_resolvers; | |
| 121 | 138 | std::unique_ptr<runtime::vm_scheduler_t> _scheduler; |
| 122 | 139 | std::unique_ptr<runtime::vm_garbage_collector_t> _gc; |
| 123 | 140 | std::unique_ptr<runtime::vm_tool_dispatch_t> _tool_dispatch; |
| 124 | 141 | std::mutex _tool_dispatch_lock; |
| 142 | + | |
| 143 | + std::atomic_flag _last_syscall_error_message_lock; | |
| 144 | + std::array<char, 128> _last_syscall_error_message; | |
| 145 | + | |
| 146 | + friend void disvm::runtime::push_syscall_error_message(disvm::vm_t &, const char *) noexcept; | |
| 147 | + friend std::string disvm::runtime::pop_syscall_error_message(disvm::vm_t &); | |
| 125 | 148 | }; |
| 126 | 149 | } |
| 127 | 150 | |
| @@ -16,6 +16,10 @@ namespace disvm · 15 unmodified lines | ||
| 16 | 16 | { |
| 17 | 17 | namespace runtime |
| 18 | 18 | { |
| 19 | + // Functions for managing syscall exception messages | |
| 20 | + void push_syscall_error_message(disvm::vm_t &, const char *) noexcept; | |
| 21 | + std::string pop_syscall_error_message(disvm::vm_t &); | |
| 22 | + | |
| 19 | 23 | class vm_user_exception : public std::runtime_error |
| 20 | 24 | { |
| 21 | 25 | public: |
| @@ -32,6 +36,16 @@ namespace disvm · 10 unmodified lines | ||
| 32 | 36 | { } |
| 33 | 37 | }; |
| 34 | 38 | |
| 39 | + class vm_syscall_exception : public vm_system_exception | |
| 40 | + { | |
| 41 | + public: | |
| 42 | + explicit vm_syscall_exception(disvm::vm_t &vm, const char *message) | |
| 43 | + : vm_system_exception(message) | |
| 44 | + { | |
| 45 | + push_syscall_error_message(vm, message); | |
| 46 | + } | |
| 47 | + }; | |
| 48 | + | |
| 35 | 49 | class vm_module_exception : public vm_system_exception |
| 36 | 50 | { |
| 37 | 51 | public: |
| @@ -43,10 +57,6 @@ namespace disvm · 5 unmodified lines | ||
| 43 | 57 | class module_reader_exception final : public vm_module_exception |
| 44 | 58 | { |
| 45 | 59 | public: |
| 46 | − explicit module_reader_exception() | |
| 47 | − : vm_module_exception("Invalid module") | |
| 48 | − { } | |
| 49 | − | |
| 50 | 60 | explicit module_reader_exception(const char *message) |
| 51 | 61 | : vm_module_exception(message) |
| 52 | 62 | { } |
| @@ -880,6 +880,19 @@ namespace disvm · 879 unmodified lines | ||
| 880 | 880 | // Create a garbage collector that does nothing. |
| 881 | 881 | std::unique_ptr<vm_garbage_collector_t> create_no_op_gc(vm_t &); |
| 882 | 882 | |
| 883 | + // VM module path resolver interface | |
| 884 | + class vm_module_resolver_t | |
| 885 | + { | |
| 886 | + public: | |
| 887 | + virtual ~vm_module_resolver_t() = 0; | |
| 888 | + | |
| 889 | + // Try to resolve the supplied path to a module. | |
| 890 | + // Implementers of this interface should never explicitly throw | |
| 891 | + // an exception and instead return 'false'. Exception thrown | |
| 892 | + // indirectly by calling supplied DisVM functions are valid. | |
| 893 | + virtual bool try_resolve_module(const char *path, std::unique_ptr<vm_module_t> &new_module) = 0; | |
| 894 | + }; | |
| 895 | + | |
| 883 | 896 | // Forward declaration |
| 884 | 897 | class vm_tool_controller_t; |
| 885 | 898 | |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | ||
| 11 | 11 | #include <type_traits> |
| 12 | 12 | #include <string> |
| 13 | 13 | #include <cstring> |
| 14 | +#include <cassert> | |
| 14 | 15 | #include <vector> |
| 15 | 16 | |
| 16 | 17 | namespace disvm |
| @@ -36,34 +37,39 @@ namespace disvm · 19 unmodified lines | ||
| 36 | 37 | } |
| 37 | 38 | |
| 38 | 39 | // split the string using the delimiters contained in the supplied string |
| 39 | − inline std::vector<std::string> split(const std::string &str, const char *delims) | |
| 40 | + inline std::vector<std::string> split(const char *str, const char *delims) | |
| 40 | 41 | { |
| 41 | − auto split_string = std::vector<std::string>{}; | |
| 42 | + assert(str != nullptr); | |
| 42 | 43 | |
| 43 | − auto curr = str.begin(); | |
| 44 | − auto end = str.cend(); | |
| 45 | − while (curr != end) | |
| 44 | + std::vector<std::string> parts; | |
| 45 | + auto begin = str; | |
| 46 | + while (*str != '\0') | |
| 46 | 47 | { |
| 47 | − auto curr_begin = curr; | |
| 48 | − | |
| 49 | − while (curr != end && (nullptr == std::strchr(delims, *curr))) | |
| 50 | − ++curr; | |
| 48 | + auto curr = str++; | |
| 49 | + // Check for any delimiter | |
| 50 | + if (nullptr != std::strchr(delims, *curr)) | |
| 51 | + { | |
| 52 | + if (begin != curr) | |
| 53 | + parts.push_back({ begin, curr }); | |
| 51 | 54 | |
| 52 | − if (curr_begin != curr) | |
| 53 | − split_string.push_back(std::move(std::string{ curr_begin, curr })); | |
| 55 | + begin = str; | |
| 56 | + } | |
| 57 | + } | |
| 54 | 58 | |
| 55 | − if (curr == end) | |
| 56 | − break; | |
| 59 | + if (begin != str) | |
| 60 | + parts.push_back({ begin, str }); | |
| 57 | 61 | |
| 58 | − ++curr; | |
| 59 | − } | |
| 62 | + return parts; | |
| 63 | + } | |
| 60 | 64 | |
| 61 | − return split_string; | |
| 65 | + inline std::vector<std::string> split(const std::string &str, const char *delims) | |
| 66 | + { | |
| 67 | + return split(str.c_str(), delims); | |
| 62 | 68 | } |
| 63 | 69 | |
| 64 | 70 | inline std::vector<std::string> split(const std::string &str) |
| 65 | 71 | { |
| 66 | − return split(str, " "); | |
| 72 | + return split(str.c_str(), " "); | |
| 67 | 73 | } |
| 68 | 74 | } |
| 69 | 75 | } |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | ||
| 13 | 13 | 'list.cpp', |
| 14 | 14 | 'module_reader.cpp', |
| 15 | 15 | 'module_ref.cpp', |
| 16 | + 'module_resolver.cpp', | |
| 16 | 17 | 'scheduler.cpp', |
| 17 | 18 | 'stack.cpp', |
| 18 | 19 | 'string.cpp', |
| @@ -1773,7 +1773,6 @@ namespace · 1772 unmodified lines | ||
| 1773 | 1773 | catch (const vm_module_exception &e) |
| 1774 | 1774 | { |
| 1775 | 1775 | debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "exception: load: >>%s<<", e.what()); |
| 1776 | − assert(false && "Failure reading module"); | |
| 1777 | 1776 | return; |
| 1778 | 1777 | } |
| 1779 | 1778 | } |
| @@ -8,7 +8,7 @@ · 7 unmodified lines | ||
| 8 | 8 | #include <limits> |
| 9 | 9 | #include <memory> |
| 10 | 10 | #include <iostream> |
| 11 | −#include <fstream> | |
| 11 | +#include <sstream> | |
| 12 | 12 | #include <array> |
| 13 | 13 | #include <tuple> |
| 14 | 14 | #include <disvm.h> |
| @@ -739,20 +739,6 @@ vm_module_t::~vm_module_t() · 724 unmodified lines | ||
| 739 | 739 | original_mp->release(); |
| 740 | 740 | } |
| 741 | 741 | |
| 742 | −std::unique_ptr<vm_module_t> disvm::read_module(const char *path) | |
| 743 | −{ | |
| 744 | − assert(path != nullptr); | |
| 745 | − | |
| 746 | − auto module_file = std::ifstream{ path, std::ifstream::binary }; | |
| 747 | − if (!module_file.is_open()) | |
| 748 | − { | |
| 749 | − assert(false && "Failed to open module"); | |
| 750 | − throw vm_user_exception{ "Failed to open module" }; | |
| 751 | − } | |
| 752 | − | |
| 753 | − return read_module(module_file); | |
| 754 | −} | |
| 755 | − | |
| 756 | 742 | std::unique_ptr<runtime::vm_module_t> disvm::read_module(std::istream &data) |
| 757 | 743 | { |
| 758 | 744 | util::buffered_reader_t reader{ data }; |
| @@ -0,0 +1,72 @@ | ||
| 1 | +// | |
| 2 | +// Dis VM | |
| 3 | +// File: module_resolver.cpp | |
| 4 | +// Author: arr | |
| 5 | +// | |
| 6 | + | |
| 7 | +#include <cassert> | |
| 8 | +#include <fstream> | |
| 9 | +#include <sstream> | |
| 10 | +#include <disvm.h> | |
| 11 | +#include <debug.h> | |
| 12 | +#include <exceptions.h> | |
| 13 | +#include "module_resolver.h" | |
| 14 | + | |
| 15 | +using namespace disvm::runtime; | |
| 16 | + | |
| 17 | +// Empty destructor for vm module resolver 'interface' | |
| 18 | +vm_module_resolver_t::~vm_module_resolver_t() | |
| 19 | +{ } | |
| 20 | + | |
| 21 | +default_resolver_t::default_resolver_t(disvm::vm_t &vm) | |
| 22 | + : _vm{ vm } | |
| 23 | +{ } | |
| 24 | + | |
| 25 | +default_resolver_t::default_resolver_t(disvm::vm_t &vm, std::vector<std::string> probing_paths) | |
| 26 | + : _probing_paths{ std::move(probing_paths) } | |
| 27 | + , _vm{ vm } | |
| 28 | +{ } | |
| 29 | + | |
| 30 | +default_resolver_t::~default_resolver_t() | |
| 31 | +{ } | |
| 32 | + | |
| 33 | +bool default_resolver_t::try_resolve_module(const char *path, std::unique_ptr<vm_module_t> &new_module) | |
| 34 | +{ | |
| 35 | + assert(path != nullptr); | |
| 36 | + | |
| 37 | + auto log = debug::is_component_tracing_enabled<debug::component_trace_t::module>(); | |
| 38 | + if (log) | |
| 39 | + debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "resolve: try module path: >>%s<<", path); | |
| 40 | + | |
| 41 | + auto module_file = std::ifstream{ path, std::ifstream::binary }; | |
| 42 | + if (!module_file.is_open()) | |
| 43 | + { | |
| 44 | + // The raw path isn't valid, try using probing paths | |
| 45 | + for (auto p : _probing_paths) | |
| 46 | + { | |
| 47 | + p.append(path); | |
| 48 | + if (log) | |
| 49 | + debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "resolve: try module path: >>%s<<", p.c_str()); | |
| 50 | + | |
| 51 | + module_file = std::ifstream{ p, std::ifstream::binary }; | |
| 52 | + if (module_file.is_open()) | |
| 53 | + { | |
| 54 | + if (log) | |
| 55 | + debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "resolve: successful modified module path: >>%s<< >>%s<<", path, p.c_str()); | |
| 56 | + | |
| 57 | + break; | |
| 58 | + } | |
| 59 | + } | |
| 60 | + | |
| 61 | + if (!module_file.is_open()) | |
| 62 | + { | |
| 63 | + push_syscall_error_message(_vm, "Unable to resolve path"); | |
| 64 | + return false; | |
| 65 | + } | |
| 66 | + } | |
| 67 | + | |
| 68 | + new_module = disvm::read_module(module_file); | |
| 69 | + assert(new_module != nullptr); | |
| 70 | + | |
| 71 | + return true; | |
| 72 | +} | |
| @@ -0,0 +1,37 @@ | ||
| 1 | +// | |
| 2 | +// Dis VM | |
| 3 | +// File: module_resolver.h | |
| 4 | +// Author: arr | |
| 5 | +// | |
| 6 | + | |
| 7 | +#ifndef _DISVM_SRC_VM_MODULE_RESOLVER_H_ | |
| 8 | +#define _DISVM_SRC_VM_MODULE_RESOLVER_H_ | |
| 9 | + | |
| 10 | +#include <cstdint> | |
| 11 | +#include <string> | |
| 12 | +#include <vector> | |
| 13 | +#include <runtime.h> | |
| 14 | + | |
| 15 | +namespace disvm | |
| 16 | +{ | |
| 17 | + namespace runtime | |
| 18 | + { | |
| 19 | + // Default module resolver | |
| 20 | + class default_resolver_t final : public vm_module_resolver_t | |
| 21 | + { | |
| 22 | + public: | |
| 23 | + default_resolver_t(disvm::vm_t &vm); | |
| 24 | + default_resolver_t(disvm::vm_t &vm, std::vector<std::string> probing_paths); | |
| 25 | + ~default_resolver_t(); | |
| 26 | + | |
| 27 | + public: // vm_module_resolver_t | |
| 28 | + bool try_resolve_module(const char *path, std::unique_ptr<vm_module_t> &new_module); | |
| 29 | + | |
| 30 | + private: | |
| 31 | + const std::vector<std::string> _probing_paths; | |
| 32 | + disvm::vm_t &_vm; | |
| 33 | + }; | |
| 34 | + } | |
| 35 | +} | |
| 36 | + | |
| 37 | +#endif // _DISVM_SRC_VM_MODULE_RESOLVER_H_ | |
| @@ -431,8 +431,6 @@ bool default_scheduler_t::enqueue_thread_unsafe(std::shared_ptr<thread_instance_ · 430 unmodified lines | ||
| 431 | 431 | throw vm_term_request{ err_msg }; |
| 432 | 432 | } |
| 433 | 433 | |
| 434 | − case vm_thread_state_t::release: | |
| 435 | − case vm_thread_state_t::running: | |
| 436 | 434 | default: |
| 437 | 435 | assert(false && "Unexpected thread state"); |
| 438 | 436 | throw vm_system_exception{ "Unexpected thread state" }; |
| @@ -166,26 +166,26 @@ namespace · 165 unmodified lines | ||
| 166 | 166 | return _fd_path; |
| 167 | 167 | } |
| 168 | 168 | |
| 169 | − word_t read(const word_t buffer_size_in_bytes, void *buffer) | |
| 169 | + word_t read(disvm::vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) | |
| 170 | 170 | { |
| 171 | 171 | if (_istream == nullptr) |
| 172 | 172 | throw vm_system_exception{ "File descriptor not open for read operation" }; |
| 173 | 173 | |
| 174 | 174 | _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes)); |
| 175 | 175 | if (_istream->bad()) |
| 176 | − throw vm_user_exception{ "Read operation error" }; | |
| 176 | + throw vm_syscall_exception{ vm, "Read operation error" }; | |
| 177 | 177 | |
| 178 | 178 | return static_cast<word_t>(_istream->gcount()); |
| 179 | 179 | } |
| 180 | 180 | |
| 181 | − void write(const word_t buffer_size_in_bytes, void *buffer) | |
| 181 | + void write(disvm::vm_t &vm, const word_t buffer_size_in_bytes, void *buffer) | |
| 182 | 182 | { |
| 183 | 183 | if (_ostream == nullptr) |
| 184 | 184 | throw vm_system_exception{ "File descriptor not open for write operation" }; |
| 185 | 185 | |
| 186 | 186 | _ostream->write(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes)); |
| 187 | 187 | if (_ostream->bad()) |
| 188 | − throw vm_user_exception{ "Write operation error" }; | |
| 188 | + throw vm_syscall_exception{ vm, "Write operation error" }; | |
| 189 | 189 | } |
| 190 | 190 | |
| 191 | 191 | big_t seek(const std::ios::seekdir start, const big_t offset) |
| @@ -470,7 +470,16 @@ void · 278 unmodified lines | ||
| 470 | 470 | Sys_fildes(vm_registers_t &r, vm_t &vm) |
| 471 | 471 | { |
| 472 | 472 | auto &fp = r.stack.peek_frame()->base<F_Sys_fildes>(); |
| 473 | − throw vm_system_exception{ "Instruction not implemented" }; | |
| 473 | + | |
| 474 | + dec_ref_count_and_free(vm_alloc_t::from_allocation(*fp.ret)); | |
| 475 | + *fp.ret = nullptr; | |
| 476 | + | |
| 477 | + auto fd = sys::fetch_fd_record(fp.fd); | |
| 478 | + if (fd != nullptr) | |
| 479 | + { | |
| 480 | + fd->add_ref(); | |
| 481 | + *fp.ret = fd->get_allocation(); | |
| 482 | + } | |
| 474 | 483 | } |
| 475 | 484 | |
| 476 | 485 | void |
| @@ -608,16 +617,16 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 131 unmodified lines | ||
| 608 | 617 | |
| 609 | 618 | auto static_buffer = std::array<char, 1024>{}; |
| 610 | 619 | auto msg_args = &fp.vargs; |
| 611 | − auto written = sys::printf_to_buffer(*str, msg_args, fp_base, static_buffer.size(), static_buffer.data()); | |
| 620 | + auto written = sys::printf_to_buffer(vm, *str, msg_args, fp_base, static_buffer.size(), static_buffer.data()); | |
| 612 | 621 | if (written >= 0) |
| 613 | 622 | { |
| 614 | − fd_stdout->write(written, static_buffer.data()); | |
| 623 | + fd_stdout->write(vm, written, static_buffer.data()); | |
| 615 | 624 | } |
| 616 | 625 | else |
| 617 | 626 | { |
| 618 | 627 | auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2); |
| 619 | − written = sys::printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer); | |
| 620 | − fd_stdout->write(written, dynamic_buffer.data()); | |
| 628 | + written = sys::printf_to_dynamic_buffer(vm, *str, msg_args, fp_base, dynamic_buffer); | |
| 629 | + fd_stdout->write(vm, written, dynamic_buffer.data()); | |
| 621 | 630 | } |
| 622 | 631 | |
| 623 | 632 | *fp.ret = written; |
| @@ -657,7 +666,7 @@ Sys_read(vm_registers_t &r, vm_t &vm) · 33 unmodified lines | ||
| 657 | 666 | assert(fd_alloc->alloc_type == T_FD); |
| 658 | 667 | |
| 659 | 668 | auto fd = fd_alloc->get_allocation<vm_fd_t>(); |
| 660 | − *fp.ret = fd->read(n, buffer->at(0)); | |
| 669 | + *fp.ret = fd->read(vm, n, buffer->at(0)); | |
| 661 | 670 | } |
| 662 | 671 | |
| 663 | 672 | void |
| @@ -729,19 +738,28 @@ namespace · 65 unmodified lines | ||
| 729 | 738 | bool is_delim(const runtime::rune_t c, const vm_string_t &delim) |
| 730 | 739 | { |
| 731 | 740 | const auto max = delim.get_length(); |
| 732 | − assert(max > 0); | |
| 733 | 741 | |
| 734 | 742 | // Optimization for 3 or less delimiters |
| 735 | 743 | switch (max) |
| 736 | 744 | { |
| 737 | 745 | default: |
| 738 | 746 | break; |
| 739 | − case 1: | |
| 740 | − return (delim.get_rune(0) == c); | |
| 741 | − case 2: | |
| 742 | − return (delim.get_rune(0) == c) || (delim.get_rune(1) == c); | |
| 743 | 747 | case 3: |
| 744 | − return (delim.get_rune(0) == c) || (delim.get_rune(1) == c) || (delim.get_rune(2) == c); | |
| 748 | + if (delim.get_rune(2) != c) | |
| 749 | + { | |
| 750 | + case 2: | |
| 751 | + if (delim.get_rune(1) != c) | |
| 752 | + { | |
| 753 | + case 1: | |
| 754 | + if (delim.get_rune(0) != c) | |
| 755 | + { | |
| 756 | + case 0: | |
| 757 | + return false; | |
| 758 | + } | |
| 759 | + } | |
| 760 | + } | |
| 761 | + | |
| 762 | + return true; | |
| 745 | 763 | } |
| 746 | 764 | |
| 747 | 765 | for (auto i = word_t{ 0 }; i < max; ++i) |
| @@ -900,7 +918,7 @@ Sys_write(vm_registers_t &r, vm_t &vm) · 152 unmodified lines | ||
| 900 | 918 | assert(fd_alloc->alloc_type == T_FD); |
| 901 | 919 | |
| 902 | 920 | auto fd = fd_alloc->get_allocation<vm_fd_t>(); |
| 903 | − fd->write(n, buffer->at(0)); | |
| 921 | + fd->write(vm, n, buffer->at(0)); | |
| 904 | 922 | |
| 905 | 923 | *fp.ret = n; |
| 906 | 924 | } |
| @@ -64,7 +64,7 @@ vm_alloc_t * disvm::runtime::sys::fetch_fd_record(word_t fd) · 63 unmodified lines | ||
| 64 | 64 | std::lock_guard<std::mutex> lock{ fd_map_lock }; |
| 65 | 65 | auto iter = fd_map.find(fd); |
| 66 | 66 | if (iter == fd_map.cend()) |
| 67 | − throw vm_user_exception{ "Unknown file descriptor" }; | |
| 67 | + return nullptr; | |
| 68 | 68 | |
| 69 | 69 | return iter->second; |
| 70 | 70 | } |
| @@ -30,8 +30,9 @@ namespace · 29 unmodified lines | ||
| 30 | 30 | } |
| 31 | 31 | |
| 32 | 32 | // Shamelessly adopted and altered from Inferno implementation (xprint - libinterp/runt.c) |
| 33 | −// [TODO] Handle UTF-8 | |
| 33 | +// [TODO] Handle UTF-8, binary format 'B' | |
| 34 | 34 | word_t disvm::runtime::sys::printf_to_buffer( |
| 35 | + disvm::vm_t &vm, | |
| 35 | 36 | const disvm::runtime::vm_string_t &msg_fmt, |
| 36 | 37 | disvm::runtime::byte_t *msg_args, |
| 37 | 38 | disvm::runtime::pointer_t base, |
| @@ -101,8 +102,8 @@ word_t disvm::runtime::sys::printf_to_buffer( · 63 unmodified lines | ||
| 101 | 102 | } |
| 102 | 103 | case 'o': // octal |
| 103 | 104 | case 'd': // decimal |
| 104 | − case 'x': // hexidecimal | |
| 105 | − case 'X': // Upper-case hexidecimal | |
| 105 | + case 'x': // hexadecimal | |
| 106 | + case 'X': // Upper-case hexadecimal | |
| 106 | 107 | format.push_back(static_cast<char>(c)); |
| 107 | 108 | format.push_back('\0'); |
| 108 | 109 | if (big_flag) |
| @@ -130,6 +131,12 @@ word_t disvm::runtime::sys::printf_to_buffer( · 21 unmodified lines | ||
| 130 | 131 | msg_args += sizeof(p); |
| 131 | 132 | break; |
| 132 | 133 | } |
| 134 | + case 'r': // Last system error message | |
| 135 | + { | |
| 136 | + auto err_msg = pop_syscall_error_message(vm); | |
| 137 | + wb = std::snprintf(b_curr, (b_end - b_curr), "%s", err_msg.c_str()); | |
| 138 | + break; | |
| 139 | + } | |
| 133 | 140 | // [SPEC] Undocumented format verb for debugging VM objects. |
| 134 | 141 | case 'H': |
| 135 | 142 | { |
| @@ -163,13 +170,14 @@ word_t disvm::runtime::sys::printf_to_buffer( · 27 unmodified lines | ||
| 163 | 170 | } |
| 164 | 171 | |
| 165 | 172 | word_t disvm::runtime::sys::printf_to_dynamic_buffer( |
| 173 | + disvm::vm_t &vm, | |
| 166 | 174 | const disvm::runtime::vm_string_t &msg_fmt, |
| 167 | 175 | disvm::runtime::byte_t *msg_args, |
| 168 | 176 | disvm::runtime::pointer_t base, |
| 169 | 177 | std::vector<char> &buffer) |
| 170 | 178 | { |
| 171 | 179 | auto result = word_t{ 0 }; |
| 172 | − while (-1 == (result = printf_to_buffer(msg_fmt, msg_args, base, buffer.size(), buffer.data()))) | |
| 180 | + while (-1 == (result = printf_to_buffer(vm, msg_fmt, msg_args, base, buffer.size(), buffer.data()))) | |
| 173 | 181 | buffer.resize(buffer.size() * 2); |
| 174 | 182 | |
| 175 | 183 | return result; |
| @@ -22,6 +22,7 @@ namespace disvm · 21 unmodified lines | ||
| 22 | 22 | // Prints the format and arguments to the supplied buffer. |
| 23 | 23 | // Returns the number of bytes written. |
| 24 | 24 | word_t printf_to_buffer( |
| 25 | + disvm::vm_t &vm, | |
| 25 | 26 | const vm_string_t &msg_fmt, |
| 26 | 27 | byte_t *msg_args, |
| 27 | 28 | pointer_t base, |
| @@ -31,6 +32,7 @@ namespace disvm · 3 unmodified lines | ||
| 31 | 32 | // Prints the format and arguments to the supplied dynamic buffer. |
| 32 | 33 | // Returns the number of bytes written. |
| 33 | 34 | word_t printf_to_dynamic_buffer( |
| 35 | + disvm::vm_t &vm, | |
| 34 | 36 | const vm_string_t &msg_fmt, |
| 35 | 37 | byte_t *msg_args, |
| 36 | 38 | pointer_t base, |
| @@ -52,10 +54,10 @@ namespace disvm · 15 unmodified lines | ||
| 52 | 54 | // The first three FD records are optimized for lock-less access. |
| 53 | 55 | word_t create_fd_record(vm_alloc_t *fd_alloc); |
| 54 | 56 | |
| 57 | + // Returns null if the record does not exist. | |
| 55 | 58 | vm_alloc_t * fetch_fd_record(word_t fd); |
| 56 | 59 | |
| 57 | 60 | void drop_fd_record(word_t fd); |
| 58 | − | |
| 59 | 61 | } |
| 60 | 62 | } |
| 61 | 63 | } |
| @@ -7,6 +7,7 @@ · 6 unmodified lines | ||
| 7 | 7 | #include <cassert> |
| 8 | 8 | #include <memory> |
| 9 | 9 | #include <algorithm> |
| 10 | +#include <sstream> | |
| 10 | 11 | #include <disvm.h> |
| 11 | 12 | #include <debug.h> |
| 12 | 13 | #include <runtime.h> |
| @@ -16,41 +17,107 @@ · 3 unmodified lines | ||
| 16 | 17 | #include "scheduler.h" |
| 17 | 18 | #include "garbage_collector.h" |
| 18 | 19 | #include "tool_dispatch.h" |
| 20 | +#include "module_resolver.h" | |
| 19 | 21 | |
| 20 | 22 | using namespace disvm; |
| 21 | 23 | using namespace disvm::runtime; |
| 22 | 24 | |
| 25 | +namespace | |
| 26 | +{ | |
| 27 | + // Ideally this would be custom assembly based on the CPU, | |
| 28 | + // but since this is just for the setting of the last error | |
| 29 | + // message there is already a large cost associated with the | |
| 30 | + // fact an error occurred so this is okay. | |
| 31 | + struct inefficient_spin_lock_t final | |
| 32 | + { | |
| 33 | + inefficient_spin_lock_t(std::atomic_flag &is_lock) | |
| 34 | + : _lf{ is_lock } | |
| 35 | + { | |
| 36 | + while (!_lf.test_and_set()) | |
| 37 | + ; | |
| 38 | + } | |
| 39 | + | |
| 40 | + ~inefficient_spin_lock_t() | |
| 41 | + { | |
| 42 | + _lf.clear(); | |
| 43 | + } | |
| 44 | + | |
| 45 | + private: | |
| 46 | + std::atomic_flag &_lf; | |
| 47 | + }; | |
| 48 | +} | |
| 49 | + | |
| 50 | +void disvm::runtime::push_syscall_error_message(disvm::vm_t &vm, const char *msg) noexcept | |
| 51 | +{ | |
| 52 | + inefficient_spin_lock_t sl{ vm._last_syscall_error_message_lock }; | |
| 53 | + | |
| 54 | + assert(msg != nullptr); | |
| 55 | + auto bytes_to_copy = std::min(std::strlen(msg), vm._last_syscall_error_message.size() - 1); | |
| 56 | + std::memcpy(vm._last_syscall_error_message.data(), msg, bytes_to_copy); | |
| 57 | + | |
| 58 | + // Ensure there is always a null | |
| 59 | + vm._last_syscall_error_message[bytes_to_copy] = '\0'; | |
| 60 | +} | |
| 61 | + | |
| 62 | +std::string disvm::runtime::pop_syscall_error_message(disvm::vm_t &vm) | |
| 63 | +{ | |
| 64 | + inefficient_spin_lock_t sl{ vm._last_syscall_error_message_lock }; | |
| 65 | + | |
| 66 | + std::string msg{ vm._last_syscall_error_message.data() }; | |
| 67 | + vm._last_syscall_error_message[0] = '\0'; | |
| 68 | + return msg; | |
| 69 | +} | |
| 70 | + | |
| 23 | 71 | const uint32_t vm_t::root_vm_thread_id = 0; |
| 24 | −const uint32_t vm_t::default_system_thread_count = 1; | |
| 25 | 72 | |
| 26 | 73 | // The Inferno implementation defined the thread quanta as 2048 (include/interp.h) |
| 27 | −const uint32_t vm_t::default_thread_quanta = 2048; | |
| 28 | − | |
| 29 | −vm_t::vm_t( | |
| 30 | − create_vm_interface_callback_t<runtime::vm_scheduler_t> create_scheduler, | |
| 31 | − create_vm_interface_callback_t<runtime::vm_garbage_collector_t> create_gc) | |
| 74 | +const uint32_t default_thread_quanta = 2048; | |
| 75 | +const uint32_t default_system_thread_count = 1; | |
| 76 | + | |
| 77 | +vm_config_t::vm_config_t() | |
| 78 | + : create_gc{ nullptr } | |
| 79 | + , create_scheduler{ nullptr } | |
| 80 | + , sys_thread_pool_size{ default_system_thread_count } | |
| 81 | + , thread_quanta{ default_thread_quanta } | |
| 82 | +{ } | |
| 83 | + | |
| 84 | +vm_config_t::vm_config_t(vm_config_t &&other) | |
| 85 | + : additional_resolvers{ std::move(other.additional_resolvers) } | |
| 86 | + , create_gc{ other.create_gc } | |
| 87 | + , create_scheduler{ other.create_scheduler } | |
| 88 | + , probing_paths{ std::move(other.probing_paths) } | |
| 89 | + , sys_thread_pool_size{ other.sys_thread_pool_size } | |
| 90 | + , thread_quanta{ other.thread_quanta } | |
| 91 | +{ } | |
| 92 | + | |
| 93 | +vm_t::vm_t() | |
| 94 | + : _last_syscall_error_message_lock{ ATOMIC_FLAG_INIT } | |
| 32 | 95 | { |
| 33 | 96 | // Initialize built-in modules. |
| 34 | 97 | builtin::initialize_builtin_modules(); |
| 35 | 98 | |
| 36 | − if (create_gc == nullptr) | |
| 37 | − _gc = std::make_unique<default_garbage_collector_t>(*this); | |
| 38 | − else | |
| 39 | − _gc = create_gc(*this); | |
| 40 | − | |
| 41 | − if (create_scheduler == nullptr) | |
| 42 | − _scheduler = std::make_unique<default_scheduler_t>(*this, default_system_thread_count, default_thread_quanta); | |
| 43 | − else | |
| 44 | − _scheduler = create_scheduler(*this); | |
| 99 | + _gc = std::make_unique<default_garbage_collector_t>(*this); | |
| 100 | + _scheduler = std::make_unique<default_scheduler_t>(*this, default_system_thread_count, default_thread_quanta); | |
| 101 | + _module_resolvers.push_back(std::make_unique<default_resolver_t>(*this)); | |
| 45 | 102 | } |
| 46 | 103 | |
| 47 | −vm_t::vm_t(uint32_t system_thread_count, uint32_t thread_quanta) | |
| 104 | +vm_t::vm_t(vm_config_t config) | |
| 48 | 105 | { |
| 49 | 106 | // Initialize built-in modules. |
| 50 | 107 | builtin::initialize_builtin_modules(); |
| 51 | 108 | |
| 52 | − _gc = std::make_unique<default_garbage_collector_t>(*this); | |
| 53 | − _scheduler = std::make_unique<default_scheduler_t>(*this, system_thread_count, thread_quanta); | |
| 109 | + if (config.create_gc == nullptr) | |
| 110 | + _gc = std::make_unique<default_garbage_collector_t>(*this); | |
| 111 | + else | |
| 112 | + _gc = config.create_gc(*this); | |
| 113 | + | |
| 114 | + if (config.create_scheduler == nullptr) | |
| 115 | + _scheduler = std::make_unique<default_scheduler_t>(*this, config.sys_thread_pool_size, config.thread_quanta); | |
| 116 | + else | |
| 117 | + _scheduler = config.create_scheduler(*this); | |
| 118 | + | |
| 119 | + _module_resolvers = std::move(config.additional_resolvers); | |
| 120 | + _module_resolvers.push_back(std::make_unique<default_resolver_t>(*this, std::move(config.probing_paths))); | |
| 54 | 121 | } |
| 55 | 122 | |
| 56 | 123 | vm_t::~vm_t() |
| @@ -74,7 +141,7 @@ vm_version_t vm_t::get_version() const · 17 unmodified lines | ||
| 74 | 141 | |
| 75 | 142 | namespace |
| 76 | 143 | { |
| 77 | − std::unique_ptr<runtime::vm_thread_t> _create_thread_safe(std::unique_ptr<runtime::vm_module_ref_t> &entry_module_ref) | |
| 144 | + std::unique_ptr<runtime::vm_thread_t> _create_thread_safe(std::unique_ptr<runtime::vm_module_ref_t> entry_module_ref) | |
| 78 | 145 | { |
| 79 | 146 | auto thread = std::make_unique<vm_thread_t>(*entry_module_ref, disvm::vm_t::root_vm_thread_id); |
| 80 | 147 | |
| @@ -92,7 +159,7 @@ const runtime::vm_thread_t& vm_t::exec(const char *path) · 11 unmodified lines | ||
| 92 | 159 | auto entry_module = load_module(path); |
| 93 | 160 | |
| 94 | 161 | auto entry_module_ref = std::make_unique<vm_module_ref_t>(entry_module); |
| 95 | − auto thread = _create_thread_safe(entry_module_ref); | |
| 162 | + auto thread = _create_thread_safe(std::move(entry_module_ref)); | |
| 96 | 163 | return schedule_thread(std::move(thread)); |
| 97 | 164 | } |
| 98 | 165 | |
| @@ -101,7 +168,7 @@ const runtime::vm_thread_t& vm_t::exec(std::unique_ptr<runtime::vm_module_t> ent · 2 unmodified lines | ||
| 101 | 168 | assert(entry_module != nullptr); |
| 102 | 169 | |
| 103 | 170 | auto entry_module_ref = std::make_unique<vm_module_ref_t>(std::move(entry_module)); |
| 104 | − auto thread = _create_thread_safe(entry_module_ref); | |
| 171 | + auto thread = _create_thread_safe(std::move(entry_module_ref)); | |
| 105 | 172 | return schedule_thread(std::move(thread)); |
| 106 | 173 | } |
| 107 | 174 | |
| @@ -112,12 +179,64 @@ const runtime::vm_thread_t& vm_t::fork( · 4 unmodified lines | ||
| 112 | 179 | runtime::vm_pc_t initial_pc) |
| 113 | 180 | { |
| 114 | 181 | if (initial_pc < 0 || static_cast<vm_pc_t>(module_ref.code_section.size()) <= initial_pc) |
| 115 | − throw vm_system_exception{ "Invalid entry program counter" }; | |
| 182 | + throw vm_user_exception{ "Invalid entry program counter" }; | |
| 116 | 183 | |
| 117 | 184 | auto thread = std::make_unique<vm_thread_t>(module_ref, parent_tid, initial_frame, initial_pc); |
| 118 | 185 | return schedule_thread(std::move(thread)); |
| 119 | 186 | } |
| 120 | 187 | |
| 188 | +namespace | |
| 189 | +{ | |
| 190 | + std::unique_ptr<vm_module_t> resolve_module_from_path(const char *path, const std::vector<std::unique_ptr<runtime::vm_module_resolver_t>> &resolvers) | |
| 191 | + { | |
| 192 | + assert(!resolvers.empty()); | |
| 193 | + std::unique_ptr<vm_module_t> resolved_module; | |
| 194 | + | |
| 195 | + // Check if the path is for the Inferno OS. | |
| 196 | + const char *inferno_root_path = "/dis/"; | |
| 197 | + if (std::strncmp(path, inferno_root_path, sizeof(inferno_root_path)) == 0) | |
| 198 | + { | |
| 199 | + if (debug::is_component_tracing_enabled<debug::component_trace_t::module>()) | |
| 200 | + debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "load: vm module: Inferno OS path detected - >>%s<<", path); | |
| 201 | + | |
| 202 | + // Plus 1 for the next character. We know this will either be | |
| 203 | + // null or valid character since we searched for a string | |
| 204 | + // containing the character above. | |
| 205 | + const char *module_name = std::strrchr(path, '/') + 1; | |
| 206 | + | |
| 207 | + if (*module_name != '\0') | |
| 208 | + { | |
| 209 | + for (auto &r : resolvers) | |
| 210 | + { | |
| 211 | + // Since we are using a modified path, throwing an exception | |
| 212 | + // isn't fair. We will catch all exceptions here and rely on | |
| 213 | + // non-modified path resolution to trigger the actual failure. | |
| 214 | + try | |
| 215 | + { | |
| 216 | + if (r->try_resolve_module(module_name, resolved_module)) | |
| 217 | + return resolved_module; | |
| 218 | + } | |
| 219 | + catch (...) | |
| 220 | + { | |
| 221 | + // No-op | |
| 222 | + } | |
| 223 | + } | |
| 224 | + } | |
| 225 | + } | |
| 226 | + | |
| 227 | + // Attempt to resolve the path | |
| 228 | + for (auto &r : resolvers) | |
| 229 | + { | |
| 230 | + if (r->try_resolve_module(path, resolved_module)) | |
| 231 | + return resolved_module; | |
| 232 | + } | |
| 233 | + | |
| 234 | + std::stringstream ss; | |
| 235 | + ss << "Failed to resolve path to module: " << path; | |
| 236 | + throw vm_module_exception{ ss.str().c_str() }; | |
| 237 | + } | |
| 238 | +} | |
| 239 | + | |
| 121 | 240 | std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) |
| 122 | 241 | { |
| 123 | 242 | if (path == nullptr) |
| @@ -139,12 +258,13 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 15 unmodified lines | ||
| 139 | 258 | // If an entry exists but is null, the module was loaded before. Read the module again and return. |
| 140 | 259 | if (module == nullptr) |
| 141 | 260 | { |
| 142 | − module = std::shared_ptr<vm_module_t>{ std::move(read_module(path)) }; | |
| 261 | + module = std::shared_ptr<vm_module_t>{ std::move(resolve_module_from_path(path, _module_resolvers)) }; | |
| 143 | 262 | module->vm_id = iter->vm_id; |
| 144 | 263 | |
| 145 | 264 | iter->module = module; |
| 146 | 265 | |
| 147 | − debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<", path); | |
| 266 | + if (debug::is_component_tracing_enabled<debug::component_trace_t::module>()) | |
| 267 | + debug::log_msg(debug::component_trace_t::module, debug::log_level_t::debug, "reload: vm module: >>%s<<", path); | |
| 148 | 268 | } |
| 149 | 269 | |
| 150 | 270 | return module; |
| @@ -159,8 +279,8 @@ std::shared_ptr<runtime::vm_module_t> vm_t::load_module(const char *path) · 8 unmodified lines | ||
| 159 | 279 | } |
| 160 | 280 | else |
| 161 | 281 | { |
| 162 | − // Read in the module | |
| 163 | − new_module = std::move(read_module(path)); | |
| 282 | + new_module = resolve_module_from_path(path, _module_resolvers); | |
| 283 | + assert(new_module != nullptr); | |
| 164 | 284 | } |
| 165 | 285 | |
| 166 | 286 | auto path_local = std::make_unique<vm_string_t>(std::strlen(path), reinterpret_cast<const uint8_t *>(path)); |
| @@ -283,7 +283,12 @@ vm_alloc_t::vm_alloc_t(std::shared_ptr<const type_descriptor_t> td) · 282 unmodified lines | ||
| 283 | 283 | |
| 284 | 284 | vm_alloc_t::~vm_alloc_t() |
| 285 | 285 | { |
| 286 | − assert(_ref_count == 0); | |
| 286 | +#ifndef NDEBUG | |
| 287 | + if (_ref_count != 0) | |
| 288 | + debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::warning, | |
| 289 | + "vm alloc being destroy with non-zero reference count. " | |
| 290 | + "This could be okay if this is happening due to frame unwinding from an exception"); | |
| 291 | +#endif | |
| 287 | 292 | |
| 288 | 293 | auto finalizer = alloc_type->finalizer; |
| 289 | 294 | if (finalizer != type_descriptor_t::no_finalizer) |
| @@ -291,7 +296,9 @@ vm_alloc_t::~vm_alloc_t() · 1 unmodified line | ||
| 291 | 296 | |
| 292 | 297 | // Free pointer types |
| 293 | 298 | destroy_memory(*alloc_type, get_allocation()); |
| 294 | − debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: vm alloc"); | |
| 299 | + | |
| 300 | + if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>()) | |
| 301 | + debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: vm alloc"); | |
| 295 | 302 | } |
| 296 | 303 | |
| 297 | 304 | std::size_t vm_alloc_t::add_ref() |