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

ArkScript-lang / Ark / 24087581331

07 Apr 2026 02:45PM UTC coverage: 93.945% (+0.1%) from 93.837%
24087581331

Pull #673

github

web-flow
Merge c7d5ed591 into b3a547cd0
Pull Request #673: Feat/inst tracking

1051 of 1109 new or added lines in 6 files covered. (94.77%)

2 existing lines in 1 file now uncovered.

9868 of 10504 relevant lines covered (93.95%)

648642.64 hits per line

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

86.17
/src/arkreactor/Compiler/BytecodeReader.cpp
1
#include <Ark/Compiler/BytecodeReader.hpp>
2

3
#include <Ark/Compiler/Instructions.hpp>
4
#include <Ark/Builtins/Builtins.hpp>
5

6
#include <unordered_map>
7
#include <Proxy/Picosha2.hpp>
8
#include <Ark/Compiler/Serialization/IEEE754Serializer.hpp>
9
#include <Ark/Compiler/Serialization/IntegerSerializer.hpp>
10
#include <fmt/core.h>
11
#include <fmt/color.h>
12
#include <fmt/ostream.h>
13

14
namespace Ark
15
{
16
    using namespace Ark::internal;
17

18
    BytecodeReader::BytecodeReader()
754✔
19
    {
754✔
20
        m_arg_kinds = {
754✔
21
            { LOAD_FAST, ArgKind::Symbol },
22
            { LOAD_FAST_BY_INDEX, ArgKind::Raw },
23
            { LOAD_SYMBOL, ArgKind::Symbol },
24
            { LOAD_CONST, ArgKind::Constant },
25
            { POP_JUMP_IF_TRUE, ArgKind::Raw },
26
            { STORE, ArgKind::Symbol },
27
            { STORE_REF, ArgKind::Symbol },
28
            { SET_VAL, ArgKind::Symbol },
29
            { POP_JUMP_IF_FALSE, ArgKind::Raw },
30
            { JUMP, ArgKind::Raw },
31
            { PUSH_RETURN_ADDRESS, ArgKind::RawHex },
32
            { CALL, ArgKind::Raw },
33
            { CAPTURE, ArgKind::Symbol },
34
            { RENAME_NEXT_CAPTURE, ArgKind::Symbol },
35
            { BUILTIN, ArgKind::Builtin },
36
            { DEL, ArgKind::Symbol },
37
            { MAKE_CLOSURE, ArgKind::Constant },
38
            { GET_FIELD, ArgKind::Symbol },
39
            { PLUGIN, ArgKind::Constant },
40
            { LIST, ArgKind::Raw },
41
            { APPEND, ArgKind::Raw },
42
            { CONCAT, ArgKind::Raw },
43
            { APPEND_IN_PLACE, ArgKind::Raw },
44
            { CONCAT_IN_PLACE, ArgKind::Raw },
45
            { POP_LIST, ArgKind::Raw },
46
            { POP_LIST_IN_PLACE, ArgKind::Raw },
47
            { SET_AT_INDEX, ArgKind::Raw },
NEW
48
            { SET_AT_2_INDEX, ArgKind::Raw },
×
49
            { RESET_SCOPE_JUMP, ArgKind::Raw },
50
            { LOAD_CONST_LOAD_CONST, ArgKind::ConstConst },
51
            { LOAD_CONST_STORE, ArgKind::ConstSym },
52
            { LOAD_CONST_SET_VAL, ArgKind::ConstSym },
53
            { STORE_FROM, ArgKind::SymSym },
54
            { STORE_FROM_INDEX, ArgKind::RawSym },
NEW
55
            { SET_VAL_FROM, ArgKind::SymSym },
×
56
            { SET_VAL_FROM_INDEX, ArgKind::RawSym },
57
            { INCREMENT, ArgKind::SymRaw },
58
            { INCREMENT_BY_INDEX, ArgKind::RawRaw },
59
            { INCREMENT_STORE, ArgKind::RawRaw },
60
            { DECREMENT, ArgKind::SymRaw },
61
            { DECREMENT_BY_INDEX, ArgKind::RawRaw },
NEW
62
            { DECREMENT_STORE, ArgKind::SymRaw },
×
63
            { STORE_TAIL, ArgKind::SymSym },
64
            { STORE_TAIL_BY_INDEX, ArgKind::RawSym },
65
            { STORE_HEAD, ArgKind::SymSym },
66
            { STORE_HEAD_BY_INDEX, ArgKind::RawSym },
67
            { STORE_LIST, ArgKind::RawSym },
68
            { SET_VAL_TAIL, ArgKind::SymSym },
NEW
69
            { SET_VAL_TAIL_BY_INDEX, ArgKind::RawSym },
×
70
            { SET_VAL_HEAD, ArgKind::SymSym },
71
            { SET_VAL_HEAD_BY_INDEX, ArgKind::RawSym },
72
            { CALL_BUILTIN, ArgKind::BuiltinRaw },
73
            { CALL_BUILTIN_WITHOUT_RETURN_ADDRESS, ArgKind::BuiltinRaw },
74
            { LT_CONST_JUMP_IF_FALSE, ArgKind::ConstRaw },
75
            { LT_CONST_JUMP_IF_TRUE, ArgKind::ConstRaw },
NEW
76
            { LT_SYM_JUMP_IF_FALSE, ArgKind::SymRaw },
×
77
            { GT_CONST_JUMP_IF_TRUE, ArgKind::ConstRaw },
78
            { GT_CONST_JUMP_IF_FALSE, ArgKind::ConstRaw },
79
            { GT_SYM_JUMP_IF_FALSE, ArgKind::SymRaw },
80
            { EQ_CONST_JUMP_IF_TRUE, ArgKind::ConstRaw },
81
            { EQ_SYM_INDEX_JUMP_IF_TRUE, ArgKind::SymRaw },
82
            { NEQ_CONST_JUMP_IF_TRUE, ArgKind::ConstRaw },
83
            { NEQ_SYM_JUMP_IF_FALSE, ArgKind::SymRaw },
84
            { CALL_SYMBOL, ArgKind::SymRaw },
85
            { CALL_SYMBOL_BY_INDEX, ArgKind::RawRaw },
86
            { CALL_CURRENT_PAGE, ArgKind::SymRaw },
87
            { GET_FIELD_FROM_SYMBOL, ArgKind::SymSym },
88
            { GET_FIELD_FROM_SYMBOL_INDEX, ArgKind::RawSym },
89
            { AT_SYM_SYM, ArgKind::SymSym },
90
            { AT_SYM_INDEX_SYM_INDEX, ArgKind::RawRaw },
91
            { AT_SYM_INDEX_CONST, ArgKind::RawConst },
92
            { CHECK_TYPE_OF, ArgKind::SymConst },
93
            { CHECK_TYPE_OF_BY_INDEX, ArgKind::RawConst },
94
            { APPEND_IN_PLACE_SYM, ArgKind::SymRaw },
95
            { APPEND_IN_PLACE_SYM_INDEX, ArgKind::RawRaw },
96
            { STORE_LEN, ArgKind::RawSym },
97
            { LT_LEN_SYM_JUMP_IF_FALSE, ArgKind::SymRaw },
98
            { MUL_BY, ArgKind::RawRaw },
99
            { MUL_BY_INDEX, ArgKind::RawRaw },
100
            { MUL_SET_VAL, ArgKind::RawRaw },
101
            { FUSED_MATH, ArgKind::RawRawRaw }
102
        };
103
    }
754✔
104

105
    void BytecodeReader::feed(const bytecode_t& bytecode)
752✔
106
    {
752✔
107
        m_bytecode = bytecode;
752✔
108
    }
752✔
109

110
    void BytecodeReader::feed(const std::string& file)
1✔
111
    {
1✔
112
        std::ifstream ifs(file, std::ios::binary | std::ios::ate);
1✔
113
        if (!ifs.good())
1✔
114
            throw std::runtime_error(fmt::format("[BytecodeReader] Couldn't open file '{}'", file));
×
115

116
        const auto pos = ifs.tellg();
1✔
117
        // reserve appropriate number of bytes
118
        std::vector<char> temp(static_cast<std::size_t>(pos));
1✔
119
        ifs.seekg(0, std::ios::beg);
1✔
120
        ifs.read(&temp[0], pos);
1✔
121
        ifs.close();
1✔
122

123
        m_bytecode = bytecode_t(static_cast<std::size_t>(pos));
1✔
124
        for (std::size_t i = 0; i < static_cast<std::size_t>(pos); ++i)
194✔
125
            m_bytecode[i] = static_cast<uint8_t>(temp[i]);
193✔
126
    }
1✔
127

128
    bool BytecodeReader::checkMagic() const
2,973✔
129
    {
2,973✔
130
        return m_bytecode.size() >= bytecode::Magic.size() &&
5,945✔
131
            m_bytecode[0] == bytecode::Magic[0] &&
2,972✔
132
            m_bytecode[1] == bytecode::Magic[1] &&
2,550✔
133
            m_bytecode[2] == bytecode::Magic[2] &&
5,100✔
134
            m_bytecode[3] == bytecode::Magic[3];
2,550✔
135
    }
136

137
    Version BytecodeReader::version() const
309✔
138
    {
309✔
139
        if (!checkMagic() || m_bytecode.size() < bytecode::Magic.size() + bytecode::Version.size())
309✔
140
            return Version { 0, 0, 0 };
×
141

142
        return Version {
1,236✔
143
            .major = static_cast<uint16_t>((m_bytecode[4] << 8) + m_bytecode[5]),
309✔
144
            .minor = static_cast<uint16_t>((m_bytecode[6] << 8) + m_bytecode[7]),
309✔
145
            .patch = static_cast<uint16_t>((m_bytecode[8] << 8) + m_bytecode[9])
309✔
146
        };
147
    }
309✔
148

149
    unsigned long long BytecodeReader::timestamp() const
2✔
150
    {
2✔
151
        // 4 (ark\0) + version (2 bytes / number) + timestamp = 18 bytes
152
        if (!checkMagic() || m_bytecode.size() < bytecode::HeaderSize)
2✔
153
            return 0;
×
154

155
        // reading the timestamp in big endian
156
        using timestamp_t = unsigned long long;
157
        return (static_cast<timestamp_t>(m_bytecode[10]) << 56) +
6✔
158
            (static_cast<timestamp_t>(m_bytecode[11]) << 48) +
4✔
159
            (static_cast<timestamp_t>(m_bytecode[12]) << 40) +
4✔
160
            (static_cast<timestamp_t>(m_bytecode[13]) << 32) +
4✔
161
            (static_cast<timestamp_t>(m_bytecode[14]) << 24) +
4✔
162
            (static_cast<timestamp_t>(m_bytecode[15]) << 16) +
4✔
163
            (static_cast<timestamp_t>(m_bytecode[16]) << 8) +
4✔
164
            static_cast<timestamp_t>(m_bytecode[17]);
2✔
165
    }
2✔
166

167
    std::vector<unsigned char> BytecodeReader::sha256() const
308✔
168
    {
308✔
169
        if (!checkMagic() || m_bytecode.size() < bytecode::HeaderSize + picosha2::k_digest_size)
308✔
170
            return {};
×
171

172
        std::vector<unsigned char> sha(picosha2::k_digest_size);
308✔
173
        for (std::size_t i = 0; i < picosha2::k_digest_size; ++i)
10,164✔
174
            sha[i] = m_bytecode[bytecode::HeaderSize + i];
9,856✔
175
        return sha;
308✔
176
    }
616✔
177

178
    Symbols BytecodeReader::symbols() const
328✔
179
    {
328✔
180
        if (!checkMagic() || m_bytecode.size() < bytecode::HeaderSize + picosha2::k_digest_size ||
656✔
181
            m_bytecode[bytecode::HeaderSize + picosha2::k_digest_size] != SYM_TABLE_START)
328✔
182
            return {};
×
183

184
        std::size_t i = bytecode::HeaderSize + picosha2::k_digest_size + 1;
328✔
185
        const uint16_t size = readNumber(i);
328✔
186
        i++;
328✔
187

188
        Symbols block;
328✔
189
        block.start = bytecode::HeaderSize + picosha2::k_digest_size;
328✔
190
        block.symbols.reserve(size);
328✔
191

192
        for (uint16_t j = 0; j < size; ++j)
11,306✔
193
        {
194
            std::string content;
10,978✔
195
            while (m_bytecode[i] != 0)
123,135✔
196
                content.push_back(static_cast<char>(m_bytecode[i++]));
112,157✔
197
            i++;
10,978✔
198

199
            block.symbols.push_back(content);
10,978✔
200
        }
10,978✔
201

202
        block.end = i;
328✔
203
        return block;
328✔
204
    }
328✔
205

206
    Values BytecodeReader::values(const Symbols& symbols) const
328✔
207
    {
328✔
208
        if (!checkMagic())
328✔
209
            return {};
×
210

211
        std::size_t i = symbols.end;
328✔
212
        if (m_bytecode[i] != VAL_TABLE_START)
328✔
213
            return {};
×
214
        i++;
328✔
215

216
        const uint16_t size = readNumber(i);
328✔
217
        i++;
328✔
218
        Values block;
328✔
219
        block.start = symbols.end;
328✔
220
        block.values.reserve(size);
328✔
221

222
        for (uint16_t j = 0; j < size; ++j)
11,593✔
223
        {
224
            const uint8_t type = m_bytecode[i];
11,265✔
225
            i++;
11,265✔
226

227
            if (type == NUMBER_TYPE)
11,265✔
228
            {
229
                auto exp = deserializeLE<decltype(ieee754::DecomposedDouble::exponent)>(
2,606✔
230
                    m_bytecode.begin() + static_cast<std::vector<uint8_t>::difference_type>(i), m_bytecode.end());
1,303✔
231
                i += sizeof(decltype(exp));
1,303✔
232
                auto mant = deserializeLE<decltype(ieee754::DecomposedDouble::mantissa)>(
2,606✔
233
                    m_bytecode.begin() + static_cast<std::vector<uint8_t>::difference_type>(i), m_bytecode.end());
1,303✔
234
                i += sizeof(decltype(mant));
1,303✔
235

236
                const ieee754::DecomposedDouble d { exp, mant };
1,303✔
237
                double val = ieee754::deserialize(d);
1,303✔
238
                block.values.emplace_back(val);
1,303✔
239
            }
1,303✔
240
            else if (type == STRING_TYPE)
9,962✔
241
            {
242
                std::string val;
4,228✔
243
                while (m_bytecode[i] != 0)
83,318✔
244
                    val.push_back(static_cast<char>(m_bytecode[i++]));
79,090✔
245
                block.values.emplace_back(val);
4,228✔
246
            }
4,228✔
247
            else if (type == FUNC_TYPE)
5,734✔
248
            {
249
                const uint16_t addr = readNumber(i);
5,734✔
250
                i++;
5,734✔
251
                block.values.emplace_back(addr);
5,734✔
252
            }
5,734✔
253
            else
254
                throw std::runtime_error(fmt::format("Unknown value type: {:x}", type));
×
255
            i++;
11,265✔
256
        }
11,265✔
257

258
        block.end = i;
328✔
259
        return block;
328✔
260
    }
328✔
261

262
    Filenames BytecodeReader::filenames(const Ark::Values& values) const
322✔
263
    {
322✔
264
        if (!checkMagic())
322✔
265
            return {};
×
266

267
        std::size_t i = values.end;
322✔
268
        if (m_bytecode[i] != FILENAMES_TABLE_START)
322✔
269
            return {};
×
270
        i++;
322✔
271

272
        const uint16_t size = readNumber(i);
322✔
273
        i++;
322✔
274

275
        Filenames block;
322✔
276
        block.start = values.end;
322✔
277
        block.filenames.reserve(size);
322✔
278

279
        for (uint16_t j = 0; j < size; ++j)
837✔
280
        {
281
            std::string val;
515✔
282
            while (m_bytecode[i] != 0)
39,118✔
283
                val.push_back(static_cast<char>(m_bytecode[i++]));
38,603✔
284
            block.filenames.emplace_back(val);
515✔
285
            i++;
515✔
286
        }
515✔
287

288
        block.end = i;
322✔
289
        return block;
322✔
290
    }
322✔
291

292
    InstLocations BytecodeReader::instLocations(const Ark::Filenames& filenames) const
322✔
293
    {
322✔
294
        if (!checkMagic())
322✔
295
            return {};
×
296

297
        std::size_t i = filenames.end;
322✔
298
        if (m_bytecode[i] != INST_LOC_TABLE_START)
322✔
299
            return {};
×
300
        i++;
322✔
301

302
        const uint16_t size = readNumber(i);
322✔
303
        i++;
322✔
304

305
        InstLocations block;
322✔
306
        block.start = filenames.end;
322✔
307
        block.locations.reserve(size);
322✔
308

309
        for (uint16_t j = 0; j < size; ++j)
44,931✔
310
        {
311
            auto pp = readNumber(i);
44,609✔
312
            i++;
44,609✔
313

314
            auto ip = readNumber(i);
44,609✔
315
            i++;
44,609✔
316

317
            auto file_id = readNumber(i);
44,609✔
318
            i++;
44,609✔
319

320
            auto line = deserializeBE<uint32_t>(
89,218✔
321
                m_bytecode.begin() + static_cast<std::vector<uint8_t>::difference_type>(i), m_bytecode.end());
44,609✔
322
            i += 4;
44,609✔
323

324
            block.locations.push_back(
44,609✔
325
                { .page_pointer = pp,
178,436✔
326
                  .inst_pointer = ip,
44,609✔
327
                  .filename_id = file_id,
44,609✔
328
                  .line = line });
44,609✔
329
        }
44,609✔
330

331
        block.end = i;
322✔
332
        return block;
322✔
333
    }
322✔
334

335
    Code BytecodeReader::code(const InstLocations& instLocations) const
322✔
336
    {
322✔
337
        if (!checkMagic())
322✔
338
            return {};
×
339

340
        std::size_t i = instLocations.end;
322✔
341

342
        Code block;
322✔
343
        block.start = i;
322✔
344

345
        while (m_bytecode[i] == CODE_SEGMENT_START)
6,047✔
346
        {
347
            i++;
6,047✔
348
            const std::size_t size = readNumber(i) * 4;
6,047✔
349
            i++;
6,047✔
350

351
            block.pages.emplace_back().reserve(size);
6,047✔
352
            for (std::size_t j = 0; j < size; ++j)
647,915✔
353
                block.pages.back().push_back(m_bytecode[i++]);
641,868✔
354

355
            if (i == m_bytecode.size())
6,047✔
356
                break;
322✔
357
        }
6,047✔
358

359
        return block;
322✔
360
    }
322✔
361

362
    std::optional<InstLoc> BytecodeReader::findSourceLocation(const std::vector<InstLoc>& inst_locations, const std::size_t ip, const std::size_t pp) const
15✔
363
    {
15✔
364
        std::optional<InstLoc> match = std::nullopt;
15✔
365

366
        for (const auto location : inst_locations)
15,110✔
367
        {
368
            if (location.page_pointer == pp && !match)
15,095✔
369
                match = location;
15✔
370

371
            // select the best match: we want to find the location that's nearest our instruction pointer,
372
            // but not equal to it as the IP will always be pointing to the next instruction,
373
            // not yet executed. Thus, the erroneous instruction is the previous one.
374
            if (location.page_pointer == pp && match && location.inst_pointer < ip / 4)
15,095✔
375
                match = location;
10✔
376

377
            // early exit because we won't find anything better, as inst locations are ordered by ascending (pp, ip)
378
            if (location.page_pointer > pp || (location.page_pointer == pp && location.inst_pointer >= ip / 4))
15,095✔
379
                break;
10✔
380
        }
15,095✔
381

382
        return match;
15✔
383
    }
384

385
    void BytecodeReader::display(const BytecodeSegment segment,
2✔
386
                                 const std::optional<uint16_t> sStart,
387
                                 const std::optional<uint16_t> sEnd,
388
                                 const std::optional<uint16_t> cPage) const
389
    {
2✔
390
        if (!checkMagic())
2✔
391
        {
392
            fmt::println("Invalid format");
×
393
            return;
×
394
        }
395

396
        if (segment == BytecodeSegment::All || segment == BytecodeSegment::HeadersOnly)
2✔
397
        {
398
            auto [major, minor, patch] = version();
2✔
399
            fmt::println("Version:   {}.{}.{}", major, minor, patch);
2✔
400
            fmt::println("Timestamp: {}", timestamp());
1✔
401
            fmt::print("SHA256:    ");
1✔
402
            for (const auto sha = sha256(); unsigned char h : sha)
33✔
403
                fmt::print("{:02x}", h);
32✔
404
            fmt::print("\n\n");
1✔
405
        }
1✔
406

407
        // reading the different tables, one after another
408

409
        if ((sStart.has_value() && !sEnd.has_value()) || (!sStart.has_value() && sEnd.has_value()))
2✔
410
        {
411
            fmt::print(fmt::fg(fmt::color::red), "Both start and end parameter need to be provided together\n");
×
412
            return;
×
413
        }
414
        if (sStart.has_value() && sEnd.has_value() && sStart.value() >= sEnd.value())
2✔
415
        {
416
            fmt::print(fmt::fg(fmt::color::red), "Invalid slice start and end arguments\n");
×
417
            return;
×
418
        }
419

420
        const auto syms = symbols();
2✔
421
        const auto vals = values(syms);
2✔
422
        const auto files = filenames(vals);
2✔
423
        const auto inst_locs = instLocations(files);
2✔
424
        const auto code_block = code(inst_locs);
2✔
425

426
        // symbols table
427
        {
428
            std::size_t size = syms.symbols.size();
2✔
429
            std::size_t sliceSize = size;
2✔
430
            bool showSym = (segment == BytecodeSegment::All || segment == BytecodeSegment::Symbols);
2✔
431

432
            if (showSym && sStart.has_value() && sEnd.has_value() && (sStart.value() > size || sEnd.value() > size))
2✔
433
                fmt::print(fmt::fg(fmt::color::red), "Slice start or end can't be greater than the segment size: {}\n", size);
×
434
            else if (showSym && sStart.has_value() && sEnd.has_value())
2✔
435
                sliceSize = sEnd.value() - sStart.value() + 1;
×
436

437
            if (showSym || segment == BytecodeSegment::HeadersOnly)
2✔
438
                fmt::println("{} (length: {})", fmt::styled("Symbols table", fmt::fg(fmt::color::cyan)), sliceSize);
1✔
439

440
            for (std::size_t j = 0; j < size; ++j)
415✔
441
            {
442
                if (auto start = sStart; auto end = sEnd)
413✔
443
                    showSym = showSym && (j >= start.value() && j <= end.value());
×
444

445
                if (showSym)
413✔
446
                    fmt::println("{}) {}", j, syms.symbols[j]);
×
447
            }
413✔
448

449
            if (showSym)
2✔
450
                fmt::print("\n");
1✔
451
            if (segment == BytecodeSegment::Symbols)
2✔
452
                return;
×
453
        }
2✔
454

455
        // values table
456
        {
457
            std::size_t size = vals.values.size();
2✔
458
            std::size_t sliceSize = size;
2✔
459

460
            bool showVal = (segment == BytecodeSegment::All || segment == BytecodeSegment::Values);
2✔
461
            if (showVal && sStart.has_value() && sEnd.has_value() && (sStart.value() > size || sEnd.value() > size))
2✔
462
                fmt::print(fmt::fg(fmt::color::red), "Slice start or end can't be greater than the segment size: {}\n", size);
×
463
            else if (showVal && sStart.has_value() && sEnd.has_value())
2✔
464
                sliceSize = sEnd.value() - sStart.value() + 1;
×
465

466
            if (showVal || segment == BytecodeSegment::HeadersOnly)
2✔
467
                fmt::println("{} (length: {})", fmt::styled("Constants table", fmt::fg(fmt::color::cyan)), sliceSize);
1✔
468

469
            for (std::size_t j = 0; j < size; ++j)
1,179✔
470
            {
471
                if (auto start = sStart; auto end = sEnd)
1,177✔
472
                    showVal = showVal && (j >= start.value() && j <= end.value());
×
473

474
                if (showVal)
1,177✔
475
                {
476
                    switch (const auto val = vals.values[j]; val.valueType())
1✔
477
                    {
×
478
                        case ValueType::Number:
479
                            fmt::println("{}) (Number) {}", j, val.number());
×
480
                            break;
1✔
481
                        case ValueType::String:
482
                            fmt::println("{}) (String) {}", j, val.string());
1✔
483
                            break;
1✔
484
                        case ValueType::PageAddr:
485
                            fmt::println("{}) (PageAddr) {}", j, val.pageAddr());
×
486
                            break;
×
487
                        default:
488
                            fmt::print(fmt::fg(fmt::color::red), "Value type not handled: {}\n", std::to_string(val.valueType()));
×
489
                            break;
×
490
                    }
1✔
491
                }
1✔
492
            }
1,177✔
493

494
            if (showVal)
2✔
495
                fmt::print("\n");
1✔
496
            if (segment == BytecodeSegment::Values)
2✔
497
                return;
×
498
        }
2✔
499

500
        // inst locs + file
501
        {
502
            std::size_t size = inst_locs.locations.size();
2✔
503
            std::size_t sliceSize = size;
2✔
504

505
            bool showVal = (segment == BytecodeSegment::All || segment == BytecodeSegment::InstructionLocation);
2✔
506
            if (showVal && sStart.has_value() && sEnd.has_value() && (sStart.value() > size || sEnd.value() > size))
2✔
507
                fmt::print(fmt::fg(fmt::color::red), "Slice start or end can't be greater than the segment size: {}\n", size);
×
508
            else if (showVal && sStart.has_value() && sEnd.has_value())
2✔
509
                sliceSize = sEnd.value() - sStart.value() + 1;
×
510

511
            if (showVal || segment == BytecodeSegment::HeadersOnly)
2✔
512
                fmt::println("{} (length: {})", fmt::styled("Instruction locations table", fmt::fg(fmt::color::cyan)), sliceSize);
1✔
513
            if (showVal && size > 0)
2✔
514
                fmt::println(" PP, IP");
1✔
515

516
            for (std::size_t j = 0; j < size; ++j)
4,755✔
517
            {
518
                if (auto start = sStart; auto end = sEnd)
4,753✔
519
                    showVal = showVal && (j >= start.value() && j <= end.value());
×
520

521
                const auto& location = inst_locs.locations[j];
4,753✔
522
                if (showVal)
4,753✔
523
                    fmt::println("{:>3},{:>3} -> {}:{}", location.page_pointer, location.inst_pointer, files.filenames[location.filename_id], location.line);
1✔
524
            }
4,753✔
525

526
            if (showVal)
2✔
527
                fmt::print("\n");
1✔
528
        }
2✔
529

530
        if (segment == BytecodeSegment::All || segment == BytecodeSegment::Code || segment == BytecodeSegment::HeadersOnly)
2✔
531
        {
532
            uint16_t pp = 0;
2✔
533

534
            for (const auto& page : code_block.pages)
211✔
535
            {
536
                bool displayCode = true;
209✔
537

538
                if (auto wanted_page = cPage)
417✔
539
                    displayCode = pp == wanted_page.value();
208✔
540

541
                if (displayCode)
209✔
542
                    fmt::println(
2✔
543
                        "{} {} (length: {})",
2✔
544
                        fmt::styled("Code segment", fmt::fg(fmt::color::magenta)),
2✔
545
                        fmt::styled(pp, fmt::fg(fmt::color::magenta)),
2✔
546
                        page.size());
2✔
547

548
                if (page.empty())
209✔
549
                {
550
                    if (displayCode)
×
551
                        fmt::print("NOP");
×
552
                }
×
553
                else if (cPage.value_or(pp) == pp && segment != BytecodeSegment::HeadersOnly)
209✔
554
                {
555
                    if (sStart.has_value() && sEnd.has_value() && ((sStart.value() > page.size()) || (sEnd.value() > page.size())))
2✔
556
                    {
557
                        fmt::print(fmt::fg(fmt::color::red), "Slice start or end can't be greater than the segment size: {}\n", page.size());
×
558
                        return;
×
559
                    }
560

561
                    std::optional<InstLoc> previous_loc = std::nullopt;
2✔
562

563
                    for (std::size_t j = sStart.value_or(0), end = sEnd.value_or(page.size()); j < end; j += 4)
17✔
564
                    {
565
                        const uint8_t inst = page[j];
15✔
566
                        const uint8_t padding = page[j + 1];
15✔
567
                        const auto arg = static_cast<uint16_t>((page[j + 2] << 8) + page[j + 3]);
15✔
568

569
                        auto maybe_loc = findSourceLocation(inst_locs.locations, j, pp);
15✔
570

571
                        // location
572
                        // we want to print it only when it changed, either the file, the line, or both
573
                        if (maybe_loc && (!previous_loc || maybe_loc != previous_loc))
15✔
574
                        {
575
                            if (!previous_loc || previous_loc->filename_id != maybe_loc->filename_id)
2✔
576
                                fmt::println("{}", files.filenames[maybe_loc->filename_id]);
2✔
577
                            fmt::print("{:>4}", maybe_loc->line + 1);
2✔
578
                            previous_loc = maybe_loc;
2✔
579
                        }
2✔
580
                        else
581
                            fmt::print("    ");
13✔
582
                        // instruction number
583
                        fmt::print(fmt::fg(fmt::color::cyan), "{:>4x}", j / 4);
15✔
584
                        // padding inst arg arg
585
                        fmt::print(" {:02x} {:02x} {:02x} {:02x} ", inst, padding, page[j + 2], page[j + 3]);
15✔
586

587
                        printInstruction(std::cout, inst, padding, arg, syms, vals);
15✔
588
                    }
15✔
589
                }
2✔
590
                if (displayCode && segment != BytecodeSegment::HeadersOnly)
209✔
591
                    fmt::print("\n");
2✔
592

593
                ++pp;
209✔
594
            }
209✔
595
        }
2✔
596
    }
2✔
597

598
    uint16_t BytecodeReader::readNumber(std::size_t& i) const
146,908✔
599
    {
146,908✔
600
        const auto x = static_cast<uint16_t>(m_bytecode[i] << 8);
146,908✔
601
        const uint16_t y = m_bytecode[++i];
146,908✔
602
        return x + y;
293,816✔
603
    }
146,908✔
604

605
    fmt::text_style withForeColor(const fmt::color color, const bool colorize)
116✔
606
    {
116✔
607
        if (colorize)
116✔
608
            return fmt::fg(color);
48✔
609
        return {};
68✔
610
    }
116✔
611

612
    void BytecodeReader::printInstruction(std::ostream& os, const uint8_t inst, const uint8_t padding, const uint16_t imm_arg, const Symbols& syms, const Values& vals, const bool colorize) const
38✔
613
    {
38✔
614
        const auto stringify_value = [](const Value& val) -> std::string {
41✔
615
            switch (val.valueType())
3✔
616
            {
2✔
617
                case ValueType::Number:
618
                    return fmt::format("{} (Number)", val.number());
3✔
619
                case ValueType::String:
620
                    return fmt::format("{} (String)", val.string());
1✔
621
                case ValueType::PageAddr:
NEW
622
                    return fmt::format("{} (PageAddr)", val.pageAddr());
×
623
                default:
NEW
624
                    return "";
×
625
            }
626
        };
3✔
627

628
        const auto builtin_name = [](const uint16_t idx) -> std::string {
41✔
629
            return Builtins::builtins[idx].first;
3✔
630
        };
631
        const auto value_str = [&stringify_value, &vals](const uint16_t idx) -> std::string {
41✔
632
            if (idx < vals.values.size())
3✔
633
                return stringify_value(vals.values[idx]);
3✔
NEW
634
            return "?";
×
635
        };
3✔
636
        const auto symbol_name = [&syms](const uint16_t idx) -> std::string {
52✔
637
            if (idx < syms.symbols.size())
14✔
638
                return syms.symbols[idx];
13✔
639
            return "?";
1✔
640
        };
14✔
641

642
        if (const auto inst_idx = static_cast<std::size_t>(inst); inst_idx < InstructionNames.size())
76✔
643
        {
644
            const std::string name = InstructionNames[inst_idx];
38✔
645
            std::optional<Arg> arg = std::nullopt;
38✔
646
            if (const auto iinst = static_cast<Instruction>(inst); m_arg_kinds.contains(iinst))
64✔
647
                arg = Arg { m_arg_kinds.at(iinst), padding, imm_arg };
26✔
648

649
            fmt::print(os, "{}", fmt::styled(name, withForeColor(fmt::color::gold, colorize)));
38✔
650
            if (arg.has_value())
38✔
651
            {
652
                const auto sym_color = withForeColor(fmt::color::green, colorize);
26✔
653
                const auto const_color = withForeColor(fmt::color::magenta, colorize);
26✔
654
                const auto raw_color = withForeColor(fmt::color::red, colorize);
26✔
655

656
                switch (auto [kind, _, idx] = arg.value(); kind)
46✔
657
                {
10✔
658
                    case ArgKind::Symbol:
659
                        fmt::print(os, " {}\n", fmt::styled(symbol_name(idx), sym_color));
10✔
660
                        break;
11✔
661
                    case ArgKind::Constant:
662
                        fmt::print(os, " {}\n", fmt::styled(value_str(idx), const_color));
1✔
663
                        break;
3✔
664
                    case ArgKind::Builtin:
665
                        fmt::print(os, " {}\n", builtin_name(idx));
2✔
666
                        break;
6✔
667
                    case ArgKind::Raw:
668
                        fmt::print(os, " ({})\n", fmt::styled(idx, raw_color));
4✔
669
                        break;
7✔
670
                    case ArgKind::RawHex:
671
                        fmt::print(os, " ({:#x})\n", fmt::styled(idx, raw_color));
3✔
672
                        break;
3✔
673
                    case ArgKind::ConstConst:
NEW
674
                        fmt::print(os, " {}, {}\n", fmt::styled(value_str(arg->primary()), const_color), fmt::styled(value_str(arg->secondary()), const_color));
×
675
                        break;
2✔
676
                    case ArgKind::ConstSym:
677
                        fmt::print(os, " {}, {}\n", fmt::styled(value_str(arg->primary()), const_color), fmt::styled(symbol_name(arg->secondary()), sym_color));
2✔
678
                        break;
2✔
679
                    case ArgKind::SymConst:
NEW
680
                        fmt::print(os, " {}, {}\n", fmt::styled(symbol_name(arg->primary()), sym_color), fmt::styled(value_str(arg->secondary()), const_color));
×
NEW
681
                        break;
×
682
                    case ArgKind::SymSym:
NEW
683
                        fmt::print(os, " {}, {}\n", fmt::styled(symbol_name(arg->primary()), sym_color), fmt::styled(symbol_name(arg->secondary()), sym_color));
×
684
                        break;
1✔
685
                    case ArgKind::BuiltinRaw:
686
                        fmt::print(os, " {}, {}\n", builtin_name(arg->primary()), fmt::styled(arg->secondary(), raw_color));
1✔
687
                        break;
1✔
688
                    case ArgKind::ConstRaw:
NEW
689
                        fmt::print(os, " {}, {}\n", fmt::styled(value_str(arg->primary()), const_color), fmt::styled(arg->secondary(), raw_color));
×
690
                        break;
2✔
691
                    case ArgKind::SymRaw:
692
                        fmt::print(os, " {}, {}\n", fmt::styled(symbol_name(arg->primary()), sym_color), fmt::styled(arg->secondary(), raw_color));
2✔
693
                        break;
2✔
694
                    case ArgKind::RawSym:
NEW
695
                        fmt::print(os, " {}, {}\n", fmt::styled(arg->primary(), raw_color), fmt::styled(symbol_name(arg->secondary()), sym_color));
×
NEW
696
                        break;
×
697
                    case ArgKind::RawConst:
NEW
698
                        fmt::print(os, " {}, {}\n", fmt::styled(arg->primary(), raw_color), fmt::styled(value_str(arg->secondary()), const_color));
×
NEW
699
                        break;
×
700
                    case ArgKind::RawRaw:
NEW
701
                        fmt::print(os, " {}, {}\n", fmt::styled(arg->primary(), raw_color), fmt::styled(arg->secondary(), raw_color));
×
702
                        break;
1✔
703
                    case ArgKind::RawRawRaw:
704
                        fmt::print(os, " {}, {}, {}\n", fmt::styled(arg->padding, raw_color), fmt::styled((arg->arg & 0xff00) >> 8, raw_color), fmt::styled(arg->arg & 0x00ff, raw_color));
1✔
705
                        break;
1✔
706
                }
26✔
707
            }
26✔
708
            else
709
                fmt::print(os, "\n");
12✔
710
        }
38✔
711
        else
NEW
712
            fmt::println(os, "Unknown instruction");
×
713
    }
38✔
714
}
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