| // 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 |