blob: 038dc3b83311cca3b114022073dee5f2abc8e0c4 [file] [log] [blame]
// 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