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

ArkScript-lang / Ark / 11205144931

06 Oct 2024 09:02PM CUT coverage: 75.552% (+0.3%) from 75.225%
11205144931

Pull #498

github

web-flow
Merge 5e2e3b660 into 973eba8f6
Pull Request #498: Feat/ir

207 of 231 new or added lines in 11 files covered. (89.61%)

7 existing lines in 2 files now uncovered.

4929 of 6524 relevant lines covered (75.55%)

9357.91 hits per line

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

34.76
/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 <iomanip>
7
#include <picosha2.h>
8
#include <fmt/core.h>
9
#include <fmt/color.h>
10

11
namespace Ark
12
{
13
    using namespace Ark::internal;
14

15
    void BytecodeReader::feed(const bytecode_t& bytecode)
100✔
16
    {
100✔
17
        m_bytecode = bytecode;
100✔
18
    }
100✔
19

20
    void BytecodeReader::feed(const std::string& file)
1✔
21
    {
1✔
22
        std::ifstream ifs(file, std::ios::binary | std::ios::ate);
1✔
23
        if (!ifs.good())
1✔
24
            throw std::runtime_error(fmt::format("[BytecodeReader] Couldn't open file '{}'", file));
×
25

26
        const auto pos = ifs.tellg();
1✔
27
        // reserve appropriate number of bytes
28
        std::vector<char> temp(static_cast<std::size_t>(pos));
1✔
29
        ifs.seekg(0, std::ios::beg);
1✔
30
        ifs.read(&temp[0], pos);
1✔
31
        ifs.close();
1✔
32

33
        m_bytecode = bytecode_t(static_cast<std::size_t>(pos));
1✔
34
        for (std::size_t i = 0; i < static_cast<std::size_t>(pos); ++i)
277✔
35
            m_bytecode[i] = static_cast<uint8_t>(temp[i]);
276✔
36
    }
1✔
37

38
    bool BytecodeReader::checkMagic() const
236✔
39
    {
236✔
40
        return m_bytecode.size() >= 4 && m_bytecode[0] == 'a' &&
398✔
41
            m_bytecode[1] == 'r' && m_bytecode[2] == 'k' &&
324✔
42
            m_bytecode[3] == internal::Instruction::NOP;
162✔
43
    }
44

45
    const bytecode_t& BytecodeReader::bytecode() noexcept
×
46
    {
×
47
        return m_bytecode;
×
48
    }
49

50
    Version BytecodeReader::version() const
27✔
51
    {
27✔
52
        if (!checkMagic() || m_bytecode.size() < 10)
27✔
53
            return Version { 0, 0, 0 };
×
54

55
        return Version {
108✔
56
            .major = static_cast<uint16_t>((m_bytecode[4] << 8) + m_bytecode[5]),
27✔
57
            .minor = static_cast<uint16_t>((m_bytecode[6] << 8) + m_bytecode[7]),
27✔
58
            .patch = static_cast<uint16_t>((m_bytecode[8] << 8) + m_bytecode[9])
27✔
59
        };
60
    }
27✔
61

62
    unsigned long long BytecodeReader::timestamp() const
1✔
63
    {
1✔
64
        // 4 (ark\0) + version (2 bytes / number) + timestamp = 18 bytes
65
        if (!checkMagic() || m_bytecode.size() < 18)
1✔
66
            return 0;
×
67

68
        // reading the timestamp in big endian
69
        using timestamp_t = unsigned long long;
70
        return (static_cast<timestamp_t>(m_bytecode[10]) << 56) +
3✔
71
            (static_cast<timestamp_t>(m_bytecode[11]) << 48) +
2✔
72
            (static_cast<timestamp_t>(m_bytecode[12]) << 40) +
2✔
73
            (static_cast<timestamp_t>(m_bytecode[13]) << 32) +
2✔
74
            (static_cast<timestamp_t>(m_bytecode[14]) << 24) +
2✔
75
            (static_cast<timestamp_t>(m_bytecode[15]) << 16) +
2✔
76
            (static_cast<timestamp_t>(m_bytecode[16]) << 8) +
2✔
77
            static_cast<timestamp_t>(m_bytecode[17]);
1✔
78
    }
1✔
79

80
    std::vector<unsigned char> BytecodeReader::sha256() const
27✔
81
    {
27✔
82
        if (!checkMagic() || m_bytecode.size() < 18 + picosha2::k_digest_size)
27✔
83
            return {};
×
84

85
        std::vector<unsigned char> sha(picosha2::k_digest_size);
27✔
86
        for (std::size_t i = 0; i < picosha2::k_digest_size; ++i)
891✔
87
            sha[i] = m_bytecode[18 + i];
864✔
88
        return sha;
27✔
89
    }
54✔
90

91
    Symbols BytecodeReader::symbols() const
27✔
92
    {
27✔
93
        if (!checkMagic() || m_bytecode.size() < 18 + picosha2::k_digest_size ||
54✔
94
            m_bytecode[18 + picosha2::k_digest_size] != SYM_TABLE_START)
27✔
95
            return {};
×
96

97
        std::size_t i = 18 + picosha2::k_digest_size + 1;
27✔
98
        const uint16_t size = readNumber(i);
27✔
99
        i++;
27✔
100

101
        Symbols block;
27✔
102
        block.start = 18 + picosha2::k_digest_size;
27✔
103
        block.symbols.reserve(size);
27✔
104

105
        for (uint16_t j = 0; j < size; ++j)
289✔
106
        {
107
            std::string content;
262✔
108
            while (m_bytecode[i] != 0)
2,121✔
109
                content.push_back(static_cast<char>(m_bytecode[i++]));
1,859✔
110
            i++;
262✔
111

112
            block.symbols.push_back(content);
262✔
113
        }
262✔
114

115
        block.end = i;
27✔
116
        return block;
27✔
117
    }
27✔
118

119
    Values BytecodeReader::values(const Symbols& symbols) const
27✔
120
    {
27✔
121
        if (!checkMagic())
27✔
122
            return {};
×
123

124
        std::size_t i = symbols.end;
27✔
125
        if (m_bytecode[i] != VAL_TABLE_START)
27✔
126
            return {};
×
127
        i++;
27✔
128

129
        const uint16_t size = readNumber(i);
27✔
130
        i++;
27✔
131
        Values block;
27✔
132
        block.start = symbols.end;
27✔
133
        block.values.reserve(size);
27✔
134

135
        for (uint16_t j = 0; j < size; ++j)
673✔
136
        {
137
            const uint8_t type = m_bytecode[i];
646✔
138
            i++;
646✔
139

140
            if (type == NUMBER_TYPE)
646✔
141
            {
142
                std::string val;
112✔
143
                while (m_bytecode[i] != 0)
1,042✔
144
                    val.push_back(static_cast<char>(m_bytecode[i++]));
930✔
145
                block.values.emplace_back(std::stod(val));
112✔
146
            }
112✔
147
            else if (type == STRING_TYPE)
534✔
148
            {
149
                std::string val;
430✔
150
                while (m_bytecode[i] != 0)
6,407✔
151
                    val.push_back(static_cast<char>(m_bytecode[i++]));
5,977✔
152
                block.values.emplace_back(val);
430✔
153
            }
430✔
154
            else if (type == FUNC_TYPE)
104✔
155
            {
156
                const uint16_t addr = readNumber(i);
104✔
157
                i++;
104✔
158
                block.values.emplace_back(addr);
104✔
159
            }
104✔
160
            else
161
                throw std::runtime_error(fmt::format("Unknown value type: {:x}", type));
×
162
            i++;
646✔
163
        }
646✔
164

165
        block.end = i;
27✔
166
        return block;
27✔
167
    }
27✔
168

169
    Code BytecodeReader::code(const Values& values) const
27✔
170
    {
27✔
171
        if (!checkMagic())
27✔
172
            return {};
×
173

174
        std::size_t i = values.end;
27✔
175

176
        Code block;
27✔
177
        block.start = i;
27✔
178

179
        while (m_bytecode[i] == CODE_SEGMENT_START)
131✔
180
        {
181
            i++;
131✔
182
            const std::size_t size = readNumber(i) * 4;
131✔
183
            i++;
131✔
184

185
            block.pages.emplace_back().reserve(size);
131✔
186
            for (std::size_t j = 0; j < size; ++j)
33,799✔
187
                block.pages.back().push_back(m_bytecode[i++]);
33,668✔
188

189
            if (i == m_bytecode.size())
131✔
190
                break;
27✔
191
        }
131✔
192

193
        return block;
27✔
194
    }
27✔
195

196
    void BytecodeReader::display(const BytecodeSegment segment,
×
197
                                 const std::optional<uint16_t> sStart,
198
                                 const std::optional<uint16_t> sEnd,
199
                                 const std::optional<uint16_t> cPage) const
200
    {
×
201
        if (!checkMagic())
×
202
        {
203
            fmt::print("Invalid format");
×
204
            return;
×
205
        }
206

207
        auto [major, minor, patch] = version();
×
208
        fmt::println("Version:   {}.{}.{}", major, minor, patch);
×
209
        fmt::println("Timestamp: {}", timestamp());
×
210
        fmt::print("SHA256:    ");
×
211
        for (const auto sha = sha256(); unsigned char h : sha)
×
212
            fmt::print("{:02x}", h);
×
213
        fmt::print("\n\n");
×
214

215
        // reading the different tables, one after another
216

217
        if ((sStart.has_value() && !sEnd.has_value()) || (!sStart.has_value() && sEnd.has_value()))
×
218
        {
219
            fmt::print(fmt::fg(fmt::color::red), "Both start and end parameter need to be provided together\n");
×
220
            return;
×
221
        }
222
        if (sStart.has_value() && sEnd.has_value() && sStart.value() >= sEnd.value())
×
223
        {
224
            fmt::print(fmt::fg(fmt::color::red), "Invalid slice start and end arguments\n");
×
225
            return;
×
226
        }
227

228
        const auto syms = symbols();
×
229
        const auto vals = values(syms);
×
230
        const auto code_block = code(vals);
×
231

232
        // symbols table
233
        {
234
            std::size_t size = syms.symbols.size();
×
235
            std::size_t sliceSize = size;
×
236
            bool showSym = (segment == BytecodeSegment::All || segment == BytecodeSegment::Symbols);
×
237

238
            if (showSym && sStart.has_value() && sEnd.has_value() && (sStart.value() > size || sEnd.value() > size))
×
239
                fmt::print(fmt::fg(fmt::color::red), "Slice start or end can't be greater than the segment size: {}\n", size);
×
240
            else if (showSym && sStart.has_value() && sEnd.has_value())
×
241
                sliceSize = sEnd.value() - sStart.value() + 1;
×
242

243
            if (showSym || segment == BytecodeSegment::HeadersOnly)
×
244
                fmt::println("{} (length: {})", fmt::styled("Symbols table", fmt::fg(fmt::color::cyan)), sliceSize);
×
245

246
            for (std::size_t j = 0; j < size; ++j)
×
247
            {
248
                if (auto start = sStart; auto end = sEnd)
×
249
                    showSym = showSym && (j >= start.value() && j <= end.value());
×
250

251
                if (showSym)
×
252
                    fmt::println("{}) {}", j, syms.symbols[j]);
×
253
            }
×
254

255
            if (showSym)
×
256
                fmt::print("\n");
×
257
            if (segment == BytecodeSegment::Symbols)
×
258
                return;
×
259
        }
×
260

261
        // values table
262
        {
263
            std::size_t size = vals.values.size();
×
264
            std::size_t sliceSize = size;
×
265

266
            bool showVal = (segment == BytecodeSegment::All || segment == BytecodeSegment::Values);
×
267
            if (showVal && sStart.has_value() && sEnd.has_value() && (sStart.value() > size || sEnd.value() > size))
×
268
                fmt::print(fmt::fg(fmt::color::red), "Slice start or end can't be greater than the segment size: {}\n", size);
×
269
            else if (showVal && sStart.has_value() && sEnd.has_value())
×
270
                sliceSize = sEnd.value() - sStart.value() + 1;
×
271

272
            if (showVal || segment == BytecodeSegment::HeadersOnly)
×
273
                fmt::println("{} (length: {})", fmt::styled("Constants table", fmt::fg(fmt::color::cyan)), sliceSize);
×
274

275
            for (std::size_t j = 0; j < size; ++j)
×
276
            {
277
                if (auto start = sStart; auto end = sEnd)
×
278
                    showVal = showVal && (j >= start.value() && j <= end.value());
×
279

280
                if (showVal)
×
281
                {
282
                    switch (const auto val = vals.values[j]; val.valueType())
×
283
                    {
×
284
                        case ValueType::Number:
285
                            fmt::println("{}) (Number) {}", j, val.number());
×
286
                            break;
×
287
                        case ValueType::String:
288
                            fmt::println("{}) (String) {}", j, val.string());
×
289
                            break;
×
290
                        case ValueType::PageAddr:
291
                            fmt::println("{}) (PageAddr) {}", j, val.pageAddr());
×
292
                            break;
×
293
                        default:
294
                            fmt::print(fmt::fg(fmt::color::red), "Value type not handled: {}\n", types_to_str[static_cast<std::size_t>(val.valueType())]);
×
295
                            break;
×
296
                    }
×
297
                }
×
298
            }
×
299

300
            if (showVal)
×
301
                fmt::print("\n");
×
302
            if (segment == BytecodeSegment::Values)
×
303
                return;
×
304
        }
×
305

306
        const auto stringify_value = [](const Value& val) -> std::string {
×
307
            switch (val.valueType())
×
308
            {
×
309
                case ValueType::Number:
310
                    return fmt::format("{} (Number)", val.number());
×
311
                case ValueType::String:
312
                    return fmt::format("{} (String)", val.string());
×
313
                case ValueType::PageAddr:
314
                    return fmt::format("{} (PageAddr)", val.pageAddr());
×
315
                default:
316
                    return "";
×
317
            }
318
        };
×
319

320
        enum class ArgKind
321
        {
322
            Symbol,
323
            Value,
324
            Builtin,
325
            Raw
326
        };
327

328
        struct Arg
329
        {
330
            ArgKind kind;
331
            uint16_t arg;
332
        };
333

334
        const auto color_print_inst = [&syms, &vals, &stringify_value](const std::string& name, std::optional<Arg> arg = std::nullopt) {
×
335
            fmt::print("{}", fmt::styled(name, fmt::fg(fmt::color::gold)));
×
336
            if (arg.has_value())
×
337
            {
338
                switch (auto [kind, idx] = arg.value(); kind)
×
339
                {
×
340
                    case ArgKind::Symbol:
341
                        fmt::print(fmt::fg(fmt::color::green), " {}\n", syms.symbols[idx]);
×
342
                        break;
×
343
                    case ArgKind::Value:
344
                        fmt::print(fmt::fg(fmt::color::magenta), " {}\n", stringify_value(vals.values[idx]));
×
345
                        break;
×
346
                    case ArgKind::Builtin:
347
                        fmt::print(" {}\n", Builtins::builtins[idx].first);
×
348
                        break;
×
349
                    case ArgKind::Raw:
350
                        fmt::print(fmt::fg(fmt::color::red), " ({})\n", idx);
×
351
                        break;
×
352
                }
×
353
            }
×
354
            else
355
                fmt::print("\n");
×
356
        };
×
357

358
        if (segment == BytecodeSegment::All || segment == BytecodeSegment::Code || segment == BytecodeSegment::HeadersOnly)
×
359
        {
360
            uint16_t pp = 0;
×
361

362
            for (const auto& page : code_block.pages)
×
363
            {
364
                bool displayCode = true;
×
365

366
                if (auto wanted_page = cPage)
×
367
                    displayCode = pp == wanted_page.value();
×
368

369
                if (displayCode)
×
370
                    fmt::println(
×
371
                        "{} {} (length: {})",
×
372
                        fmt::styled("Code segment", fmt::fg(fmt::color::magenta)),
×
373
                        fmt::styled(pp, fmt::fg(fmt::color::magenta)),
×
374
                        page.size());
×
375

376
                if (page.empty())
×
377
                {
378
                    if (displayCode)
×
379
                        fmt::print("NOP");
×
380
                }
×
381
                else
382
                {
383
                    if (cPage.value_or(pp) != pp)
×
384
                        continue;
×
385
                    if (segment == BytecodeSegment::HeadersOnly)
×
386
                        continue;
×
387
                    if (sStart.has_value() && sEnd.has_value() && ((sStart.value() > page.size()) || (sEnd.value() > page.size())))
×
388
                    {
389
                        fmt::print(fmt::fg(fmt::color::red), "Slice start or end can't be greater than the segment size: {}\n", page.size());
×
390
                        return;
×
391
                    }
392

393
                    for (std::size_t j = sStart.value_or(0), end = sEnd.value_or(page.size()); j < end; j += 4)
×
394
                    {
NEW
395
                        const uint8_t inst = page[j];
×
396
                        // TEMP
NEW
397
                        const uint8_t padding = page[j + 1];
×
UNCOV
398
                        const auto arg = static_cast<uint16_t>((page[j + 2] << 8) + page[j + 3]);
×
399

400
                        // instruction number
401
                        fmt::print(fmt::fg(fmt::color::cyan), "{:>4}", j / 4);
×
402
                        // padding inst arg arg
NEW
403
                        fmt::print(" {:02x} {:02x} {:02x} {:02x} ", inst, padding, page[j + 2], page[j + 3]);
×
404

405
                        if (inst == NOP)
×
406
                            color_print_inst("NOP");
×
407
                        else if (inst == LOAD_SYMBOL)
×
408
                            color_print_inst("LOAD_SYMBOL", Arg { ArgKind::Symbol, arg });
×
409
                        else if (inst == LOAD_CONST)
×
410
                            color_print_inst("LOAD_CONST", Arg { ArgKind::Value, arg });
×
411
                        else if (inst == POP_JUMP_IF_TRUE)
×
412
                            color_print_inst("POP_JUMP_IF_TRUE", Arg { ArgKind::Raw, arg });
×
413
                        else if (inst == STORE)
×
414
                            color_print_inst("STORE", Arg { ArgKind::Symbol, arg });
×
415
                        else if (inst == SET_VAL)
×
416
                            color_print_inst("SET_VAL", Arg { ArgKind::Symbol, arg });
×
417
                        else if (inst == POP_JUMP_IF_FALSE)
×
418
                            color_print_inst("POP_JUMP_IF_FALSE", Arg { ArgKind::Raw, arg });
×
419
                        else if (inst == JUMP)
×
420
                            color_print_inst("JUMP", Arg { ArgKind::Raw, arg });
×
421
                        else if (inst == RET)
×
422
                            color_print_inst("RET");
×
423
                        else if (inst == HALT)
×
424
                            color_print_inst("HALT");
×
425
                        else if (inst == CALL)
×
426
                            color_print_inst("CALL", Arg { ArgKind::Raw, arg });
×
427
                        else if (inst == CAPTURE)
×
428
                            color_print_inst("CAPTURE", Arg { ArgKind::Symbol, arg });
×
429
                        else if (inst == BUILTIN)
×
430
                            color_print_inst("BUILTIN", Arg { ArgKind::Builtin, arg });
×
431
                        else if (inst == DEL)
×
432
                            color_print_inst("DEL", Arg { ArgKind::Symbol, arg });
×
433
                        else if (inst == MAKE_CLOSURE)
×
434
                            color_print_inst("MAKE_CLOSURE", Arg { ArgKind::Value, arg });
×
435
                        else if (inst == GET_FIELD)
×
436
                            color_print_inst("GET_FIELD", Arg { ArgKind::Symbol, arg });
×
437
                        else if (inst == PLUGIN)
×
438
                            color_print_inst("PLUGIN", Arg { ArgKind::Value, arg });
×
439
                        else if (inst == LIST)
×
440
                            color_print_inst("LIST", Arg { ArgKind::Raw, arg });
×
441
                        else if (inst == APPEND)
×
442
                            color_print_inst("APPEND", Arg { ArgKind::Raw, arg });
×
443
                        else if (inst == CONCAT)
×
444
                            color_print_inst("CONCAT", Arg { ArgKind::Raw, arg });
×
445
                        else if (inst == APPEND_IN_PLACE)
×
446
                            color_print_inst("APPEND_IN_PLACE", Arg { ArgKind::Raw, arg });
×
447
                        else if (inst == CONCAT_IN_PLACE)
×
448
                            color_print_inst("CONCAT_IN_PLACE", Arg { ArgKind::Raw, arg });
×
449
                        else if (inst == POP_LIST)
×
450
                            color_print_inst("POP_LIST");
×
451
                        else if (inst == POP_LIST_IN_PLACE)
×
452
                            color_print_inst("POP_LIST_IN_PLACE");
×
453
                        else if (inst == POP)
×
454
                            color_print_inst("POP");
×
455
                        else if (inst == DUP)
×
456
                            color_print_inst("DUP");
×
457
                        else if (inst == ADD)
×
458
                            color_print_inst("ADD");
×
459
                        else if (inst == SUB)
×
460
                            color_print_inst("SUB");
×
461
                        else if (inst == MUL)
×
462
                            color_print_inst("MUL");
×
463
                        else if (inst == DIV)
×
464
                            color_print_inst("DIV");
×
465
                        else if (inst == GT)
×
466
                            color_print_inst("GT");
×
467
                        else if (inst == LT)
×
468
                            color_print_inst("LT");
×
469
                        else if (inst == LE)
×
470
                            color_print_inst("LE");
×
471
                        else if (inst == GE)
×
472
                            color_print_inst("GE");
×
473
                        else if (inst == NEQ)
×
474
                            color_print_inst("NEQ");
×
475
                        else if (inst == EQ)
×
476
                            color_print_inst("EQ");
×
477
                        else if (inst == LEN)
×
478
                            color_print_inst("LEN");
×
479
                        else if (inst == EMPTY)
×
480
                            color_print_inst("EMPTY");
×
481
                        else if (inst == TAIL)
×
482
                            color_print_inst("TAIL");
×
483
                        else if (inst == HEAD)
×
484
                            color_print_inst("HEAD");
×
485
                        else if (inst == ISNIL)
×
486
                            color_print_inst("ISNIL");
×
487
                        else if (inst == ASSERT)
×
488
                            color_print_inst("ASSERT");
×
489
                        else if (inst == TO_NUM)
×
490
                            color_print_inst("TO_NUM");
×
491
                        else if (inst == TO_STR)
×
492
                            color_print_inst("TO_STR");
×
493
                        else if (inst == AT)
×
494
                            color_print_inst("AT");
×
495
                        else if (inst == MOD)
×
496
                            color_print_inst("MOD");
×
497
                        else if (inst == TYPE)
×
498
                            color_print_inst("TYPE");
×
499
                        else if (inst == HASFIELD)
×
500
                            color_print_inst("HASFIELD");
×
501
                        else if (inst == NOT)
×
502
                            color_print_inst("NOT");
×
503
                        else
504
                            fmt::println("Unknown instruction");
×
505
                    }
×
506
                }
507
                if (displayCode && segment != BytecodeSegment::HeadersOnly)
×
508
                    fmt::print("\n");
×
509

510
                ++pp;
×
511
            }
×
512
        }
×
513
    }
×
514

515
    uint16_t BytecodeReader::readNumber(std::size_t& i) const
289✔
516
    {
289✔
517
        const auto x = static_cast<uint16_t>(m_bytecode[i] << 8);
289✔
518
        const uint16_t y = m_bytecode[++i];
289✔
519
        return x + y;
578✔
520
    }
289✔
521
}
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

© 2025 Coveralls, Inc