Unified · Split
| @@ -24,6 +24,7 @@ namespace disvm · 23 unmodified lines | ||
| 24 | 24 | stack, |
| 25 | 25 | channel, |
| 26 | 26 | exception, |
| 27 | + tool, | |
| 27 | 28 | memory, // Noisy component |
| 28 | 29 | addressing, // Noisy component [DEBUG build only] |
| 29 | 30 | |
| @@ -71,6 +71,10 @@ namespace disvm · 70 unmodified lines | ||
| 71 | 71 | // Load the module at the supplied path into the virtual machine |
| 72 | 72 | std::shared_ptr<const runtime::vm_module_t> load_module(const char *path); |
| 73 | 73 | |
| 74 | + // Get a collection of all currently loaded modules. | |
| 75 | + // Note this will only include modules that have been loaded by a call to vm_t::load_module. | |
| 76 | + std::vector<std::shared_ptr<const runtime::vm_module_t>> get_loaded_modules() const; | |
| 77 | + | |
| 74 | 78 | // Access the scheduler control for this VM. |
| 75 | 79 | runtime::vm_scheduler_control_t &get_scheduler_control() const; |
| 76 | 80 | |
| @@ -86,7 +90,7 @@ namespace disvm · 9 unmodified lines | ||
| 86 | 90 | class loaded_module_t final |
| 87 | 91 | { |
| 88 | 92 | public: |
| 89 | − loaded_module_t(std::unique_ptr<runtime::vm_string_t> origin, std::shared_ptr<const runtime::vm_module_t> module); | |
| 93 | + loaded_module_t(std::unique_ptr<runtime::vm_string_t> origin, std::shared_ptr<runtime::vm_module_t> module); | |
| 90 | 94 | loaded_module_t(const loaded_module_t &) = delete; |
| 91 | 95 | loaded_module_t &operator=(const loaded_module_t &) = delete; |
| 92 | 96 | |
| @@ -97,11 +101,11 @@ namespace disvm · 4 unmodified lines | ||
| 97 | 101 | std::unique_ptr<runtime::vm_string_t> origin; |
| 98 | 102 | |
| 99 | 103 | // Weak reference to a loaded module |
| 100 | − std::weak_ptr<const runtime::vm_module_t> module; | |
| 104 | + std::weak_ptr<runtime::vm_module_t> module; | |
| 101 | 105 | }; |
| 102 | 106 | |
| 103 | 107 | using loaded_modules_t = std::forward_list<loaded_module_t>; |
| 104 | − std::mutex _modules_lock; | |
| 108 | + mutable std::mutex _modules_lock; | |
| 105 | 109 | loaded_modules_t _modules; |
| 106 | 110 | |
| 107 | 111 | std::unique_ptr<runtime::vm_scheduler_t> _scheduler; |
| @@ -190,9 +190,12 @@ namespace disvm · 189 unmodified lines | ||
| 190 | 190 | expf, |
| 191 | 191 | self, |
| 192 | 192 | |
| 193 | + // [SPEC] Added opcode for debugging support. | |
| 194 | + brkpt, | |
| 195 | + | |
| 193 | 196 | // markers |
| 194 | 197 | first_opcode = invalid, |
| 195 | − last_opcode = self | |
| 198 | + last_opcode = brkpt | |
| 196 | 199 | }; |
| 197 | 200 | } |
| 198 | 201 | |
| @@ -21,7 +21,7 @@ · 20 unmodified lines | ||
| 21 | 21 | |
| 22 | 22 | namespace disvm |
| 23 | 23 | { |
| 24 | − // VM forward declation | |
| 24 | + // Forward declaration | |
| 25 | 25 | class vm_t; |
| 26 | 26 | |
| 27 | 27 | namespace runtime |
| @@ -43,7 +43,7 @@ namespace disvm · 15 unmodified lines | ||
| 43 | 43 | // VM memory management |
| 44 | 44 | // |
| 45 | 45 | |
| 46 | − // VM forward declation | |
| 46 | + // Forward declaration | |
| 47 | 47 | class vm_alloc_t; |
| 48 | 48 | |
| 49 | 49 | // Finalizer callback for allocations. |
| @@ -75,7 +75,7 @@ namespace disvm · 25 unmodified lines | ||
| 75 | 75 | const vm_alloc_instance_finalizer_t finalizer; |
| 76 | 76 | }; |
| 77 | 77 | |
| 78 | − // Access to type descriptors for VM intrinsic types. | |
| 78 | + // Access to type descriptors for VM intrinsic types | |
| 79 | 79 | class intrinsic_type_desc final |
| 80 | 80 | { |
| 81 | 81 | public: |
| @@ -262,7 +262,7 @@ namespace disvm · 180 unmodified lines | ||
| 262 | 262 | const bool _is_alloc; |
| 263 | 263 | }; |
| 264 | 264 | |
| 265 | − // VM forward declation | |
| 265 | + // Forward declaration | |
| 266 | 266 | class vm_channel_t; |
| 267 | 267 | |
| 268 | 268 | // VM request mutex |
| @@ -299,10 +299,12 @@ namespace disvm · 30 unmodified lines | ||
| 299 | 299 | public: //static |
| 300 | 300 | static std::shared_ptr<const type_descriptor_t> type_desc(); |
| 301 | 301 | |
| 302 | + using data_transfer_func_t = void(*)(pointer_t, pointer_t, const type_descriptor_t *); | |
| 303 | + | |
| 302 | 304 | public: |
| 303 | 305 | vm_channel_t( |
| 304 | 306 | std::shared_ptr<const type_descriptor_t> td, |
| 305 | − std::function<void(pointer_t, pointer_t, const type_descriptor_t *)> transfer, | |
| 307 | + data_transfer_func_t transfer, | |
| 306 | 308 | word_t buffer_len); |
| 307 | 309 | ~vm_channel_t(); |
| 308 | 310 | |
| @@ -331,7 +333,7 @@ namespace disvm · 22 unmodified lines | ||
| 331 | 333 | private: |
| 332 | 334 | std::mutex _data_lock; |
| 333 | 335 | std::shared_ptr<const type_descriptor_t> _data_type; |
| 334 | − std::function<void(pointer_t, pointer_t, const type_descriptor_t *)> _data_transfer; | |
| 336 | + data_transfer_func_t _data_transfer; | |
| 335 | 337 | |
| 336 | 338 | vm_array_t *_data_buffer; |
| 337 | 339 | uint32_t _data_buffer_head; // Next element to remove from buffer |
| @@ -406,7 +408,7 @@ namespace disvm · 68 unmodified lines | ||
| 406 | 408 | using src_data_t = inst_data_generic_t; |
| 407 | 409 | using dest_data_t = inst_data_generic_t; |
| 408 | 410 | |
| 409 | − // VM forward declation | |
| 411 | + // Forward declaration | |
| 410 | 412 | class vm_registers_t; |
| 411 | 413 | |
| 412 | 414 | // Instruction execution operation |
| @@ -710,6 +712,7 @@ namespace disvm · 297 unmodified lines | ||
| 710 | 712 | |
| 711 | 713 | // Forward declaration |
| 712 | 714 | class vm_thread_t; |
| 715 | + class vm_tool_dispatch_t; | |
| 713 | 716 | |
| 714 | 717 | // VM registers |
| 715 | 718 | class vm_registers_t |
| @@ -721,6 +724,7 @@ namespace disvm · 5 unmodified lines | ||
| 721 | 724 | ~vm_registers_t(); |
| 722 | 725 | |
| 723 | 726 | const vm_thread_t &thread; |
| 727 | + std::atomic<vm_tool_dispatch_t *> tool_dispatch; | |
| 724 | 728 | vm_stack_t stack; // Frame pointer access (FP) |
| 725 | 729 | vm_pc_t pc; // Program counter (Index into module code section) |
| 726 | 730 | vm_alloc_t *mp_base; // Module data base pointer (MP) |
| @@ -752,13 +756,17 @@ namespace disvm · 25 unmodified lines | ||
| 752 | 756 | uint32_t parent_thread_id); |
| 753 | 757 | vm_thread_t( |
| 754 | 758 | vm_module_ref_t &entry, |
| 759 | + uint32_t parent_thread_id, | |
| 755 | 760 | const vm_frame_t &initial_frame, |
| 756 | − vm_pc_t start_pc, | |
| 757 | − uint32_t parent_thread_id); | |
| 761 | + vm_pc_t start_pc); | |
| 758 | 762 | |
| 759 | 763 | ~vm_thread_t(); |
| 760 | 764 | |
| 761 | − vm_thread_state_t execute(vm_t &virtual_machine, const uint32_t quanta); | |
| 765 | + vm_thread_state_t execute(vm_t &vm, const uint32_t quanta); | |
| 766 | + | |
| 767 | + // There is no way to get the current dispatch value. | |
| 768 | + // Setting the dispatch to 'null' detaches all tool related behavior | |
| 769 | + void set_tool_dispatch(vm_tool_dispatch_t *dispatch); | |
| 762 | 770 | |
| 763 | 771 | vm_thread_state_t get_state() const; |
| 764 | 772 | |
| @@ -834,6 +842,25 @@ namespace disvm · 69 unmodified lines | ||
| 834 | 842 | |
| 835 | 843 | // Create a garbage collector that does nothing. |
| 836 | 844 | std::unique_ptr<vm_garbage_collector_t> create_no_op_gc(vm_t &); |
| 845 | + | |
| 846 | + // Forward declaration | |
| 847 | + class vm_tool_controller_t; | |
| 848 | + | |
| 849 | + // VM tool interface | |
| 850 | + // Used to implement tools (e.g. debugger or profiler) | |
| 851 | + class vm_tool_t | |
| 852 | + { | |
| 853 | + public: | |
| 854 | + virtual ~vm_tool_t() = 0; | |
| 855 | + | |
| 856 | + // Called when the tool is loaded into the VM | |
| 857 | + virtual void on_load(vm_t &vm, vm_tool_controller_t &controller, std::size_t tool_id) = 0; | |
| 858 | + | |
| 859 | + // Called when the tool is unloaded from the VM. | |
| 860 | + // Note once the tool has been unloaded the vm and controller references supplied during load | |
| 861 | + // should not be used as they may no longer be valid. | |
| 862 | + virtual void on_unload() = 0; | |
| 863 | + }; | |
| 837 | 864 | } |
| 838 | 865 | } |
| 839 | 866 | |
| @@ -33,7 +33,7 @@ namespace disvm · 32 unmodified lines | ||
| 33 | 33 | // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. |
| 34 | 34 | std::size_t count_codepoints(const uint8_t *str, const std::size_t max_len); |
| 35 | 35 | |
| 36 | − enum class decode_state | |
| 36 | + enum class decode_state_t | |
| 37 | 37 | { |
| 38 | 38 | accept = 0, |
| 39 | 39 | reject = 12 |
| @@ -41,7 +41,7 @@ namespace disvm · 1 unmodified line | ||
| 41 | 41 | |
| 42 | 42 | // Decode a byte at a time to form a codepoint. |
| 43 | 43 | // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. |
| 44 | − decode_state decode_step(decode_state &state, rune_t &codepoint, uint8_t byte); | |
| 44 | + decode_state_t decode_step(decode_state_t &state, rune_t &codepoint, uint8_t byte); | |
| 45 | 45 | |
| 46 | 46 | // Decode the next codepoint in the supplied string. |
| 47 | 47 | // Returns number of characters in string consumed. |
| @@ -0,0 +1,63 @@ | ||
| 1 | +// | |
| 2 | +// Dis VM | |
| 3 | +// File: vm_tools.h | |
| 4 | +// Author: arr | |
| 5 | +// | |
| 6 | + | |
| 7 | +#ifndef _DISVM_SRC_INCLUDE_VM_TOOLS_H_ | |
| 8 | +#define _DISVM_SRC_INCLUDE_VM_TOOLS_H_ | |
| 9 | + | |
| 10 | +#include <cstdint> | |
| 11 | +#include <vector> | |
| 12 | +#include <memory> | |
| 13 | +#include <functional> | |
| 14 | +#include "runtime.h" | |
| 15 | + | |
| 16 | +namespace disvm | |
| 17 | +{ | |
| 18 | + namespace runtime | |
| 19 | + { | |
| 20 | + union vm_context_value_t | |
| 21 | + { | |
| 22 | + std::size_t i; | |
| 23 | + vm_t *vm; | |
| 24 | + vm_module_t *module; | |
| 25 | + vm_registers_t *registers; | |
| 26 | + vm_alloc_t *alloc; | |
| 27 | + }; | |
| 28 | + | |
| 29 | + struct vm_event_context_t | |
| 30 | + { | |
| 31 | + vm_context_value_t value1; | |
| 32 | + vm_context_value_t value2; | |
| 33 | + }; | |
| 34 | + | |
| 35 | + enum class vm_event_t | |
| 36 | + { | |
| 37 | + module_load, // value1: vm_module_t | |
| 38 | + module_unload, // value1: vm_module_t | |
| 39 | + thread_begin, // value1: vm_registers_t | |
| 40 | + thread_end, // value1: vm_registers_t | |
| 41 | + exception_raised, // value1: vm_registers_t value2: vm_alloc_t | |
| 42 | + exception_unhandled, // value1: vm_registers_t value2: vm_alloc_t | |
| 43 | + breakpoint, // value1: vm_registers_t value2: std::size_t | |
| 44 | + }; | |
| 45 | + | |
| 46 | + using vm_event_callback_t = std::function<void(vm_event_t, vm_event_context_t &)>; | |
| 47 | + | |
| 48 | + // VM tool controller | |
| 49 | + class vm_tool_controller_t | |
| 50 | + { | |
| 51 | + public: | |
| 52 | + // Call the supplied callback when the specified event occurs | |
| 53 | + virtual std::size_t subscribe_event(vm_event_t, vm_event_callback_t) = 0; | |
| 54 | + virtual void unsubscribe_event(std::size_t) = 0; | |
| 55 | + | |
| 56 | + // Set a breakpoint in the supplied module at the specified PC location | |
| 57 | + virtual std::size_t set_breakpoint(std::shared_ptr<const vm_module_t>, vm_pc_t) = 0; | |
| 58 | + virtual void clear_breakpoint(std::size_t) = 0; | |
| 59 | + }; | |
| 60 | + } | |
| 61 | +} | |
| 62 | + | |
| 63 | +#endif // _DISVM_SRC_INCLUDE_VM_TOOLS_H_ | |
| @@ -29,7 +29,7 @@ std::shared_ptr<const type_descriptor_t> vm_channel_t::type_desc() · 28 unmodified lines | ||
| 29 | 29 | |
| 30 | 30 | vm_channel_t::vm_channel_t( |
| 31 | 31 | std::shared_ptr<const type_descriptor_t> td, |
| 32 | − std::function<void(pointer_t, pointer_t, const type_descriptor_t *)> transfer, | |
| 32 | + data_transfer_func_t transfer, | |
| 33 | 33 | word_t buffer_len) |
| 34 | 34 | : vm_alloc_t(vm_channel_t::type_desc()) |
| 35 | 35 | , _data_buffer{ nullptr } |
| @@ -17,6 +17,7 @@ · 16 unmodified lines | ||
| 17 | 17 | 'stack.cpp', |
| 18 | 18 | 'string.cpp', |
| 19 | 19 | 'thread.cpp', |
| 20 | + 'tool_dispatch.cpp', | |
| 20 | 21 | 'utf8.cpp', |
| 21 | 22 | 'vm.cpp', |
| 22 | 23 | 'vm_memory.cpp', |
| @@ -18,6 +18,7 @@ · 17 unmodified lines | ||
| 18 | 18 | #include <exceptions.h> |
| 19 | 19 | #include <debug.h> |
| 20 | 20 | #include <builtin_module.h> |
| 21 | +#include "tool_dispatch.h" | |
| 21 | 22 | #include "execution_table.h" |
| 22 | 23 | |
| 23 | 24 | using namespace disvm; |
| @@ -186,7 +187,7 @@ namespace · 162 unmodified lines | ||
| 186 | 187 | auto buffer = std::array<char, sizeof("�2147483648")>{}; |
| 187 | 188 | |
| 188 | 189 | const auto w = vt_ref<word_t>(r.src); |
| 189 | − const auto len = ::sprintf(buffer.data(), "%d", w); | |
| 190 | + const auto len = std::sprintf(buffer.data(), "%d", w); | |
| 190 | 191 | assert(static_cast<std::size_t>(len) <= buffer.size()); |
| 191 | 192 | |
| 192 | 193 | auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data())); |
| @@ -212,7 +213,7 @@ namespace · 19 unmodified lines | ||
| 212 | 213 | auto buffer = std::array<char, sizeof("-2.2250738585072014e-308")>{}; |
| 213 | 214 | |
| 214 | 215 | const auto real = vt_ref<real_t>(r.src); |
| 215 | − const auto len = ::sprintf(buffer.data(), "%g", real); | |
| 216 | + const auto len = std::sprintf(buffer.data(), "%g", real); | |
| 216 | 217 | assert(static_cast<std::size_t>(len) <= buffer.size()); |
| 217 | 218 | |
| 218 | 219 | auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data())); |
| @@ -238,7 +239,7 @@ namespace · 19 unmodified lines | ||
| 238 | 239 | auto buffer = std::array<char, sizeof("-9223372036854775808")>{}; |
| 239 | 240 | |
| 240 | 241 | const auto b = vt_ref<big_t>(r.src); |
| 241 | − const auto len = ::sprintf(buffer.data(), "%lld", b); | |
| 242 | + const auto len = std::sprintf(buffer.data(), "%lld", b); | |
| 242 | 243 | assert(static_cast<std::size_t>(len) <= buffer.size()); |
| 243 | 244 | |
| 244 | 245 | auto new_string = new vm_string_t(len, reinterpret_cast<uint8_t *>(buffer.data())); |
| @@ -643,6 +644,7 @@ namespace · 398 unmodified lines | ||
| 643 | 644 | break; |
| 644 | 645 | } |
| 645 | 646 | |
| 647 | + assert(0 <= static_cast<std::size_t>(target_pc) && static_cast<std::size_t>(target_pc) < r.module_ref->code_section.size()); | |
| 646 | 648 | r.pc = target_pc; |
| 647 | 649 | } |
| 648 | 650 | |
| @@ -1036,7 +1038,7 @@ namespace · 387 unmodified lines | ||
| 1036 | 1038 | vm_registers_t &r, |
| 1037 | 1039 | const vm_t &vm, |
| 1038 | 1040 | std::shared_ptr<const type_descriptor_t> type_desc, |
| 1039 | − std::function<void(pointer_t, pointer_t, const type_descriptor_t *)> transfer) | |
| 1041 | + vm_channel_t::data_transfer_func_t transfer) | |
| 1040 | 1042 | { |
| 1041 | 1043 | auto buffer_len = word_t{ 0 }; |
| 1042 | 1044 | if (r.mid != r.dest) |
| @@ -1066,7 +1068,7 @@ namespace · 23 unmodified lines | ||
| 1066 | 1068 | { |
| 1067 | 1069 | auto memory_size = vt_ref<word_t>(r.src); |
| 1068 | 1070 | auto channel_data_type = type_descriptor_t::create(memory_size); |
| 1069 | − | |
| 1071 | + | |
| 1070 | 1072 | _newc_(r, vm, channel_data_type, _channel_movm); |
| 1071 | 1073 | } |
| 1072 | 1074 | |
| @@ -1669,6 +1671,24 @@ namespace · 596 unmodified lines | ||
| 1669 | 1671 | r.pc = handler_pc; |
| 1670 | 1672 | } |
| 1671 | 1673 | |
| 1674 | + // [SPEC] Added to support debugger. | |
| 1675 | + // The actual opcode for execution can be retrieved from the tool dispatch interface. | |
| 1676 | + // The current register state is for the real opcode, the breakpoint opcode has no inputs/outputs. | |
| 1677 | + EXEC_DECL(brkpt) | |
| 1678 | + { | |
| 1679 | + auto tool_dispatch = r.tool_dispatch.load(); | |
| 1680 | + if (tool_dispatch == nullptr) | |
| 1681 | + { | |
| 1682 | + assert(false && "Breakpoint set but no tool dispatcher found" ); | |
| 1683 | + throw vm_system_exception{ "Breakpoint set but no tool dispatcher found" }; | |
| 1684 | + } | |
| 1685 | + | |
| 1686 | + const auto original_op = tool_dispatch->on_breakpoint(r, vm); | |
| 1687 | + | |
| 1688 | + // Continue execution | |
| 1689 | + vm_exec_table[static_cast<std::size_t>(original_op)](r, vm); | |
| 1690 | + } | |
| 1691 | + | |
| 1672 | 1692 | // |
| 1673 | 1693 | // Module operations |
| 1674 | 1694 | // |
| @@ -1895,4 +1915,5 @@ const vm_exec_t disvm::runtime::vm_exec_table[static_cast<std::size_t>(opcode_t: · 220 unmodified lines | ||
| 1895 | 1915 | expl, |
| 1896 | 1916 | expf, |
| 1897 | 1917 | notimpl, // self, |
| 1918 | + brkpt, | |
| 1898 | 1919 | }; |
| @@ -5,6 +5,7 @@ · 4 unmodified lines | ||
| 5 | 5 | // |
| 6 | 6 | |
| 7 | 7 | #include <debug.h> |
| 8 | +#include <iostream> | |
| 8 | 9 | #include <sstream> |
| 9 | 10 | #include <exceptions.h> |
| 10 | 11 | #include "scheduler.h" |
| @@ -52,8 +53,7 @@ void default_scheduler_t::worker_main(default_scheduler_t &instance) · 41 unmodified lines | ||
| 52 | 53 | if (current_thread != nullptr) |
| 53 | 54 | { |
| 54 | 55 | const auto &r = current_thread->vm_thread->get_registers(); |
| 55 | − walk_stack(r, [&err_msg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref) | |
| 56 | − { | |
| 56 | + walk_stack(r, [&err_msg](const pointer_t, const vm_pc_t pc, const vm_module_ref_t &module_ref) { | |
| 57 | 57 | auto module_name = "<No Name>"; |
| 58 | 58 | if (module_ref.module->module_name != nullptr) |
| 59 | 59 | module_name = module_ref.module->module_name->str(); |
| @@ -63,7 +63,9 @@ void default_scheduler_t::worker_main(default_scheduler_t &instance) · 3 unmodified lines | ||
| 63 | 63 | }); |
| 64 | 64 | } |
| 65 | 65 | |
| 66 | − std::fprintf(stderr, "%s\n", err_msg.str().c_str()); | |
| 66 | + auto err_str = err_msg.str(); | |
| 67 | + std::cerr << err_str.c_str() << std::endl; | |
| 68 | + | |
| 67 | 69 | instance._terminating = true; |
| 68 | 70 | instance._worker_event.notify_all(); |
| 69 | 71 | } |
| @@ -78,6 +80,11 @@ default_scheduler_t::default_scheduler_t(vm_t &vm, uint32_t system_thread_count, · 8 unmodified lines | ||
| 78 | 80 | , _vm{ vm } |
| 79 | 81 | , _vm_thread_quanta{ thread_quanta } |
| 80 | 82 | { |
| 83 | + if (_worker_thread_count == 0) | |
| 84 | + throw vm_system_exception{ "Work thread count must be > 0" }; | |
| 85 | + | |
| 86 | + if (_vm_thread_quanta == 0) | |
| 87 | + throw vm_system_exception{ "Work thread quanta must be > 0" }; | |
| 81 | 88 | } |
| 82 | 89 | |
| 83 | 90 | default_scheduler_t::~default_scheduler_t() |
| @@ -292,21 +292,21 @@ Sys_byte2char(vm_registers_t &r, vm_t &vm) · 291 unmodified lines | ||
| 292 | 292 | fp.ret->t1 = 0; |
| 293 | 293 | fp.ret->t2 = 0; |
| 294 | 294 | |
| 295 | − auto state = utf8::decode_state::accept; | |
| 295 | + auto state = utf8::decode_state_t::accept; | |
| 296 | 296 | auto result = rune_t{}; |
| 297 | 297 | const auto buffer_len = buffer->get_length(); |
| 298 | 298 | for (auto i = n; i < buffer_len; ++i) |
| 299 | 299 | { |
| 300 | 300 | auto b = buffer->at<byte_t>(i); |
| 301 | 301 | state = utf8::decode_step(state, result, b); |
| 302 | − if (state == utf8::decode_state::accept) | |
| 302 | + if (state == utf8::decode_state_t::accept) | |
| 303 | 303 | { |
| 304 | 304 | fp.ret->t0 = result; |
| 305 | 305 | fp.ret->t1 = (i - n) + 1; // Number of bytes consumed |
| 306 | 306 | fp.ret->t2 = 1; |
| 307 | 307 | return; |
| 308 | 308 | } |
| 309 | − else if (state == utf8::decode_state::reject) | |
| 309 | + else if (state == utf8::decode_state_t::reject) | |
| 310 | 310 | { |
| 311 | 311 | fp.ret->t1 = 1; |
| 312 | 312 | return; |
| @@ -133,6 +133,7 @@ vm_registers_t::vm_registers_t( · 132 unmodified lines | ||
| 133 | 133 | , thread{ thread } |
| 134 | 134 | , pc{ entry.module->header.entry_pc } |
| 135 | 135 | , stack{ entry.module->header.stack_extent } |
| 136 | + , tool_dispatch{ nullptr } | |
| 136 | 137 | , src{ nullptr } |
| 137 | 138 | , mid{ nullptr } |
| 138 | 139 | , dest{ nullptr } |
| @@ -203,9 +204,9 @@ vm_thread_t::vm_thread_t( · 64 unmodified lines | ||
| 203 | 204 | |
| 204 | 205 | vm_thread_t::vm_thread_t( |
| 205 | 206 | vm_module_ref_t &entry, |
| 207 | + uint32_t parent_thread_id, | |
| 206 | 208 | const vm_frame_t &initial_frame, |
| 207 | − vm_pc_t start_pc, | |
| 208 | − uint32_t parent_thread_id) | |
| 209 | + vm_pc_t start_pc) | |
| 209 | 210 | : vm_alloc_t(vm_thread_t::type_desc()) |
| 210 | 211 | , _parent_thread_id{ parent_thread_id } |
| 211 | 212 | , _registers{ *this, entry } |
| @@ -236,7 +237,7 @@ vm_thread_t::~vm_thread_t() · 24 unmodified lines | ||
| 236 | 237 | debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "destroy: vm thread: %d %d\n", _thread_id, _parent_thread_id); |
| 237 | 238 | } |
| 238 | 239 | |
| 239 | −vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, const uint32_t quanta) | |
| 240 | +vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) | |
| 240 | 241 | { |
| 241 | 242 | _registers.current_thread_state = vm_thread_state_t::running; |
| 242 | 243 | assert(_registers.pc != runtime_constants::invalid_program_counter); |
| @@ -249,7 +250,9 @@ vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, const uint32_t qua · 6 unmodified lines | ||
| 249 | 250 | { |
| 250 | 251 | if (_registers.stack.peek_frame() == nullptr) |
| 251 | 252 | { |
| 252 | − debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "exit: vm thread: stack exhausted: %d\n", _thread_id); | |
| 253 | + if (debug::is_component_tracing_enabled<debug::component_trace_t::thread>()) | |
| 254 | + debug::log_msg(debug::component_trace_t::thread, debug::log_level_t::debug, "exit: vm thread: stack exhausted: %d\n", _thread_id); | |
| 255 | + | |
| 253 | 256 | _registers.current_thread_state = vm_thread_state_t::empty_stack; |
| 254 | 257 | break; |
| 255 | 258 | } |
| @@ -257,14 +260,16 @@ vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, const uint32_t qua · 1 unmodified line | ||
| 257 | 260 | const auto &code_section = _registers.module_ref->code_section; |
| 258 | 261 | assert(static_cast<std::size_t>(_registers.pc) < code_section.size()); |
| 259 | 262 | |
| 263 | + assert(!_registers.module_ref->is_builtin_module() && "Interpreter thread is unable to execute native instructions"); | |
| 260 | 264 | const auto &inst = code_section[_registers.pc].op; |
| 261 | − const auto op_code = static_cast<std::size_t>(inst.opcode); | |
| 262 | − if (static_cast<std::size_t>(opcode_t::last_opcode) < op_code) | |
| 265 | + const auto opcode = static_cast<std::size_t>(inst.opcode); | |
| 266 | + if (static_cast<std::size_t>(opcode_t::last_opcode) < opcode) | |
| 263 | 267 | throw vm_system_exception{ "Unknown op-code" }; |
| 264 | 268 | |
| 269 | + // Instruction is valid, decode and update registers for operation | |
| 265 | 270 | decode_address(inst, _registers); |
| 266 | 271 | _registers.pc++; |
| 267 | − vm_exec_table[op_code](_registers, virtual_machine); | |
| 272 | + vm_exec_table[opcode](_registers, vm); | |
| 268 | 273 | |
| 269 | 274 | if (_registers.current_thread_state != vm_thread_state_t::running) |
| 270 | 275 | break; |
| @@ -307,6 +312,12 @@ vm_thread_state_t vm_thread_t::execute(vm_t &virtual_machine, const uint32_t qua · 36 unmodified lines | ||
| 307 | 312 | return _registers.current_thread_state; |
| 308 | 313 | } |
| 309 | 314 | |
| 315 | +void vm_thread_t::set_tool_dispatch(vm_tool_dispatch_t *dispatch) | |
| 316 | +{ | |
| 317 | + assert((_registers.tool_dispatch.load() == nullptr) != (dispatch == nullptr) && "Dispatch value should toggle between null and non-null"); | |
| 318 | + _registers.tool_dispatch = dispatch; | |
| 319 | +} | |
| 320 | + | |
| 310 | 321 | vm_thread_state_t vm_thread_t::get_state() const |
| 311 | 322 | { |
| 312 | 323 | return _registers.current_thread_state; |
| @@ -0,0 +1,232 @@ | ||
| 1 | +// | |
| 2 | +// Dis VM | |
| 3 | +// File: tool_dispatch.cpp | |
| 4 | +// Author: arr | |
| 5 | +// | |
| 6 | + | |
| 7 | +#include <cassert> | |
| 8 | +#include <debug.h> | |
| 9 | +#include <vm_tools.h> | |
| 10 | +#include <exceptions.h> | |
| 11 | +#include <unordered_set> | |
| 12 | +#include <utils.h> | |
| 13 | +#include "tool_dispatch.h" | |
| 14 | + | |
| 15 | +using namespace disvm; | |
| 16 | +using namespace disvm::runtime; | |
| 17 | + | |
| 18 | +// Empty destructor for vm tool 'interface' | |
| 19 | +vm_tool_t::~vm_tool_t() | |
| 20 | +{ | |
| 21 | +} | |
| 22 | + | |
| 23 | +vm_tool_dispatch_t::vm_tool_dispatch_t(vm_t &vm) | |
| 24 | + : _vm{ vm } | |
| 25 | +{ | |
| 26 | + _events.cookie_counter = 1; | |
| 27 | + _breakpoints.cookie_counter = 1; | |
| 28 | +} | |
| 29 | + | |
| 30 | +vm_tool_dispatch_t::~vm_tool_dispatch_t() | |
| 31 | +{ | |
| 32 | + std::lock_guard<std::mutex> lock{ _tools_lock }; | |
| 33 | + for (auto &t : _tools) | |
| 34 | + t.second->on_unload(); | |
| 35 | +} | |
| 36 | + | |
| 37 | +std::size_t vm_tool_dispatch_t::load_tool(std::shared_ptr<runtime::vm_tool_t> tool) | |
| 38 | +{ | |
| 39 | + static std::size_t _tool_id_counter{ 1 }; | |
| 40 | + | |
| 41 | + if (tool == nullptr) | |
| 42 | + throw vm_system_exception{ "Tool cannot be null" }; | |
| 43 | + | |
| 44 | + // Keep the lock for the collection until the tool has been completed being loaded. | |
| 45 | + std::lock_guard<std::mutex> lock{ _tools_lock }; | |
| 46 | + const auto current_id = _tool_id_counter++; | |
| 47 | + _tools[current_id] = tool; | |
| 48 | + | |
| 49 | + try | |
| 50 | + { | |
| 51 | + // [TODO] For debug builds, it might be useful to wrap the controller in a proxy that records | |
| 52 | + // the subscribed callbacks and asserts they are all properly unsubscribed during agent unload. | |
| 53 | + tool->on_load(_vm, *this, current_id); | |
| 54 | + } | |
| 55 | + catch (...) | |
| 56 | + { | |
| 57 | + if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>()) | |
| 58 | + debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::warning, "load: tool: failure"); | |
| 59 | + | |
| 60 | + // The ID is tainted and will not be re-used | |
| 61 | + _tools.erase(current_id); | |
| 62 | + throw; | |
| 63 | + } | |
| 64 | + | |
| 65 | + if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>()) | |
| 66 | + debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "load: tool: %d", current_id); | |
| 67 | + | |
| 68 | + return current_id; | |
| 69 | +} | |
| 70 | + | |
| 71 | +std::size_t vm_tool_dispatch_t::unload_tool(std::size_t tool_id) | |
| 72 | +{ | |
| 73 | + // Keep the lock for the collection until the tool has been completed being unloaded. | |
| 74 | + std::lock_guard<std::mutex> lock{ _tools_lock }; | |
| 75 | + auto iter = _tools.find(tool_id); | |
| 76 | + if (iter == _tools.cend()) | |
| 77 | + return _tools.size(); | |
| 78 | + | |
| 79 | + auto tool = iter->second; | |
| 80 | + _tools.erase(iter); | |
| 81 | + | |
| 82 | + if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>()) | |
| 83 | + debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "unload: tool: %d", tool_id); | |
| 84 | + | |
| 85 | + assert(tool != nullptr); | |
| 86 | + tool->on_unload(); | |
| 87 | + | |
| 88 | + return _tools.size(); | |
| 89 | +} | |
| 90 | + | |
| 91 | +opcode_t vm_tool_dispatch_t::on_breakpoint(vm_registers_t &r, vm_t &vm) | |
| 92 | +{ | |
| 93 | + return get_original_opcode(r.module_ref->module.get(), r.pc - 1); | |
| 94 | +} | |
| 95 | + | |
| 96 | +std::size_t vm_tool_dispatch_t::subscribe_event(vm_event_t evt, vm_event_callback_t cb) | |
| 97 | +{ | |
| 98 | + if (!cb) | |
| 99 | + throw vm_system_exception{ "Invalid callback for event" }; | |
| 100 | + | |
| 101 | + std::lock_guard<std::mutex> lock{ _events.lock }; | |
| 102 | + | |
| 103 | + const auto cookie_id = _events.cookie_counter++; | |
| 104 | + | |
| 105 | + // Store the event callback | |
| 106 | + auto &event_callbacks = _events.callbacks[evt]; | |
| 107 | + event_callbacks[cookie_id] = cb; | |
| 108 | + | |
| 109 | + // Store the cookie with the associate event | |
| 110 | + _events.cookie_to_event[cookie_id] = evt; | |
| 111 | + | |
| 112 | + if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>()) | |
| 113 | + debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "subscribe: event: %d %d", evt, cookie_id); | |
| 114 | + | |
| 115 | + return cookie_id; | |
| 116 | +} | |
| 117 | + | |
| 118 | +void vm_tool_dispatch_t::unsubscribe_event(std::size_t cookie_id) | |
| 119 | +{ | |
| 120 | + std::lock_guard<std::mutex> lock{ _events.lock }; | |
| 121 | + | |
| 122 | + auto iter = _events.cookie_to_event.find(cookie_id); | |
| 123 | + if (iter == _events.cookie_to_event.cend()) | |
| 124 | + return; | |
| 125 | + | |
| 126 | + // Erase cookie and event | |
| 127 | + const auto evt = iter->second; | |
| 128 | + auto &event_callbacks = _events.callbacks[evt]; | |
| 129 | + event_callbacks.erase(cookie_id); | |
| 130 | + _events.cookie_to_event.erase(iter); | |
| 131 | + | |
| 132 | + if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>()) | |
| 133 | + debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "unsubscribe: event: %d %d", evt, cookie_id); | |
| 134 | +} | |
| 135 | + | |
| 136 | +std::size_t vm_tool_dispatch_t::set_breakpoint(std::shared_ptr<const vm_module_t> module, vm_pc_t pc) | |
| 137 | +{ | |
| 138 | + if (module == nullptr || util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin)) | |
| 139 | + throw vm_system_exception{ "Unable to set breakpoint in supplied module" }; | |
| 140 | + | |
| 141 | + // [TODO] This should be re-considered. Removing 'const' is less than ideal. | |
| 142 | + // Remove the const value since the code section needs to be updated. | |
| 143 | + auto &code_section = const_cast<code_section_t &>(module->code_section); | |
| 144 | + if (pc >= code_section.size()) | |
| 145 | + throw vm_system_exception{ "Invalid PC for module" }; | |
| 146 | + | |
| 147 | + const auto real_opcode = code_section[pc].op.opcode; | |
| 148 | + | |
| 149 | + { | |
| 150 | + std::lock_guard<std::mutex> lock{ _breakpoints.lock }; | |
| 151 | + if (real_opcode == opcode_t::brkpt) | |
| 152 | + throw vm_system_exception{ "Breakpoint already set at PC" }; | |
| 153 | + | |
| 154 | + const auto cookie_id = _breakpoints.cookie_counter++; | |
| 155 | + | |
| 156 | + // Map the cookie to the module/pc pair. | |
| 157 | + _breakpoints.cookie_to_modulepc[cookie_id] = std::make_pair(module, pc); | |
| 158 | + | |
| 159 | + // Record the original opcode | |
| 160 | + auto &pc_map = _breakpoints.original_opcodes[reinterpret_cast<std::uintptr_t>(module.get())]; | |
| 161 | + pc_map[pc] = real_opcode; | |
| 162 | + | |
| 163 | + // Replace the current opcode with breakpoint | |
| 164 | + code_section[pc].op.opcode = opcode_t::brkpt; | |
| 165 | + | |
| 166 | + if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>()) | |
| 167 | + debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "breakpoint: set: %d %d >>%s<<", cookie_id, pc, module->module_name->str()); | |
| 168 | + | |
| 169 | + return cookie_id; | |
| 170 | + } | |
| 171 | +} | |
| 172 | + | |
| 173 | +void vm_tool_dispatch_t::clear_breakpoint(std::size_t cookie_id) | |
| 174 | +{ | |
| 175 | + std::lock_guard<std::mutex> lock{ _breakpoints.lock }; | |
| 176 | + | |
| 177 | + // Resolve the cookie to the module/pc pair | |
| 178 | + auto iter_cookie = _breakpoints.cookie_to_modulepc.find(cookie_id); | |
| 179 | + if (iter_cookie == _breakpoints.cookie_to_modulepc.cend()) | |
| 180 | + return; | |
| 181 | + | |
| 182 | + auto mod_pc_pair = iter_cookie->second; | |
| 183 | + const auto target_pc = mod_pc_pair.second; | |
| 184 | + | |
| 185 | + // Determine the original opcode | |
| 186 | + auto iter_orig = _breakpoints.original_opcodes.find(reinterpret_cast<std::uintptr_t>(mod_pc_pair.first.get())); | |
| 187 | + assert(iter_orig != _breakpoints.original_opcodes.cend()); | |
| 188 | + | |
| 189 | + auto &pc_map = iter_orig->second; | |
| 190 | + auto iter_pc = pc_map.find(target_pc); | |
| 191 | + assert(iter_pc != pc_map.cend()); | |
| 192 | + | |
| 193 | + auto original_opcode = iter_pc->second; | |
| 194 | + | |
| 195 | + // Replace the breakpoint opcode with the original | |
| 196 | + auto &code_section = const_cast<code_section_t &>(mod_pc_pair.first->code_section); | |
| 197 | + assert(code_section[target_pc].op.opcode == opcode_t::brkpt); | |
| 198 | + code_section[target_pc].op.opcode = original_opcode; | |
| 199 | + | |
| 200 | + if (debug::is_component_tracing_enabled<debug::component_trace_t::tool>()) | |
| 201 | + debug::log_msg(debug::component_trace_t::tool, debug::log_level_t::debug, "breakpoint: unset: %d %d >>%s<<", cookie_id, target_pc, mod_pc_pair.first->module_name->str()); | |
| 202 | + | |
| 203 | + // Clean-up the PC mapping to opcode | |
| 204 | + pc_map.erase(iter_pc); | |
| 205 | + | |
| 206 | + // If the PC map is empty, erase the module mapping as well | |
| 207 | + if (pc_map.size() == 0) | |
| 208 | + _breakpoints.original_opcodes.erase(iter_orig); | |
| 209 | + | |
| 210 | + // Remove the cookie map | |
| 211 | + _breakpoints.cookie_to_modulepc.erase(iter_cookie); | |
| 212 | +} | |
| 213 | + | |
| 214 | +opcode_t vm_tool_dispatch_t::get_original_opcode(const vm_module_t *module, vm_pc_t pc) | |
| 215 | +{ | |
| 216 | + if (module == nullptr || util::has_flag(module->header.runtime_flag, runtime_flags_t::builtin)) | |
| 217 | + throw vm_system_exception{ "Unable to determine original opcode in supplied module" }; | |
| 218 | + | |
| 219 | + std::lock_guard<std::mutex> lock{ _breakpoints.lock }; | |
| 220 | + | |
| 221 | + auto iter_orig = _breakpoints.original_opcodes.find(reinterpret_cast<std::uintptr_t>(module)); | |
| 222 | + if (iter_orig != _breakpoints.original_opcodes.cend()) | |
| 223 | + { | |
| 224 | + auto &pc_map = iter_orig->second; | |
| 225 | + auto iter_pc = pc_map.find(pc); | |
| 226 | + if (iter_pc != pc_map.cend()) | |
| 227 | + return iter_pc->second; | |
| 228 | + } | |
| 229 | + | |
| 230 | + assert(false && "Why are we missing this information?"); | |
| 231 | + return opcode_t::runt; // Return NOP instruction? | |
| 232 | +} | |
| @@ -0,0 +1,84 @@ | ||
| 1 | +// | |
| 2 | +// Dis VM | |
| 3 | +// File: tool_dispatch.h | |
| 4 | +// Author: arr | |
| 5 | +// | |
| 6 | + | |
| 7 | +#ifndef _DISVM_SRC_VM_TOOL_DISPATCH_H_ | |
| 8 | +#define _DISVM_SRC_VM_TOOL_DISPATCH_H_ | |
| 9 | + | |
| 10 | +#include <cstdint> | |
| 11 | +#include <memory> | |
| 12 | +#include <unordered_map> | |
| 13 | +#include <mutex> | |
| 14 | +#include <runtime.h> | |
| 15 | +#include <vm_tools.h> | |
| 16 | + | |
| 17 | +namespace disvm | |
| 18 | +{ | |
| 19 | + namespace runtime | |
| 20 | + { | |
| 21 | + class vm_tool_dispatch_t final : private vm_tool_controller_t | |
| 22 | + { | |
| 23 | + public: | |
| 24 | + vm_tool_dispatch_t(vm_t &vm); | |
| 25 | + | |
| 26 | + virtual ~vm_tool_dispatch_t(); | |
| 27 | + | |
| 28 | + // Loads a tool for dispatch and returns the tool's ID | |
| 29 | + std::size_t load_tool(std::shared_ptr<runtime::vm_tool_t> tool); | |
| 30 | + | |
| 31 | + // Unloads the tool associated with the supplied ID and returns the current tool count | |
| 32 | + std::size_t unload_tool(std::size_t tool_id); | |
| 33 | + | |
| 34 | + // Handle a breakpoint event. | |
| 35 | + // Returns opcode that was replaced by breakpoint opcode. | |
| 36 | + opcode_t on_breakpoint(vm_registers_t &r, vm_t &vm); | |
| 37 | + | |
| 38 | + public: // vm_tool_controller_t | |
| 39 | + std::size_t subscribe_event(vm_event_t evt, vm_event_callback_t cb) override final; | |
| 40 | + void unsubscribe_event(std::size_t cookie_id) override final; | |
| 41 | + | |
| 42 | + std::size_t set_breakpoint(std::shared_ptr<const vm_module_t> module, vm_pc_t pc) override final; | |
| 43 | + void clear_breakpoint(std::size_t cookie_id) override final; | |
| 44 | + | |
| 45 | + private: | |
| 46 | + opcode_t get_original_opcode(const vm_module_t *module, vm_pc_t pc); | |
| 47 | + | |
| 48 | + private: | |
| 49 | + vm_t &_vm; | |
| 50 | + | |
| 51 | + using tool_collection_t = std::unordered_map<std::size_t, std::shared_ptr<vm_tool_t>>; | |
| 52 | + tool_collection_t _tools; | |
| 53 | + std::mutex _tools_lock; | |
| 54 | + | |
| 55 | + struct | |
| 56 | + { | |
| 57 | + using cookie_to_callback_map_t = std::unordered_map<std::size_t, vm_event_callback_t>; | |
| 58 | + using event_to_callbacks_map_t = std::unordered_map<vm_event_t, cookie_to_callback_map_t>; | |
| 59 | + event_to_callbacks_map_t callbacks; | |
| 60 | + | |
| 61 | + using cookie_to_event_map_t = std::unordered_map<std::size_t, vm_event_t>; | |
| 62 | + cookie_to_event_map_t cookie_to_event; | |
| 63 | + | |
| 64 | + std::mutex lock; | |
| 65 | + std::size_t cookie_counter; | |
| 66 | + } _events; | |
| 67 | + | |
| 68 | + struct | |
| 69 | + { | |
| 70 | + using cookie_to_modulepc_t = std::unordered_map<vm_pc_t, std::pair<std::shared_ptr<const vm_module_t>, vm_pc_t>>; | |
| 71 | + cookie_to_modulepc_t cookie_to_modulepc; | |
| 72 | + | |
| 73 | + using pc_to_opcode_map_t = std::unordered_map<vm_pc_t, opcode_t>; | |
| 74 | + using module_to_pc_map_t = std::unordered_map<std::uintptr_t, pc_to_opcode_map_t>; | |
| 75 | + module_to_pc_map_t original_opcodes; | |
| 76 | + | |
| 77 | + std::mutex lock; | |
| 78 | + std::size_t cookie_counter; | |
| 79 | + } _breakpoints; | |
| 80 | + }; | |
| 81 | + } | |
| 82 | +} | |
| 83 | + | |
| 84 | +#endif // _DISVM_SRC_VM_TOOL_DISPATCH_H_ | |
| @@ -40,15 +40,15 @@ namespace · 39 unmodified lines | ||
| 40 | 40 | } |
| 41 | 41 | |
| 42 | 42 | // See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. |
| 43 | −utf8::decode_state disvm::runtime::utf8::decode_step(utf8::decode_state &state, rune_t &codepoint, uint8_t byte) | |
| 43 | +utf8::decode_state_t disvm::runtime::utf8::decode_step(utf8::decode_state_t &state, rune_t &codepoint, uint8_t byte) | |
| 44 | 44 | { |
| 45 | 45 | auto type = uint32_t{ utf8d[byte] }; |
| 46 | 46 | |
| 47 | − codepoint = (state != utf8::decode_state::accept) ? | |
| 47 | + codepoint = (state != utf8::decode_state_t::accept) ? | |
| 48 | 48 | (byte & 0x3fu) | (codepoint << 6) : |
| 49 | 49 | (0xff >> type) & (byte); |
| 50 | 50 | |
| 51 | − state = static_cast<utf8::decode_state>(utf8d[256 + static_cast<uint32_t>(state) + type]); | |
| 51 | + state = static_cast<utf8::decode_state_t>(utf8d[256 + static_cast<uint32_t>(state) + type]); | |
| 52 | 52 | return state; |
| 53 | 53 | } |
| 54 | 54 | |
| @@ -59,15 +59,15 @@ std::size_t disvm::runtime::utf8::count_codepoints(const uint8_t *str, const std · 4 unmodified lines | ||
| 59 | 59 | assert(str != nullptr); |
| 60 | 60 | auto single_character = rune_t{}; |
| 61 | 61 | auto points = std::size_t{ 0 }; |
| 62 | − auto state = utf8::decode_state::accept; | |
| 62 | + auto state = utf8::decode_state_t::accept; | |
| 63 | 63 | |
| 64 | 64 | for (auto i = std::size_t{ 0 }; i < max_len; ++i) |
| 65 | 65 | { |
| 66 | − if (utf8::decode_state::accept == utf8::decode_step(state, single_character, str[i])) | |
| 66 | + if (utf8::decode_state_t::accept == utf8::decode_step(state, single_character, str[i])) | |
| 67 | 67 | points++; |
| 68 | 68 | } |
| 69 | 69 | |
| 70 | − if (state != utf8::decode_state::accept) | |
| 70 | + if (state != utf8::decode_state_t::accept) | |
| 71 | 71 | throw invalid_utf8{}; |
| 72 | 72 | |
| 73 | 73 | return points; |
| @@ -76,7 +76,7 @@ std::size_t disvm::runtime::utf8::count_codepoints(const uint8_t *str, const std · 2 unmodified lines | ||
| 76 | 76 | std::size_t disvm::runtime::utf8::decode(const uint8_t *str, rune_t &codepoint) |
| 77 | 77 | { |
| 78 | 78 | assert(str != nullptr); |
| 79 | − auto state = utf8::decode_state::accept; | |
| 79 | + auto state = utf8::decode_state_t::accept; | |
| 80 | 80 | |
| 81 | 81 | codepoint = 0; |
| 82 | 82 | auto begin = str; |
| @@ -86,7 +86,7 @@ std::size_t disvm::runtime::utf8::decode(const uint8_t *str, rune_t &codepoint) · 3 unmodified lines | ||
| 86 | 86 | { |
| 87 | 87 | curr++; |
| 88 | 88 | state = utf8::decode_step(state, codepoint, c); |
| 89 | − if (state == utf8::decode_state::accept) | |
| 89 | + if (state == utf8::decode_state_t::accept) | |
| 90 | 90 | return (curr - begin); |
| 91 | 91 | } |
| 92 | 92 | |
| @@ -19,7 +19,7 @@ · 18 unmodified lines | ||
| 19 | 19 | using namespace disvm; |
| 20 | 20 | using namespace disvm::runtime; |
| 21 | 21 | |
| 22 | −vm_t::loaded_module_t::loaded_module_t(std::unique_ptr<runtime::vm_string_t> origin, std::shared_ptr<const runtime::vm_module_t> module) | |
| 22 | +vm_t::loaded_module_t::loaded_module_t(std::unique_ptr<runtime::vm_string_t> origin, std::shared_ptr<runtime::vm_module_t> module) | |
| 23 | 23 | : origin{ std::move(origin) } |
| 24 | 24 | , module{ module } |
| 25 | 25 | { |
| @@ -126,7 +126,7 @@ const runtime::vm_thread_t& vm_t::fork( · 100 unmodified lines | ||
| 126 | 126 | if (initial_pc < 0 || static_cast<vm_pc_t>(module_ref.code_section.size()) <= initial_pc) |
| 127 | 127 | throw vm_system_exception{ "Invalid entry program counter" }; |
| 128 | 128 | |
| 129 | − auto thread = std::make_unique<vm_thread_t>(module_ref, initial_frame, initial_pc, parent.get_thread_id()); | |
| 129 | + auto thread = std::make_unique<vm_thread_t>(module_ref, parent.get_thread_id(), initial_frame, initial_pc); | |
| 130 | 130 | return _scheduler->schedule_thread(std::move(thread)); |
| 131 | 131 | } |
| 132 | 132 | |
| @@ -179,6 +179,21 @@ std::shared_ptr<const runtime::vm_module_t> vm_t::load_module(const char *path) · 46 unmodified lines | ||
| 179 | 179 | return new_module; |
| 180 | 180 | } |
| 181 | 181 | |
| 182 | +std::vector<std::shared_ptr<const runtime::vm_module_t>> vm_t::get_loaded_modules() const | |
| 183 | +{ | |
| 184 | + auto result = std::vector<std::shared_ptr<const runtime::vm_module_t>>{}; | |
| 185 | + | |
| 186 | + std::lock_guard<std::mutex> lock{ _modules_lock }; | |
| 187 | + for (auto &m : _modules) | |
| 188 | + { | |
| 189 | + auto loaded_module_maybe = m.module.lock(); | |
| 190 | + if (loaded_module_maybe != nullptr) | |
| 191 | + result.push_back(loaded_module_maybe); | |
| 192 | + } | |
| 193 | + | |
| 194 | + return result; | |
| 195 | +} | |
| 196 | + | |
| 182 | 197 | runtime::vm_scheduler_control_t &vm_t::get_scheduler_control() const |
| 183 | 198 | { |
| 184 | 199 | return _scheduler->get_controller(); |