blob: 400dc2dbb825e6982d79689f5b454b1a8fc7329c [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/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/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;
}
} // namespace
std::optional<FoundVariable> FindVariable(const CodeBlock* block,
const Identifier& identifier) {
if (block) {
// 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.
// TODO(brettw) implement this.
// FindGlobalVariable(name)...
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) {
// Find the function to see if it has a |this| pointer.
const Function* function = block->GetContainingFunction();
if (!function || !function->object_pointer())
return std::nullopt; // No "this" pointer.
// The "this" variable.
const Variable* this_var = function->object_pointer().Get()->AsVariable();
if (!this_var)
return std::nullopt; // Symbols likely corrupt.
// 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;
}
} // namespace zxdb