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

ArkScript-lang / Ark / 28841712321

07 Jul 2026 04:32AM UTC coverage: 94.381% (+0.03%) from 94.349%
28841712321

push

github

SuperFola
chore(ci): ignore leaks when copying a ClosureScope

10313 of 10927 relevant lines covered (94.38%)

991593.76 hits per line

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

77.33
/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 :
1,506✔
20
        m_debug_level(0),
502✔
21
        m_features(0),
502✔
22
        m_libenv(libenv),
502✔
23
        m_filename(ARK_NO_NAME_FILE),
502✔
24
        m_max_page_size(0)
502✔
25
    {
502✔
26
        // default value for builtin__sys:args is empty list
27
        const Value val(ValueType::List);
502✔
28
        m_bound[std::string(internal::Language::SysArgs)] = val;
502✔
29
        m_bound[std::string(internal::Language::SysProgramName)] = Value("");
502✔
30
    }
502✔
31

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

37
        return feed(Utils::readFileAsBytes(bytecode_filename), fail_with_exception);
×
38
    }
×
39

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

47
        m_bytecode = bytecode;
361✔
48

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

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

64
    bool State::compile(const std::string& file, const std::string& output, std::ostream* stream)
478✔
65
    {
478✔
66
        Welder welder(m_debug_level, m_libenv, m_features);
478✔
67
        if (stream != nullptr)
478✔
68
            welder.redirectLogsTo(*stream);
265✔
69

70
        for (const auto& key : m_bound | std::views::keys)
1,441✔
71
            welder.registerSymbol(key);
963✔
72

73
        if (!welder.computeASTFromFile(file))
478✔
74
            return false;
×
75
        if (!welder.generateBytecode())
397✔
76
            return false;
×
77

78
        const std::string destination = output.empty() ? (file.substr(0, file.find_last_of('.')) + ".arkc") : output;
345✔
79
        if ((m_features & DisableCache) == 0 && !welder.saveBytecodeToFile(destination))
345✔
80
            return false;
×
81
        if (!feed(welder.bytecode()))
345✔
82
            return false;
×
83

84
        return true;
345✔
85
    }
478✔
86

87
    bool State::doFile(const std::string& file_path, const uint16_t features, std::ostream* stream)
478✔
88
    {
478✔
89
        m_features = features;
478✔
90

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

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

109
            if (!exists(cache_directory) && (m_features & DisableCache) == 0)
478✔
110
            {
111
                try
112
                {
113
                    create_directory(cache_directory);
20✔
114
                }
20✔
115
                catch (const std::filesystem::filesystem_error&)
116
                {
117
                    m_features |= DisableCache;
×
118
                }
20✔
119
            }
20✔
120

121
            if (compile(file_path, bytecode_path, stream))
478✔
122
                return true;
345✔
123
        }
478✔
124
        else if (feed(bytecode))  // it's a bytecode file
×
125
            return true;
×
126
        return false;
×
127
    }
611✔
128

129
    bool State::doString(const std::string& code, const uint16_t features, std::ostream* stream)
13✔
130
    {
13✔
131
        m_features = features;
13✔
132

133
        Welder welder(m_debug_level, m_libenv, m_features);
13✔
134
        if (stream != nullptr)
13✔
135
            welder.redirectLogsTo(*stream);
×
136

137
        for (const auto& p : m_bound)
47✔
138
            welder.registerSymbol(p.first);
34✔
139

140
        if (!welder.computeASTFromString(code))
13✔
141
            return false;
×
142
        if (!welder.generateBytecode())
12✔
143
            return false;
×
144
        return feed(welder.bytecode());
12✔
145
    }
13✔
146

147
    void State::loadFunction(const std::string& name, Procedure::CallbackType&& function) noexcept
15✔
148
    {
15✔
149
        m_bound[name] = Value(std::move(function));
15✔
150
    }
15✔
151

152
    void State::setArgs(const std::vector<std::string>& args) noexcept
11✔
153
    {
11✔
154
        Value val(ValueType::List);
11✔
155
        std::ranges::transform(args, std::back_inserter(val.list()), [](const std::string& arg) {
14✔
156
            return Value(arg);
3✔
157
        });
158

159
        m_bound[std::string(internal::Language::SysArgs)] = val;
11✔
160
    }
11✔
161

162
    void State::setDebug(const unsigned level) noexcept
1✔
163
    {
1✔
164
        m_debug_level = level;
1✔
165
    }
1✔
166

167
    void State::setLibDirs(const std::vector<std::filesystem::path>& libenv) noexcept
