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