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

ArkScript-lang / Ark / 21488144923

29 Jan 2026 05:26PM UTC coverage: 93.408% (-0.009%) from 93.417%
21488144923

push

github

SuperFola
chore: update credits

8828 of 9451 relevant lines covered (93.41%)

274416.0 hits per line

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

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

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

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

14
namespace Ark::internal
15
{
16
    Debugger::Debugger(const ExecutionContext& context, const std::vector<std::filesystem::path>& libenv, const std::vector<std::string>& symbols, const std::vector<Value>& constants) :
×
17
        m_libenv(libenv),
×
18
        m_symbols(symbols),
×
19
        m_constants(constants),
×
20
        m_os(std::cout),
×
21
        m_colorize(true)
×
22
    {
×
23
        saveState(context);
×
24
    }
×
25

26
    Debugger::Debugger(const std::vector<std::filesystem::path>& libenv, const std::string& path_to_prompt_file, std::ostream& os, const std::vector<std::string>& symbols, const std::vector<Value>& constants) :
10✔
27
        m_libenv(libenv),
5✔
28
        m_symbols(symbols),
5✔
29
        m_constants(constants),
5✔
30
        m_os(os),
5✔
31
        m_colorize(false),
5✔
32
        m_prompt_stream(std::make_unique<std::ifstream>(path_to_prompt_file))
5✔
33
    {}
10✔
34

35
    void Debugger::saveState(const ExecutionContext& context)
10✔
36
    {
10✔
37
        m_states.emplace_back(
20✔
38
            std::make_unique<SavedState>(
20✔
39
                context.ip,
10✔
40
                context.pp,
10✔
41
                context.sp,
10✔
42
                context.fc,
10✔
43
                context.locals,
10✔
44
                context.stacked_closure_scopes));
10✔
45
    }
10✔
46

47
    void Debugger::resetContextToSavedState(ExecutionContext& context)
10✔
48
    {
10✔
49
        const auto& [ip, pp, sp, fc, locals, closure_scopes] = *m_states.back();
30✔
50
        context.locals = locals;
10✔
51
        context.stacked_closure_scopes = closure_scopes;
10✔
52
        context.ip = ip;
10✔
53
        context.pp = pp;
10✔
54
        context.sp = sp;
10✔
55
        context.fc = fc;
10✔
56

57
        m_states.pop_back();
10✔
58
    }
10✔
59

60
    void Debugger::run(VM& vm, ExecutionContext& context, const bool from_breakpoint)
10✔
61
    {
10✔
62
        if (from_breakpoint)
10✔
63
            showContext(vm, context);
9✔
64

65
        m_running = true;
10✔
66
        const bool is_vm_running = vm.m_running;
10✔
67
        const std::size_t ip_at_breakpoint = context.ip,
10✔
68
                          pp_at_breakpoint = context.pp;
10✔
69
        // create dedicated scope, so that we won't be overwriting existing variables
70
        context.locals.emplace_back(context.scopes_storage.data(), context.locals.back().storageEnd());
10✔
71
        std::size_t last_ip = 0;
10✔
72

73
        while (true)
23✔
74
        {
75
            std::optional<std::string> maybe_input = prompt(ip_at_breakpoint, pp_at_breakpoint);
23✔
76

77
            if (maybe_input)
23✔
78
            {
79
                const std::string& line = maybe_input.value();
13✔
80

81
                if (const auto compiled = compile(m_code + line, vm.m_state.m_pages.size()); compiled.has_value())
26✔
82
                {
83
                    context.ip = last_ip;
13✔
84
                    context.pp = vm.m_state.m_pages.size();
13✔
85

86
                    vm.m_state.extendBytecode(compiled->pages, compiled->symbols, compiled->constants);
13✔
87

88
                    if (vm.safeRun(context) == 0)
13✔
89
                    {
90
                        // executing code worked
91
                        m_code += line + "\n";
13✔
92
                        // place ip to end of bytecode instruction (HALT)
93
                        last_ip = context.ip - 4;
13✔
94

95
                        const Value* maybe_value = vm.peekAndResolveAsPtr(context);
13✔
96
                        if (maybe_value != nullptr && maybe_value->valueType() != ValueType::Undefined && maybe_value->valueType() != ValueType::InstPtr)
13✔
97
                            fmt::println(
20✔
98
                                m_os,
10✔
99
                                "{}",
10✔
100
                                fmt::styled(
20✔
101
                                    maybe_value->toString(vm),
10✔
102
                                    m_colorize ? fmt::fg(fmt::color::chocolate) : fmt::text_style()));
10✔
103
                    }
13✔
104
                }
13✔
105
            }
13✔
106
            else
107
                break;
10✔
108
        }
23✔
109

110
        context.locals.pop_back();
15✔
111

5✔
112
        // we do not want to retain code from the past executions
113
        m_code.clear();
10✔
114
        m_line_count = 0;
10✔
115

116
        // we hit a HALT instruction that set 'running' to false, ignore that if we were still running!
117
        vm.m_running = is_vm_running;
15✔
118
        m_running = false;
10✔
119
    }
10✔
120

121
    void Debugger::showContext(const VM& vm, const ExecutionContext& context) const
9✔
122
    {
9✔
123
        // show the line where the breakpoint hit
124
        const auto maybe_source_loc = vm.findSourceLocation(context.ip, context.pp);
9✔
125
        if (maybe_source_loc)
9✔
126
        {
127
            const auto filename = vm.m_state.m_filenames[maybe_source_loc->filename_id];
9✔
128

129
            if (Utils::fileExists(filename))
9✔
130
            {
131
                fmt::println(m_os, "");
9✔
132
                Diagnostics::makeContext(
18✔
133
                    Diagnostics::ErrorLocation {
18✔
134
                        .filename = filename,
9✔
135
                        .start = FilePos { .line = maybe_source_loc->line, .column = 0 },
9✔
136
                        .end = std::nullopt,
9✔
137
                        .maybe_content = std::nullopt },
9✔
138
                    m_os,
9✔
139
                    /* maybe_context= */ std::nullopt,
9✔
140
                    /* colorize= */ m_colorize);
9✔
141
                fmt::println(m_os, "");
9✔
142
            }
9✔
143
        }
9✔
144
    }
9✔
145

146
    std::optional<std::string> Debugger::prompt(const std::size_t ip, const std::size_t pp)
23✔
147
    {
23✔
148
        std::string code;
23✔
149
        long open_parens = 0;
23✔
150
        long open_braces = 0;
23✔
151

152
        while (true)
24✔
153
        {
154
            const bool unfinished_block = open_parens != 0 || open_braces != 0;
24✔
155
            fmt::print(
72✔
156
                m_os,
24✔
157
                "dbg[{},{}]:{:0>3}{} ",
24✔
158
                fmt::format("pp:{}", fmt::styled(pp, m_colorize ? fmt::fg(fmt::color::green) : fmt::text_style())),
24✔
159
                fmt::format("ip:{}", fmt::styled(ip, m_colorize ? fmt::fg(fmt::color::cyan) : fmt::text_style())),
24✔
160
                m_line_count,
24✔
161
                unfinished_block ? ":" : ">");
24✔
162

163
            std::string line;
24✔
164
            if (m_prompt_stream)
24✔
165
            {
166
                std::getline(*m_prompt_stream, line);
24✔
167
                fmt::println(m_os, "{}", line);  // because nothing is printed otherwise, and prompts get printed on the same line
24✔
168
            }
24✔
169
            else
170
                std::getline(std::cin, line);
×
171

172
            Utils::trimWhitespace(line);
24✔
173

174
            if (line == "c" || line == "continue" || line.empty())
24✔
175
            {
176
                fmt::println(m_os, "dbg: continue");
9✔
177
                return std::nullopt;
9✔
178
            }
179
            else if (line == "q" || line == "quit")
15✔
180
            {
181
                fmt::println(m_os, "dbg: stop");
1✔
182
                m_quit_vm = true;
1✔
183
                return std::nullopt;
1✔
184
            }
185
            else if (line == "help")
14✔
186
            {
187
                fmt::println(m_os, "Available commands:");
1✔
188
                fmt::println(m_os, "  help -- display this message");
1✔
189
                fmt::println(m_os, "  c, continue -- resume execution");
1✔
190
                fmt::println(m_os, "  q, quit -- quit the debugger, stopping the script execution");
1✔
191
            }
1✔
192
            else
193
            {
194
                code += line;
13✔
195

196
                open_parens += Utils::countOpenEnclosures(line, '(', ')');
13✔
197
                open_braces += Utils::countOpenEnclosures(line, '{', '}');
13✔
198

199
                ++m_line_count;
13✔
200
                if (open_braces == 0 && open_parens == 0)
13✔
201
                    break;
13✔
202
            }
203
        }
24✔
204

205
        return code;
13✔
206
    }
23✔
207

208
    std::optional<CompiledPrompt> Debugger::compile(const std::string& code, const std::size_t start_page_at_offset) const
13✔
209
    {
13✔
210
        Welder welder(0, m_libenv, DefaultFeatures);
13✔
211
        if (!welder.computeASTFromStringWithKnownSymbols(code, m_symbols))
13✔
212
            return std::nullopt;
×
213
        if (!welder.generateBytecodeUsingTables(m_symbols, m_constants, start_page_at_offset))
13✔
214
            return std::nullopt;
×
215

216
        BytecodeReader bcr;
13✔
217
        bcr.feed(welder.bytecode());
13✔
218
        const auto syms = bcr.symbols();
13✔
219
        const auto vals = bcr.values(syms);
13✔
220
        const auto files = bcr.filenames(vals);
13✔
221
        const auto inst_locs = bcr.instLocations(files);
13✔
222
        const auto [pages, _] = bcr.code(inst_locs);
13✔
223

224
        return std::optional(CompiledPrompt(pages, syms.symbols, vals.values));
26✔
225
    }
13✔
226
}
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