| // Copyright 2018 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_EXPR_NODE_H_ |
| #define SRC_DEVELOPER_DEBUG_ZXDB_EXPR_EXPR_NODE_H_ |
| |
| #include <iosfwd> |
| #include <memory> |
| |
| #include "lib/fit/function.h" |
| #include "src/developer/debug/zxdb/expr/cast.h" |
| #include "src/developer/debug/zxdb/expr/eval_callback.h" |
| #include "src/developer/debug/zxdb/expr/expr_token.h" |
| #include "src/developer/debug/zxdb/expr/expr_value.h" |
| #include "src/developer/debug/zxdb/expr/parsed_identifier.h" |
| #include "src/developer/debug/zxdb/expr/variable_decl.h" |
| #include "src/developer/debug/zxdb/expr/vm_stream.h" |
| #include "src/lib/fxl/memory/ref_counted.h" |
| #include "src/lib/fxl/memory/ref_ptr.h" |
| |
| namespace zxdb { |
| |
| class Err; |
| class EvalContext; |
| class Type; |
| |
| // Node subclasses. |
| class AddressOfExprNode; |
| class ArrayAccessExprNode; |
| class BinaryOpExprNode; |
| class BlockExprNode; |
| class BreakExprNode; |
| class CastExprNode; |
| class ConditionExprNode; |
| class DereferenceExprNode; |
| class FunctionCallExprNode; |
| class IdentifierExprNode; |
| class LiteralExprNode; |
| class LocalVarExprNode; |
| class LoopExprNode; |
| class MemberAccessExprNode; |
| class SizeofExprNode; |
| class TypeExprNode; |
| class UnaryOpExprNode; |
| class VariableDeclExprNode; |
| |
| // Represents one node in the abstract syntax tree. |
| class ExprNode : public fxl::RefCountedThreadSafe<ExprNode> { |
| public: |
| virtual const AddressOfExprNode* AsAddressOf() const { return nullptr; } |
| virtual const ArrayAccessExprNode* AsArrayAccess() const { return nullptr; } |
| virtual const BinaryOpExprNode* AsBinaryOp() const { return nullptr; } |
| virtual const BlockExprNode* AsBlock() const { return nullptr; } |
| virtual const BreakExprNode* AsBreak() const { return nullptr; } |
| virtual const CastExprNode* AsCast() const { return nullptr; } |
| virtual const ConditionExprNode* AsCondition() const { return nullptr; } |
| virtual const DereferenceExprNode* AsDereference() const { return nullptr; } |
| virtual const FunctionCallExprNode* AsFunctionCall() const { return nullptr; } |
| virtual const IdentifierExprNode* AsIdentifier() const { return nullptr; } |
| virtual const LiteralExprNode* AsLiteral() const { return nullptr; } |
| virtual const LocalVarExprNode* AsLocalVar() const { return nullptr; } |
| virtual const LoopExprNode* AsLoop() const { return nullptr; } |
| virtual const MemberAccessExprNode* AsMemberAccess() const { return nullptr; } |
| virtual const SizeofExprNode* AsSizeof() const { return nullptr; } |
| virtual const TypeExprNode* AsType() const { return nullptr; } |
| virtual const UnaryOpExprNode* AsUnaryOp() const { return nullptr; } |
| virtual const VariableDeclExprNode* AsVariableDecl() const { return nullptr; } |
| |
| // Appends the bytecode necessary to execute this node. The bytecode machine is a stack-based |
| // machine. |
| // |
| // Each ExprNode pushes temporary values it needs to the stack (usually by evaluating |
| // sub-expressions that will leave these values on the stack). It must consume these values and |
| // push exactly one result value on the stack when it is done. This value will be the "result" of |
| // the expression. Even expressions with no results (like a loop) must push a value to the stack |
| // (typically an empty ExprValue()) since the node calling it always expects one value. If the |
| // calling node doesn't want a value, it should "drop" it after running the expression. |
| // |
| // After the entire program is executed, the result should be a stack containing exactly one |
| // ExprValue which is the result of evaluation. |
| virtual void EmitBytecode(VmStream& stream) const = 0; |
| |
| // Wrapper around EmitBytecode() that automatically expands C++ references to their values. |
| // Used when callers know they want the effective value. |
| void EmitBytecodeExpandRef(VmStream& stream) const; |
| |
| // Dumps the tree to a stream with the given indent. Used for unit testing and debugging. |
| virtual void Print(std::ostream& out, int indent) const = 0; |
| |
| protected: |
| FRIEND_REF_COUNTED_THREAD_SAFE(ExprNode); |
| |
| ExprNode() = default; |
| virtual ~ExprNode() = default; |
| }; |
| |
| // Implements taking an address of n expression ("&" in C). |
| class AddressOfExprNode : public ExprNode { |
| public: |
| const AddressOfExprNode* AsAddressOf() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(AddressOfExprNode); |
| FRIEND_MAKE_REF_COUNTED(AddressOfExprNode); |
| |
| AddressOfExprNode(); |
| AddressOfExprNode(fxl::RefPtr<ExprNode> expr) : expr_(std::move(expr)) {} |
| ~AddressOfExprNode() override = default; |
| |
| fxl::RefPtr<ExprNode> expr_; |
| }; |
| |
| // Implements an array access: foo[bar]. |
| class ArrayAccessExprNode : public ExprNode { |
| public: |
| const ArrayAccessExprNode* AsArrayAccess() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(ArrayAccessExprNode); |
| FRIEND_MAKE_REF_COUNTED(ArrayAccessExprNode); |
| |
| ArrayAccessExprNode(); |
| ArrayAccessExprNode(fxl::RefPtr<ExprNode> left, fxl::RefPtr<ExprNode> inner) |
| : left_(std::move(left)), inner_(std::move(inner)) {} |
| ~ArrayAccessExprNode() override = default; |
| |
| // Converts the given value which is the result of executing the "inner" expression and converts |
| // it to an integer if possible. |
| static Err InnerValueToOffset(const fxl::RefPtr<EvalContext>& context, const ExprValue& inner, |
| int64_t* offset); |
| |
| fxl::RefPtr<ExprNode> left_; |
| fxl::RefPtr<ExprNode> inner_; |
| }; |
| |
| // Implements all binary operators. |
| class BinaryOpExprNode : public ExprNode { |
| public: |
| const BinaryOpExprNode* AsBinaryOp() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(BinaryOpExprNode); |
| FRIEND_MAKE_REF_COUNTED(BinaryOpExprNode); |
| |
| BinaryOpExprNode(); |
| BinaryOpExprNode(fxl::RefPtr<ExprNode> left, ExprToken op, fxl::RefPtr<ExprNode> right) |
| : left_(std::move(left)), op_(std::move(op)), right_(std::move(right)) {} |
| ~BinaryOpExprNode() override = default; |
| |
| fxl::RefPtr<ExprNode> left_; |
| ExprToken op_; |
| fxl::RefPtr<ExprNode> right_; |
| }; |
| |
| class BlockExprNode : public ExprNode { |
| public: |
| const BlockExprNode* AsBlock() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| const std::vector<fxl::RefPtr<ExprNode>>& statements() const { return statements_; } |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(BlockExprNode); |
| FRIEND_MAKE_REF_COUNTED(BlockExprNode); |
| |
| BlockExprNode(); |
| BlockExprNode(std::vector<fxl::RefPtr<ExprNode>> statements, |
| std::optional<uint32_t> entry_local_var_count) |
| : statements_(std::move(statements)), entry_local_var_count_(entry_local_var_count) {} |
| ~BlockExprNode() override = default; |
| |
| std::vector<fxl::RefPtr<ExprNode>> statements_; |
| |
| // The number of local variables in scope at the entry of this block. The block uses this to |
| // emit bytecode at the exit of the block to clean up local variables back to this number. |
| // |
| // Nullopt means that there were no local variables used in the block so they don't have to be |
| // popped. |
| std::optional<uint32_t> entry_local_var_count_; |
| }; |
| |
| class BreakExprNode : public ExprNode { |
| public: |
| const BreakExprNode* AsBreak() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(BreakExprNode); |
| FRIEND_MAKE_REF_COUNTED(BreakExprNode); |
| |
| explicit BreakExprNode(const ExprToken& token) : token_(token) {} |
| |
| ExprToken token_; |
| }; |
| |
| // Implements all types of casts. |
| class CastExprNode : public ExprNode { |
| public: |
| const CastExprNode* AsCast() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(CastExprNode); |
| FRIEND_MAKE_REF_COUNTED(CastExprNode); |
| |
| CastExprNode(); |
| CastExprNode(CastType cast_type, fxl::RefPtr<TypeExprNode> to_type, fxl::RefPtr<ExprNode> from) |
| : cast_type_(cast_type), to_type_(std::move(to_type)), from_(std::move(from)) {} |
| ~CastExprNode() override = default; |
| |
| CastType cast_type_; |
| fxl::RefPtr<TypeExprNode> to_type_; |
| fxl::RefPtr<ExprNode> from_; |
| }; |
| |
| // Implements all types of if and if/else |
| class ConditionExprNode : public ExprNode { |
| public: |
| struct Condition { |
| bool IsRustIfLet() const { return !rust_pattern_name.empty(); } |
| |
| // For "if let" conditions, the rust_pattern_name will be !empty(), use IsRustIfLet(). Even in |
| // that case, rust_pattern_local_slots may be empty for matching an enum with no value. |
| ParsedIdentifier rust_pattern_name; |
| std::vector<uint32_t> rust_pattern_local_slots; // Variable(s) to assign to on match. |
| |
| fxl::RefPtr<ExprNode> cond; // Conditional expression to evaluate. |
| }; |
| struct Pair { |
| Pair(Condition c, fxl::RefPtr<ExprNode> t) : cond(std::move(c)), then(std::move(t)) {} |
| |
| Condition cond; |
| fxl::RefPtr<ExprNode> then; // Code to execute when condition is satisfied. Possibly null. |
| }; |
| |
| const ConditionExprNode* AsCondition() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(ConditionExprNode); |
| FRIEND_MAKE_REF_COUNTED(ConditionExprNode); |
| |
| ConditionExprNode(); |
| // The conditions are evaluated in-order until one is true. The "else" can be null in which case |
| // it will be ignored. See BlockExprNode for the entry_local_var_count. |
| ConditionExprNode(std::vector<Pair> conds, fxl::RefPtr<ExprNode> else_case, |
| std::optional<uint32_t> entry_local_var_count) |
| : conds_(std::move(conds)), |
| else_(std::move(else_case)), |
| entry_local_var_count_(entry_local_var_count) {} |
| ~ConditionExprNode() override = default; |
| |
| std::vector<Pair> conds_; |
| fxl::RefPtr<ExprNode> else_; // Possibly null. |
| std::optional<uint32_t> entry_local_var_count_; |
| }; |
| |
| // Implements dereferencing a pointer ("*" in C). |
| class DereferenceExprNode : public ExprNode { |
| public: |
| const DereferenceExprNode* AsDereference() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(DereferenceExprNode); |
| FRIEND_MAKE_REF_COUNTED(DereferenceExprNode); |
| |
| DereferenceExprNode(); |
| DereferenceExprNode(fxl::RefPtr<ExprNode> expr) : expr_(std::move(expr)) {} |
| ~DereferenceExprNode() override = default; |
| |
| fxl::RefPtr<ExprNode> expr_; |
| }; |
| |
| // Function calls include things like: "Foo()", "ns::Foo<int>(6, 5)". |
| class FunctionCallExprNode : public ExprNode { |
| public: |
| const FunctionCallExprNode* AsFunctionCall() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| const fxl::RefPtr<ExprNode>& call() const { return call_; } |
| const std::vector<fxl::RefPtr<ExprNode>>& args() const { return args_; } |
| |
| // Returns true if the given ExprNode is valid for the "call" of a function. |
| static bool IsValidCall(const fxl::RefPtr<ExprNode>& call); |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(FunctionCallExprNode); |
| FRIEND_MAKE_REF_COUNTED(FunctionCallExprNode); |
| |
| FunctionCallExprNode(); |
| explicit FunctionCallExprNode(fxl::RefPtr<ExprNode> call, |
| std::vector<fxl::RefPtr<ExprNode>> args = {}) |
| : call_(std::move(call)), args_(std::move(args)) {} |
| ~FunctionCallExprNode() override = default; |
| |
| // Backend to evaluate a member function call on the given base object. For example, |
| // "object.fn_name()". |
| // |
| // This assumes no function parameters (it's currently used for the PrettyType getters only). |
| // |
| // The second version handle the "->" case where the object should be a pointer. |
| static void EvalMemberCall(const fxl::RefPtr<EvalContext>& context, const ExprValue& object, |
| const std::string& fn_name, EvalCallback cb); |
| static void EvalMemberPtrCall(const fxl::RefPtr<EvalContext>& context, |
| const ExprValue& object_ptr, const std::string& fn_name, |
| EvalCallback cb); |
| |
| // This will either be an IdentifierExprNode which gives the function name, or a |
| // MemberAccessExprNode which gives an object and the function name. |
| fxl::RefPtr<ExprNode> call_; |
| |
| std::vector<fxl::RefPtr<ExprNode>> args_; |
| }; |
| |
| // Implements a bare identifier. |
| class IdentifierExprNode : public ExprNode { |
| public: |
| const IdentifierExprNode* AsIdentifier() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| ParsedIdentifier& ident() { return ident_; } |
| const ParsedIdentifier& ident() const { return ident_; } |
| |
| // Destructively moves the identifier out of this class. This unusual mutating getter is |
| // implemented because the expression parser is also used to parse identifiers, and this will hold |
| // the result which we would prefer not to copy to get out. |
| ParsedIdentifier TakeIdentifier() { return std::move(ident_); } |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(IdentifierExprNode); |
| FRIEND_MAKE_REF_COUNTED(IdentifierExprNode); |
| |
| IdentifierExprNode() = default; |
| |
| // Simple one-name identifier. |
| IdentifierExprNode(std::string name) : ident_(ParsedIdentifierComponent(std::move(name))) {} |
| |
| IdentifierExprNode(ParsedIdentifier id) : ident_(std::move(id)) {} |
| ~IdentifierExprNode() override = default; |
| |
| ParsedIdentifier ident_; |
| }; |
| |
| // Implements a literal like a number, boolean, or string. |
| class LiteralExprNode : public ExprNode { |
| public: |
| const LiteralExprNode* AsLiteral() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| // The token's value won't have been checked that it's valid, only that it starts like the type of |
| // literal it is. This checking will be done at evaluation time. |
| const ExprToken& token() const { return token_; } |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(LiteralExprNode); |
| FRIEND_MAKE_REF_COUNTED(LiteralExprNode); |
| |
| LiteralExprNode() = default; |
| LiteralExprNode(ExprLanguage lang, const ExprToken& token) : language_(lang), token_(token) {} |
| ~LiteralExprNode() override = default; |
| |
| ExprLanguage language_; |
| ExprToken token_; |
| }; |
| |
| // Local variable access. This is different than IdentifierExprNode because we know in advance |
| // where the value is coming from. |
| class LocalVarExprNode : public ExprNode { |
| public: |
| const LocalVarExprNode* AsLocalVar() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(LocalVarExprNode); |
| FRIEND_MAKE_REF_COUNTED(LocalVarExprNode); |
| |
| explicit LocalVarExprNode(uint32_t slot) : slot_(slot) {} |
| ~LocalVarExprNode() override = default; |
| |
| uint32_t slot_; |
| }; |
| |
| // Represents all types of loops. By supporting both pre- and post-condition termination checks, |
| // do/while loops can be represented at the same time as for and while loops. Any expression can be |
| // null if it doesn't apply to the current loop type, or is omitted by the user: |
| // |
| // for ( <init>; <precondition>; <incr> ) <contents> |
| // while ( <precondition> ) <contents> |
| // do <contents> while ( <postcondition> ) |
| class LoopExprNode : public ExprNode { |
| public: |
| const ExprToken& token() const { return token_; } |
| |
| const LoopExprNode* AsLoop() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(LoopExprNode); |
| FRIEND_MAKE_REF_COUNTED(LoopExprNode); |
| |
| LoopExprNode() = default; |
| |
| // The init_local_var_count is the number of local variables in scope at the beginning of the |
| // init expression. This allows us to pop any local variables introduced by the init expression |
| // (as in C++ "for (int i = 0; ..."). If it is nullopt, nothing needs to be popped. |
| LoopExprNode(ExprToken token, fxl::RefPtr<ExprNode> init, |
| std::optional<uint32_t> init_local_var_count, fxl::RefPtr<ExprNode> precondition, |
| fxl::RefPtr<ExprNode> postcondition, fxl::RefPtr<ExprNode> incr, |
| fxl::RefPtr<ExprNode> contents) |
| : token_(std::move(token)), |
| init_(std::move(init)), |
| precondition_(std::move(precondition)), |
| postcondition_(std::move(postcondition)), |
| incr_(std::move(incr)), |
| contents_(std::move(contents)), |
| init_local_var_count_(init_local_var_count) {} |
| ~LoopExprNode() override = default; |
| |
| // The token is the opening "for", "do", "while", "loop" token. |
| ExprToken token_; |
| |
| // All expressions can be null. |
| fxl::RefPtr<ExprNode> init_; // Executed before any loops are executed. |
| fxl::RefPtr<ExprNode> precondition_; // Checked at top of each loop iteration. |
| fxl::RefPtr<ExprNode> postcondition_; // Checked at bottom of each loop iteration (do/while). |
| fxl::RefPtr<ExprNode> incr_; // Executed after every loop iteration. |
| fxl::RefPtr<ExprNode> contents_; // Loop body. |
| |
| std::optional<uint32_t> init_local_var_count_; |
| }; |
| |
| // Implements both "." and "->" struct/class/union data member accesses. |
| class MemberAccessExprNode : public ExprNode { |
| public: |
| const MemberAccessExprNode* AsMemberAccess() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| // Expression on the left side of the "." or "->". |
| const ExprNode* left() const { return left_.get(); } |
| |
| // The "." or "->" token itself. |
| const ExprToken& accessor() const { return accessor_; } |
| |
| // The name of the data member. |
| const ParsedIdentifier& member() const { return member_; } |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(MemberAccessExprNode); |
| FRIEND_MAKE_REF_COUNTED(MemberAccessExprNode); |
| |
| MemberAccessExprNode() = default; |
| MemberAccessExprNode(fxl::RefPtr<ExprNode> left, const ExprToken& access, |
| const ParsedIdentifier& member) |
| : left_(std::move(left)), accessor_(access), member_(member) {} |
| ~MemberAccessExprNode() override = default; |
| |
| fxl::RefPtr<ExprNode> left_; |
| ExprToken accessor_; |
| ParsedIdentifier member_; |
| }; |
| |
| class SizeofExprNode : public ExprNode { |
| public: |
| const SizeofExprNode* AsSizeof() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(SizeofExprNode); |
| FRIEND_MAKE_REF_COUNTED(SizeofExprNode); |
| |
| SizeofExprNode(); |
| SizeofExprNode(fxl::RefPtr<ExprNode> expr) : expr_(std::move(expr)) {} |
| ~SizeofExprNode() override = default; |
| |
| static ErrOrValue SizeofType(const fxl::RefPtr<EvalContext>& context, const Type* type); |
| |
| fxl::RefPtr<ExprNode> expr_; |
| }; |
| |
| // Implements references to type names. This mostly appears in casts. |
| class TypeExprNode : public ExprNode { |
| public: |
| const TypeExprNode* AsType() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| fxl::RefPtr<Type>& type() { return type_; } |
| const fxl::RefPtr<Type>& type() const { return type_; } |
| |
| fxl::RefPtr<Type>& concrete_type() { return concrete_type_; } |
| const fxl::RefPtr<Type>& concrete_type() const { return concrete_type_; } |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(TypeExprNode); |
| FRIEND_MAKE_REF_COUNTED(TypeExprNode); |
| |
| TypeExprNode(); |
| TypeExprNode(fxl::RefPtr<Type> type, fxl::RefPtr<Type> concrete_type) |
| : type_(std::move(type)), concrete_type_(std::move(concrete_type)) {} |
| ~TypeExprNode() override = default; |
| |
| // The type as specified (could be typedef, forward declaration, etc.). |
| fxl::RefPtr<Type> type_; |
| |
| // The concrete type that type_ resolved to. Guaranteed non-null, it will often be the same as |
| // type_. This is required for certain local variable uses that need the concrete type when |
| // generating bytecode. |
| fxl::RefPtr<Type> concrete_type_; |
| }; |
| |
| // Implements unary mathematical operators (the operation depends on the operator token). |
| class UnaryOpExprNode : public ExprNode { |
| public: |
| const UnaryOpExprNode* AsUnaryOp() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(UnaryOpExprNode); |
| FRIEND_MAKE_REF_COUNTED(UnaryOpExprNode); |
| |
| UnaryOpExprNode(); |
| UnaryOpExprNode(const ExprToken& op, fxl::RefPtr<ExprNode> expr) |
| : op_(op), expr_(std::move(expr)) {} |
| ~UnaryOpExprNode() override = default; |
| |
| ExprToken op_; |
| fxl::RefPtr<ExprNode> expr_; |
| }; |
| |
| class VariableDeclExprNode : public ExprNode { |
| public: |
| const VariableDeclExprNode* AsVariableDecl() const override { return this; } |
| void EmitBytecode(VmStream& stream) const override; |
| void Print(std::ostream& out, int indent) const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(VariableDeclExprNode); |
| FRIEND_MAKE_REF_COUNTED(VariableDeclExprNode); |
| |
| VariableDeclExprNode(VariableDeclTypeInfo decl_info, uint32_t local_slot, const ExprToken& name, |
| fxl::RefPtr<ExprNode> init_expr) |
| : decl_info_(std::move(decl_info)), |
| local_slot_(local_slot), |
| name_(name), |
| init_expr_(std::move(init_expr)) {} |
| ~VariableDeclExprNode() override = default; |
| |
| VariableDeclTypeInfo decl_info_; |
| uint32_t local_slot_; |
| ExprToken name_; |
| fxl::RefPtr<ExprNode> init_expr_; |
| }; |
| |
| } // namespace zxdb |
| |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_EXPR_EXPR_NODE_H_ |