blob: 1816ef8272ce2889da069b588f728b09d6ccff09 [file] [log] [blame]
// 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_