History

Merged in debug_set_options (pull request #4)

Debug set options

Aaron R Robinson committed 9 years ago

Commit c55b47f2 parents 3cea79f 02a7d6a26 files · +523 −436Browse files

Unified · Split

26 files
  1. src/asm/type_signature.cpp +1 −1
  2. src/compiler_settings.gypi +2 −0
  3. src/exec/debugger.cpp +99 −43
  4. src/exec/exec.h +7 −1
  5. src/exec/main.cpp +19 −23
  6. src/include/buffered_reader.h +1 −1
  7. src/include/builtin_module.h +1 −1
  8. src/include/debug.h +1 −1
  9. src/include/disvm.h +1 −1
  10. src/include/exceptions.h +1 −1
  11. src/include/module_reader.h +1 −1
  12. src/include/opcodes.h +1 −1
  13. src/include/utf8.h +1 −1
  14. src/include/utils.h +1 −1
  15. src/include/vm_asm.h +1 −346
  16. src/include/vm_asm_sigkind.h +366 −0
  17. src/include/vm_memory.h +1 −1
  18. src/include/vm_tools.h +1 −1
  19. src/include/vm_version.h +1 −1
  20. src/vm/execution_table.cpp +2 −2
  21. src/vm/execution_table.h +1 −1
  22. src/vm/garbage_collector.h +1 −1
  23. src/vm/scheduler.h +1 −1
  24. src/vm/sys/Sysmod.h +1 −1
  25. src/vm/thread.cpp +9 −3
  26. src/vm/tool_dispatch.h +1 −1
src/asm/type_signature.cpp+1−1
@@ -4,8 +4,8 @@ · 3 unmodified lines
44 // Author: arr
55 //
66
7−#include <vm_asm.h>
87 #include <array>
8+#include <vm_asm_sigkind.h>
99 #include "rfc1321/global.h"
1010 #include "rfc1321/md5.h"
1111
src/compiler_settings.gypi+2−0
@@ -33,6 +33,8 @@ · 32 unmodified lines
3333 },
3434 'msvs_settings': {
3535 'VCCLCompilerTool': {
36+ 'AdditionalOptions': '/Zc:rvalueCast',
37+ 'DisableLanguageExtensions': 'true',
3638 'TreatWChar_tAsBuiltInType': 'false',
3739 'WarningLevel': 3,
3840 'DebugInformationFormat': '3', # PDB
src/exec/debugger.cpp+99−43
@@ -11,6 +11,7 @@ · 10 unmodified lines
1111 #include <string>
1212 #include <algorithm>
1313 #include <map>
14+#include <unordered_set>
1415 #include <utils.h>
1516 #include <vm_memory.h>
1617 #include <vm_asm.h>
@@ -383,9 +384,9 @@ namespace · 366 unmodified lines
383384
384385 auto msg = std::stringstream{};
385386 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]";
387388 for (auto &f : module_funcs)
388− msg << " " << f << "\n";
389+ msg << "\n " << f;
389390
390391 debug_print_info(msg.str());
391392 }
@@ -615,6 +616,7 @@ namespace · 223 unmodified lines
615616 }
616617 }
617618
619+ auto &dbg = cxt.dbg;
618620 const auto &r = *cxt.current_register;
619621 const auto &code_section = r.module_ref->code_section;
620622
@@ -623,19 +625,19 @@ namespace · 2 unmodified lines
623625 if (disassemble_length < 0)
624626 {
625627 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));
627629 }
628630 else
629631 {
630632 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);
632634 }
633635
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);
635637 assert(resolved_pc.empty() || resolved_pc.size() == (end_pc - begin_pc + 1) && "Resolved PCs should have failed or match the PC count");
636638
637639 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)
639641 {
640642 auto op = code_section[i].op;
641643
@@ -645,9 +647,9 @@ namespace · 3 unmodified lines
645647 if (op.opcode == opcode_t::brkpt)
646648 {
647649 // 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)
649651 {
650− const auto details = cxt.dbg.controller->get_breakpoint_details(bp_cookie);
652+ const auto details = dbg.controller->get_breakpoint_details(bp_cookie);
651653 if (details.pc == i && details.module == r.module_ref->module)
652654 {
653655 op.opcode = details.original_opcode;
@@ -660,9 +662,9 @@ namespace · 6 unmodified lines
660662 // Print source location if resolved
661663 if (!resolved_pc.empty())
662664 {
663− const auto & r = resolved_pc[i - begin_pc];
664− if (!r.empty())
665− msg << r << "\n";
665+ const auto &rpc = resolved_pc[i - begin_pc];
666+ if (!rpc.empty())
667+ msg << rpc << "\n";
666668 }
667669
668670 msg << op_prefix << std::setw(5) << i << ": " << op << "\n";
@@ -811,18 +813,47 @@ namespace · 142 unmodified lines
811813 << console_modifiers::reset_all;
812814 }
813815
814− void set_value_cmd(const std::vector<std::string> &args, dbg_cmd_cxt_t &)
816+ std::unordered_set<std::string> dbg_options =
815817 {
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)
817825 {
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());
819837 return;
820838 }
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)
822848 {
823− // [TODO] Print help for 'set' command
824− return;
849+ new_value << args[i];
850+
851+ // For the last parsed arg
852+ if (i + 1 < args.size())
853+ new_value << " ";
825854 }
855+
856+ cxt.dbg.set_option(curr_option, new_value.str());
826857 }
827858
828859 const dbg_cmd_t cmds[] =
@@ -844,7 +875,7 @@ namespace · 15 unmodified lines
844875 { "d", "Disassemble the next/previous N instructions (default N = 4)", "d (-?[0-9]+)?", "d, d -4, d 5", cmd_disassemble },
845876 { "x", "Examine memory", "x [mp|fp] [0-9]+ ([0-9]+)?", "x mp 3, x fp 20 6", cmd_examine },
846877
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 },
848879 { "?", "Print help", nullptr, nullptr, cmd_help },
849880
850881 // Undocumented commands
@@ -865,8 +896,8 @@ namespace · 14 unmodified lines
865896
866897 void print_help()
867898 {
868− auto ss = std::stringstream{};
869− ss << "Commands:\n";
899+ auto msg = std::stringstream{};
900+ msg << "Commands:\n";
870901 for (auto i = std::size_t{}; i < sizeof(cmds) / sizeof(cmds[0]); ++i)
871902 {
872903 auto &c = cmds[i];
@@ -875,16 +906,33 @@ namespace · 2 unmodified lines
875906 if (c.description == nullptr)
876907 continue;
877908
878− ss << " " << c.cmd << "\t" << c.description << "\n";
909+ msg << " " << c.cmd << "\t" << c.description << "\n";
879910
880911 if (c.syntax != nullptr)
881− ss << "\t Syntax: " << c.syntax << "\n";
912+ msg << "\t Syntax: " << c.syntax << "\n";
882913
883914 if (c.example != nullptr)
884− ss << "\t Example(s): " << c.example << "\n";
915+ msg << "\t Example(s): " << c.example << "\n";
885916 }
886917
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+ }
888936 }
889937
890938 void prompt(debugger &debugger, const vm_registers_t &r)
@@ -910,21 +958,11 @@ namespace · 19 unmodified lines
910958 << console_modifiers::reset_all;
911959
912960 std::getline(std::cin, cmd);
913− const auto cmd_tokens = util::split(cmd);
914− if (cmd_tokens.empty())
915− continue;
916961
917− try
918− {
919− auto cmd_exec = find_cmd(cmd_tokens[0]);
920− cmd_exec(cmd_tokens, cxt);
921− if (cxt.exit_break)
922− break;
923− }
924− catch (const debug_cmd_error_t &e)
925− {
926− debug_print_error(e.what());
927− }
962+ assert(!cxt.exit_break && "Pre-condition on exit_break state before execute failed");
963+ execute_single_command(cxt, cmd);
964+ if (cxt.exit_break)
965+ break;
928966 }
929967
930968 debugger.controller->resume_all_threads();
@@ -1019,6 +1057,20 @@ bool debugger::symbols_exist(disvm::runtime::module_id_t vm_id) const · 88 unmodified lines
10191057 return _vm_id_to_symbol_info.find(vm_id) != std::cend(_vm_id_to_symbol_info);
10201058 }
10211059
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+
10221074 void debugger::on_load(vm_tool_controller_t &controller_, std::size_t tool_id)
10231075 {
10241076 _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
11151167
11161168 debug_print_info(ss.str());
11171169
1118− auto reg_str = registers_to_string(*cxt.value1.registers);
1119− debug_print_info(reg_str);
1170+ const auto bp_cmd = get_option({ "bp-cmd" });
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+ }
11201176
11211177 prompt(*this, *cxt.value1.registers);
11221178 }));
@@ -1163,7 +1219,7 @@ void debugger::load_symbols(const disvm::loaded_vm_module_t &loaded_mod) · 40 unmodified lines
11631219 catch (const std::runtime_error &e)
11641220 {
11651221 // File with assumed symbol name/extension is not the correct format
1166− std::stringstream msg;
1222+ auto msg = std::stringstream{};
11671223 msg << "Assumed symbol file '"
11681224 << symbol_path_maybe
11691225 << "' has invalid symbol format. ("
@@ -1178,7 +1234,7 @@ void debugger::load_symbols(const disvm::loaded_vm_module_t &loaded_mod) · 8 unmodified lines
11781234 || symbol_data->get_instruction_count() != m->code_section.size())
11791235 {
11801236 // Symbol file does not match the loaded modules - stale file?
1181− std::stringstream msg;
1237+ auto msg = std::stringstream{};
11821238 msg << "Symbol file '"
11831239 << symbol_path_maybe
11841240 << "' does not match loaded module '"
@@ -1193,7 +1249,7 @@ void debugger::load_symbols(const disvm::loaded_vm_module_t &loaded_mod) · 8 unmodified lines
11931249 auto symbol_iter = symbol_data->get_pc_iter();
11941250 _vm_id_to_symbol_info[vm_id] = { std::move(symbol_data), std::move(symbol_iter) };
11951251
1196− std::stringstream msg;
1252+ auto msg = std::stringstream{};
11971253 msg << "Symbol file '"
11981254 << symbol_path_maybe
11991255 << "' loaded for '"
src/exec/exec.h+7−1
@@ -13,6 +13,7 @@ · 12 unmodified lines
1313 #include <string>
1414 #include <memory>
1515 #include <disvm.h>
16+#include <vm_asm.h>
1617 #include <vm_tools.h>
1718 #include <utils.h>
1819
@@ -48,6 +49,10 @@ public: · 29 unmodified lines
4849 // Check if the supplied module ID has symbols loaded
4950 bool symbols_exist(disvm::runtime::module_id_t vm_id) const;
5051
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+
5156 public: // vm_tool_t
5257 void on_load(disvm::runtime::vm_tool_controller_t &, std::size_t tool_id) override;
5358
@@ -71,6 +76,7 @@ private: · 17 unmodified lines
7176 };
7277
7378 std::unordered_map<std::size_t, symbol_info_t> _vm_id_to_symbol_info;
79+ std::unordered_map<std::string, std::string> _debugger_options;
7480 };
7581
76−#endif // _DISVM_SRC_EXEC_EXEC_H_
82+#endif // _DISVM_SRC_EXEC_EXEC_H_
src/exec/main.cpp+19−23
@@ -11,7 +11,7 @@ · 10 unmodified lines
1111 #include <builtin_module.h>
1212 #include <debug.h>
1313 #include <exceptions.h>
14−#include <vm_asm.h>
14+#include <vm_asm_sigkind.h>
1515 #include <vm_version.h>
1616 #include "exec.h"
1717
@@ -21,6 +21,12 @@ using namespace disvm::assembly::sigkind; · 3 unmodified lines
2121
2222 namespace
2323 {
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+
2430 // Entry frame layout
2531 struct vm_entry_frame_t final : public vm_frame_base_alloc_t
2632 {
@@ -35,24 +41,17 @@ namespace · 8 unmodified lines
3541
3642 std::unique_ptr<vm_module_t> create_entry_module(vm_t &vm, const std::vector<char *> &vm_args)
3743 {
38− if (vm_args.size() == 0)
39− throw vm_user_exception{ "Entry module not supplied" };
40−
4144 // Convert arguments to a list
4245 vm_string_t *command_module_path = nullptr;
43− vm_list_t *args = nullptr;
44− vm_list_t *last = nullptr;
45− for (auto i = std::size_t{ 0 }; i < vm_args.size(); ++i)
46+ vm_list_t *mod_args = new vm_list_t{ intrinsic_type_desc::type<vm_string_t>() };
47+ vm_list_t *last = mod_args;
48+ for (auto raw_arg : vm_args)
4649 {
47− auto tmp = vm_args[i];
48− auto arg = new vm_string_t{ std::strlen(tmp), reinterpret_cast<uint8_t *>(tmp) };
49− if (args == nullptr)
50+ auto curr_arg = new vm_string_t{ std::strlen(raw_arg), reinterpret_cast<uint8_t *>(raw_arg) };
51+ if (command_module_path == nullptr)
5052 {
5153 // First argument is the module to load
52− command_module_path = arg;
53− assert(last == nullptr);
54− args = new vm_list_t{ intrinsic_type_desc::type<vm_string_t>() };
55− last = args;
54+ command_module_path = curr_arg;
5655 }
5756 else
5857 {
@@ -63,10 +62,11 @@ namespace · 4 unmodified lines
6362 last = new_tail;
6463 }
6564
66− pt_ref(last->value()) = arg->get_allocation();
65+ pt_ref(last->value()) = curr_arg->get_allocation();
6766 }
6867
69− assert(args != nullptr);
68+ if (command_module_path == nullptr)
69+ throw vm_user_exception{ "Entry module not supplied" };
7070
7171 // Pre-load the command module in the VM.
7272 command_module = vm.load_module(command_module_path->str());
@@ -79,9 +79,7 @@ namespace · 6 unmodified lines
7979 e.header.entry_pc = 0;
8080 e.header.entry_type = 0;
8181 e.header.runtime_flag = (runtime_flags_t::has_import);
82−
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());
82+ e.module_name = create_string_from_const("_entry_");
8583
8684 // Inherit the stack extent from the command module and add the entry frame size.
8785 e.header.stack_extent = command_module->header.stack_extent + sizeof(vm_entry_frame_t);
@@ -99,7 +97,7 @@ namespace · 11 unmodified lines
9997 // Take a reference on the command module path and store it on the entry frame
10098 command_module_path->add_ref();
10199 mp_base[0] = command_module_path->get_allocation();
102− mp_base[1] = args->get_allocation();
100+ mp_base[1] = mod_args->get_allocation();
103101
104102 // Define the frame offset for the argument
105103 const auto first_arg_offset = vm_frame_constants::limbo_first_arg_register_offset();
@@ -168,9 +166,7 @@ namespace · 62 unmodified lines
168166 assert(exec_init_sig == sig_stream_t::compute_signature_hash("f(Ls)n"));
169167 #endif
170168 func.sig = exec_init_sig;
171−
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());
169+ func.name = create_string_from_const("init");
174170
175171 import_vm_module_t import{};
176172 import.functions.push_back(std::move(func));
src/include/buffered_reader.h+1−1
@@ -156,4 +156,4 @@ namespace disvm · 155 unmodified lines
156156 }
157157 }
158158
159−#endif // _DISVM_SRC_INCLUDE_BUFFERED_READER_H_
159+#endif // _DISVM_SRC_INCLUDE_BUFFERED_READER_H_
src/include/builtin_module.h+1−1
@@ -118,4 +118,4 @@ namespace disvm · 117 unmodified lines
118118 }
119119 }
120120
121−#endif // _DISVM_SRC_INCLUDE_BUILTIN_MODULE_H_
121+#endif // _DISVM_SRC_INCLUDE_BUILTIN_MODULE_H_
src/include/debug.h+1−1
@@ -97,4 +97,4 @@ namespace disvm · 96 unmodified lines
9797 }
9898 }
9999
100−#endif // _DISVM_SRC_INCLUDE_DEBUG_H_
100+#endif // _DISVM_SRC_INCLUDE_DEBUG_H_
src/include/disvm.h+1−1
@@ -125,4 +125,4 @@ namespace disvm · 124 unmodified lines
125125 };
126126 }
127127
128−#endif // _DISVM_SRC_INCLUDE_DISVM_H_
128+#endif // _DISVM_SRC_INCLUDE_DISVM_H_
src/include/exceptions.h+1−1
@@ -143,4 +143,4 @@ namespace disvm · 142 unmodified lines
143143 }
144144 }
145145
146−#endif // _DISVM_SRC_INCLUDE_EXCEPTIONS_H_
146+#endif // _DISVM_SRC_INCLUDE_EXCEPTIONS_H_
src/include/module_reader.h+1−1
@@ -26,4 +26,4 @@ namespace disvm · 25 unmodified lines
2626 }
2727 }
2828
29−#endif // _DISVM_SRC_INCLUDE_MODULE_READER_H_
29+#endif // _DISVM_SRC_INCLUDE_MODULE_READER_H_
src/include/opcodes.h+1−1
@@ -199,4 +199,4 @@ namespace disvm · 198 unmodified lines
199199 };
200200 }
201201
202−#endif // _DISVM_SRC_INCLUDE_OPCODES_H_
202+#endif // _DISVM_SRC_INCLUDE_OPCODES_H_
src/include/utf8.h+1−1
@@ -54,4 +54,4 @@ namespace disvm · 53 unmodified lines
5454 }
5555 }
5656
57−#endif // _DISVM_SRC_INCLUDE_UTF8_H_
57+#endif // _DISVM_SRC_INCLUDE_UTF8_H_
src/include/utils.h+1−1
@@ -78,4 +78,4 @@ inline TYPE operator ^ (TYPE a, TYPE b) { return static_cast<TYPE>(static_cast<s · 77 unmodified lines
7878 inline TYPE &operator ^= (TYPE &a, TYPE b) { a = a ^ b; return a; } \
7979 inline TYPE operator ~ (TYPE a) { return static_cast<TYPE>(~static_cast<std::underlying_type_t<TYPE>>(a)); }
8080
81−#endif // _DISVM_SRC_INCLUDE_UTILS_H_
81+#endif // _DISVM_SRC_INCLUDE_UTILS_H_
src/include/vm_asm.h+1−346
@@ -8,8 +8,6 @@ · 7 unmodified lines
88 #define _DISVM_SRC_INCLUDE_VM_ASM_H_
99
1010 #include <iosfwd>
11−#include <sstream>
12−#include <cassert>
1311 #include <cstdint>
1412 #include "opcodes.h"
1513 #include "runtime.h"
@@ -34,349 +32,6 @@ namespace disvm · 18 unmodified lines
3432 // Get the opcode from this string.
3533 // If the supplied string cannot be resolved or if it is null, the 'invalid' opcode is returned.
3634 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− }
38035 }
38136
38237 namespace symbol
@@ -495,4 +150,4 @@ namespace disvm · 112 unmodified lines
495150 }
496151 }
497152
498−#endif // _DISVM_SRC_INCLUDE_VM_ASM_H_
153+#endif // _DISVM_SRC_INCLUDE_VM_ASM_H_
src/include/vm_asm_sigkind.h+366−0
@@ -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_
src/include/vm_memory.h+1−1
@@ -131,4 +131,4 @@ namespace disvm · 130 unmodified lines
131131 }
132132 }
133133
134−#endif // _DISVM_SRC_INCLUDE_VM_MEMORY_H_
134+#endif // _DISVM_SRC_INCLUDE_VM_MEMORY_H_
src/include/vm_tools.h+1−1
@@ -95,4 +95,4 @@ namespace disvm · 94 unmodified lines
9595 }
9696 }
9797
98−#endif // _DISVM_SRC_INCLUDE_VM_TOOLS_H_
98+#endif // _DISVM_SRC_INCLUDE_VM_TOOLS_H_
src/include/vm_version.h+1−1
@@ -12,4 +12,4 @@ · 11 unmodified lines
1212 #define DISVM_VERSION_PATCH 0
1313 #define DISVM_VERSION_LABEL "alpha"
1414
15−#endif // _DISVM_SRC_INCLUDE_VM_VERSION_H_
15+#endif // _DISVM_SRC_INCLUDE_VM_VERSION_H_
src/vm/execution_table.cpp+2−2
@@ -1678,7 +1678,7 @@ namespace · 1677 unmodified lines
16781678
16791679 assert(r.module_ref != nullptr);
16801680 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)
16821682 {
16831683 if (tool_dispatch != nullptr)
16841684 tool_dispatch->on_exception_unhandled(r, *exception_id, *e);
@@ -1687,7 +1687,7 @@ namespace · 2 unmodified lines
16871687 throw unhandled_user_exception{ exception_id->str(), faulting_module_name, faulting_pc };
16881688 }
16891689
1690− assert(target_frame != nullptr);
1690+ assert(target_frame != nullptr && handler_pc != runtime_constants::invalid_program_counter);
16911691
16921692 // Now that we know the exception has a handler, take a reference.
16931693 e->add_ref();
src/vm/execution_table.h+1−1
@@ -18,4 +18,4 @@ namespace disvm · 17 unmodified lines
1818 }
1919 }
2020
21−#endif // _DISVM_SRC_VM_EXECUTION_TABLE_H_
21+#endif // _DISVM_SRC_VM_EXECUTION_TABLE_H_
src/vm/garbage_collector.h+1−1
@@ -42,4 +42,4 @@ namespace disvm · 41 unmodified lines
4242 }
4343 }
4444
45−#endif // _DISVM_SRC_VM_GARBAGE_COLLECTOR_H_
45+#endif // _DISVM_SRC_VM_GARBAGE_COLLECTOR_H_
src/vm/scheduler.h+1−1
@@ -102,4 +102,4 @@ namespace disvm · 101 unmodified lines
102102 }
103103 }
104104
105−#endif // _DISVM_SRC_VM_SCHEDULER_H_
105+#endif // _DISVM_SRC_VM_SCHEDULER_H_
src/vm/sys/Sysmod.h+1−1
@@ -461,4 +461,4 @@ const word_t Sys_UTFerror = 0xfffd; · 460 unmodified lines
461461 const word_t Sys_Runemax = 0x10ffff;
462462 const word_t Sys_Runemask = 0x1fffff;
463463
464−#endif // _DISVM_SRC_VM_SYS_SYSMOD_H_
464+#endif // _DISVM_SRC_VM_SYS_SYSMOD_H_
src/vm/thread.cpp+9−3
@@ -239,19 +239,25 @@ vm_thread_state_t vm_thread_t::execute(vm_t &vm, const uint32_t quanta) · 238 unmodified lines
239239 catch (const unhandled_user_exception &uue)
240240 {
241241 _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;
243245 _registers.current_thread_state = vm_thread_state_t::broken;
244246 }
245247 catch (const index_out_of_range_memory &ioor)
246248 {
247249 _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;
249253 _registers.current_thread_state = vm_thread_state_t::broken;
250254 }
251255 catch (const vm_user_exception &ue)
252256 {
253257 _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;
255261 _registers.current_thread_state = vm_thread_state_t::broken;
256262 }
257263
src/vm/tool_dispatch.h+1−1
@@ -122,4 +122,4 @@ namespace disvm · 121 unmodified lines
122122 }
123123 }
124124
125−#endif // _DISVM_SRC_VM_TOOL_DISPATCH_H_
125+#endif // _DISVM_SRC_VM_TOOL_DISPATCH_H_