blob: 903566ce8513fd6774a9eab31a095498d9b29977 [file] [log] [blame]
// Copyright 2019 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/pretty_tree.h"
#include "src/developer/debug/zxdb/expr/format_node.h"
#include "src/developer/debug/zxdb/expr/format_options.h"
#include "src/developer/debug/zxdb/expr/resolve_collection.h"
#include "src/developer/debug/zxdb/symbols/type.h"
#include "src/lib/fxl/strings/string_printf.h"
namespace zxdb {
namespace {
// Fills the given node for a set/map iterator. The "value" is the referenced value. The container
// type will be either "map" or "set" to make the description.
void FillTreeIteratorNode(const char* container_type, FormatNode* node, ErrOrValue value) {
if (value.has_error())
return node->SetDescribedError(value.err());
// Declare it as a pointer with the value as the pointed-to thing.
node->set_description_kind(FormatNode::kPointer);
// There isn't a good address to show since the actual pointer is to the tree node and showing
// the node address in the description looks misleading. But some generic text.
node->set_description(std::string(container_type) + "::iterator");
// Make the dereference child node the value.
auto deref_node = std::make_unique<FormatNode>("*", value.take_value());
deref_node->set_child_kind(FormatNode::kPointerExpansion);
node->children().push_back(std::move(deref_node));
}
} // namespace
// PrettyTreeIterator ------------------------------------------------------------------------------
void PrettyTreeIterator::Format(FormatNode* node, const FormatOptions& options,
const fxl::RefPtr<EvalContext>& context,
fit::deferred_callback cb) {
GetIteratorValue(context, node->value(),
[weak_node = node->GetWeakPtr(), cb = std::move(cb)](ErrOrValue value) {
if (weak_node)
FillTreeIteratorNode("std::set", weak_node.get(), std::move(value));
});
}
PrettyTreeIterator::EvalFunction PrettyTreeIterator::GetDereferencer() const {
return [](const fxl::RefPtr<EvalContext>& context, const ExprValue& iter, EvalCallback cb) {
GetIteratorValue(std::move(context), iter, std::move(cb));
};
}
void PrettyTreeIterator::GetIteratorValue(const fxl::RefPtr<EvalContext>& context,
const ExprValue& iter, EvalCallback cb) {
// Evaluate "static_cast<ITER_TYPE::__node_pointer>(iter.__ptr_)->__value_"
//
// Unfortunately, there is no way with the implementation to know when an iterator points to
// "end" other than it's values look fishy.
//
// It would be nice to express this solely in terms of expressions if we can figure out how to
// express "TREE_TYPE" in the above expression.
EvalExpressionOn(
context, iter,
"reinterpret_cast<" + iter.type()->GetFullName() + "::__node_pointer>(__ptr_)->__value_",
std::move(cb));
}
// PrettyMapIterator -------------------------------------------------------------------------------
void PrettyMapIterator::Format(FormatNode* node, const FormatOptions& options,
const fxl::RefPtr<EvalContext>& context, fit::deferred_callback cb) {
GetIteratorValue(context, node->value(),
[weak_node = node->GetWeakPtr(), cb = std::move(cb)](ErrOrValue value) {
if (weak_node)
FillTreeIteratorNode("std::map", weak_node.get(), std::move(value));
});
}
PrettyMapIterator::EvalFunction PrettyMapIterator::GetDereferencer() const {
return [](const fxl::RefPtr<EvalContext>& context, const ExprValue& iter, EvalCallback cb) {
GetIteratorValue(std::move(context), iter, std::move(cb));
};
}
void PrettyMapIterator::GetIteratorValue(const fxl::RefPtr<EvalContext>& context,
const ExprValue& iter, EvalCallback cb) {
// Evaluate "static_cast<ITER_TYPE::__node_pointer>(iter.__i_.__ptr_)->__value_.__cc"
// Where ITER_TYPE is actually the type of "iter.__i_".
ErrOrValue i_value = ResolveNonstaticMember(context, iter, {"__i_"});
if (i_value.has_error())
return cb(i_value);
// See PrettyTreeIterator above.
EvalExpressionOn(context, i_value.value(),
"reinterpret_cast<" + i_value.value().type()->GetFullName() +
"::__node_pointer>(__ptr_)->__value_.__cc",
std::move(cb));
}
// PrettyTree --------------------------------------------------------------------------------------
// C++ expression that resolves to a set/map size.
#define TREE_SIZE_EXPRESSION "__tree_.__pair3_.__value_"
PrettyTree::PrettyTree(const std::string& container_name)
: PrettyType({{"size", TREE_SIZE_EXPRESSION}, {"empty", TREE_SIZE_EXPRESSION " == 0"}}),
container_name_(container_name) {}
void PrettyTree::Format(FormatNode* node, const FormatOptions& options,
const fxl::RefPtr<EvalContext>& context, fit::deferred_callback cb) {
// Actually getting the set contents in our C++ code with our current asynchronous API is
// prohibitive. When we have a way that walking the tree can be expressed in a synchronous
// fashion (either by a scripting language or fancier expressions) we can add this ability.
//
// For now, just show the size as the description.
EvalExpressionOn(context, node->value(), TREE_SIZE_EXPRESSION,
[container_name = container_name_, weak_node = node->GetWeakPtr(),
cb = std::move(cb)](ErrOrValue size_value) mutable {
if (!weak_node)
return;
if (size_value.has_error())
return weak_node->SetDescribedError(size_value.err());
uint64_t size = 0;
if (Err e = size_value.value().PromoteTo64(&size); e.has_error())
return weak_node->SetDescribedError(e);
weak_node->set_description(
fxl::StringPrintf("%s{size = %" PRIu64 "}", container_name.c_str(), size));
});
}
} // namespace zxdb