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