• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ArkScript-lang / Ark / 21114730479

18 Jan 2026 04:11PM UTC coverage: 91.335% (-1.4%) from 92.743%
21114730479

Pull #628

github

web-flow
Merge 31f78d00a into ada0e0686
Pull Request #628: Feat/debugger

148 of 320 new or added lines in 11 files covered. (46.25%)

2 existing lines in 1 file now uncovered.

8580 of 9394 relevant lines covered (91.33%)

274862.98 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/arkreactor/VM/Debugger.cpp
1
#include <Ark/VM/Debugger.hpp>
2

3
#include <fmt/core.h>
4
#include <fmt/color.h>
5

6
#include <Ark/State.hpp>
7
#include <Ark/VM/VM.hpp>
8
#include <Ark/Utils/Files.hpp>
9
#include <Ark/Compiler/Welder.hpp>
10
#include <Ark/Compiler/BytecodeReader.hpp>
11
#include <Ark/Error/Diagnostics.hpp>
12

13
namespace Ark::internal
14
{
NEW
15
    Debugger::Debugger(const ExecutionContext& context, const std::vector<std::filesystem::path>& libenv, const std::vector<std::string>& symbols, const std::vector<Value>& constants) :
×
NEW
16
        m_libenv(libenv), m_symbols(symbols), m_constants(constants), m_running(false), m_quit_vm(false)
×
NEW
17
    {
×
NEW
18
        saveState(context);
×
NEW
19
    }
×
20

NEW
21
    void Debugger::saveState(const ExecutionContext& context)
×
NEW
22
    {
×
NEW
23
        m_states.emplace_back(
×
NEW
24
            std::make_unique<SavedState>(
×
NEW
25
                context.ip,
×
NEW
26
                context.pp,
×
NEW
27
                context.sp,
×
NEW
28
                context.fc,
×
NEW
29
                context.locals,
×
NEW
30
                context.stacked_closure_scopes));
×
NEW
31
    }
×
32

NEW
33
    void Debugger::resetContextToSavedState(ExecutionContext& context)
×
NEW
34
    {
×
NEW
35
        const auto& [ip, pp, sp, fc, locals, closure_scopes] = *m_states.back();
×
NEW
36
        context.locals = locals;
×
NEW
37
        context.stacked_closure_scopes = closure_scopes;
×
NEW
38
        context.ip = ip;
×
NEW
39
        context.pp = pp;
×
NEW
40
        context.sp = sp;
×
NEW
41
        context.fc = fc;
×
42

NEW
43
        m_states.pop_back();
×
NEW
44
    }
×
45

NEW
46
    void Debugger::run(VM& vm, ExecutionContext& context)
×
NEW
47
    {
×
NEW
48
        m_running = true;
×
NEW
49
        const bool is_vm_running = vm.m_running;
×
50

51
        // show the line where the breakpoint hit
NEW
52
        const auto maybe_source_loc = vm.findSourceLocation(context.ip, context.pp);
×
NEW
53
        if (maybe_source_loc)
×
54
        {
NEW
55
            const auto filename = vm.m_state.m_filenames[maybe_source_loc->filename_id];
×
56

NEW
57
            if (Utils::fileExists(filename))
×
58
            {
NEW
59
                fmt::println("");
×
NEW
60
                Diagnostics::makeContext(
×
NEW
61
                    Diagnostics::ErrorLocation {
×
NEW
62
                        .filename = filename,
×
NEW
63
                        .start = FilePos { .line = maybe_source_loc->line, .column = 0 },
×
NEW
64
                        .end = std::nullopt },
×
65
                    std::cout,
NEW
66
                    /* maybe_context= */ std::nullopt,
×
67
                    /* colorize= */ true);
NEW
68
                fmt::println("");
×
NEW
69
            }
×
NEW
70
        }
×
71

NEW
72
        while (true)
×
73
        {
NEW
74
            std::optional<std::string> maybe_input = prompt();
×
75

NEW
76
            if (maybe_input)
×
77
            {
NEW
78
                const std::string& line = maybe_input.value();
×
79

NEW
80
                if (const auto pages = compile(m_code + line, vm.m_state.m_pages.size()); pages.has_value())
×
81
                {
NEW
82
                    context.ip = 0;
×
NEW
83
                    context.pp = vm.m_state.m_pages.size();
×
84
                    // create dedicated scope, so that we won't be overwriting existing variables
NEW
85
                    context.locals.emplace_back(context.scopes_storage.data(), context.locals.back().storageEnd());
×
86

NEW
87
                    vm.m_state.extendBytecode(pages.value(), m_symbols, m_constants);
×
88

NEW
89
                    if (vm.safeRun(context) == 0)
×
90
                    {
91
                        // executing code worked
NEW
92
                        m_code += line;
×
93

NEW
94
                        const Value* maybe_value = vm.peekAndResolveAsPtr(context);
×
NEW
95
                        if (maybe_value != nullptr && maybe_value->valueType() != ValueType::Undefined && maybe_value->valueType() != ValueType::InstPtr)
×
NEW
96
                            fmt::println("{}", fmt::styled(maybe_value->toString(vm), fmt::fg(fmt::color::chocolate)));
×
NEW
97
                    }
×
98

NEW
99
                    context.locals.pop_back();
×
NEW
100
                }
×
NEW
101
            }
×
102
            else
NEW
103
                break;
×
NEW
104
        }
×
105

NEW
106
        m_running = false;
×
107
        // we do not want to retain code from the past executions
NEW
108
        m_code.clear();
×
109
        // we hit a HALT instruction that set 'running' to false, ignore that if we were still running!
NEW
110
        vm.m_running = is_vm_running;
×
NEW
111
    }
×
112

NEW
113
    std::optional<std::string> Debugger::prompt()
×
NEW
114
    {
×
NEW
115
        std::string code;
×
NEW
116
        long open_parens = 0;
×
NEW
117
        long open_braces = 0;
×
118

NEW
119
        while (true)
×
120
        {
NEW
121
            const bool unfinished_block = open_parens != 0 || open_braces != 0;
×
NEW
122
            fmt::print("dbg:{:0>3}{} ", m_line_count, unfinished_block ? ":" : ">");
×
NEW
123
            std::string line;
×
NEW
124
            std::getline(std::cin, line);
×
125

NEW
126
            Utils::trimWhitespace(line);
×
127

NEW
128
            if (line == "c" || line == "continue" || line.empty())
×
129
            {
NEW
130
                fmt::println("dbg: continue");
×
NEW
131
                return std::nullopt;
×
132
            }
NEW
133
            else if (line == "q" || line == "quit")
×
134
            {
NEW
135
                fmt::println("dbg: stop");
×
NEW
136
                m_quit_vm = true;
×
NEW
137
                return std::nullopt;
×
138
            }
NEW
139
            else if (line == "help")
×
140
            {
NEW
141
                fmt::println("Available commands:");
×
NEW
142
                fmt::println("  help -- display this message");
×
NEW
143
                fmt::println("  c, continue -- resume execution");
×
NEW
144
                fmt::println("  q, quit -- quit the debugger, stopping the script execution");
×
NEW
145
            }
×
146
            else
147
            {
NEW
148
                code += line;
×
149

NEW
150
                open_parens += Utils::countOpenEnclosures(line, '(', ')');
×
NEW
151
                open_braces += Utils::countOpenEnclosures(line, '{', '}');
×
152

NEW
153
                ++m_line_count;
×
NEW
154
                if (open_braces == 0 && open_parens == 0)
×
NEW
155
                    break;
×
156
            }
NEW
157
        }
×
158

NEW
159
        return code;
×
NEW
160
    }
×
161

NEW
162
    std::optional<std::vector<bytecode_t>> Debugger::compile(const std::string& code, const std::size_t start_page_at_offset)
×
NEW
163
    {
×
NEW
164
        Welder welder(0, m_libenv, DefaultFeatures);
×
NEW
165
        if (!welder.computeASTFromStringWithKnownSymbols(code, m_symbols))
×
NEW
166
            return std::nullopt;
×
NEW
167
        if (!welder.generateBytecodeUsingTables(m_symbols, m_constants, start_page_at_offset))
×
NEW
168
            return std::nullopt;
×
169

NEW
170
        BytecodeReader bcr;
×
NEW
171
        bcr.feed(welder.bytecode());
×
NEW
172
        const auto syms = bcr.symbols();
×
NEW
173
        const auto vals = bcr.values(syms);
×
NEW
174
        const auto files = bcr.filenames(vals);
×
NEW
175
        const auto inst_locs = bcr.instLocations(files);
×
NEW
176
        const auto [pages, _] = bcr.code(inst_locs);
×
177

NEW
178
        m_symbols = syms.symbols;
×
NEW
179
        m_constants = vals.values;
×
180

NEW
181
        return pages;
×
NEW
182
    }
×
183
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc