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

ArkScript-lang / Ark / 16124772858

07 Jul 2025 06:21PM UTC coverage: 86.675% (+0.01%) from 86.665%
16124772858

push

github

SuperFola
feat(vm): make the bytecode contiguous in memory to remove an indirection when fetching instructions

This is also way more cache friendly and should remove cache misses.

24 of 26 new or added lines in 4 files covered. (92.31%)

1 existing line in 1 file now uncovered.

7292 of 8413 relevant lines covered (86.68%)

113275.92 hits per line

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

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

3
#include <Ark/Constants.hpp>
4
#include <Ark/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 :
798✔
20
        m_debug_level(0),
266✔
21
        m_libenv(libenv),
266✔
22
        m_filename(ARK_NO_NAME_FILE),
266✔
23
        m_max_page_size(0)
266✔
24
    {
266✔
25
        // default value for builtin__sys:args is empty list
26
        const Value val(ValueType::List);
266✔
27
        m_binded[std::string(internal::Language::SysArgs)] = val;
266✔
28
    }
266✔
29

30
    bool State::feed(const std::string& bytecode_filename)
161✔
31
    {
161✔
32
        if (!Utils::fileExists(bytecode_filename))
161✔
33
            return false;
×
34

35
        return feed(Utils::readFileAsBytes(bytecode_filename));
161✔
36
    }
161✔
37

38
    bool State::feed(const bytecode_t& bytecode)
170✔
39
    {
170✔
40
        BytecodeReader bcr;
170✔
41
        bcr.feed(bytecode);
170✔
42
        if (!bcr.checkMagic())
170✔
43
            return false;
×
44

45
        m_bytecode = bytecode;
170✔
46

47
        try
48
        {
49
            configure(bcr);
170✔
50
            return true;
170✔
51
        }
×
52
        catch (const std::exception& e)  // FIXME I don't like this shit
53
        {
54
            fmt::println("{}", e.what());
×
55
            return false;
×
56
        }
×
57
    }
170✔
58

59
    bool State::compile(const std::string& file, const std::string& output, const uint16_t features) const
257✔
60
    {
257✔
61
        Welder welder(m_debug_level, m_libenv, features);
257✔
62
        for (const auto& p : m_binded)
514✔
63
            welder.registerSymbol(p.first);
257✔
64

65
        if (!welder.computeASTFromFile(file))
257✔
66
            return false;
×
67
        if (!welder.generateBytecode())
195✔
68
            return false;
×
69

70
        const std::string destination = output.empty() ? (file.substr(0, file.find_last_of('.')) + ".arkc") : output;
161✔
71
        if (!welder.saveBytecodeToFile(destination))
161✔
72
            return false;
×
73

74
        return true;
161✔
75
    }
257✔
76

77
    bool State::doFile(const std::string& file, const uint16_t features)
257✔
78
    {
257✔
79
        if (!Utils::fileExists(file))
257✔
80
        {
81
            fmt::print(fmt::fg(fmt::color::red), "Can not find file '{}'\n", file);
×
82
            return false;
×
83
        }
84
        m_filename = file;
257✔
85

86
        const bytecode_t bytecode = Utils::readFileAsBytes(file);
257✔
87
        BytecodeReader bcr;
257✔
88
        bcr.feed(bytecode);
257✔
89
        if (!bcr.checkMagic())  // couldn't read magic number, it's a source file
257✔
90
        {
91
            // check if it's in the arkscript cache
92
            const std::string short_filename = (std::filesystem::path(file)).filename().string();
257✔
93
            const std::string filename = short_filename.substr(0, short_filename.find_last_of('.')) + ".arkc";
257✔
94
            const std::filesystem::path directory = (std::filesystem::path(file)).parent_path() / ARK_CACHE_DIRNAME;
257✔
95
            const std::string path = (directory / filename).string();
257✔
96

97
            if (!exists(directory))  // create ark cache directory
257✔
98
                create_directory(directory);
15✔
99

100
            if (compile(file, path, features) && feed(path))
257✔
101
                return true;
161✔
102
        }
257✔
103
        else if (feed(bytecode))  // it's a bytecode file
×
104
            return true;
×
105
        return false;
×
106
    }
353✔
107

108
    bool State::doString(const std::string& code, const uint16_t features)
9✔
109
    {
9✔
110
        Welder welder(m_debug_level, m_libenv, features);
9✔
111
        for (const auto& p : m_binded)
24✔
112
            welder.registerSymbol(p.first);
15✔
113

114
        if (!welder.computeASTFromString(code))
9✔
115
            return false;
×
116
        if (!welder.generateBytecode())
9✔
117
            return false;
×
118
        return feed(welder.bytecode());
9✔
119
    }
