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

ArkScript-lang / Ark / 16859398604

10 Aug 2025 08:42AM UTC coverage: 86.849% (-0.02%) from 86.869%
16859398604

Pull #568

github

web-flow
Merge 5e848d20e into c65ea7e5b
Pull Request #568: feat(closures): captures are no longer fully qualified

83 of 104 new or added lines in 11 files covered. (79.81%)

6 existing lines in 2 files now uncovered.

7542 of 8684 relevant lines covered (86.85%)

125696.68 hits per line

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

94.2
/src/arkreactor/Compiler/IntermediateRepresentation/IRCompiler.cpp
1
#include <Ark/Compiler/IntermediateRepresentation/IRCompiler.hpp>
2

3
#include <chrono>
4
#include <utility>
5
#include <optional>
6
#include <unordered_map>
7
#include <Proxy/Picosha2.hpp>
8
#include <fmt/ostream.h>
9

10
#include <Ark/Constants.hpp>
11
#include <Ark/Utils/Literals.hpp>
12
#include <Ark/Compiler/IntermediateRepresentation/InstLoc.hpp>
13
#include <Ark/Compiler/Serialization/IntegerSerializer.hpp>
14
#include <Ark/Compiler/Serialization/IEEE754Serializer.hpp>
15

16
namespace Ark::internal
17
{
18
    using namespace literals;
19

20
    IRCompiler::IRCompiler(const unsigned debug) :
604✔
21
        m_logger("IRCompiler", debug)
302✔
22
    {}
604✔
23

24
    void IRCompiler::process(const std::vector<IR::Block>& pages, const std::vector<std::string>& symbols, const std::vector<ValTableElem>& values)
195✔
25
    {
195✔
26
        m_logger.traceStart("process");
195✔
27
        pushFileHeader();
195✔
28
        pushSymbolTable(symbols);
195✔
29
        pushValueTable(values);
195✔
30

31
        // compute a list of unique filenames
32
        for (const auto& page : pages)
2,737✔
33
        {
34
            for (const auto& inst : page)
62,372✔
35
            {
36
                if (std::ranges::find(m_filenames, inst.filename()) == m_filenames.end() && inst.hasValidSourceLocation())
59,830✔
37
                    m_filenames.push_back(inst.filename());
309✔
38
            }
59,830✔
39
        }
2,542✔
40

41
        pushFilenameTable();
195✔
42
        pushInstLocTable(pages);
195✔
43

44
        m_ir = pages;
195✔
45
        compile();
195✔
46

47
        if (m_ir.empty())
195✔
48
        {
49
            // code segment with a single instruction
50
            m_bytecode.push_back(CODE_SEGMENT_START);
×
51
            m_bytecode.push_back(0_u8);
×
52
            m_bytecode.push_back(1_u8);
×
53

54
            m_bytecode.push_back(0_u8);
×
55
            m_bytecode.push_back(HALT);
×
56
            m_bytecode.push_back(0_u8);
×
57
            m_bytecode.push_back(0_u8);
×
58
        }
×
59

60
        // generate a hash of the tables + bytecode
61
        std::vector<unsigned char> hash_out(picosha2::k_digest_size);
195✔
62
        picosha2::hash256(m_bytecode.begin() + bytecode::HeaderSize, m_bytecode.end(), hash_out);
195✔
63
        m_bytecode.insert(m_bytecode.begin() + bytecode::HeaderSize, hash_out.begin(), hash_out.end());
195✔
64

65
        m_logger.traceEnd();
195✔
66
    }
195✔
67

68
    void IRCompiler::dumpToStream(std::ostream& stream) const
15✔
69
    {
15✔
70
        std::size_t index = 0;
15✔
71
        for (const auto& block : m_ir)
55✔
72
        {
73
            fmt::println(stream, "page_{}", index);
40✔
74
            for (const auto& entity : block)
839✔
75
            {
76
                switch (entity.kind())
799✔
77
                {
126✔
78
                    case IR::Kind::Label:
79
                        fmt::println(stream, ".L{}:", entity.label());
126✔
80
                        break;
232✔
81

82
                    case IR::Kind::Goto:
83
                        fmt::println(stream, "\t{} L{}", InstructionNames[entity.inst()], entity.label());
106✔
84
                        break;
120✔
85

86
                    case IR::Kind::GotoWithArg:
87
                        fmt::println(stream, "\t{} L{}, {}", InstructionNames[entity.inst()], entity.label(), entity.primaryArg());
14✔
88
                        break;
459✔
89

90
                    case IR::Kind::Opcode:
91
                        fmt::println(stream, "\t{} {}", InstructionNames[entity.inst()], entity.primaryArg());
445✔
92
                        break;
553✔
93

94
                    case IR::Kind::Opcode2Args:
95
                        fmt::println(stream, "\t{} {}, {}", InstructionNames[entity.inst()], entity.primaryArg(), entity.secondaryArg());
108✔
96
                        break;
108✔
97
                }
799✔
98
            }
799✔
99

100
            fmt::println(stream, "");
40✔
101
            ++index;
40✔
102
        }
40✔
103
    }
15✔
104

105
    const bytecode_t& IRCompiler::bytecode() const noexcept
195✔
106
    {
195✔
107
        return m_bytecode;
195✔
108
    }
109

110
    void IRCompiler::compile()
195✔
111
    {
195✔
112
        // push the different code segments
113
        for (std::size_t i = 0, end = m_ir.size(); i < end; ++i)
2,737✔
114
        {
115
            IR::Block& page = m_ir[i];
2,542✔
116
            // just in case we got too far, always add a HALT to be sure the
117
            // VM won't do anything crazy
118
            page.emplace_back(HALT);
2,542✔
119

120
            // push number of elements
121
            const auto page_size = std::ranges::count_if(page, [](const auto& a) {
64,914✔
122
                return a.kind() != IR::Kind::Label;
62,372✔
123
            });
124
            if (std::cmp_greater(page_size, std::numeric_limits<uint16_t>::max()))
2,542✔
NEW
125
                throw std::overflow_error(fmt::format("Size of page {} exceeds the maximum size of 2^16 - 1", i));
×
126

127
            m_bytecode.push_back(CODE_SEGMENT_START);
2,542✔
128
            serializeOn2BytesToVecBE(page_size, m_bytecode);
2,542✔
129

130
            // register labels position
131
            uint16_t pos = 0;
2,542✔
132
            std::unordered_map<IR::label_t, uint16_t> label_to_position;
2,542✔
133
            for (auto& inst : page)
64,914✔
134
            {
135
                switch (inst.kind())
62,372✔
136
                {
9,531✔
137
                    case IR::Kind::Label:
138
                        label_to_position[inst.label()] = pos;
9,531✔
139
                        break;
62,372✔
140

141
                    default:
142
                        ++pos;
52,841✔
143
                }
62,372✔
144
            }
62,372✔
145

146
            for (auto& inst : page)
64,914✔
147
            {
148
                switch (inst.kind())
62,372✔
149
                {
8,281✔
150
                    case IR::Kind::Goto:
151
                        pushWord(Word(inst.inst(), label_to_position[inst.label()]));
8,281✔
152
                        break;
8,902✔
153

154
                    case IR::Kind::GotoWithArg:
155
                        pushWord(Word(inst.inst(), inst.primaryArg(), label_to_position[inst.label()]));
621✔
156
                        break;
44,560✔
157

158
                    case IR::Kind::Opcode:
159
                        [[fallthrough]];
160
                    case IR::Kind::Opcode2Args:
161
                        pushWord(inst.bytecode());
43,939✔
162
                        break;
53,470✔
163

164
                    default:
165
                        break;
9,531✔
166
                }
62,372✔
167
            }
62,372✔
168
        }
2,542✔
169
    }
195✔
170

171
    void IRCompiler::pushWord(const Word& word)
52,841✔
172
    {
52,841✔
173
        m_bytecode.push_back(word.opcode);
52,841✔
174
        m_bytecode.push_back(word.byte_1);
52,841✔
175
        m_bytecode.push_back(word.byte_2);
52,841✔
176
        m_bytecode.push_back(word.byte_3);
52,841✔
177
    }
52,841✔
178

179
    void IRCompiler::pushFileHeader() noexcept
195✔
180
    {
195✔
181
        /*
182
            Generating headers:
183
                - lang name (to be sure we are executing an ArkScript file)
184
                    on 4 bytes (ark + padding)
185
                - version (major: 2 bytes, minor: 2 bytes, patch: 2 bytes)
186
                - timestamp (8 bytes, unix format)
187
        */
188

189
        m_bytecode.push_back('a');
195✔
190
        m_bytecode.push_back('r');
195✔
191
        m_bytecode.push_back('k');
195✔
192
        m_bytecode.push_back(0_u8);
195✔
193

194
        // push version
195
        for (const int n : std::array { ARK_VERSION_MAJOR, ARK_VERSION_MINOR, ARK_VERSION_PATCH })
780✔
196
            serializeOn2BytesToVecBE(n, m_bytecode);
585✔
197

198
        // push timestamp
199
        const long long timestamp = std::chrono::duration_cast<std::chrono::seconds>(
195✔
200
                                        std::chrono::system_clock::now().time_since_epoch())
195✔
201
                                        .count();
195✔
202
        for (long i = 0; i < 8; ++i)
1,755✔
203
        {
204
            const long shift = 8 * (7 - i);
1,560✔
205
            const auto ts_byte = static_cast<uint8_t>((timestamp & (0xffLL << shift)) >> shift);
1,560✔
206
            m_bytecode.push_back(ts_byte);
1,560✔
207
        }
1,560✔
208
    }
195✔
209

210
    void IRCompiler::pushSymbolTable(const std::vector<std::string>& symbols)
195✔
211
    {
195✔
212
        const std::size_t symbol_size = symbols.size();
195✔
213
        if (symbol_size > std::numeric_limits<uint16_t>::max())
195✔
NEW
214
            throw std::overflow_error(fmt::format("Too many symbols: {}, exceeds the maximum size of 2^16 - 1", symbol_size));
×
215

216
        m_bytecode.push_back(SYM_TABLE_START);
195✔
217
        serializeOn2BytesToVecBE(symbol_size, m_bytecode);
195✔
218

219
        for (const auto& sym : symbols)
4,559✔
220
        {
221
            // push the string, null terminated
222
            std::ranges::transform(sym, std::back_inserter(m_bytecode), [](const char i) {
46,010✔
223
                return static_cast<uint8_t>(i);
41,646✔
224
            });
225
            m_bytecode.push_back(0_u8);
4,364✔
226
        }
4,364✔
227
    }
195✔
228

229
    void IRCompiler::pushValueTable(const std::vector<ValTableElem>& values)
195✔
230
    {
195✔
231
        const std::size_t value_size = values.size();
195✔
232
        if (value_size > std::numeric_limits<uint16_t>::max())
195✔
NEW
233
            throw std::overflow_error(fmt::format("Too many values: {}, exceeds the maximum size of 2^16 - 1", value_size));
×
234

235
        m_bytecode.push_back(VAL_TABLE_START);
195✔
236
        serializeOn2BytesToVecBE(value_size, m_bytecode);
195✔
237

238
        for (const ValTableElem& val : values)
4,883✔
239
        {
240
            switch (val.type)
4,688✔
241
            {
761✔
242
                case ValTableElemType::Number:
243
                {
244
                    m_bytecode.push_back(NUMBER_TYPE);
761✔
245
                    const auto n = std::get<double>(val.value);
761✔
246
                    const auto [exponent, mantissa] = ieee754::serialize(n);
761✔
247
                    serializeToVecLE(exponent, m_bytecode);
761✔
248
                    serializeToVecLE(mantissa, m_bytecode);
761✔
249
                    break;
250
                }
2,341✔
251

252
                case ValTableElemType::String:
253
                {
254
                    m_bytecode.push_back(STRING_TYPE);
1,580✔
255
                    auto t = std::get<std::string>(val.value);
1,580✔
256
                    std::ranges::transform(t, std::back_inserter(m_bytecode), [](const char i) {
28,890✔
257
                        return static_cast<uint8_t>(i);
27,310✔
258
                    });
259
                    break;
260
                }
3,927✔
261

262
                case ValTableElemType::PageAddr:
263
                {
264
                    m_bytecode.push_back(FUNC_TYPE);
2,347✔
265
                    const std::size_t addr = std::get<std::size_t>(val.value);
2,347✔
266
                    serializeOn2BytesToVecBE(addr, m_bytecode);
2,347✔
267
                    break;
268
                }
2,347✔
269
            }
4,688✔
270

271
            m_bytecode.push_back(0_u8);
4,688✔
272
        }
4,688✔
273
    }
195✔
274

275
    void IRCompiler::pushFilenameTable()
195✔
276
    {
195✔
277
        if (m_filenames.size() > std::numeric_limits<uint16_t>::max())
195✔
NEW
278
            throw std::overflow_error(fmt::format("Too many filenames: {}, exceeds the maximum size of 2^16 - 1", m_filenames.size()));
×
279

280
        m_bytecode.push_back(FILENAMES_TABLE_START);
195✔
281
        // push number of elements
282
        serializeOn2BytesToVecBE(m_filenames.size(), m_bytecode);
195✔
283

284
        for (const auto& name : m_filenames)
504✔
285
        {
286
            std::ranges::transform(name, std::back_inserter(m_bytecode), [](const char i) {
23,970✔
287
                return static_cast<uint8_t>(i);
23,661✔
288
            });
289
            m_bytecode.push_back(0_u8);
309✔
290
        }
309✔
291
    }
195✔
292

293
    void IRCompiler::pushInstLocTable(const std::vector<IR::Block>& pages)
195✔
294
    {
195✔
295
        std::vector<internal::InstLoc> locations;
195✔
296
        for (std::size_t i = 0, end = pages.size(); i < end; ++i)
2,737✔
297
        {
298
            const auto& page = pages[i];
2,542✔
299
            uint16_t ip = 0;
2,542✔
300

301
            for (const auto& inst : page)
62,372✔
302
            {
303
                if (inst.hasValidSourceLocation())
59,830✔
304
                {
305
                    // we are guaranteed to have a value since we listed all existing filenames in IRCompiler::process before,
306
                    // thus we do not have to check if std::ranges::find returned a valid iterator.
307
                    auto file_id = static_cast<uint16_t>(std::distance(m_filenames.begin(), std::ranges::find(m_filenames, inst.filename())));
23,619✔
308

309
                    std::optional<internal::InstLoc> prev = std::nullopt;
23,619✔
310
                    if (!locations.empty())
23,619✔
311
                        prev = locations.back();
23,424✔
312

313
                    // skip redundant instruction location
314
                    if (!(prev.has_value() && prev->filename_id == file_id && prev->line == inst.sourceLine() && prev->page_pointer == i))
23,619✔
315
                        locations.push_back(
13,500✔
316
                            { .page_pointer = static_cast<uint16_t>(i),
54,000✔
317
                              .inst_pointer = ip,
13,500✔
318
                              .filename_id = file_id,
13,500✔
319
                              .line = static_cast<uint32_t>(inst.sourceLine()) });
13,500✔
320
                }
23,619✔
321

322
                if (inst.kind() != IR::Kind::Label)
59,830✔
323
                    ++ip;
50,299✔
324
            }
59,830✔
325
        }
2,542✔
326

327
        m_bytecode.push_back(INST_LOC_TABLE_START);
195✔
328
        serializeOn2BytesToVecBE(locations.size(), m_bytecode);
195✔
329

330
        std::optional<internal::InstLoc> prev = std::nullopt;
195✔
331

332
        for (const auto& loc : locations)
13,695✔
333
        {
334
            serializeOn2BytesToVecBE(loc.page_pointer, m_bytecode);
13,500✔
335
            serializeOn2BytesToVecBE(loc.inst_pointer, m_bytecode);
13,500✔
336
            serializeOn2BytesToVecBE(loc.filename_id, m_bytecode);
13,500✔
337
            serializeToVecBE(loc.line, m_bytecode);
13,500✔
338

339
            prev = loc;
13,500✔
340
        }
13,500✔
341
    }
195✔
342
}
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