|
14 | 14 |
|
15 | 15 | #include "lldb/Core/PluginManager.h"
|
16 | 16 | #include "lldb/Interpreter/OptionValueProperties.h"
|
| 17 | +#include "lldb/Symbol/CompileUnit.h" |
17 | 18 | #include "lldb/Symbol/SymbolFile.h"
|
18 | 19 | #include "lldb/Symbol/TypeList.h"
|
| 20 | +#include "lldb/Symbol/VariableList.h" |
19 | 21 | #include "lldb/Target/Target.h"
|
20 | 22 | #include "lldb/Utility/Stream.h"
|
21 | 23 |
|
@@ -574,3 +576,165 @@ bool SourceLanguage::IsObjC() const {
|
574 | 576 | bool SourceLanguage::IsCPlusPlus() const {
|
575 | 577 | return name == llvm::dwarf::DW_LNAME_C_plus_plus;
|
576 | 578 | }
|
| 579 | + |
| 580 | +// BEGIN SWIFT |
| 581 | +// Implement LanguageCPlusPlus::GetParentNameIfClosure and upstream this. |
| 582 | +// rdar://152321823 |
| 583 | + |
| 584 | +/// If `sc` represents a "closure"-like function (according to |
| 585 | +/// Language::GetParentNameIfClosure), returns sc.function and all parent |
| 586 | +/// functions up to and including the first non-closure-like function. If `sc` |
| 587 | +/// is not a closure, or if the query does not make sense for `language`, |
| 588 | +/// returns a list containing only sc.function. |
| 589 | +static llvm::SmallVector<Function *> |
| 590 | +GetParentFunctionsWhileClosure(const SymbolContext &sc, |
| 591 | + const Language &language) { |
| 592 | + // The algorithm below terminates on the assumption that |
| 593 | + // `GetParentNameIfClosure` produces an empty string when composing that |
| 594 | + // function with itself enough times. For safety, define an upper limit. |
| 595 | + constexpr auto upper_limit = 8; |
| 596 | + |
| 597 | + llvm::SmallVector<Function *> parents; |
| 598 | + Function *root = sc.function; |
| 599 | + if (root == nullptr) |
| 600 | + return parents; |
| 601 | + |
| 602 | + parents.push_back(root); |
| 603 | + for (int idx = 0; idx < upper_limit; idx++) { |
| 604 | + ConstString mangled = root->GetMangled().GetMangledName(); |
| 605 | + std::string parent = language.GetParentNameIfClosure(mangled); |
| 606 | + if (parent.empty()) |
| 607 | + break; |
| 608 | + |
| 609 | + // Find the enclosing function, if it exists. |
| 610 | + SymbolContextList sc_list; |
| 611 | + Module::LookupInfo lookup_info( |
| 612 | + ConstString(parent), lldb::FunctionNameType::eFunctionNameTypeFull, |
| 613 | + lldb::eLanguageTypeSwift); |
| 614 | + sc.module_sp->FindFunctions(lookup_info, CompilerDeclContext(), |
| 615 | + ModuleFunctionSearchOptions(), sc_list); |
| 616 | + if (sc_list.GetSize() != 1 || sc_list[0].function == nullptr) |
| 617 | + break; |
| 618 | + parents.push_back(sc_list[0].function); |
| 619 | + root = sc_list[0].function; |
| 620 | + } |
| 621 | + return parents; |
| 622 | +} |
| 623 | + |
| 624 | +/// Scans the line table of `function` looking for the first entry whose line |
| 625 | +/// number is `line_number`. If no such entry is found, returns the entry |
| 626 | +/// closest to but after `line_number`. |
| 627 | +static std::optional<Address> FindAddressForLineNumber(Function &function, |
| 628 | + uint32_t line_number) { |
| 629 | + CompileUnit *cu = function.GetCompileUnit(); |
| 630 | + LineTable *line_table = cu ? cu->GetLineTable() : nullptr; |
| 631 | + if (line_table == nullptr) |
| 632 | + return std::nullopt; |
| 633 | + |
| 634 | + // Get the first line entry for this function. |
| 635 | + AddressRange func_range = function.GetAddressRange(); |
| 636 | + uint32_t first_entry_idx; |
| 637 | + { |
| 638 | + LineEntry first_line_entry; |
| 639 | + line_table->FindLineEntryByAddress(func_range.GetBaseAddress(), |
| 640 | + first_line_entry, &first_entry_idx); |
| 641 | + } |
| 642 | + |
| 643 | + LineEntry best_match; |
| 644 | + for (uint32_t entry_idx = first_entry_idx; entry_idx < line_table->GetSize(); |
| 645 | + entry_idx++) { |
| 646 | + LineEntry next_line; |
| 647 | + line_table->GetLineEntryAtIndex(entry_idx, next_line); |
| 648 | + |
| 649 | + // Stop if this entry is outside the range of `function`. |
| 650 | + Address base_addr = next_line.range.GetBaseAddress(); |
| 651 | + if (!func_range.ContainsFileAddress(base_addr)) |
| 652 | + break; |
| 653 | + |
| 654 | + // Stop on an exact match. |
| 655 | + if (next_line.line == line_number) { |
| 656 | + best_match = next_line; |
| 657 | + break; |
| 658 | + } |
| 659 | + |
| 660 | + // Otherwise, keep track of the best match so far. |
| 661 | + if (next_line.line > line_number && next_line.line < best_match.line) |
| 662 | + best_match = next_line; |
| 663 | + } |
| 664 | + |
| 665 | + return best_match.range.GetBaseAddress(); |
| 666 | +} |
| 667 | + |
| 668 | +/// Given a list of functions, returns a map: Function -> VariableList |
| 669 | +/// containing local variables of each function. |
| 670 | +static std::map<Function *, VariableList> |
| 671 | +GetFuncToLocalVariablesMap(llvm::ArrayRef<Function *> funcs) { |
| 672 | + std::map<Function *, VariableList> map; |
| 673 | + for (Function *function : funcs) { |
| 674 | + VariableList &variable_list = map[function]; |
| 675 | + Block &block = function->GetBlock(true /*can_create=*/); |
| 676 | + block.AppendBlockVariables( |
| 677 | + true /*can_create=*/, true /*get_child_block_variables=*/, |
| 678 | + true /*stop_if_child_block_is_inlined_function=*/, |
| 679 | + [](Variable *v) { return true; }, &variable_list); |
| 680 | + } |
| 681 | + return map; |
| 682 | +} |
| 683 | + |
| 684 | +/// Returns the first line associated with `function`. |
| 685 | +static uint32_t GetLineNumberForFunction(Function &function) { |
| 686 | + FileSpec filespec; |
| 687 | + uint32_t line_num = 0; |
| 688 | + function.GetStartLineSourceInfo(filespec, line_num); |
| 689 | + return line_num; |
| 690 | +} |
| 691 | + |
| 692 | +/// Checks if `var` is in scope inside `function` at line `line_number`. |
| 693 | +/// If this check can't be done, a best-effort comparison of: |
| 694 | +/// line_number >= var.line_number |
| 695 | +/// is performed. |
| 696 | +static bool IsVariableInScopeAtLine(uint32_t line_number, |
| 697 | + std::optional<Address> line_addr, |
| 698 | + Variable &var) { |
| 699 | + auto fallback_line_comp = [&] { |
| 700 | + return line_number >= var.GetDeclaration().GetLine(); |
| 701 | + }; |
| 702 | + |
| 703 | + if (!line_addr) |
| 704 | + return fallback_line_comp(); |
| 705 | + |
| 706 | + Block *defining_block = line_addr->CalculateSymbolContextBlock(); |
| 707 | + if (defining_block) |
| 708 | + return var.IsInScope(*defining_block, *line_addr); |
| 709 | + return fallback_line_comp(); |
| 710 | +} |
| 711 | + |
| 712 | +Function *Language::FindParentOfClosureWithVariable( |
| 713 | + llvm::StringRef variable_name, const SymbolContext &closure_sc) const { |
| 714 | + llvm::SmallVector<Function *> function_chain = |
| 715 | + GetParentFunctionsWhileClosure(closure_sc, *this); |
| 716 | + if (function_chain.empty()) |
| 717 | + return nullptr; |
| 718 | + |
| 719 | + std::map<Function *, VariableList> func_to_locals = |
| 720 | + GetFuncToLocalVariablesMap(function_chain); |
| 721 | + |
| 722 | + llvm::ArrayRef<Function *> children = |
| 723 | + llvm::ArrayRef(function_chain).drop_back(); |
| 724 | + llvm::ArrayRef<Function *> parents = |
| 725 | + llvm::ArrayRef(function_chain).drop_front(); |
| 726 | + |
| 727 | + for (auto [parent, child] : llvm::zip_equal(parents, children)) { |
| 728 | + VariableList &parent_variables = func_to_locals[parent]; |
| 729 | + uint32_t child_line_number = GetLineNumberForFunction(*child); |
| 730 | + std::optional<Address> parent_line_addr = |
| 731 | + FindAddressForLineNumber(*parent, child_line_number); |
| 732 | + |
| 733 | + for (const VariableSP &var : parent_variables) |
| 734 | + if (var->GetName() == variable_name && |
| 735 | + IsVariableInScopeAtLine(child_line_number, parent_line_addr, *var)) |
| 736 | + return parent; |
| 737 | + } |
| 738 | + return nullptr; |
| 739 | +} |
| 740 | +// END SWIFT |
0 commit comments