| // 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 |