9✔
120

121
    void State::loadFunction(const std::string& name, Procedure::CallbackType&& function) noexcept
6✔
122
    {
6✔
123
        m_binded[name] = Value(std::move(function));
6✔
124
    }
6✔
125

126
    void State::setArgs(const std::vector<std::string>& args) noexcept
10✔
127
    {
10✔
128
        Value val(ValueType::List);
10✔
129
        std::ranges::transform(args, std::back_inserter(val.list()), [](const std::string& arg) {
13✔
130
            return Value(arg);
3✔
131
        });
132

133
        m_binded[std::string(internal::Language::SysArgs)] = val;
10✔
134
    }
10✔
135

136
    void State::setDebug(const unsigned level) noexcept
×
137
    {
×
138
        m_debug_level = level;
×
139
    }
×
140

141
    void State::setLibDirs(const std::vector<std::filesystem::path>& libenv) noexcept
×
142
    {
×
143
        m_libenv = libenv;
×
144
    }
×
145

146
    void State::configure(const BytecodeReader& bcr)
170✔
147
    {
170✔
148
        using namespace internal;
149

150
        const auto [major, minor, patch] = bcr.version();
170✔
151
        if (major != ARK_VERSION_MAJOR)
170✔
152
        {
153
            std::string str_version = std::to_string(major) + "." +
×
154
                std::to_string(minor) + "." +
×
155
                std::to_string(patch);
×
156
            throwStateError(fmt::format("Compiler and VM versions don't match: got {} while running {}", str_version, ARK_VERSION));
×
157
        }
×
158

159
        const auto bytecode_hash = bcr.sha256();
170✔
160

161
        std::vector<unsigned char> hash(picosha2::k_digest_size);
170✔
162
        picosha2::hash256(m_bytecode.begin() + bytecode::HeaderSize + picosha2::k_digest_size, m_bytecode.end(), hash);
170✔
163
        // checking integrity
164
        for (std::size_t j = 0; j < picosha2::k_digest_size; ++j)
5,610✔
165
        {
166
#ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
167
            if (hash[j] != bytecode_hash[j])
5,440✔
168
                throwStateError("Integrity check failed");
×
169
#endif
170
        }
5,440✔
171

172
        const auto syms = bcr.symbols();
170✔
173
        const auto vals = bcr.values(syms);
170✔
174
        const auto files = bcr.filenames(vals);
170✔
175
        const auto inst_locs = bcr.instLocations(files);
170✔
176
        const auto [pages, _] = bcr.code(inst_locs);
190,564✔
177

178
        m_symbols = syms.symbols;
170✔
179
        m_constants = vals.values;
170✔
180
        m_filenames = files.filenames;
170✔
181
        m_inst_locations = inst_locs.locations;
170✔
182

183
        m_max_page_size = 0;
170✔
184
        for (const bytecode_t& page : pages)
2,658✔
185
        {
186
            if (page.size() > m_max_page_size)
2,318✔
187
                m_max_page_size = page.size();
189✔
188
        }
2,318✔
189

190
        // Make m_code as a big contiguous chunk of instructions,
191
        // aligned on the biggest page size.
192
        // This might have a downside when we have a single big page and
193
        // a bunch of smaller ones, though I couldn't measure it while testing.
194
        m_code.resize(m_max_page_size * pages.size(), Instruction::NOP);
170✔
195
        for (std::size_t i = 0, end = pages.size(); i < end; ++i)
2,658✔
196
        {
197
            for (std::size_t j = 0, end_j = pages[i].size(); j < end_j; ++j)
192,372✔
198
                m_code[i * m_max_page_size + j] = pages[i][j];
187,736✔
199
        }
2,318✔
200
    }
170✔
201

202
    void State::reset() noexcept
×
203
    {
×
204
        m_symbols.clear();
×
205
        m_constants.clear();
×
206
        m_filenames.clear();
×
207
        m_inst_locations.clear();
×
NEW
208
        m_max_page_size = 0;
×
NEW
209
        m_code.clear();
×
UNCOV
210
        m_binded.clear();
×
211

212
        // default value for builtin__sys:args is empty list
213
        const Value val(ValueType::List);
×
214
        m_binded[std::string(internal::Language::SysArgs)] = val;
×
215
    }
×
216
}
217

218
#ifdef _MSC_VER
219
#    pragma warning(pop)
220
#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