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

ArkScript-lang / Ark / 20948190269

13 Jan 2026 07:19AM UTC coverage: 92.246% (-0.5%) from 92.743%
20948190269

Pull #628

github

web-flow
Merge 59ad4b9eb into ada0e0686
Pull Request #628: Feat/debugger

52 of 115 new or added lines in 6 files covered. (45.22%)

2 existing lines in 1 file now uncovered.

8506 of 9221 relevant lines covered (92.25%)

279965.56 hits per line

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

79.19
/src/arkreactor/State.cpp
1
#include <Ark/State.hpp>
2

3
#include <Ark/Constants.hpp>
4
#include <Ark/Utils/Files.hpp>
5
#include <Ark/Compiler/Welder.hpp>
6

7
#ifdef _MSC_VER
8
#    pragma warning(push)
9
#    pragma warning(disable : 4996)
10
#endif
11

12
#include <Proxy/Picosha2.hpp>
13
#include <Ark/Compiler/BytecodeReader.hpp>
14
#include <fmt/core.h>
15
#include <fmt/color.h>
16

17
namespace Ark
18
{
19
    State::State(const std::vector<std::filesystem::path>& libenv) noexcept :
999✔
20
        m_debug_level(0),
333✔
21
        m_features(0),
333✔
22
        m_libenv(libenv),
333✔
23
        m_filename(ARK_NO_NAME_FILE),
333✔
24
        m_max_page_size(0)
333✔
25
    {
333✔
26
        // default value for builtin__sys:args is empty list
27
        const Value val(ValueType::List);
333✔
28
        m_bound[std::string(internal::Language::SysArgs)] = val;
333✔
29

30
        m_bound[std::string(internal::Language::SysProgramName)] = Value("");
333✔
31
    }
333✔
32

33
    bool State::feed(const std::string& bytecode_filename, const bool fail_with_exception)
201✔
34
    {
201✔
35
        if (!Utils::fileExists(bytecode_filename))
201✔
36
            return false;
×
37

38
        return feed(Utils::readFileAsBytes(bytecode_filename), fail_with_exception);
201✔
39
    }
201✔
40

41
    bool State::feed(const bytecode_t& bytecode, const bool fail_with_exception)
215✔
42
    {
215✔
43
        BytecodeReader bcr;
215✔
44
        bcr.feed(bytecode);
215✔
45
        if (!bcr.checkMagic())
215✔
46
            return false;
×
47

48
        m_bytecode = bytecode;
215✔
49

50
        try
51
        {
52
            configure(bcr);
215✔
53
            return true;
213✔
54
        }
2✔
55
        catch (const std::exception& e)
56
        {
57
            if (fail_with_exception)
2✔
58
                throw;
2✔
59

60
            fmt::println("{}", e.what());
×
61
            return false;
×
62
        }
2✔
63
    }
219✔
64

65
    bool State::compile(const std::string& file, const std::string& output) const
312✔
66
    {
312✔
67
        Welder welder(m_debug_level, m_libenv, m_features);
312✔
68
        for (const auto& key : m_bound | std::views::keys)
936✔
69
            welder.registerSymbol(key);
624✔
70

71
        if (!welder.computeASTFromFile(file))
312✔
72
            return false;
×
73
        if (!welder.generateBytecode())
238✔
74
            return false;
×
75

76
        const std::string destination = output.empty() ? (file.substr(0, file.find_last_of('.')) + ".arkc") : output;
201✔
77
        if (!welder.saveBytecodeToFile(destination))
201✔
78
            return false;
×
79

80
        return true;
201✔
81
    }
312✔
82

83
    bool State::doFile(const std::string& file_path, const uint16_t features)
312✔
84
    {
312✔
85
        m_features = features;
312✔
86

87
        if (!Utils::fileExists(file_path))
312✔
88
        {
89
            fmt::print(fmt::fg(fmt::color::red), "Can not find file '{}'\n", file_path);
×
90
            return false;
×
91
        }
92
        m_filename = file_path;
312✔
93
        m_bound[std::string(internal::Language::SysProgramName)] = Value(std::filesystem::path(m_filename).filename().string());
423✔
94

95
        const bytecode_t bytecode = Utils::readFileAsBytes(file_path);
312✔
96
        BytecodeReader bcr;
312✔
97
        bcr.feed(bytecode);
312✔
98
        if (!bcr.checkMagic())  // couldn't read magic number, it's a source file
312✔
99
        {
100
            // check if it's in the arkscript cache
101
            const std::string filename = std::filesystem::path(file_path).filename().replace_extension(".arkc").string();
312✔
102
            const std::filesystem::path cache_directory = std::filesystem::path(file_path).parent_path() / ARK_CACHE_DIRNAME;
312✔
103
            const std::string bytecode_path = (cache_directory / filename).string();
312✔
104

105
            if (!exists(cache_directory))
312✔
106
                create_directory(cache_directory);
17✔
107

108
            if (compile(file_path, bytecode_path) && feed(bytecode_path))
312✔
109
                return true;
201✔
110
        }
312✔
111
        else if (feed(bytecode))  // it's a bytecode file
×
112
            return true;
×
113
        return false;
×
114
    }
423✔
115

116
    bool State::doString(const std::string& code, const uint16_t features)
12✔
117
    {
12✔
118
        m_features = features;
12✔
119

120
        Welder welder(m_debug_level, m_libenv, m_features);
12✔
121
        for (const auto& p : m_bound)
44✔
122
            welder.registerSymbol(p.first);
32✔
123

124
        if (!welder.computeASTFromString(code))
12✔
125
            return false;
×
126
        if (!welder.generateBytecode())
12✔
127
            return false;
×
128
        return feed(welder.bytecode());
12✔
129
    }
12✔
130

131
    void State::loadFunction(const std::string& name, Procedure::CallbackType&& function) noexcept
8✔
132
    {
8✔
133
        m_bound[name] = Value(std::move(function));
8✔
134
    }
8✔
135

136
    void State::setArgs(const std::vector<std::string>& args) noexcept
10✔
137
    {
10✔
138
        Value val(ValueType::List);
10✔
139
        std::ranges::transform(args, std::back_inserter(val.list()), [](const std::string& arg) {
13✔
140
            return Value(arg);
3✔
141
        });
142

143
        m_bound[std::string(internal::Language::SysArgs)] = val;
10✔
144
    }
10✔
145

146
    void State::setDebug(const unsigned level) noexcept
1✔
147
    {
1✔
148
        m_debug_level = level;
1✔
149
    }
1✔
150

151
    void State::setLibDirs(const std::vector<std::filesystem::path>& libenv) noexcept
×
152
    {
×
153
        m_libenv = libenv;
×
154
    }
×
155

156
    void State::configure(const BytecodeReader& bcr)
215✔
157
    {
215✔
158
        using namespace internal;
159

160
        const auto [major, minor, patch] = bcr.version();
215✔
161
        if (major != ARK_VERSION_MAJOR)
215✔
162
        {
163
            const std::string str_version = fmt::format("{}.{}.{}", major, minor, patch);
2✔
164
            throwStateError(fmt::format("Compiler and VM versions don't match: got {} while running {}", str_version, ARK_VERSION));
1✔
165
        }
1✔
166

167
        const auto bytecode_hash = bcr.sha256();
214✔
168

169
        std::vector<unsigned char> hash(picosha2::k_digest_size);
214✔
170
        picosha2::hash256(m_bytecode.begin() + bytecode::HeaderSize + picosha2::k_digest_size, m_bytecode.end(), hash);
214✔
171
        // checking integrity
172
        for (std::size_t j = 0; j < picosha2::k_digest_size; ++j)
7,033✔
173
        {
174
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
175
            if (hash[j] != bytecode_hash[j])
6,819✔
176
                throwStateError("Integrity check failed");
1✔
177
#endif
178
        }
6,818✔
179

180
        const auto syms = bcr.symbols();
213✔
181
        const auto vals = bcr.values(syms);
213✔
182
        const auto files = bcr.filenames(vals);
213✔
183
        const auto inst_locs = bcr.instLocations(files);
213✔
184
        const auto [pages, _] = bcr.code(inst_locs);
375,103✔
185

186
        m_symbols = syms.symbols;
213✔
187
        m_constants = vals.values;
213✔
188
        m_filenames = files.filenames;
213✔
189
        m_inst_locations = inst_locs.locations;
213✔
190

191
        m_max_page_size = 0;
213✔
192
        for (const bytecode_t& page : pages)
4,374✔
193
        {
194
            if (page.size() > m_max_page_size)
3,948✔
195
                m_max_page_size = page.size();
241✔
196
        }
3,948✔
197

198
        // Make m_code as a big contiguous chunk of instructions,
199
        // aligned on the biggest page size.
200
        // This might have a downside when we have a single big page and
201
        // a bunch of smaller ones, though I couldn't measure it while testing.
202
        m_code.resize(m_max_page_size * pages.size(), Instruction::NOP);
213✔
203
        for (std::size_t i = 0, end = pages.size(); i < end; ++i)
4,374✔
204
        {
205
            for (std::size_t j = 0, end_j = pages[i].size(); j < end_j; ++j)
378,412✔
206
                m_code[i * m_max_page_size + j] = pages[i][j];
370,516✔
207
        }
3,948✔
208
    }
217✔
209

210
    void State::reset() noexcept
×
211
    {
×
212
        m_symbols.clear();
×
213
        m_constants.clear();
×
214
        m_filenames.clear();
×
215
        m_inst_locations.clear();
×
216
        m_max_page_size = 0;
×
217
        m_code.clear();
×
NEW
218
        m_bound.clear();
×
219

220
        // default value for builtin__sys:args is empty list
221
        const Value val(ValueType::List);
×
NEW
222
        m_bound[std::string(internal::Language::SysArgs)] = val;
×
223

NEW
224
        m_bound[std::string(internal::Language::SysProgramName)] = Value("");
×
225
    }
×
226
}
227

228
#ifdef _MSC_VER
229
#    pragma warning(pop)
230
#endif
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