| // 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_SYMBOLS_FUNCTION_H_ |
| #define SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_FUNCTION_H_ |
| |
| #include <string> |
| |
| #include "src/developer/debug/zxdb/symbols/code_block.h" |
| #include "src/developer/debug/zxdb/symbols/file_line.h" |
| #include "src/developer/debug/zxdb/symbols/variable_location.h" |
| |
| namespace zxdb { |
| |
| // Represents a function (a "subprogram" in DWARF parlance). This is different than a "FunctionType" |
| // which is the type used to represent function pointers. |
| // |
| // Some functions in DWARF are "implementations" that have code ranges associated with them, and |
| // some are "specifications" (akin to C forward declarations) that don't. The context about the |
| // namespaces and parent classes comes from the specification, while the implementation of the |
| // function may be outside of any namespace or class definitions. |
| // |
| // It seems Clang puts the function parameters in both places, some attributes like DW_AT_frame_base |
| // will only be on the implementation, and others like DW_AT_decl_file/line, DW_AT_accessibility, |
| // and the return type (DW_AT_type) are only on the specification. |
| // |
| // In the case of an implementation, the decoder will attempt to fill in the attributes from the |
| // specification automatically so this Function object will have full context. Be aware that this |
| // won't necessarily match the DIE that generated the object. |
| // |
| // NAMING: The "full name" (as returned by Symbol::GetFullName()) of a function is the qualified |
| // name without any return types or parameters. Some callers may want parameters, we can add a |
| // helper function in the future if necessary (for display we would often want to syntax highlight |
| // these differently so this is often better done at a different layer). |
| class Function final : public CodeBlock { |
| public: |
| // Construct with fxl::MakeRefCounted(). |
| |
| // CodeBlock override: |
| fxl::RefPtr<CodeBlock> GetContainingBlock() const override; |
| |
| // Symbol overrides. |
| const std::string& GetAssignedName() const final { return assigned_name_; } |
| |
| // Returns true if this function is an inlined function instance. |
| bool is_inline() const { return tag() == DwarfTag::kInlinedSubroutine; } |
| |
| // The containing block is the CodeBlock that contains an inlined function. This will be null for |
| // non-inlined functions. |
| // |
| // Use GetContainingBlock() to access this containing block while falling back to the parent which |
| // will transparently handle inlines vs. non-inlines. |
| // |
| // For inlined functions, Symbol::parent() will contain the lexical parent of the inlined function |
| // (a class or namespace) while the containing block will be the CodeBlock (of any type) that the |
| // code is inlined into. |
| // |
| // "Uncached" symbols must be used for all upward-pointing symbol references to prevent cycles. |
| const UncachedLazySymbol& containing_block() const { return containing_block_; } |
| void set_containing_block(UncachedLazySymbol c) { containing_block_ = std::move(c); } |
| |
| // Unmangled name. Does not include any class or namespace qualifications. (see |
| // Symbol::GetAssignedName) |
| void set_assigned_name(std::string n) { assigned_name_ = std::move(n); } |
| |
| // Mangled name. |
| const std::string& linkage_name() const { return linkage_name_; } |
| void set_linkage_name(std::string n) { linkage_name_ = std::move(n); } |
| |
| // The location in the source code of the declaration. May be empty. |
| const FileLine& decl_line() const { return decl_line_; } |
| void set_decl_line(FileLine decl) { decl_line_ = std::move(decl); } |
| |
| // For inline functions, this can be set to indicate the call location. |
| const FileLine& call_line() const { return call_line_; } |
| void set_call_line(FileLine call) { call_line_ = std::move(call); } |
| |
| // The return value type. This should be some kind of Type object. Will be empty for void return |
| // types. |
| const LazySymbol& return_type() const { return return_type_; } |
| void set_return_type(const LazySymbol& rt) { return_type_ = rt; } |
| |
| // Parameters passed to the function. These should be Variable objects. |
| const std::vector<LazySymbol>& parameters() const { return parameters_; } |
| void set_parameters(std::vector<LazySymbol> p) { parameters_ = std::move(p); } |
| |
| // Template parameters if this function is a template instantiation. |
| const std::vector<LazySymbol>& template_params() const { return template_params_; } |
| void set_template_params(std::vector<LazySymbol> p) { template_params_ = std::move(p); } |
| |
| // The frame base is the location where "fbreg" expressions are evaluated relative to (this will |
| // be most local variables in a function). This can be an empty location if there's no frame base |
| // or it's not needed (e.g. for inline functions). |
| // |
| // When compiled with full stack frames, this will usually evaluate to the contents of the CPU's |
| // "BP" register, but can be different or arbitrarily complicated, especially when things are |
| // optimized. |
| const VariableLocation& frame_base() const { return frame_base_; } |
| void set_frame_base(VariableLocation base) { frame_base_ = std::move(base); } |
| |
| // The object pointer is the "this" object for the current function. Quick summary: Use |
| // GetObjectPointerVariable() to retrieve "this". |
| // |
| // The object_pointer() will be a reference to a parameter (object type Variable). It should |
| // theoretically match one of the entries in the parameters() list but we can't guarantee what the |
| // compiler has generated. The variable will be the implicit object ("this") pointer for member |
| // functions. For nonmember or static member functions the object pointer will be null. |
| // |
| // For inlined functions the location on the object_pointer variable may be wrong. Typically an |
| // inlined subroutine consists of two entries: |
| // |
| // Shared entry for all inlined instances: |
| // (1) DW_TAG_subprogram "InlinedFunc" |
| // DW_AT_object_pointer = reference to (2) |
| // (2) DW_TAG_formal_parameter "this" |
| // <info on the parameter> |
| // |
| // Specific inlined function instance: |
| // (3) DW_TAG_inlined_subroutine "InlinedFunc" |
| // DW_AT_abstract_origin = reference to (1) |
| // (4) DW_TAG_formal_parameter "this" |
| // DW_AT_abstract_origin = reference to (2) |
| // <info on parameter, possibly different than (2)> |
| // |
| // Looking at the object pointer will give the variable on the abstract origin, while the inlined |
| // subroutine will have its own declaration for "this" which will have a location specific to this |
| // inlined instantiation. |
| // |
| // The GetObjectPointerVariable() function handles this case and returns the correct resulting |
| // variable. It will return null if there is no object pointer. |
| const LazySymbol& object_pointer() const { return object_pointer_; } |
| void set_object_pointer(const LazySymbol& op) { object_pointer_ = op; } |
| const Variable* GetObjectPointerVariable() const; |
| |
| protected: |
| // Symbol protected overrides. |
| const Function* AsFunction() const override; |
| |
| private: |
| FRIEND_REF_COUNTED_THREAD_SAFE(Function); |
| FRIEND_MAKE_REF_COUNTED(Function); |
| |
| // The tag must be either "subprogram" or "inlined subroutine" according to whether or not this is |
| // an inlined function. |
| explicit Function(DwarfTag tag); |
| ~Function(); |
| |
| // Symbol protected overrides. |
| Identifier ComputeIdentifier() const override; |
| |
| UncachedLazySymbol containing_block_; |
| std::string assigned_name_; |
| std::string linkage_name_; |
| FileLine decl_line_; |
| FileLine call_line_; |
| LazySymbol return_type_; |
| std::vector<LazySymbol> parameters_; |
| std::vector<LazySymbol> template_params_; |
| VariableLocation frame_base_; |
| LazySymbol object_pointer_; |
| }; |
| |
| } // namespace zxdb |
| |
| #endif // SRC_DEVELOPER_DEBUG_ZXDB_SYMBOLS_FUNCTION_H_ |