History

SBL file reader. Create and implement basic symbol interface used during debugging.

arr committed 9 years ago

Commit a3c184b1 parent 3b54d824 files · +852 −1Browse files

Showing the changes to src/asm/sbl_reader.cpp (1 of 4 files). Show all

Unified · Split

src/asm/sbl_reader.cpp+710−0
@@ -0,0 +1,710 @@
1//
2// Dis VM
3// File: sbl_reader.cpp
4// Author: arr
5//
6
7#include <cassert>
8#include <string>
9#include <vector>
10#include <cerrno>
11#include <memory>
12#include <vm_asm.h>
13#include <utils.h>
14#include <buffered_reader.h>
15
16using namespace disvm;
17using namespace disvm::symbol;
18
19namespace
20{
21 enum class sbl_version_t
22 {
23 unknown,
24 sbl_2_0,
25 sbl_2_1,
26 };
27
28 struct header_t
29 {
30 sbl_version_t version_id;
31 std::string module_name;
32 };
33
34 using file_table_t = std::vector<std::string>;
35
36 struct sbl_read_context_t
37 {
38 int32_t pc_count;
39 int32_t last_file;
40 int32_t last_line;
41 };
42
43 struct pc_item_t
44 {
45 source_ref_t source;
46 int32_t debug_statement;
47 };
48
49 using pc_table_t = std::vector<pc_item_t>;
50
51 struct type_t
52 {
53 type_class_t type_class;
54 int32_t value_index_1;
55 int32_t value_index_2;
56 };
57
58 struct id_item_t
59 {
60 uint32_t offset;
61 std::string name;
62 source_ref_t source;
63 type_t type;
64 };
65
66 using id_table_t = std::vector<id_item_t>;
67
68 struct func_item_t
69 {
70 runtime::vm_pc_t entry_pc;
71 runtime::vm_pc_t limit_pc;
72 std::string name;
73 id_table_t args;
74 id_table_t locals;
75 type_t return_type;
76 };
77
78 using func_table_t = std::vector<func_item_t>;
79
80 struct name_source_size_t
81 {
82 std::string name;
83 source_ref_t source;
84 int32_t size_in_bytes;
85 id_table_t id_table;
86 std::unique_ptr<std::vector<name_source_size_t>> tag_table;
87 };
88
89 using adt_item_t = name_source_size_t;
90
91 struct tuple_item_t
92 {
93 int32_t size_in_bytes;
94 id_table_t id_table;
95 };
96
97 struct type_table_t
98 {
99 static const uint32_t string_table_mask = 0x80000000;
100 static const uint32_t inner_type_table_mask = 0x40000000;
101 static const uint32_t source_table_mask = 0x20000000;
102 static const uint32_t adt_table_mask = 0x10000000;
103 static const uint32_t tuple_type_mask = 0x08000000;
104 static const uint32_t types_mask = 0;
105
106 std::vector<type_t> types;
107 std::vector<std::string> strings_table;
108 std::vector<type_t> inner_type_table;
109 std::vector<source_ref_t> source_table;
110 std::vector<name_source_size_t> adt_table;
111 std::vector<tuple_item_t> tuple_table;
112
113 template<typename T>
114 const T &resolve_table_index(int32_t idx) const;
115 };
116
117 template<>
118 const std::string &type_table_t::resolve_table_index<std::string>(int32_t idx) const
119 {
120 const auto idx_local = idx & ~string_table_mask;
121 if (0 > idx_local || idx_local >= strings_table.size())
122 throw std::out_of_range{ "Invalid string table index" };
123
124 return strings_table[idx_local];
125 }
126
127 template<>
128 const type_t &type_table_t::resolve_table_index<type_t>(int32_t idx) const
129 {
130 const auto idx_local = idx & ~inner_type_table_mask;
131 if (0 > idx_local || idx_local >= inner_type_table.size())
132 throw std::out_of_range{ "Invalid inner type table index" };
133
134 return inner_type_table[idx_local];
135 }
136
137 template<>
138 const source_ref_t &type_table_t::resolve_table_index<source_ref_t>(int32_t idx) const
139 {
140 const auto idx_local = idx & ~source_table_mask;
141 if (0 > idx_local || idx_local >= source_table.size())
142 throw std::out_of_range{ "Invalid source table index" };
143
144 return source_table[idx_local];
145 }
146
147 template<>
148 const name_source_size_t &type_table_t::resolve_table_index<name_source_size_t>(int32_t idx) const
149 {
150 const auto idx_local = idx & ~adt_table_mask;
151 if (0 > idx_local || idx_local >= adt_table.size())
152 throw std::out_of_range{ "Invalid adt table index" };
153
154 return adt_table[idx_local];
155 }
156
157 template<>
158 const tuple_item_t &type_table_t::resolve_table_index<tuple_item_t>(int32_t idx) const
159 {
160 const auto idx_local = idx & ~tuple_type_mask;
161 if (0 > idx_local || idx_local >= tuple_table.size())
162 throw std::out_of_range{ "Invalid tuple table index" };
163
164 return tuple_table[idx_local];
165 }
166
167 // sbl: header file-table pc-table type-table func-table data-table
168 struct sbl_file_t final
169 {
170 header_t header;
171
172 file_table_t file_table;
173 pc_table_t pc_table;
174 type_table_t type_table;
175 func_table_t func_table;
176 id_table_t data_table;
177 };
178
179 // header: <magic> '\n' module '\n'
180 // module: <string>
181 header_t read_header(util::buffered_reader_t &r)
182 {
183 header_t header;
184 auto v = r.get_as_string_until('\n');
185 if (v.compare("limbo .sbl 2.1") == 0)
186 header.version_id = sbl_version_t::sbl_2_1;
187 else if (v.compare("limbo .sbl 2.0") == 0)
188 header.version_id = sbl_version_t::sbl_2_0;
189 else
190 throw std::runtime_error{ "Unknown symbol file version" };
191
192 header.module_name = r.get_as_string_until('\n');
193
194 return header;
195 }
196
197 // file-table: <count> '\n' file*
198 // file: <string> '\n'
199 file_table_t read_file_table(util::buffered_reader_t &r)
200 {
201 auto count_str = r.get_as_string_until('\n');
202
203 file_table_t file_table;
204 const auto count = std::stoi(count_str);
205 file_table.resize(count);
206 for (auto i = 0; i < count; ++i)
207 {
208 auto file = r.get_as_string_until('\n');
209 file_table[i] = std::move(file);
210 }
211
212 return file_table;
213 }
214
215 // src: pos ',' pos
216 // pos: file':'? line'.'? char
217 // file: <int>
218 // line: <int>
219 // char: <int>
220 source_ref_t read_src_entry(const std::string &src_str, sbl_read_context_t &fc)
221 {
222 auto src_parts = util::split(src_str, ",");
223 if (src_parts.size() != 2)
224 throw std::runtime_error{ "Invalid source item" };
225
226 source_ref_t src;
227
228 // Initialize the source with the last state
229 src.source_id = fc.last_file;
230 src.begin_line = fc.last_line;
231 src.end_line = fc.last_line;
232 for (auto i = std::size_t{ 0 }; i < src_parts.size(); ++i)
233 {
234 const auto &pos = src_parts[i];
235 const auto len = pos.length();
236
237 // Parse position
238 for (auto j = std::size_t{ 0 }; j < len; ++j)
239 {
240 auto consumed = std::size_t{ 0 };
241 auto v = std::stoi(pos.data() + j, &consumed);
242 j += consumed;
243 if (j >= len)
244 {
245 if (i == 0)
246 src.begin_column = v;
247
248 src.end_column = v;
249 }
250 else if (pos[j] == '.')
251 {
252 if (i == 0)
253 src.begin_line = v;
254
255 src.end_line = v;
256 fc.last_line = v;
257 }
258 else if (pos[j] == ':')
259 {
260 assert((i == 0 || src.source_id == v) && "Source position should not change across statements");
261 src.source_id = v;
262 fc.last_file = v;
263 }
264 }
265 }
266
267 return src;
268 }
269
270 // pc-table: <count> '\n' pc-item*
271 // pc-item: src ' ' stmt '\n'
272 // stmt: <int>
273 pc_table_t read_pc_table(util::buffered_reader_t &r, sbl_read_context_t &fc)
274 {
275 auto count_str = r.get_as_string_until('\n');
276
277 uint32_t last_source = 0;
278 uint32_t last_line = 0;
279
280 pc_table_t pc_table;
281 const auto count = std::stoi(count_str);
282 pc_table.resize(count);
283 for (auto i = 0; i < count; ++i)
284 {
285 auto item = r.get_as_string_until('\n');
286
287 auto pc_item_parts = util::split(item);
288 if (pc_item_parts.size() != 2)
289 throw std::runtime_error{ "Invalid PC item" };
290
291 pc_item_t curr_item;
292 curr_item.source = read_src_entry(pc_item_parts[0], fc);
293 curr_item.debug_statement = std::stoi(pc_item_parts[1]);
294
295 // Record the current source and line ID since those are the new default values
296 last_source = curr_item.source.source_id;
297 last_line = curr_item.source.end_line;
298
299 pc_table[i] = std::move(curr_item);
300 }
301
302 fc.pc_count = count;
303 return pc_table;
304 }
305
306 // Forward declaration
307 type_t read_type(util::buffered_reader_t &, type_table_t &, sbl_read_context_t &);
308
309 // id-table: <count> '\n' id-item*
310 // id-item: id-offset ':' id-name ':' src ' ' type '\n'
311 // id-offset: <int>
312 // id-name: <string>
313 id_table_t read_id_table(util::buffered_reader_t &r, type_table_t &tt, sbl_read_context_t &fc)
314 {
315 auto count_str = r.get_as_string_until('\n');
316 int32_t count = std::stoi(count_str);
317
318 bool success;
319 uint8_t tmp;
320 id_table_t id_table;
321 id_table.resize(count);
322 for (auto i = 0; i < count; ++i)
323 {
324 const auto offset_part = r.get_as_string_until(':');
325 if (offset_part.empty())
326 throw std::runtime_error{ "Invalid ID table item (offset)" };
327
328 const auto name_part = r.get_as_string_until(':');
329 if (name_part.empty())
330 throw std::runtime_error{ "Invalid ID table item (name)" };
331
332 const auto source_part = r.get_as_string_until(' ');
333 if (source_part.empty())
334 throw std::runtime_error{ "Invalid ID table item (source)" };
335
336 id_item_t e;
337 e.offset = std::stoul(offset_part);
338 e.name = std::move(name_part);
339 e.source = read_src_entry(source_part, fc);
340 e.type = read_type(r, tt, fc);
341
342 std::tie(success, tmp) = r.get_next_byte();
343 if (!success || tmp != '\n')
344 throw std::runtime_error{ "Invalid ID table item" };
345
346 id_table[i] = std::move(e);
347 }
348
349 return id_table;
350 }
351
352 // adt-item: adt-name ' ' src ' ' size '\n' id-table
353 // adt-name: <string>
354 // size: <int>
355 //
356 // tag-table: <count> '\n' tag-item*
357 // tag-item: tag-name ':' src ' ' size '\n' id-table
358 // | tag-name ':' src '\n'
359 // tag-name: <string>
360 name_source_size_t create_name_source_size(std::vector<std::string> &parts, util::buffered_reader_t &r, type_table_t &tt, sbl_read_context_t &fc)
361 {
362 assert(parts.size() == 2 || parts.size() == 3);
363 name_source_size_t item;
364 item.name = std::move(parts[0]);
365 item.source = read_src_entry(parts[1], fc);
366
367 if (parts.size() == 3)
368 {
369 item.size_in_bytes = std::strtol(parts[2].c_str(), nullptr, 10);
370 if (item.size_in_bytes == 0 && errno != 0)
371 throw std::runtime_error{ "Invalid adt size" };
372
373 item.id_table = read_id_table(r, tt, fc);
374 }
375
376 return item;
377 }
378
379 // type:
380 // '@' type-index '\n' # Type table reference
381 // 'a' adt-item # ADT
382 // 'p' adt-item tag-table # ADT-Pick
383 // 't' size '.' id-table # Tuple
384 // 'm' module '\n' src # Module
385 // 'F' fn-name ' ' type # Function
386 // 'A' type # Array
387 // 'C' type # Channel
388 // 'L' type # List
389 // 'R' type # Ref
390 // 'n' # No type
391 // 'N' # Nil
392 // 'B' # Big
393 // 'b' # Byte
394 // 'i' # Int
395 // 'f' # Real
396 // 's' # String
397 // type-index: <int>
398 type_t read_type(util::buffered_reader_t &r, type_table_t &tt, sbl_read_context_t &fc)
399 {
400 bool success;
401 uint8_t tc_byte;
402 std::tie(success, tc_byte) = r.get_next_byte();
403 if (!success)
404 throw std::runtime_error{ "Invalid type" };
405
406 const type_class_t tc = static_cast<type_class_t>(tc_byte);
407 type_t t{ tc, 0, 0 };
408 switch (t.type_class)
409 {
410 case type_class_t::none:
411 case type_class_t::nil:
412 case type_class_t::big:
413 case type_class_t::byte:
414 case type_class_t::integer:
415 case type_class_t::real:
416 case type_class_t::string:
417 case type_class_t::poly:
418 break;
419
420 case type_class_t::array:
421 case type_class_t::channel:
422 case type_class_t::list:
423 case type_class_t::ref:
424 t.value_index_1 = type_table_t::inner_type_table_mask | tt.inner_type_table.size();
425 tt.inner_type_table.push_back(read_type(r, tt, fc));
426 break;
427
428 case type_class_t::type_index:
429 {
430 auto type_index = r.get_as_string_until('\n');
431 const auto idx = std::stoi(type_index);
432 if (idx == 0)
433 throw std::runtime_error{ "Invalid type index" };
434
435 t.value_index_1 = type_table_t::types_mask | idx;
436 break;
437 }
438 case type_class_t::adt:
439 case type_class_t::adt_pick:
440 {
441 auto adt_line = r.get_as_string_until('\n');
442 auto adt_parts = util::split(adt_line);
443 if (adt_parts.size() != 3)
444 throw std::runtime_error{ "Invalid adt item" };
445
446 auto adt_item = create_name_source_size(adt_parts, r, tt, fc);
447
448 // Check if the adt has a tag table
449 if (t.type_class == type_class_t::adt_pick)
450 {
451 adt_item.tag_table = std::make_unique<std::vector<name_source_size_t>>();
452 auto tmp = r.get_as_string_until('\n');
453
454 const auto count = std::stoi(tmp);
455 for (auto i = 0; i < count; ++i)
456 {
457 tmp = r.get_as_string_until('\n');
458 auto tag_parts = util::split(tmp, " :");
459 if (tag_parts.size() != 2 && tag_parts.size() != 3)
460 throw std::runtime_error{ "Invalid tag item" };
461
462 auto tag = create_name_source_size(tag_parts, r, tt, fc);
463 adt_item.tag_table->push_back(std::move(tag));
464 }
465 }
466
467 t.value_index_1 = type_table_t::adt_table_mask | tt.adt_table.size();
468 tt.adt_table.push_back(std::move(adt_item));
469 break;
470 }
471 case type_class_t::tuple:
472 {
473 // Record tuple size
474 t.value_index_1 = type_table_t::tuple_type_mask | tt.tuple_table.size();
475
476 tuple_item_t tuple{};
477 auto size_str = r.get_as_string_until('.');
478 tuple.size_in_bytes = std::stoi(size_str);
479
480 tuple.id_table = read_id_table(r, tt, fc);
481 tt.tuple_table.push_back(std::move(tuple));
482 break;
483 }
484 case type_class_t::module:
485 {
486 // Record module name
487 t.value_index_1 = type_table_t::string_table_mask | tt.strings_table.size();
488 tt.strings_table.push_back(r.get_as_string_until('\n'));
489
490 auto src_str = r.get_as_string_until('\n');
491 t.value_index_2 = type_table_t::source_table_mask | tt.source_table.size();
492 tt.source_table.push_back(read_src_entry(src_str, fc));
493 break;
494 }
495
496 case type_class_t::function:
497 {
498 // Record function name
499 t.value_index_1 = type_table_t::string_table_mask | tt.strings_table.size();
500 auto name = r.get_as_string_until(' ');
501 if (name.empty())
502 throw std::runtime_error{ "Invalid function type" };
503
504 tt.strings_table.push_back(std::move(name));
505
506 // Record return type
507 t.value_index_2 = type_table_t::inner_type_table_mask | tt.inner_type_table.size();
508 tt.inner_type_table.push_back(read_type(r, tt, fc));
509 break;
510 }
511
512 default:
513 throw std::runtime_error{ "Invalid type value" };
514 }
515
516 return t;
517 }
518
519 // type-table: <count> '\n' type*
520 type_table_t read_type_table(util::buffered_reader_t &r, sbl_read_context_t &fc)
521 {
522 auto count_str = r.get_as_string_until('\n');
523
524 type_table_t type_table;
525 const auto count = std::stoi(count_str);
526 type_table.types.resize(count);
527 for (auto i = 0; i < count; ++i)
528 type_table.types[i] = read_type(r, type_table, fc);
529
530 return type_table;
531 }
532
533 // func-table: <count> '\n' fn-item*
534 // fn-item: fn-pc ':' fn-name '\n' args locals return
535 // fn-pc: <int>
536 // fn-name: <string>
537 // args: id-table
538 // locals: id-table
539 // return: type
540 func_table_t read_func_table(util::buffered_reader_t &r, type_table_t &tt, sbl_read_context_t &fc)
541 {
542 auto count_str = r.get_as_string_until('\n');
543
544 const auto last_pc = fc.pc_count - 1;
545 func_table_t func_table;
546 const auto count = std::stoi(count_str);
547 func_table.resize(count);
548 for (auto i = 0; i < count; ++i)
549 {
550 func_item_t func;
551 func.entry_pc = std::stoi(r.get_as_string_until(':'));
552 func.limit_pc = last_pc;
553 func.name = std::move(r.get_as_string_until('\n'));
554 func.args = read_id_table(r, tt, fc);
555 func.locals = read_id_table(r, tt, fc);
556 func.return_type = read_type(r, tt, fc);
557
558 // Update the previous functions limit
559 if (i > 0)
560 {
561 assert(func_table[i - 1].entry_pc < func.entry_pc && "Function table in sbl file is assumed to be in pc order");
562 func_table[i - 1].limit_pc = func.entry_pc - 1;
563 }
564
565 func_table[i] = std::move(func);
566 }
567
568 return func_table;
569 }
570
571 // data-table: id-table
572 id_table_t read_data_table(util::buffered_reader_t &r, type_table_t &tt, sbl_read_context_t &fc)
573 {
574 return read_id_table(r, tt, fc);
575 }
576}
577
578namespace
579{
580 class sbl_symbol_debug final : public symbol_debug_t
581 {
582 public:
583 sbl_symbol_debug(std::shared_ptr<sbl_file_t> f)
584 : _current_pc{ 0 }
585 , _file{ f }
586 {
587 assert(_file != nullptr);
588 }
589
590 public: // symbol_debug_t
591 void set_current_pc(disvm::runtime::vm_pc_t pc) override
592 {
593 assert(0 <= pc && pc < static_cast<int32_t>(_file->pc_table.size()));
594 _current_pc = pc;
595 }
596
597 bool try_advance_pc(advance_pc_t adv, disvm::runtime::vm_pc_t *pc_after_advance) override
598 {
599 runtime::vm_pc_t new_pc = _current_pc;
600 switch (adv)
601 {
602 case advance_pc_t::next_debug_statement:
603 {
604 auto iter = std::next(std::cbegin(_file->pc_table), new_pc);
605 const auto current_dbg = iter->debug_statement;
606
607 // Iterate until the next pc entry that is not the same debug statement or the end.
608 while (iter != std::cend(_file->pc_table) && iter->debug_statement == current_dbg)
609 {
610 ++iter;
611 }
612
613 if (iter == std::cend(_file->pc_table))
614 return false;
615
616 new_pc = std::distance(std::cbegin(_file->pc_table), iter);
617 break;
618 }
619
620 case advance_pc_t::next_pc:
621 if (new_pc + 1 >= static_cast<runtime::vm_pc_t>(_file->pc_table.size()))
622 return false;
623 ++new_pc;
624 break;
625
626 case advance_pc_t::current:
627 assert(pc_after_advance != nullptr);
628 break;
629
630 default:
631 throw std::runtime_error{ "Invalid advance value" };
632 }
633
634 if (pc_after_advance != nullptr)
635 *pc_after_advance = new_pc;
636
637 _current_pc = new_pc;
638 return true;
639 }
640
641 const std::string &current_function_name() const override
642 {
643 for (auto &e : _file->func_table)
644 {
645 if (e.entry_pc <= _current_pc && _current_pc <= e.limit_pc)
646 return e.name;
647 }
648
649 throw std::runtime_error{ "Unknown function" };
650 }
651
652 source_ref_t current_source_location() const override
653 {
654 assert(0 <= _current_pc && _current_pc < static_cast<int32_t>(_file->pc_table.size()));
655 return _file->pc_table[_current_pc].source;
656 }
657
658 const std::string &get_source_by_id(int32_t source_id) const override
659 {
660 assert(0 <= source_id && source_id < static_cast<int32_t>(_file->file_table.size()));
661 return _file->file_table[source_id];
662 }
663
664 private:
665 runtime::vm_pc_t _current_pc;
666 std::shared_ptr<sbl_file_t> _file;
667 };
668
669 class sbl_symbol_data final : public symbol_data_t
670 {
671 public:
672 sbl_symbol_data(std::shared_ptr<sbl_file_t> f)
673 : _file{ f }
674 {
675 assert(_file != nullptr);
676 }
677
678 public: // symbol_data_t
679 const std::string & get_module_name() const override
680 {
681 return _file->header.module_name;
682 }
683
684 std::unique_ptr<symbol_debug_t> get_debug() override
685 {
686 return std::make_unique<sbl_symbol_debug>(_file);
687 }
688
689 private:
690 std::shared_ptr<sbl_file_t> _file;
691 };
692}
693
694std::unique_ptr<symbol_data_t> disvm::symbol::read(std::istream &f)
695{
696 auto file = std::make_shared<sbl_file_t>();
697
698 util::buffered_reader_t reader{ f };
699
700 file->header = read_header(reader);
701 file->file_table = read_file_table(reader);
702
703 sbl_read_context_t fc{};
704 file->pc_table = read_pc_table(reader, fc);
705 file->type_table = read_type_table(reader, fc);
706 file->func_table = read_func_table(reader, file->type_table, fc);
707 file->data_table = read_data_table(reader, file->type_table, fc);
708
709 return std::make_unique<sbl_symbol_data>(file);
710}