//===-- GoASTContext.cpp ----------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include #include #include #include "lldb/Core/DumpDataExtractor.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/UniqueCStringMap.h" #include "lldb/Core/ValueObject.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/GoASTContext.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/Type.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Target.h" #include "llvm/Support/Threading.h" #include "Plugins/ExpressionParser/Go/GoUserExpression.h" #include "Plugins/SymbolFile/DWARF/DWARFASTParserGo.h" using namespace lldb; namespace lldb_private { class GoArray; class GoFunction; class GoStruct; class GoType { public: enum { KIND_BOOL = 1, KIND_INT = 2, KIND_INT8 = 3, KIND_INT16 = 4, KIND_INT32 = 5, KIND_INT64 = 6, KIND_UINT = 7, KIND_UINT8 = 8, KIND_UINT16 = 9, KIND_UINT32 = 10, KIND_UINT64 = 11, KIND_UINTPTR = 12, KIND_FLOAT32 = 13, KIND_FLOAT64 = 14, KIND_COMPLEX64 = 15, KIND_COMPLEX128 = 16, KIND_ARRAY = 17, KIND_CHAN = 18, KIND_FUNC = 19, KIND_INTERFACE = 20, KIND_MAP = 21, KIND_PTR = 22, KIND_SLICE = 23, KIND_STRING = 24, KIND_STRUCT = 25, KIND_UNSAFEPOINTER = 26, KIND_LLDB_VOID, // Extension for LLDB, not used by go runtime. KIND_MASK = (1 << 5) - 1, KIND_DIRECT_IFACE = 1 << 5 }; GoType(int kind, const ConstString &name) : m_kind(kind & KIND_MASK), m_name(name) { if (m_kind == KIND_FUNC) m_kind = KIND_FUNC; } virtual ~GoType() {} int GetGoKind() const { return m_kind; } const ConstString &GetName() const { return m_name; } virtual CompilerType GetElementType() const { return CompilerType(); } bool IsTypedef() const { switch (m_kind) { case KIND_CHAN: case KIND_MAP: case KIND_INTERFACE: return true; default: return false; } } GoArray *GetArray(); GoFunction *GetFunction(); GoStruct *GetStruct(); private: int m_kind; ConstString m_name; GoType(const GoType &) = delete; const GoType &operator=(const GoType &) = delete; }; class GoElem : public GoType { public: GoElem(int kind, const ConstString &name, const CompilerType &elem) : GoType(kind, name), m_elem(elem) {} virtual CompilerType GetElementType() const { return m_elem; } private: // TODO: should we store this differently? CompilerType m_elem; GoElem(const GoElem &) = delete; const GoElem &operator=(const GoElem &) = delete; }; class GoArray : public GoElem { public: GoArray(const ConstString &name, uint64_t length, const CompilerType &elem) : GoElem(KIND_ARRAY, name, elem), m_length(length) {} uint64_t GetLength() const { return m_length; } private: uint64_t m_length; GoArray(const GoArray &) = delete; const GoArray &operator=(const GoArray &) = delete; }; class GoFunction : public GoType { public: GoFunction(const ConstString &name, bool is_variadic) : GoType(KIND_FUNC, name), m_is_variadic(is_variadic) {} bool IsVariadic() const { return m_is_variadic; } private: bool m_is_variadic; GoFunction(const GoFunction &) = delete; const GoFunction &operator=(const GoFunction &) = delete; }; class GoStruct : public GoType { public: struct Field { Field(const ConstString &name, const CompilerType &type, uint64_t offset) : m_name(name), m_type(type), m_byte_offset(offset) {} ConstString m_name; CompilerType m_type; uint64_t m_byte_offset; }; GoStruct(int kind, const ConstString &name, int64_t byte_size) : GoType(kind == 0 ? KIND_STRUCT : kind, name), m_is_complete(false), m_byte_size(byte_size) {} uint32_t GetNumFields() const { return m_fields.size(); } const Field *GetField(uint32_t i) const { if (i < m_fields.size()) return &m_fields[i]; return nullptr; } void AddField(const ConstString &name, const CompilerType &type, uint64_t offset) { m_fields.push_back(Field(name, type, offset)); } bool IsComplete() const { return m_is_complete; } void SetComplete() { m_is_complete = true; } int64_t GetByteSize() const { return m_byte_size; } private: bool m_is_complete; int64_t m_byte_size; std::vector m_fields; GoStruct(const GoStruct &) = delete; const GoStruct &operator=(const GoStruct &) = delete; }; GoArray *GoType::GetArray() { if (m_kind == KIND_ARRAY) { return static_cast(this); } return nullptr; } GoFunction *GoType::GetFunction() { if (m_kind == KIND_FUNC) { return static_cast(this); } return nullptr; } GoStruct *GoType::GetStruct() { switch (m_kind) { case KIND_STRING: case KIND_STRUCT: case KIND_SLICE: return static_cast(this); } return nullptr; } } // namespace lldb_private using namespace lldb_private; GoASTContext::GoASTContext() : TypeSystem(eKindGo), m_pointer_byte_size(0), m_int_byte_size(0), m_types(new TypeMap) {} GoASTContext::~GoASTContext() {} //------------------------------------------------------------------ // PluginInterface functions //------------------------------------------------------------------ ConstString GoASTContext::GetPluginNameStatic() { return ConstString("go"); } ConstString GoASTContext::GetPluginName() { return GoASTContext::GetPluginNameStatic(); } uint32_t GoASTContext::GetPluginVersion() { return 1; } lldb::TypeSystemSP GoASTContext::CreateInstance(lldb::LanguageType language, Module *module, Target *target) { if (language == eLanguageTypeGo) { ArchSpec arch; std::shared_ptr go_ast_sp; if (module) { arch = module->GetArchitecture(); go_ast_sp = std::shared_ptr(new GoASTContext); } else if (target) { arch = target->GetArchitecture(); go_ast_sp = std::shared_ptr( new GoASTContextForExpr(target->shared_from_this())); } if (arch.IsValid()) { go_ast_sp->SetAddressByteSize(arch.GetAddressByteSize()); return go_ast_sp; } } return lldb::TypeSystemSP(); } void GoASTContext::EnumerateSupportedLanguages( std::set &languages_for_types, std::set &languages_for_expressions) { static std::vector s_supported_languages_for_types( {lldb::eLanguageTypeGo}); static std::vector s_supported_languages_for_expressions( {}); languages_for_types.insert(s_supported_languages_for_types.begin(), s_supported_languages_for_types.end()); languages_for_expressions.insert( s_supported_languages_for_expressions.begin(), s_supported_languages_for_expressions.end()); } void GoASTContext::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), "AST context plug-in", CreateInstance, EnumerateSupportedLanguages); } void GoASTContext::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } //---------------------------------------------------------------------- // Tests //---------------------------------------------------------------------- bool GoASTContext::IsArrayType(lldb::opaque_compiler_type_t type, CompilerType *element_type, uint64_t *size, bool *is_incomplete) { if (element_type) element_type->Clear(); if (size) *size = 0; if (is_incomplete) *is_incomplete = false; GoArray *array = static_cast(type)->GetArray(); if (array) { if (size) *size = array->GetLength(); if (element_type) *element_type = array->GetElementType(); return true; } return false; } bool GoASTContext::IsVectorType(lldb::opaque_compiler_type_t type, CompilerType *element_type, uint64_t *size) { if (element_type) element_type->Clear(); if (size) *size = 0; return false; } bool GoASTContext::IsAggregateType(lldb::opaque_compiler_type_t type) { int kind = static_cast(type)->GetGoKind(); if (kind < GoType::KIND_ARRAY) return false; if (kind == GoType::KIND_PTR) return false; if (kind == GoType::KIND_CHAN) return false; if (kind == GoType::KIND_MAP) return false; if (kind == GoType::KIND_STRING) return false; if (kind == GoType::KIND_UNSAFEPOINTER) return false; return true; } bool GoASTContext::IsBeingDefined(lldb::opaque_compiler_type_t type) { return false; } bool GoASTContext::IsCharType(lldb::opaque_compiler_type_t type) { // Go's DWARF doesn't distinguish between rune and int32. return false; } bool GoASTContext::IsCompleteType(lldb::opaque_compiler_type_t type) { if (!type) return false; GoType *t = static_cast(type); if (GoStruct *s = t->GetStruct()) return s->IsComplete(); if (t->IsTypedef() || t->GetGoKind() == GoType::KIND_PTR) return t->GetElementType().IsCompleteType(); return true; } bool GoASTContext::IsConst(lldb::opaque_compiler_type_t type) { return false; } bool GoASTContext::IsCStringType(lldb::opaque_compiler_type_t type, uint32_t &length) { return false; } bool GoASTContext::IsDefined(lldb::opaque_compiler_type_t type) { return type != nullptr; } bool GoASTContext::IsFloatingPointType(lldb::opaque_compiler_type_t type, uint32_t &count, bool &is_complex) { int kind = static_cast(type)->GetGoKind(); if (kind >= GoType::KIND_FLOAT32 && kind <= GoType::KIND_COMPLEX128) { if (kind >= GoType::KIND_COMPLEX64) { is_complex = true; count = 2; } else { is_complex = false; count = 1; } return true; } count = 0; is_complex = false; return false; } bool GoASTContext::IsFunctionType(lldb::opaque_compiler_type_t type, bool *is_variadic_ptr) { GoFunction *func = static_cast(type)->GetFunction(); if (func) { if (is_variadic_ptr) *is_variadic_ptr = func->IsVariadic(); return true; } if (is_variadic_ptr) *is_variadic_ptr = false; return false; } uint32_t GoASTContext::IsHomogeneousAggregate(lldb::opaque_compiler_type_t type, CompilerType *base_type_ptr) { return false; } size_t GoASTContext::GetNumberOfFunctionArguments(lldb::opaque_compiler_type_t type) { return 0; } CompilerType GoASTContext::GetFunctionArgumentAtIndex(lldb::opaque_compiler_type_t type, const size_t index) { return CompilerType(); } bool GoASTContext::IsFunctionPointerType(lldb::opaque_compiler_type_t type) { return IsFunctionType(type); } bool GoASTContext::IsBlockPointerType(lldb::opaque_compiler_type_t type, CompilerType *function_pointer_type_ptr) { return false; } bool GoASTContext::IsIntegerType(lldb::opaque_compiler_type_t type, bool &is_signed) { is_signed = false; // TODO: Is bool an integer? if (type) { int kind = static_cast(type)->GetGoKind(); if (kind <= GoType::KIND_UINTPTR) { is_signed = (kind != GoType::KIND_BOOL) & (kind <= GoType::KIND_INT64); return true; } } return false; } bool GoASTContext::IsPolymorphicClass(lldb::opaque_compiler_type_t type) { return false; } bool GoASTContext::IsPossibleDynamicType( lldb::opaque_compiler_type_t type, CompilerType *target_type, // Can pass NULL bool check_cplusplus, bool check_objc) { if (target_type) target_type->Clear(); if (type) return static_cast(type)->GetGoKind() == GoType::KIND_INTERFACE; return false; } bool GoASTContext::IsRuntimeGeneratedType(lldb::opaque_compiler_type_t type) { return false; } bool GoASTContext::IsPointerType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type) { if (!type) return false; GoType *t = static_cast(type); if (pointee_type) { *pointee_type = t->GetElementType(); } switch (t->GetGoKind()) { case GoType::KIND_PTR: case GoType::KIND_UNSAFEPOINTER: case GoType::KIND_CHAN: case GoType::KIND_MAP: // TODO: is function a pointer? return true; default: return false; } } bool GoASTContext::IsPointerOrReferenceType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type) { return IsPointerType(type, pointee_type); } bool GoASTContext::IsReferenceType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type, bool *is_rvalue) { return false; } bool GoASTContext::IsScalarType(lldb::opaque_compiler_type_t type) { return !IsAggregateType(type); } bool GoASTContext::IsTypedefType(lldb::opaque_compiler_type_t type) { if (type) return static_cast(type)->IsTypedef(); return false; } bool GoASTContext::IsVoidType(lldb::opaque_compiler_type_t type) { if (!type) return false; return static_cast(type)->GetGoKind() == GoType::KIND_LLDB_VOID; } bool GoASTContext::SupportsLanguage(lldb::LanguageType language) { return language == eLanguageTypeGo; } //---------------------------------------------------------------------- // Type Completion //---------------------------------------------------------------------- bool GoASTContext::GetCompleteType(lldb::opaque_compiler_type_t type) { if (!type) return false; GoType *t = static_cast(type); if (t->IsTypedef() || t->GetGoKind() == GoType::KIND_PTR || t->GetArray()) return t->GetElementType().GetCompleteType(); if (GoStruct *s = t->GetStruct()) { if (s->IsComplete()) return true; CompilerType compiler_type(this, s); SymbolFile *symbols = GetSymbolFile(); return symbols && symbols->CompleteType(compiler_type); } return true; } //---------------------------------------------------------------------- // AST related queries //---------------------------------------------------------------------- uint32_t GoASTContext::GetPointerByteSize() { return m_pointer_byte_size; } //---------------------------------------------------------------------- // Accessors //---------------------------------------------------------------------- ConstString GoASTContext::GetTypeName(lldb::opaque_compiler_type_t type) { if (type) return static_cast(type)->GetName(); return ConstString(); } uint32_t GoASTContext::GetTypeInfo(lldb::opaque_compiler_type_t type, CompilerType *pointee_or_element_compiler_type) { if (pointee_or_element_compiler_type) pointee_or_element_compiler_type->Clear(); if (!type) return 0; GoType *t = static_cast(type); if (pointee_or_element_compiler_type) *pointee_or_element_compiler_type = t->GetElementType(); int kind = t->GetGoKind(); if (kind == GoType::KIND_ARRAY) return eTypeHasChildren | eTypeIsArray; if (kind < GoType::KIND_ARRAY) { uint32_t builtin_type_flags = eTypeIsBuiltIn | eTypeHasValue; if (kind < GoType::KIND_FLOAT32) { builtin_type_flags |= eTypeIsInteger | eTypeIsScalar; if (kind >= GoType::KIND_INT && kind <= GoType::KIND_INT64) builtin_type_flags |= eTypeIsSigned; } else { builtin_type_flags |= eTypeIsFloat; if (kind < GoType::KIND_COMPLEX64) builtin_type_flags |= eTypeIsComplex; else builtin_type_flags |= eTypeIsScalar; } return builtin_type_flags; } if (kind == GoType::KIND_STRING) return eTypeHasValue | eTypeIsBuiltIn; if (kind == GoType::KIND_FUNC) return eTypeIsFuncPrototype | eTypeHasValue; if (IsPointerType(type)) return eTypeIsPointer | eTypeHasValue | eTypeHasChildren; if (kind == GoType::KIND_LLDB_VOID) return 0; return eTypeHasChildren | eTypeIsStructUnion; } lldb::TypeClass GoASTContext::GetTypeClass(lldb::opaque_compiler_type_t type) { if (!type) return eTypeClassInvalid; int kind = static_cast(type)->GetGoKind(); if (kind == GoType::KIND_FUNC) return eTypeClassFunction; if (IsPointerType(type)) return eTypeClassPointer; if (kind < GoType::KIND_COMPLEX64) return eTypeClassBuiltin; if (kind <= GoType::KIND_COMPLEX128) return eTypeClassComplexFloat; if (kind == GoType::KIND_LLDB_VOID) return eTypeClassInvalid; return eTypeClassStruct; } lldb::BasicType GoASTContext::GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) { ConstString name = GetTypeName(type); if (name) { typedef UniqueCStringMap TypeNameToBasicTypeMap; static TypeNameToBasicTypeMap g_type_map; static llvm::once_flag g_once_flag; llvm::call_once(g_once_flag, []() { // "void" g_type_map.Append(ConstString("void"), eBasicTypeVoid); // "int" g_type_map.Append(ConstString("int"), eBasicTypeInt); g_type_map.Append(ConstString("uint"), eBasicTypeUnsignedInt); // Miscellaneous g_type_map.Append(ConstString("bool"), eBasicTypeBool); // Others. Should these map to C types? g_type_map.Append(ConstString("byte"), eBasicTypeOther); g_type_map.Append(ConstString("uint8"), eBasicTypeOther); g_type_map.Append(ConstString("uint16"), eBasicTypeOther); g_type_map.Append(ConstString("uint32"), eBasicTypeOther); g_type_map.Append(ConstString("uint64"), eBasicTypeOther); g_type_map.Append(ConstString("int8"), eBasicTypeOther); g_type_map.Append(ConstString("int16"), eBasicTypeOther); g_type_map.Append(ConstString("int32"), eBasicTypeOther); g_type_map.Append(ConstString("int64"), eBasicTypeOther); g_type_map.Append(ConstString("float32"), eBasicTypeOther); g_type_map.Append(ConstString("float64"), eBasicTypeOther); g_type_map.Append(ConstString("uintptr"), eBasicTypeOther); g_type_map.Sort(); }); return g_type_map.Find(name, eBasicTypeInvalid); } return eBasicTypeInvalid; } lldb::LanguageType GoASTContext::GetMinimumLanguage(lldb::opaque_compiler_type_t type) { return lldb::eLanguageTypeGo; } unsigned GoASTContext::GetTypeQualifiers(lldb::opaque_compiler_type_t type) { return 0; } //---------------------------------------------------------------------- // Creating related types //---------------------------------------------------------------------- CompilerType GoASTContext::GetArrayElementType(lldb::opaque_compiler_type_t type, uint64_t *stride) { GoArray *array = static_cast(type)->GetArray(); if (array) { if (stride) { *stride = array->GetElementType().GetByteSize(nullptr); } return array->GetElementType(); } return CompilerType(); } CompilerType GoASTContext::GetCanonicalType(lldb::opaque_compiler_type_t type) { GoType *t = static_cast(type); if (t->IsTypedef()) return t->GetElementType(); return CompilerType(this, type); } CompilerType GoASTContext::GetFullyUnqualifiedType(lldb::opaque_compiler_type_t type) { return CompilerType(this, type); } // Returns -1 if this isn't a function of if the function doesn't have a // prototype // Returns a value >= 0 if there is a prototype. int GoASTContext::GetFunctionArgumentCount(lldb::opaque_compiler_type_t type) { return GetNumberOfFunctionArguments(type); } CompilerType GoASTContext::GetFunctionArgumentTypeAtIndex(lldb::opaque_compiler_type_t type, size_t idx) { return GetFunctionArgumentAtIndex(type, idx); } CompilerType GoASTContext::GetFunctionReturnType(lldb::opaque_compiler_type_t type) { CompilerType result; if (type) { GoType *t = static_cast(type); if (t->GetGoKind() == GoType::KIND_FUNC) result = t->GetElementType(); } return result; } size_t GoASTContext::GetNumMemberFunctions(lldb::opaque_compiler_type_t type) { return 0; } TypeMemberFunctionImpl GoASTContext::GetMemberFunctionAtIndex(lldb::opaque_compiler_type_t type, size_t idx) { return TypeMemberFunctionImpl(); } CompilerType GoASTContext::GetNonReferenceType(lldb::opaque_compiler_type_t type) { return CompilerType(this, type); } CompilerType GoASTContext::GetPointeeType(lldb::opaque_compiler_type_t type) { if (!type) return CompilerType(); return static_cast(type)->GetElementType(); } CompilerType GoASTContext::GetPointerType(lldb::opaque_compiler_type_t type) { if (!type) return CompilerType(); ConstString type_name = GetTypeName(type); ConstString pointer_name(std::string("*") + type_name.GetCString()); GoType *pointer = (*m_types)[pointer_name].get(); if (pointer == nullptr) { pointer = new GoElem(GoType::KIND_PTR, pointer_name, CompilerType(this, type)); (*m_types)[pointer_name].reset(pointer); } return CompilerType(this, pointer); } // If the current object represents a typedef type, get the underlying type CompilerType GoASTContext::GetTypedefedType(lldb::opaque_compiler_type_t type) { if (IsTypedefType(type)) return static_cast(type)->GetElementType(); return CompilerType(); } //---------------------------------------------------------------------- // Create related types using the current type's AST //---------------------------------------------------------------------- CompilerType GoASTContext::GetBasicTypeFromAST(lldb::BasicType basic_type) { return CompilerType(); } CompilerType GoASTContext::GetBuiltinTypeForEncodingAndBitSize(lldb::Encoding encoding, size_t bit_size) { return CompilerType(); } //---------------------------------------------------------------------- // Exploring the type //---------------------------------------------------------------------- uint64_t GoASTContext::GetBitSize(lldb::opaque_compiler_type_t type, ExecutionContextScope *exe_scope) { if (!type) return 0; if (!GetCompleteType(type)) return 0; GoType *t = static_cast(type); GoArray *array = nullptr; switch (t->GetGoKind()) { case GoType::KIND_BOOL: case GoType::KIND_INT8: case GoType::KIND_UINT8: return 8; case GoType::KIND_INT16: case GoType::KIND_UINT16: return 16; case GoType::KIND_INT32: case GoType::KIND_UINT32: case GoType::KIND_FLOAT32: return 32; case GoType::KIND_INT64: case GoType::KIND_UINT64: case GoType::KIND_FLOAT64: case GoType::KIND_COMPLEX64: return 64; case GoType::KIND_COMPLEX128: return 128; case GoType::KIND_INT: case GoType::KIND_UINT: return m_int_byte_size * 8; case GoType::KIND_UINTPTR: case GoType::KIND_FUNC: // I assume this is a pointer? case GoType::KIND_CHAN: case GoType::KIND_PTR: case GoType::KIND_UNSAFEPOINTER: case GoType::KIND_MAP: return m_pointer_byte_size * 8; case GoType::KIND_ARRAY: array = t->GetArray(); return array->GetLength() * array->GetElementType().GetBitSize(exe_scope); case GoType::KIND_INTERFACE: return t->GetElementType().GetBitSize(exe_scope); case GoType::KIND_SLICE: case GoType::KIND_STRING: case GoType::KIND_STRUCT: return t->GetStruct()->GetByteSize() * 8; default: assert(false); } return 0; } lldb::Encoding GoASTContext::GetEncoding(lldb::opaque_compiler_type_t type, uint64_t &count) { count = 1; bool is_signed; if (IsIntegerType(type, is_signed)) return is_signed ? lldb::eEncodingSint : eEncodingUint; bool is_complex; uint32_t complex_count; if (IsFloatingPointType(type, complex_count, is_complex)) { count = complex_count; return eEncodingIEEE754; } if (IsPointerType(type)) return eEncodingUint; return eEncodingInvalid; } lldb::Format GoASTContext::GetFormat(lldb::opaque_compiler_type_t type) { if (!type) return eFormatDefault; switch (static_cast(type)->GetGoKind()) { case GoType::KIND_BOOL: return eFormatBoolean; case GoType::KIND_INT: case GoType::KIND_INT8: case GoType::KIND_INT16: case GoType::KIND_INT32: case GoType::KIND_INT64: return eFormatDecimal; case GoType::KIND_UINT: case GoType::KIND_UINT8: case GoType::KIND_UINT16: case GoType::KIND_UINT32: case GoType::KIND_UINT64: return eFormatUnsigned; case GoType::KIND_FLOAT32: case GoType::KIND_FLOAT64: return eFormatFloat; case GoType::KIND_COMPLEX64: case GoType::KIND_COMPLEX128: return eFormatComplexFloat; case GoType::KIND_UINTPTR: case GoType::KIND_CHAN: case GoType::KIND_PTR: case GoType::KIND_MAP: case GoType::KIND_UNSAFEPOINTER: return eFormatHex; case GoType::KIND_STRING: return eFormatCString; case GoType::KIND_ARRAY: case GoType::KIND_INTERFACE: case GoType::KIND_SLICE: case GoType::KIND_STRUCT: default: // Don't know how to display this. return eFormatBytes; } } size_t GoASTContext::GetTypeBitAlign(lldb::opaque_compiler_type_t type) { return 0; } uint32_t GoASTContext::GetNumChildren(lldb::opaque_compiler_type_t type, bool omit_empty_base_classes) { if (!type || !GetCompleteType(type)) return 0; GoType *t = static_cast(type); if (t->GetGoKind() == GoType::KIND_PTR) { CompilerType elem = t->GetElementType(); if (elem.IsAggregateType()) return elem.GetNumChildren(omit_empty_base_classes); return 1; } else if (GoArray *array = t->GetArray()) { return array->GetLength(); } else if (t->IsTypedef()) { return t->GetElementType().GetNumChildren(omit_empty_base_classes); } return GetNumFields(type); } uint32_t GoASTContext::GetNumFields(lldb::opaque_compiler_type_t type) { if (!type || !GetCompleteType(type)) return 0; GoType *t = static_cast(type); if (t->IsTypedef()) return t->GetElementType().GetNumFields(); GoStruct *s = t->GetStruct(); if (s) return s->GetNumFields(); return 0; } CompilerType GoASTContext::GetFieldAtIndex(lldb::opaque_compiler_type_t type, size_t idx, std::string &name, uint64_t *bit_offset_ptr, uint32_t *bitfield_bit_size_ptr, bool *is_bitfield_ptr) { if (bit_offset_ptr) *bit_offset_ptr = 0; if (bitfield_bit_size_ptr) *bitfield_bit_size_ptr = 0; if (is_bitfield_ptr) *is_bitfield_ptr = false; if (!type || !GetCompleteType(type)) return CompilerType(); GoType *t = static_cast(type); if (t->IsTypedef()) return t->GetElementType().GetFieldAtIndex( idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr); GoStruct *s = t->GetStruct(); if (s) { const auto *field = s->GetField(idx); if (field) { name = field->m_name.GetStringRef(); if (bit_offset_ptr) *bit_offset_ptr = field->m_byte_offset * 8; return field->m_type; } } return CompilerType(); } CompilerType GoASTContext::GetChildCompilerTypeAtIndex( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, size_t idx, bool transparent_pointers, bool omit_empty_base_classes, bool ignore_array_bounds, std::string &child_name, uint32_t &child_byte_size, int32_t &child_byte_offset, uint32_t &child_bitfield_bit_size, uint32_t &child_bitfield_bit_offset, bool &child_is_base_class, bool &child_is_deref_of_parent, ValueObject *valobj, uint64_t &language_flags) { child_name.clear(); child_byte_size = 0; child_byte_offset = 0; child_bitfield_bit_size = 0; child_bitfield_bit_offset = 0; child_is_base_class = false; child_is_deref_of_parent = false; language_flags = 0; if (!type || !GetCompleteType(type)) return CompilerType(); GoType *t = static_cast(type); if (t->GetStruct()) { uint64_t bit_offset; CompilerType ret = GetFieldAtIndex(type, idx, child_name, &bit_offset, nullptr, nullptr); child_byte_size = ret.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : nullptr); child_byte_offset = bit_offset / 8; return ret; } else if (t->GetGoKind() == GoType::KIND_PTR) { CompilerType pointee = t->GetElementType(); if (!pointee.IsValid() || pointee.IsVoidType()) return CompilerType(); if (transparent_pointers && pointee.IsAggregateType()) { bool tmp_child_is_deref_of_parent = false; return pointee.GetChildCompilerTypeAtIndex( exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, tmp_child_is_deref_of_parent, valobj, language_flags); } else { child_is_deref_of_parent = true; const char *parent_name = valobj ? valobj->GetName().GetCString() : NULL; if (parent_name) { child_name.assign(1, '*'); child_name += parent_name; } // We have a pointer to an simple type if (idx == 0 && pointee.GetCompleteType()) { child_byte_size = pointee.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = 0; return pointee; } } } else if (GoArray *a = t->GetArray()) { if (ignore_array_bounds || idx < a->GetLength()) { CompilerType element_type = a->GetElementType(); if (element_type.GetCompleteType()) { char element_name[64]; ::snprintf(element_name, sizeof(element_name), "[%zu]", idx); child_name.assign(element_name); child_byte_size = element_type.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = (int32_t)idx * (int32_t)child_byte_size; return element_type; } } } else if (t->IsTypedef()) { return t->GetElementType().GetChildCompilerTypeAtIndex( exe_ctx, idx, transparent_pointers, omit_empty_base_classes, ignore_array_bounds, child_name, child_byte_size, child_byte_offset, child_bitfield_bit_size, child_bitfield_bit_offset, child_is_base_class, child_is_deref_of_parent, valobj, language_flags); } return CompilerType(); } // Lookup a child given a name. This function will match base class names // and member member names in "clang_type" only, not descendants. uint32_t GoASTContext::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type, const char *name, bool omit_empty_base_classes) { if (!type || !GetCompleteType(type)) return UINT_MAX; GoType *t = static_cast(type); GoStruct *s = t->GetStruct(); if (s) { for (uint32_t i = 0; i < s->GetNumFields(); ++i) { const GoStruct::Field *f = s->GetField(i); if (f->m_name.GetStringRef() == name) return i; } } else if (t->GetGoKind() == GoType::KIND_PTR || t->IsTypedef()) { return t->GetElementType().GetIndexOfChildWithName(name, omit_empty_base_classes); } return UINT_MAX; } // Lookup a child member given a name. This function will match member names // only and will descend into "clang_type" children in search for the first // member in this class, or any base class that matches "name". // TODO: Return all matches for a given name by returning a // vector> // so we catch all names that match a given child name, not just the first. size_t GoASTContext::GetIndexOfChildMemberWithName( lldb::opaque_compiler_type_t type, const char *name, bool omit_empty_base_classes, std::vector &child_indexes) { uint32_t index = GetIndexOfChildWithName(type, name, omit_empty_base_classes); if (index == UINT_MAX) return 0; child_indexes.push_back(index); return 1; } // Converts "s" to a floating point value and place resulting floating // point bytes in the "dst" buffer. size_t GoASTContext::ConvertStringToFloatValue(lldb::opaque_compiler_type_t type, const char *s, uint8_t *dst, size_t dst_size) { assert(false); return 0; } //---------------------------------------------------------------------- // Dumping types //---------------------------------------------------------------------- #define DEPTH_INCREMENT 2 void GoASTContext::DumpValue(lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, Stream *s, lldb::Format format, const DataExtractor &data, lldb::offset_t data_byte_offset, size_t data_byte_size, uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset, bool show_types, bool show_summary, bool verbose, uint32_t depth) { if (IsTypedefType(type)) type = GetTypedefedType(type).GetOpaqueQualType(); if (!type) return; GoType *t = static_cast(type); if (GoStruct *st = t->GetStruct()) { if (GetCompleteType(type)) { uint32_t field_idx = 0; for (auto *field = st->GetField(field_idx); field != nullptr; field_idx++) { // Print the starting squiggly bracket (if this is the // first member) or comma (for member 2 and beyond) for // the struct/union/class member. if (field_idx == 0) s->PutChar('{'); else s->PutChar(','); // Indent s->Printf("\n%*s", depth + DEPTH_INCREMENT, ""); // Print the member type if requested if (show_types) { ConstString field_type_name = field->m_type.GetTypeName(); s->Printf("(%s) ", field_type_name.AsCString()); } // Print the member name and equal sign s->Printf("%s = ", field->m_name.AsCString()); // Dump the value of the member CompilerType field_type = field->m_type; field_type.DumpValue( exe_ctx, s, // Stream to dump to field_type .GetFormat(), // The format with which to display the member data, // Data buffer containing all bytes for this type data_byte_offset + field->m_byte_offset, // Offset into "data" where // to grab value from field->m_type.GetByteSize( exe_ctx->GetBestExecutionContextScope()), // Size of this type // in bytes 0, // Bitfield bit size 0, // Bitfield bit offset show_types, // Boolean indicating if we should show the variable // types show_summary, // Boolean indicating if we should show a summary for // the current type verbose, // Verbose output? depth + DEPTH_INCREMENT); // Scope depth for any types that have // children } // Indent the trailing squiggly bracket if (field_idx > 0) s->Printf("\n%*s}", depth, ""); } } if (GoArray *a = t->GetArray()) { CompilerType element_clang_type = a->GetElementType(); lldb::Format element_format = element_clang_type.GetFormat(); uint32_t element_byte_size = element_clang_type.GetByteSize(exe_ctx->GetBestExecutionContextScope()); uint64_t element_idx; for (element_idx = 0; element_idx < a->GetLength(); ++element_idx) { // Print the starting squiggly bracket (if this is the // first member) or comman (for member 2 and beyong) for // the struct/union/class member. if (element_idx == 0) s->PutChar('{'); else s->PutChar(','); // Indent and print the index s->Printf("\n%*s[%" PRIu64 "] ", depth + DEPTH_INCREMENT, "", element_idx); // Figure out the field offset within the current struct/union/class type uint64_t element_offset = element_idx * element_byte_size; // Dump the value of the member element_clang_type.DumpValue( exe_ctx, s, // Stream to dump to element_format, // The format with which to display the element data, // Data buffer containing all bytes for this type data_byte_offset + element_offset, // Offset into "data" where to grab value from element_byte_size, // Size of this type in bytes 0, // Bitfield bit size 0, // Bitfield bit offset show_types, // Boolean indicating if we should show the variable types show_summary, // Boolean indicating if we should show a summary for // the current type verbose, // Verbose output? depth + DEPTH_INCREMENT); // Scope depth for any types that have children } // Indent the trailing squiggly bracket if (element_idx > 0) s->Printf("\n%*s}", depth, ""); } if (show_summary) DumpSummary(type, exe_ctx, s, data, data_byte_offset, data_byte_size); } bool GoASTContext::DumpTypeValue(lldb::opaque_compiler_type_t type, Stream *s, lldb::Format format, const DataExtractor &data, lldb::offset_t byte_offset, size_t byte_size, uint32_t bitfield_bit_size, uint32_t bitfield_bit_offset, ExecutionContextScope *exe_scope) { if (!type) return false; if (IsAggregateType(type)) { return false; } else { GoType *t = static_cast(type); if (t->IsTypedef()) { CompilerType typedef_compiler_type = t->GetElementType(); if (format == eFormatDefault) format = typedef_compiler_type.GetFormat(); uint64_t typedef_byte_size = typedef_compiler_type.GetByteSize(exe_scope); return typedef_compiler_type.DumpTypeValue( s, format, // The format with which to display the element data, // Data buffer containing all bytes for this type byte_offset, // Offset into "data" where to grab value from typedef_byte_size, // Size of this type in bytes bitfield_bit_size, // Size in bits of a bitfield value, if zero don't // treat as a bitfield bitfield_bit_offset, // Offset in bits of a bitfield value if // bitfield_bit_size != 0 exe_scope); } uint32_t item_count = 1; // A few formats, we might need to modify our size and count for depending // on how we are trying to display the value... switch (format) { default: case eFormatBoolean: case eFormatBinary: case eFormatComplex: case eFormatCString: // NULL terminated C strings case eFormatDecimal: case eFormatEnum: case eFormatHex: case eFormatHexUppercase: case eFormatFloat: case eFormatOctal: case eFormatOSType: case eFormatUnsigned: case eFormatPointer: case eFormatVectorOfChar: case eFormatVectorOfSInt8: case eFormatVectorOfUInt8: case eFormatVectorOfSInt16: case eFormatVectorOfUInt16: case eFormatVectorOfSInt32: case eFormatVectorOfUInt32: case eFormatVectorOfSInt64: case eFormatVectorOfUInt64: case eFormatVectorOfFloat32: case eFormatVectorOfFloat64: case eFormatVectorOfUInt128: break; case eFormatChar: case eFormatCharPrintable: case eFormatCharArray: case eFormatBytes: case eFormatBytesWithASCII: item_count = byte_size; byte_size = 1; break; case eFormatUnicode16: item_count = byte_size / 2; byte_size = 2; break; case eFormatUnicode32: item_count = byte_size / 4; byte_size = 4; break; } return DumpDataExtractor(data, s, byte_offset, format, byte_size, item_count, UINT32_MAX, LLDB_INVALID_ADDRESS, bitfield_bit_size, bitfield_bit_offset, exe_scope); } return 0; } void GoASTContext::DumpSummary(lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, Stream *s, const DataExtractor &data, lldb::offset_t data_offset, size_t data_byte_size) { if (type && GoType::KIND_STRING == static_cast(type)->GetGoKind()) { // TODO(ribrdb): read length and data } } void GoASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type) { // Dump to stdout StreamFile s(stdout, false); DumpTypeDescription(type, &s); } void GoASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type, Stream *s) { if (!type) return; ConstString name = GetTypeName(type); GoType *t = static_cast(type); if (GoStruct *st = t->GetStruct()) { if (GetCompleteType(type)) { if (NULL == strchr(name.AsCString(), '{')) s->Printf("type %s ", name.AsCString()); s->PutCString("struct {"); if (st->GetNumFields() == 0) { s->PutChar('}'); return; } s->IndentMore(); uint32_t field_idx = 0; for (auto *field = st->GetField(field_idx); field != nullptr; field_idx++) { s->PutChar('\n'); s->Indent(); s->Printf("%s %s", field->m_name.AsCString(), field->m_type.GetTypeName().AsCString()); } s->IndentLess(); s->PutChar('\n'); s->Indent("}"); return; } } s->PutCString(name.AsCString()); } CompilerType GoASTContext::CreateArrayType(const ConstString &name, const CompilerType &element_type, uint64_t length) { GoType *type = new GoArray(name, length, element_type); (*m_types)[name].reset(type); return CompilerType(this, type); } CompilerType GoASTContext::CreateBaseType(int go_kind, const lldb_private::ConstString &name, uint64_t byte_size) { if (go_kind == GoType::KIND_UINT || go_kind == GoType::KIND_INT) m_int_byte_size = byte_size; GoType *type = new GoType(go_kind, name); (*m_types)[name].reset(type); return CompilerType(this, type); } CompilerType GoASTContext::CreateTypedefType(int kind, const ConstString &name, CompilerType impl) { GoType *type = new GoElem(kind, name, impl); (*m_types)[name].reset(type); return CompilerType(this, type); } CompilerType GoASTContext::CreateVoidType(const lldb_private::ConstString &name) { GoType *type = new GoType(GoType::KIND_LLDB_VOID, name); (*m_types)[name].reset(type); return CompilerType(this, type); } CompilerType GoASTContext::CreateStructType(int kind, const lldb_private::ConstString &name, uint32_t byte_size) { GoType *type = new GoStruct(kind, name, byte_size); (*m_types)[name].reset(type); return CompilerType(this, type); } void GoASTContext::AddFieldToStruct( const lldb_private::CompilerType &struct_type, const lldb_private::ConstString &name, const lldb_private::CompilerType &field_type, uint32_t byte_offset) { if (!struct_type) return; GoASTContext *ast = llvm::dyn_cast_or_null(struct_type.GetTypeSystem()); if (!ast) return; GoType *type = static_cast(struct_type.GetOpaqueQualType()); if (GoStruct *s = type->GetStruct()) s->AddField(name, field_type, byte_offset); } void GoASTContext::CompleteStructType( const lldb_private::CompilerType &struct_type) { if (!struct_type) return; GoASTContext *ast = llvm::dyn_cast_or_null(struct_type.GetTypeSystem()); if (!ast) return; GoType *type = static_cast(struct_type.GetOpaqueQualType()); if (GoStruct *s = type->GetStruct()) s->SetComplete(); } CompilerType GoASTContext::CreateFunctionType(const lldb_private::ConstString &name, CompilerType *params, size_t params_count, bool is_variadic) { GoType *type = new GoFunction(name, is_variadic); (*m_types)[name].reset(type); return CompilerType(this, type); } bool GoASTContext::IsGoString(const lldb_private::CompilerType &type) { if (!type.IsValid() || !llvm::dyn_cast_or_null(type.GetTypeSystem())) return false; return GoType::KIND_STRING == static_cast(type.GetOpaqueQualType())->GetGoKind(); } bool GoASTContext::IsGoSlice(const lldb_private::CompilerType &type) { if (!type.IsValid() || !llvm::dyn_cast_or_null(type.GetTypeSystem())) return false; return GoType::KIND_SLICE == static_cast(type.GetOpaqueQualType())->GetGoKind(); } bool GoASTContext::IsGoInterface(const lldb_private::CompilerType &type) { if (!type.IsValid() || !llvm::dyn_cast_or_null(type.GetTypeSystem())) return false; return GoType::KIND_INTERFACE == static_cast(type.GetOpaqueQualType())->GetGoKind(); } bool GoASTContext::IsPointerKind(uint8_t kind) { return (kind & GoType::KIND_MASK) == GoType::KIND_PTR; } bool GoASTContext::IsDirectIface(uint8_t kind) { return (kind & GoType::KIND_DIRECT_IFACE) == GoType::KIND_DIRECT_IFACE; } DWARFASTParser *GoASTContext::GetDWARFParser() { if (!m_dwarf_ast_parser_ap) m_dwarf_ast_parser_ap.reset(new DWARFASTParserGo(*this)); return m_dwarf_ast_parser_ap.get(); } UserExpression *GoASTContextForExpr::GetUserExpression( llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, Expression::ResultType desired_type, const EvaluateExpressionOptions &options) { TargetSP target = m_target_wp.lock(); if (target) return new GoUserExpression(*target, expr, prefix, language, desired_type, options); return nullptr; }