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