blob: f253cd9d3be0e1a4d69b2f389c9fa6aafcfa00c5 [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 "garnet/bin/zxdb/expr/find_variable.h"
#include "garnet/bin/zxdb/expr/found_variable.h"
#include "garnet/bin/zxdb/expr/identifier.h"
#include "garnet/bin/zxdb/expr/index_walker.h"
#include "garnet/bin/zxdb/symbols/code_block.h"
#include "garnet/bin/zxdb/symbols/collection.h"
#include "garnet/bin/zxdb/symbols/data_member.h"
#include "garnet/bin/zxdb/symbols/function.h"
#include "garnet/bin/zxdb/symbols/loaded_module_symbols.h"
#include "garnet/bin/zxdb/symbols/module_symbol_index.h"
#include "garnet/bin/zxdb/symbols/module_symbol_index_node.h"
#include "garnet/bin/zxdb/symbols/module_symbols.h"
#include "garnet/bin/zxdb/symbols/process_symbols.h"
#include "garnet/bin/zxdb/symbols/type_utils.h"
#include "garnet/bin/zxdb/symbols/visit_scopes.h"
namespace zxdb {
namespace {
// Searches the given vector of values for one with the given name. If found,
// returns it, otherwise returns null.
const Variable* SearchVariableVector(const std::vector<LazySymbol>& vect,
const std::string& search_for) {
for (const auto& cur : vect) {
const Variable* var = cur.Get()->AsVariable();
if (!var)
continue; // Symbols are corrupt.
if (search_for == var->GetAssignedName())
return var;
}
return nullptr;
}
// Searches the list for a reference to a variable and returns the first one
// it finds.
std::optional<FoundVariable> GetVariableFromDieList(
const ModuleSymbols* module_symbols,
const std::vector<ModuleSymbolIndexNode::DieRef>& dies) {
for (const ModuleSymbolIndexNode::DieRef& cur : dies) {
LazySymbol lazy_symbol = module_symbols->IndexDieRefToSymbol(cur);
if (!lazy_symbol)
continue;
const Symbol* symbol = lazy_symbol.Get();
if (const Variable* var = symbol->AsVariable())
return FoundVariable(var);
}
return std::nullopt;
}
} // namespace
std::optional<FoundVariable> FindVariable(
const ProcessSymbols* process_symbols, const CodeBlock* block,
const SymbolContext* block_symbol_context, const Identifier& identifier) {
if (block && !identifier.InGlobalNamespace()) {
// Search for local variables and function parameters.
if (auto found = FindLocalVariable(block, identifier))
return found;
// Search the "this" object.
if (auto found = FindMemberOnThis(block, identifier))
return found;
}
// Fall back to searching global vars.
if (process_symbols) {
// Get the scope for the current function. This may fail in which case
// we'll be left with an empty current scope. This is non-fatal: it just
// means we won't implicitly search the current namespace and will search
// only the global one.
Identifier current_scope;
if (const Function* function = block->GetContainingFunction()) {
auto [err, func_name] = Identifier::FromString(function->GetFullName());
if (!err.has_error())
current_scope = func_name.GetScope();
}
return FindGlobalVariable(process_symbols, current_scope,
block_symbol_context, identifier);
}
return std::nullopt;
}
std::optional<FoundVariable> FindLocalVariable(const CodeBlock* block,
const Identifier& identifier) {
// Local variables can only be simple names.
const std::string* name = identifier.GetSingleComponentName();
if (!name)
return std::nullopt;
// Search backwards in the nested lexical scopes searching for the first
// variable or function parameter with the given name.
const CodeBlock* cur_block = block;
while (cur_block) {
// Check for variables in this block.
if (auto* var = SearchVariableVector(cur_block->variables(), *name))
return FoundVariable(var);
if (const Function* function = cur_block->AsFunction()) {
// Found a function, check for a match in its parameters.
if (auto* var = SearchVariableVector(function->parameters(), *name))
return FoundVariable(var);
break; // Don't recurse into higher levels of nesting than a function.
}
if (!cur_block->parent())
break;
cur_block = cur_block->parent().Get()->AsCodeBlock();
}
return std::nullopt;
}
std::optional<FoundMember> FindMember(const Collection* object,
const Identifier& identifier) {
// TODO(brettw) allow "BaseClass::foo" syntax for specifically naming a
// member of a base class. Watch out: the base class could be qualified (or
// not) in various ways: ns::BaseClass::foo, BaseClass::foo, etc.
const std::string* ident_name = identifier.GetSingleComponentName();
if (!ident_name)
return std::nullopt;
// This code will check the object and all base classes.
std::optional<FoundMember> result;
VisitClassHierarchy(
object,
[ident_name, &result](const Collection* cur_collection,
uint32_t cur_offset) -> bool {
// Called for each collection in the hierarchy.
for (const auto& lazy : cur_collection->data_members()) {
const DataMember* data = lazy.Get()->AsDataMember();
if (data && data->GetAssignedName() == *ident_name) {
result.emplace(data, cur_offset + data->member_location());
return true;
}
}
return false; // Not found in this scope, continue search.
});
return result;
}
std::optional<FoundVariable> FindMemberOnThis(const CodeBlock* block,
const Identifier& identifier) {
const Function* function = block->GetContainingFunction();
if (!function)
return std::nullopt;
const Variable* this_var = function->GetObjectPointerVariable();
if (!this_var)
return std::nullopt; // No "this" pointer.
// Pointed-to type for "this".
const Collection* collection = nullptr;
if (GetPointedToCollection(this_var->type().Get()->AsType(), &collection)
.has_error())
return std::nullopt; // Symbols likely corrupt.
if (auto member = FindMember(collection, identifier))
return FoundVariable(this_var, std::move(*member));
return std::nullopt;
}
std::optional<FoundVariable> FindGlobalVariable(
const ProcessSymbols* process_symbols, const Identifier& current_scope,
const SymbolContext* symbol_context, const Identifier& identifier) {
std::vector<const LoadedModuleSymbols*> modules =
process_symbols->GetLoadedModuleSymbols();
if (modules.empty())
return std::nullopt;
// When we're given a block to start searching from, always search
// that module for symbol matches first. If there are duplicates in other
// modules, one normally wants the current one.
const LoadedModuleSymbols* current_module = nullptr;
if (symbol_context) {
// Find the module that corresponds to the symbol context.
uint64_t module_load_address = symbol_context->RelativeToAbsolute(0);
for (const LoadedModuleSymbols* mod : modules) {
if (mod->load_address() == module_load_address) {
current_module = mod;
break;
}
}
if (current_module) {
// Search the current module.
if (auto found = FindGlobalVariableInModule(
current_module->module_symbols(), current_scope, identifier))
return found;
}
}
// Search all non-current modules.
for (const LoadedModuleSymbols* loaded_mod : modules) {
if (loaded_mod != current_module) {
if (auto found = FindGlobalVariableInModule(loaded_mod->module_symbols(),
current_scope, identifier))
return found;
}
}
return std::nullopt;
}
std::optional<FoundVariable> FindGlobalVariableInModule(
const ModuleSymbols* module_symbols, const Identifier& current_scope,
const Identifier& identifier) {
IndexWalker walker(&module_symbols->GetIndex());
if (!identifier.InGlobalNamespace()) {
// Unless the input identifier is fully qualifed, start the search in the
// current context.
walker.WalkIntoClosest(current_scope);
}
// Search from the current namespace going up.
do {
IndexWalker query_walker(walker);
if (query_walker.WalkInto(identifier)) {
// Found a match, see if it's actually a variable we can return.
const ModuleSymbolIndexNode* match = query_walker.current();
if (auto found = GetVariableFromDieList(module_symbols, match->dies()))
return found;
}
// No variable match, move up one level of scope and try again.
} while (walker.WalkUp());
return std::nullopt;
}
} // namespace zxdb