blob: 30fee14b12f63570241d13b8a29f8838f20f9708 [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 tag == DwarfTag::kConstType || tag == DwarfTag::kVolatileType ||
tag == DwarfTag::kTypedef || tag == DwarfTag::kRestrictType ||
tag == DwarfTag::kImportedDeclaration;
}
// Returns true if this modified holds some kind of pointer to the modified
// type.
bool IsPointerTag(DwarfTag tag) {
return tag == DwarfTag::kPointerType || tag == DwarfTag::kReferenceType ||
tag == DwarfTag::kRvalueReferenceType;
}
} // namespace
ModifiedType::ModifiedType(DwarfTag kind, LazySymbol modified)
: Type(kind), modified_(modified) {
FXL_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 (IsPointerTag(kind)) {
set_byte_size(kTargetPointerSize);
}
}
ModifiedType::~ModifiedType() = default;
const ModifiedType* ModifiedType::AsModifiedType() const { return this; }
const Type* ModifiedType::GetConcreteType() const {
if (IsTransparentTag(tag())) {
const Type* mod = modified_.Get()->AsType();
if (mod)
return mod->GetConcreteType();
}
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->GetConcreteType()->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 GetAssignedName();
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(DX-683) 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;
}
}
} // namespace zxdb