blob: 73b69a7d216beb0c6cf38c6810b535da3ebf8180 [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/symbols/visit_scopes.h"
#include "src/developer/debug/zxdb/symbols/code_block.h"
#include "src/developer/debug/zxdb/symbols/collection.h"
#include "src/developer/debug/zxdb/symbols/data_member.h"
#include "src/developer/debug/zxdb/symbols/inheritance_path.h"
#include "src/developer/debug/zxdb/symbols/inherited_from.h"
namespace zxdb {
namespace {
VisitResult DoVisitClassHierarchy(InheritancePath* path,
fit::function<VisitResult(const InheritancePath& path)>& cb) {
if (VisitResult result = cb(*path); result != VisitResult::kContinue)
return result;
// Iterate through base classes.
for (const auto& lazy_from : path->path().back().collection->inherited_from()) {
const InheritedFrom* inherited_from = lazy_from.Get()->As<InheritedFrom>();
if (!inherited_from)
continue;
const Collection* from_coll = inherited_from->from().Get()->As<Collection>();
if (!from_coll)
continue;
path->path().emplace_back(RefPtrTo(inherited_from), RefPtrTo(from_coll));
if (VisitResult result = DoVisitClassHierarchy(path, cb); result != VisitResult::kContinue)
return result;
path->path().pop_back();
}
return VisitResult::kContinue;
}
// This takes the current byte offset of the input Collection from the original call for the
// callbacks when this is issued recursively. The remaining_iters variable is decremented and if
// it reaches 0, iteration will abort with a failure.
VisitResult DoVisitDataMembers(const Collection* collection, const VisitDataMembersCallback& cb,
uint32_t collection_byte_offset, int& remaining_iters) {
return VisitClassHierarchy(collection, [collection_byte_offset, &remaining_iters,
&cb](const InheritancePath& path) {
if (remaining_iters == 0)
return VisitResult::kAbort;
remaining_iters--;
std::optional<uint32_t> base_offset_or = path.BaseOffsetInDerived();
if (!base_offset_or)
return VisitResult::kContinue; // Virtual inheritance, skip this one.
// Check all data members of this step in the class hierarchy.
for (const auto& member_symbol : path.base()->data_members()) {
const DataMember* member = member_symbol.Get()->As<DataMember>();
if (!member)
return VisitResult::kAbort;
// Get the type of the member to see if we need to recurse into a collection's members.
//
// Assume that all members will have concrete types because they will be needed to define the
// layout of the input concrete type. The StripCVT() call will have decoded to the underlying
// collection if it is one.
const Type* member_type = member->type().Get()->As<Type>();
if (!member_type)
return VisitResult::kAbort; // Expect all members to have a type.
member_type = member_type->StripCVT();
const Collection* collection_member = member_type->As<Collection>();
// Issue the callback for this data member. The byte offset of this data member is sum of the
// byte offset of the current collection in the original call (collection_byte_offset), the
// current base class in the input collection, and the member offset in the current base
// class.
bool is_leaf = !collection_member;
uint32_t member_offset = collection_byte_offset + *base_offset_or + member->member_location();
VisitResult result = cb(is_leaf, member_offset, member);
if (result != VisitResult::kContinue)
return result;
if (!collection_member)
continue; // This member it itself not a collection, no need to recurse.
// Recursively visit the data member's members.
result = DoVisitDataMembers(collection_member, cb, member_offset, remaining_iters);
if (result != VisitResult::kContinue)
return result;
}
return VisitResult::kContinue;
});
}
} // namespace
VisitResult VisitLocalBlocks(const CodeBlock* starting,
fit::function<VisitResult(const CodeBlock*)> cb) {
// Need to hold references when walking up the symbol hierarchy.
fxl::RefPtr<CodeBlock> cur_block = RefPtrTo(starting);
while (cur_block) {
VisitResult result = cb(cur_block.get());
if (result != VisitResult::kContinue)
return result;
if (cur_block->As<Function>() || !cur_block->parent())
break; // Don't iterate above functions.
auto parent_ref = cur_block->parent().Get();
cur_block = RefPtrTo(parent_ref->As<CodeBlock>());
}
return VisitResult::kContinue;
}
VisitResult VisitClassHierarchy(const Collection* starting,
fit::function<VisitResult(const InheritancePath& path)> cb) {
InheritancePath path(RefPtrTo(starting));
return DoVisitClassHierarchy(&path, cb);
}
VisitResult VisitDataMembers(const Collection* collection, const VisitDataMembersCallback& cb,
int max_members) {
int remaining_iters = max_members;
return DoVisitDataMembers(collection, cb, 0, remaining_iters);
}
} // namespace zxdb