blob: 9432081169d63e1b4958a90e76182a6e31284ad2 [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.
#ifndef SRC_DEVELOPER_DEBUG_ZXDB_EXPR_FORMAT_NODE_H_
#define SRC_DEVELOPER_DEBUG_ZXDB_EXPR_FORMAT_NODE_H_
#include <map>
#include <string>
#include "lib/fit/defer.h"
#include "lib/fit/function.h"
#include "src/developer/debug/zxdb/common/err.h"
#include "src/developer/debug/zxdb/expr/expr_value.h"
#include "src/lib/fxl/memory/weak_ptr.h"
namespace zxdb {
// A node in a tree of formatted "stuff" for displaying the user. Currently this stuff can be
// expressions which are evaluated, and ExprValue classes which contain already-evaluated values.
// This tree can represent expansions for things like struct members.
//
// DESIGN
// ------
// Think of this class as being a tree node in a GUI debugger's "watch" window. The "source" is the
// most fundamental thing that the node represents. They can be expressions which are evaluated in
// the current context or can be derived automatically from a parent value (say class members).
//
// The node can be in several states. It can be empty (State::kEmpty), it can have an expression
// that hasn't been evaluated (State::kUnevaluated, say for a tree node where the user has typed a
// watch expression in), that expression can be evaluated to get an ExprValue (a value + type =
// State::kHasValue), and that type to get a stringified description + type (State::kDescribed). A
// node can also have an error state. A node might not go through all states, to format a known
// value, the FormatNode can be given a value directly, skipping the "expression" state.
//
// Frontend code can take this tree and format it however is most appropriate for the environment.
//
// CHILDREN
// --------
// A node can have children. The most obvious example is structure members. Children can also be
// anything else that might be expanded from a parent, including base classes or pointer
// dereferences (again, imagine a watch window tree view).
//
// "Describing" a node will fill in the children as well as the single-line description. The
// children might not themselves be evaluated or described until explicitly filled. This allows lazy
// expansion for things like pointer dereferencing where computing the fully described value might
// be slow or infintely recursive.
class FormatNode {
public:
using ChildVector = std::vector<std::unique_ptr<FormatNode>>;
// Type of function to use when the value is programatically generated. The callback will issue
// the callback with the result or error.
//
// The callback can be issued immediately (within the callstack of the caller of the getter) or
// asynchronously in the future. The implementation of GetProgramaticValue does not have to worry
// about the lifetime of the FormatNode, that will be handled by the implementation of the
// callback passed to it.
using GetProgramaticValue =
fit::function<void(const fxl::RefPtr<EvalContext>& context,
fit::callback<void(const Err& err, ExprValue value)> cb)>;
// The original source or the value for this node.
enum Source {
kValue, // Value is given, nothing to do.
kExpression, // Evaluate an expression in some context to get the value.
kProgramatic, // Evaluate a GetProgramaticValue() callback.
kDescription, // This FormatNode is already described and shouldn't be reevaluated.
};
// The kind of thing the description describes. This is set when the node is put in the described
// state according to what it evaluated to.
enum DescriptionKind {
kNone = 0,
kGroup, // List of bare children with no overall description, type, or brackets.
kArray,
kBaseType, // Integers, characters, bools, etc.
kCollection, // Structs, classes.
kFunctionPointer, // Pointer to a standalone function or member function.
kOther, // Unknown or stuff that doesn't fit into other categories.
kPointer,
kReference,
kRustEnum, // Rust-style enum (can have values associated with enums).
kRustTuple, // Unnamed tuple.
kRustTupleStruct, // Named tuple.
kString,
kWrapper, // Wrapper around some other value, like a std::optional. Has one child.
};
// What this node means to its parent. This is not based on the value in any way and can only
// be computed by the parent.
enum ChildKind {
kNormalChild = 0, // No special meaning.
// The base class of a collection. Normally a collection itself.
kBaseClass,
// One member of an array.
kArrayItem,
// The child of a pointer, reference, or some other node that represents the thing it points or
// otherwise expands to.
kPointerExpansion,
// This type indicates that the node represents a toplevel global or local variable.
//
// Some languages format variables (function or global scope) differently than members of
// structs or other hierarchical things. For example, Rust and Go both use colons to initialize
// struct members, but equals signs for assignments to locals:
//
// let p = Person{FirstName: "Buzz", LastName: "Lightyear", Age: 25}
kVariable,
};
// See the class comment above about the lifecycle.
enum State {
kEmpty = 0, // No value, default constructed. An empty node can have a name to indicate
// "nothing with that name.".
kUnevaluated, // Unevaluated expression.
kHasValue, // Have the value but not converted to a string.
kDescribed // Have the full type and value description.
};
// Constructor for an empty node. Empty nodes have optional names.
FormatNode(const std::string& name = std::string());
// Constructor for a known value.
FormatNode(const std::string& name, ExprValue value);
FormatNode(const std::string& name, ErrOrValue err_or_value);
// Constructor for the error case.
FormatNode(const std::string& name, Err err);
// Constructor with an expression.
explicit FormatNode(const std::string& name, const std::string& expression);
// Constructor for a programatically-filled value.
FormatNode(const std::string& name, GetProgramaticValue get_value);
// Constructor for a group. The creator should set the children.
struct GroupTag {};
explicit FormatNode(GroupTag);
// Not copyable nor moveable since this doesn't work with the weak ptr factory.
~FormatNode();
fxl::WeakPtr<FormatNode> GetWeakPtr();
Source source() const { return source_; }
void set_source(Source s) { source_ = s; }
State state() const { return state_; }
void set_state(State s) { state_ = s; }
// See the ChildKind enum above. This is set by the parent node when it creates a child.
ChildKind child_kind() const { return child_kind_; }
void set_child_kind(ChildKind ck) { child_kind_ = ck; }
// The name of this node. This is used for things like structure member names when nodes are
// expanded. For nodes with an expression type, this name is not used.
void set_name(const std::string& n) { name_ = n; }
const std::string& name() const { return name_; }
// When source() == kExpression this is the expression to evaluate. Use FillFormatNodeValue() to
// convert this expression to a value.
const std::string& expression() const { return expression_; }
void set_expression(std::string e) { expression_ = std::move(e); }
// Call when source == kProgramatic to fill the value from the getter. The callback will be issued
// (possibly from within this call stack) when the value is filled.
void FillProgramaticValue(const fxl::RefPtr<EvalContext>& context, fit::deferred_callback cb);
// The value. This will be valid when the State == kHasValue. The description and type might not
// be up-to-date, see FillFormatNodeDescription().
//
// The setter is out-of-line because we expect this will need to send change notifications in the
// future.
void SetValue(ExprValue v);
const ExprValue& value() const { return value_; }
// The type() is the stringified version of value_.type(). It is valid when State == kDescribed.
const std::string& type() const { return type_; }
void set_type(std::string t) { type_ = std::move(t); }
// The short description of this node's value. It is valid when State == kDescribed. For composite
// things like structs, the description might be an abbreviated version of the struct's members.
const std::string& description() const { return description_; }
void set_description(std::string d) { description_ = std::move(d); }
DescriptionKind description_kind() const { return description_kind_; }
void set_description_kind(DescriptionKind dk) { description_kind_ = dk; }
// When this is a "wrapper" node the formatter node will want to provide a begin and end string
// for expressing the contained object. For example prefix = "std::optional(", suffix = ")".
//
// NOTE FOR FUTURE: We may want to expand this to be usable for non-wrappers also. Currently the
// console frontend knows that Rust structs get a certain type prefix and that tuples get certain
// types of backets, but that information could be expressed here instead since it may be
// desirable for all situations, not just the console frontend. For that, we may also want to add
// a "verbose" prefix and a "regular" prefix.
const std::string& wrapper_prefix() const { return wrapper_prefix_; }
const std::string& wrapper_suffix() const { return wrapper_suffix_; }
void set_wrapper_prefix(const std::string& s) { wrapper_prefix_ = s; }
void set_wrapper_suffix(const std::string& s) { wrapper_suffix_ = s; }
const ChildVector& children() const { return children_; }
ChildVector& children() { return children_; }
// There could have been an error filling in the node. The error could be from computing the value
// of the expression, or in formatting the ExprValue.
//
// The state of the node will represent the last good state. So if there was an error evaluating
// the expression, the state will be "unevaluated" and it could be evaluated again in a new
// context to resolve the error. If there was an error formatting the value (say symbols are
// incorrect) the state will be "has value" and in this case trying to reevaluate won't recover
// from the error without the value changing.
const Err& err() const { return err_; }
void set_err(const Err& e) { err_ = e; }
void SetDescribedError(const Err& e); // Sets the state to "kDescribed" and the error.
private:
// See the getters above for documentation.
Source source_ = kValue;
State state_ = kEmpty;
ChildKind child_kind_ = kNormalChild;
std::string name_;
// Valid when source == kExpression.
std::string expression_;
// Valid when source == kProgramatic.
GetProgramaticValue get_programatic_value_;
ExprValue value_; // Value when source == kValue or when state == kHasValue.
// Valid when state == kDescribed.
std::string type_;
std::string description_;
DescriptionKind description_kind_ = kNone;
Err err_;
std::string wrapper_prefix_;
std::string wrapper_suffix_;
ChildVector children_;
fxl::WeakPtrFactory<FormatNode> weak_factory_;
};
} // namespace zxdb
#endif // SRC_DEVELOPER_DEBUG_ZXDB_EXPR_FORMAT_NODE_H_