blob: 420f03772d0980ec579f131ee7803f681ddf2d68 [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.
#include "src/developer/debug/zxdb/symbols/modified_type.h"
#include "src/developer/debug/zxdb/symbols/arch.h"
#include "src/developer/debug/zxdb/symbols/base_type.h"
#include "src/developer/debug/zxdb/symbols/function_type.h"
namespace zxdb {
namespace {
// Returns true if this tag is a modified type that is transparent with respect to the data stored
// in it.
bool IsTransparentTag(DwarfTag tag) {
return DwarfTagIsCVQualifier(tag) || tag == DwarfTag::kTypedef ||
tag == DwarfTag::kImportedDeclaration;
}
} // namespace
ModifiedType::ModifiedType(DwarfTag kind, LazySymbol modified) : Type(kind), modified_(modified) {
FX_DCHECK(DwarfTagIsTypeModifier(kind));
if (IsTransparentTag(kind)) {
const Type* mod_type = modified_.Get()->AsType();
if (mod_type)
set_byte_size(mod_type->byte_size());
} else if (DwarfTagIsPointerOrReference(kind)) {
set_byte_size(kTargetPointerSize);
}
}
ModifiedType::~ModifiedType() = default;
const ModifiedType* ModifiedType::AsModifiedType() const { return this; }
const Type* ModifiedType::StripCV() const {
if (DwarfTagIsCVQualifier(tag())) {
if (const Type* mod = modified_.Get()->AsType()) // Apply recursively.
return mod->StripCV();
}
return this;
}
const Type* ModifiedType::StripCVT() const {
if (IsTransparentTag(tag())) {
const Type* mod = modified_.Get()->AsType();
if (mod) // Apply recursively.
return mod->StripCVT();
}
return this;
}
bool ModifiedType::ModifiesVoid() const {
// Void can be represented two ways, via a null modified type, or via a base type that's a "none"
// type.
if (!modified_)
return true;
const Type* type = modified_.Get()->AsType();
if (!type) {
// Corrupted symbols as this references a non-type or there was an error decoding. Say it's
// non-void for the caller to handle when it tries to figure out what the type is.
return false;
}
if (const BaseType* base = type->StripCVT()->AsBaseType())
return base->base_type() == BaseType::kBaseTypeNone;
return false;
}
std::string ModifiedType::ComputeFullName() const {
static const char kUnknown[] = "<unknown>";
// Typedefs are special and just use the assigned name. Every other modifier below is based on the
// underlying type name.
if (tag() == DwarfTag::kTypedef)
return GetIdentifier().GetFullNameNoQual();
const Type* modified_type = nullptr;
std::string modified_name;
if (!modified()) {
// No modified type means "void".
modified_name = "void";
} else {
if (auto func_type = modified().Get()->AsFunctionType();
func_type && tag() == DwarfTag::kPointerType) {
// Special-case pointer-to-funcion which has unusual syntax.
// TODO(fxbug.dev/5533) this doesn't handle pointers of references to pointers-to-member
// functions
return func_type->ComputeFullNameForFunctionPtr(std::string());
} else if ((modified_type = modified().Get()->AsType())) {
// All other types.
modified_name = modified_type->GetFullName();
} else {
// Symbols likely corrupt.
modified_name = kUnknown;
}
}
switch (tag()) {
case DwarfTag::kConstType:
if (modified_type && modified_type->AsModifiedType()) {
// When the underlying type is another modifier, add it to the end, e.g. a "constant pointer
// to a nonconstant int" is "int* const".
return modified_name + " const";
} else {
// Though the above formatting is always valid, most people write a "constant int" /
// "pointer to a constant int" as either "const int" / "const int*" so special-case.
return "const " + modified_name;
}
case DwarfTag::kPointerType:
return modified_name + "*";
case DwarfTag::kReferenceType:
return modified_name + "&";
case DwarfTag::kRestrictType:
return modified_name + " restrict";
case DwarfTag::kRvalueReferenceType:
return modified_name + "&&";
case DwarfTag::kVolatileType:
return "volatile " + modified_name;
case DwarfTag::kImportedDeclaration:
// Using statements. This is use the kind that moves stuff between namespaces like "using
// std::vector;" -- the renaming type is encoded as a typedef.
//
// TODO(brettw) this is probably wrong because we need to strip namespaces from the modified
// type and instead use the namespaces from the using statement. Currently we don't encounter
// these as Clang follows the using statement when defining types of variables so we only see
// the underlying type. When we support type lookup by name, these will matter.
return modified_name;
default:
return kUnknown;
}
}
Identifier ModifiedType::ComputeIdentifier() const {
// Typedefs are special and just use the assigned name.
if (tag() == DwarfTag::kTypedef)
return Symbol::ComputeIdentifier();
// Every other modifier has decorations around it that means it can't have an identifier.
return Identifier();
}
} // namespace zxdb