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

ArkScript-lang / Ark / 16862444454

10 Aug 2025 02:08PM UTC coverage: 87.685% (+0.8%) from 86.869%
16862444454

Pull #568

github

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

106 of 118 new or added lines in 11 files covered. (89.83%)

6 existing lines in 1 file now uncovered.

7633 of 8705 relevant lines covered (87.69%)

125464.95 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) :
606✔
21
        m_logger("IRCompiler", debug)
303✔
22
    {}
606✔
23

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

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

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

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

47
        if (m_ir.empty())
196✔
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);
196✔
62
        picosha2::hash256(m_bytecode.begin() + bytecode::HeaderSize, m_bytecode.end(), hash_out);
196✔
63
        m_bytecode.insert(m_bytecode.begin() + bytecode::HeaderSize, hash_out.begin(), hash_out.end());
196✔
64

65
        m_logger.traceEnd();
196✔
66
    }
196✔
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
196✔
106
    {
196✔
107
        return m_bytecode;
196✔
108
    }
109

110
    void IRCompiler::compile()
196✔
111
    {
196✔
112
        // push the different code segments
113
        for (std::size_t i = 0, end = m_ir.size(); i < end; ++i)
2,740✔
114
        {
115
            IR::Block& page = m_ir[i];
2,544✔
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,544✔
119

120
            // push number of elements
121
            const auto page_size = std::ranges::count_if(page, [](const auto& a) {
65,017✔
122
                return a.kind() != IR::Kind::Label;
62,473✔
123
            });
124
            if (std::cmp_greater(page_size, MaxValue16Bits))
2,544✔
NEW
125
                throw std::overflow_error(fmt::format("Size of page {} exceeds the maximum size of {}", i, MaxValue16Bits));
×
126

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

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

141
                    default:
142
                        ++pos;
52,924✔
143
                }
62,473✔
144
            }
62,473✔
145

146
            for (auto& inst : page)
65,017✔
147
            {
148
                switch (inst.kind())
62,473✔
149
                {
8,298✔
150
                    case IR::Kind::Goto:
151
                        pushWord(Word(inst.inst(), label_to_position[inst.label()]));
8,298✔
152
                        break;
8,920✔
153

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

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

164
                    default:
165
                        break;
9,549✔
166
                }
62,473✔
167
            }
62,473✔
168
        }
2,544✔
169
    }
196✔
170

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

179
    void IRCompiler::pushFileHeader() noexcept
196✔
180
    {
196✔
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');
196✔
190
        m_bytecode.push_back('r');
196✔
191
        m_bytecode.push_back('k');
196✔
192
        m_bytecode.push_back(0_u8);
196✔
193

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

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

210
    void IRCompiler::pushSymbolTable(const std::vector<std::string>& symbols)
196✔
211
    {
196✔
212
        const std::size_t symbol_size = symbols.size();
196✔
213
        if (std::cmp_greater(symbol_size, MaxValue16Bits))
196✔
NEW
214
            throw std::overflow_error(fmt::format("Too many symbols: {}, exceeds the maximum size of {}", symbol_size, MaxValue16Bits));
×
215

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

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

229
    void IRCompiler::pushValueTable(const std::vector<ValTableElem>& values)
196✔
230
    {
196✔
231
        const std::size_t value_size = values.size();
196✔
232
        if (std::cmp_greater(value_size, MaxValue16Bits))
196✔
NEW
233
            throw std::overflow_error(fmt::format("Too many values: {}, exceeds the maximum size of {}", value_size, MaxValue16Bits));
×
234

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

238
        for (const ValTableElem& val : values)
4,891✔
239
        {
240
            switch (val.type)
4,695✔
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,347✔
251

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

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

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

275
    void IRCompiler::pushFilenameTable()
196✔
276
    {
196✔
277
        if (std::cmp_greater(m_filenames.size(), MaxValue16Bits))
196✔
NEW
278
            throw std::overflow_error(fmt::format("Too many filenames: {}, exceeds the maximum size of {}", m_filenames.size(), MaxValue16Bits));
×
279

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

284
        for (const auto& name : m_filenames)
507✔
285
        {
286
            std::ranges::transform(name, std::back_inserter(m_bytecode), [](const char i) {
24,137✔
287
                return static_cast<uint8_t>(i);
23,826✔
288
            });
289
            m_bytecode.push_back(0_u8);
311✔
290
        }
311✔
291
    }
196✔
292

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

301
            for (const auto& inst : page)
62,473✔
302
            {
303
                if (inst.hasValidSourceLocation())
59,929✔
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,657✔
308

309
                    std::optional<internal::InstLoc> prev = std::nullopt;
23,657✔
310
                    if (!locations.empty())
23,657✔
311
                        prev = locations.back();
23,461✔
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,657✔
315
                        locations.push_back(
13,520✔
316
                            { .page_pointer = static_cast<uint16_t>(i),
54,080✔
317
                              .inst_pointer = ip,
13,520✔
318
                              .filename_id = file_id,
13,520✔
319
                              .line = static_cast<uint32_t>(inst.sourceLine()) });
13,520✔
320
                }
23,657✔
321

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

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

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

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

339
            prev = loc;
13,520✔
340
        }
13,520✔
341
    }
196✔
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