×
168
    {
×
169
        m_libenv = libenv;
×
170
    }
×
171

172
    void State::configure(const BytecodeReader& bcr)
361✔
173
    {
361✔
174
        using namespace internal;
175

176
        const auto [major, minor, patch] = bcr.version();
361✔
177
        if (major != ARK_VERSION_MAJOR)
361✔
178
        {
179
            const std::string str_version = fmt::format("{}.{}.{}", major, minor, patch);
2✔
180
            throwStateError(fmt::format("Compiler and VM versions don't match: got {} while running {}", str_version, ARK_VERSION));
1✔
181
        }
1✔
182

183
        const auto bytecode_hash = bcr.sha256();
360✔
184

185
        std::vector<unsigned char> hash(picosha2::k_digest_size);
360✔
186
        picosha2::hash256(m_bytecode.begin() + bytecode::HeaderSize + picosha2::k_digest_size, m_bytecode.end(), hash);
360✔
187
        // checking integrity
188
        for (std::size_t j = 0; j < picosha2::k_digest_size; ++j)
11,851✔
189
        {
190
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
191
            if (hash[j] != bytecode_hash[j])
11,491✔
192
                throwStateError("Integrity check failed");
1✔
193
#endif
194
        }
11,490✔
195

196
        const auto syms = bcr.symbols();
359✔
197
        const auto vals = bcr.values(syms);
359✔
198
        const auto files = bcr.filenames(vals);
359✔
199
        const auto inst_locs = bcr.instLocations(files);
359✔
200
        const auto [pages, _] = bcr.code(inst_locs);
1,077✔
201

202
        m_symbols = syms.symbols;
359✔
203
        m_constants = vals.values;
359✔
204
        m_filenames = files.filenames;
359✔
205
        m_inst_locations = inst_locs.locations;
359✔
206
        m_pages = pages;
359✔
207
        m_max_page_size = maxPageSize(m_pages);
359✔
208

209
        // Make m_code as a big contiguous chunk of instructions,
210
        // aligned on the biggest page size.
211
        // This might have a downside when we have a single big page and
212
        // a bunch of smaller ones, though I couldn't measure it while testing.
213
        m_code.resize(m_max_page_size * pages.size(), Instruction::NOP);
359✔
214
        addPagesToContiguousBytecode(pages, /* start= */ 0);
359✔
215
    }
363✔
216

217
    void State::reset() noexcept
×
218
    {
×
219
        m_symbols.clear();
×
220
        m_constants.clear();
×
221
        m_filenames.clear();
×
222
        m_inst_locations.clear();
×
223
        m_max_page_size = 0;
×
224
        m_code.clear();
×
225
        m_bound.clear();
×
226

227
        // default value for builtin__sys:args is empty list
228
        const Value val(ValueType::List);
×
229
        m_bound[std::string(internal::Language::SysArgs)] = val;
×
230
        m_bound[std::string(internal::Language::SysProgramName)] = Value("");
×
231
    }
×
232

233
    void State::addPagesToContiguousBytecode(const std::vector<bytecode_t>& pages, const std::size_t start)
387✔
234
    {
387✔
235
        for (std::size_t i = 0, end = pages.size(); i < end; ++i)
8,356✔
236
        {
237
            for (std::size_t j = 0, end_j = pages[i].size(); j < end_j; ++j)
824,945✔
238
                m_code[(start + i) * m_max_page_size + j] = pages[i][j];
816,976✔
239
        }
7,969✔
240
    }
387✔
241

242
    std::size_t State::maxPageSize(const std::vector<bytecode_t>& pages)
373✔
243
    {
373✔
244
        return std::ranges::max(pages, {}, &bytecode_t::size).size();
373✔
245
    }
246

247
    void State::extendBytecode(const std::vector<bytecode_t>& pages, const std::vector<std::string>& symbols, const std::vector<Value>& constants)
14✔
248
    {
14✔
249
        m_symbols = symbols;
14✔
250
        m_constants = constants;
14✔
251

252
        // do not modify m_pages so that we can start over
253
        m_max_page_size = std::max(m_max_page_size, maxPageSize(pages));
14✔
254

255
        m_code.resize(m_max_page_size * (m_pages.size() + pages.size()), internal::Instruction::NOP);
14✔
256
        addPagesToContiguousBytecode(m_pages, /* start= */ 0);
14✔
257
        addPagesToContiguousBytecode(pages, /* start= */ m_pages.size());
14✔
258
    }
14✔
259
}
260

261
#ifdef _MSC_VER
262
#    pragma warning(pop)
263
#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