| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "src/developer/debug/zxdb/expr/index_walker.h" |
| |
| #include <ctype.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <string.h> |
| |
| #include <string_view> |
| |
| #include "src/developer/debug/zxdb/common/err.h" |
| #include "src/developer/debug/zxdb/expr/expr_parser.h" |
| #include "src/developer/debug/zxdb/symbols/index.h" |
| #include "src/developer/debug/zxdb/symbols/index_node.h" |
| |
| namespace zxdb { |
| |
| namespace { |
| |
| // We don't expect to have identifiers with whitespace in them. If somebody does "Foo < Bar>" stop |
| // considering the name at the space. |
| inline bool IsNameEnd(char ch) { return isspace(ch) || ch == '<'; } |
| |
| // Finds all anonymous children of the nodes in the given stage and appends them recursively until |
| // there are no more to add. |
| // |
| // In the future we may want an option to trigger whether this function is called or not. |
| void AddAnonymousChildrenToStage(IndexWalker::Stage* stage) { |
| // This implements a breadth-first search, adding all unnamed items. |
| const std::string kEmpty; |
| |
| size_t last_pass_begin = 0; |
| while (last_pass_begin < stage->size()) { |
| size_t last_pass_end = stage->size(); |
| for (size_t i = last_pass_begin; i < last_pass_end; i++) { |
| const IndexNode* node = (*stage)[i]; |
| |
| // Add unnamed items. The common case is anonymous namespaces but we might also have unnamed |
| // types for anonymous enums and structs. |
| if (auto found = node->namespaces().find(kEmpty); found != node->namespaces().end()) |
| stage->push_back(&found->second); |
| if (auto found = node->types().find(kEmpty); found != node->types().end()) |
| stage->push_back(&found->second); |
| } |
| last_pass_begin = last_pass_end; |
| } |
| } |
| |
| } // namespace |
| |
| IndexWalker::IndexWalker(const Index* index) { |
| // Prefer not to reallocate the vector-of-vectors. It is rare for C++ namespace hierarchies to |
| // be more than a couple of components long, so this number should cover most cases. |
| path_.reserve(8); |
| |
| path_.push_back({&index->root()}); |
| AddAnonymousChildrenToStage(&path_[0]); |
| } |
| |
| IndexWalker::~IndexWalker() = default; |
| |
| bool IndexWalker::WalkUp() { |
| if (path_.size() > 1) { |
| // Don't walk above the root. |
| path_.pop_back(); |
| return true; |
| } |
| return false; |
| } |
| |
| // TODO(bug 6410) When we encounter an "inline" namespace, implicitly walk into it here, or have |
| // that controllable as an option. Inline namespaces produce a namespace with an implicit "using" |
| // statement. |
| bool IndexWalker::WalkInto(const ParsedIdentifierComponent& comp) { |
| const Stage& old_stage = path_.back(); |
| |
| const std::string& comp_name = comp.name(); |
| if (comp_name.empty()) |
| return true; // No-op. |
| |
| Stage new_stage; |
| for (const auto* old_node : old_stage) { |
| for (int i = 0; i < static_cast<int>(IndexNode::Kind::kEndPhysical); i++) { |
| const IndexNode::Map& map = old_node->MapForKind(static_cast<IndexNode::Kind>(i)); |
| |
| // This is complicated by templates which can't be string-compared for equality without |
| // canonicalization. Search everything in the index with the same base (non-template-part) |
| // name. With the index being sorted, we can start at the item that begins lexicographically |
| // >= the input. |
| auto iter = map.lower_bound(comp_name); |
| if (iter == map.end()) |
| continue; // Nothing can match of this kind. |
| |
| if (!comp.has_template()) { |
| // In the common case there is no template in the input, so we can just check for exact |
| // string equality and be done with this kind. |
| if (iter->first == comp_name) |
| new_stage.push_back(&iter->second); |
| continue; |
| } |
| |
| // Check all nodes until template canonicalization can't affect the comparison. |
| while (iter != map.end() && !IsIndexStringBeyondName(iter->first, comp_name)) { |
| if (ComponentMatches(iter->first, comp)) { |
| // Found match. |
| new_stage.push_back(&iter->second); |
| break; |
| } |
| |
| ++iter; |
| } |
| } |
| } |
| |
| if (new_stage.empty()) |
| return false; // No children found. |
| |
| AddAnonymousChildrenToStage(&new_stage); |
| |
| // Commit the new found stuff. |
| path_.push_back(std::move(new_stage)); |
| return true; |
| } |
| |
| bool IndexWalker::WalkInto(const ParsedIdentifier& ident) { |
| IndexWalker sub(*this); |
| if (!sub.WalkIntoClosest(ident)) |
| return false; |
| |
| // Full walk succeeded, commit. |
| std::swap(path_, sub.path_); |
| return true; |
| } |
| |
| void IndexWalker::WalkIntoSpecific(const IndexNode* node) { path_.push_back(Stage{node}); } |
| |
| bool IndexWalker::WalkIntoClosest(const ParsedIdentifier& ident) { |
| if (ident.qualification() == IdentifierQualification::kGlobal) |
| path_.resize(1); // Only keep the root. |
| |
| for (const auto& comp : ident.components()) { |
| if (!WalkInto(comp)) |
| return false; // This component not found. |
| } |
| return true; |
| } |
| |
| // static |
| bool IndexWalker::ComponentMatches(const std::string& index_string, |
| const ParsedIdentifierComponent& comp) { |
| if (!ComponentMatchesNameOnly(index_string, comp)) |
| return false; |
| // Only bother with the expensive template comparison on demand. |
| return ComponentMatchesTemplateOnly(index_string, comp); |
| } |
| |
| // static |
| bool IndexWalker::ComponentMatchesNameOnly(const std::string& index_string, |
| const ParsedIdentifierComponent& comp) { |
| const std::string& comp_name = comp.name(); |
| if (comp_name.size() > index_string.size()) |
| return false; // Index string can't contain the name. |
| |
| if (strncmp(comp_name.c_str(), index_string.c_str(), comp_name.size()) != 0) |
| return false; // Name prefix doesn't match. |
| |
| // The index string should be at the end or should have a template spec |
| // following the name. |
| return index_string.size() == comp_name.size() || IsNameEnd(index_string[comp_name.size()]); |
| } |
| |
| // static |
| bool IndexWalker::ComponentMatchesTemplateOnly(const std::string& index_string, |
| const ParsedIdentifierComponent& comp) { |
| ParsedIdentifier index_ident; |
| Err err = ExprParser::ParseIdentifier(index_string, &index_ident); |
| if (err.has_error()) |
| return false; |
| |
| // Each namespaced component should be a different layer of the index so it should produce a |
| // one-component identifier. But this depends how the symbols are structured which we don't want |
| // to make assumptions about. |
| if (index_ident.components().size() != 1) |
| return false; |
| const auto& index_comp = index_ident.components()[0]; |
| |
| if (comp.has_template() != index_comp.has_template()) |
| return false; |
| return comp.template_contents() == index_comp.template_contents(); |
| } |
| |
| // static |
| bool IndexWalker::IsIndexStringBeyondName(std::string_view index_name, std::string_view name) { |
| if (index_name.size() <= name.size()) { |
| // The |index_name| is too small to start with the name and have template stuff on it (which |
| // requires special handling), so we can directly return the answer by string comparison. |
| return index_name > name; |
| } |
| |
| // When the first name.size() characters of the index string aren't the same as the name, we don't |
| // need to worry about templates or anything and can just return that comparison. |
| std::string_view index_prefix = index_name.substr(0, name.size()); |
| int prefix_compare = index_prefix.compare(name); |
| if (prefix_compare != 0) |
| return prefix_compare > 0; // Index is beyond the name by prefix only. |
| |
| // |index_name| starts with |name|. For the index node to be after all possible templates of |
| // |name|, compare against the template begin character. This does make the assumption that the |
| // compiler won't write templates with a space after the name ("vector < int >"). |
| return index_name[name.size()] > '<'; |
| } |
| |
| } // namespace zxdb |