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

ArkScript-lang / Ark / 22823553555

08 Mar 2026 02:59PM UTC coverage: 93.335% (-0.2%) from 93.519%
22823553555

Pull #654

github

web-flow
Merge a9dde3b83 into caed4a158
Pull Request #654: Feat/show deprecation warnings

5 of 30 new or added lines in 5 files covered. (16.67%)

36 existing lines in 3 files now uncovered.

9438 of 10112 relevant lines covered (93.33%)

271685.22 hits per line

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

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

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

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

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

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

48
        m_bytecode = bytecode;
281✔
49

50
        try
51
        {
52
            configure(bcr);
281✔
53
            return true;
279✔
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
    }
285✔
64

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

71
        if (!welder.computeASTFromFile(file))
387✔
UNCOV
72
            return false;
×
73
        if (!welder.generateBytecode())
308✔
UNCOV
74
            return false;
×
75

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

82
        return true;
265✔
83
    }
387✔
84

85
    bool State::doFile(const std::string& file_path, const uint16_t features)
387✔
86
    {
387✔
87
        m_features = features;
387✔
88

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

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

107
            if (!exists(cache_directory) && (m_features & DisableCache) == 0)
387✔
108
            {
109
                try
110
                {
111
                    create_directory(cache_directory);
18✔
112
                }
18✔
113
                catch (const std::filesystem::filesystem_error&)
114
                {
UNCOV
115
                    m_features |= DisableCache;
×
116
                }
18✔
117
            }
18✔
118

119
            if (compile(file_path, bytecode_path))
387✔
120
                return true;
265✔
121
        }
387✔
122
        else if (feed(bytecode))  // it's a bytecode file
×
UNCOV
123
            return true;
×
UNCOV
124
        return false;
×
125
    }
509✔
126

127
    bool State::doString(const std::string& code, const uint16_t features)
13✔
128
    {
13✔
129
        m_features = features;
13✔
130

131
        Welder welder(m_debug_level, m_libenv, m_features);
13✔
132
        for (const auto& p : m_bound)
47✔
133
            welder.registerSymbol(p.first);
34✔
134

135
        if (!welder.computeASTFromString(code))
13✔
136
            return false;
×
137
        if (!welder.generateBytecode())
12✔
UNCOV
138
            return false;
×
139
        return feed(welder.bytecode());
12✔
140
    }
13✔
141

142
    void State::loadFunction(const std::string& name, Procedure::CallbackType&& function) noexcept
14✔
143
    {
14✔
144
        m_bound[name] = Value(std::move(function));
14✔
145
    }
14✔
146

147
    void State::setArgs(const std::vector<std::string>& args) noexcept
10✔
148
    {
10✔
149
        Value val(ValueType::List);
10✔
150
        std::ranges::transform(args, std::back_inserter(val.list()), [](const std::string& arg) {
13✔
151
            return Value(arg);
3✔
152
        });
153

154
        m_bound[std::string(internal::Language::SysArgs)] = val;
10✔
155
    }
10✔
156

157
    void State::setDebug(const unsigned level) noexcept
1✔
158
    {
1✔
159
        m_debug_level = level;
1✔
160
    }
1✔
161

UNCOV
162
    void State::setLibDirs(const std::vector<std::filesystem::path>& libenv) noexcept
×
UNCOV
163
    {
×
UNCOV
164
        m_libenv = libenv;
×
UNCOV
165
    }
×
166

167
    void State::configure(const BytecodeReader& bcr)
281✔
168
    {
281✔
169
        using namespace internal;
170

171
        const auto [major, minor, patch] = bcr.version();
281✔
172
        if (major != ARK_VERSION_MAJOR)
281✔
173
        {
174
            const std::string str_version = fmt::format("{}.{}.{}", major, minor, patch);
2✔
175
            throwStateError(fmt::format("Compiler and VM versions don't match: got {} while running {}", str_version, ARK_VERSION));
1✔
176
        }
1✔
177

178
        const auto bytecode_hash = bcr.sha256();
280✔
179

180
        std::vector<unsigned char> hash(picosha2::k_digest_size);
280✔
181
        picosha2::hash256(m_bytecode.begin() + bytecode::HeaderSize + picosha2::k_digest_size, m_bytecode.end(), hash);
280✔
182
        // checking integrity
183
        for (std::size_t j = 0; j < picosha2::k_digest_size; ++j)
9,211✔
184
        {
185
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
186
            if (hash[j] != bytecode_hash[j])
8,931✔
187
                throwStateError("Integrity check failed");
1✔
188
#endif
189
        }
8,930✔
190

191
        const auto syms = bcr.symbols();
279✔
192
        const auto vals = bcr.values(syms);
279✔
193
        const auto files = bcr.filenames(vals);
279✔
194
        const auto inst_locs = bcr.instLocations(files);
279✔
195
        const auto [pages, _] = bcr.code(inst_locs);
837✔
196

197
        m_symbols = syms.symbols;
279✔
198
        m_constants = vals.values;
279✔
199
        m_filenames = files.filenames;
279✔
200
        m_inst_locations = inst_locs.locations;
279✔
201
        m_pages = pages;
279✔
202
        m_max_page_size = maxPageSize(m_pages);
279✔
203

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

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

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

226
        m_bound[std::string(internal::Language::SysProgramName)] = Value("");
×
UNCOV
227
    }
×
228

229
    void State::addPagesToContiguousBytecode(const std::vector<bytecode_t>& pages, const std::size_t start)
307✔
230
    {
307✔
231
        for (std::size_t i = 0, end = pages.size(); i < end; ++i)
4,387✔
232
        {
233
            for (std::size_t j = 0, end_j = pages[i].size(); j < end_j; ++j)
410,528✔
234
                m_code[(start + i) * m_max_page_size + j] = pages[i][j];
406,448✔
235
        }
4,080✔
236
    }
307✔
237

238
    std::size_t State::maxPageSize(const std::vector<bytecode_t>& pages)
293✔
239
    {
293✔
240
        return std::ranges::max(pages, {}, &bytecode_t::size).size();
293✔
241
    }
242

243
    void State::extendBytecode(const std::vector<bytecode_t>& pages, const std::vector<std::string>& symbols, const std::vector<Value>& constants)
14✔
244
    {
14✔
245
        m_symbols = symbols;
14✔
246
        m_constants = constants;
14✔
247

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

251
        m_code.resize(m_max_page_size * (m_pages.size() + pages.size()), internal::Instruction::NOP);
14✔
252
        addPagesToContiguousBytecode(m_pages, /* start= */ 0);
14✔
253
        addPagesToContiguousBytecode(pages, /* start= */ m_pages.size());
14✔
254
    }
14✔
255
}
256

257
#ifdef _MSC_VER
258
#    pragma warning(pop)
259
#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