|
10 | 10 | #include "lldb/Utility/LLDBAssert.h"
|
11 | 11 | #include "lldb/Utility/LLDBLog.h"
|
12 | 12 | #include "lldb/Utility/Log.h"
|
| 13 | +#include "clang/AST/ASTContext.h" |
13 | 14 | #include "clang/AST/Decl.h"
|
14 | 15 | #include "clang/AST/DeclCXX.h"
|
15 | 16 | #include "clang/AST/DeclObjC.h"
|
| 17 | +#include "clang/AST/RecordLayout.h" |
16 | 18 | #include "clang/Sema/Lookup.h"
|
17 | 19 | #include "clang/Sema/Sema.h"
|
18 | 20 | #include "llvm/Support/raw_ostream.h"
|
|
26 | 28 |
|
27 | 29 | #include <memory>
|
28 | 30 | #include <optional>
|
| 31 | +#include <type_traits> |
29 | 32 |
|
30 | 33 | using namespace lldb_private;
|
31 | 34 | using namespace clang;
|
@@ -517,6 +520,236 @@ bool ClangASTImporter::CompleteType(const CompilerType &compiler_type) {
|
517 | 520 | return false;
|
518 | 521 | }
|
519 | 522 |
|
| 523 | +/// Copy layout information from \ref source_map to the \ref destination_map. |
| 524 | +/// |
| 525 | +/// In the process of copying over layout info, we may need to import |
| 526 | +/// decls from the \ref source_map. This function will use the supplied |
| 527 | +/// \ref importer to import the necessary decls into \ref dest_ctx. |
| 528 | +/// |
| 529 | +/// \param[in,out] dest_ctx Destination ASTContext into which we import |
| 530 | +/// decls from the \ref source_map. |
| 531 | +/// \param[out] destination_map A map from decls in \ref dest_ctx to an |
| 532 | +/// integral offest, which will be copies |
| 533 | +/// of the decl/offest pairs in \ref source_map |
| 534 | +/// if successful. |
| 535 | +/// \param[in] source_map A map from decls to integral offests. These will |
| 536 | +/// be copied into \ref destination_map. |
| 537 | +/// \param[in,out] importer Used to import decls into \ref dest_ctx. |
| 538 | +/// |
| 539 | +/// \returns On success, will return 'true' and the offsets in \ref |
| 540 | +/// destination_map |
| 541 | +/// are usable copies of \ref source_map. |
| 542 | +template <class D, class O> |
| 543 | +static bool ImportOffsetMap(clang::ASTContext *dest_ctx, |
| 544 | + llvm::DenseMap<const D *, O> &destination_map, |
| 545 | + llvm::DenseMap<const D *, O> &source_map, |
| 546 | + ClangASTImporter &importer) { |
| 547 | + // When importing fields into a new record, clang has a hard requirement that |
| 548 | + // fields be imported in field offset order. Since they are stored in a |
| 549 | + // DenseMap with a pointer as the key type, this means we cannot simply |
| 550 | + // iterate over the map, as the order will be non-deterministic. Instead we |
| 551 | + // have to sort by the offset and then insert in sorted order. |
| 552 | + typedef llvm::DenseMap<const D *, O> MapType; |
| 553 | + typedef typename MapType::value_type PairType; |
| 554 | + std::vector<PairType> sorted_items; |
| 555 | + sorted_items.reserve(source_map.size()); |
| 556 | + sorted_items.assign(source_map.begin(), source_map.end()); |
| 557 | + llvm::sort(sorted_items, llvm::less_second()); |
| 558 | + |
| 559 | + for (const auto &item : sorted_items) { |
| 560 | + DeclFromUser<D> user_decl(const_cast<D *>(item.first)); |
| 561 | + DeclFromParser<D> parser_decl(user_decl.Import(dest_ctx, importer)); |
| 562 | + if (parser_decl.IsInvalid()) |
| 563 | + return false; |
| 564 | + destination_map.insert( |
| 565 | + std::pair<const D *, O>(parser_decl.decl, item.second)); |
| 566 | + } |
| 567 | + |
| 568 | + return true; |
| 569 | +} |
| 570 | + |
| 571 | +/// Given a CXXRecordDecl, will calculate and populate \ref base_offsets |
| 572 | +/// with the integral offsets of any of its (possibly virtual) base classes. |
| 573 | +/// |
| 574 | +/// \param[in] record_layout ASTRecordLayout of \ref record. |
| 575 | +/// \param[in] record The record that we're calculating the base layouts of. |
| 576 | +/// \param[out] base_offsets Map of base-class decl to integral offset which |
| 577 | +/// this function will fill in. |
| 578 | +/// |
| 579 | +/// \returns On success, will return 'true' and the offsets in \ref base_offsets |
| 580 | +/// are usable. |
| 581 | +template <bool IsVirtual> |
| 582 | +bool ExtractBaseOffsets(const ASTRecordLayout &record_layout, |
| 583 | + DeclFromUser<const CXXRecordDecl> &record, |
| 584 | + llvm::DenseMap<const clang::CXXRecordDecl *, |
| 585 | + clang::CharUnits> &base_offsets) { |
| 586 | + for (CXXRecordDecl::base_class_const_iterator |
| 587 | + bi = (IsVirtual ? record->vbases_begin() : record->bases_begin()), |
| 588 | + be = (IsVirtual ? record->vbases_end() : record->bases_end()); |
| 589 | + bi != be; ++bi) { |
| 590 | + if (!IsVirtual && bi->isVirtual()) |
| 591 | + continue; |
| 592 | + |
| 593 | + const clang::Type *origin_base_type = bi->getType().getTypePtr(); |
| 594 | + const clang::RecordType *origin_base_record_type = |
| 595 | + origin_base_type->getAs<RecordType>(); |
| 596 | + |
| 597 | + if (!origin_base_record_type) |
| 598 | + return false; |
| 599 | + |
| 600 | + DeclFromUser<RecordDecl> origin_base_record( |
| 601 | + origin_base_record_type->getDecl()); |
| 602 | + |
| 603 | + if (origin_base_record.IsInvalid()) |
| 604 | + return false; |
| 605 | + |
| 606 | + DeclFromUser<CXXRecordDecl> origin_base_cxx_record( |
| 607 | + DynCast<CXXRecordDecl>(origin_base_record)); |
| 608 | + |
| 609 | + if (origin_base_cxx_record.IsInvalid()) |
| 610 | + return false; |
| 611 | + |
| 612 | + CharUnits base_offset; |
| 613 | + |
| 614 | + if (IsVirtual) |
| 615 | + base_offset = |
| 616 | + record_layout.getVBaseClassOffset(origin_base_cxx_record.decl); |
| 617 | + else |
| 618 | + base_offset = |
| 619 | + record_layout.getBaseClassOffset(origin_base_cxx_record.decl); |
| 620 | + |
| 621 | + base_offsets.insert(std::pair<const CXXRecordDecl *, CharUnits>( |
| 622 | + origin_base_cxx_record.decl, base_offset)); |
| 623 | + } |
| 624 | + |
| 625 | + return true; |
| 626 | +} |
| 627 | + |
| 628 | +bool ClangASTImporter::importRecordLayoutFromOrigin( |
| 629 | + const RecordDecl *record, uint64_t &size, uint64_t &alignment, |
| 630 | + llvm::DenseMap<const clang::FieldDecl *, uint64_t> &field_offsets, |
| 631 | + llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> |
| 632 | + &base_offsets, |
| 633 | + llvm::DenseMap<const clang::CXXRecordDecl *, clang::CharUnits> |
| 634 | + &vbase_offsets) { |
| 635 | + |
| 636 | + Log *log = GetLog(LLDBLog::Expressions); |
| 637 | + |
| 638 | + clang::ASTContext &dest_ctx = record->getASTContext(); |
| 639 | + LLDB_LOG(log, |
| 640 | + "LayoutRecordType on (ASTContext*){0} '{1}' for (RecordDecl*)" |
| 641 | + "{2} [name = '{3}']", |
| 642 | + &dest_ctx, |
| 643 | + TypeSystemClang::GetASTContext(&dest_ctx)->getDisplayName(), record, |
| 644 | + record->getName()); |
| 645 | + |
| 646 | + DeclFromParser<const RecordDecl> parser_record(record); |
| 647 | + DeclFromUser<const RecordDecl> origin_record(parser_record.GetOrigin(*this)); |
| 648 | + |
| 649 | + if (origin_record.IsInvalid()) |
| 650 | + return false; |
| 651 | + |
| 652 | + std::remove_reference_t<decltype(field_offsets)> origin_field_offsets; |
| 653 | + std::remove_reference_t<decltype(base_offsets)> origin_base_offsets; |
| 654 | + std::remove_reference_t<decltype(vbase_offsets)> origin_virtual_base_offsets; |
| 655 | + |
| 656 | + TypeSystemClang::GetCompleteDecl( |
| 657 | + &origin_record->getASTContext(), |
| 658 | + const_cast<RecordDecl *>(origin_record.decl)); |
| 659 | + |
| 660 | + clang::RecordDecl *definition = origin_record.decl->getDefinition(); |
| 661 | + if (!definition || !definition->isCompleteDefinition()) |
| 662 | + return false; |
| 663 | + |
| 664 | + const ASTRecordLayout &record_layout( |
| 665 | + origin_record->getASTContext().getASTRecordLayout(origin_record.decl)); |
| 666 | + |
| 667 | + int field_idx = 0, field_count = record_layout.getFieldCount(); |
| 668 | + |
| 669 | + for (RecordDecl::field_iterator fi = origin_record->field_begin(), |
| 670 | + fe = origin_record->field_end(); |
| 671 | + fi != fe; ++fi) { |
| 672 | + if (field_idx >= field_count) |
| 673 | + return false; // Layout didn't go well. Bail out. |
| 674 | + |
| 675 | + uint64_t field_offset = record_layout.getFieldOffset(field_idx); |
| 676 | + |
| 677 | + origin_field_offsets.insert( |
| 678 | + std::pair<const FieldDecl *, uint64_t>(*fi, field_offset)); |
| 679 | + |
| 680 | + field_idx++; |
| 681 | + } |
| 682 | + |
| 683 | + DeclFromUser<const CXXRecordDecl> origin_cxx_record( |
| 684 | + DynCast<const CXXRecordDecl>(origin_record)); |
| 685 | + |
| 686 | + if (origin_cxx_record.IsValid()) { |
| 687 | + if (!ExtractBaseOffsets<false>(record_layout, origin_cxx_record, |
| 688 | + origin_base_offsets) || |
| 689 | + !ExtractBaseOffsets<true>(record_layout, origin_cxx_record, |
| 690 | + origin_virtual_base_offsets)) |
| 691 | + return false; |
| 692 | + } |
| 693 | + |
| 694 | + if (!ImportOffsetMap(&dest_ctx, field_offsets, origin_field_offsets, *this) || |
| 695 | + !ImportOffsetMap(&dest_ctx, base_offsets, origin_base_offsets, *this) || |
| 696 | + !ImportOffsetMap(&dest_ctx, vbase_offsets, origin_virtual_base_offsets, |
| 697 | + *this)) |
| 698 | + return false; |
| 699 | + |
| 700 | + size = record_layout.getSize().getQuantity() * dest_ctx.getCharWidth(); |
| 701 | + alignment = |
| 702 | + record_layout.getAlignment().getQuantity() * dest_ctx.getCharWidth(); |
| 703 | + |
| 704 | + if (log) { |
| 705 | + LLDB_LOG(log, "LRT returned:"); |
| 706 | + LLDB_LOG(log, "LRT Original = (RecordDecl*){0}", |
| 707 | + static_cast<const void *>(origin_record.decl)); |
| 708 | + LLDB_LOG(log, "LRT Size = {0}", size); |
| 709 | + LLDB_LOG(log, "LRT Alignment = {0}", alignment); |
| 710 | + LLDB_LOG(log, "LRT Fields:"); |
| 711 | + for (RecordDecl::field_iterator fi = record->field_begin(), |
| 712 | + fe = record->field_end(); |
| 713 | + fi != fe; ++fi) { |
| 714 | + LLDB_LOG(log, |
| 715 | + "LRT (FieldDecl*){0}, Name = '{1}', Type = '{2}', Offset = " |
| 716 | + "{3} bits", |
| 717 | + *fi, fi->getName(), fi->getType().getAsString(), |
| 718 | + field_offsets[*fi]); |
| 719 | + } |
| 720 | + DeclFromParser<const CXXRecordDecl> parser_cxx_record = |
| 721 | + DynCast<const CXXRecordDecl>(parser_record); |
| 722 | + if (parser_cxx_record.IsValid()) { |
| 723 | + LLDB_LOG(log, "LRT Bases:"); |
| 724 | + for (CXXRecordDecl::base_class_const_iterator |
| 725 | + bi = parser_cxx_record->bases_begin(), |
| 726 | + be = parser_cxx_record->bases_end(); |
| 727 | + bi != be; ++bi) { |
| 728 | + bool is_virtual = bi->isVirtual(); |
| 729 | + |
| 730 | + QualType base_type = bi->getType(); |
| 731 | + const RecordType *base_record_type = base_type->getAs<RecordType>(); |
| 732 | + DeclFromParser<RecordDecl> base_record(base_record_type->getDecl()); |
| 733 | + DeclFromParser<CXXRecordDecl> base_cxx_record = |
| 734 | + DynCast<CXXRecordDecl>(base_record); |
| 735 | + |
| 736 | + LLDB_LOG(log, |
| 737 | + "LRT {0}(CXXRecordDecl*){1}, Name = '{2}', Offset = " |
| 738 | + "{3} chars", |
| 739 | + (is_virtual ? "Virtual " : ""), base_cxx_record.decl, |
| 740 | + base_cxx_record.decl->getName(), |
| 741 | + (is_virtual |
| 742 | + ? vbase_offsets[base_cxx_record.decl].getQuantity() |
| 743 | + : base_offsets[base_cxx_record.decl].getQuantity())); |
| 744 | + } |
| 745 | + } else { |
| 746 | + LLDB_LOG(log, "LRD Not a CXXRecord, so no bases"); |
| 747 | + } |
| 748 | + } |
| 749 | + |
| 750 | + return true; |
| 751 | +} |
| 752 | + |
520 | 753 | bool ClangASTImporter::LayoutRecordType(
|
521 | 754 | const clang::RecordDecl *record_decl, uint64_t &bit_size,
|
522 | 755 | uint64_t &alignment,
|
|
0 commit comments