Debug set options
Unified · Split
| @@ -4,8 +4,8 @@ · 3 unmodified lines | |||
| 4 | // Author: arr | 4 | // Author: arr |
| 5 | // | 5 | // |
| 6 | 6 | ||
| 7 | #include <vm_asm.h> | ||
| 8 | #include <array> | 7 | #include <array> |
| 8 | #include <vm_asm_sigkind.h> | ||
| 9 | #include "rfc1321/global.h" | 9 | #include "rfc1321/global.h" |
| 10 | #include "rfc1321/md5.h" | 10 | #include "rfc1321/md5.h" |
| 11 | 11 | ||
| @@ -33,6 +33,8 @@ · 32 unmodified lines | |||
| 33 | }, | 33 | }, |
| 34 | 'msvs_settings': { | 34 | 'msvs_settings': { |
| 35 | 'VCCLCompilerTool': { | 35 | 'VCCLCompilerTool': { |
| 36 | 'AdditionalOptions': '/Zc:rvalueCast', | ||
| 37 | 'DisableLanguageExtensions': 'true', | ||
| 36 | 'TreatWChar_tAsBuiltInType': 'false', | 38 | 'TreatWChar_tAsBuiltInType': 'false', |
| 37 | 'WarningLevel': 3, | 39 | 'WarningLevel': 3, |
| 38 | 'DebugInformationFormat': '3', # PDB | 40 | 'DebugInformationFormat': '3', # PDB |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | |||
| 11 | #include <string> | 11 | #include <string> |
| 12 | #include <algorithm> | 12 | #include <algorithm> |
| 13 | #include <map> | 13 | #include <map> |
| 14 | #include <unordered_set> | ||
| 14 | #include <utils.h> | 15 | #include <utils.h> |
| 15 | #include <vm_memory.h> | 16 | #include <vm_memory.h> |
| 16 | #include <vm_asm.h> | 17 | #include <vm_asm.h> |
| @@ -383,9 +384,9 @@ namespace · 366 unmodified lines | |||
| 383 | 384 | ||
| 384 | auto msg = std::stringstream{}; | 385 | auto msg = std::stringstream{}; |
| 385 | auto module_funcs = cxt.dbg.get_module_functions(vm_id, name_to_match); | 386 | auto module_funcs = cxt.dbg.get_module_functions(vm_id, name_to_match); |
| 386 | msg << "Function name [PC range]\n"; | 387 | msg << "Function name [PC range]"; |
| 387 | for (auto &f : module_funcs) | 388 | for (auto &f : module_funcs) |
| 388 | msg << " " << f << "\n"; | 389 | msg << "\n " << f; |
| 389 | 390 | ||
| 390 | debug_print_info(msg.str()); | 391 | debug_print_info(msg.str()); |
| 391 | } | 392 | } |
| @@ -615,6 +616,7 @@ namespace · 223 unmodified lines | |||
| 615 | } | 616 | } |
| 616 | } | 617 | } |
| 617 | 618 | ||
| 619 | auto &dbg = cxt.dbg; | ||
| 618 | const auto &r = *cxt.current_register; | 620 | const auto &r = *cxt.current_register; |
| 619 | const auto &code_section = r.module_ref->code_section; | 621 | const auto &code_section = r.module_ref->code_section; |
| 620 | 622 | ||
| @@ -623,19 +625,19 @@ namespace · 2 unmodified lines | |||
| 623 | if (disassemble_length < 0) | 625 | if (disassemble_length < 0) |
| 624 | { | 626 | { |
| 625 | const auto begin_maybe = r.pc - (-disassemble_length) + 1; | 627 | const auto begin_maybe = r.pc - (-disassemble_length) + 1; |
| 626 | begin_pc= static_cast<std::size_t>(std::max(begin_maybe, 0)); | 628 | begin_pc = static_cast<std::size_t>(std::max(begin_maybe, 0)); |
| 627 | } | 629 | } |
| 628 | else | 630 | else |
| 629 | { | 631 | { |
| 630 | const auto end_maybe = static_cast<std::size_t>(r.pc + disassemble_length); | 632 | const auto end_maybe = static_cast<std::size_t>(r.pc + disassemble_length); |
| 631 | end_pc = std::min(end_maybe, code_section.size()); | 633 | end_pc = std::min(end_maybe, code_section.size() - 1); |
| 632 | } | 634 | } |
| 633 | 635 | ||
| 634 | auto resolved_pc = cxt.dbg.resolve_to_function_source_line(r.module_ref->module->vm_id, begin_pc, end_pc, symbol::function_name_format_t::name); | 636 | auto resolved_pc = dbg.resolve_to_function_source_line(r.module_ref->module->vm_id, begin_pc, end_pc, symbol::function_name_format_t::name); |
| 635 | assert(resolved_pc.empty() || resolved_pc.size() == (end_pc - begin_pc + 1) && "Resolved PCs should have failed or match the PC count"); | 637 | assert(resolved_pc.empty() || resolved_pc.size() == (end_pc - begin_pc + 1) && "Resolved PCs should have failed or match the PC count"); |
| 636 | 638 | ||
| 637 | auto msg = std::stringstream{}; | 639 | auto msg = std::stringstream{}; |
| 638 | for (auto i = begin_pc; i < (end_pc + 1); ++i) | 640 | for (auto i = begin_pc; i <= end_pc; ++i) |
| 639 | { | 641 | { |
| 640 | auto op = code_section[i].op; | 642 | auto op = code_section[i].op; |
| 641 | 643 | ||
| @@ -645,9 +647,9 @@ namespace · 3 unmodified lines | |||
| 645 | if (op.opcode == opcode_t::brkpt) | 647 | if (op.opcode == opcode_t::brkpt) |
| 646 | { | 648 | { |
| 647 | // Look up each of the known cookies to try and find the matching PC and module | 649 | // Look up each of the known cookies to try and find the matching PC and module |
| 648 | for (auto bp_cookie : cxt.dbg.breakpoint_cookies) | 650 | for (auto bp_cookie : dbg.breakpoint_cookies) |
| 649 | { | 651 | { |
| 650 | const auto details = cxt.dbg.controller->get_breakpoint_details(bp_cookie); | 652 | const auto details = dbg.controller->get_breakpoint_details(bp_cookie); |
| 651 | if (details.pc == i && details.module == r.module_ref->module) | 653 | if (details.pc == i && details.module == r.module_ref->module) |
| 652 | { | 654 | { |
| 653 | op.opcode = details.original_opcode; | 655 | op.opcode = details.original_opcode; |
| @@ -660,9 +662,9 @@ namespace · 6 unmodified lines | |||
| 660 | // Print source location if resolved | 662 | // Print source location if resolved |
| 661 | if (!resolved_pc.empty()) | 663 | if (!resolved_pc.empty()) |
| 662 | { | 664 | { |
| 663 | const auto & r = resolved_pc[i - begin_pc]; | 665 | const auto &rpc = resolved_pc[i - begin_pc]; |
| 664 | if (!r.empty()) | 666 | if (!rpc.empty()) |
| 665 | msg << r << "\n"; | 667 | msg << rpc << "\n"; |
| 666 | } | 668 | } |
| 667 | 669 | ||
| 668 | msg << op_prefix << std::setw(5) << i << ": " << op << "\n"; | 670 | msg << op_prefix << std::setw(5) << i << ": " << op << "\n"; |
| @@ -811,18 +813,47 @@ namespace · 142 unmodified lines | |||
| 811 | << console_modifiers::reset_all; | 813 | << console_modifiers::reset_all; |
| 812 | } | 814 | } |
| 813 | 815 | ||
| 814 | void set_value_cmd(const std::vector<std::string> &args, dbg_cmd_cxt_t &) | 816 | std::unordered_set<std::string> dbg_options = |
| 815 | { | 817 | { |
| 816 | if (args.size() == 0) | 818 | { "bp-cmd" } |
| 819 | }; | ||
| 820 | |||
| 821 | void set_value_cmd(const std::vector<std::string> &args, dbg_cmd_cxt_t &cxt) | ||
| 822 | { | ||
| 823 | auto msg = std::stringstream{}; | ||
| 824 | if (args.size() == 1) | ||
| 817 | { | 825 | { |
| 818 | // [TODO] Print all options current state | 826 | msg << "Debug options\n"; |
| 827 | for (auto &option : dbg_options) | ||
| 828 | { | ||
| 829 | auto v = cxt.dbg.get_option(option); | ||
| 830 | msg << std::setw(16) << option | ||
| 831 | << ": " | ||
| 832 | << (v.empty() ? "<none>" : v) | ||
| 833 | << "\n"; | ||
| 834 | } | ||
| 835 | |||
| 836 | debug_print_info(msg.str()); | ||
| 819 | return; | 837 | return; |
| 820 | } | 838 | } |
| 821 | else if (0 == args[0].compare("help")) | 839 | |
| 840 | auto &curr_option = args[1]; | ||
| 841 | if (std::cend(dbg_options) == dbg_options.find(curr_option)) | ||
| 842 | throw debug_cmd_error_t{ "Invalid debug option" }; | ||
| 843 | |||
| 844 | auto new_value = std::stringstream{}; | ||
| 845 | |||
| 846 | // Reconstitute the parsed debug arguments - in-case of space breaks. | ||
| 847 | for (auto i = std::size_t{ 2 }; i < args.size(); ++i) | ||
| 822 | { | 848 | { |
| 823 | // [TODO] Print help for 'set' command | 849 | new_value << args[i]; |
| 824 | return; | 850 | |
| 851 | // For the last parsed arg | ||
| 852 | if (i + 1 < args.size()) | ||
| 853 | new_value << " "; | ||
| 825 | } | 854 | } |
| 855 | |||
| 856 | cxt.dbg.set_option(curr_option, new_value.str()); | ||
| 826 | } | 857 | } |
| 827 | 858 | ||
| 828 | const dbg_cmd_t cmds[] = | 859 | const dbg_cmd_t cmds[] = |
| @@ -844,7 +875,7 @@ namespace · 15 unmodified lines | |||
| 844 | { "d", "Disassemble the next/previous N instructions (default N = 4)", "d (-?[0-9]+)?", "d, d -4, d 5", cmd_disassemble }, | 875 | { "d", "Disassemble the next/previous N instructions (default N = 4)", "d (-?[0-9]+)?", "d, d -4, d 5", cmd_disassemble }, |
| 845 | { "x", "Examine memory", "x [mp|fp] [0-9]+ ([0-9]+)?", "x mp 3, x fp 20 6", cmd_examine }, | 876 | { "x", "Examine memory", "x [mp|fp] [0-9]+ ([0-9]+)?", "x mp 3, x fp 20 6", cmd_examine }, |
| 846 | 877 | ||
| 847 | //{ "set" , "Set a debugger option", "set [ _a-zA-Z0-9]*", "'set help' for available options", set_value_cmd }, | 878 | { "set" , "Set a debug option", "set [ _a-zA-Z0-9]*", "'set' for available options", set_value_cmd }, |
| 848 | { "?", "Print help", nullptr, nullptr, cmd_help }, | 879 | { "?", "Print help", nullptr, nullptr, cmd_help }, |
| 849 | 880 | ||
| 850 | // Undocumented commands | 881 | // Undocumented commands |
| @@ -865,8 +896,8 @@ namespace · 14 unmodified lines | |||
| 865 | 896 | ||
| 866 | void print_help() | 897 | void print_help() |
| 867 | { | 898 | { |
| 868 | auto ss = std::stringstream{}; | 899 | auto msg = std::stringstream{}; |
| 869 | ss << "Commands:\n"; | 900 | msg << "Commands:\n"; |
| 870 | for (auto i = std::size_t{}; i < sizeof(cmds) / sizeof(cmds[0]); ++i) | 901 | for (auto i = std::size_t{}; i < sizeof(cmds) / sizeof(cmds[0]); ++i) |
| 871 | { | 902 | { |
| 872 | auto &c = cmds[i]; | 903 | auto &c = cmds[i]; |
| @@ -875,16 +906,33 @@ namespace · 2 unmodified lines | |||
| 875 | if (c.description == nullptr) | 906 | if (c.description == nullptr) |
| 876 | continue; | 907 | continue; |
| 877 | 908 | ||
| 878 | ss << " " << c.cmd << "\t" << c.description << "\n"; | 909 | msg << " " << c.cmd << "\t" << c.description << "\n"; |
| 879 | 910 | ||
| 880 | if (c.syntax != nullptr) | 911 | if (c.syntax != nullptr) |
| 881 | ss << "\t Syntax: " << c.syntax << "\n"; | 912 | msg << "\t Syntax: " << c.syntax << "\n"; |
| 882 | 913 | ||
| 883 | if (c.example != nullptr) | 914 | if (c.example != nullptr) |
| 884 | ss << "\t Example(s): " << c.example << "\n"; | 915 | msg << "\t Example(s): " << c.example << "\n"; |
| 885 | } | 916 | } |
| 886 | 917 | ||
| 887 | debug_print_info(ss.str()); | 918 | debug_print_info(msg.str()); |
| 919 | } | ||
| 920 | |||
| 921 | void execute_single_command(dbg_cmd_cxt_t &cxt, const std::string &cmd) | ||
| 922 | { | ||
| 923 | const auto cmd_tokens = util::split(cmd); | ||
| 924 | if (cmd_tokens.empty()) | ||
| 925 | return; | ||
| 926 | |||
| 927 | try | ||
| 928 | { | ||
| 929 | auto cmd_exec = find_cmd(cmd_tokens[0]); | ||
| 930 | cmd_exec(cmd_tokens, cxt); | ||
| 931 | } | ||
| 932 | catch (const debug_cmd_error_t &e) | ||
| 933 | { | ||
| 934 | debug_print_error(e.what()); | ||
| 935 | } | ||
| 888 | } | 936 | } |
| 889 | 937 | ||
| 890 | void prompt(debugger &debugger, const vm_registers_t &r) | 938 | void prompt(debugger &debugger, const vm_registers_t &r) |
| @@ -910,21 +958,11 @@ namespace · 19 unmodified lines | |||
| 910 | << console_modifiers::reset_all; | 958 | << console_modifiers::reset_all; |
| 911 | 959 | ||
| 912 | std::getline(std::cin, cmd); | 960 | std::getline(std::cin, cmd); |
| 913 | const auto cmd_tokens = util::split(cmd); | ||
| 914 | if (cmd_tokens.empty()) | ||
| 915 | continue; | ||
| 916 | 961 | ||
| 917 | try | 962 | assert(!cxt.exit_break && "Pre-condition on exit_break state before execute failed"); |
| 918 | { | 963 | execute_single_command(cxt, cmd); |
| 919 | auto cmd_exec = find_cmd(cmd_tokens[0]); | 964 | if (cxt.exit_break) |
| 920 | cmd_exec(cmd_tokens, cxt); | 965 | break; |
| 921 | if (cxt.exit_break) | ||
| 922 | break; | ||
| 923 | } | ||
| 924 | catch (const debug_cmd_error_t &e) | ||
| 925 | { | ||
| 926 | debug_print_error(e.what()); | ||
| 927 | } | ||
| 928 | } | 966 | } |
| 929 | 967 | ||
| 930 | debugger.controller->resume_all_threads(); | 968 | debugger.controller->resume_all_threads(); |
| @@ -1019,6 +1057,20 @@ bool debugger::symbols_exist(disvm::runtime::module_id_t vm_id) const · 88 unmodified lines | |||
| 1019 | return _vm_id_to_symbol_info.find(vm_id) != std::cend(_vm_id_to_symbol_info); | 1057 | return _vm_id_to_symbol_info.find(vm_id) != std::cend(_vm_id_to_symbol_info); |
| 1020 | } | 1058 | } |
| 1021 | 1059 | ||
| 1060 | std::string debugger::get_option(const std::string &option) const | ||
| 1061 | { | ||
| 1062 | auto iter = _debugger_options.find(option); | ||
| 1063 | if (iter == std::cend(_debugger_options)) | ||
| 1064 | return{}; | ||
| 1065 | |||
| 1066 | return iter->second; | ||
| 1067 | } | ||
| 1068 | |||
| 1069 | void debugger::set_option(const std::string &option, std::string value) | ||
| 1070 | { | ||
| 1071 | _debugger_options[option] = std::move(value); | ||
| 1072 | } | ||
| 1073 | |||
| 1022 | void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id) | 1074 | void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id) |
| 1023 | { | 1075 | { |
| 1024 | _tool_id = tool_id; | 1076 | _tool_id = tool_id; |
| @@ -1115,8 +1167,12 @@ void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id) · 90 unmodified lines | |||
| 1115 | 1167 | ||
| 1116 | debug_print_info(ss.str()); | 1168 | debug_print_info(ss.str()); |
| 1117 | 1169 | ||
| 1118 | auto reg_str = registers_to_string(*cxt.value1.registers); | 1170 | const auto bp_cmd = get_option({ "bp-cmd" }); |
| 1119 | debug_print_info(reg_str); | 1171 | if (!bp_cmd.empty()) |
| 1172 | { | ||
| 1173 | dbg_cmd_cxt_t dbg_cxt{ *this, *cxt.value1.registers }; | ||
| 1174 | execute_single_command(dbg_cxt, bp_cmd); | ||
| 1175 | } | ||
| 1120 | 1176 | ||
| 1121 | prompt(*this, *cxt.value1.registers); | 1177 | prompt(*this, *cxt.value1.registers); |
| 1122 | })); | 1178 | })); |
| @@ -1163,7 +1219,7 @@ void debugger::load_symbols(const disvm::loaded_vm_module_t &loaded_mod) · 40 unmodified lines | |||
| 1163 | catch (const std::runtime_error &e) | 1219 | catch (const std::runtime_error &e) |
| 1164 | { | 1220 | { |
| 1165 | // File with assumed symbol name/extension is not the correct format | 1221 | // File with assumed symbol name/extension is not the correct format |
| 1166 | std::stringstream msg; | 1222 | auto msg = std::stringstream{}; |
| 1167 | msg << "Assumed symbol file '" | 1223 | msg << "Assumed symbol file '" |
| 1168 | << symbol_path_maybe | 1224 | << symbol_path_maybe |
| 1169 | << "' has invalid symbol format. (" | 1225 | << "' has invalid symbol format. (" |
| @@ -1178,7 +1234,7 @@ void debugger::load_symbols(const disvm::loaded_vm_module_t &loaded_mod) · 8 unmodified lines | |||
| 1178 | || symbol_data->get_instruction_count() != m->code_section.size()) | 1234 | || symbol_data->get_instruction_count() != m->code_section.size()) |
| 1179 | { | 1235 | { |
| 1180 | // Symbol file does not match the loaded modules - stale file? | 1236 | // Symbol file does not match the loaded modules - stale file? |
| 1181 | std::stringstream msg; | 1237 | auto msg = std::stringstream{}; |
| 1182 | msg << "Symbol file '" | 1238 | msg << "Symbol file '" |
| 1183 | << symbol_path_maybe | 1239 | << symbol_path_maybe |
| 1184 | << "' does not match loaded module '" | 1240 | << "' does not match loaded module '" |
| @@ -1193,7 +1249,7 @@ void debugger::load_symbols(const disvm::loaded_vm_module_t &loaded_mod) · 8 unmodified lines | |||
| 1193 | auto symbol_iter = symbol_data->get_pc_iter(); | 1249 | auto symbol_iter = symbol_data->get_pc_iter(); |
| 1194 | _vm_id_to_symbol_info[vm_id] = { std::move(symbol_data), std::move(symbol_iter) }; | 1250 | _vm_id_to_symbol_info[vm_id] = { std::move(symbol_data), std::move(symbol_iter) }; |
| 1195 | 1251 | ||
| 1196 | std::stringstream msg; | 1252 | auto msg = std::stringstream{}; |
| 1197 | msg << "Symbol file '" | 1253 | msg << "Symbol file '" |
| 1198 | << symbol_path_maybe | 1254 | << symbol_path_maybe |
| 1199 | << "' loaded for '" | 1255 | << "' loaded for '" |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | |||
| 13 | #include <string> | 13 | #include <string> |
| 14 | #include <memory> | 14 | #include <memory> |
| 15 | #include <disvm.h> | 15 | #include <disvm.h> |
| 16 | #include <vm_asm.h> | ||
| 16 | #include <vm_tools.h> | 17 | #include <vm_tools.h> |
| 17 | #include <utils.h> | 18 | #include <utils.h> |
| 18 | 19 | ||
| @@ -48,6 +49,10 @@ public: · 29 unmodified lines | |||
| 48 | // Check if the supplied module ID has symbols loaded | 49 | // Check if the supplied module ID has symbols loaded |
| 49 | bool symbols_exist(disvm::runtime::module_id_t vm_id) const; | 50 | bool symbols_exist(disvm::runtime::module_id_t vm_id) const; |
| 50 | 51 | ||
| 52 | // Get/set debugger option | ||
| 53 | std::string get_option(const std::string &option) const; | ||
| 54 | void set_option(const std::string &option, std::string value); | ||
| 55 | |||
| 51 | public: // vm_tool_t | 56 | public: // vm_tool_t |
| 52 | void on_load(disvm::runtime::vm_tool_controller_t &, std::size_t tool_id) override; | 57 | void on_load(disvm::runtime::vm_tool_controller_t &, std::size_t tool_id) override; |
| 53 | 58 | ||
| @@ -71,6 +76,7 @@ private: · 17 unmodified lines | |||
| 71 | }; | 76 | }; |
| 72 | 77 | ||
| 73 | std::unordered_map<std::size_t, symbol_info_t> _vm_id_to_symbol_info; | 78 | std::unordered_map<std::size_t, symbol_info_t> _vm_id_to_symbol_info; |
| 79 | std::unordered_map<std::string, std::string> _debugger_options; | ||
| 74 | }; | 80 | }; |
| 75 | 81 | ||
| 76 | #endif // _DISVM_SRC_EXEC_EXEC_H_ | 82 | #endif // _DISVM_SRC_EXEC_EXEC_H_ |
| @@ -11,7 +11,7 @@ · 10 unmodified lines | |||
| 11 | #include <builtin_module.h> | 11 | #include <builtin_module.h> |
| 12 | #include <debug.h> | 12 | #include <debug.h> |
| 13 | #include <exceptions.h> | 13 | #include <exceptions.h> |
| 14 | #include <vm_asm.h> | 14 | #include <vm_asm_sigkind.h> |
| 15 | #include <vm_version.h> | 15 | #include <vm_version.h> |
| 16 | #include "exec.h" | 16 | #include "exec.h" |
| 17 | 17 | ||
| @@ -21,6 +21,12 @@ using namespace disvm::assembly::sigkind; · 3 unmodified lines | |||
| 21 | 21 | ||
| 22 | namespace | 22 | namespace |
| 23 | { | 23 | { |
| 24 | template<int L> | ||
| 25 | std::unique_ptr<vm_string_t> create_string_from_const(const char(&ca)[L]) | ||
| 26 | { | ||
| 27 | return std::make_unique<vm_string_t>(L - 1, reinterpret_cast<const uint8_t*>(ca)); | ||
| 28 | } | ||
| 29 | |||
| 24 | // Entry frame layout | 30 | // Entry frame layout |
| 25 | struct vm_entry_frame_t final : public vm_frame_base_alloc_t | 31 | struct vm_entry_frame_t final : public vm_frame_base_alloc_t |
| 26 | { | 32 | { |
| @@ -35,24 +41,17 @@ namespace · 8 unmodified lines | |||
| 35 | 41 | ||
| 36 | std::unique_ptr<vm_module_t> create_entry_module(vm_t &vm, const std::vector<char *> &vm_args) | 42 | std::unique_ptr<vm_module_t> create_entry_module(vm_t &vm, const std::vector<char *> &vm_args) |
| 37 | { | 43 | { |
| 38 | if (vm_args.size() == 0) | ||
| 39 | throw vm_user_exception{ "Entry module not supplied" }; | ||
| 40 | |||
| 41 | // Convert arguments to a list | 44 | // Convert arguments to a list |
| 42 | vm_string_t *command_module_path = nullptr; | 45 | vm_string_t *command_module_path = nullptr; |
| 43 | vm_list_t *args = nullptr; | 46 | vm_list_t *mod_args = new vm_list_t{ intrinsic_type_desc::type<vm_string_t>() }; |
| 44 | vm_list_t *last = nullptr; | 47 | vm_list_t *last = mod_args; |
| 45 | for (auto i = std::size_t{ 0 }; i < vm_args.size(); ++i) | 48 | for (auto raw_arg : vm_args) |
| 46 | { | 49 | { |
| 47 | auto tmp = vm_args[i]; | 50 | auto curr_arg = new vm_string_t{ std::strlen(raw_arg), reinterpret_cast<uint8_t *>(raw_arg) }; |
| 48 | auto arg = new vm_string_t{ std::strlen(tmp), reinterpret_cast<uint8_t *>(tmp) }; | 51 | if (command_module_path == nullptr) |
| 49 | if (args == nullptr) | ||
| 50 | { | 52 | { |
| 51 | // First argument is the module to load | 53 | // First argument is the module to load |
| 52 | command_module_path = arg; | 54 | command_module_path = curr_arg; |
| 53 | assert(last == nullptr); | ||
| 54 | args = new vm_list_t{ intrinsic_type_desc::type<vm_string_t>() }; | ||
| 55 | last = args; | ||
| 56 | } | 55 | } |
| 57 | else | 56 | else |
| 58 | { | 57 | { |
| @@ -63,10 +62,11 @@ namespace · 4 unmodified lines | |||
| 63 | last = new_tail; | 62 | last = new_tail; |
| 64 | } | 63 | } |
| 65 | 64 | ||
| 66 | pt_ref(last->value()) = arg->get_allocation(); | 65 | pt_ref(last->value()) = curr_arg->get_allocation(); |
| 67 | } | 66 | } |
| 68 | 67 | ||
| 69 | assert(args != nullptr); | 68 | if (command_module_path == nullptr) |
| 69 | throw vm_user_exception{ "Entry module not supplied" }; | ||
| 70 | 70 | ||
| 71 | // Pre-load the command module in the VM. | 71 | // Pre-load the command module in the VM. |
| 72 | command_module = vm.load_module(command_module_path->str()); | 72 | command_module = vm.load_module(command_module_path->str()); |
| @@ -79,9 +79,7 @@ namespace · 6 unmodified lines | |||
| 79 | e.header.entry_pc = 0; | 79 | e.header.entry_pc = 0; |
| 80 | e.header.entry_type = 0; | 80 | e.header.entry_type = 0; |
| 81 | e.header.runtime_flag = (runtime_flags_t::has_import); | 81 | e.header.runtime_flag = (runtime_flags_t::has_import); |
| 82 | 82 | e.module_name = create_string_from_const("_entry_"); | |
| 83 | std::array<uint8_t, 8> mod_name = { "_entry_" }; | ||
| 84 | e.module_name = std::make_unique<vm_string_t>(mod_name.size() - 1, mod_name.data()); | ||
| 85 | 83 | ||
| 86 | // Inherit the stack extent from the command module and add the entry frame size. | 84 | // Inherit the stack extent from the command module and add the entry frame size. |
| 87 | e.header.stack_extent = command_module->header.stack_extent + sizeof(vm_entry_frame_t); | 85 | e.header.stack_extent = command_module->header.stack_extent + sizeof(vm_entry_frame_t); |
| @@ -99,7 +97,7 @@ namespace · 11 unmodified lines | |||
| 99 | // Take a reference on the command module path and store it on the entry frame | 97 | // Take a reference on the command module path and store it on the entry frame |
| 100 | command_module_path->add_ref(); | 98 | command_module_path->add_ref(); |
| 101 | mp_base[0] = command_module_path->get_allocation(); | 99 | mp_base[0] = command_module_path->get_allocation(); |
| 102 | mp_base[1] = args->get_allocation(); | 100 | mp_base[1] = mod_args->get_allocation(); |
| 103 | 101 | ||
| 104 | // Define the frame offset for the argument | 102 | // Define the frame offset for the argument |
| 105 | const auto first_arg_offset = vm_frame_constants::limbo_first_arg_register_offset(); | 103 | const auto first_arg_offset = vm_frame_constants::limbo_first_arg_register_offset(); |
| @@ -168,9 +166,7 @@ namespace · 62 unmodified lines | |||
| 168 | assert(exec_init_sig == sig_stream_t::compute_signature_hash("f(Ls)n")); | 166 | assert(exec_init_sig == sig_stream_t::compute_signature_hash("f(Ls)n")); |
| 169 | #endif | 167 | #endif |
| 170 | func.sig = exec_init_sig; | 168 | func.sig = exec_init_sig; |
| 171 | 169 | func.name = create_string_from_const("init"); | |
| 172 | std::array<uint8_t, 5> func_name = { "init" }; | ||
| 173 | func.name = std::make_unique<vm_string_t>(func_name.size() - 1, func_name.data()); | ||
| 174 | 170 | ||
| 175 | import_vm_module_t import{}; | 171 | import_vm_module_t import{}; |
| 176 | import.functions.push_back(std::move(func)); | 172 | import.functions.push_back(std::move(func)); |
| @@ -156,4 +156,4 @@ namespace disvm · 155 unmodified lines | |||
| 156 | } | 156 | } |
| 157 | } | 157 | } |
| 158 | 158 | ||
| 159 | #endif // _DISVM_SRC_INCLUDE_BUFFERED_READER_H_ | 159 | #endif // _DISVM_SRC_INCLUDE_BUFFERED_READER_H_ |
| @@ -118,4 +118,4 @@ namespace disvm · 117 unmodified lines | |||
| 118 | } | 118 | } |
| 119 | } | 119 | } |
| 120 | 120 | ||
| 121 | #endif // _DISVM_SRC_INCLUDE_BUILTIN_MODULE_H_ | 121 | #endif // _DISVM_SRC_INCLUDE_BUILTIN_MODULE_H_ |
| @@ -97,4 +97,4 @@ namespace disvm · 96 unmodified lines | |||
| 97 | } | 97 | } |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | #endif // _DISVM_SRC_INCLUDE_DEBUG_H_ | 100 | #endif // _DISVM_SRC_INCLUDE_DEBUG_H_ |
| @@ -125,4 +125,4 @@ namespace disvm · 124 unmodified lines | |||
| 125 | }; | 125 | }; |
| 126 | } | 126 | } |
| 127 | 127 | ||
| 128 | #endif // _DISVM_SRC_INCLUDE_DISVM_H_ | 128 | #endif // _DISVM_SRC_INCLUDE_DISVM_H_ |
| @@ -143,4 +143,4 @@ namespace disvm · 142 unmodified lines | |||
| 143 | } | 143 | } |
| 144 | } | 144 | } |
| 145 | 145 | ||
| 146 | #endif // _DISVM_SRC_INCLUDE_EXCEPTIONS_H_ | 146 | #endif // _DISVM_SRC_INCLUDE_EXCEPTIONS_H_ |
| @@ -26,4 +26,4 @@ namespace disvm · 25 unmodified lines | |||
| 26 | } | 26 | } |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | #endif // _DISVM_SRC_INCLUDE_MODULE_READER_H_ | 29 | #endif // _DISVM_SRC_INCLUDE_MODULE_READER_H_ |
| @@ -199,4 +199,4 @@ namespace disvm · 198 unmodified lines | |||
| 199 | }; | 199 | }; |
| 200 | } | 200 | } |
| 201 | 201 | ||
| 202 | #endif // _DISVM_SRC_INCLUDE_OPCODES_H_ | 202 | #endif // _DISVM_SRC_INCLUDE_OPCODES_H_ |
| @@ -54,4 +54,4 @@ namespace disvm · 53 unmodified lines | |||
| 54 | } | 54 | } |
| 55 | } | 55 | } |
| 56 | 56 | ||
| 57 | #endif // _DISVM_SRC_INCLUDE_UTF8_H_ | 57 | #endif // _DISVM_SRC_INCLUDE_UTF8_H_ |
| @@ -78,4 +78,4 @@ inline TYPE operator ^ (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<s · 77 unmodified lines | |||
| 78 | inline TYPE &operator ^= (TYPE &a, TYPE b) { a = a ^ b; return a; } \ | 78 | inline TYPE &operator ^= (TYPE &a, TYPE b) { a = a ^ b; return a; } \ |
| 79 | inline TYPE operator ~ (TYPE a) { return static_cast<TYPE>(~static_cast<std::underlying_type_t<TYPE>>(a)); } | 79 | inline TYPE operator ~ (TYPE a) { return static_cast<TYPE>(~static_cast<std::underlying_type_t<TYPE>>(a)); } |
| 80 | 80 | ||
| 81 | #endif // _DISVM_SRC_INCLUDE_UTILS_H_ | 81 | #endif // _DISVM_SRC_INCLUDE_UTILS_H_ |
| @@ -8,8 +8,6 @@ · 7 unmodified lines | |||
| 8 | #define _DISVM_SRC_INCLUDE_VM_ASM_H_ | 8 | #define _DISVM_SRC_INCLUDE_VM_ASM_H_ |
| 9 | 9 | ||
| 10 | #include <iosfwd> | 10 | #include <iosfwd> |
| 11 | #include <sstream> | ||
| 12 | #include <cassert> | ||
| 13 | #include <cstdint> | 11 | #include <cstdint> |
| 14 | #include "opcodes.h" | 12 | #include "opcodes.h" |
| 15 | #include "runtime.h" | 13 | #include "runtime.h" |
| @@ -34,349 +32,6 @@ namespace disvm · 18 unmodified lines | |||
| 34 | // Get the opcode from this string. | 32 | // Get the opcode from this string. |
| 35 | // If the supplied string cannot be resolved or if it is null, the 'invalid' opcode is returned. | 33 | // If the supplied string cannot be resolved or if it is null, the 'invalid' opcode is returned. |
| 36 | disvm::opcode_t token_to_opcode(const char *); | 34 | disvm::opcode_t token_to_opcode(const char *); |
| 37 | |||
| 38 | // Namespace named after enum type in Limbo compiler | ||
| 39 | // Source: limbo/types.c | ||
| 40 | namespace sigkind | ||
| 41 | { | ||
| 42 | // Signature stream | ||
| 43 | class sig_stream_t final | ||
| 44 | { | ||
| 45 | public: // static | ||
| 46 | static uint32_t compute_signature_hash(const char *); | ||
| 47 | |||
| 48 | // Type delimiter | ||
| 49 | static const char delim; | ||
| 50 | |||
| 51 | public: | ||
| 52 | std::string get_signature() const; | ||
| 53 | |||
| 54 | uint32_t get_signature_hash() const; | ||
| 55 | |||
| 56 | sig_stream_t &operator<<(const char); | ||
| 57 | sig_stream_t &operator<<(const char *); | ||
| 58 | sig_stream_t &operator<<(const uint32_t); | ||
| 59 | sig_stream_t &operator<<(const sig_stream_t &); | ||
| 60 | |||
| 61 | private: | ||
| 62 | mutable std::stringstream _ss; | ||
| 63 | }; | ||
| 64 | |||
| 65 | // Type context modifiers | ||
| 66 | enum class modifier_id_t : uint8_t | ||
| 67 | { | ||
| 68 | self = 'S', | ||
| 69 | cycle = 'y', | ||
| 70 | }; | ||
| 71 | |||
| 72 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::modifier_id_t &); | ||
| 73 | |||
| 74 | // Instrinsic VM types | ||
| 75 | enum class type_id_t : uint8_t | ||
| 76 | { | ||
| 77 | unknown = 0, // This will be interpreted as a 'null' value which will help debug where the type building failed. | ||
| 78 | none = 'n', | ||
| 79 | byte = 'b', | ||
| 80 | integer = 'i', | ||
| 81 | real = 'r', | ||
| 82 | big = 'B', | ||
| 83 | string = 's', | ||
| 84 | poly = 'P', | ||
| 85 | fixed = 'x', | ||
| 86 | array = 'A', | ||
| 87 | ref = 'R', | ||
| 88 | list = 'L', | ||
| 89 | channel = 'C', | ||
| 90 | adt = 'a', | ||
| 91 | adt_pick_tag = 'p', | ||
| 92 | tuple = 't', | ||
| 93 | module = 'm', | ||
| 94 | function = 'f', | ||
| 95 | function_varargs = 0xff, // "f*" | ||
| 96 | }; | ||
| 97 | |||
| 98 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::type_id_t &); | ||
| 99 | |||
| 100 | template<type_id_t T> | ||
| 101 | struct Tbasic_type | ||
| 102 | { | ||
| 103 | static const type_id_t id; | ||
| 104 | |||
| 105 | constexpr operator type_id_t() const | ||
| 106 | { | ||
| 107 | return Tbasic_type::id; | ||
| 108 | } | ||
| 109 | }; | ||
| 110 | |||
| 111 | template<type_id_t T> | ||
| 112 | const type_id_t Tbasic_type<T>::id = T; | ||
| 113 | |||
| 114 | using Tnone = Tbasic_type<type_id_t::none>; | ||
| 115 | using Tbyte = Tbasic_type<type_id_t::byte>; | ||
| 116 | using Tinteger = Tbasic_type<type_id_t::integer>; | ||
| 117 | using Treal = Tbasic_type<type_id_t::real>; | ||
| 118 | using Tbig = Tbasic_type<type_id_t::big>; | ||
| 119 | using Tstring = Tbasic_type<type_id_t::string>; | ||
| 120 | using Tpoly = Tbasic_type<type_id_t::poly>; | ||
| 121 | |||
| 122 | class Tfixed final : public Tbasic_type<type_id_t::fixed> | ||
| 123 | { | ||
| 124 | public: | ||
| 125 | Tfixed(double scale); | ||
| 126 | |||
| 127 | friend sig_stream_t &operator<<(sig_stream_t &os, const Tfixed &); | ||
| 128 | |||
| 129 | private: | ||
| 130 | const double _scale; | ||
| 131 | }; | ||
| 132 | |||
| 133 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tfixed &); | ||
| 134 | |||
| 135 | // Type that contains/modifies another type | ||
| 136 | template <type_id_t ID> | ||
| 137 | class Tbasic_aggregate_type : public Tbasic_type<ID> | ||
| 138 | { | ||
| 139 | public: | ||
| 140 | template<typename U> | ||
| 141 | static Tbasic_aggregate_type create(const U &type) | ||
| 142 | { | ||
| 143 | sig_stream_t ss; | ||
| 144 | ss << Tbasic_type<ID>::id << type; | ||
| 145 | |||
| 146 | return Tbasic_aggregate_type{ ss }; | ||
| 147 | } | ||
| 148 | |||
| 149 | friend sig_stream_t &operator<<(sig_stream_t &, const Tbasic_aggregate_type &); | ||
| 150 | |||
| 151 | private: | ||
| 152 | sig_stream_t _s; | ||
| 153 | Tbasic_aggregate_type(sig_stream_t &s) : _s{ std::move(s) } { } | ||
| 154 | }; | ||
| 155 | |||
| 156 | using Tarray = Tbasic_aggregate_type<type_id_t::array>; | ||
| 157 | using Tref = Tbasic_aggregate_type<type_id_t::ref>; | ||
| 158 | using Tlist = Tbasic_aggregate_type<type_id_t::list>; | ||
| 159 | using Tchannel = Tbasic_aggregate_type<type_id_t::channel>; | ||
| 160 | |||
| 161 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tarray &); | ||
| 162 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tref &); | ||
| 163 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tlist &); | ||
| 164 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tchannel &); | ||
| 165 | |||
| 166 | // Reference to another previously defined type in the current signature | ||
| 167 | class Ttype_ref final | ||
| 168 | { | ||
| 169 | public: // static | ||
| 170 | static const char id; | ||
| 171 | |||
| 172 | public: | ||
| 173 | Ttype_ref(uint32_t num); | ||
| 174 | |||
| 175 | friend sig_stream_t &operator<<(sig_stream_t &os, const Ttype_ref &); | ||
| 176 | |||
| 177 | private: | ||
| 178 | const uint32_t _num; | ||
| 179 | }; | ||
| 180 | |||
| 181 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Ttype_ref &); | ||
| 182 | |||
| 183 | // Named member type | ||
| 184 | class Tm final | ||
| 185 | { | ||
| 186 | public: | ||
| 187 | template <typename T> | ||
| 188 | Tm(const char *name, const T &type) | ||
| 189 | { | ||
| 190 | _s << name << ':' << type; | ||
| 191 | } | ||
| 192 | |||
| 193 | friend sig_stream_t &operator<<(sig_stream_t &os, const Tm &); | ||
| 194 | |||
| 195 | private: | ||
| 196 | sig_stream_t _s; | ||
| 197 | }; | ||
| 198 | |||
| 199 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tm &); | ||
| 200 | |||
| 201 | inline void nested_type(const sig_stream_t &) | ||
| 202 | { | ||
| 203 | return; | ||
| 204 | } | ||
| 205 | |||
| 206 | template<typename T, typename ...S> | ||
| 207 | void nested_type(sig_stream_t &ss, const T &t, const S&...s) | ||
| 208 | { | ||
| 209 | ss << t; | ||
| 210 | nested_type(ss, s...); | ||
| 211 | } | ||
| 212 | |||
| 213 | template<typename T1, typename T2, typename ...S> | ||
| 214 | void nested_type(sig_stream_t &ss, const T1 &t1, const T2 &t2, const S&...s) | ||
| 215 | { | ||
| 216 | ss << t1 << sig_stream_t::delim; | ||
| 217 | nested_type(ss, t2, s...); | ||
| 218 | } | ||
| 219 | |||
| 220 | // Type whose definition depends on contained types | ||
| 221 | template <type_id_t ID> | ||
| 222 | class Tnested_aggregate_type : public Tbasic_type<ID> | ||
| 223 | { | ||
| 224 | public: // static | ||
| 225 | static const char begin; | ||
| 226 | static const char end; | ||
| 227 | |||
| 228 | template<typename ...NT> | ||
| 229 | static Tnested_aggregate_type create(const NT&... nestedTypes) | ||
| 230 | { | ||
| 231 | sig_stream_t ss; | ||
| 232 | ss << Tbasic_type<ID>::id << Tnested_aggregate_type::begin; | ||
| 233 | |||
| 234 | nested_type(ss, nestedTypes...); | ||
| 235 | |||
| 236 | ss << Tnested_aggregate_type::end; | ||
| 237 | |||
| 238 | return Tnested_aggregate_type{ ss }; | ||
| 239 | } | ||
| 240 | |||
| 241 | public: | ||
| 242 | Tnested_aggregate_type(sig_stream_t &s) | ||
| 243 | : _s{ std::move(s) } | ||
| 244 | { } | ||
| 245 | |||
| 246 | friend sig_stream_t &operator<<(sig_stream_t &, const Tnested_aggregate_type &); | ||
| 247 | |||
| 248 | private: | ||
| 249 | sig_stream_t _s; | ||
| 250 | }; | ||
| 251 | |||
| 252 | using Ttuple = Tnested_aggregate_type<type_id_t::tuple>; | ||
| 253 | using Tmodule = Tnested_aggregate_type<type_id_t::module>; | ||
| 254 | |||
| 255 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Ttuple &); | ||
| 256 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tmodule &); | ||
| 257 | |||
| 258 | // Abstract data type (ADT) pick tag | ||
| 259 | // Note: This type should only be a part of an ADT instance. | ||
| 260 | class Tadt_pick_tag final : public Tbasic_type<type_id_t::adt_pick_tag> | ||
| 261 | { | ||
| 262 | public: // static | ||
| 263 | static const char *tag_delim; | ||
| 264 | static const char begin; | ||
| 265 | static const char end; | ||
| 266 | |||
| 267 | static Tadt_pick_tag create(const char *pick_tag, uint32_t adt_ref, std::initializer_list<const Tm> tms); | ||
| 268 | |||
| 269 | friend sig_stream_t &operator<<(sig_stream_t &, const Tadt_pick_tag &); | ||
| 270 | |||
| 271 | private: | ||
| 272 | sig_stream_t _s; | ||
| 273 | Tadt_pick_tag(sig_stream_t &s) : _s{ std::move(s) } { } | ||
| 274 | }; | ||
| 275 | |||
| 276 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tadt_pick_tag &); | ||
| 277 | |||
| 278 | // Abstract data type (ADT) | ||
| 279 | class Tadt final : public Tnested_aggregate_type<type_id_t::adt> | ||
| 280 | { | ||
| 281 | public: // static | ||
| 282 | // Unhide base class version of function | ||
| 283 | using Tnested_aggregate_type::create; | ||
| 284 | |||
| 285 | // Create an ADT with a single pick tag | ||
| 286 | static Tadt create(const Tadt_pick_tag &p) | ||
| 287 | { | ||
| 288 | sig_stream_t ss; | ||
| 289 | ss << Tbasic_type::id | ||
| 290 | << Tadt::begin | ||
| 291 | << sig_stream_t::delim | ||
| 292 | << p | ||
| 293 | << Tadt::end; | ||
| 294 | |||
| 295 | return Tadt{ ss }; | ||
| 296 | } | ||
| 297 | |||
| 298 | template<typename T, typename ...NT> | ||
| 299 | static Tadt create(const Tadt_pick_tag &p, const T &t, const NT&... nestedTypes) | ||
| 300 | { | ||
| 301 | // [SPEC] When generating type signatures, the Limbo compiler | ||
| 302 | // doesn't identify the case where a pick tag is the first type | ||
| 303 | // so it always prefixes the nested signature with a leading ','. | ||
| 304 | // We special case this scenario to maintain compatibility. The | ||
| 305 | // single pick tag for an ADT must also be handled (above). | ||
| 306 | sig_stream_t ss; | ||
| 307 | ss << Tbasic_type::id | ||
| 308 | << Tadt::begin | ||
| 309 | << sig_stream_t::delim | ||
| 310 | << p | ||
| 311 | << sig_stream_t::delim; | ||
| 312 | |||
| 313 | nested_type(ss, t, nestedTypes...); | ||
| 314 | |||
| 315 | ss << Tadt::end; | ||
| 316 | |||
| 317 | return Tadt{ ss }; | ||
| 318 | } | ||
| 319 | |||
| 320 | private: | ||
| 321 | Tadt(sig_stream_t &s) : Tnested_aggregate_type{ s } { } | ||
| 322 | }; | ||
| 323 | |||
| 324 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tnested_aggregate_type<type_id_t::adt> &); | ||
| 325 | |||
| 326 | // Function type | ||
| 327 | template<type_id_t T> | ||
| 328 | class Tfunction_base_type final : public Tbasic_type<T> | ||
| 329 | { | ||
| 330 | public: // static | ||
| 331 | static const char begin; | ||
| 332 | static const char end; | ||
| 333 | |||
| 334 | template<typename ...AT> | ||
| 335 | static Tfunction_base_type create(AT... argTypes) | ||
| 336 | { | ||
| 337 | Tfunction_base_type a; | ||
| 338 | a._s << Tfunction_base_type::id << Tfunction_base_type::begin; | ||
| 339 | |||
| 340 | nested_type(a._s, argTypes...); | ||
| 341 | |||
| 342 | a._s << Tfunction_base_type::end; | ||
| 343 | return a; | ||
| 344 | } | ||
| 345 | |||
| 346 | public: | ||
| 347 | template<typename RT> | ||
| 348 | Tfunction_base_type &returns(RT r) | ||
| 349 | { | ||
| 350 | assert(!_fully_defined && "Function type already complete"); | ||
| 351 | _s << r; | ||
| 352 | _fully_defined = true; | ||
| 353 | |||
| 354 | return *this; | ||
| 355 | } | ||
| 356 | |||
| 357 | friend sig_stream_t &operator<<(sig_stream_t &, Tfunction_base_type &); | ||
| 358 | friend sig_stream_t &operator<<(sig_stream_t &, const Tfunction_base_type &); | ||
| 359 | |||
| 360 | private: | ||
| 361 | bool _fully_defined; | ||
| 362 | sig_stream_t _s; | ||
| 363 | Tfunction_base_type() : _fully_defined{ false } { } | ||
| 364 | }; | ||
| 365 | |||
| 366 | template<type_id_t T> | ||
| 367 | const char Tfunction_base_type<T>::begin = '('; | ||
| 368 | |||
| 369 | template<type_id_t T> | ||
| 370 | const char Tfunction_base_type<T>::end = ')'; | ||
| 371 | |||
| 372 | using Tfunction = Tfunction_base_type<type_id_t::function>; | ||
| 373 | using Tfunction_varargs = Tfunction_base_type<type_id_t::function_varargs>; | ||
| 374 | |||
| 375 | sig_stream_t &operator<<(sig_stream_t &, disvm::assembly::sigkind::Tfunction &); | ||
| 376 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tfunction &); | ||
| 377 | sig_stream_t &operator<<(sig_stream_t &, disvm::assembly::sigkind::Tfunction_varargs &); | ||
| 378 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tfunction_varargs &); | ||
| 379 | } | ||
| 380 | } | 35 | } |
| 381 | 36 | ||
| 382 | namespace symbol | 37 | namespace symbol |
| @@ -495,4 +150,4 @@ namespace disvm · 112 unmodified lines | |||
| 495 | } | 150 | } |
| 496 | } | 151 | } |
| 497 | 152 | ||
| 498 | #endif // _DISVM_SRC_INCLUDE_VM_ASM_H_ | 153 | #endif // _DISVM_SRC_INCLUDE_VM_ASM_H_ |
| @@ -0,0 +1,366 @@ | |||
| 1 | // | ||
| 2 | // Dis VM | ||
| 3 | // File: vm_asm_sigkind.h | ||
| 4 | // Author: arr | ||
| 5 | // | ||
| 6 | |||
| 7 | #ifndef _DISVM_SRC_INCLUDE_VM_ASM_SIGKIND_H_ | ||
| 8 | #define _DISVM_SRC_INCLUDE_VM_ASM_SIGKIND_H_ | ||
| 9 | |||
| 10 | #include <iosfwd> | ||
| 11 | #include <sstream> | ||
| 12 | #include <cassert> | ||
| 13 | #include <cstdint> | ||
| 14 | #include "opcodes.h" | ||
| 15 | #include "runtime.h" | ||
| 16 | |||
| 17 | namespace disvm | ||
| 18 | { | ||
| 19 | namespace assembly | ||
| 20 | { | ||
| 21 | // Namespace named after enum type in Limbo compiler | ||
| 22 | // Source: limbo/types.c | ||
| 23 | namespace sigkind | ||
| 24 | { | ||
| 25 | // Signature stream | ||
| 26 | class sig_stream_t final | ||
| 27 | { | ||
| 28 | public: // static | ||
| 29 | static uint32_t compute_signature_hash(const char *); | ||
| 30 | |||
| 31 | // Type delimiter | ||
| 32 | static const char delim; | ||
| 33 | |||
| 34 | public: | ||
| 35 | std::string get_signature() const; | ||
| 36 | |||
| 37 | uint32_t get_signature_hash() const; | ||
| 38 | |||
| 39 | sig_stream_t &operator<<(const char); | ||
| 40 | sig_stream_t &operator<<(const char *); | ||
| 41 | sig_stream_t &operator<<(const uint32_t); | ||
| 42 | sig_stream_t &operator<<(const sig_stream_t &); | ||
| 43 | |||
| 44 | private: | ||
| 45 | mutable std::stringstream _ss; | ||
| 46 | }; | ||
| 47 | |||
| 48 | // Type context modifiers | ||
| 49 | enum class modifier_id_t : uint8_t | ||
| 50 | { | ||
| 51 | self = 'S', | ||
| 52 | cycle = 'y', | ||
| 53 | }; | ||
| 54 | |||
| 55 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::modifier_id_t &); | ||
| 56 | |||
| 57 | // Instrinsic VM types | ||
| 58 | enum class type_id_t : uint8_t | ||
| 59 | { | ||
| 60 | unknown = 0, // This will be interpreted as a 'null' value which will help debug where the type building failed. | ||
| 61 | none = 'n', | ||
| 62 | byte = 'b', | ||
| 63 | integer = 'i', | ||
| 64 | real = 'r', | ||
| 65 | big = 'B', | ||
| 66 | string = 's', | ||
| 67 | poly = 'P', | ||
| 68 | fixed = 'x', | ||
| 69 | array = 'A', | ||
| 70 | ref = 'R', | ||
| 71 | list = 'L', | ||
| 72 | channel = 'C', | ||
| 73 | adt = 'a', | ||
| 74 | adt_pick_tag = 'p', | ||
| 75 | tuple = 't', | ||
| 76 | module = 'm', | ||
| 77 | function = 'f', | ||
| 78 | function_varargs = 0xff, // "f*" | ||
| 79 | }; | ||
| 80 | |||
| 81 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::type_id_t &); | ||
| 82 | |||
| 83 | template<type_id_t T> | ||
| 84 | struct Tbasic_type | ||
| 85 | { | ||
| 86 | static const type_id_t id; | ||
| 87 | |||
| 88 | constexpr operator type_id_t() const | ||
| 89 | { | ||
| 90 | return Tbasic_type::id; | ||
| 91 | } | ||
| 92 | }; | ||
| 93 | |||
| 94 | template<type_id_t T> | ||
| 95 | const type_id_t Tbasic_type<T>::id = T; | ||
| 96 | |||
| 97 | using Tnone = Tbasic_type<type_id_t::none>; | ||
| 98 | using Tbyte = Tbasic_type<type_id_t::byte>; | ||
| 99 | using Tinteger = Tbasic_type<type_id_t::integer>; | ||
| 100 | using Treal = Tbasic_type<type_id_t::real>; | ||
| 101 | using Tbig = Tbasic_type<type_id_t::big>; | ||
| 102 | using Tstring = Tbasic_type<type_id_t::string>; | ||
| 103 | using Tpoly = Tbasic_type<type_id_t::poly>; | ||
| 104 | |||
| 105 | class Tfixed final : public Tbasic_type<type_id_t::fixed> | ||
| 106 | { | ||
| 107 | public: | ||
| 108 | Tfixed(double scale); | ||
| 109 | |||
| 110 | friend sig_stream_t &operator<<(sig_stream_t &os, const Tfixed &); | ||
| 111 | |||
| 112 | private: | ||
| 113 | const double _scale; | ||
| 114 | }; | ||
| 115 | |||
| 116 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tfixed &); | ||
| 117 | |||
| 118 | // Type that contains/modifies another type | ||
| 119 | template <type_id_t ID> | ||
| 120 | class Tbasic_aggregate_type : public Tbasic_type<ID> | ||
| 121 | { | ||
| 122 | public: | ||
| 123 | template<typename U> | ||
| 124 | static Tbasic_aggregate_type create(const U &type) | ||
| 125 | { | ||
| 126 | sig_stream_t ss; | ||
| 127 | ss << Tbasic_type<ID>::id << type; | ||
| 128 | |||
| 129 | return Tbasic_aggregate_type{ ss }; | ||
| 130 | } | ||
| 131 | |||
| 132 | friend sig_stream_t &operator<<(sig_stream_t &, const Tbasic_aggregate_type &); | ||
| 133 | |||
| 134 | private: | ||
| 135 | sig_stream_t _s; | ||
| 136 | Tbasic_aggregate_type(sig_stream_t &s) : _s{ std::move(s) } { } | ||
| 137 | }; | ||
| 138 | |||
| 139 | using Tarray = Tbasic_aggregate_type<type_id_t::array>; | ||
| 140 | using Tref = Tbasic_aggregate_type<type_id_t::ref>; | ||
| 141 | using Tlist = Tbasic_aggregate_type<type_id_t::list>; | ||
| 142 | using Tchannel = Tbasic_aggregate_type<type_id_t::channel>; | ||
| 143 | |||
| 144 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tarray &); | ||
| 145 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tref &); | ||
| 146 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tlist &); | ||
| 147 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tchannel &); | ||
| 148 | |||
| 149 | // Reference to another previously defined type in the current signature | ||
| 150 | class Ttype_ref final | ||
| 151 | { | ||
| 152 | public: // static | ||
| 153 | static const char id; | ||
| 154 | |||
| 155 | public: | ||
| 156 | Ttype_ref(uint32_t num); | ||
| 157 | |||
| 158 | friend sig_stream_t &operator<<(sig_stream_t &os, const Ttype_ref &); | ||
| 159 | |||
| 160 | private: | ||
| 161 | const uint32_t _num; | ||
| 162 | }; | ||
| 163 | |||
| 164 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Ttype_ref &); | ||
| 165 | |||
| 166 | // Named member type | ||
| 167 | class Tm final | ||
| 168 | { | ||
| 169 | public: | ||
| 170 | template <typename T> | ||
| 171 | Tm(const char *name, const T &type) | ||
| 172 | { | ||
| 173 | _s << name << ':' << type; | ||
| 174 | } | ||
| 175 | |||
| 176 | friend sig_stream_t &operator<<(sig_stream_t &os, const Tm &); | ||
| 177 | |||
| 178 | private: | ||
| 179 | sig_stream_t _s; | ||
| 180 | }; | ||
| 181 | |||
| 182 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tm &); | ||
| 183 | |||
| 184 | inline void nested_type(const sig_stream_t &) | ||
| 185 | { | ||
| 186 | return; | ||
| 187 | } | ||
| 188 | |||
| 189 | template<typename T, typename ...S> | ||
| 190 | void nested_type(sig_stream_t &ss, const T &t, const S&...s) | ||
| 191 | { | ||
| 192 | ss << t; | ||
| 193 | nested_type(ss, s...); | ||
| 194 | } | ||
| 195 | |||
| 196 | template<typename T1, typename T2, typename ...S> | ||
| 197 | void nested_type(sig_stream_t &ss, const T1 &t1, const T2 &t2, const S&...s) | ||
| 198 | { | ||
| 199 | ss << t1 << sig_stream_t::delim; | ||
| 200 | nested_type(ss, t2, s...); | ||
| 201 | } | ||
| 202 | |||
| 203 | // Type whose definition depends on contained types | ||
| 204 | template <type_id_t ID> | ||
| 205 | class Tnested_aggregate_type : public Tbasic_type<ID> | ||
| 206 | { | ||
| 207 | public: // static | ||
| 208 | static const char begin; | ||
| 209 | static const char end; | ||
| 210 | |||
| 211 | template<typename ...NT> | ||
| 212 | static Tnested_aggregate_type create(const NT&... nestedTypes) | ||
| 213 | { | ||
| 214 | sig_stream_t ss; | ||
| 215 | ss << Tbasic_type<ID>::id << Tnested_aggregate_type::begin; | ||
| 216 | |||
| 217 | nested_type(ss, nestedTypes...); | ||
| 218 | |||
| 219 | ss << Tnested_aggregate_type::end; | ||
| 220 | |||
| 221 | return Tnested_aggregate_type{ ss }; | ||
| 222 | } | ||
| 223 | |||
| 224 | public: | ||
| 225 | Tnested_aggregate_type(sig_stream_t &s) | ||
| 226 | : _s{ std::move(s) } | ||
| 227 | { } | ||
| 228 | |||
| 229 | friend sig_stream_t &operator<<(sig_stream_t &, const Tnested_aggregate_type &); | ||
| 230 | |||
| 231 | private: | ||
| 232 | sig_stream_t _s; | ||
| 233 | }; | ||
| 234 | |||
| 235 | using Ttuple = Tnested_aggregate_type<type_id_t::tuple>; | ||
| 236 | using Tmodule = Tnested_aggregate_type<type_id_t::module>; | ||
| 237 | |||
| 238 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Ttuple &); | ||
| 239 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tmodule &); | ||
| 240 | |||
| 241 | // Abstract data type (ADT) pick tag | ||
| 242 | // Note: This type should only be a part of an ADT instance. | ||
| 243 | class Tadt_pick_tag final : public Tbasic_type<type_id_t::adt_pick_tag> | ||
| 244 | { | ||
| 245 | public: // static | ||
| 246 | static const char *tag_delim; | ||
| 247 | static const char begin; | ||
| 248 | static const char end; | ||
| 249 | |||
| 250 | static Tadt_pick_tag create(const char *pick_tag, uint32_t adt_ref, std::initializer_list<const Tm> tms); | ||
| 251 | |||
| 252 | friend sig_stream_t &operator<<(sig_stream_t &, const Tadt_pick_tag &); | ||
| 253 | |||
| 254 | private: | ||
| 255 | sig_stream_t _s; | ||
| 256 | Tadt_pick_tag(sig_stream_t &s) : _s{ std::move(s) } { } | ||
| 257 | }; | ||
| 258 | |||
| 259 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tadt_pick_tag &); | ||
| 260 | |||
| 261 | // Abstract data type (ADT) | ||
| 262 | class Tadt final : public Tnested_aggregate_type<type_id_t::adt> | ||
| 263 | { | ||
| 264 | public: // static | ||
| 265 | // Unhide base class version of function | ||
| 266 | using Tnested_aggregate_type::create; | ||
| 267 | |||
| 268 | // Create an ADT with a single pick tag | ||
| 269 | static Tadt create(const Tadt_pick_tag &p) | ||
| 270 | { | ||
| 271 | sig_stream_t ss; | ||
| 272 | ss << Tbasic_type::id | ||
| 273 | << Tadt::begin | ||
| 274 | << sig_stream_t::delim | ||
| 275 | << p | ||
| 276 | << Tadt::end; | ||
| 277 | |||
| 278 | return Tadt{ ss }; | ||
| 279 | } | ||
| 280 | |||
| 281 | template<typename T, typename ...NT> | ||
| 282 | static Tadt create(const Tadt_pick_tag &p, const T &t, const NT&... nestedTypes) | ||
| 283 | { | ||
| 284 | // [SPEC] When generating type signatures, the Limbo compiler | ||
| 285 | // doesn't identify the case where a pick tag is the first type | ||
| 286 | // so it always prefixes the nested signature with a leading ','. | ||
| 287 | // We special case this scenario to maintain compatibility. The | ||
| 288 | // single pick tag for an ADT must also be handled (above). | ||
| 289 | sig_stream_t ss; | ||
| 290 | ss << Tbasic_type::id | ||
| 291 | << Tadt::begin | ||
| 292 | << sig_stream_t::delim | ||
| 293 | << p | ||
| 294 | << sig_stream_t::delim; | ||
| 295 | |||
| 296 | nested_type(ss, t, nestedTypes...); | ||
| 297 | |||
| 298 | ss << Tadt::end; | ||
| 299 | |||
| 300 | return Tadt{ ss }; | ||
| 301 | } | ||
| 302 | |||
| 303 | private: | ||
| 304 | Tadt(sig_stream_t &s) : Tnested_aggregate_type{ s } { } | ||
| 305 | }; | ||
| 306 | |||
| 307 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tnested_aggregate_type<type_id_t::adt> &); | ||
| 308 | |||
| 309 | // Function type | ||
| 310 | template<type_id_t T> | ||
| 311 | class Tfunction_base_type final : public Tbasic_type<T> | ||
| 312 | { | ||
| 313 | public: // static | ||
| 314 | static const char begin; | ||
| 315 | static const char end; | ||
| 316 | |||
| 317 | template<typename ...AT> | ||
| 318 | static Tfunction_base_type create(AT... argTypes) | ||
| 319 | { | ||
| 320 | Tfunction_base_type a; | ||
| 321 | a._s << Tfunction_base_type::id << Tfunction_base_type::begin; | ||
| 322 | |||
| 323 | nested_type(a._s, argTypes...); | ||
| 324 | |||
| 325 | a._s << Tfunction_base_type::end; | ||
| 326 | return a; | ||
| 327 | } | ||
| 328 | |||
| 329 | public: | ||
| 330 | template<typename RT> | ||
| 331 | Tfunction_base_type &returns(RT r) | ||
| 332 | { | ||
| 333 | assert(!_fully_defined && "Function type already complete"); | ||
| 334 | _s << r; | ||
| 335 | _fully_defined = true; | ||
| 336 | |||
| 337 | return *this; | ||
| 338 | } | ||
| 339 | |||
| 340 | friend sig_stream_t &operator<<(sig_stream_t &, Tfunction_base_type &); | ||
| 341 | friend sig_stream_t &operator<<(sig_stream_t &, const Tfunction_base_type &); | ||
| 342 | |||
| 343 | private: | ||
| 344 | bool _fully_defined; | ||
| 345 | sig_stream_t _s; | ||
| 346 | Tfunction_base_type() : _fully_defined{ false } { } | ||
| 347 | }; | ||
| 348 | |||
| 349 | template<type_id_t T> | ||
| 350 | const char Tfunction_base_type<T>::begin = '('; | ||
| 351 | |||
| 352 | template<type_id_t T> | ||
| 353 | const char Tfunction_base_type<T>::end = ')'; | ||
| 354 | |||
| 355 | using Tfunction = Tfunction_base_type<type_id_t::function>; | ||
| 356 | using Tfunction_varargs = Tfunction_base_type<type_id_t::function_varargs>; | ||
| 357 | |||
| 358 | sig_stream_t &operator<<(sig_stream_t &, disvm::assembly::sigkind::Tfunction &); | ||
| 359 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tfunction &); | ||
| 360 | sig_stream_t &operator<<(sig_stream_t &, disvm::assembly::sigkind::Tfunction_varargs &); | ||
| 361 | sig_stream_t &operator<<(sig_stream_t &, const disvm::assembly::sigkind::Tfunction_varargs &); | ||
| 362 | } | ||
| 363 | } | ||
| 364 | } | ||
| 365 | |||
| 366 | #endif // _DISVM_SRC_INCLUDE_VM_ASM_SIGKIND_H_ | ||
| @@ -131,4 +131,4 @@ namespace disvm · 130 unmodified lines | |||
| 131 | } | 131 | } |
| 132 | } | 132 | } |
| 133 | 133 | ||
| 134 | #endif // _DISVM_SRC_INCLUDE_VM_MEMORY_H_ | 134 | #endif // _DISVM_SRC_INCLUDE_VM_MEMORY_H_ |
| @@ -95,4 +95,4 @@ namespace disvm · 94 unmodified lines | |||
| 95 | } | 95 | } |
| 96 | } | 96 | } |
| 97 | 97 | ||
| 98 | #endif // _DISVM_SRC_INCLUDE_VM_TOOLS_H_ | 98 | #endif // _DISVM_SRC_INCLUDE_VM_TOOLS_H_ |
| @@ -12,4 +12,4 @@ · 11 unmodified lines | |||
| 12 | #define DISVM_VERSION_PATCH 0 | 12 | #define DISVM_VERSION_PATCH 0 |
| 13 | #define DISVM_VERSION_LABEL "alpha" | 13 | #define DISVM_VERSION_LABEL "alpha" |
| 14 | 14 | ||
| 15 | #endif // _DISVM_SRC_INCLUDE_VM_VERSION_H_ | 15 | #endif // _DISVM_SRC_INCLUDE_VM_VERSION_H_ |
| @@ -1678,7 +1678,7 @@ namespace · 1677 unmodified lines | |||
| 1678 | 1678 | ||
| 1679 | assert(r.module_ref != nullptr); | 1679 | assert(r.module_ref != nullptr); |
| 1680 | std::tie(handler, target_frame, handler_pc) = find_exception_handler(r.stack, *r.module_ref, faulting_pc, *exception_id); | 1680 | std::tie(handler, target_frame, handler_pc) = find_exception_handler(r.stack, *r.module_ref, faulting_pc, *exception_id); |
| 1681 | if (handler == nullptr && target_frame == nullptr && handler_pc == runtime_constants::invalid_program_counter) | 1681 | if (handler == nullptr) |
| 1682 | { | 1682 | { |
| 1683 | if (tool_dispatch != nullptr) | 1683 | if (tool_dispatch != nullptr) |
| 1684 | tool_dispatch->on_exception_unhandled(r, *exception_id, *e); | 1684 | tool_dispatch->on_exception_unhandled(r, *exception_id, *e); |
| @@ -1687,7 +1687,7 @@ namespace · 2 unmodified lines | |||
| 1687 | throw unhandled_user_exception{ exception_id->str(), faulting_module_name, faulting_pc }; | 1687 | throw unhandled_user_exception{ exception_id->str(), faulting_module_name, faulting_pc }; |
| 1688 | } | 1688 | } |
| 1689 | 1689 | ||
| 1690 | assert(target_frame != nullptr); | 1690 | assert(target_frame != nullptr && handler_pc != runtime_constants::invalid_program_counter); |
| 1691 | 1691 | ||
| 1692 | // Now that we know the exception has a handler, take a reference. | 1692 | // Now that we know the exception has a handler, take a reference. |
| 1693 | e->add_ref(); | 1693 | e->add_ref(); |
| @@ -18,4 +18,4 @@ namespace disvm · 17 unmodified lines | |||
| 18 | } | 18 | } |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | #endif // _DISVM_SRC_VM_EXECUTION_TABLE_H_ | 21 | #endif // _DISVM_SRC_VM_EXECUTION_TABLE_H_ |
| @@ -42,4 +42,4 @@ namespace disvm · 41 unmodified lines | |||
| 42 | } | 42 | } |
| 43 | } | 43 | } |
| 44 | 44 | ||
| 45 | #endif // _DISVM_SRC_VM_GARBAGE_COLLECTOR_H_ | 45 | #endif // _DISVM_SRC_VM_GARBAGE_COLLECTOR_H_ |
| @@ -102,4 +102,4 @@ namespace disvm · 101 unmodified lines | |||
| 102 | } | 102 | } |
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | #endif // _DISVM_SRC_VM_SCHEDULER_H_ | 105 | #endif // _DISVM_SRC_VM_SCHEDULER_H_ |
| @@ -461,4 +461,4 @@ const word_t Sys_UTFerror = 0xfffd; · 460 unmodified lines | |||
| 461 | const word_t Sys_Runemax = 0x10ffff; | 461 | const word_t Sys_Runemax = 0x10ffff; |
| 462 | const word_t Sys_Runemask = 0x1fffff; | 462 | const word_t Sys_Runemask = 0x1fffff; |
| 463 | 463 | ||
| 464 | #endif // _DISVM_SRC_VM_SYS_SYSMOD_H_ | 464 | #endif // _DISVM_SRC_VM_SYS_SYSMOD_H_ |
| @@ -239,19 +239,25 @@ vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) · 238 unmodified lines | |||
| 239 | catch (const unhandled_user_exception &uue) | 239 | catch (const unhandled_user_exception &uue) |
| 240 | { | 240 | { |
| 241 | _error_message = calloc_memory<char>(max_error_message); | 241 | _error_message = calloc_memory<char>(max_error_message); |
| 242 | std::snprintf(_error_message, max_error_message, "%s - '%s' in %s at instruction %d", uue.what(), uue.exception_id, uue.module_name, uue.program_counter); | 242 | const int ec = std::snprintf(_error_message, max_error_message, "%s - '%s' in %s at instruction %d", uue.what(), uue.exception_id, uue.module_name, uue.program_counter); |
| 243 | assert(ec > 0 && ec < max_error_message); | ||
| 244 | (void)ec; | ||
| 243 | _registers.current_thread_state = vm_thread_state_t::broken; | 245 | _registers.current_thread_state = vm_thread_state_t::broken; |
| 244 | } | 246 | } |
| 245 | catch (const index_out_of_range_memory &ioor) | 247 | catch (const index_out_of_range_memory &ioor) |
| 246 | { | 248 | { |
| 247 | _error_message = calloc_memory<char>(max_error_message); | 249 | _error_message = calloc_memory<char>(max_error_message); |
| 248 | std::snprintf(_error_message, max_error_message, "%s - %d [%d,%d]", ioor.what(), ioor.invalid_value, ioor.valid_min, ioor.valid_max); | 250 | const int ec = std::snprintf(_error_message, max_error_message, "%s - %d [%d,%d]", ioor.what(), ioor.invalid_value, ioor.valid_min, ioor.valid_max); |
| 251 | assert(ec > 0 && ec < max_error_message); | ||
| 252 | (void)ec; | ||
| 249 | _registers.current_thread_state = vm_thread_state_t::broken; | 253 | _registers.current_thread_state = vm_thread_state_t::broken; |
| 250 | } | 254 | } |
| 251 | catch (const vm_user_exception &ue) | 255 | catch (const vm_user_exception &ue) |
| 252 | { | 256 | { |
| 253 | _error_message = calloc_memory<char>(max_error_message); | 257 | _error_message = calloc_memory<char>(max_error_message); |
| 254 | std::snprintf(_error_message, max_error_message, "%s", ue.what()); | 258 | const int ec = std::snprintf(_error_message, max_error_message, "%s", ue.what()); |
| 259 | assert(ec > 0 && ec < max_error_message); | ||
| 260 | (void)ec; | ||
| 255 | _registers.current_thread_state = vm_thread_state_t::broken; | 261 | _registers.current_thread_state = vm_thread_state_t::broken; |
| 256 | } | 262 | } |
| 257 | 263 | ||
| @@ -122,4 +122,4 @@ namespace disvm · 121 unmodified lines | |||
| 122 | } | 122 | } |
| 123 | } | 123 | } |
| 124 | 124 | ||
| 125 | #endif // _DISVM_SRC_VM_TOOL_DISPATCH_H_ | 125 | #endif // _DISVM_SRC_VM_TOOL_DISPATCH_H_ |