ArkScript
A small, lisp-inspired, functional scripting language
BytecodeReader.cpp
Go to the documentation of this file.
2
5
6#include <unordered_map>
7#include <Proxy/Picosha2.hpp>
10#include <fmt/core.h>
11#include <fmt/color.h>
12#include <fmt/ostream.h>
13
14namespace Ark
15{
16 using namespace Ark::internal;
17
19 {
20 m_arg_kinds = {
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 },
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 },
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 },
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 },
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 },
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 }
104
105 void BytecodeReader::feed(const bytecode_t& bytecode)
106 {
107 m_bytecode = bytecode;
108 }
109
110 void BytecodeReader::feed(const std::string& file)
111 {
112 std::ifstream ifs(file, std::ios::binary | std::ios::ate);
113 if (!ifs.good())
114 throw std::runtime_error(fmt::format("[BytecodeReader] Couldn't open file '{}'", file));
115
116 const auto pos = ifs.tellg();
117 // reserve appropriate number of bytes
118 std::vector<char> temp(static_cast<std::size_t>(pos));
119 ifs.seekg(0, std::ios::beg);
120 ifs.read(&temp[0], pos);
121 ifs.close();
122
123 m_bytecode = bytecode_t(static_cast<std::size_t>(pos));
124 for (std::size_t i = 0; i < static_cast<std::size_t>(pos); ++i)
125 m_bytecode[i] = static_cast<uint8_t>(temp[i]);
126 }
127
129 {
130 return m_bytecode.size() >= bytecode::Magic.size() &&
131 m_bytecode[0] == bytecode::Magic[0] &&
132 m_bytecode[1] == bytecode::Magic[1] &&
133 m_bytecode[2] == bytecode::Magic[2] &&
135 }
136
138 {
139 if (!checkMagic() || m_bytecode.size() < bytecode::Magic.size() + bytecode::Version.size())
140 return Version { 0, 0, 0 };
141
142 return Version {
143 .major = static_cast<uint16_t>((m_bytecode[4] << 8) + m_bytecode[5]),
144 .minor = static_cast<uint16_t>((m_bytecode[6] << 8) + m_bytecode[7]),
145 .patch = static_cast<uint16_t>((m_bytecode[8] << 8) + m_bytecode[9])
146 };
147 }
148
149 unsigned long long BytecodeReader::timestamp() const
150 {
151 // 4 (ark\0) + version (2 bytes / number) + timestamp = 18 bytes
152 if (!checkMagic() || m_bytecode.size() < bytecode::HeaderSize)
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) +
158 (static_cast<timestamp_t>(m_bytecode[11]) << 48) +
159 (static_cast<timestamp_t>(m_bytecode[12]) << 40) +
160 (static_cast<timestamp_t>(m_bytecode[13]) << 32) +
161 (static_cast<timestamp_t>(m_bytecode[14]) << 24) +
162 (static_cast<timestamp_t>(m_bytecode[15]) << 16) +
163 (static_cast<timestamp_t>(m_bytecode[16]) << 8) +
164 static_cast<timestamp_t>(m_bytecode[17]);
165 }
166
167 std::vector<unsigned char> BytecodeReader::sha256() const
168 {
169 if (!checkMagic() || m_bytecode.size() < bytecode::HeaderSize + picosha2::k_digest_size)
170 return {};
171
172 std::vector<unsigned char> sha(picosha2::k_digest_size);
173 for (std::size_t i = 0; i < picosha2::k_digest_size; ++i)
174 sha[i] = m_bytecode[bytecode::HeaderSize + i];
175 return sha;
176 }
177
179 {
180 if (!checkMagic() || m_bytecode.size() < bytecode::HeaderSize + picosha2::k_digest_size ||
181 m_bytecode[bytecode::HeaderSize + picosha2::k_digest_size] != SYM_TABLE_START)
182 return {};
183
184 std::size_t i = bytecode::HeaderSize + picosha2::k_digest_size + 1;
185 const uint16_t size = readNumber(i);
186 i++;
187
188 Symbols block;
189 block.start = bytecode::HeaderSize + picosha2::k_digest_size;
190 block.symbols.reserve(size);
191
192 for (uint16_t j = 0; j < size; ++j)
193 {
194 std::string content;
195 while (m_bytecode[i] != 0)
196 content.push_back(static_cast<char>(m_bytecode[i++]));
197 i++;
198
199 block.symbols.push_back(content);
200 }
201
202 block.end = i;
203 return block;
204 }
205
207 {
208 if (!checkMagic())
209 return {};
210
211 std::size_t i = symbols.end;
212 if (m_bytecode[i] != VAL_TABLE_START)
213 return {};
214 i++;
215
216 const uint16_t size = readNumber(i);
217 i++;
218 Values block;
219 block.start = symbols.end;
220 block.values.reserve(size);
221
222 for (uint16_t j = 0; j < size; ++j)
223 {
224 const uint8_t type = m_bytecode[i];
225 i++;
226
227 if (type == NUMBER_TYPE)
228 {
230 m_bytecode.begin() + static_cast<std::vector<uint8_t>::difference_type>(i), m_bytecode.end());
231 i += sizeof(decltype(exp));
233 m_bytecode.begin() + static_cast<std::vector<uint8_t>::difference_type>(i), m_bytecode.end());
234 i += sizeof(decltype(mant));
235
236 const ieee754::DecomposedDouble d { exp, mant };
237 double val = ieee754::deserialize(d);
238 block.values.emplace_back(val);
239 }
240 else if (type == STRING_TYPE)
241 {
242 std::string val;
243 while (m_bytecode[i] != 0)
244 val.push_back(static_cast<char>(m_bytecode[i++]));
245 block.values.emplace_back(val);
246 }
247 else if (type == FUNC_TYPE)
248 {
249 const uint16_t addr = readNumber(i);
250 i++;
251 block.values.emplace_back(addr);
252 }
253 else
254 throw std::runtime_error(fmt::format("Unknown value type: {:x}", type));
255 i++;
256 }
257
258 block.end = i;
259 return block;
260 }
261
263 {
264 if (!checkMagic())
265 return {};
266
267 std::size_t i = values.end;
269 return {};
270 i++;
271
272 const uint16_t size = readNumber(i);
273 i++;
274
275 Filenames block;
276 block.start = values.end;
277 block.filenames.reserve(size);
278
279 for (uint16_t j = 0; j < size; ++j)
280 {
281 std::string val;
282 while (m_bytecode[i] != 0)
283 val.push_back(static_cast<char>(m_bytecode[i++]));
284 block.filenames.emplace_back(val);
285 i++;
286 }
287
288 block.end = i;
289 return block;
290 }
291
293 {
294 if (!checkMagic())
295 return {};
296
297 std::size_t i = filenames.end;
299 return {};
300 i++;
301
302 const uint16_t size = readNumber(i);
303 i++;
304
305 InstLocations block;
306 block.start = filenames.end;
307 block.locations.reserve(size);
308
309 for (uint16_t j = 0; j < size; ++j)
310 {
311 auto pp = readNumber(i);
312 i++;
313
314 auto ip = readNumber(i);
315 i++;
316
317 auto file_id = readNumber(i);
318 i++;
319
320 auto line = deserializeBE<uint32_t>(
321 m_bytecode.begin() + static_cast<std::vector<uint8_t>::difference_type>(i), m_bytecode.end());
322 i += 4;
323
324 block.locations.push_back(
325 { .page_pointer = pp,
326 .inst_pointer = ip,
327 .filename_id = file_id,
328 .line = line });
329 }
330
331 block.end = i;
332 return block;
333 }
334
335 Code BytecodeReader::code(const InstLocations& instLocations) const
336 {
337 if (!checkMagic())
338 return {};
339
340 std::size_t i = instLocations.end;
341
342 Code block;
343 block.start = i;
344
345 while (m_bytecode[i] == CODE_SEGMENT_START)
346 {
347 i++;
348 const std::size_t size = readNumber(i) * 4;
349 i++;
350
351 block.pages.emplace_back().reserve(size);
352 for (std::size_t j = 0; j < size; ++j)
353 block.pages.back().push_back(m_bytecode[i++]);
354
355 if (i == m_bytecode.size())
356 break;
357 }
358
359 return block;
360 }
361
362 std::optional<InstLoc> BytecodeReader::findSourceLocation(const std::vector<InstLoc>& inst_locations, const std::size_t ip, const std::size_t pp) const
363 {
364 std::optional<InstLoc> match = std::nullopt;
365
366 for (const auto location : inst_locations)
367 {
368 if (location.page_pointer == pp && !match)
369 match = location;
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)
375 match = location;
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))
379 break;
380 }
381
382 return match;
383 }
384
386 const std::optional<uint16_t> sStart,
387 const std::optional<uint16_t> sEnd,
388 const std::optional<uint16_t> cPage) const
389 {
390 if (!checkMagic())
391 {
392 fmt::println("Invalid format");
393 return;
394 }
395
396 if (segment == BytecodeSegment::All || segment == BytecodeSegment::HeadersOnly)
397 {
398 auto [major, minor, patch] = version();
399 fmt::println("Version: {}.{}.{}", major, minor, patch);
400 fmt::println("Timestamp: {}", timestamp());
401 fmt::print("SHA256: ");
402 for (const auto sha = sha256(); unsigned char h : sha)
403 fmt::print("{:02x}", h);
404 fmt::print("\n\n");
405 }
406
407 // reading the different tables, one after another
408
409 if ((sStart.has_value() && !sEnd.has_value()) || (!sStart.has_value() && sEnd.has_value()))
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())
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();
421 const auto vals = values(syms);
422 const auto files = filenames(vals);
423 const auto inst_locs = instLocations(files);
424 const auto code_block = code(inst_locs);
425
426 // symbols table
427 {
428 std::size_t size = syms.symbols.size();
429 std::size_t sliceSize = size;
430 bool showSym = (segment == BytecodeSegment::All || segment == BytecodeSegment::Symbols);
431
432 if (showSym && sStart.has_value() && sEnd.has_value() && (sStart.value() > size || sEnd.value() > size))
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())
435 sliceSize = sEnd.value() - sStart.value() + 1;
436
437 if (showSym || segment == BytecodeSegment::HeadersOnly)
438 fmt::println("{} (length: {})", fmt::styled("Symbols table", fmt::fg(fmt::color::cyan)), sliceSize);
439
440 for (std::size_t j = 0; j < size; ++j)
441 {
442 if (auto start = sStart; auto end = sEnd)
443 showSym = showSym && (j >= start.value() && j <= end.value());
444
445 if (showSym)
446 fmt::println("{}) {}", j, syms.symbols[j]);
447 }
448
449 if (showSym)
450 fmt::print("\n");
451 if (segment == BytecodeSegment::Symbols)
452 return;
453 }
454
455 // values table
456 {
457 std::size_t size = vals.values.size();
458 std::size_t sliceSize = size;
459
460 bool showVal = (segment == BytecodeSegment::All || segment == BytecodeSegment::Values);
461 if (showVal && sStart.has_value() && sEnd.has_value() && (sStart.value() > size || sEnd.value() > size))
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())
464 sliceSize = sEnd.value() - sStart.value() + 1;
465
466 if (showVal || segment == BytecodeSegment::HeadersOnly)
467 fmt::println("{} (length: {})", fmt::styled("Constants table", fmt::fg(fmt::color::cyan)), sliceSize);
468
469 for (std::size_t j = 0; j < size; ++j)
470 {
471 if (auto start = sStart; auto end = sEnd)
472 showVal = showVal && (j >= start.value() && j <= end.value());
473
474 if (showVal)
475 {
476 switch (const auto val = vals.values[j]; val.valueType())
477 {
479 fmt::println("{}) (Number) {}", j, val.number());
480 break;
482 fmt::println("{}) (String) {}", j, val.string());
483 break;
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 }
491 }
492 }
493
494 if (showVal)
495 fmt::print("\n");
496 if (segment == BytecodeSegment::Values)
497 return;
498 }
499
500 // inst locs + file
501 {
502 std::size_t size = inst_locs.locations.size();
503 std::size_t sliceSize = size;
504
505 bool showVal = (segment == BytecodeSegment::All || segment == BytecodeSegment::InstructionLocation);
506 if (showVal && sStart.has_value() && sEnd.has_value() && (sStart.value() > size || sEnd.value() > size))
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())
509 sliceSize = sEnd.value() - sStart.value() + 1;
510
511 if (showVal || segment == BytecodeSegment::HeadersOnly)
512 fmt::println("{} (length: {})", fmt::styled("Instruction locations table", fmt::fg(fmt::color::cyan)), sliceSize);
513 if (showVal && size > 0)
514 fmt::println(" PP, IP");
515
516 for (std::size_t j = 0; j < size; ++j)
517 {
518 if (auto start = sStart; auto end = sEnd)
519 showVal = showVal && (j >= start.value() && j <= end.value());
520
521 const auto& location = inst_locs.locations[j];
522 if (showVal)
523 fmt::println("{:>3},{:>3} -> {}:{}", location.page_pointer, location.inst_pointer, files.filenames[location.filename_id], location.line);
524 }
525
526 if (showVal)
527 fmt::print("\n");
528 }
529
530 if (segment == BytecodeSegment::All || segment == BytecodeSegment::Code || segment == BytecodeSegment::HeadersOnly)
531 {
532 uint16_t pp = 0;
533
534 for (const auto& page : code_block.pages)
535 {
536 bool displayCode = true;
537
538 if (auto wanted_page = cPage)
539 displayCode = pp == wanted_page.value();
540
541 if (displayCode)
542 fmt::println(
543 "{} {} (length: {})",
544 fmt::styled("Code segment", fmt::fg(fmt::color::magenta)),
545 fmt::styled(pp, fmt::fg(fmt::color::magenta)),
546 page.size() / 4);
547
548 if (page.empty())
549 {
550 if (displayCode)
551 fmt::print("NOP");
552 }
553 else if (cPage.value_or(pp) == pp && segment != BytecodeSegment::HeadersOnly)
554 {
555 if (sStart.has_value() && sEnd.has_value() && ((sStart.value() > page.size()) || (sEnd.value() > page.size())))
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;
562
563 for (std::size_t j = sStart.value_or(0), end = sEnd.value_or(page.size()); j < end; j += 4)
564 {
565 const uint8_t inst = page[j];
566 const uint8_t padding = page[j + 1];
567 const auto arg = static_cast<uint16_t>((page[j + 2] << 8) + page[j + 3]);
568
569 auto maybe_loc = findSourceLocation(inst_locs.locations, j, pp);
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))
574 {
575 if (!previous_loc || previous_loc->filename_id != maybe_loc->filename_id)
576 fmt::println("{}", files.filenames[maybe_loc->filename_id]);
577 fmt::print("{:>4}", maybe_loc->line + 1);
578 previous_loc = maybe_loc;
579 }
580 else
581 fmt::print(" ");
582 // instruction number
583 fmt::print(fmt::fg(fmt::color::cyan), "{:>4x}", j / 4);
584 // padding inst arg arg
585 fmt::print(" {:02x} {:02x} {:02x} {:02x} ", inst, padding, page[j + 2], page[j + 3]);
586
587 printInstruction(std::cout, inst, padding, arg, syms, vals);
588 }
589 }
590 if (displayCode && segment != BytecodeSegment::HeadersOnly)
591 fmt::print("\n");
592
593 ++pp;
594 }
595 }
596 }
597
598 uint16_t BytecodeReader::readNumber(std::size_t& i) const
599 {
600 const auto x = static_cast<uint16_t>(m_bytecode[i] << 8);
601 const uint16_t y = m_bytecode[++i];
602 return x + y;
603 }
604
605 fmt::text_style withForeColor(const fmt::color color, const bool colorize)
606 {
607 if (colorize)
608 return fmt::fg(color);
609 return {};
610 }
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
613 {
614 const auto stringify_value = [](const Value& val) -> std::string {
615 switch (val.valueType())
616 {
618 return fmt::format("{} (Number)", val.number());
620 return fmt::format("{} (String)", val.string());
622 return fmt::format("{} (PageAddr)", val.pageAddr());
623 default:
624 return "";
625 }
626 };
627
628 const auto builtin_name = [](const uint16_t idx) -> std::string {
629 return Builtins::builtins[idx].first;
630 };
631 const auto value_str = [&stringify_value, &vals](const uint16_t idx) -> std::string {
632 if (idx < vals.values.size())
633 return stringify_value(vals.values[idx]);
634 return "?";
635 };
636 const auto symbol_name = [&syms](const uint16_t idx) -> std::string {
637 if (idx < syms.symbols.size())
638 return syms.symbols[idx];
639 return "?";
640 };
641
642 if (const auto inst_idx = static_cast<std::size_t>(inst); inst_idx < InstructionNames.size())
643 {
644 const std::string name = InstructionNames[inst_idx];
645 std::optional<Arg> arg = std::nullopt;
646 if (const auto iinst = static_cast<Instruction>(inst); m_arg_kinds.contains(iinst))
647 arg = Arg { m_arg_kinds.at(iinst), padding, imm_arg };
648
649 fmt::print(os, "{}", fmt::styled(name, withForeColor(fmt::color::gold, colorize)));
650 if (arg.has_value())
651 {
652 const auto sym_color = withForeColor(fmt::color::green, colorize);
653 const auto const_color = withForeColor(fmt::color::magenta, colorize);
654 const auto raw_color = withForeColor(fmt::color::red, colorize);
655
656 switch (auto [kind, _, idx] = arg.value(); kind)
657 {
658 case ArgKind::Symbol:
659 fmt::print(os, " {}\n", fmt::styled(symbol_name(idx), sym_color));
660 break;
661 case ArgKind::Constant:
662 fmt::print(os, " {}\n", fmt::styled(value_str(idx), const_color));
663 break;
664 case ArgKind::Builtin:
665 fmt::print(os, " {}\n", builtin_name(idx));
666 break;
667 case ArgKind::Raw:
668 fmt::print(os, " ({})\n", fmt::styled(idx, raw_color));
669 break;
670 case ArgKind::RawHex:
671 fmt::print(os, " ({:#x})\n", fmt::styled(idx, raw_color));
672 break;
673 case ArgKind::ConstConst:
674 fmt::print(os, " {}, {}\n", fmt::styled(value_str(arg->primary()), const_color), fmt::styled(value_str(arg->secondary()), const_color));
675 break;
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));
678 break;
679 case ArgKind::SymConst:
680 fmt::print(os, " {}, {}\n", fmt::styled(symbol_name(arg->primary()), sym_color), fmt::styled(value_str(arg->secondary()), const_color));
681 break;
682 case ArgKind::SymSym:
683 fmt::print(os, " {}, {}\n", fmt::styled(symbol_name(arg->primary()), sym_color), fmt::styled(symbol_name(arg->secondary()), sym_color));
684 break;
685 case ArgKind::BuiltinRaw:
686 fmt::print(os, " {}, {}\n", builtin_name(arg->primary()), fmt::styled(arg->secondary(), raw_color));
687 break;
688 case ArgKind::ConstRaw:
689 fmt::print(os, " {}, {}\n", fmt::styled(value_str(arg->primary()), const_color), fmt::styled(arg->secondary(), raw_color));
690 break;
691 case ArgKind::SymRaw:
692 fmt::print(os, " {}, {}\n", fmt::styled(symbol_name(arg->primary()), sym_color), fmt::styled(arg->secondary(), raw_color));
693 break;
694 case ArgKind::RawSym:
695 fmt::print(os, " {}, {}\n", fmt::styled(arg->primary(), raw_color), fmt::styled(symbol_name(arg->secondary()), sym_color));
696 break;
697 case ArgKind::RawConst:
698 fmt::print(os, " {}, {}\n", fmt::styled(arg->primary(), raw_color), fmt::styled(value_str(arg->secondary()), const_color));
699 break;
700 case ArgKind::RawRaw:
701 fmt::print(os, " {}, {}\n", fmt::styled(arg->primary(), raw_color), fmt::styled(arg->secondary(), raw_color));
702 break;
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));
705 break;
706 }
707 }
708 else
709 fmt::print(os, "\n");
710 }
711 else
712 fmt::println(os, "Unknown instruction");
713 }
714}
Host the declaration of all the ArkScript builtins.
A bytecode disassembler for ArkScript.
The different instructions used by the compiler and virtual machine.
std::optional< internal::InstLoc > findSourceLocation(const std::vector< internal::InstLoc > &inst_locations, std::size_t ip, std::size_t pp) const
Find the location of an instruction.
Symbols symbols() const
unsigned long long timestamp() const
Return the read timestamp from the bytecode file.
uint16_t readNumber(std::size_t &i) const
Read a number from the bytecode, under the instruction pointer i.
Filenames filenames(const Values &values) const
std::unordered_map< internal::Instruction, internal::ArgKind > m_arg_kinds
BytecodeReader()
Construct a new Bytecode Reader object.
InstLocations instLocations(const Filenames &filenames) const
Version version() const
Code code(const InstLocations &instLocations) const
Values values(const Symbols &symbols) const
void printInstruction(std::ostream &os, uint8_t inst, uint8_t padding, uint16_t imm_arg, const Symbols &syms, const Values &vals, bool colorize=true) const
void display(BytecodeSegment segment=BytecodeSegment::All, std::optional< uint16_t > sStart=std::nullopt, std::optional< uint16_t > sEnd=std::nullopt, std::optional< uint16_t > cPage=std::nullopt) const
Display the bytecode opcode in a human friendly way.
std::vector< unsigned char > sha256() const
void feed(const std::string &file)
Construct needed data before displaying information about a given file.
ARK_API const std::vector< std::pair< std::string, Value > > builtins
constexpr std::array Magic
Definition Common.hpp:29
constexpr std::array Version
Definition Common.hpp:30
constexpr std::size_t HeaderSize
Definition Common.hpp:39
double deserialize(const DecomposedDouble d)
T deserializeBE(std::vector< uint8_t >::const_iterator begin, std::vector< uint8_t >::const_iterator end)
Instruction
The different bytecodes are stored here.
T deserializeLE(std::vector< uint8_t >::const_iterator begin, std::vector< uint8_t >::const_iterator end)
constexpr std::array InstructionNames
fmt::text_style withForeColor(const fmt::color color, const bool colorize)
std::vector< uint8_t > bytecode_t
Definition Common.hpp:22
std::string to_string(const Ark::ValueType type) noexcept
Definition Value.hpp:235
std::vector< bytecode_t > pages
std::size_t start
Point to the CODE_SEGMENT_START byte in the bytecode.
std::size_t end
Point to the byte following the last byte of the table in the bytecode.
std::vector< std::string > filenames
std::size_t start
Point to the FILENAMES_TABLE_START byte in the bytecode.
std::size_t end
Point to the byte following the last byte of the table in the bytecode.
std::size_t start
Point to the INST_LOC_TABLE_START byte in the bytecode.
std::vector< internal::InstLoc > locations
std::vector< std::string > symbols
std::size_t end
Point to the byte following the last byte of the table in the bytecode.
std::size_t start
Point to the SYM_TABLE_START byte in the bytecode.
std::vector< Value > values
std::size_t end
Point to the byte following the last byte of the table in the bytecode.
std::size_t start
Point to the VAL_TABLE_START byte in the bytecode.