Index: vendor/lldb/dist/CMakeLists.txt =================================================================== --- vendor/lldb/dist/CMakeLists.txt (revision 312182) +++ vendor/lldb/dist/CMakeLists.txt (revision 312183) @@ -1,88 +1,88 @@ cmake_minimum_required(VERSION 3.4.3) include(cmake/modules/LLDBStandalone.cmake) include(cmake/modules/LLDBConfig.cmake) include(cmake/modules/AddLLDB.cmake) if (__ANDROID_NDK__ OR (CMAKE_SYSTEM_NAME MATCHES "Windows")) set(LLDB_DEFAULT_DISABLE_LIBEDIT 1) else() set(LLDB_DEFAULT_DISABLE_LIBEDIT 0) endif () # We need libedit support to go down both the source and # the scripts directories. set(LLDB_DISABLE_LIBEDIT ${LLDB_DEFAULT_DISABLE_LIBEDIT} CACHE BOOL "Disables the use of editline.") if (LLDB_DISABLE_LIBEDIT) add_definitions( -DLLDB_DISABLE_LIBEDIT ) endif() # add_subdirectory(include) add_subdirectory(docs) if (NOT LLDB_DISABLE_PYTHON) set(LLDB_PYTHON_TARGET_DIR ${LLDB_BINARY_DIR}/scripts) if(LLDB_BUILD_FRAMEWORK) set(LLDB_PYTHON_TARGET_DIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/${LLDB_FRAMEWORK_INSTALL_DIR}) else() # Don't set -m when building the framework. set(FINISH_EXTRA_ARGS "-m") endif() - set(LLDB_WRAP_PYTHON ${LLDB_PYTHON_TARGET_DIR}/LLDBWrapPython.cpp) + set(LLDB_WRAP_PYTHON ${LLDB_BINARY_DIR}/scripts/LLDBWrapPython.cpp) add_subdirectory(scripts) endif () add_subdirectory(source) add_subdirectory(test) add_subdirectory(tools) add_subdirectory(unittests) add_subdirectory(lit) if (NOT LLDB_DISABLE_PYTHON) # Add a Post-Build Event to copy over Python files and create the symlink # to liblldb.so for the Python API(hardlink on Windows) add_custom_target(finish_swig ALL COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/scripts/finishSwigWrapperClasses.py --srcRoot=${LLDB_SOURCE_DIR} --targetDir=${LLDB_PYTHON_TARGET_DIR} --cfgBldDir=${CMAKE_CURRENT_BINARY_DIR}/scripts --prefix=${CMAKE_BINARY_DIR} --cmakeBuildConfiguration=${CMAKE_CFG_INTDIR} --lldbLibDir=lib${LLVM_LIBDIR_SUFFIX} ${FINISH_EXTRA_ARGS} VERBATIM DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/scripts/finishSwigWrapperClasses.py DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/scripts/lldb.py COMMENT "Python script sym-linking LLDB Python API") # We depend on liblldb being built before we can do this step. add_dependencies(finish_swig liblldb lldb-argdumper) # If we build the readline module, we depend on that happening # first. if (TARGET readline) add_dependencies(finish_swig readline) endif() # Ensure we do the python post-build step when building lldb. add_dependencies(lldb finish_swig) if(LLDB_BUILD_FRAMEWORK) # The target to install libLLDB needs to depend on finish swig so that the # framework build properly copies over the Python files. add_dependencies(install-liblldb finish_swig) endif() # Add a Post-Build Event to copy the custom Python DLL to the lldb binaries dir so that Windows can find it when launching # lldb.exe or any other executables that were linked with liblldb. if (WIN32 AND NOT "${PYTHON_DLL}" STREQUAL "") # When using the Visual Studio CMake generator the lldb binaries end up in Release/bin, Debug/bin etc. file(TO_NATIVE_PATH "${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin" LLDB_BIN_DIR) file(TO_NATIVE_PATH "${PYTHON_DLL}" PYTHON_DLL_NATIVE_PATH) add_custom_command( TARGET finish_swig POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy ${PYTHON_DLL_NATIVE_PATH} ${LLDB_BIN_DIR} VERBATIM COMMENT "Copying Python DLL to LLDB binaries directory.") endif () endif () Index: vendor/lldb/dist/cmake/modules/LLDBStandalone.cmake =================================================================== --- vendor/lldb/dist/cmake/modules/LLDBStandalone.cmake (revision 312182) +++ vendor/lldb/dist/cmake/modules/LLDBStandalone.cmake (revision 312183) @@ -1,134 +1,135 @@ # If we are not building as a part of LLVM, build LLDB as an # standalone project, using LLVM as an external library: if (CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR) project(lldb) cmake_minimum_required(VERSION 2.8.12.2) if (POLICY CMP0022) cmake_policy(SET CMP0022 NEW) # automatic when 2.8.12 is required endif() option(LLVM_INSTALL_TOOLCHAIN_ONLY "Only include toolchain files in the 'install' target." OFF) # Rely on llvm-config. set(CONFIG_OUTPUT) find_program(LLVM_CONFIG "llvm-config") if(LLVM_CONFIG) message(STATUS "Found LLVM_CONFIG as ${LLVM_CONFIG}") set(CONFIG_COMMAND ${LLVM_CONFIG} "--assertion-mode" "--bindir" "--libdir" "--includedir" "--prefix" - "--src-root") + "--src-root" + "--cmakedir") execute_process( COMMAND ${CONFIG_COMMAND} RESULT_VARIABLE HAD_ERROR OUTPUT_VARIABLE CONFIG_OUTPUT ) if(NOT HAD_ERROR) string(REGEX REPLACE "[ \t]*[\r\n]+[ \t]*" ";" CONFIG_OUTPUT ${CONFIG_OUTPUT}) else() string(REPLACE ";" " " CONFIG_COMMAND_STR "${CONFIG_COMMAND}") message(STATUS "${CONFIG_COMMAND_STR}") message(FATAL_ERROR "llvm-config failed with status ${HAD_ERROR}") endif() else() message(FATAL_ERROR "llvm-config not found -- ${LLVM_CONFIG}") endif() list(GET CONFIG_OUTPUT 0 ENABLE_ASSERTIONS) list(GET CONFIG_OUTPUT 1 TOOLS_BINARY_DIR) list(GET CONFIG_OUTPUT 2 LIBRARY_DIR) list(GET CONFIG_OUTPUT 3 INCLUDE_DIR) list(GET CONFIG_OUTPUT 4 LLVM_OBJ_ROOT) list(GET CONFIG_OUTPUT 5 MAIN_SRC_DIR) + list(GET CONFIG_OUTPUT 6 LLVM_CMAKE_PATH) if(NOT MSVC_IDE) set(LLVM_ENABLE_ASSERTIONS ${ENABLE_ASSERTIONS} CACHE BOOL "Enable assertions") # Assertions should follow llvm-config's. mark_as_advanced(LLVM_ENABLE_ASSERTIONS) endif() set(LLVM_TOOLS_BINARY_DIR ${TOOLS_BINARY_DIR} CACHE PATH "Path to llvm/bin") set(LLVM_LIBRARY_DIR ${LIBRARY_DIR} CACHE PATH "Path to llvm/lib") set(LLVM_MAIN_INCLUDE_DIR ${INCLUDE_DIR} CACHE PATH "Path to llvm/include") set(LLVM_DIR ${LLVM_OBJ_ROOT}/cmake/modules/CMakeFiles CACHE PATH "Path to LLVM build tree CMake files") set(LLVM_BINARY_DIR ${LLVM_OBJ_ROOT} CACHE PATH "Path to LLVM build tree") set(LLVM_MAIN_SRC_DIR ${MAIN_SRC_DIR} CACHE PATH "Path to LLVM source tree") find_program(LLVM_TABLEGEN_EXE "llvm-tblgen" ${LLVM_TOOLS_BINARY_DIR} NO_DEFAULT_PATH) - set(LLVM_CMAKE_PATH "${LLVM_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm") set(LLVMCONFIG_FILE "${LLVM_CMAKE_PATH}/LLVMConfig.cmake") if(EXISTS ${LLVMCONFIG_FILE}) list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_PATH}") include(${LLVMCONFIG_FILE}) else() message(FATAL_ERROR "Not found: ${LLVMCONFIG_FILE}") endif() # They are used as destination of target generators. set(LLVM_RUNTIME_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin) set(LLVM_LIBRARY_OUTPUT_INTDIR ${CMAKE_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib${LLVM_LIBDIR_SUFFIX}) if(WIN32 OR CYGWIN) # DLL platform -- put DLLs into bin. set(LLVM_SHLIB_OUTPUT_INTDIR ${LLVM_RUNTIME_OUTPUT_INTDIR}) else() set(LLVM_SHLIB_OUTPUT_INTDIR ${LLVM_LIBRARY_OUTPUT_INTDIR}) endif() include(AddLLVM) include(HandleLLVMOptions) include(CheckAtomic) if (PYTHON_EXECUTABLE STREQUAL "") set(Python_ADDITIONAL_VERSIONS 3.5 3.4 3.3 3.2 3.1 3.0 2.7 2.6 2.5) include(FindPythonInterp) if( NOT PYTHONINTERP_FOUND ) message(FATAL_ERROR "Unable to find Python interpreter, required for builds and testing. Please install Python or specify the PYTHON_EXECUTABLE CMake variable.") endif() else() message("-- Found PythonInterp: ${PYTHON_EXECUTABLE}") endif() # Import CMake library targets from LLVM and Clang. include("${LLVM_OBJ_ROOT}/lib${LLVM_LIBDIR_SUFFIX}/cmake/llvm/LLVMConfig.cmake") # cmake/clang/ClangConfig.cmake is not created when LLVM and Cland are built together. if (EXISTS "${LLVM_OBJ_ROOT}/lib${LLVM_LIBDIR_SUFFIX}/cmake/clang/ClangConfig.cmake") include("${LLVM_OBJ_ROOT}/lib${LLVM_LIBDIR_SUFFIX}/cmake/clang/ClangConfig.cmake") endif() set(PACKAGE_VERSION "${LLVM_PACKAGE_VERSION}") set(LLVM_BINARY_DIR ${CMAKE_BINARY_DIR}) set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories("${LLVM_BINARY_DIR}/include" "${LLVM_MAIN_INCLUDE_DIR}") # Next three include directories are needed when llvm-config is located in build directory. # LLVM and Cland are assumed to be built together if (EXISTS "${LLVM_OBJ_ROOT}/include") include_directories("${LLVM_OBJ_ROOT}/include") endif() if (EXISTS "${LLVM_MAIN_SRC_DIR}/tools/clang/include") include_directories("${LLVM_MAIN_SRC_DIR}/tools/clang/include") endif() if (EXISTS "${LLVM_OBJ_ROOT}/tools/clang/include") include_directories("${LLVM_OBJ_ROOT}/tools/clang/include") endif() link_directories("${LLVM_LIBRARY_DIR}") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib${LLVM_LIBDIR_SUFFIX}) set(LLDB_BUILT_STANDALONE 1) endif() Index: vendor/lldb/dist/include/lldb/Core/Error.h =================================================================== --- vendor/lldb/dist/include/lldb/Core/Error.h (revision 312182) +++ vendor/lldb/dist/include/lldb/Core/Error.h (revision 312183) @@ -1,304 +1,315 @@ //===-- Error.h -------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef __DCError_h__ #define __DCError_h__ #if defined(__cplusplus) #include "llvm/Support/DataTypes.h" +#include "llvm/Support/FormatVariadic.h" #include #include #include #include "lldb/lldb-private.h" #include "llvm/Support/FormatVariadic.h" namespace lldb_private { class Log; //---------------------------------------------------------------------- /// @class Error Error.h "lldb/Core/Error.h" /// @brief An error handling class. /// /// This class is designed to be able to hold any error code that can be /// encountered on a given platform. The errors are stored as a value /// of type Error::ValueType. This value should be large enough to hold /// any and all errors that the class supports. Each error has an /// associated type that is of type lldb::ErrorType. New types /// can be added to support new error types, and architecture specific /// types can be enabled. In the future we may wish to switch to a /// registration mechanism where new error types can be registered at /// runtime instead of a hard coded scheme. /// /// All errors in this class also know how to generate a string /// representation of themselves for printing results and error codes. /// The string value will be fetched on demand and its string value will /// be cached until the error is cleared of the value of the error /// changes. //---------------------------------------------------------------------- class Error { public: //------------------------------------------------------------------ /// Every error value that this object can contain needs to be able /// to fit into ValueType. //------------------------------------------------------------------ typedef uint32_t ValueType; //------------------------------------------------------------------ /// Default constructor. /// /// Initialize the error object with a generic success value. /// /// @param[in] err /// An error code. /// /// @param[in] type /// The type for \a err. //------------------------------------------------------------------ Error(); explicit Error(ValueType err, lldb::ErrorType type = lldb::eErrorTypeGeneric); explicit Error(const char *format, ...) __attribute__((format(printf, 2, 3))); Error(const Error &rhs); //------------------------------------------------------------------ /// Assignment operator. /// /// @param[in] err /// An error code. /// /// @return /// A const reference to this object. //------------------------------------------------------------------ const Error &operator=(const Error &rhs); //------------------------------------------------------------------ /// Assignment operator from a kern_return_t. /// /// Sets the type to \c MachKernel and the error code to \a err. /// /// @param[in] err /// A mach error code. /// /// @return /// A const reference to this object. //------------------------------------------------------------------ const Error &operator=(uint32_t err); ~Error(); //------------------------------------------------------------------ /// Get the error string associated with the current error. // /// Gets the error value as a NULL terminated C string. The error /// string will be fetched and cached on demand. The error string /// will be retrieved from a callback that is appropriate for the /// type of the error and will be cached until the error value is /// changed or cleared. /// /// @return /// The error as a NULL terminated C string value if the error /// is valid and is able to be converted to a string value, /// NULL otherwise. //------------------------------------------------------------------ const char *AsCString(const char *default_error_str = "unknown error") const; //------------------------------------------------------------------ /// Clear the object state. /// /// Reverts the state of this object to contain a generic success /// value and frees any cached error string value. //------------------------------------------------------------------ void Clear(); //------------------------------------------------------------------ /// Test for error condition. /// /// @return /// \b true if this object contains an error, \b false /// otherwise. //------------------------------------------------------------------ bool Fail() const; //------------------------------------------------------------------ /// Access the error value. /// /// @return /// The error value. //------------------------------------------------------------------ ValueType GetError() const; //------------------------------------------------------------------ /// Access the error type. /// /// @return /// The error type enumeration value. //------------------------------------------------------------------ lldb::ErrorType GetType() const; //------------------------------------------------------------------ /// Log an error to Log(). /// /// Log the error given a formatted string \a format. If the this /// object contains an error code, update the error string to /// contain the prefix "error: ", followed by the formatted string, /// followed by the error value and any string that describes the /// error value. This allows more context to be given to an error /// string that remains cached in this object. Logging always occurs /// even when the error code contains a non-error value. /// /// @param[in] format /// A printf style format string. /// /// @param[in] ... /// Variable arguments that are needed for the printf style /// format string \a format. //------------------------------------------------------------------ void PutToLog(Log *log, const char *format, ...) __attribute__((format(printf, 3, 4))); //------------------------------------------------------------------ /// Log an error to Log() if the error value is an error. /// /// Log the error given a formatted string \a format only if the /// error value in this object describes an error condition. If the /// this object contains an error, update the error string to /// contain the prefix "error: " followed by the formatted string, /// followed by the error value and any string that describes the /// error value. This allows more context to be given to an error /// string that remains cached in this object. /// /// @param[in] format /// A printf style format string. /// /// @param[in] ... /// Variable arguments that are needed for the printf style /// format string \a format. //------------------------------------------------------------------ void LogIfError(Log *log, const char *format, ...) __attribute__((format(printf, 3, 4))); //------------------------------------------------------------------ /// Set accessor from a kern_return_t. /// /// Set accesssor for the error value to \a err and the error type /// to \c MachKernel. /// /// @param[in] err /// A mach error code. //------------------------------------------------------------------ void SetMachError(uint32_t err); void SetExpressionError(lldb::ExpressionResults, const char *mssg); int SetExpressionErrorWithFormat(lldb::ExpressionResults, const char *format, ...) __attribute__((format(printf, 3, 4))); //------------------------------------------------------------------ /// Set accesssor with an error value and type. /// /// Set accesssor for the error value to \a err and the error type /// to \a type. /// /// @param[in] err /// A mach error code. /// /// @param[in] type /// The type for \a err. //------------------------------------------------------------------ void SetError(ValueType err, lldb::ErrorType type); //------------------------------------------------------------------ /// Set the current error to errno. /// /// Update the error value to be \c errno and update the type to /// be \c Error::POSIX. //------------------------------------------------------------------ void SetErrorToErrno(); //------------------------------------------------------------------ /// Set the current error to a generic error. /// /// Update the error value to be \c LLDB_GENERIC_ERROR and update the /// type to be \c Error::Generic. //------------------------------------------------------------------ void SetErrorToGenericError(); //------------------------------------------------------------------ /// Set the current error string to \a err_str. /// /// Set accessor for the error string value for a generic errors, /// or to supply additional details above and beyond the standard /// error strings that the standard type callbacks typically /// provide. This allows custom strings to be supplied as an /// error explanation. The error string value will remain until the /// error value is cleared or a new error value/type is assigned. /// /// @param err_str /// The new custom error string to copy and cache. //------------------------------------------------------------------ void SetErrorString(llvm::StringRef err_str); //------------------------------------------------------------------ /// Set the current error string to a formatted error string. /// /// @param format /// A printf style format string //------------------------------------------------------------------ int SetErrorStringWithFormat(const char *format, ...) __attribute__((format(printf, 2, 3))); int SetErrorStringWithVarArg(const char *format, va_list args); template void SetErrorStringWithFormatv(const char *format, Args &&... args) { SetErrorString(llvm::formatv(format, std::forward(args)...).str()); } //------------------------------------------------------------------ /// Test for success condition. /// /// Returns true if the error code in this object is considered a /// successful return value. /// /// @return /// \b true if this object contains an value that describes /// success (non-erro), \b false otherwise. //------------------------------------------------------------------ bool Success() const; //------------------------------------------------------------------ /// Test for a failure due to a generic interrupt. /// /// Returns true if the error code in this object was caused by an interrupt. /// At present only supports Posix EINTR. /// /// @return /// \b true if this object contains an value that describes /// failure due to interrupt, \b false otherwise. //------------------------------------------------------------------ bool WasInterrupted() const; protected: //------------------------------------------------------------------ /// Member variables //------------------------------------------------------------------ ValueType m_code; ///< Error code as an integer value. lldb::ErrorType m_type; ///< The type of the above error code. mutable std::string m_string; ///< A string representation of the error code. }; } // namespace lldb_private + +namespace llvm { +template <> struct format_provider { + static void format(const lldb_private::Error &error, llvm::raw_ostream &OS, + llvm::StringRef Options) { + llvm::format_provider::format(error.AsCString(), OS, + Options); + } +}; +} #endif // #if defined(__cplusplus) #endif // #ifndef __DCError_h__ Index: vendor/lldb/dist/include/lldb/Symbol/Type.h =================================================================== --- vendor/lldb/dist/include/lldb/Symbol/Type.h (revision 312182) +++ vendor/lldb/dist/include/lldb/Symbol/Type.h (revision 312183) @@ -1,674 +1,675 @@ //===-- Type.h --------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #ifndef liblldb_Type_h_ #define liblldb_Type_h_ #include "lldb/Core/ClangForward.h" #include "lldb/Core/ConstString.h" #include "lldb/Core/UserID.h" #include "lldb/Symbol/CompilerDecl.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/Declaration.h" #include "lldb/lldb-private.h" #include "llvm/ADT/APSInt.h" #include namespace lldb_private { //---------------------------------------------------------------------- // CompilerContext allows an array of these items to be passed to // perform detailed lookups in SymbolVendor and SymbolFile functions. //---------------------------------------------------------------------- struct CompilerContext { CompilerContext(CompilerContextKind t, const ConstString &n) : type(t), name(n) {} bool operator==(const CompilerContext &rhs) const { return type == rhs.type && name == rhs.name; } void Dump() const; CompilerContextKind type; ConstString name; }; class SymbolFileType : public std::enable_shared_from_this, public UserID { public: SymbolFileType(SymbolFile &symbol_file, lldb::user_id_t uid) : UserID(uid), m_symbol_file(symbol_file) {} SymbolFileType(SymbolFile &symbol_file, const lldb::TypeSP &type_sp); ~SymbolFileType() {} Type *operator->() { return GetType(); } Type *GetType(); protected: SymbolFile &m_symbol_file; lldb::TypeSP m_type_sp; }; class Type : public std::enable_shared_from_this, public UserID { public: typedef enum EncodingDataTypeTag { eEncodingInvalid, eEncodingIsUID, ///< This type is the type whose UID is m_encoding_uid eEncodingIsConstUID, ///< This type is the type whose UID is m_encoding_uid ///with the const qualifier added eEncodingIsRestrictUID, ///< This type is the type whose UID is ///m_encoding_uid with the restrict qualifier added eEncodingIsVolatileUID, ///< This type is the type whose UID is ///m_encoding_uid with the volatile qualifier added eEncodingIsTypedefUID, ///< This type is pointer to a type whose UID is ///m_encoding_uid eEncodingIsPointerUID, ///< This type is pointer to a type whose UID is ///m_encoding_uid eEncodingIsLValueReferenceUID, ///< This type is L value reference to a type ///whose UID is m_encoding_uid eEncodingIsRValueReferenceUID, ///< This type is R value reference to a type ///whose UID is m_encoding_uid eEncodingIsSyntheticUID } EncodingDataType; // We must force the underlying type of the enum to be unsigned here. Not all // compilers // behave the same with regards to the default underlying type of an enum, but // because // this enum is used in an enum bitfield and integer comparisons are done with // the value // we need to guarantee that it's always unsigned so that, for example, // eResolveStateFull // doesn't compare less than eResolveStateUnresolved when used in a 2-bit // bitfield. typedef enum ResolveStateTag : unsigned { eResolveStateUnresolved = 0, eResolveStateForward = 1, eResolveStateLayout = 2, eResolveStateFull = 3 } ResolveState; Type(lldb::user_id_t uid, SymbolFile *symbol_file, const ConstString &name, uint64_t byte_size, SymbolContextScope *context, lldb::user_id_t encoding_uid, EncodingDataType encoding_uid_type, const Declaration &decl, const CompilerType &compiler_qual_type, ResolveState compiler_type_resolve_state); // This makes an invalid type. Used for functions that return a Type when // they // get an error. Type(); Type(const Type &rhs); const Type &operator=(const Type &rhs); void Dump(Stream *s, bool show_context); void DumpTypeName(Stream *s); // Since Type instances only keep a "SymbolFile *" internally, other classes // like TypeImpl need make sure the module is still around before playing with // Type instances. They can store a weak pointer to the Module; lldb::ModuleSP GetModule(); void GetDescription(Stream *s, lldb::DescriptionLevel level, bool show_name); SymbolFile *GetSymbolFile() { return m_symbol_file; } const SymbolFile *GetSymbolFile() const { return m_symbol_file; } TypeList *GetTypeList(); const ConstString &GetName(); uint64_t GetByteSize(); uint32_t GetNumChildren(bool omit_empty_base_classes); bool IsAggregateType(); bool IsValidType() { return m_encoding_uid_type != eEncodingInvalid; } bool IsTypedef() { return m_encoding_uid_type == eEncodingIsTypedefUID; } lldb::TypeSP GetTypedefType(); const ConstString &GetName() const { return m_name; } ConstString GetQualifiedName(); void DumpValue(ExecutionContext *exe_ctx, Stream *s, const DataExtractor &data, uint32_t data_offset, bool show_type, bool show_summary, bool verbose, lldb::Format format = lldb::eFormatDefault); bool DumpValueInMemory(ExecutionContext *exe_ctx, Stream *s, lldb::addr_t address, AddressType address_type, bool show_types, bool show_summary, bool verbose); bool ReadFromMemory(ExecutionContext *exe_ctx, lldb::addr_t address, AddressType address_type, DataExtractor &data); bool WriteToMemory(ExecutionContext *exe_ctx, lldb::addr_t address, AddressType address_type, DataExtractor &data); bool GetIsDeclaration() const; void SetIsDeclaration(bool b); bool GetIsExternal() const; void SetIsExternal(bool b); lldb::Format GetFormat(); lldb::Encoding GetEncoding(uint64_t &count); SymbolContextScope *GetSymbolContextScope() { return m_context; } const SymbolContextScope *GetSymbolContextScope() const { return m_context; } void SetSymbolContextScope(SymbolContextScope *context) { m_context = context; } const lldb_private::Declaration &GetDeclaration() const; // Get the clang type, and resolve definitions for any // class/struct/union/enum types completely. CompilerType GetFullCompilerType(); // Get the clang type, and resolve definitions enough so that the type could // have layout performed. This allows ptrs and refs to class/struct/union/enum // types remain forward declarations. CompilerType GetLayoutCompilerType(); // Get the clang type and leave class/struct/union/enum types as forward // declarations if they haven't already been fully defined. CompilerType GetForwardCompilerType(); static int Compare(const Type &a, const Type &b); // From a fully qualified typename, split the type into the type basename // and the remaining type scope (namespaces/classes). - static bool GetTypeScopeAndBasename(const char *&name_cstr, - std::string &scope, std::string &basename, + static bool GetTypeScopeAndBasename(const llvm::StringRef& name, + llvm::StringRef &scope, + llvm::StringRef &basename, lldb::TypeClass &type_class); void SetEncodingType(Type *encoding_type) { m_encoding_type = encoding_type; } uint32_t GetEncodingMask(); bool IsCompleteObjCClass() { return m_flags.is_complete_objc_class; } void SetIsCompleteObjCClass(bool is_complete_objc_class) { m_flags.is_complete_objc_class = is_complete_objc_class; } protected: ConstString m_name; SymbolFile *m_symbol_file; SymbolContextScope *m_context; // The symbol context in which this type is defined Type *m_encoding_type; lldb::user_id_t m_encoding_uid; EncodingDataType m_encoding_uid_type; uint64_t m_byte_size; Declaration m_decl; CompilerType m_compiler_type; struct Flags { #ifdef __GNUC__ // using unsigned type here to work around a very noisy gcc warning unsigned compiler_type_resolve_state : 2; #else ResolveState compiler_type_resolve_state : 2; #endif bool is_complete_objc_class : 1; } m_flags; Type *GetEncodingType(); bool ResolveClangType(ResolveState compiler_type_resolve_state); }; // these classes are used to back the SBType* objects class TypePair { public: TypePair() : compiler_type(), type_sp() {} TypePair(CompilerType type) : compiler_type(type), type_sp() {} TypePair(lldb::TypeSP type) : compiler_type(), type_sp(type) { compiler_type = type_sp->GetForwardCompilerType(); } bool IsValid() const { return compiler_type.IsValid() || (type_sp.get() != nullptr); } explicit operator bool() const { return IsValid(); } bool operator==(const TypePair &rhs) const { return compiler_type == rhs.compiler_type && type_sp.get() == rhs.type_sp.get(); } bool operator!=(const TypePair &rhs) const { return compiler_type != rhs.compiler_type || type_sp.get() != rhs.type_sp.get(); } void Clear() { compiler_type.Clear(); type_sp.reset(); } ConstString GetName() const { if (type_sp) return type_sp->GetName(); if (compiler_type) return compiler_type.GetTypeName(); return ConstString(); } ConstString GetDisplayTypeName() const { if (type_sp) return type_sp->GetForwardCompilerType().GetDisplayTypeName(); if (compiler_type) return compiler_type.GetDisplayTypeName(); return ConstString(); } void SetType(CompilerType type) { type_sp.reset(); compiler_type = type; } void SetType(lldb::TypeSP type) { type_sp = type; if (type_sp) compiler_type = type_sp->GetForwardCompilerType(); else compiler_type.Clear(); } lldb::TypeSP GetTypeSP() const { return type_sp; } CompilerType GetCompilerType() const { return compiler_type; } CompilerType GetPointerType() const { if (type_sp) return type_sp->GetForwardCompilerType().GetPointerType(); return compiler_type.GetPointerType(); } CompilerType GetPointeeType() const { if (type_sp) return type_sp->GetForwardCompilerType().GetPointeeType(); return compiler_type.GetPointeeType(); } CompilerType GetReferenceType() const { if (type_sp) return type_sp->GetForwardCompilerType().GetLValueReferenceType(); else return compiler_type.GetLValueReferenceType(); } CompilerType GetTypedefedType() const { if (type_sp) return type_sp->GetForwardCompilerType().GetTypedefedType(); else return compiler_type.GetTypedefedType(); } CompilerType GetDereferencedType() const { if (type_sp) return type_sp->GetForwardCompilerType().GetNonReferenceType(); else return compiler_type.GetNonReferenceType(); } CompilerType GetUnqualifiedType() const { if (type_sp) return type_sp->GetForwardCompilerType().GetFullyUnqualifiedType(); else return compiler_type.GetFullyUnqualifiedType(); } CompilerType GetCanonicalType() const { if (type_sp) return type_sp->GetForwardCompilerType().GetCanonicalType(); return compiler_type.GetCanonicalType(); } TypeSystem *GetTypeSystem() const { return compiler_type.GetTypeSystem(); } lldb::ModuleSP GetModule() const { if (type_sp) return type_sp->GetModule(); return lldb::ModuleSP(); } protected: CompilerType compiler_type; lldb::TypeSP type_sp; }; // the two classes here are used by the public API as a backend to // the SBType and SBTypeList classes class TypeImpl { public: TypeImpl(); ~TypeImpl() {} TypeImpl(const TypeImpl &rhs); TypeImpl(const lldb::TypeSP &type_sp); TypeImpl(const CompilerType &compiler_type); TypeImpl(const lldb::TypeSP &type_sp, const CompilerType &dynamic); TypeImpl(const CompilerType &compiler_type, const CompilerType &dynamic); TypeImpl(const TypePair &pair, const CompilerType &dynamic); void SetType(const lldb::TypeSP &type_sp); void SetType(const CompilerType &compiler_type); void SetType(const lldb::TypeSP &type_sp, const CompilerType &dynamic); void SetType(const CompilerType &compiler_type, const CompilerType &dynamic); void SetType(const TypePair &pair, const CompilerType &dynamic); TypeImpl &operator=(const TypeImpl &rhs); bool operator==(const TypeImpl &rhs) const; bool operator!=(const TypeImpl &rhs) const; bool IsValid() const; explicit operator bool() const; void Clear(); ConstString GetName() const; ConstString GetDisplayTypeName() const; TypeImpl GetPointerType() const; TypeImpl GetPointeeType() const; TypeImpl GetReferenceType() const; TypeImpl GetTypedefedType() const; TypeImpl GetDereferencedType() const; TypeImpl GetUnqualifiedType() const; TypeImpl GetCanonicalType() const; CompilerType GetCompilerType(bool prefer_dynamic); TypeSystem *GetTypeSystem(bool prefer_dynamic); bool GetDescription(lldb_private::Stream &strm, lldb::DescriptionLevel description_level); private: bool CheckModule(lldb::ModuleSP &module_sp) const; lldb::ModuleWP m_module_wp; TypePair m_static_type; CompilerType m_dynamic_type; }; class TypeListImpl { public: TypeListImpl() : m_content() {} void Append(const lldb::TypeImplSP &type) { m_content.push_back(type); } class AppendVisitor { public: AppendVisitor(TypeListImpl &type_list) : m_type_list(type_list) {} void operator()(const lldb::TypeImplSP &type) { m_type_list.Append(type); } private: TypeListImpl &m_type_list; }; void Append(const lldb_private::TypeList &type_list); lldb::TypeImplSP GetTypeAtIndex(size_t idx) { lldb::TypeImplSP type_sp; if (idx < GetSize()) type_sp = m_content[idx]; return type_sp; } size_t GetSize() { return m_content.size(); } private: std::vector m_content; }; class TypeMemberImpl { public: TypeMemberImpl() : m_type_impl_sp(), m_bit_offset(0), m_name(), m_bitfield_bit_size(0), m_is_bitfield(false) {} TypeMemberImpl(const lldb::TypeImplSP &type_impl_sp, uint64_t bit_offset, const ConstString &name, uint32_t bitfield_bit_size = 0, bool is_bitfield = false) : m_type_impl_sp(type_impl_sp), m_bit_offset(bit_offset), m_name(name), m_bitfield_bit_size(bitfield_bit_size), m_is_bitfield(is_bitfield) {} TypeMemberImpl(const lldb::TypeImplSP &type_impl_sp, uint64_t bit_offset) : m_type_impl_sp(type_impl_sp), m_bit_offset(bit_offset), m_name(), m_bitfield_bit_size(0), m_is_bitfield(false) { if (m_type_impl_sp) m_name = m_type_impl_sp->GetName(); } const lldb::TypeImplSP &GetTypeImpl() { return m_type_impl_sp; } const ConstString &GetName() const { return m_name; } uint64_t GetBitOffset() const { return m_bit_offset; } uint32_t GetBitfieldBitSize() const { return m_bitfield_bit_size; } void SetBitfieldBitSize(uint32_t bitfield_bit_size) { m_bitfield_bit_size = bitfield_bit_size; } bool GetIsBitfield() const { return m_is_bitfield; } void SetIsBitfield(bool is_bitfield) { m_is_bitfield = is_bitfield; } protected: lldb::TypeImplSP m_type_impl_sp; uint64_t m_bit_offset; ConstString m_name; uint32_t m_bitfield_bit_size; // Bit size for bitfield members only bool m_is_bitfield; }; /// /// Sometimes you can find the name of the type corresponding to an object, but /// we don't have debug /// information for it. If that is the case, you can return one of these /// objects, and then if it /// has a full type, you can use that, but if not at least you can print the /// name for informational /// purposes. /// class TypeAndOrName { public: TypeAndOrName(); TypeAndOrName(lldb::TypeSP &type_sp); TypeAndOrName(const CompilerType &compiler_type); TypeAndOrName(const char *type_str); TypeAndOrName(const TypeAndOrName &rhs); TypeAndOrName(ConstString &type_const_string); TypeAndOrName &operator=(const TypeAndOrName &rhs); bool operator==(const TypeAndOrName &other) const; bool operator!=(const TypeAndOrName &other) const; ConstString GetName() const; lldb::TypeSP GetTypeSP() const { return m_type_pair.GetTypeSP(); } CompilerType GetCompilerType() const { return m_type_pair.GetCompilerType(); } void SetName(const ConstString &type_name); void SetName(const char *type_name_cstr); void SetTypeSP(lldb::TypeSP type_sp); void SetCompilerType(CompilerType compiler_type); bool IsEmpty() const; bool HasName() const; bool HasTypeSP() const; bool HasCompilerType() const; bool HasType() const { return HasTypeSP() || HasCompilerType(); } void Clear(); explicit operator bool() { return !IsEmpty(); } private: TypePair m_type_pair; ConstString m_type_name; }; class TypeMemberFunctionImpl { public: TypeMemberFunctionImpl() : m_type(), m_decl(), m_name(), m_kind(lldb::eMemberFunctionKindUnknown) { } TypeMemberFunctionImpl(const CompilerType &type, const CompilerDecl &decl, const std::string &name, const lldb::MemberFunctionKind &kind) : m_type(type), m_decl(decl), m_name(name), m_kind(kind) {} bool IsValid(); ConstString GetName() const; ConstString GetMangledName() const; CompilerType GetType() const; CompilerType GetReturnType() const; size_t GetNumArguments() const; CompilerType GetArgumentAtIndex(size_t idx) const; lldb::MemberFunctionKind GetKind() const; bool GetDescription(Stream &stream); protected: std::string GetPrintableTypeName(); private: CompilerType m_type; CompilerDecl m_decl; ConstString m_name; lldb::MemberFunctionKind m_kind; }; class TypeEnumMemberImpl { public: TypeEnumMemberImpl() : m_integer_type_sp(), m_name(""), m_value(), m_valid(false) {} TypeEnumMemberImpl(const lldb::TypeImplSP &integer_type_sp, const ConstString &name, const llvm::APSInt &value); TypeEnumMemberImpl(const TypeEnumMemberImpl &rhs) : m_integer_type_sp(rhs.m_integer_type_sp), m_name(rhs.m_name), m_value(rhs.m_value), m_valid(rhs.m_valid) {} TypeEnumMemberImpl &operator=(const TypeEnumMemberImpl &rhs); bool IsValid() { return m_valid; } const ConstString &GetName() const { return m_name; } const lldb::TypeImplSP &GetIntegerType() const { return m_integer_type_sp; } uint64_t GetValueAsUnsigned() const { return m_value.getZExtValue(); } int64_t GetValueAsSigned() const { return m_value.getSExtValue(); } protected: lldb::TypeImplSP m_integer_type_sp; ConstString m_name; llvm::APSInt m_value; bool m_valid; }; class TypeEnumMemberListImpl { public: TypeEnumMemberListImpl() : m_content() {} void Append(const lldb::TypeEnumMemberImplSP &type) { m_content.push_back(type); } void Append(const lldb_private::TypeEnumMemberListImpl &type_list); lldb::TypeEnumMemberImplSP GetTypeEnumMemberAtIndex(size_t idx) { lldb::TypeEnumMemberImplSP enum_member; if (idx < GetSize()) enum_member = m_content[idx]; return enum_member; } size_t GetSize() { return m_content.size(); } private: std::vector m_content; }; } // namespace lldb_private #endif // liblldb_Type_h_ Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/register_variables/TestRegisterVariables.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/register_variables/TestRegisterVariables.py (revision 312182) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lang/c/register_variables/TestRegisterVariables.py (revision 312183) @@ -1,204 +1,206 @@ """Check that compiler-generated register values work correctly""" from __future__ import print_function import os import time import re import lldb from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * from lldbsuite.test import lldbutil # This method attempts to figure out if a given variable # is in a register. # # Return: # True if the value has a readable value and is in a register # False otherwise def is_variable_in_register(frame, var_name): # Ensure we can lookup the variable. var = frame.FindVariable(var_name) # print("\nchecking {}...".format(var_name)) if var is None or not var.IsValid(): # print("{} cannot be found".format(var_name)) return False # Check that we can get its value. If not, this # may be a variable that is just out of scope at this point. value = var.GetValue() # print("checking value...") if value is None: # print("value is invalid") return False # else: # print("value is {}".format(value)) # We have a variable and we can get its value. The variable is in # a register if we cannot get an address for it, assuming it is # not a struct pointer. (This is an approximation - compilers can # do other things with spitting up a value into multiple parts of # multiple registers, but what we're verifying here is much more # than it was doing before). var_addr = var.GetAddress() # print("checking address...") if var_addr.IsValid(): # We have an address, it must not be in a register. # print("var {} is not in a register: has a valid address {}".format(var_name, var_addr)) return False else: # We don't have an address but we can read the value. # It is likely stored in a register. # print("var {} is in a register (we don't have an address for it)".format(var_name)) return True def is_struct_pointer_in_register(frame, var_name, trace): # Ensure we can lookup the variable. var = frame.FindVariable(var_name) if trace: print("\nchecking {}...".format(var_name)) if var is None or not var.IsValid(): # print("{} cannot be found".format(var_name)) return False # Check that we can get its value. If not, this # may be a variable that is just out of scope at this point. value = var.GetValue() # print("checking value...") if value is None: if trace: print("value is invalid") return False else: if trace: print("value is {}".format(value)) var_loc = var.GetLocation() if trace: print("checking location: {}".format(var_loc)) if var_loc is None or var_loc.startswith("0x"): # The frame var is not in a register but rather a memory location. # print("frame var {} is not in a register".format(var_name)) return False else: # print("frame var {} is in a register".format(var_name)) return True def re_expr_equals(val_type, val): # Match ({val_type}) ${sum_digits} = {val} return re.compile(r'\(' + val_type + '\) \$\d+ = ' + str(val)) class RegisterVariableTestCase(TestBase): mydir = TestBase.compute_mydir(__file__) @expectedFailureAll(compiler="clang", compiler_version=['<', '3.5']) @expectedFailureAll(compiler="gcc", compiler_version=[ '>=', '4.8.2'], archs=["i386"]) + @expectedFailureAll(compiler="gcc", compiler_version=[ + '<', '4.9'], archs=["x86_64"]) def test_and_run_command(self): """Test expressions on register values.""" # This test now ensures that each probable # register variable location is actually a register, and # if so, whether we can print out the variable there. # It only requires one of them to be handled in a non-error # way. register_variables_count = 0 self.build() exe = os.path.join(os.getcwd(), "a.out") self.runCmd("file " + exe, CURRENT_EXECUTABLE_SET) # Break inside the main. lldbutil.run_break_set_by_source_regexp( self, "break", num_expected_locations=3) #################### # First breakpoint self.runCmd("run", RUN_SUCCEEDED) # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) # The breakpoint should have a hit count of 1. self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, substrs=[' resolved, hit count = 1']) # Try some variables that should be visible frame = self.dbg.GetSelectedTarget().GetProcess( ).GetSelectedThread().GetSelectedFrame() if is_variable_in_register(frame, 'a'): register_variables_count += 1 self.expect("expr a", VARIABLES_DISPLAYED_CORRECTLY, patterns=[re_expr_equals('int', 2)]) if is_struct_pointer_in_register(frame, 'b', self.TraceOn()): register_variables_count += 1 self.expect("expr b->m1", VARIABLES_DISPLAYED_CORRECTLY, patterns=[re_expr_equals('int', 3)]) ##################### # Second breakpoint self.runCmd("continue") # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) # The breakpoint should have a hit count of 1. self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, substrs=[' resolved, hit count = 1']) # Try some variables that should be visible frame = self.dbg.GetSelectedTarget().GetProcess( ).GetSelectedThread().GetSelectedFrame() if is_struct_pointer_in_register(frame, 'b', self.TraceOn()): register_variables_count += 1 self.expect("expr b->m2", VARIABLES_DISPLAYED_CORRECTLY, patterns=[re_expr_equals('int', 5)]) if is_variable_in_register(frame, 'c'): register_variables_count += 1 self.expect("expr c", VARIABLES_DISPLAYED_CORRECTLY, patterns=[re_expr_equals('int', 5)]) ##################### # Third breakpoint self.runCmd("continue") # The stop reason of the thread should be breakpoint. self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT, substrs=['stopped', 'stop reason = breakpoint']) # The breakpoint should have a hit count of 1. self.expect("breakpoint list -f", BREAKPOINT_HIT_ONCE, substrs=[' resolved, hit count = 1']) # Try some variables that should be visible frame = self.dbg.GetSelectedTarget().GetProcess( ).GetSelectedThread().GetSelectedFrame() if is_variable_in_register(frame, 'f'): register_variables_count += 1 self.expect("expr f", VARIABLES_DISPLAYED_CORRECTLY, patterns=[re_expr_equals('float', '3.1')]) # Validate that we verified at least one register variable self.assertTrue( register_variables_count > 0, "expected to verify at least one variable in a register") # print("executed {} expressions with values in registers".format(register_variables_count)) self.runCmd("kill") Index: vendor/lldb/dist/packages/Python/lldbsuite/test/lldbdwarf.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/lldbdwarf.py (nonexistent) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/lldbdwarf.py (revision 312183) @@ -0,0 +1,256 @@ +""" This module implement Dwarf expression opcode parser. """ + +import lldb + +# DWARF Expression operators. +DW_OP_addr = 0x03 +DW_OP_deref = 0x06 +DW_OP_const1u = 0x08 +DW_OP_const1s = 0x09 +DW_OP_const2u = 0x0A +DW_OP_const2s = 0x0B +DW_OP_const4u = 0x0C +DW_OP_const4s = 0x0D +DW_OP_const8u = 0x0E +DW_OP_const8s = 0x0F +DW_OP_constu = 0x10 +DW_OP_consts = 0x11 +DW_OP_dup = 0x12 +DW_OP_drop = 0x13 +DW_OP_over = 0x14 +DW_OP_pick = 0x15 +DW_OP_swap = 0x16 +DW_OP_rot = 0x17 +DW_OP_xderef = 0x18 +DW_OP_abs = 0x19 +DW_OP_and = 0x1A +DW_OP_div = 0x1B +DW_OP_minus = 0x1C +DW_OP_mod = 0x1D +DW_OP_mul = 0x1E +DW_OP_neg = 0x1F +DW_OP_not = 0x20 +DW_OP_or = 0x21 +DW_OP_plus = 0x22 +DW_OP_plus_uconst = 0x23 +DW_OP_shl = 0x24 +DW_OP_shr = 0x25 +DW_OP_shra = 0x26 +DW_OP_xor = 0x27 +DW_OP_skip = 0x2F +DW_OP_bra = 0x28 +DW_OP_eq = 0x29 +DW_OP_ge = 0x2A +DW_OP_gt = 0x2B +DW_OP_le = 0x2C +DW_OP_lt = 0x2D +DW_OP_ne = 0x2E +DW_OP_lit0 = 0x30 +DW_OP_lit1 = 0x31 +DW_OP_lit2 = 0x32 +DW_OP_lit3 = 0x33 +DW_OP_lit4 = 0x34 +DW_OP_lit5 = 0x35 +DW_OP_lit6 = 0x36 +DW_OP_lit7 = 0x37 +DW_OP_lit8 = 0x38 +DW_OP_lit9 = 0x39 +DW_OP_lit10 = 0x3A +DW_OP_lit11 = 0x3B +DW_OP_lit12 = 0x3C +DW_OP_lit13 = 0x3D +DW_OP_lit14 = 0x3E +DW_OP_lit15 = 0x3F +DW_OP_lit16 = 0x40 +DW_OP_lit17 = 0x41 +DW_OP_lit18 = 0x42 +DW_OP_lit19 = 0x43 +DW_OP_lit20 = 0x44 +DW_OP_lit21 = 0x45 +DW_OP_lit22 = 0x46 +DW_OP_lit23 = 0x47 +DW_OP_lit24 = 0x48 +DW_OP_lit25 = 0x49 +DW_OP_lit26 = 0x4A +DW_OP_lit27 = 0x4B +DW_OP_lit28 = 0x4C +DW_OP_lit29 = 0x4D +DW_OP_lit30 = 0x4E +DW_OP_lit31 = 0x4F +DW_OP_reg0 = 0x50 +DW_OP_reg1 = 0x51 +DW_OP_reg2 = 0x52 +DW_OP_reg3 = 0x53 +DW_OP_reg4 = 0x54 +DW_OP_reg5 = 0x55 +DW_OP_reg6 = 0x56 +DW_OP_reg7 = 0x57 +DW_OP_reg8 = 0x58 +DW_OP_reg9 = 0x59 +DW_OP_reg10 = 0x5A +DW_OP_reg11 = 0x5B +DW_OP_reg12 = 0x5C +DW_OP_reg13 = 0x5D +DW_OP_reg14 = 0x5E +DW_OP_reg15 = 0x5F +DW_OP_reg16 = 0x60 +DW_OP_reg17 = 0x61 +DW_OP_reg18 = 0x62 +DW_OP_reg19 = 0x63 +DW_OP_reg20 = 0x64 +DW_OP_reg21 = 0x65 +DW_OP_reg22 = 0x66 +DW_OP_reg23 = 0x67 +DW_OP_reg24 = 0x68 +DW_OP_reg25 = 0x69 +DW_OP_reg26 = 0x6A +DW_OP_reg27 = 0x6B +DW_OP_reg28 = 0x6C +DW_OP_reg29 = 0x6D +DW_OP_reg30 = 0x6E +DW_OP_reg31 = 0x6F +DW_OP_breg0 = 0x70 +DW_OP_breg1 = 0x71 +DW_OP_breg2 = 0x72 +DW_OP_breg3 = 0x73 +DW_OP_breg4 = 0x74 +DW_OP_breg5 = 0x75 +DW_OP_breg6 = 0x76 +DW_OP_breg7 = 0x77 +DW_OP_breg8 = 0x78 +DW_OP_breg9 = 0x79 +DW_OP_breg10 = 0x7A +DW_OP_breg11 = 0x7B +DW_OP_breg12 = 0x7C +DW_OP_breg13 = 0x7D +DW_OP_breg14 = 0x7E +DW_OP_breg15 = 0x7F +DW_OP_breg16 = 0x80 +DW_OP_breg17 = 0x81 +DW_OP_breg18 = 0x82 +DW_OP_breg19 = 0x83 +DW_OP_breg20 = 0x84 +DW_OP_breg21 = 0x85 +DW_OP_breg22 = 0x86 +DW_OP_breg23 = 0x87 +DW_OP_breg24 = 0x88 +DW_OP_breg25 = 0x89 +DW_OP_breg26 = 0x8A +DW_OP_breg27 = 0x8B +DW_OP_breg28 = 0x8C +DW_OP_breg29 = 0x8D +DW_OP_breg30 = 0x8E +DW_OP_breg31 = 0x8F +DW_OP_regx = 0x90 +DW_OP_fbreg = 0x91 +DW_OP_bregx = 0x92 +DW_OP_piece = 0x93 +DW_OP_deref_size = 0x94 +DW_OP_xderef_size = 0x95 +DW_OP_nop = 0x96 +DW_OP_push_object_address = 0x97 +DW_OP_call2 = 0x98 +DW_OP_call4 = 0x99 +DW_OP_call_ref = 0x9A +DW_OP_form_tls_address = 0x9B +DW_OP_call_frame_cfa = 0x9C +DW_OP_bit_piece = 0x9D +DW_OP_implicit_value = 0x9E +DW_OP_stack_value = 0x9F +DW_OP_lo_user = 0xE0 +DW_OP_GNU_push_tls_address = 0xE0 +DW_OP_APPLE_uninit = 0xF0 +DW_OP_hi_user = 0xFF + + +class DwarfOpcodeParser(object): + + def updateRegInfoBitsize(self, reg_info, byte_order): + """ Update the regInfo bit size. """ + + # Evaluate Dwarf Expression + expr_result = self.evaluateDwarfExpression(reg_info["dynamic_size_dwarf_expr_bytes"], + byte_order) + + if expr_result == 0: + reg_info["bitsize"] = 32 + elif expr_result == 1: + reg_info["bitsize"] = 64 + + + def evaluateDwarfExpression(self, dwarf_opcode, byte_order): + """Evaluate Dwarf Expression. """ + + dwarf_opcode = [dwarf_opcode[i:i+2] for i in range(0,len(dwarf_opcode),2)] + dwarf_data = [] + for index in range(len(dwarf_opcode)): + + if index < len(dwarf_opcode): + val = int(dwarf_opcode[index], 16) + else: + break + + if val == DW_OP_regx: + # Read register number + self.assertTrue(len(dwarf_opcode) > (index + 1)) + reg_no = int(dwarf_opcode.pop(index + 1), 16) + + self.reset_test_sequence() + # Read register value + self.test_sequence.add_log_lines( + ["read packet: $p{0:x}#00".format(reg_no), + {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", + "capture": {1: "p_response"}}],True) + + Context = self.expect_gdbremote_sequence() + self.assertIsNotNone(Context) + p_response = Context.get("p_response") + self.assertIsNotNone(p_response) + + if byte_order == lldb.eByteOrderLittle: + # In case of little endian + # first decode the HEX ASCII bytes and then reverse it + # to get actual value of SR register + p_response = "".join(reversed([p_response[i:i+2] for i in range(0, + len(p_response),2)])) + # Push register value + dwarf_data.append(int(p_response,16)) + + elif val == DW_OP_lit1: + # Push literal 1 + dwarf_data.append(1) + + elif val == DW_OP_lit26: + # Push literal 26 + dwarf_data.append(26) + + elif val == DW_OP_shl: + # left shift and push the result back + self.assertTrue(len(dwarf_data) > 1) + shift_amount = dwarf_data.pop() + val_to_shift = dwarf_data.pop() + result = val_to_shift << shift_amount + dwarf_data.append(result) + + elif val == DW_OP_shr: + # Right shift and push the result back + self.assertTrue(len(dwarf_data) > 1) + shift_amount = dwarf_data.pop() + val_to_shift = dwarf_data.pop() + result = val_to_shift >> shift_amount + dwarf_data.append(result) + + elif val == DW_OP_and: + # And of topmost 2 elements and push the result back + first_ele = dwarf_data.pop() + second_ele = dwarf_data.pop() + result = first_ele & second_ele + dwarf_data.append(result) + + else: + self.assertTrue(False and "Unprocess Dwarf Opcode") + + self.assertTrue(len(dwarf_data) == 1) + expr_result = dwarf_data.pop() + return expr_result + Property changes on: vendor/lldb/dist/packages/Python/lldbsuite/test/lldbdwarf.py ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py =================================================================== --- vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py (revision 312182) +++ vendor/lldb/dist/packages/Python/lldbsuite/test/tools/lldb-server/TestLldbGdbServer.py (revision 312183) @@ -1,1490 +1,1498 @@ """ Test case for testing the gdbremote protocol. Tests run against debugserver and lldb-server (llgs). lldb-server tests run where the lldb-server exe is available. This class will be broken into smaller test case classes by gdb remote packet functional areas. For now it contains the initial set of tests implemented. """ from __future__ import print_function import unittest2 import gdbremote_testcase import lldbgdbserverutils import platform import signal from lldbsuite.test.decorators import * from lldbsuite.test.lldbtest import * +from lldbsuite.test.lldbdwarf import * from lldbsuite.test import lldbutil -class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase): +class LldbGdbServerTestCase(gdbremote_testcase.GdbRemoteTestCaseBase, DwarfOpcodeParser): mydir = TestBase.compute_mydir(__file__) @debugserver_test def test_exe_starts_debugserver(self): self.init_debugserver_test() server = self.connect_to_debug_monitor() @llgs_test def test_exe_starts_llgs(self): self.init_llgs_test() server = self.connect_to_debug_monitor() def start_no_ack_mode(self): server = self.connect_to_debug_monitor() self.assertIsNotNone(server) self.add_no_ack_remote_stream() self.expect_gdbremote_sequence() @debugserver_test def test_start_no_ack_mode_debugserver(self): self.init_debugserver_test() self.start_no_ack_mode() @llgs_test def test_start_no_ack_mode_llgs(self): self.init_llgs_test() self.start_no_ack_mode() def thread_suffix_supported(self): server = self.connect_to_debug_monitor() self.assertIsNotNone(server) self.add_no_ack_remote_stream() self.test_sequence.add_log_lines( ["lldb-server < 26> read packet: $QThreadSuffixSupported#e4", "lldb-server < 6> send packet: $OK#9a"], True) self.expect_gdbremote_sequence() @debugserver_test def test_thread_suffix_supported_debugserver(self): self.init_debugserver_test() self.thread_suffix_supported() @llgs_test def test_thread_suffix_supported_llgs(self): self.init_llgs_test() self.thread_suffix_supported() def list_threads_in_stop_reply_supported(self): server = self.connect_to_debug_monitor() self.assertIsNotNone(server) self.add_no_ack_remote_stream() self.test_sequence.add_log_lines( ["lldb-server < 27> read packet: $QListThreadsInStopReply#21", "lldb-server < 6> send packet: $OK#9a"], True) self.expect_gdbremote_sequence() @debugserver_test def test_list_threads_in_stop_reply_supported_debugserver(self): self.init_debugserver_test() self.list_threads_in_stop_reply_supported() @llgs_test def test_list_threads_in_stop_reply_supported_llgs(self): self.init_llgs_test() self.list_threads_in_stop_reply_supported() def c_packet_works(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.test_sequence.add_log_lines( ["read packet: $c#63", "send packet: $W00#00"], True) self.expect_gdbremote_sequence() @debugserver_test def test_c_packet_works_debugserver(self): self.init_debugserver_test() self.build() self.c_packet_works() @llgs_test def test_c_packet_works_llgs(self): self.init_llgs_test() self.build() self.c_packet_works() def inferior_print_exit(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # build launch args launch_args += ["hello, world"] self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.test_sequence.add_log_lines( ["read packet: $vCont;c#a8", {"type": "output_match", "regex": self.maybe_strict_output_regex(r"hello, world\r\n")}, "send packet: $W00#00"], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) @debugserver_test def test_inferior_print_exit_debugserver(self): self.init_debugserver_test() self.build() self.inferior_print_exit() @llgs_test @expectedFlakeyLinux("llvm.org/pr25652") def test_inferior_print_exit_llgs(self): self.init_llgs_test() self.build() self.inferior_print_exit() def first_launch_stop_reply_thread_matches_first_qC(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # build launch args launch_args += ["hello, world"] self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.test_sequence.add_log_lines(["read packet: $qC#00", {"direction": "send", "regex": r"^\$QC([0-9a-fA-F]+)#", "capture": {1: "thread_id"}}, "read packet: $?#00", {"direction": "send", "regex": r"^\$T[0-9a-fA-F]{2}thread:([0-9a-fA-F]+)", "expect_captures": {1: "thread_id"}}], True) self.expect_gdbremote_sequence() @debugserver_test def test_first_launch_stop_reply_thread_matches_first_qC_debugserver(self): self.init_debugserver_test() self.build() self.first_launch_stop_reply_thread_matches_first_qC() @llgs_test def test_first_launch_stop_reply_thread_matches_first_qC_llgs(self): self.init_llgs_test() self.build() self.first_launch_stop_reply_thread_matches_first_qC() def attach_commandline_continue_app_exits(self): procs = self.prep_debug_monitor_and_inferior() self.test_sequence.add_log_lines( ["read packet: $vCont;c#a8", "send packet: $W00#00"], True) self.expect_gdbremote_sequence() # Wait a moment for completed and now-detached inferior process to # clear. time.sleep(1) if not lldb.remote_platform: # Process should be dead now. Reap results. poll_result = procs["inferior"].poll() self.assertIsNotNone(poll_result) # Where possible, verify at the system level that the process is not # running. self.assertFalse( lldbgdbserverutils.process_is_running( procs["inferior"].pid, False)) @debugserver_test def test_attach_commandline_continue_app_exits_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_attach() self.attach_commandline_continue_app_exits() @llgs_test def test_attach_commandline_continue_app_exits_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_attach() self.attach_commandline_continue_app_exits() def qRegisterInfo_returns_one_valid_result(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # Build the expected protocol stream self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.test_sequence.add_log_lines( ["read packet: $qRegisterInfo0#00", {"direction": "send", "regex": r"^\$(.+);#[0-9A-Fa-f]{2}", "capture": {1: "reginfo_0"}}], True) # Run the stream context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) reg_info_packet = context.get("reginfo_0") self.assertIsNotNone(reg_info_packet) self.assert_valid_reg_info( lldbgdbserverutils.parse_reg_info_response(reg_info_packet)) @debugserver_test @expectedFailureDarwin("llvm.org/pr25486") def test_qRegisterInfo_returns_one_valid_result_debugserver(self): self.init_debugserver_test() self.build() self.qRegisterInfo_returns_one_valid_result() @llgs_test def test_qRegisterInfo_returns_one_valid_result_llgs(self): self.init_llgs_test() self.build() self.qRegisterInfo_returns_one_valid_result() def qRegisterInfo_returns_all_valid_results(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # Build the expected protocol stream. self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.add_register_info_collection_packets() # Run the stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Validate that each register info returned validates. for reg_info in self.parse_register_info_packets(context): self.assert_valid_reg_info(reg_info) @debugserver_test @expectedFailureDarwin("llvm.org/pr25486") def test_qRegisterInfo_returns_all_valid_results_debugserver(self): self.init_debugserver_test() self.build() self.qRegisterInfo_returns_all_valid_results() @llgs_test def test_qRegisterInfo_returns_all_valid_results_llgs(self): self.init_llgs_test() self.build() self.qRegisterInfo_returns_all_valid_results() def qRegisterInfo_contains_required_generics(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # Build the expected protocol stream self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.add_register_info_collection_packets() # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather register info entries. reg_infos = self.parse_register_info_packets(context) # Collect all generic registers found. generic_regs = { reg_info['generic']: 1 for reg_info in reg_infos if 'generic' in reg_info} # Ensure we have a program counter register. self.assertTrue('pc' in generic_regs) # Ensure we have a frame pointer register. self.assertTrue('fp' in generic_regs) # Ensure we have a stack pointer register. self.assertTrue('sp' in generic_regs) # Ensure we have a flags register. self.assertTrue('flags' in generic_regs) @debugserver_test def test_qRegisterInfo_contains_required_generics_debugserver(self): self.init_debugserver_test() self.build() self.qRegisterInfo_contains_required_generics() @llgs_test def test_qRegisterInfo_contains_required_generics_llgs(self): self.init_llgs_test() self.build() self.qRegisterInfo_contains_required_generics() def qRegisterInfo_contains_at_least_one_register_set(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # Build the expected protocol stream self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.add_register_info_collection_packets() # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather register info entries. reg_infos = self.parse_register_info_packets(context) # Collect all register sets found. register_sets = { reg_info['set']: 1 for reg_info in reg_infos if 'set' in reg_info} self.assertTrue(len(register_sets) >= 1) @debugserver_test def test_qRegisterInfo_contains_at_least_one_register_set_debugserver( self): self.init_debugserver_test() self.build() self.qRegisterInfo_contains_at_least_one_register_set() @llgs_test def test_qRegisterInfo_contains_at_least_one_register_set_llgs(self): self.init_llgs_test() self.build() self.qRegisterInfo_contains_at_least_one_register_set() def targetHasAVX(self): triple = self.dbg.GetSelectedPlatform().GetTriple() # TODO other platforms, please implement this function if not re.match(".*-.*-linux", triple): return True # Need to do something different for non-Linux/Android targets if lldb.remote_platform: self.runCmd('platform get-file "/proc/cpuinfo" "cpuinfo"') cpuinfo_path = "cpuinfo" self.addTearDownHook(lambda: os.unlink("cpuinfo")) else: cpuinfo_path = "/proc/cpuinfo" f = open(cpuinfo_path, 'r') cpuinfo = f.read() f.close() return " avx " in cpuinfo def qRegisterInfo_contains_avx_registers(self): launch_args = self.install_and_create_launch_args() server = self.connect_to_debug_monitor() self.assertIsNotNone(server) # Build the expected protocol stream self.add_no_ack_remote_stream() self.add_verified_launch_packets(launch_args) self.add_register_info_collection_packets() # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather register info entries. reg_infos = self.parse_register_info_packets(context) # Collect all generics found. register_sets = { reg_info['set']: 1 for reg_info in reg_infos if 'set' in reg_info} self.assertEqual( self.targetHasAVX(), "Advanced Vector Extensions" in register_sets) @llgs_test def test_qRegisterInfo_contains_avx_registers_llgs(self): self.init_llgs_test() self.build() self.qRegisterInfo_contains_avx_registers() def qThreadInfo_contains_thread(self): procs = self.prep_debug_monitor_and_inferior() self.add_threadinfo_collection_packets() # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather threadinfo entries. threads = self.parse_threadinfo_packets(context) self.assertIsNotNone(threads) # We should have exactly one thread. self.assertEqual(len(threads), 1) @debugserver_test def test_qThreadInfo_contains_thread_launch_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qThreadInfo_contains_thread() @llgs_test def test_qThreadInfo_contains_thread_launch_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qThreadInfo_contains_thread() @debugserver_test def test_qThreadInfo_contains_thread_attach_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_attach() self.qThreadInfo_contains_thread() @llgs_test def test_qThreadInfo_contains_thread_attach_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_attach() self.qThreadInfo_contains_thread() def qThreadInfo_matches_qC(self): procs = self.prep_debug_monitor_and_inferior() self.add_threadinfo_collection_packets() self.test_sequence.add_log_lines( ["read packet: $qC#00", {"direction": "send", "regex": r"^\$QC([0-9a-fA-F]+)#", "capture": {1: "thread_id"}} ], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather threadinfo entries. threads = self.parse_threadinfo_packets(context) self.assertIsNotNone(threads) # We should have exactly one thread from threadinfo. self.assertEqual(len(threads), 1) # We should have a valid thread_id from $QC. QC_thread_id_hex = context.get("thread_id") self.assertIsNotNone(QC_thread_id_hex) QC_thread_id = int(QC_thread_id_hex, 16) # Those two should be the same. self.assertEqual(threads[0], QC_thread_id) @debugserver_test def test_qThreadInfo_matches_qC_launch_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qThreadInfo_matches_qC() @llgs_test def test_qThreadInfo_matches_qC_launch_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qThreadInfo_matches_qC() @debugserver_test def test_qThreadInfo_matches_qC_attach_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_attach() self.qThreadInfo_matches_qC() @llgs_test def test_qThreadInfo_matches_qC_attach_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_attach() self.qThreadInfo_matches_qC() def p_returns_correct_data_size_for_each_qRegisterInfo(self): procs = self.prep_debug_monitor_and_inferior() self.add_register_info_collection_packets() # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather register info entries. reg_infos = self.parse_register_info_packets(context) self.assertIsNotNone(reg_infos) self.assertTrue(len(reg_infos) > 0) + inferior_exe_path = os.path.abspath("a.out") + Target = self.dbg.CreateTarget(inferior_exe_path) + byte_order = Target.GetByteOrder() + # Read value for each register. reg_index = 0 for reg_info in reg_infos: # Skip registers that don't have a register set. For x86, these are # the DRx registers, which have no LLDB-kind register number and thus # cannot be read via normal # NativeRegisterContext::ReadRegister(reg_info,...) calls. if not "set" in reg_info: continue # Clear existing packet expectations. self.reset_test_sequence() # Run the register query self.test_sequence.add_log_lines( ["read packet: $p{0:x}#00".format(reg_index), {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", "capture": {1: "p_response"}}], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Verify the response length. p_response = context.get("p_response") self.assertIsNotNone(p_response) + + if "dynamic_size_dwarf_expr_bytes" in reg_info: + self.updateRegInfoBitsize(reg_info, byte_order) self.assertEqual(len(p_response), 2 * int(reg_info["bitsize"]) / 8) # Increment loop reg_index += 1 @debugserver_test def test_p_returns_correct_data_size_for_each_qRegisterInfo_launch_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.p_returns_correct_data_size_for_each_qRegisterInfo() @llgs_test def test_p_returns_correct_data_size_for_each_qRegisterInfo_launch_llgs( self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.p_returns_correct_data_size_for_each_qRegisterInfo() @debugserver_test def test_p_returns_correct_data_size_for_each_qRegisterInfo_attach_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_attach() self.p_returns_correct_data_size_for_each_qRegisterInfo() @llgs_test def test_p_returns_correct_data_size_for_each_qRegisterInfo_attach_llgs( self): self.init_llgs_test() self.build() self.set_inferior_startup_attach() self.p_returns_correct_data_size_for_each_qRegisterInfo() def Hg_switches_to_3_threads(self): # Startup the inferior with three threads (main + 2 new ones). procs = self.prep_debug_monitor_and_inferior( inferior_args=["thread:new", "thread:new"]) # Let the inferior process have a few moments to start up the thread # when launched. (The launch scenario has no time to run, so threads # won't be there yet.) self.run_process_then_stop(run_seconds=1) # Wait at most x seconds for 3 threads to be present. threads = self.wait_for_thread_count(3, timeout_seconds=5) self.assertEqual(len(threads), 3) # verify we can $H to each thead, and $qC matches the thread we set. for thread in threads: # Change to each thread, verify current thread id. self.reset_test_sequence() self.test_sequence.add_log_lines( ["read packet: $Hg{0:x}#00".format(thread), # Set current thread. "send packet: $OK#00", "read packet: $qC#00", {"direction": "send", "regex": r"^\$QC([0-9a-fA-F]+)#", "capture": {1: "thread_id"}}], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Verify the thread id. self.assertIsNotNone(context.get("thread_id")) self.assertEqual(int(context.get("thread_id"), 16), thread) @debugserver_test def test_Hg_switches_to_3_threads_launch_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.Hg_switches_to_3_threads() @llgs_test def test_Hg_switches_to_3_threads_launch_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.Hg_switches_to_3_threads() @debugserver_test def test_Hg_switches_to_3_threads_attach_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_attach() self.Hg_switches_to_3_threads() @llgs_test def test_Hg_switches_to_3_threads_attach_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_attach() self.Hg_switches_to_3_threads() def Hc_then_Csignal_signals_correct_thread(self, segfault_signo): # NOTE only run this one in inferior-launched mode: we can't grab inferior stdout when running attached, # and the test requires getting stdout from the exe. NUM_THREADS = 3 # Startup the inferior with three threads (main + NUM_THREADS-1 worker threads). # inferior_args=["thread:print-ids"] inferior_args = ["thread:segfault"] for i in range(NUM_THREADS - 1): # if i > 0: # Give time between thread creation/segfaulting for the handler to work. # inferior_args.append("sleep:1") inferior_args.append("thread:new") inferior_args.append("sleep:10") # Launch/attach. (In our case, this should only ever be launched since # we need inferior stdout/stderr). procs = self.prep_debug_monitor_and_inferior( inferior_args=inferior_args) self.test_sequence.add_log_lines(["read packet: $c#63"], True) context = self.expect_gdbremote_sequence() # Let the inferior process have a few moments to start up the thread when launched. # context = self.run_process_then_stop(run_seconds=1) # Wait at most x seconds for all threads to be present. # threads = self.wait_for_thread_count(NUM_THREADS, timeout_seconds=5) # self.assertEquals(len(threads), NUM_THREADS) signaled_tids = {} print_thread_ids = {} # Switch to each thread, deliver a signal, and verify signal delivery for i in range(NUM_THREADS - 1): # Run until SIGSEGV comes in. self.reset_test_sequence() self.test_sequence.add_log_lines([{"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "signo", 2: "thread_id"}}], True) context = self.expect_gdbremote_sequence(timeout_seconds=10) self.assertIsNotNone(context) signo = context.get("signo") self.assertEqual(int(signo, 16), segfault_signo) # Ensure we haven't seen this tid yet. thread_id = int(context.get("thread_id"), 16) self.assertFalse(thread_id in signaled_tids) signaled_tids[thread_id] = 1 # Send SIGUSR1 to the thread that signaled the SIGSEGV. self.reset_test_sequence() self.test_sequence.add_log_lines( [ # Set the continue thread. # Set current thread. "read packet: $Hc{0:x}#00".format(thread_id), "send packet: $OK#00", # Continue sending the signal number to the continue thread. # The commented out packet is a way to do this same operation without using # a $Hc (but this test is testing $Hc, so we'll stick with the former). "read packet: $C{0:x}#00".format(lldbutil.get_signal_number('SIGUSR1')), # "read packet: $vCont;C{0:x}:{1:x};c#00".format(lldbutil.get_signal_number('SIGUSR1'), thread_id), # FIXME: Linux does not report the thread stop on the delivered signal (SIGUSR1 here). MacOSX debugserver does. # But MacOSX debugserver isn't guaranteeing the thread the signal handler runs on, so currently its an XFAIL. # Need to rectify behavior here. The linux behavior is more intuitive to me since we're essentially swapping out # an about-to-be-delivered signal (for which we already sent a stop packet) to a different signal. # {"direction":"send", "regex":r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture":{1:"stop_signo", 2:"stop_thread_id"} }, # "read packet: $c#63", {"type": "output_match", "regex": r"^received SIGUSR1 on thread id: ([0-9a-fA-F]+)\r\nthread ([0-9a-fA-F]+): past SIGSEGV\r\n", "capture": {1: "print_thread_id", 2: "post_handle_thread_id"}}, ], True) # Run the sequence. context = self.expect_gdbremote_sequence(timeout_seconds=10) self.assertIsNotNone(context) # Ensure the stop signal is the signal we delivered. # stop_signo = context.get("stop_signo") # self.assertIsNotNone(stop_signo) # self.assertEquals(int(stop_signo,16), lldbutil.get_signal_number('SIGUSR1')) # Ensure the stop thread is the thread to which we delivered the signal. # stop_thread_id = context.get("stop_thread_id") # self.assertIsNotNone(stop_thread_id) # self.assertEquals(int(stop_thread_id,16), thread_id) # Ensure we haven't seen this thread id yet. The inferior's # self-obtained thread ids are not guaranteed to match the stub # tids (at least on MacOSX). print_thread_id = context.get("print_thread_id") self.assertIsNotNone(print_thread_id) print_thread_id = int(print_thread_id, 16) self.assertFalse(print_thread_id in print_thread_ids) # Now remember this print (i.e. inferior-reflected) thread id and # ensure we don't hit it again. print_thread_ids[print_thread_id] = 1 # Ensure post signal-handle thread id matches the thread that # initially raised the SIGSEGV. post_handle_thread_id = context.get("post_handle_thread_id") self.assertIsNotNone(post_handle_thread_id) post_handle_thread_id = int(post_handle_thread_id, 16) self.assertEqual(post_handle_thread_id, print_thread_id) @unittest2.expectedFailure() @debugserver_test def test_Hc_then_Csignal_signals_correct_thread_launch_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() # Darwin debugserver translates some signals like SIGSEGV into some gdb # expectations about fixed signal numbers. self.Hc_then_Csignal_signals_correct_thread(self.TARGET_EXC_BAD_ACCESS) @llgs_test def test_Hc_then_Csignal_signals_correct_thread_launch_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.Hc_then_Csignal_signals_correct_thread( lldbutil.get_signal_number('SIGSEGV')) def m_packet_reads_memory(self): # This is the memory we will write into the inferior and then ensure we # can read back with $m. MEMORY_CONTENTS = "Test contents 0123456789 ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz" # Start up the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=[ "set-message:%s" % MEMORY_CONTENTS, "get-data-address-hex:g_message", "sleep:5"]) # Run the process self.test_sequence.add_log_lines( [ # Start running after initial stop. "read packet: $c#63", # Match output line that prints the memory address of the message buffer within the inferior. # Note we require launch-only testing so we can get inferior otuput. {"type": "output_match", "regex": self.maybe_strict_output_regex(r"data address: 0x([0-9a-fA-F]+)\r\n"), "capture": {1: "message_address"}}, # Now stop the inferior. "read packet: {}".format(chr(3)), # And wait for the stop notification. {"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "stop_signo", 2: "stop_thread_id"}}], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Grab the message address. self.assertIsNotNone(context.get("message_address")) message_address = int(context.get("message_address"), 16) # Grab contents from the inferior. self.reset_test_sequence() self.test_sequence.add_log_lines( ["read packet: $m{0:x},{1:x}#00".format(message_address, len(MEMORY_CONTENTS)), {"direction": "send", "regex": r"^\$(.+)#[0-9a-fA-F]{2}$", "capture": {1: "read_contents"}}], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Ensure what we read from inferior memory is what we wrote. self.assertIsNotNone(context.get("read_contents")) read_contents = context.get("read_contents").decode("hex") self.assertEqual(read_contents, MEMORY_CONTENTS) @debugserver_test def test_m_packet_reads_memory_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.m_packet_reads_memory() @llgs_test def test_m_packet_reads_memory_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.m_packet_reads_memory() def qMemoryRegionInfo_is_supported(self): # Start up the inferior. procs = self.prep_debug_monitor_and_inferior() # Ask if it supports $qMemoryRegionInfo. self.test_sequence.add_log_lines( ["read packet: $qMemoryRegionInfo#00", "send packet: $OK#00" ], True) self.expect_gdbremote_sequence() @debugserver_test def test_qMemoryRegionInfo_is_supported_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_is_supported() @llgs_test def test_qMemoryRegionInfo_is_supported_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_is_supported() def qMemoryRegionInfo_reports_code_address_as_executable(self): # Start up the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=["get-code-address-hex:hello", "sleep:5"]) # Run the process self.test_sequence.add_log_lines( [ # Start running after initial stop. "read packet: $c#63", # Match output line that prints the memory address of the message buffer within the inferior. # Note we require launch-only testing so we can get inferior otuput. {"type": "output_match", "regex": self.maybe_strict_output_regex(r"code address: 0x([0-9a-fA-F]+)\r\n"), "capture": {1: "code_address"}}, # Now stop the inferior. "read packet: {}".format(chr(3)), # And wait for the stop notification. {"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "stop_signo", 2: "stop_thread_id"}}], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Grab the code address. self.assertIsNotNone(context.get("code_address")) code_address = int(context.get("code_address"), 16) # Grab memory region info from the inferior. self.reset_test_sequence() self.add_query_memory_region_packets(code_address) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) mem_region_dict = self.parse_memory_region_packet(context) # Ensure there are no errors reported. self.assertFalse("error" in mem_region_dict) # Ensure code address is readable and executable. self.assertTrue("permissions" in mem_region_dict) self.assertTrue("r" in mem_region_dict["permissions"]) self.assertTrue("x" in mem_region_dict["permissions"]) # Ensure the start address and size encompass the address we queried. self.assert_address_within_memory_region(code_address, mem_region_dict) @debugserver_test def test_qMemoryRegionInfo_reports_code_address_as_executable_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_code_address_as_executable() @llgs_test def test_qMemoryRegionInfo_reports_code_address_as_executable_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_code_address_as_executable() def qMemoryRegionInfo_reports_stack_address_as_readable_writeable(self): # Start up the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=["get-stack-address-hex:", "sleep:5"]) # Run the process self.test_sequence.add_log_lines( [ # Start running after initial stop. "read packet: $c#63", # Match output line that prints the memory address of the message buffer within the inferior. # Note we require launch-only testing so we can get inferior otuput. {"type": "output_match", "regex": self.maybe_strict_output_regex(r"stack address: 0x([0-9a-fA-F]+)\r\n"), "capture": {1: "stack_address"}}, # Now stop the inferior. "read packet: {}".format(chr(3)), # And wait for the stop notification. {"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "stop_signo", 2: "stop_thread_id"}}], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Grab the address. self.assertIsNotNone(context.get("stack_address")) stack_address = int(context.get("stack_address"), 16) # Grab memory region info from the inferior. self.reset_test_sequence() self.add_query_memory_region_packets(stack_address) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) mem_region_dict = self.parse_memory_region_packet(context) # Ensure there are no errors reported. self.assertFalse("error" in mem_region_dict) # Ensure address is readable and executable. self.assertTrue("permissions" in mem_region_dict) self.assertTrue("r" in mem_region_dict["permissions"]) self.assertTrue("w" in mem_region_dict["permissions"]) # Ensure the start address and size encompass the address we queried. self.assert_address_within_memory_region( stack_address, mem_region_dict) @debugserver_test def test_qMemoryRegionInfo_reports_stack_address_as_readable_writeable_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_stack_address_as_readable_writeable() @llgs_test def test_qMemoryRegionInfo_reports_stack_address_as_readable_writeable_llgs( self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_stack_address_as_readable_writeable() def qMemoryRegionInfo_reports_heap_address_as_readable_writeable(self): # Start up the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=["get-heap-address-hex:", "sleep:5"]) # Run the process self.test_sequence.add_log_lines( [ # Start running after initial stop. "read packet: $c#63", # Match output line that prints the memory address of the message buffer within the inferior. # Note we require launch-only testing so we can get inferior otuput. {"type": "output_match", "regex": self.maybe_strict_output_regex(r"heap address: 0x([0-9a-fA-F]+)\r\n"), "capture": {1: "heap_address"}}, # Now stop the inferior. "read packet: {}".format(chr(3)), # And wait for the stop notification. {"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "stop_signo", 2: "stop_thread_id"}}], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Grab the address. self.assertIsNotNone(context.get("heap_address")) heap_address = int(context.get("heap_address"), 16) # Grab memory region info from the inferior. self.reset_test_sequence() self.add_query_memory_region_packets(heap_address) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) mem_region_dict = self.parse_memory_region_packet(context) # Ensure there are no errors reported. self.assertFalse("error" in mem_region_dict) # Ensure address is readable and executable. self.assertTrue("permissions" in mem_region_dict) self.assertTrue("r" in mem_region_dict["permissions"]) self.assertTrue("w" in mem_region_dict["permissions"]) # Ensure the start address and size encompass the address we queried. self.assert_address_within_memory_region(heap_address, mem_region_dict) @debugserver_test def test_qMemoryRegionInfo_reports_heap_address_as_readable_writeable_debugserver( self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_heap_address_as_readable_writeable() @llgs_test def test_qMemoryRegionInfo_reports_heap_address_as_readable_writeable_llgs( self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qMemoryRegionInfo_reports_heap_address_as_readable_writeable() def software_breakpoint_set_and_remove_work(self): # Start up the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=[ "get-code-address-hex:hello", "sleep:1", "call-function:hello"]) # Run the process self.add_register_info_collection_packets() self.add_process_info_collection_packets() self.test_sequence.add_log_lines( [ # Start running after initial stop. "read packet: $c#63", # Match output line that prints the memory address of the function call entry point. # Note we require launch-only testing so we can get inferior otuput. {"type": "output_match", "regex": self.maybe_strict_output_regex(r"code address: 0x([0-9a-fA-F]+)\r\n"), "capture": {1: "function_address"}}, # Now stop the inferior. "read packet: {}".format(chr(3)), # And wait for the stop notification. {"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "stop_signo", 2: "stop_thread_id"}}], True) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Gather process info - we need endian of target to handle register # value conversions. process_info = self.parse_process_info_response(context) endian = process_info.get("endian") self.assertIsNotNone(endian) # Gather register info entries. reg_infos = self.parse_register_info_packets(context) (pc_lldb_reg_index, pc_reg_info) = self.find_pc_reg_info(reg_infos) self.assertIsNotNone(pc_lldb_reg_index) self.assertIsNotNone(pc_reg_info) # Grab the function address. self.assertIsNotNone(context.get("function_address")) function_address = int(context.get("function_address"), 16) # Set the breakpoint. if self.getArchitecture() == "arm": # TODO: Handle case when setting breakpoint in thumb code BREAKPOINT_KIND = 4 else: BREAKPOINT_KIND = 1 self.reset_test_sequence() self.add_set_breakpoint_packets( function_address, do_continue=True, breakpoint_kind=BREAKPOINT_KIND) # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Verify the stop signal reported was the breakpoint signal number. stop_signo = context.get("stop_signo") self.assertIsNotNone(stop_signo) self.assertEqual(int(stop_signo, 16), lldbutil.get_signal_number('SIGTRAP')) # Ensure we did not receive any output. If the breakpoint was not set, we would # see output (from a launched process with captured stdio) printing a hello, world message. # That would indicate the breakpoint didn't take. self.assertEqual(len(context["O_content"]), 0) # Verify that the PC for the main thread is where we expect it - right at the breakpoint address. # This acts as a another validation on the register reading code. self.reset_test_sequence() self.test_sequence.add_log_lines( [ # Print the PC. This should match the breakpoint address. "read packet: $p{0:x}#00".format(pc_lldb_reg_index), # Capture $p results. {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", "capture": {1: "p_response"}}, ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Verify the PC is where we expect. Note response is in endianness of # the inferior. p_response = context.get("p_response") self.assertIsNotNone(p_response) # Convert from target endian to int. returned_pc = lldbgdbserverutils.unpack_register_hex_unsigned( endian, p_response) self.assertEqual(returned_pc, function_address) # Verify that a breakpoint remove and continue gets us the expected # output. self.reset_test_sequence() self.test_sequence.add_log_lines( [ # Remove the breakpoint. "read packet: $z0,{0:x},{1}#00".format( function_address, BREAKPOINT_KIND), # Verify the stub could unset it. "send packet: $OK#00", # Continue running. "read packet: $c#63", # We should now receive the output from the call. {"type": "output_match", "regex": r"^hello, world\r\n$"}, # And wait for program completion. {"direction": "send", "regex": r"^\$W00(.*)#[0-9a-fA-F]{2}$"}, ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) @debugserver_test def test_software_breakpoint_set_and_remove_work_debugserver(self): self.init_debugserver_test() if self.getArchitecture() == "arm": # TODO: Handle case when setting breakpoint in thumb code self.build(dictionary={'CFLAGS_EXTRAS': '-marm'}) else: self.build() self.set_inferior_startup_launch() self.software_breakpoint_set_and_remove_work() @llgs_test @expectedFlakeyLinux("llvm.org/pr25652") def test_software_breakpoint_set_and_remove_work_llgs(self): self.init_llgs_test() if self.getArchitecture() == "arm": # TODO: Handle case when setting breakpoint in thumb code self.build(dictionary={'CFLAGS_EXTRAS': '-marm'}) else: self.build() self.set_inferior_startup_launch() self.software_breakpoint_set_and_remove_work() def qSupported_returns_known_stub_features(self): # Start up the stub and start/prep the inferior. procs = self.prep_debug_monitor_and_inferior() self.add_qSupported_packets() # Run the packet stream. context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Retrieve the qSupported features. supported_dict = self.parse_qSupported_response(context) self.assertIsNotNone(supported_dict) self.assertTrue(len(supported_dict) > 0) @debugserver_test def test_qSupported_returns_known_stub_features_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.qSupported_returns_known_stub_features() @llgs_test def test_qSupported_returns_known_stub_features_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.qSupported_returns_known_stub_features() def written_M_content_reads_back_correctly(self): TEST_MESSAGE = "Hello, memory" # Start up the stub and start/prep the inferior. procs = self.prep_debug_monitor_and_inferior( inferior_args=[ "set-message:xxxxxxxxxxxxxX", "get-data-address-hex:g_message", "sleep:1", "print-message:"]) self.test_sequence.add_log_lines( [ # Start running after initial stop. "read packet: $c#63", # Match output line that prints the memory address of the message buffer within the inferior. # Note we require launch-only testing so we can get inferior otuput. {"type": "output_match", "regex": self.maybe_strict_output_regex(r"data address: 0x([0-9a-fA-F]+)\r\n"), "capture": {1: "message_address"}}, # Now stop the inferior. "read packet: {}".format(chr(3)), # And wait for the stop notification. {"direction": "send", "regex": r"^\$T([0-9a-fA-F]{2})thread:([0-9a-fA-F]+);", "capture": {1: "stop_signo", 2: "stop_thread_id"}}], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Grab the message address. self.assertIsNotNone(context.get("message_address")) message_address = int(context.get("message_address"), 16) # Hex-encode the test message, adding null termination. hex_encoded_message = TEST_MESSAGE.encode("hex") # Write the message to the inferior. Verify that we can read it with the hex-encoded (m) # and binary (x) memory read packets. self.reset_test_sequence() self.test_sequence.add_log_lines( ["read packet: $M{0:x},{1:x}:{2}#00".format(message_address, len(TEST_MESSAGE), hex_encoded_message), "send packet: $OK#00", "read packet: $m{0:x},{1:x}#00".format(message_address, len(TEST_MESSAGE)), "send packet: ${0}#00".format(hex_encoded_message), "read packet: $x{0:x},{1:x}#00".format(message_address, len(TEST_MESSAGE)), "send packet: ${0}#00".format(TEST_MESSAGE), "read packet: $m{0:x},4#00".format(message_address), "send packet: ${0}#00".format(hex_encoded_message[0:8]), "read packet: $x{0:x},4#00".format(message_address), "send packet: ${0}#00".format(TEST_MESSAGE[0:4]), "read packet: $c#63", {"type": "output_match", "regex": r"^message: (.+)\r\n$", "capture": {1: "printed_message"}}, "send packet: $W00#00", ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Ensure what we read from inferior memory is what we wrote. printed_message = context.get("printed_message") self.assertIsNotNone(printed_message) self.assertEqual(printed_message, TEST_MESSAGE + "X") @debugserver_test def test_written_M_content_reads_back_correctly_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.written_M_content_reads_back_correctly() @llgs_test @expectedFlakeyLinux("llvm.org/pr25652") def test_written_M_content_reads_back_correctly_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.written_M_content_reads_back_correctly() def P_writes_all_gpr_registers(self): # Start inferior debug session, grab all register info. procs = self.prep_debug_monitor_and_inferior(inferior_args=["sleep:2"]) self.add_register_info_collection_packets() self.add_process_info_collection_packets() context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Process register infos. reg_infos = self.parse_register_info_packets(context) self.assertIsNotNone(reg_infos) self.add_lldb_register_index(reg_infos) # Process endian. process_info = self.parse_process_info_response(context) endian = process_info.get("endian") self.assertIsNotNone(endian) # Pull out the register infos that we think we can bit flip # successfully,. gpr_reg_infos = [ reg_info for reg_info in reg_infos if self.is_bit_flippable_register(reg_info)] self.assertTrue(len(gpr_reg_infos) > 0) # Write flipped bit pattern of existing value to each register. (successful_writes, failed_writes) = self.flip_all_bits_in_each_register_value( gpr_reg_infos, endian) # print("successful writes: {}, failed writes: {}".format(successful_writes, failed_writes)) self.assertTrue(successful_writes > 0) # Note: as of this moment, a hefty number of the GPR writes are failing with E32 (everything except rax-rdx, rdi, rsi, rbp). # Come back to this. I have the test rigged to verify that at least some # of the bit-flip writes work. @debugserver_test def test_P_writes_all_gpr_registers_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.P_writes_all_gpr_registers() @llgs_test def test_P_writes_all_gpr_registers_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.P_writes_all_gpr_registers() def P_and_p_thread_suffix_work(self): # Startup the inferior with three threads. procs = self.prep_debug_monitor_and_inferior( inferior_args=["thread:new", "thread:new"]) self.add_thread_suffix_request_packets() self.add_register_info_collection_packets() self.add_process_info_collection_packets() context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) process_info = self.parse_process_info_response(context) self.assertIsNotNone(process_info) endian = process_info.get("endian") self.assertIsNotNone(endian) reg_infos = self.parse_register_info_packets(context) self.assertIsNotNone(reg_infos) self.add_lldb_register_index(reg_infos) reg_index = self.select_modifiable_register(reg_infos) self.assertIsNotNone(reg_index) reg_byte_size = int(reg_infos[reg_index]["bitsize"]) / 8 self.assertTrue(reg_byte_size > 0) # Run the process a bit so threads can start up, and collect register # info. context = self.run_process_then_stop(run_seconds=1) self.assertIsNotNone(context) # Wait for 3 threads to be present. threads = self.wait_for_thread_count(3, timeout_seconds=5) self.assertEqual(len(threads), 3) expected_reg_values = [] register_increment = 1 next_value = None # Set the same register in each of 3 threads to a different value. # Verify each one has the unique value. for thread in threads: # If we don't have a next value yet, start it with the initial read # value + 1 if not next_value: # Read pre-existing register value. self.reset_test_sequence() self.test_sequence.add_log_lines( ["read packet: $p{0:x};thread:{1:x}#00".format(reg_index, thread), {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", "capture": {1: "p_response"}}, ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Set the next value to use for writing as the increment plus # current value. p_response = context.get("p_response") self.assertIsNotNone(p_response) next_value = lldbgdbserverutils.unpack_register_hex_unsigned( endian, p_response) # Set new value using P and thread suffix. self.reset_test_sequence() self.test_sequence.add_log_lines( [ "read packet: $P{0:x}={1};thread:{2:x}#00".format( reg_index, lldbgdbserverutils.pack_register_hex( endian, next_value, byte_size=reg_byte_size), thread), "send packet: $OK#00", ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Save the value we set. expected_reg_values.append(next_value) # Increment value for next thread to use (we want them all # different so we can verify they wrote to each thread correctly # next.) next_value += register_increment # Revisit each thread and verify they have the expected value set for # the register we wrote. thread_index = 0 for thread in threads: # Read pre-existing register value. self.reset_test_sequence() self.test_sequence.add_log_lines( ["read packet: $p{0:x};thread:{1:x}#00".format(reg_index, thread), {"direction": "send", "regex": r"^\$([0-9a-fA-F]+)#", "capture": {1: "p_response"}}, ], True) context = self.expect_gdbremote_sequence() self.assertIsNotNone(context) # Get the register value. p_response = context.get("p_response") self.assertIsNotNone(p_response) read_value = lldbgdbserverutils.unpack_register_hex_unsigned( endian, p_response) # Make sure we read back what we wrote. self.assertEqual(read_value, expected_reg_values[thread_index]) thread_index += 1 # Note: as of this moment, a hefty number of the GPR writes are failing # with E32 (everything except rax-rdx, rdi, rsi, rbp). @debugserver_test def test_P_and_p_thread_suffix_work_debugserver(self): self.init_debugserver_test() self.build() self.set_inferior_startup_launch() self.P_and_p_thread_suffix_work() @llgs_test def test_P_and_p_thread_suffix_work_llgs(self): self.init_llgs_test() self.build() self.set_inferior_startup_launch() self.P_and_p_thread_suffix_work() Index: vendor/lldb/dist/scripts/Xcode/build-llvm.py =================================================================== --- vendor/lldb/dist/scripts/Xcode/build-llvm.py (revision 312182) +++ vendor/lldb/dist/scripts/Xcode/build-llvm.py (revision 312183) @@ -1,442 +1,442 @@ #!/usr/bin/env python import errno import hashlib import fnmatch import os import platform import re import subprocess import sys from lldbbuild import * #### SETTINGS #### def LLVM_HASH_INCLUDES_DIFFS(): return False # The use of "x = "..."; return x" here is important because tooling looks for # it with regexps. Only change how this works if you know what you are doing. def LLVM_REF(): - llvm_ref = "master" + llvm_ref = "release_40" return llvm_ref def CLANG_REF(): - clang_ref = "master" + clang_ref = "release_40" return clang_ref # For use with Xcode-style builds def XCODE_REPOSITORIES(): return [ {'name': "llvm", 'vcs': VCS.git, 'root': llvm_source_path(), 'url': "http://llvm.org/git/llvm.git", 'ref': LLVM_REF()}, {'name': "clang", 'vcs': VCS.git, 'root': clang_source_path(), 'url': "http://llvm.org/git/clang.git", 'ref': CLANG_REF()}, {'name': "ninja", 'vcs': VCS.git, 'root': ninja_source_path(), 'url': "https://github.com/ninja-build/ninja.git", 'ref': "master"} ] def get_c_compiler(): return subprocess.check_output([ 'xcrun', '--sdk', 'macosx', '-find', 'clang' ]).rstrip() def get_cxx_compiler(): return subprocess.check_output([ 'xcrun', '--sdk', 'macosx', '-find', 'clang++' ]).rstrip() # CFLAGS="-isysroot $(xcrun --sdk macosx --show-sdk-path) -mmacosx-version-min=${DARWIN_DEPLOYMENT_VERSION_OSX}" \ # LDFLAGS="-mmacosx-version-min=${DARWIN_DEPLOYMENT_VERSION_OSX}" \ def get_deployment_target(): return os.environ.get('MACOSX_DEPLOYMENT_TARGET', None) def get_c_flags(): cflags = '' # sdk_path = subprocess.check_output([ # 'xcrun', # '--sdk', 'macosx', # '--show-sdk-path']).rstrip() # cflags += '-isysroot {}'.format(sdk_path) deployment_target = get_deployment_target() if deployment_target: # cflags += ' -mmacosx-version-min={}'.format(deployment_target) pass return cflags def get_cxx_flags(): return get_c_flags() def get_common_linker_flags(): linker_flags = "" deployment_target = get_deployment_target() if deployment_target: # if len(linker_flags) > 0: # linker_flags += ' ' # linker_flags += '-mmacosx-version-min={}'.format(deployment_target) pass return linker_flags def get_exe_linker_flags(): return get_common_linker_flags() def get_shared_linker_flags(): return get_common_linker_flags() def CMAKE_FLAGS(): return { "Debug": [ "-DCMAKE_BUILD_TYPE=RelWithDebInfo", "-DLLVM_ENABLE_ASSERTIONS=ON", ], "DebugClang": [ "-DCMAKE_BUILD_TYPE=Debug", "-DLLVM_ENABLE_ASSERTIONS=ON", ], "Release": [ "-DCMAKE_BUILD_TYPE=Release", "-DLLVM_ENABLE_ASSERTIONS=ON", ], "BuildAndIntegration": [ "-DCMAKE_BUILD_TYPE=Release", "-DLLVM_ENABLE_ASSERTIONS=OFF", ], } def CMAKE_ENVIRONMENT(): return { } #### COLLECTING ALL ARCHIVES #### def collect_archives_in_path(path): files = os.listdir(path) # Only use libclang and libLLVM archives, and exclude libclang_rt regexp = "^lib(clang[^_]|LLVM|gtest).*$" return [ os.path.join( path, file) for file in files if file.endswith(".a") and re.match( regexp, file)] def archive_list(): paths = library_paths() archive_lists = [collect_archives_in_path(path) for path in paths] return [archive for archive_list in archive_lists for archive in archive_list] def write_archives_txt(): f = open(archives_txt(), 'w') for archive in archive_list(): f.write(archive + "\n") f.close() #### COLLECTING REPOSITORY MD5S #### def source_control_status(spec): vcs_for_spec = vcs(spec) if LLVM_HASH_INCLUDES_DIFFS(): return vcs_for_spec.status() + vcs_for_spec.diff() else: return vcs_for_spec.status() def source_control_status_for_specs(specs): statuses = [source_control_status(spec) for spec in specs] return "".join(statuses) def all_source_control_status(): return source_control_status_for_specs(XCODE_REPOSITORIES()) def md5(string): m = hashlib.md5() m.update(string) return m.hexdigest() def all_source_control_status_md5(): return md5(all_source_control_status()) #### CHECKING OUT AND BUILDING LLVM #### def apply_patches(spec): files = os.listdir(os.path.join(lldb_source_path(), 'scripts')) patches = [ f for f in files if fnmatch.fnmatch( f, spec['name'] + '.*.diff')] for p in patches: run_in_directory(["patch", "-p0", "-i", os.path.join(lldb_source_path(), 'scripts', p)], spec['root']) def check_out_if_needed(spec): if not os.path.isdir(spec['root']): vcs(spec).check_out() apply_patches(spec) def all_check_out_if_needed(): map(check_out_if_needed, XCODE_REPOSITORIES()) def should_build_llvm(): if build_type() == BuildType.Xcode: # TODO use md5 sums return True def do_symlink(source_path, link_path): print "Symlinking " + source_path + " to " + link_path if not os.path.exists(link_path): os.symlink(source_path, link_path) def setup_source_symlink(repo): source_path = repo["root"] link_path = os.path.join(lldb_source_path(), os.path.basename(source_path)) do_symlink(source_path, link_path) def setup_source_symlinks(): map(setup_source_symlink, XCODE_REPOSITORIES()) def setup_build_symlink(): # We don't use the build symlinks in llvm.org Xcode-based builds. if build_type() != BuildType.Xcode: source_path = package_build_path() link_path = expected_package_build_path() do_symlink(source_path, link_path) def should_run_cmake(cmake_build_dir): # We need to run cmake if our llvm build directory doesn't yet exist. if not os.path.exists(cmake_build_dir): return True # Wee also need to run cmake if for some reason we don't have a ninja # build file. (Perhaps the cmake invocation failed, which this current # build may have fixed). ninja_path = os.path.join(cmake_build_dir, "build.ninja") return not os.path.exists(ninja_path) def cmake_environment(): cmake_env = join_dicts(os.environ, CMAKE_ENVIRONMENT()) return cmake_env def is_executable(path): return os.path.isfile(path) and os.access(path, os.X_OK) def find_executable_in_paths(program, paths_to_check): program_dir, program_name = os.path.split(program) if program_dir: if is_executable(program): return program else: for path_dir in paths_to_check: path_dir = path_dir.strip('"') executable_file = os.path.join(path_dir, program) if is_executable(executable_file): return executable_file return None def find_cmake(): # First check the system PATH env var for cmake cmake_binary = find_executable_in_paths( "cmake", os.environ["PATH"].split(os.pathsep)) if cmake_binary: # We found it there, use it. return cmake_binary # Check a few more common spots. Xcode launched from Finder # will have the default environment, and may not have # all the normal places present. extra_cmake_dirs = [ "/usr/local/bin", "/opt/local/bin", os.path.join(os.path.expanduser("~"), "bin") ] if platform.system() == "Darwin": # Add locations where an official CMake.app package may be installed. extra_cmake_dirs.extend([ os.path.join( os.path.expanduser("~"), "Applications", "CMake.app", "Contents", "bin"), os.path.join( os.sep, "Applications", "CMake.app", "Contents", "bin")]) cmake_binary = find_executable_in_paths("cmake", extra_cmake_dirs) if cmake_binary: # We found it in one of the usual places. Use that. return cmake_binary # We couldn't find cmake. Tell the user what to do. raise Exception( "could not find cmake in PATH ({}) or in any of these locations ({}), " "please install cmake or add a link to it in one of those locations".format( os.environ["PATH"], extra_cmake_dirs)) def cmake_flags(): cmake_flags = CMAKE_FLAGS()[lldb_configuration()] cmake_flags += ["-GNinja", "-DCMAKE_C_COMPILER={}".format(get_c_compiler()), "-DCMAKE_CXX_COMPILER={}".format(get_cxx_compiler()), "-DCMAKE_INSTALL_PREFIX={}".format(expected_package_build_path_for("llvm")), "-DCMAKE_C_FLAGS={}".format(get_c_flags()), "-DCMAKE_CXX_FLAGS={}".format(get_cxx_flags()), "-DCMAKE_EXE_LINKER_FLAGS={}".format(get_exe_linker_flags()), "-DCMAKE_SHARED_LINKER_FLAGS={}".format(get_shared_linker_flags()), "-DHAVE_CRASHREPORTER_INFO=1"] deployment_target = get_deployment_target() if deployment_target: cmake_flags.append( "-DCMAKE_OSX_DEPLOYMENT_TARGET={}".format(deployment_target)) return cmake_flags def run_cmake(cmake_build_dir, ninja_binary_path): cmake_binary = find_cmake() print "found cmake binary: using \"{}\"".format(cmake_binary) command_line = [cmake_binary] + cmake_flags() + [ "-DCMAKE_MAKE_PROGRAM={}".format(ninja_binary_path), llvm_source_path()] print "running cmake like so: ({}) in dir ({})".format(command_line, cmake_build_dir) subprocess.check_call( command_line, cwd=cmake_build_dir, env=cmake_environment()) def create_directories_as_needed(path): try: os.makedirs(path) except OSError as error: # An error indicating that the directory exists already is fine. # Anything else should be passed along. if error.errno != errno.EEXIST: raise error def run_cmake_if_needed(ninja_binary_path): cmake_build_dir = package_build_path() if should_run_cmake(cmake_build_dir): # Create the build directory as needed create_directories_as_needed(cmake_build_dir) run_cmake(cmake_build_dir, ninja_binary_path) def build_ninja_if_needed(): # First check if ninja is in our path. If so, there's nothing to do. ninja_binary_path = find_executable_in_paths( "ninja", os.environ["PATH"].split(os.pathsep)) if ninja_binary_path: # It's on the path. cmake will find it. We're good. print "found ninja here: \"{}\"".format(ninja_binary_path) return ninja_binary_path # Figure out if we need to build it. ninja_build_dir = ninja_source_path() ninja_binary_path = os.path.join(ninja_build_dir, "ninja") if not is_executable(ninja_binary_path): # Build ninja command_line = ["python", "configure.py", "--bootstrap"] print "building ninja like so: ({}) in dir ({})".format(command_line, ninja_build_dir) subprocess.check_call( command_line, cwd=ninja_build_dir, env=os.environ) return ninja_binary_path def join_dicts(dict1, dict2): d = dict1.copy() d.update(dict2) return d def build_llvm(ninja_binary_path): cmake_build_dir = package_build_path() subprocess.check_call( [ninja_binary_path], cwd=cmake_build_dir, env=cmake_environment()) def build_llvm_if_needed(): if should_build_llvm(): ninja_binary_path = build_ninja_if_needed() run_cmake_if_needed(ninja_binary_path) build_llvm(ninja_binary_path) setup_build_symlink() #### MAIN LOGIC #### all_check_out_if_needed() build_llvm_if_needed() write_archives_txt() sys.exit(0) Index: vendor/lldb/dist/source/Core/Module.cpp =================================================================== --- vendor/lldb/dist/source/Core/Module.cpp (revision 312182) +++ vendor/lldb/dist/source/Core/Module.cpp (revision 312183) @@ -1,1670 +1,1666 @@ //===-- Module.cpp ----------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Core/Module.h" // C Includes // C++ Includes // Other libraries and framework includes #include "llvm/Support/Signals.h" #include "llvm/Support/raw_os_ostream.h" // Project includes #include "Plugins/Language/CPlusPlus/CPlusPlusLanguage.h" #include "Plugins/Language/ObjC/ObjCLanguage.h" #include "lldb/Core/AddressResolverFileLine.h" #include "lldb/Core/DataBuffer.h" #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "lldb/Core/ModuleList.h" #include "lldb/Core/ModuleSpec.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/Section.h" #include "lldb/Core/StreamString.h" #include "lldb/Core/Timer.h" #include "lldb/Host/FileSystem.h" #include "lldb/Host/Host.h" #include "lldb/Host/Symbols.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/ScriptInterpreter.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/TypeMap.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Target/Language.h" #include "lldb/Target/Process.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" #include "Plugins/ObjectFile/JIT/ObjectFileJIT.h" using namespace lldb; using namespace lldb_private; // Shared pointers to modules track module lifetimes in // targets and in the global module, but this collection // will track all module objects that are still alive typedef std::vector ModuleCollection; static ModuleCollection &GetModuleCollection() { // This module collection needs to live past any module, so we could either // make it a // shared pointer in each module or just leak is. Since it is only an empty // vector by // the time all the modules have gone away, we just leak it for now. If we // decide this // is a big problem we can introduce a Finalize method that will tear // everything down in // a predictable order. static ModuleCollection *g_module_collection = nullptr; if (g_module_collection == nullptr) g_module_collection = new ModuleCollection(); return *g_module_collection; } std::recursive_mutex &Module::GetAllocationModuleCollectionMutex() { // NOTE: The mutex below must be leaked since the global module list in // the ModuleList class will get torn at some point, and we can't know // if it will tear itself down before the "g_module_collection_mutex" below // will. So we leak a Mutex object below to safeguard against that static std::recursive_mutex *g_module_collection_mutex = nullptr; if (g_module_collection_mutex == nullptr) g_module_collection_mutex = new std::recursive_mutex; // NOTE: known leak return *g_module_collection_mutex; } size_t Module::GetNumberAllocatedModules() { std::lock_guard guard( GetAllocationModuleCollectionMutex()); return GetModuleCollection().size(); } Module *Module::GetAllocatedModuleAtIndex(size_t idx) { std::lock_guard guard( GetAllocationModuleCollectionMutex()); ModuleCollection &modules = GetModuleCollection(); if (idx < modules.size()) return modules[idx]; return nullptr; } #if 0 // These functions help us to determine if modules are still loaded, yet don't require that // you have a command interpreter and can easily be called from an external debugger. namespace lldb { void ClearModuleInfo (void) { const bool mandatory = true; ModuleList::RemoveOrphanSharedModules(mandatory); } void DumpModuleInfo (void) { Mutex::Locker locker (Module::GetAllocationModuleCollectionMutex()); ModuleCollection &modules = GetModuleCollection(); const size_t count = modules.size(); printf ("%s: %" PRIu64 " modules:\n", LLVM_PRETTY_FUNCTION, (uint64_t)count); for (size_t i = 0; i < count; ++i) { StreamString strm; Module *module = modules[i]; const bool in_shared_module_list = ModuleList::ModuleIsInCache (module); module->GetDescription(&strm, eDescriptionLevelFull); printf ("%p: shared = %i, ref_count = %3u, module = %s\n", module, in_shared_module_list, (uint32_t)module->use_count(), strm.GetString().c_str()); } } } #endif Module::Module(const ModuleSpec &module_spec) : m_object_offset(0), m_file_has_changed(false), m_first_file_changed_log(false) { // Scope for locker below... { std::lock_guard guard( GetAllocationModuleCollectionMutex()); GetModuleCollection().push_back(this); } Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_MODULES)); if (log != nullptr) log->Printf("%p Module::Module((%s) '%s%s%s%s')", static_cast(this), module_spec.GetArchitecture().GetArchitectureName(), module_spec.GetFileSpec().GetPath().c_str(), module_spec.GetObjectName().IsEmpty() ? "" : "(", module_spec.GetObjectName().IsEmpty() ? "" : module_spec.GetObjectName().AsCString(""), module_spec.GetObjectName().IsEmpty() ? "" : ")"); // First extract all module specifications from the file using the local // file path. If there are no specifications, then don't fill anything in ModuleSpecList modules_specs; if (ObjectFile::GetModuleSpecifications(module_spec.GetFileSpec(), 0, 0, modules_specs) == 0) return; // Now make sure that one of the module specifications matches what we just // extract. We might have a module specification that specifies a file // "/usr/lib/dyld" // with UUID XXX, but we might have a local version of "/usr/lib/dyld" that // has // UUID YYY and we don't want those to match. If they don't match, just don't // fill any ivars in so we don't accidentally grab the wrong file later since // they don't match... ModuleSpec matching_module_spec; if (modules_specs.FindMatchingModuleSpec(module_spec, matching_module_spec) == 0) return; if (module_spec.GetFileSpec()) m_mod_time = FileSystem::GetModificationTime(module_spec.GetFileSpec()); else if (matching_module_spec.GetFileSpec()) m_mod_time = FileSystem::GetModificationTime(matching_module_spec.GetFileSpec()); // Copy the architecture from the actual spec if we got one back, else use the // one that was specified if (matching_module_spec.GetArchitecture().IsValid()) m_arch = matching_module_spec.GetArchitecture(); else if (module_spec.GetArchitecture().IsValid()) m_arch = module_spec.GetArchitecture(); // Copy the file spec over and use the specified one (if there was one) so we // don't use a path that might have gotten resolved a path in // 'matching_module_spec' if (module_spec.GetFileSpec()) m_file = module_spec.GetFileSpec(); else if (matching_module_spec.GetFileSpec()) m_file = matching_module_spec.GetFileSpec(); // Copy the platform file spec over if (module_spec.GetPlatformFileSpec()) m_platform_file = module_spec.GetPlatformFileSpec(); else if (matching_module_spec.GetPlatformFileSpec()) m_platform_file = matching_module_spec.GetPlatformFileSpec(); // Copy the symbol file spec over if (module_spec.GetSymbolFileSpec()) m_symfile_spec = module_spec.GetSymbolFileSpec(); else if (matching_module_spec.GetSymbolFileSpec()) m_symfile_spec = matching_module_spec.GetSymbolFileSpec(); // Copy the object name over if (matching_module_spec.GetObjectName()) m_object_name = matching_module_spec.GetObjectName(); else m_object_name = module_spec.GetObjectName(); // Always trust the object offset (file offset) and object modification // time (for mod time in a BSD static archive) of from the matching // module specification m_object_offset = matching_module_spec.GetObjectOffset(); m_object_mod_time = matching_module_spec.GetObjectModificationTime(); } Module::Module(const FileSpec &file_spec, const ArchSpec &arch, const ConstString *object_name, lldb::offset_t object_offset, const llvm::sys::TimePoint<> &object_mod_time) : m_mod_time(FileSystem::GetModificationTime(file_spec)), m_arch(arch), m_file(file_spec), m_object_offset(object_offset), m_object_mod_time(object_mod_time), m_file_has_changed(false), m_first_file_changed_log(false) { // Scope for locker below... { std::lock_guard guard( GetAllocationModuleCollectionMutex()); GetModuleCollection().push_back(this); } if (object_name) m_object_name = *object_name; Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_MODULES)); if (log != nullptr) log->Printf("%p Module::Module((%s) '%s%s%s%s')", static_cast(this), m_arch.GetArchitectureName(), m_file.GetPath().c_str(), m_object_name.IsEmpty() ? "" : "(", m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), m_object_name.IsEmpty() ? "" : ")"); } Module::Module() : m_object_offset(0), m_file_has_changed(false), m_first_file_changed_log(false) { std::lock_guard guard( GetAllocationModuleCollectionMutex()); GetModuleCollection().push_back(this); } Module::~Module() { // Lock our module down while we tear everything down to make sure // we don't get any access to the module while it is being destroyed std::lock_guard guard(m_mutex); // Scope for locker below... { std::lock_guard guard( GetAllocationModuleCollectionMutex()); ModuleCollection &modules = GetModuleCollection(); ModuleCollection::iterator end = modules.end(); ModuleCollection::iterator pos = std::find(modules.begin(), end, this); assert(pos != end); modules.erase(pos); } Log *log(lldb_private::GetLogIfAnyCategoriesSet(LIBLLDB_LOG_OBJECT | LIBLLDB_LOG_MODULES)); if (log != nullptr) log->Printf("%p Module::~Module((%s) '%s%s%s%s')", static_cast(this), m_arch.GetArchitectureName(), m_file.GetPath().c_str(), m_object_name.IsEmpty() ? "" : "(", m_object_name.IsEmpty() ? "" : m_object_name.AsCString(""), m_object_name.IsEmpty() ? "" : ")"); // Release any auto pointers before we start tearing down our member // variables since the object file and symbol files might need to make // function calls back into this module object. The ordering is important // here because symbol files can require the module object file. So we tear // down the symbol file first, then the object file. m_sections_ap.reset(); m_symfile_ap.reset(); m_objfile_sp.reset(); } ObjectFile *Module::GetMemoryObjectFile(const lldb::ProcessSP &process_sp, lldb::addr_t header_addr, Error &error, size_t size_to_read) { if (m_objfile_sp) { error.SetErrorString("object file already exists"); } else { std::lock_guard guard(m_mutex); if (process_sp) { m_did_load_objfile = true; std::unique_ptr data_ap( new DataBufferHeap(size_to_read, 0)); Error readmem_error; const size_t bytes_read = process_sp->ReadMemory(header_addr, data_ap->GetBytes(), data_ap->GetByteSize(), readmem_error); if (bytes_read == size_to_read) { DataBufferSP data_sp(data_ap.release()); m_objfile_sp = ObjectFile::FindPlugin(shared_from_this(), process_sp, header_addr, data_sp); if (m_objfile_sp) { StreamString s; s.Printf("0x%16.16" PRIx64, header_addr); m_object_name.SetString(s.GetString()); // Once we get the object file, update our module with the object // file's // architecture since it might differ in vendor/os if some parts were // unknown. m_objfile_sp->GetArchitecture(m_arch); } else { error.SetErrorString("unable to find suitable object file plug-in"); } } else { error.SetErrorStringWithFormat("unable to read header from memory: %s", readmem_error.AsCString()); } } else { error.SetErrorString("invalid process"); } } return m_objfile_sp.get(); } const lldb_private::UUID &Module::GetUUID() { if (!m_did_parse_uuid.load()) { std::lock_guard guard(m_mutex); if (!m_did_parse_uuid.load()) { ObjectFile *obj_file = GetObjectFile(); if (obj_file != nullptr) { obj_file->GetUUID(&m_uuid); m_did_parse_uuid = true; } } } return m_uuid; } TypeSystem *Module::GetTypeSystemForLanguage(LanguageType language) { return m_type_system_map.GetTypeSystemForLanguage(language, this, true); } void Module::ParseAllDebugSymbols() { std::lock_guard guard(m_mutex); size_t num_comp_units = GetNumCompileUnits(); if (num_comp_units == 0) return; SymbolContext sc; sc.module_sp = shared_from_this(); SymbolVendor *symbols = GetSymbolVendor(); for (size_t cu_idx = 0; cu_idx < num_comp_units; cu_idx++) { sc.comp_unit = symbols->GetCompileUnitAtIndex(cu_idx).get(); if (sc.comp_unit) { sc.function = nullptr; symbols->ParseVariablesForContext(sc); symbols->ParseCompileUnitFunctions(sc); for (size_t func_idx = 0; (sc.function = sc.comp_unit->GetFunctionAtIndex(func_idx).get()) != nullptr; ++func_idx) { symbols->ParseFunctionBlocks(sc); // Parse the variables for this function and all its blocks symbols->ParseVariablesForContext(sc); } // Parse all types for this compile unit sc.function = nullptr; symbols->ParseTypes(sc); } } } void Module::CalculateSymbolContext(SymbolContext *sc) { sc->module_sp = shared_from_this(); } ModuleSP Module::CalculateSymbolContextModule() { return shared_from_this(); } void Module::DumpSymbolContext(Stream *s) { s->Printf(", Module{%p}", static_cast(this)); } size_t Module::GetNumCompileUnits() { std::lock_guard guard(m_mutex); Timer scoped_timer(LLVM_PRETTY_FUNCTION, "Module::GetNumCompileUnits (module = %p)", static_cast(this)); SymbolVendor *symbols = GetSymbolVendor(); if (symbols) return symbols->GetNumCompileUnits(); return 0; } CompUnitSP Module::GetCompileUnitAtIndex(size_t index) { std::lock_guard guard(m_mutex); size_t num_comp_units = GetNumCompileUnits(); CompUnitSP cu_sp; if (index < num_comp_units) { SymbolVendor *symbols = GetSymbolVendor(); if (symbols) cu_sp = symbols->GetCompileUnitAtIndex(index); } return cu_sp; } bool Module::ResolveFileAddress(lldb::addr_t vm_addr, Address &so_addr) { std::lock_guard guard(m_mutex); Timer scoped_timer(LLVM_PRETTY_FUNCTION, "Module::ResolveFileAddress (vm_addr = 0x%" PRIx64 ")", vm_addr); SectionList *section_list = GetSectionList(); if (section_list) return so_addr.ResolveAddressUsingFileSections(vm_addr, section_list); return false; } uint32_t Module::ResolveSymbolContextForAddress( const Address &so_addr, uint32_t resolve_scope, SymbolContext &sc, bool resolve_tail_call_address) { std::lock_guard guard(m_mutex); uint32_t resolved_flags = 0; // Clear the result symbol context in case we don't find anything, but don't // clear the target sc.Clear(false); // Get the section from the section/offset address. SectionSP section_sp(so_addr.GetSection()); // Make sure the section matches this module before we try and match anything if (section_sp && section_sp->GetModule().get() == this) { // If the section offset based address resolved itself, then this // is the right module. sc.module_sp = shared_from_this(); resolved_flags |= eSymbolContextModule; SymbolVendor *sym_vendor = GetSymbolVendor(); if (!sym_vendor) return resolved_flags; // Resolve the compile unit, function, block, line table or line // entry if requested. if (resolve_scope & eSymbolContextCompUnit || resolve_scope & eSymbolContextFunction || resolve_scope & eSymbolContextBlock || resolve_scope & eSymbolContextLineEntry || resolve_scope & eSymbolContextVariable) { resolved_flags |= sym_vendor->ResolveSymbolContext(so_addr, resolve_scope, sc); } // Resolve the symbol if requested, but don't re-look it up if we've already // found it. if (resolve_scope & eSymbolContextSymbol && !(resolved_flags & eSymbolContextSymbol)) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab && so_addr.IsSectionOffset()) { Symbol *matching_symbol = nullptr; symtab->ForEachSymbolContainingFileAddress( so_addr.GetFileAddress(), [&matching_symbol](Symbol *symbol) -> bool { if (symbol->GetType() != eSymbolTypeInvalid) { matching_symbol = symbol; return false; // Stop iterating } return true; // Keep iterating }); sc.symbol = matching_symbol; if (!sc.symbol && resolve_scope & eSymbolContextFunction && !(resolved_flags & eSymbolContextFunction)) { bool verify_unique = false; // No need to check again since // ResolveSymbolContext failed to find a // symbol at this address. if (ObjectFile *obj_file = sc.module_sp->GetObjectFile()) sc.symbol = obj_file->ResolveSymbolForAddress(so_addr, verify_unique); } if (sc.symbol) { if (sc.symbol->IsSynthetic()) { // We have a synthetic symbol so lets check if the object file // from the symbol file in the symbol vendor is different than // the object file for the module, and if so search its symbol // table to see if we can come up with a better symbol. For example // dSYM files on MacOSX have an unstripped symbol table inside of // them. ObjectFile *symtab_objfile = symtab->GetObjectFile(); if (symtab_objfile && symtab_objfile->IsStripped()) { SymbolFile *symfile = sym_vendor->GetSymbolFile(); if (symfile) { ObjectFile *symfile_objfile = symfile->GetObjectFile(); if (symfile_objfile != symtab_objfile) { Symtab *symfile_symtab = symfile_objfile->GetSymtab(); if (symfile_symtab) { Symbol *symbol = symfile_symtab->FindSymbolContainingFileAddress( so_addr.GetFileAddress()); if (symbol && !symbol->IsSynthetic()) { sc.symbol = symbol; } } } } } } resolved_flags |= eSymbolContextSymbol; } } } // For function symbols, so_addr may be off by one. This is a convention // consistent // with FDE row indices in eh_frame sections, but requires extra logic here // to permit // symbol lookup for disassembly and unwind. if (resolve_scope & eSymbolContextSymbol && !(resolved_flags & eSymbolContextSymbol) && resolve_tail_call_address && so_addr.IsSectionOffset()) { Address previous_addr = so_addr; previous_addr.Slide(-1); bool do_resolve_tail_call_address = false; // prevent recursion const uint32_t flags = ResolveSymbolContextForAddress( previous_addr, resolve_scope, sc, do_resolve_tail_call_address); if (flags & eSymbolContextSymbol) { AddressRange addr_range; if (sc.GetAddressRange(eSymbolContextFunction | eSymbolContextSymbol, 0, false, addr_range)) { if (addr_range.GetBaseAddress().GetSection() == so_addr.GetSection()) { // If the requested address is one past the address range of a // function (i.e. a tail call), // or the decremented address is the start of a function (i.e. some // forms of trampoline), // indicate that the symbol has been resolved. if (so_addr.GetOffset() == addr_range.GetBaseAddress().GetOffset() || so_addr.GetOffset() == addr_range.GetBaseAddress().GetOffset() + addr_range.GetByteSize()) { resolved_flags |= flags; } } else { sc.symbol = nullptr; // Don't trust the symbol if the sections didn't match. } } } } } return resolved_flags; } uint32_t Module::ResolveSymbolContextForFilePath(const char *file_path, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList &sc_list) { FileSpec file_spec(file_path, false); return ResolveSymbolContextsForFileSpec(file_spec, line, check_inlines, resolve_scope, sc_list); } uint32_t Module::ResolveSymbolContextsForFileSpec(const FileSpec &file_spec, uint32_t line, bool check_inlines, uint32_t resolve_scope, SymbolContextList &sc_list) { std::lock_guard guard(m_mutex); Timer scoped_timer(LLVM_PRETTY_FUNCTION, "Module::ResolveSymbolContextForFilePath (%s:%u, " "check_inlines = %s, resolve_scope = 0x%8.8x)", file_spec.GetPath().c_str(), line, check_inlines ? "yes" : "no", resolve_scope); const uint32_t initial_count = sc_list.GetSize(); SymbolVendor *symbols = GetSymbolVendor(); if (symbols) symbols->ResolveSymbolContext(file_spec, line, check_inlines, resolve_scope, sc_list); return sc_list.GetSize() - initial_count; } size_t Module::FindGlobalVariables(const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, size_t max_matches, VariableList &variables) { SymbolVendor *symbols = GetSymbolVendor(); if (symbols) return symbols->FindGlobalVariables(name, parent_decl_ctx, append, max_matches, variables); return 0; } size_t Module::FindGlobalVariables(const RegularExpression ®ex, bool append, size_t max_matches, VariableList &variables) { SymbolVendor *symbols = GetSymbolVendor(); if (symbols) return symbols->FindGlobalVariables(regex, append, max_matches, variables); return 0; } size_t Module::FindCompileUnits(const FileSpec &path, bool append, SymbolContextList &sc_list) { if (!append) sc_list.Clear(); const size_t start_size = sc_list.GetSize(); const size_t num_compile_units = GetNumCompileUnits(); SymbolContext sc; sc.module_sp = shared_from_this(); const bool compare_directory = (bool)path.GetDirectory(); for (size_t i = 0; i < num_compile_units; ++i) { sc.comp_unit = GetCompileUnitAtIndex(i).get(); if (sc.comp_unit) { if (FileSpec::Equal(*sc.comp_unit, path, compare_directory)) sc_list.Append(sc); } } return sc_list.GetSize() - start_size; } Module::LookupInfo::LookupInfo(const ConstString &name, uint32_t name_type_mask, lldb::LanguageType language) : m_name(name), m_lookup_name(), m_language(language), m_name_type_mask(0), m_match_name_after_lookup(false) { const char *name_cstr = name.GetCString(); llvm::StringRef basename; llvm::StringRef context; if (name_type_mask & eFunctionNameTypeAuto) { if (CPlusPlusLanguage::IsCPPMangledName(name_cstr)) m_name_type_mask = eFunctionNameTypeFull; else if ((language == eLanguageTypeUnknown || Language::LanguageIsObjC(language)) && ObjCLanguage::IsPossibleObjCMethodName(name_cstr)) m_name_type_mask = eFunctionNameTypeFull; else if (Language::LanguageIsC(language)) { m_name_type_mask = eFunctionNameTypeFull; } else { if ((language == eLanguageTypeUnknown || Language::LanguageIsObjC(language)) && ObjCLanguage::IsPossibleObjCSelector(name_cstr)) m_name_type_mask |= eFunctionNameTypeSelector; CPlusPlusLanguage::MethodName cpp_method(name); basename = cpp_method.GetBasename(); if (basename.empty()) { if (CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context, basename)) m_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase); else m_name_type_mask |= eFunctionNameTypeFull; } else { m_name_type_mask |= (eFunctionNameTypeMethod | eFunctionNameTypeBase); } } } else { m_name_type_mask = name_type_mask; if (name_type_mask & eFunctionNameTypeMethod || name_type_mask & eFunctionNameTypeBase) { // If they've asked for a CPP method or function name and it can't be // that, we don't // even need to search for CPP methods or names. CPlusPlusLanguage::MethodName cpp_method(name); if (cpp_method.IsValid()) { basename = cpp_method.GetBasename(); if (!cpp_method.GetQualifiers().empty()) { // There is a "const" or other qualifier following the end of the // function parens, // this can't be a eFunctionNameTypeBase m_name_type_mask &= ~(eFunctionNameTypeBase); if (m_name_type_mask == eFunctionNameTypeNone) return; } } else { // If the CPP method parser didn't manage to chop this up, try to fill // in the base name if we can. // If a::b::c is passed in, we need to just look up "c", and then we'll // filter the result later. CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context, basename); } } if (name_type_mask & eFunctionNameTypeSelector) { if (!ObjCLanguage::IsPossibleObjCSelector(name_cstr)) { m_name_type_mask &= ~(eFunctionNameTypeSelector); if (m_name_type_mask == eFunctionNameTypeNone) return; } } // Still try and get a basename in case someone specifies a name type mask // of // eFunctionNameTypeFull and a name like "A::func" if (basename.empty()) { if (name_type_mask & eFunctionNameTypeFull) { CPlusPlusLanguage::MethodName cpp_method(name); basename = cpp_method.GetBasename(); if (basename.empty()) CPlusPlusLanguage::ExtractContextAndIdentifier(name_cstr, context, basename); } } } if (!basename.empty()) { // The name supplied was a partial C++ path like "a::count". In this case we // want to do a // lookup on the basename "count" and then make sure any matching results // contain "a::count" // so that it would match "b::a::count" and "a::count". This is why we set // "match_name_after_lookup" // to true m_lookup_name.SetString(basename); m_match_name_after_lookup = true; } else { // The name is already correct, just use the exact name as supplied, and we // won't need // to check if any matches contain "name" m_lookup_name = name; m_match_name_after_lookup = false; } } void Module::LookupInfo::Prune(SymbolContextList &sc_list, size_t start_idx) const { if (m_match_name_after_lookup && m_name) { SymbolContext sc; size_t i = start_idx; while (i < sc_list.GetSize()) { if (!sc_list.GetContextAtIndex(i, sc)) break; ConstString full_name(sc.GetFunctionName()); if (full_name && ::strstr(full_name.GetCString(), m_name.GetCString()) == nullptr) { sc_list.RemoveContextAtIndex(i); } else { ++i; } } } // If we have only full name matches we might have tried to set breakpoint on // "func" // and specified eFunctionNameTypeFull, but we might have found "a::func()", // "a::b::func()", "c::func()", "func()" and "func". Only "func()" and "func" // should // end up matching. if (m_name_type_mask == eFunctionNameTypeFull) { SymbolContext sc; size_t i = start_idx; while (i < sc_list.GetSize()) { if (!sc_list.GetContextAtIndex(i, sc)) break; ConstString full_name(sc.GetFunctionName()); CPlusPlusLanguage::MethodName cpp_method(full_name); if (cpp_method.IsValid()) { if (cpp_method.GetContext().empty()) { if (cpp_method.GetBasename().compare(m_name.GetStringRef()) != 0) { sc_list.RemoveContextAtIndex(i); continue; } } else { std::string qualified_name = cpp_method.GetScopeQualifiedName(); if (qualified_name.compare(m_name.GetCString()) != 0) { sc_list.RemoveContextAtIndex(i); continue; } } } ++i; } } } size_t Module::FindFunctions(const ConstString &name, const CompilerDeclContext *parent_decl_ctx, uint32_t name_type_mask, bool include_symbols, bool include_inlines, bool append, SymbolContextList &sc_list) { if (!append) sc_list.Clear(); const size_t old_size = sc_list.GetSize(); // Find all the functions (not symbols, but debug information functions... SymbolVendor *symbols = GetSymbolVendor(); if (name_type_mask & eFunctionNameTypeAuto) { LookupInfo lookup_info(name, name_type_mask, eLanguageTypeUnknown); if (symbols) { symbols->FindFunctions(lookup_info.GetLookupName(), parent_decl_ctx, lookup_info.GetNameTypeMask(), include_inlines, append, sc_list); // Now check our symbol table for symbols that are code symbols if // requested if (include_symbols) { Symtab *symtab = symbols->GetSymtab(); if (symtab) symtab->FindFunctionSymbols(lookup_info.GetLookupName(), lookup_info.GetNameTypeMask(), sc_list); } } const size_t new_size = sc_list.GetSize(); if (old_size < new_size) lookup_info.Prune(sc_list, old_size); } else { if (symbols) { symbols->FindFunctions(name, parent_decl_ctx, name_type_mask, include_inlines, append, sc_list); // Now check our symbol table for symbols that are code symbols if // requested if (include_symbols) { Symtab *symtab = symbols->GetSymtab(); if (symtab) symtab->FindFunctionSymbols(name, name_type_mask, sc_list); } } } return sc_list.GetSize() - old_size; } size_t Module::FindFunctions(const RegularExpression ®ex, bool include_symbols, bool include_inlines, bool append, SymbolContextList &sc_list) { if (!append) sc_list.Clear(); const size_t start_size = sc_list.GetSize(); SymbolVendor *symbols = GetSymbolVendor(); if (symbols) { symbols->FindFunctions(regex, include_inlines, append, sc_list); // Now check our symbol table for symbols that are code symbols if requested if (include_symbols) { Symtab *symtab = symbols->GetSymtab(); if (symtab) { std::vector symbol_indexes; symtab->AppendSymbolIndexesMatchingRegExAndType( regex, eSymbolTypeAny, Symtab::eDebugAny, Symtab::eVisibilityAny, symbol_indexes); const size_t num_matches = symbol_indexes.size(); if (num_matches) { SymbolContext sc(this); const size_t end_functions_added_index = sc_list.GetSize(); size_t num_functions_added_to_sc_list = end_functions_added_index - start_size; if (num_functions_added_to_sc_list == 0) { // No functions were added, just symbols, so we can just append them for (size_t i = 0; i < num_matches; ++i) { sc.symbol = symtab->SymbolAtIndex(symbol_indexes[i]); SymbolType sym_type = sc.symbol->GetType(); if (sc.symbol && (sym_type == eSymbolTypeCode || sym_type == eSymbolTypeResolver)) sc_list.Append(sc); } } else { typedef std::map FileAddrToIndexMap; FileAddrToIndexMap file_addr_to_index; for (size_t i = start_size; i < end_functions_added_index; ++i) { const SymbolContext &sc = sc_list[i]; if (sc.block) continue; file_addr_to_index[sc.function->GetAddressRange() .GetBaseAddress() .GetFileAddress()] = i; } FileAddrToIndexMap::const_iterator end = file_addr_to_index.end(); // Functions were added so we need to merge symbols into any // existing function symbol contexts for (size_t i = start_size; i < num_matches; ++i) { sc.symbol = symtab->SymbolAtIndex(symbol_indexes[i]); SymbolType sym_type = sc.symbol->GetType(); if (sc.symbol && sc.symbol->ValueIsAddress() && (sym_type == eSymbolTypeCode || sym_type == eSymbolTypeResolver)) { FileAddrToIndexMap::const_iterator pos = file_addr_to_index.find( sc.symbol->GetAddressRef().GetFileAddress()); if (pos == end) sc_list.Append(sc); else sc_list[pos->second].symbol = sc.symbol; } } } } } } } return sc_list.GetSize() - start_size; } void Module::FindAddressesForLine(const lldb::TargetSP target_sp, const FileSpec &file, uint32_t line, Function *function, std::vector
&output_local, std::vector
&output_extern) { SearchFilterByModule filter(target_sp, m_file); AddressResolverFileLine resolver(file, line, true); resolver.ResolveAddress(filter); for (size_t n = 0; n < resolver.GetNumberOfAddresses(); n++) { Address addr = resolver.GetAddressRangeAtIndex(n).GetBaseAddress(); Function *f = addr.CalculateSymbolContextFunction(); if (f && f == function) output_local.push_back(addr); else output_extern.push_back(addr); } } size_t Module::FindTypes_Impl( const SymbolContext &sc, const ConstString &name, const CompilerDeclContext *parent_decl_ctx, bool append, size_t max_matches, llvm::DenseSet &searched_symbol_files, TypeMap &types) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, LLVM_PRETTY_FUNCTION); if (!sc.module_sp || sc.module_sp.get() == this) { SymbolVendor *symbols = GetSymbolVendor(); if (symbols) return symbols->FindTypes(sc, name, parent_decl_ctx, append, max_matches, searched_symbol_files, types); } return 0; } size_t Module::FindTypesInNamespace(const SymbolContext &sc, const ConstString &type_name, const CompilerDeclContext *parent_decl_ctx, size_t max_matches, TypeList &type_list) { const bool append = true; TypeMap types_map; llvm::DenseSet searched_symbol_files; size_t num_types = FindTypes_Impl(sc, type_name, parent_decl_ctx, append, max_matches, searched_symbol_files, types_map); if (num_types > 0) sc.SortTypeList(types_map, type_list); return num_types; } lldb::TypeSP Module::FindFirstType(const SymbolContext &sc, const ConstString &name, bool exact_match) { TypeList type_list; llvm::DenseSet searched_symbol_files; const size_t num_matches = FindTypes(sc, name, exact_match, 1, searched_symbol_files, type_list); if (num_matches) return type_list.GetTypeAtIndex(0); return TypeSP(); } size_t Module::FindTypes( const SymbolContext &sc, const ConstString &name, bool exact_match, size_t max_matches, llvm::DenseSet &searched_symbol_files, TypeList &types) { size_t num_matches = 0; const char *type_name_cstr = name.GetCString(); - std::string type_scope; - std::string type_basename; + llvm::StringRef type_scope; + llvm::StringRef type_basename; const bool append = true; TypeClass type_class = eTypeClassAny; TypeMap typesmap; if (Type::GetTypeScopeAndBasename(type_name_cstr, type_scope, type_basename, type_class)) { // Check if "name" starts with "::" which means the qualified type starts // from the root namespace and implies and exact match. The typenames we // get back from clang do not start with "::" so we need to strip this off // in order to get the qualified names to match + exact_match = type_scope.consume_front("::"); - if (type_scope.size() >= 2 && type_scope[0] == ':' && - type_scope[1] == ':') { - type_scope.erase(0, 2); - exact_match = true; - } - ConstString type_basename_const_str(type_basename.c_str()); + ConstString type_basename_const_str(type_basename); if (FindTypes_Impl(sc, type_basename_const_str, nullptr, append, max_matches, searched_symbol_files, typesmap)) { typesmap.RemoveMismatchedTypes(type_scope, type_basename, type_class, exact_match); num_matches = typesmap.GetSize(); } } else { // The type is not in a namespace/class scope, just search for it by // basename if (type_class != eTypeClassAny) { // The "type_name_cstr" will have been modified if we have a valid type // class // prefix (like "struct", "class", "union", "typedef" etc). FindTypes_Impl(sc, ConstString(type_name_cstr), nullptr, append, max_matches, searched_symbol_files, typesmap); typesmap.RemoveMismatchedTypes(type_class); num_matches = typesmap.GetSize(); } else { num_matches = FindTypes_Impl(sc, name, nullptr, append, max_matches, searched_symbol_files, typesmap); } } if (num_matches > 0) sc.SortTypeList(typesmap, types); return num_matches; } SymbolVendor *Module::GetSymbolVendor(bool can_create, lldb_private::Stream *feedback_strm) { if (!m_did_load_symbol_vendor.load()) { std::lock_guard guard(m_mutex); if (!m_did_load_symbol_vendor.load() && can_create) { ObjectFile *obj_file = GetObjectFile(); if (obj_file != nullptr) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, LLVM_PRETTY_FUNCTION); m_symfile_ap.reset( SymbolVendor::FindPlugin(shared_from_this(), feedback_strm)); m_did_load_symbol_vendor = true; } } } return m_symfile_ap.get(); } void Module::SetFileSpecAndObjectName(const FileSpec &file, const ConstString &object_name) { // Container objects whose paths do not specify a file directly can call // this function to correct the file and object names. m_file = file; m_mod_time = FileSystem::GetModificationTime(file); m_object_name = object_name; } const ArchSpec &Module::GetArchitecture() const { return m_arch; } std::string Module::GetSpecificationDescription() const { std::string spec(GetFileSpec().GetPath()); if (m_object_name) { spec += '('; spec += m_object_name.GetCString(); spec += ')'; } return spec; } void Module::GetDescription(Stream *s, lldb::DescriptionLevel level) { std::lock_guard guard(m_mutex); if (level >= eDescriptionLevelFull) { if (m_arch.IsValid()) s->Printf("(%s) ", m_arch.GetArchitectureName()); } if (level == eDescriptionLevelBrief) { const char *filename = m_file.GetFilename().GetCString(); if (filename) s->PutCString(filename); } else { char path[PATH_MAX]; if (m_file.GetPath(path, sizeof(path))) s->PutCString(path); } const char *object_name = m_object_name.GetCString(); if (object_name) s->Printf("(%s)", object_name); } void Module::ReportError(const char *format, ...) { if (format && format[0]) { StreamString strm; strm.PutCString("error: "); GetDescription(&strm, lldb::eDescriptionLevelBrief); strm.PutChar(' '); va_list args; va_start(args, format); strm.PrintfVarArg(format, args); va_end(args); const int format_len = strlen(format); if (format_len > 0) { const char last_char = format[format_len - 1]; if (last_char != '\n' || last_char != '\r') strm.EOL(); } Host::SystemLog(Host::eSystemLogError, "%s", strm.GetData()); } } bool Module::FileHasChanged() const { if (!m_file_has_changed) m_file_has_changed = (FileSystem::GetModificationTime(m_file) != m_mod_time); return m_file_has_changed; } void Module::ReportErrorIfModifyDetected(const char *format, ...) { if (!m_first_file_changed_log) { if (FileHasChanged()) { m_first_file_changed_log = true; if (format) { StreamString strm; strm.PutCString("error: the object file "); GetDescription(&strm, lldb::eDescriptionLevelFull); strm.PutCString(" has been modified\n"); va_list args; va_start(args, format); strm.PrintfVarArg(format, args); va_end(args); const int format_len = strlen(format); if (format_len > 0) { const char last_char = format[format_len - 1]; if (last_char != '\n' || last_char != '\r') strm.EOL(); } strm.PutCString("The debug session should be aborted as the original " "debug information has been overwritten.\n"); Host::SystemLog(Host::eSystemLogError, "%s", strm.GetData()); } } } } void Module::ReportWarning(const char *format, ...) { if (format && format[0]) { StreamString strm; strm.PutCString("warning: "); GetDescription(&strm, lldb::eDescriptionLevelFull); strm.PutChar(' '); va_list args; va_start(args, format); strm.PrintfVarArg(format, args); va_end(args); const int format_len = strlen(format); if (format_len > 0) { const char last_char = format[format_len - 1]; if (last_char != '\n' || last_char != '\r') strm.EOL(); } Host::SystemLog(Host::eSystemLogWarning, "%s", strm.GetData()); } } void Module::LogMessage(Log *log, const char *format, ...) { if (log != nullptr) { StreamString log_message; GetDescription(&log_message, lldb::eDescriptionLevelFull); log_message.PutCString(": "); va_list args; va_start(args, format); log_message.PrintfVarArg(format, args); va_end(args); log->PutCString(log_message.GetData()); } } void Module::LogMessageVerboseBacktrace(Log *log, const char *format, ...) { if (log != nullptr) { StreamString log_message; GetDescription(&log_message, lldb::eDescriptionLevelFull); log_message.PutCString(": "); va_list args; va_start(args, format); log_message.PrintfVarArg(format, args); va_end(args); if (log->GetVerbose()) { std::string back_trace; llvm::raw_string_ostream stream(back_trace); llvm::sys::PrintStackTrace(stream); log_message.PutCString(back_trace); } log->PutCString(log_message.GetData()); } } void Module::Dump(Stream *s) { std::lock_guard guard(m_mutex); // s->Printf("%.*p: ", (int)sizeof(void*) * 2, this); s->Indent(); s->Printf("Module %s%s%s%s\n", m_file.GetPath().c_str(), m_object_name ? "(" : "", m_object_name ? m_object_name.GetCString() : "", m_object_name ? ")" : ""); s->IndentMore(); ObjectFile *objfile = GetObjectFile(); if (objfile) objfile->Dump(s); SymbolVendor *symbols = GetSymbolVendor(); if (symbols) symbols->Dump(s); s->IndentLess(); } TypeList *Module::GetTypeList() { SymbolVendor *symbols = GetSymbolVendor(); if (symbols) return &symbols->GetTypeList(); return nullptr; } const ConstString &Module::GetObjectName() const { return m_object_name; } ObjectFile *Module::GetObjectFile() { if (!m_did_load_objfile.load()) { std::lock_guard guard(m_mutex); if (!m_did_load_objfile.load()) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "Module::GetObjectFile () module = %s", GetFileSpec().GetFilename().AsCString("")); DataBufferSP data_sp; lldb::offset_t data_offset = 0; const lldb::offset_t file_size = m_file.GetByteSize(); if (file_size > m_object_offset) { m_did_load_objfile = true; m_objfile_sp = ObjectFile::FindPlugin( shared_from_this(), &m_file, m_object_offset, file_size - m_object_offset, data_sp, data_offset); if (m_objfile_sp) { // Once we get the object file, update our module with the object // file's // architecture since it might differ in vendor/os if some parts were // unknown. But since the matching arch might already be more // specific // than the generic COFF architecture, only merge in those values that // overwrite unspecified unknown values. ArchSpec new_arch; m_objfile_sp->GetArchitecture(new_arch); m_arch.MergeFrom(new_arch); } else { ReportError("failed to load objfile for %s", GetFileSpec().GetPath().c_str()); } } } } return m_objfile_sp.get(); } SectionList *Module::GetSectionList() { // Populate m_unified_sections_ap with sections from objfile. if (!m_sections_ap) { ObjectFile *obj_file = GetObjectFile(); if (obj_file != nullptr) obj_file->CreateSections(*GetUnifiedSectionList()); } return m_sections_ap.get(); } void Module::SectionFileAddressesChanged() { ObjectFile *obj_file = GetObjectFile(); if (obj_file) obj_file->SectionFileAddressesChanged(); SymbolVendor *sym_vendor = GetSymbolVendor(); if (sym_vendor != nullptr) sym_vendor->SectionFileAddressesChanged(); } SectionList *Module::GetUnifiedSectionList() { // Populate m_unified_sections_ap with sections from objfile. if (!m_sections_ap) m_sections_ap.reset(new SectionList()); return m_sections_ap.get(); } const Symbol *Module::FindFirstSymbolWithNameAndType(const ConstString &name, SymbolType symbol_type) { Timer scoped_timer( LLVM_PRETTY_FUNCTION, "Module::FindFirstSymbolWithNameAndType (name = %s, type = %i)", name.AsCString(), symbol_type); SymbolVendor *sym_vendor = GetSymbolVendor(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) return symtab->FindFirstSymbolWithNameAndType( name, symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny); } return nullptr; } void Module::SymbolIndicesToSymbolContextList( Symtab *symtab, std::vector &symbol_indexes, SymbolContextList &sc_list) { // No need to protect this call using m_mutex all other method calls are // already thread safe. size_t num_indices = symbol_indexes.size(); if (num_indices > 0) { SymbolContext sc; CalculateSymbolContext(&sc); for (size_t i = 0; i < num_indices; i++) { sc.symbol = symtab->SymbolAtIndex(symbol_indexes[i]); if (sc.symbol) sc_list.Append(sc); } } } size_t Module::FindFunctionSymbols(const ConstString &name, uint32_t name_type_mask, SymbolContextList &sc_list) { Timer scoped_timer(LLVM_PRETTY_FUNCTION, "Module::FindSymbolsFunctions (name = %s, mask = 0x%8.8x)", name.AsCString(), name_type_mask); SymbolVendor *sym_vendor = GetSymbolVendor(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) return symtab->FindFunctionSymbols(name, name_type_mask, sc_list); } return 0; } size_t Module::FindSymbolsWithNameAndType(const ConstString &name, SymbolType symbol_type, SymbolContextList &sc_list) { // No need to protect this call using m_mutex all other method calls are // already thread safe. Timer scoped_timer( LLVM_PRETTY_FUNCTION, "Module::FindSymbolsWithNameAndType (name = %s, type = %i)", name.AsCString(), symbol_type); const size_t initial_size = sc_list.GetSize(); SymbolVendor *sym_vendor = GetSymbolVendor(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) { std::vector symbol_indexes; symtab->FindAllSymbolsWithNameAndType(name, symbol_type, symbol_indexes); SymbolIndicesToSymbolContextList(symtab, symbol_indexes, sc_list); } } return sc_list.GetSize() - initial_size; } size_t Module::FindSymbolsMatchingRegExAndType(const RegularExpression ®ex, SymbolType symbol_type, SymbolContextList &sc_list) { // No need to protect this call using m_mutex all other method calls are // already thread safe. Timer scoped_timer( LLVM_PRETTY_FUNCTION, "Module::FindSymbolsMatchingRegExAndType (regex = %s, type = %i)", regex.GetText().str().c_str(), symbol_type); const size_t initial_size = sc_list.GetSize(); SymbolVendor *sym_vendor = GetSymbolVendor(); if (sym_vendor) { Symtab *symtab = sym_vendor->GetSymtab(); if (symtab) { std::vector symbol_indexes; symtab->FindAllSymbolsMatchingRexExAndType( regex, symbol_type, Symtab::eDebugAny, Symtab::eVisibilityAny, symbol_indexes); SymbolIndicesToSymbolContextList(symtab, symbol_indexes, sc_list); } } return sc_list.GetSize() - initial_size; } void Module::SetSymbolFileFileSpec(const FileSpec &file) { if (!file.Exists()) return; if (m_symfile_ap) { // Remove any sections in the unified section list that come from the // current symbol vendor. SectionList *section_list = GetSectionList(); SymbolFile *symbol_file = m_symfile_ap->GetSymbolFile(); if (section_list && symbol_file) { ObjectFile *obj_file = symbol_file->GetObjectFile(); // Make sure we have an object file and that the symbol vendor's objfile // isn't // the same as the module's objfile before we remove any sections for // it... if (obj_file) { // Check to make sure we aren't trying to specify the file we already // have if (obj_file->GetFileSpec() == file) { // We are being told to add the exact same file that we already have // we don't have to do anything. return; } // Cleare the current symtab as we are going to replace it with a new // one obj_file->ClearSymtab(); // The symbol file might be a directory bundle ("/tmp/a.out.dSYM") // instead // of a full path to the symbol file within the bundle // ("/tmp/a.out.dSYM/Contents/Resources/DWARF/a.out"). So we need to // check this if (file.IsDirectory()) { std::string new_path(file.GetPath()); std::string old_path(obj_file->GetFileSpec().GetPath()); if (old_path.find(new_path) == 0) { // We specified the same bundle as the symbol file that we already // have return; } } if (obj_file != m_objfile_sp.get()) { size_t num_sections = section_list->GetNumSections(0); for (size_t idx = num_sections; idx > 0; --idx) { lldb::SectionSP section_sp( section_list->GetSectionAtIndex(idx - 1)); if (section_sp->GetObjectFile() == obj_file) { section_list->DeleteSection(idx - 1); } } } } } // Keep all old symbol files around in case there are any lingering type // references in // any SBValue objects that might have been handed out. m_old_symfiles.push_back(std::move(m_symfile_ap)); } m_symfile_spec = file; m_symfile_ap.reset(); m_did_load_symbol_vendor = false; } bool Module::IsExecutable() { if (GetObjectFile() == nullptr) return false; else return GetObjectFile()->IsExecutable(); } bool Module::IsLoadedInTarget(Target *target) { ObjectFile *obj_file = GetObjectFile(); if (obj_file) { SectionList *sections = GetSectionList(); if (sections != nullptr) { size_t num_sections = sections->GetSize(); for (size_t sect_idx = 0; sect_idx < num_sections; sect_idx++) { SectionSP section_sp = sections->GetSectionAtIndex(sect_idx); if (section_sp->GetLoadBaseAddress(target) != LLDB_INVALID_ADDRESS) { return true; } } } } return false; } bool Module::LoadScriptingResourceInTarget(Target *target, Error &error, Stream *feedback_stream) { if (!target) { error.SetErrorString("invalid destination Target"); return false; } LoadScriptFromSymFile should_load = target->TargetProperties::GetLoadScriptFromSymbolFile(); if (should_load == eLoadScriptFromSymFileFalse) return false; Debugger &debugger = target->GetDebugger(); const ScriptLanguage script_language = debugger.GetScriptLanguage(); if (script_language != eScriptLanguageNone) { PlatformSP platform_sp(target->GetPlatform()); if (!platform_sp) { error.SetErrorString("invalid Platform"); return false; } FileSpecList file_specs = platform_sp->LocateExecutableScriptingResources( target, *this, feedback_stream); const uint32_t num_specs = file_specs.GetSize(); if (num_specs) { ScriptInterpreter *script_interpreter = debugger.GetCommandInterpreter().GetScriptInterpreter(); if (script_interpreter) { for (uint32_t i = 0; i < num_specs; ++i) { FileSpec scripting_fspec(file_specs.GetFileSpecAtIndex(i)); if (scripting_fspec && scripting_fspec.Exists()) { if (should_load == eLoadScriptFromSymFileWarn) { if (feedback_stream) feedback_stream->Printf( "warning: '%s' contains a debug script. To run this script " "in " "this debug session:\n\n command script import " "\"%s\"\n\n" "To run all discovered debug scripts in this session:\n\n" " settings set target.load-script-from-symbol-file " "true\n", GetFileSpec().GetFileNameStrippingExtension().GetCString(), scripting_fspec.GetPath().c_str()); return false; } StreamString scripting_stream; scripting_fspec.Dump(&scripting_stream); const bool can_reload = true; const bool init_lldb_globals = false; bool did_load = script_interpreter->LoadScriptingModule( scripting_stream.GetData(), can_reload, init_lldb_globals, error); if (!did_load) return false; } } } else { error.SetErrorString("invalid ScriptInterpreter"); return false; } } } return true; } bool Module::SetArchitecture(const ArchSpec &new_arch) { if (!m_arch.IsValid()) { m_arch = new_arch; return true; } return m_arch.IsCompatibleMatch(new_arch); } bool Module::SetLoadAddress(Target &target, lldb::addr_t value, bool value_is_offset, bool &changed) { ObjectFile *object_file = GetObjectFile(); if (object_file != nullptr) { changed = object_file->SetLoadAddress(target, value, value_is_offset); return true; } else { changed = false; } return false; } bool Module::MatchesModuleSpec(const ModuleSpec &module_ref) { const UUID &uuid = module_ref.GetUUID(); if (uuid.IsValid()) { // If the UUID matches, then nothing more needs to match... return (uuid == GetUUID()); } const FileSpec &file_spec = module_ref.GetFileSpec(); if (file_spec) { if (!FileSpec::Equal(file_spec, m_file, (bool)file_spec.GetDirectory()) && !FileSpec::Equal(file_spec, m_platform_file, (bool)file_spec.GetDirectory())) return false; } const FileSpec &platform_file_spec = module_ref.GetPlatformFileSpec(); if (platform_file_spec) { if (!FileSpec::Equal(platform_file_spec, GetPlatformFileSpec(), (bool)platform_file_spec.GetDirectory())) return false; } const ArchSpec &arch = module_ref.GetArchitecture(); if (arch.IsValid()) { if (!m_arch.IsCompatibleMatch(arch)) return false; } const ConstString &object_name = module_ref.GetObjectName(); if (object_name) { if (object_name != GetObjectName()) return false; } return true; } bool Module::FindSourceFile(const FileSpec &orig_spec, FileSpec &new_spec) const { std::lock_guard guard(m_mutex); return m_source_mappings.FindFile(orig_spec, new_spec); } bool Module::RemapSourceFile(llvm::StringRef path, std::string &new_path) const { std::lock_guard guard(m_mutex); return m_source_mappings.RemapPath(path, new_path); } uint32_t Module::GetVersion(uint32_t *versions, uint32_t num_versions) { ObjectFile *obj_file = GetObjectFile(); if (obj_file) return obj_file->GetVersion(versions, num_versions); if (versions != nullptr && num_versions != 0) { for (uint32_t i = 0; i < num_versions; ++i) versions[i] = LLDB_INVALID_MODULE_VERSION; } return 0; } ModuleSP Module::CreateJITModule(const lldb::ObjectFileJITDelegateSP &delegate_sp) { if (delegate_sp) { // Must create a module and place it into a shared pointer before // we can create an object file since it has a std::weak_ptr back // to the module, so we need to control the creation carefully in // this static function ModuleSP module_sp(new Module()); module_sp->m_objfile_sp.reset(new ObjectFileJIT(module_sp, delegate_sp)); if (module_sp->m_objfile_sp) { // Once we get the object file, update our module with the object file's // architecture since it might differ in vendor/os if some parts were // unknown. module_sp->m_objfile_sp->GetArchitecture(module_sp->m_arch); } return module_sp; } return ModuleSP(); } bool Module::GetIsDynamicLinkEditor() { ObjectFile *obj_file = GetObjectFile(); if (obj_file) return obj_file->GetIsDynamicLinkEditor(); return false; } Index: vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp (revision 312182) +++ vendor/lldb/dist/source/Plugins/ExpressionParser/Clang/ASTResultSynthesizer.cpp (revision 312183) @@ -1,517 +1,516 @@ //===-- ASTResultSynthesizer.cpp --------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ASTResultSynthesizer.h" #include "ClangPersistentVariables.h" #include "lldb/Core/Log.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangASTImporter.h" #include "lldb/Target/Target.h" #include "lldb/Utility/LLDBAssert.h" #include "stdlib.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclGroup.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/Stmt.h" #include "clang/Parse/Parser.h" #include "clang/Sema/SemaDiagnostic.h" #include "llvm/Support/Casting.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace clang; using namespace lldb_private; ASTResultSynthesizer::ASTResultSynthesizer(ASTConsumer *passthrough, bool top_level, Target &target) : m_ast_context(NULL), m_passthrough(passthrough), m_passthrough_sema(NULL), m_target(target), m_sema(NULL), m_top_level(top_level) { if (!m_passthrough) return; m_passthrough_sema = dyn_cast(passthrough); } ASTResultSynthesizer::~ASTResultSynthesizer() {} void ASTResultSynthesizer::Initialize(ASTContext &Context) { m_ast_context = &Context; if (m_passthrough) m_passthrough->Initialize(Context); } void ASTResultSynthesizer::TransformTopLevelDecl(Decl *D) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (NamedDecl *named_decl = dyn_cast(D)) { if (log && log->GetVerbose()) { if (named_decl->getIdentifier()) log->Printf("TransformTopLevelDecl(%s)", named_decl->getIdentifier()->getNameStart()); else if (ObjCMethodDecl *method_decl = dyn_cast(D)) log->Printf("TransformTopLevelDecl(%s)", method_decl->getSelector().getAsString().c_str()); else log->Printf("TransformTopLevelDecl()"); } if (m_top_level) { RecordPersistentDecl(named_decl); } } if (LinkageSpecDecl *linkage_spec_decl = dyn_cast(D)) { RecordDecl::decl_iterator decl_iterator; for (decl_iterator = linkage_spec_decl->decls_begin(); decl_iterator != linkage_spec_decl->decls_end(); ++decl_iterator) { TransformTopLevelDecl(*decl_iterator); } } else if (!m_top_level) { if (ObjCMethodDecl *method_decl = dyn_cast(D)) { if (m_ast_context && !method_decl->getSelector().getAsString().compare("$__lldb_expr:")) { RecordPersistentTypes(method_decl); SynthesizeObjCMethodResult(method_decl); } } else if (FunctionDecl *function_decl = dyn_cast(D)) { if (m_ast_context && !function_decl->getNameInfo().getAsString().compare("$__lldb_expr")) { RecordPersistentTypes(function_decl); SynthesizeFunctionResult(function_decl); } } } } bool ASTResultSynthesizer::HandleTopLevelDecl(DeclGroupRef D) { DeclGroupRef::iterator decl_iterator; for (decl_iterator = D.begin(); decl_iterator != D.end(); ++decl_iterator) { Decl *decl = *decl_iterator; TransformTopLevelDecl(decl); } if (m_passthrough) return m_passthrough->HandleTopLevelDecl(D); return true; } bool ASTResultSynthesizer::SynthesizeFunctionResult(FunctionDecl *FunDecl) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (!m_sema) return false; FunctionDecl *function_decl = FunDecl; if (!function_decl) return false; if (log && log->GetVerbose()) { std::string s; raw_string_ostream os(s); function_decl->print(os); os.flush(); log->Printf("Untransformed function AST:\n%s", s.c_str()); } Stmt *function_body = function_decl->getBody(); CompoundStmt *compound_stmt = dyn_cast(function_body); bool ret = SynthesizeBodyResult(compound_stmt, function_decl); if (log && log->GetVerbose()) { std::string s; raw_string_ostream os(s); function_decl->print(os); os.flush(); log->Printf("Transformed function AST:\n%s", s.c_str()); } return ret; } bool ASTResultSynthesizer::SynthesizeObjCMethodResult( ObjCMethodDecl *MethodDecl) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (!m_sema) return false; if (!MethodDecl) return false; if (log && log->GetVerbose()) { std::string s; raw_string_ostream os(s); MethodDecl->print(os); os.flush(); log->Printf("Untransformed method AST:\n%s", s.c_str()); } Stmt *method_body = MethodDecl->getBody(); if (!method_body) return false; CompoundStmt *compound_stmt = dyn_cast(method_body); bool ret = SynthesizeBodyResult(compound_stmt, MethodDecl); if (log && log->GetVerbose()) { std::string s; raw_string_ostream os(s); MethodDecl->print(os); os.flush(); log->Printf("Transformed method AST:\n%s", s.c_str()); } return ret; } bool ASTResultSynthesizer::SynthesizeBodyResult(CompoundStmt *Body, DeclContext *DC) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); ASTContext &Ctx(*m_ast_context); if (!Body) return false; if (Body->body_empty()) return false; Stmt **last_stmt_ptr = Body->body_end() - 1; Stmt *last_stmt = *last_stmt_ptr; while (dyn_cast(last_stmt)) { if (last_stmt_ptr != Body->body_begin()) { last_stmt_ptr--; last_stmt = *last_stmt_ptr; } else { return false; } } Expr *last_expr = dyn_cast(last_stmt); if (!last_expr) // No auxiliary variable necessary; expression returns void return true; // In C++11, last_expr can be a LValueToRvalue implicit cast. Strip that off // if that's the // case. do { ImplicitCastExpr *implicit_cast = dyn_cast(last_expr); if (!implicit_cast) break; if (implicit_cast->getCastKind() != CK_LValueToRValue) break; last_expr = implicit_cast->getSubExpr(); } while (0); // is_lvalue is used to record whether the expression returns an assignable // Lvalue or an // Rvalue. This is relevant because they are handled differently. // // For Lvalues // // - In AST result synthesis (here!) the expression E is transformed into an // initialization // T *$__lldb_expr_result_ptr = &E. // // - In structure allocation, a pointer-sized slot is allocated in the // struct that is to be // passed into the expression. // // - In IR transformations, reads and writes to $__lldb_expr_result_ptr are // redirected at // an entry in the struct ($__lldb_arg) passed into the expression. // (Other persistent // variables are treated similarly, having been materialized as // references, but in those // cases the value of the reference itself is never modified.) // // - During materialization, $0 (the result persistent variable) is ignored. // // - During dematerialization, $0 is marked up as a load address with value // equal to the // contents of the structure entry. // // For Rvalues // // - In AST result synthesis the expression E is transformed into an // initialization // static T $__lldb_expr_result = E. // // - In structure allocation, a pointer-sized slot is allocated in the // struct that is to be // passed into the expression. // // - In IR transformations, an instruction is inserted at the beginning of // the function to // dereference the pointer resident in the slot. Reads and writes to // $__lldb_expr_result // are redirected at that dereferenced version. Guard variables for the // static variable // are excised. // // - During materialization, $0 (the result persistent variable) is // populated with the location // of a newly-allocated area of memory. // // - During dematerialization, $0 is ignored. bool is_lvalue = (last_expr->getValueKind() == VK_LValue || last_expr->getValueKind() == VK_XValue) && (last_expr->getObjectKind() == OK_Ordinary); QualType expr_qual_type = last_expr->getType(); const clang::Type *expr_type = expr_qual_type.getTypePtr(); if (!expr_type) return false; if (expr_type->isVoidType()) return true; if (log) { std::string s = expr_qual_type.getAsString(); log->Printf("Last statement is an %s with type: %s", (is_lvalue ? "lvalue" : "rvalue"), s.c_str()); } clang::VarDecl *result_decl = NULL; if (is_lvalue) { IdentifierInfo *result_ptr_id; if (expr_type->isFunctionType()) result_ptr_id = &Ctx.Idents.get("$__lldb_expr_result"); // functions actually should // be treated like function // pointers else result_ptr_id = &Ctx.Idents.get("$__lldb_expr_result_ptr"); m_sema->RequireCompleteType(SourceLocation(), expr_qual_type, clang::diag::err_incomplete_type); QualType ptr_qual_type; if (expr_qual_type->getAs() != NULL) ptr_qual_type = Ctx.getObjCObjectPointerType(expr_qual_type); else ptr_qual_type = Ctx.getPointerType(expr_qual_type); result_decl = VarDecl::Create(Ctx, DC, SourceLocation(), SourceLocation(), result_ptr_id, ptr_qual_type, NULL, SC_Static); if (!result_decl) return false; ExprResult address_of_expr = m_sema->CreateBuiltinUnaryOp(SourceLocation(), UO_AddrOf, last_expr); if (address_of_expr.get()) - m_sema->AddInitializerToDecl(result_decl, address_of_expr.get(), true, - false); + m_sema->AddInitializerToDecl(result_decl, address_of_expr.get(), true); else return false; } else { IdentifierInfo &result_id = Ctx.Idents.get("$__lldb_expr_result"); result_decl = VarDecl::Create(Ctx, DC, SourceLocation(), SourceLocation(), &result_id, expr_qual_type, NULL, SC_Static); if (!result_decl) return false; - m_sema->AddInitializerToDecl(result_decl, last_expr, true, false); + m_sema->AddInitializerToDecl(result_decl, last_expr, true); } DC->addDecl(result_decl); /////////////////////////////// // call AddInitializerToDecl // // m_sema->AddInitializerToDecl(result_decl, last_expr); ///////////////////////////////// // call ConvertDeclToDeclGroup // Sema::DeclGroupPtrTy result_decl_group_ptr; result_decl_group_ptr = m_sema->ConvertDeclToDeclGroup(result_decl); //////////////////////// // call ActOnDeclStmt // StmtResult result_initialization_stmt_result(m_sema->ActOnDeclStmt( result_decl_group_ptr, SourceLocation(), SourceLocation())); //////////////////////////////////////////////// // replace the old statement with the new one // *last_stmt_ptr = reinterpret_cast(result_initialization_stmt_result.get()); return true; } void ASTResultSynthesizer::HandleTranslationUnit(ASTContext &Ctx) { if (m_passthrough) m_passthrough->HandleTranslationUnit(Ctx); } void ASTResultSynthesizer::RecordPersistentTypes(DeclContext *FunDeclCtx) { typedef DeclContext::specific_decl_iterator TypeDeclIterator; for (TypeDeclIterator i = TypeDeclIterator(FunDeclCtx->decls_begin()), e = TypeDeclIterator(FunDeclCtx->decls_end()); i != e; ++i) { MaybeRecordPersistentType(*i); } } void ASTResultSynthesizer::MaybeRecordPersistentType(TypeDecl *D) { if (!D->getIdentifier()) return; StringRef name = D->getName(); if (name.size() == 0 || name[0] != '$') return; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); ConstString name_cs(name.str().c_str()); if (log) log->Printf("Recording persistent type %s\n", name_cs.GetCString()); m_decls.push_back(D); } void ASTResultSynthesizer::RecordPersistentDecl(NamedDecl *D) { lldbassert(m_top_level); if (!D->getIdentifier()) return; StringRef name = D->getName(); if (name.size() == 0) return; Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); ConstString name_cs(name.str().c_str()); if (log) log->Printf("Recording persistent decl %s\n", name_cs.GetCString()); m_decls.push_back(D); } void ASTResultSynthesizer::CommitPersistentDecls() { for (clang::NamedDecl *decl : m_decls) { StringRef name = decl->getName(); ConstString name_cs(name.str().c_str()); Decl *D_scratch = m_target.GetClangASTImporter()->DeportDecl( m_target.GetScratchClangASTContext()->getASTContext(), m_ast_context, decl); if (!D_scratch) { Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS)); if (log) { std::string s; llvm::raw_string_ostream ss(s); decl->dump(ss); ss.flush(); log->Printf("Couldn't commit persistent decl: %s\n", s.c_str()); } continue; } if (NamedDecl *NamedDecl_scratch = dyn_cast(D_scratch)) llvm::cast( m_target.GetPersistentExpressionStateForLanguage( lldb::eLanguageTypeC)) ->RegisterPersistentDecl(name_cs, NamedDecl_scratch); } } void ASTResultSynthesizer::HandleTagDeclDefinition(TagDecl *D) { if (m_passthrough) m_passthrough->HandleTagDeclDefinition(D); } void ASTResultSynthesizer::CompleteTentativeDefinition(VarDecl *D) { if (m_passthrough) m_passthrough->CompleteTentativeDefinition(D); } void ASTResultSynthesizer::HandleVTable(CXXRecordDecl *RD) { if (m_passthrough) m_passthrough->HandleVTable(RD); } void ASTResultSynthesizer::PrintStats() { if (m_passthrough) m_passthrough->PrintStats(); } void ASTResultSynthesizer::InitializeSema(Sema &S) { m_sema = &S; if (m_passthrough_sema) m_passthrough_sema->InitializeSema(S); } void ASTResultSynthesizer::ForgetSema() { m_sema = NULL; if (m_passthrough_sema) m_passthrough_sema->ForgetSema(); } Index: vendor/lldb/dist/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp =================================================================== --- vendor/lldb/dist/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp (revision 312182) +++ vendor/lldb/dist/source/Plugins/InstrumentationRuntime/ThreadSanitizer/ThreadSanitizerRuntime.cpp (revision 312183) @@ -1,1020 +1,1021 @@ //===-- ThreadSanitizerRuntime.cpp ------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "ThreadSanitizerRuntime.h" #include "Plugins/Process/Utility/HistoryThread.h" #include "lldb/Breakpoint/StoppointCallbackContext.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginInterface.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/Stream.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/ValueObject.h" #include "lldb/Expression/UserExpression.h" #include "lldb/Interpreter/CommandReturnObject.h" #include "lldb/Symbol/Symbol.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/Variable.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/InstrumentationRuntimeStopInfo.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/StopInfo.h" #include "lldb/Target/Target.h" #include "lldb/Target/Thread.h" using namespace lldb; using namespace lldb_private; lldb::InstrumentationRuntimeSP ThreadSanitizerRuntime::CreateInstance(const lldb::ProcessSP &process_sp) { return InstrumentationRuntimeSP(new ThreadSanitizerRuntime(process_sp)); } void ThreadSanitizerRuntime::Initialize() { PluginManager::RegisterPlugin( GetPluginNameStatic(), "ThreadSanitizer instrumentation runtime plugin.", CreateInstance, GetTypeStatic); } void ThreadSanitizerRuntime::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } lldb_private::ConstString ThreadSanitizerRuntime::GetPluginNameStatic() { return ConstString("ThreadSanitizer"); } lldb::InstrumentationRuntimeType ThreadSanitizerRuntime::GetTypeStatic() { return eInstrumentationRuntimeTypeThreadSanitizer; } ThreadSanitizerRuntime::~ThreadSanitizerRuntime() { Deactivate(); } static constexpr std::chrono::seconds g_retrieve_data_function_timeout(2); const char *thread_sanitizer_retrieve_report_data_prefix = R"( extern "C" { void *__tsan_get_current_report(); int __tsan_get_report_data(void *report, const char **description, int *count, int *stack_count, int *mop_count, int *loc_count, int *mutex_count, int *thread_count, int *unique_tid_count, void **sleep_trace, unsigned long trace_size); int __tsan_get_report_stack(void *report, unsigned long idx, void **trace, unsigned long trace_size); int __tsan_get_report_mop(void *report, unsigned long idx, int *tid, void **addr, int *size, int *write, int *atomic, void **trace, unsigned long trace_size); int __tsan_get_report_loc(void *report, unsigned long idx, const char **type, void **addr, unsigned long *start, unsigned long *size, int *tid, int *fd, int *suppressable, void **trace, unsigned long trace_size); int __tsan_get_report_mutex(void *report, unsigned long idx, unsigned long *mutex_id, void **addr, int *destroyed, void **trace, unsigned long trace_size); int __tsan_get_report_thread(void *report, unsigned long idx, int *tid, unsigned long *os_id, int *running, const char **name, int *parent_tid, void **trace, unsigned long trace_size); int __tsan_get_report_unique_tid(void *report, unsigned long idx, int *tid); } const int REPORT_TRACE_SIZE = 128; const int REPORT_ARRAY_SIZE = 4; struct data { void *report; const char *description; int report_count; void *sleep_trace[REPORT_TRACE_SIZE]; int stack_count; struct { int idx; void *trace[REPORT_TRACE_SIZE]; } stacks[REPORT_ARRAY_SIZE]; int mop_count; struct { int idx; int tid; int size; int write; int atomic; void *addr; void *trace[REPORT_TRACE_SIZE]; } mops[REPORT_ARRAY_SIZE]; int loc_count; struct { int idx; const char *type; void *addr; unsigned long start; unsigned long size; int tid; int fd; int suppressable; void *trace[REPORT_TRACE_SIZE]; } locs[REPORT_ARRAY_SIZE]; int mutex_count; struct { int idx; unsigned long mutex_id; void *addr; int destroyed; void *trace[REPORT_TRACE_SIZE]; } mutexes[REPORT_ARRAY_SIZE]; int thread_count; struct { int idx; int tid; unsigned long os_id; int running; const char *name; int parent_tid; void *trace[REPORT_TRACE_SIZE]; } threads[REPORT_ARRAY_SIZE]; int unique_tid_count; struct { int idx; int tid; } unique_tids[REPORT_ARRAY_SIZE]; }; )"; const char *thread_sanitizer_retrieve_report_data_command = R"( data t = {0}; t.report = __tsan_get_current_report(); __tsan_get_report_data(t.report, &t.description, &t.report_count, &t.stack_count, &t.mop_count, &t.loc_count, &t.mutex_count, &t.thread_count, &t.unique_tid_count, t.sleep_trace, REPORT_TRACE_SIZE); if (t.stack_count > REPORT_ARRAY_SIZE) t.stack_count = REPORT_ARRAY_SIZE; for (int i = 0; i < t.stack_count; i++) { t.stacks[i].idx = i; __tsan_get_report_stack(t.report, i, t.stacks[i].trace, REPORT_TRACE_SIZE); } if (t.mop_count > REPORT_ARRAY_SIZE) t.mop_count = REPORT_ARRAY_SIZE; for (int i = 0; i < t.mop_count; i++) { t.mops[i].idx = i; __tsan_get_report_mop(t.report, i, &t.mops[i].tid, &t.mops[i].addr, &t.mops[i].size, &t.mops[i].write, &t.mops[i].atomic, t.mops[i].trace, REPORT_TRACE_SIZE); } if (t.loc_count > REPORT_ARRAY_SIZE) t.loc_count = REPORT_ARRAY_SIZE; for (int i = 0; i < t.loc_count; i++) { t.locs[i].idx = i; __tsan_get_report_loc(t.report, i, &t.locs[i].type, &t.locs[i].addr, &t.locs[i].start, &t.locs[i].size, &t.locs[i].tid, &t.locs[i].fd, &t.locs[i].suppressable, t.locs[i].trace, REPORT_TRACE_SIZE); } if (t.mutex_count > REPORT_ARRAY_SIZE) t.mutex_count = REPORT_ARRAY_SIZE; for (int i = 0; i < t.mutex_count; i++) { t.mutexes[i].idx = i; __tsan_get_report_mutex(t.report, i, &t.mutexes[i].mutex_id, &t.mutexes[i].addr, &t.mutexes[i].destroyed, t.mutexes[i].trace, REPORT_TRACE_SIZE); } if (t.thread_count > REPORT_ARRAY_SIZE) t.thread_count = REPORT_ARRAY_SIZE; for (int i = 0; i < t.thread_count; i++) { t.threads[i].idx = i; __tsan_get_report_thread(t.report, i, &t.threads[i].tid, &t.threads[i].os_id, &t.threads[i].running, &t.threads[i].name, &t.threads[i].parent_tid, t.threads[i].trace, REPORT_TRACE_SIZE); } if (t.unique_tid_count > REPORT_ARRAY_SIZE) t.unique_tid_count = REPORT_ARRAY_SIZE; for (int i = 0; i < t.unique_tid_count; i++) { t.unique_tids[i].idx = i; __tsan_get_report_unique_tid(t.report, i, &t.unique_tids[i].tid); } t; )"; static StructuredData::Array * CreateStackTrace(ValueObjectSP o, const std::string &trace_item_name = ".trace") { StructuredData::Array *trace = new StructuredData::Array(); ValueObjectSP trace_value_object = o->GetValueForExpressionPath(trace_item_name.c_str()); - for (int j = 0; j < 8; j++) { + size_t count = trace_value_object->GetNumChildren(); + for (size_t j = 0; j < count; j++) { addr_t trace_addr = trace_value_object->GetChildAtIndex(j, true)->GetValueAsUnsigned(0); if (trace_addr == 0) break; trace->AddItem( StructuredData::ObjectSP(new StructuredData::Integer(trace_addr))); } return trace; } static StructuredData::Array *ConvertToStructuredArray( ValueObjectSP return_value_sp, const std::string &items_name, const std::string &count_name, std::function const &callback) { StructuredData::Array *array = new StructuredData::Array(); unsigned int count = return_value_sp->GetValueForExpressionPath(count_name.c_str()) ->GetValueAsUnsigned(0); ValueObjectSP objects = return_value_sp->GetValueForExpressionPath(items_name.c_str()); for (unsigned int i = 0; i < count; i++) { ValueObjectSP o = objects->GetChildAtIndex(i, true); StructuredData::Dictionary *dict = new StructuredData::Dictionary(); callback(o, dict); array->AddItem(StructuredData::ObjectSP(dict)); } return array; } static std::string RetrieveString(ValueObjectSP return_value_sp, ProcessSP process_sp, const std::string &expression_path) { addr_t ptr = return_value_sp->GetValueForExpressionPath(expression_path.c_str()) ->GetValueAsUnsigned(0); std::string str; Error error; process_sp->ReadCStringFromMemory(ptr, str, error); return str; } static void GetRenumberedThreadIds(ProcessSP process_sp, ValueObjectSP data, std::map &thread_id_map) { ConvertToStructuredArray( data, ".threads", ".thread_count", [process_sp, &thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) { uint64_t thread_id = o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0); uint64_t thread_os_id = o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0); user_id_t lldb_user_id = 0; bool can_update = true; ThreadSP lldb_thread = process_sp->GetThreadList().FindThreadByID( thread_os_id, can_update); if (lldb_thread) { lldb_user_id = lldb_thread->GetIndexID(); } else { // This isn't a live thread anymore. Ask process to assign a new // Index ID (or return an old one if we've already seen this // thread_os_id). // It will also make sure that no new threads are assigned this Index // ID. lldb_user_id = process_sp->AssignIndexIDToThread(thread_os_id); } thread_id_map[thread_id] = lldb_user_id; }); } static user_id_t Renumber(uint64_t id, std::map &thread_id_map) { auto IT = thread_id_map.find(id); if (IT == thread_id_map.end()) return 0; return IT->second; } StructuredData::ObjectSP ThreadSanitizerRuntime::RetrieveReportData(ExecutionContextRef exe_ctx_ref) { ProcessSP process_sp = GetProcessSP(); if (!process_sp) return StructuredData::ObjectSP(); ThreadSP thread_sp = exe_ctx_ref.GetThreadSP(); StackFrameSP frame_sp = thread_sp->GetSelectedFrame(); if (!frame_sp) return StructuredData::ObjectSP(); EvaluateExpressionOptions options; options.SetUnwindOnError(true); options.SetTryAllThreads(true); options.SetStopOthers(true); options.SetIgnoreBreakpoints(true); options.SetTimeout(g_retrieve_data_function_timeout); options.SetPrefix(thread_sanitizer_retrieve_report_data_prefix); options.SetAutoApplyFixIts(false); options.SetLanguage(eLanguageTypeObjC_plus_plus); ValueObjectSP main_value; ExecutionContext exe_ctx; Error eval_error; frame_sp->CalculateExecutionContext(exe_ctx); ExpressionResults result = UserExpression::Evaluate( exe_ctx, options, thread_sanitizer_retrieve_report_data_command, "", main_value, eval_error); if (result != eExpressionCompleted) { process_sp->GetTarget().GetDebugger().GetAsyncOutputStream()->Printf( "Warning: Cannot evaluate ThreadSanitizer expression:\n%s\n", eval_error.AsCString()); return StructuredData::ObjectSP(); } std::map thread_id_map; GetRenumberedThreadIds(process_sp, main_value, thread_id_map); StructuredData::Dictionary *dict = new StructuredData::Dictionary(); dict->AddStringItem("instrumentation_class", "ThreadSanitizer"); dict->AddStringItem("issue_type", RetrieveString(main_value, process_sp, ".description")); dict->AddIntegerItem("report_count", main_value->GetValueForExpressionPath(".report_count") ->GetValueAsUnsigned(0)); dict->AddItem("sleep_trace", StructuredData::ObjectSP(CreateStackTrace( main_value, ".sleep_trace"))); StructuredData::Array *stacks = ConvertToStructuredArray( main_value, ".stacks", ".stack_count", [thread_sp](ValueObjectSP o, StructuredData::Dictionary *dict) { dict->AddIntegerItem( "index", o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); // "stacks" happen on the current thread dict->AddIntegerItem("thread_id", thread_sp->GetIndexID()); }); dict->AddItem("stacks", StructuredData::ObjectSP(stacks)); StructuredData::Array *mops = ConvertToStructuredArray( main_value, ".mops", ".mop_count", [&thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) { dict->AddIntegerItem( "index", o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "thread_id", Renumber( o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), thread_id_map)); dict->AddIntegerItem( "size", o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0)); dict->AddBooleanItem( "is_write", o->GetValueForExpressionPath(".write")->GetValueAsUnsigned(0)); dict->AddBooleanItem( "is_atomic", o->GetValueForExpressionPath(".atomic")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "address", o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0)); dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); }); dict->AddItem("mops", StructuredData::ObjectSP(mops)); StructuredData::Array *locs = ConvertToStructuredArray( main_value, ".locs", ".loc_count", [process_sp, &thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) { dict->AddIntegerItem( "index", o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); dict->AddStringItem("type", RetrieveString(o, process_sp, ".type")); dict->AddIntegerItem( "address", o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "start", o->GetValueForExpressionPath(".start")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "size", o->GetValueForExpressionPath(".size")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "thread_id", Renumber( o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), thread_id_map)); dict->AddIntegerItem( "file_descriptor", o->GetValueForExpressionPath(".fd")->GetValueAsUnsigned(0)); dict->AddIntegerItem("suppressable", o->GetValueForExpressionPath(".suppressable") ->GetValueAsUnsigned(0)); dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); }); dict->AddItem("locs", StructuredData::ObjectSP(locs)); StructuredData::Array *mutexes = ConvertToStructuredArray( main_value, ".mutexes", ".mutex_count", [](ValueObjectSP o, StructuredData::Dictionary *dict) { dict->AddIntegerItem( "index", o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "mutex_id", o->GetValueForExpressionPath(".mutex_id")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "address", o->GetValueForExpressionPath(".addr")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "destroyed", o->GetValueForExpressionPath(".destroyed")->GetValueAsUnsigned(0)); dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); }); dict->AddItem("mutexes", StructuredData::ObjectSP(mutexes)); StructuredData::Array *threads = ConvertToStructuredArray( main_value, ".threads", ".thread_count", [process_sp, &thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) { dict->AddIntegerItem( "index", o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "thread_id", Renumber( o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), thread_id_map)); dict->AddIntegerItem( "thread_os_id", o->GetValueForExpressionPath(".os_id")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "running", o->GetValueForExpressionPath(".running")->GetValueAsUnsigned(0)); dict->AddStringItem("name", RetrieveString(o, process_sp, ".name")); dict->AddIntegerItem( "parent_thread_id", Renumber(o->GetValueForExpressionPath(".parent_tid") ->GetValueAsUnsigned(0), thread_id_map)); dict->AddItem("trace", StructuredData::ObjectSP(CreateStackTrace(o))); }); dict->AddItem("threads", StructuredData::ObjectSP(threads)); StructuredData::Array *unique_tids = ConvertToStructuredArray( main_value, ".unique_tids", ".unique_tid_count", [&thread_id_map](ValueObjectSP o, StructuredData::Dictionary *dict) { dict->AddIntegerItem( "index", o->GetValueForExpressionPath(".idx")->GetValueAsUnsigned(0)); dict->AddIntegerItem( "tid", Renumber( o->GetValueForExpressionPath(".tid")->GetValueAsUnsigned(0), thread_id_map)); }); dict->AddItem("unique_tids", StructuredData::ObjectSP(unique_tids)); return StructuredData::ObjectSP(dict); } std::string ThreadSanitizerRuntime::FormatDescription(StructuredData::ObjectSP report) { std::string description = report->GetAsDictionary() ->GetValueForKey("issue_type") ->GetAsString() ->GetValue(); if (description == "data-race") { return "Data race"; } else if (description == "data-race-vptr") { return "Data race on C++ virtual pointer"; } else if (description == "heap-use-after-free") { return "Use of deallocated memory"; } else if (description == "heap-use-after-free-vptr") { return "Use of deallocated C++ virtual pointer"; } else if (description == "thread-leak") { return "Thread leak"; } else if (description == "locked-mutex-destroy") { return "Destruction of a locked mutex"; } else if (description == "mutex-double-lock") { return "Double lock of a mutex"; } else if (description == "mutex-invalid-access") { return "Use of an uninitialized or destroyed mutex"; } else if (description == "mutex-bad-unlock") { return "Unlock of an unlocked mutex (or by a wrong thread)"; } else if (description == "mutex-bad-read-lock") { return "Read lock of a write locked mutex"; } else if (description == "mutex-bad-read-unlock") { return "Read unlock of a write locked mutex"; } else if (description == "signal-unsafe-call") { return "Signal-unsafe call inside a signal handler"; } else if (description == "errno-in-signal-handler") { return "Overwrite of errno in a signal handler"; } else if (description == "lock-order-inversion") { return "Lock order inversion (potential deadlock)"; } // for unknown report codes just show the code return description; } static std::string Sprintf(const char *format, ...) { StreamString s; va_list args; va_start(args, format); s.PrintfVarArg(format, args); va_end(args); return s.GetString(); } static std::string GetSymbolNameFromAddress(ProcessSP process_sp, addr_t addr) { lldb_private::Address so_addr; if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) return ""; lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol(); if (!symbol) return ""; std::string sym_name = symbol->GetName().GetCString(); return sym_name; } static void GetSymbolDeclarationFromAddress(ProcessSP process_sp, addr_t addr, Declaration &decl) { lldb_private::Address so_addr; if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress(addr, so_addr)) return; lldb_private::Symbol *symbol = so_addr.CalculateSymbolContextSymbol(); if (!symbol) return; ConstString sym_name = symbol->GetMangled().GetName( lldb::eLanguageTypeUnknown, Mangled::ePreferMangled); ModuleSP module = symbol->CalculateSymbolContextModule(); if (!module) return; VariableList var_list; module->FindGlobalVariables(sym_name, nullptr, true, 1U, var_list); if (var_list.GetSize() < 1) return; VariableSP var = var_list.GetVariableAtIndex(0); decl = var->GetDeclaration(); } addr_t ThreadSanitizerRuntime::GetFirstNonInternalFramePc( StructuredData::ObjectSP trace) { ProcessSP process_sp = GetProcessSP(); ModuleSP runtime_module_sp = GetRuntimeModuleSP(); addr_t result = 0; trace->GetAsArray()->ForEach([process_sp, runtime_module_sp, &result](StructuredData::Object *o) -> bool { addr_t addr = o->GetIntegerValue(); lldb_private::Address so_addr; if (!process_sp->GetTarget().GetSectionLoadList().ResolveLoadAddress( addr, so_addr)) return true; if (so_addr.GetModule() == runtime_module_sp) return true; result = addr; return false; }); return result; } std::string ThreadSanitizerRuntime::GenerateSummary(StructuredData::ObjectSP report) { ProcessSP process_sp = GetProcessSP(); std::string summary = report->GetAsDictionary() ->GetValueForKey("description") ->GetAsString() ->GetValue(); addr_t pc = 0; if (report->GetAsDictionary() ->GetValueForKey("mops") ->GetAsArray() ->GetSize() > 0) pc = GetFirstNonInternalFramePc(report->GetAsDictionary() ->GetValueForKey("mops") ->GetAsArray() ->GetItemAtIndex(0) ->GetAsDictionary() ->GetValueForKey("trace")); if (report->GetAsDictionary() ->GetValueForKey("stacks") ->GetAsArray() ->GetSize() > 0) pc = GetFirstNonInternalFramePc(report->GetAsDictionary() ->GetValueForKey("stacks") ->GetAsArray() ->GetItemAtIndex(0) ->GetAsDictionary() ->GetValueForKey("trace")); if (pc != 0) { summary = summary + " in " + GetSymbolNameFromAddress(process_sp, pc); } if (report->GetAsDictionary() ->GetValueForKey("locs") ->GetAsArray() ->GetSize() > 0) { StructuredData::ObjectSP loc = report->GetAsDictionary() ->GetValueForKey("locs") ->GetAsArray() ->GetItemAtIndex(0); addr_t addr = loc->GetAsDictionary() ->GetValueForKey("address") ->GetAsInteger() ->GetValue(); if (addr == 0) addr = loc->GetAsDictionary() ->GetValueForKey("start") ->GetAsInteger() ->GetValue(); if (addr != 0) { std::string global_name = GetSymbolNameFromAddress(process_sp, addr); if (!global_name.empty()) { summary = summary + " at " + global_name; } else { summary = summary + " at " + Sprintf("0x%llx", addr); } } else { int fd = loc->GetAsDictionary() ->GetValueForKey("file_descriptor") ->GetAsInteger() ->GetValue(); if (fd != 0) { summary = summary + " on file descriptor " + Sprintf("%d", fd); } } } return summary; } addr_t ThreadSanitizerRuntime::GetMainRacyAddress(StructuredData::ObjectSP report) { addr_t result = (addr_t)-1; report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach( [&result](StructuredData::Object *o) -> bool { addr_t addr = o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); if (addr < result) result = addr; return true; }); return (result == (addr_t)-1) ? 0 : result; } std::string ThreadSanitizerRuntime::GetLocationDescription( StructuredData::ObjectSP report, addr_t &global_addr, std::string &global_name, std::string &filename, uint32_t &line) { std::string result = ""; ProcessSP process_sp = GetProcessSP(); if (report->GetAsDictionary() ->GetValueForKey("locs") ->GetAsArray() ->GetSize() > 0) { StructuredData::ObjectSP loc = report->GetAsDictionary() ->GetValueForKey("locs") ->GetAsArray() ->GetItemAtIndex(0); std::string type = loc->GetAsDictionary()->GetValueForKey("type")->GetStringValue(); if (type == "global") { global_addr = loc->GetAsDictionary() ->GetValueForKey("address") ->GetAsInteger() ->GetValue(); global_name = GetSymbolNameFromAddress(process_sp, global_addr); if (!global_name.empty()) { result = Sprintf("'%s' is a global variable (0x%llx)", global_name.c_str(), global_addr); } else { result = Sprintf("0x%llx is a global variable", global_addr); } Declaration decl; GetSymbolDeclarationFromAddress(process_sp, global_addr, decl); if (decl.GetFile()) { filename = decl.GetFile().GetPath(); line = decl.GetLine(); } } else if (type == "heap") { addr_t addr = loc->GetAsDictionary() ->GetValueForKey("start") ->GetAsInteger() ->GetValue(); long size = loc->GetAsDictionary() ->GetValueForKey("size") ->GetAsInteger() ->GetValue(); result = Sprintf("Location is a %ld-byte heap object at 0x%llx", size, addr); } else if (type == "stack") { int tid = loc->GetAsDictionary() ->GetValueForKey("thread_id") ->GetAsInteger() ->GetValue(); result = Sprintf("Location is stack of thread %d", tid); } else if (type == "tls") { int tid = loc->GetAsDictionary() ->GetValueForKey("thread_id") ->GetAsInteger() ->GetValue(); result = Sprintf("Location is TLS of thread %d", tid); } else if (type == "fd") { int fd = loc->GetAsDictionary() ->GetValueForKey("file_descriptor") ->GetAsInteger() ->GetValue(); result = Sprintf("Location is file descriptor %d", fd); } } return result; } bool ThreadSanitizerRuntime::NotifyBreakpointHit( void *baton, StoppointCallbackContext *context, user_id_t break_id, user_id_t break_loc_id) { assert(baton && "null baton"); if (!baton) return false; ThreadSanitizerRuntime *const instance = static_cast(baton); StructuredData::ObjectSP report = instance->RetrieveReportData(context->exe_ctx_ref); std::string stop_reason_description; if (report) { std::string issue_description = instance->FormatDescription(report); report->GetAsDictionary()->AddStringItem("description", issue_description); stop_reason_description = issue_description + " detected"; report->GetAsDictionary()->AddStringItem("stop_description", stop_reason_description); std::string summary = instance->GenerateSummary(report); report->GetAsDictionary()->AddStringItem("summary", summary); addr_t main_address = instance->GetMainRacyAddress(report); report->GetAsDictionary()->AddIntegerItem("memory_address", main_address); addr_t global_addr = 0; std::string global_name = ""; std::string location_filename = ""; uint32_t location_line = 0; std::string location_description = instance->GetLocationDescription( report, global_addr, global_name, location_filename, location_line); report->GetAsDictionary()->AddStringItem("location_description", location_description); if (global_addr != 0) { report->GetAsDictionary()->AddIntegerItem("global_address", global_addr); } if (!global_name.empty()) { report->GetAsDictionary()->AddStringItem("global_name", global_name); } if (location_filename != "") { report->GetAsDictionary()->AddStringItem("location_filename", location_filename); report->GetAsDictionary()->AddIntegerItem("location_line", location_line); } bool all_addresses_are_same = true; report->GetObjectForDotSeparatedPath("mops")->GetAsArray()->ForEach( [&all_addresses_are_same, main_address](StructuredData::Object *o) -> bool { addr_t addr = o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); if (main_address != addr) all_addresses_are_same = false; return true; }); report->GetAsDictionary()->AddBooleanItem("all_addresses_are_same", all_addresses_are_same); } ProcessSP process_sp = instance->GetProcessSP(); // Make sure this is the right process if (process_sp && process_sp == context->exe_ctx_ref.GetProcessSP()) { ThreadSP thread_sp = context->exe_ctx_ref.GetThreadSP(); if (thread_sp) thread_sp->SetStopInfo( InstrumentationRuntimeStopInfo:: CreateStopReasonWithInstrumentationData( *thread_sp, stop_reason_description, report)); StreamFileSP stream_sp( process_sp->GetTarget().GetDebugger().GetOutputFile()); if (stream_sp) { stream_sp->Printf("ThreadSanitizer report breakpoint hit. Use 'thread " "info -s' to get extended information about the " "report.\n"); } return true; // Return true to stop the target } else return false; // Let target run } const RegularExpression &ThreadSanitizerRuntime::GetPatternForRuntimeLibrary() { static RegularExpression regex(llvm::StringRef("libclang_rt.tsan_")); return regex; } bool ThreadSanitizerRuntime::CheckIfRuntimeIsValid( const lldb::ModuleSP module_sp) { static ConstString g_tsan_get_current_report("__tsan_get_current_report"); const Symbol *symbol = module_sp->FindFirstSymbolWithNameAndType( g_tsan_get_current_report, lldb::eSymbolTypeAny); return symbol != nullptr; } void ThreadSanitizerRuntime::Activate() { if (IsActive()) return; ProcessSP process_sp = GetProcessSP(); if (!process_sp) return; ConstString symbol_name("__tsan_on_report"); const Symbol *symbol = GetRuntimeModuleSP()->FindFirstSymbolWithNameAndType( symbol_name, eSymbolTypeCode); if (symbol == NULL) return; if (!symbol->ValueIsAddress() || !symbol->GetAddressRef().IsValid()) return; Target &target = process_sp->GetTarget(); addr_t symbol_address = symbol->GetAddressRef().GetOpcodeLoadAddress(&target); if (symbol_address == LLDB_INVALID_ADDRESS) return; bool internal = true; bool hardware = false; Breakpoint *breakpoint = process_sp->GetTarget() .CreateBreakpoint(symbol_address, internal, hardware) .get(); breakpoint->SetCallback(ThreadSanitizerRuntime::NotifyBreakpointHit, this, true); breakpoint->SetBreakpointKind("thread-sanitizer-report"); SetBreakpointID(breakpoint->GetID()); StreamFileSP stream_sp(process_sp->GetTarget().GetDebugger().GetOutputFile()); if (stream_sp) { stream_sp->Printf("ThreadSanitizer debugger support is active.\n"); } SetActive(true); } void ThreadSanitizerRuntime::Deactivate() { if (GetBreakpointID() != LLDB_INVALID_BREAK_ID) { ProcessSP process_sp = GetProcessSP(); if (process_sp) { process_sp->GetTarget().RemoveBreakpointByID(GetBreakpointID()); SetBreakpointID(LLDB_INVALID_BREAK_ID); } } SetActive(false); } static std::string GenerateThreadName(const std::string &path, StructuredData::Object *o, StructuredData::ObjectSP main_info) { std::string result = "additional information"; if (path == "mops") { int size = o->GetObjectForDotSeparatedPath("size")->GetIntegerValue(); int thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); bool is_write = o->GetObjectForDotSeparatedPath("is_write")->GetBooleanValue(); bool is_atomic = o->GetObjectForDotSeparatedPath("is_atomic")->GetBooleanValue(); addr_t addr = o->GetObjectForDotSeparatedPath("address")->GetIntegerValue(); std::string addr_string = Sprintf(" at 0x%llx", addr); if (main_info->GetObjectForDotSeparatedPath("all_addresses_are_same") ->GetBooleanValue()) { addr_string = ""; } result = Sprintf("%s%s of size %d%s by thread %d", is_atomic ? "atomic " : "", is_write ? "write" : "read", size, addr_string.c_str(), thread_id); } if (path == "threads") { int thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); result = Sprintf("Thread %d created", thread_id); } if (path == "locs") { std::string type = o->GetAsDictionary()->GetValueForKey("type")->GetStringValue(); int thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); int fd = o->GetObjectForDotSeparatedPath("file_descriptor")->GetIntegerValue(); if (type == "heap") { result = Sprintf("Heap block allocated by thread %d", thread_id); } else if (type == "fd") { result = Sprintf("File descriptor %d created by thread %t", fd, thread_id); } } if (path == "mutexes") { int mutex_id = o->GetObjectForDotSeparatedPath("mutex_id")->GetIntegerValue(); result = Sprintf("Mutex M%d created", mutex_id); } if (path == "stacks") { int thread_id = o->GetObjectForDotSeparatedPath("thread_id")->GetIntegerValue(); result = Sprintf("Thread %d", thread_id); } result[0] = toupper(result[0]); return result; } static void AddThreadsForPath(const std::string &path, ThreadCollectionSP threads, ProcessSP process_sp, StructuredData::ObjectSP info) { info->GetObjectForDotSeparatedPath(path)->GetAsArray()->ForEach( [process_sp, threads, path, info](StructuredData::Object *o) -> bool { std::vector pcs; o->GetObjectForDotSeparatedPath("trace")->GetAsArray()->ForEach( [&pcs](StructuredData::Object *pc) -> bool { pcs.push_back(pc->GetAsInteger()->GetValue()); return true; }); if (pcs.size() == 0) return true; StructuredData::ObjectSP thread_id_obj = o->GetObjectForDotSeparatedPath("thread_os_id"); tid_t tid = thread_id_obj ? thread_id_obj->GetIntegerValue() : 0; uint32_t stop_id = 0; bool stop_id_is_valid = false; HistoryThread *history_thread = new HistoryThread(*process_sp, tid, pcs, stop_id, stop_id_is_valid); ThreadSP new_thread_sp(history_thread); new_thread_sp->SetName(GenerateThreadName(path, o, info).c_str()); // Save this in the Process' ExtendedThreadList so a strong pointer // retains the object process_sp->GetExtendedThreadList().AddThread(new_thread_sp); threads->AddThread(new_thread_sp); return true; }); } lldb::ThreadCollectionSP ThreadSanitizerRuntime::GetBacktracesFromExtendedStopInfo( StructuredData::ObjectSP info) { ThreadCollectionSP threads; threads.reset(new ThreadCollection()); if (info->GetObjectForDotSeparatedPath("instrumentation_class") ->GetStringValue() != "ThreadSanitizer") return threads; ProcessSP process_sp = GetProcessSP(); AddThreadsForPath("stacks", threads, process_sp, info); AddThreadsForPath("mops", threads, process_sp, info); AddThreadsForPath("locs", threads, process_sp, info); AddThreadsForPath("mutexes", threads, process_sp, info); AddThreadsForPath("threads", threads, process_sp, info); return threads; } Index: vendor/lldb/dist/source/Symbol/ClangASTContext.cpp =================================================================== --- vendor/lldb/dist/source/Symbol/ClangASTContext.cpp (revision 312182) +++ vendor/lldb/dist/source/Symbol/ClangASTContext.cpp (revision 312183) @@ -1,10092 +1,10091 @@ //===-- ClangASTContext.cpp -------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// #include "lldb/Symbol/ClangASTContext.h" #include "llvm/Support/FormatAdapters.h" #include "llvm/Support/FormatVariadic.h" // C Includes // C++ Includes #include // std::once #include #include // Other libraries and framework includes // Clang headers like to use NDEBUG inside of them to enable/disable debug // related features using "#ifndef NDEBUG" preprocessor blocks to do one thing // or another. This is bad because it means that if clang was built in release // mode, it assumes that you are building in release mode which is not always // the case. You can end up with functions that are defined as empty in header // files when NDEBUG is not defined, and this can cause link errors with the // clang .a files that you have since you might be missing functions in the .a // file. So we have to define NDEBUG when including clang headers to avoid any // mismatches. This is covered by rdar://problem/8691220 #if !defined(NDEBUG) && !defined(LLVM_NDEBUG_OFF) #define LLDB_DEFINED_NDEBUG_FOR_CLANG #define NDEBUG // Need to include assert.h so it is as clang would expect it to be (disabled) #include #endif #include "clang/AST/ASTContext.h" #include "clang/AST/ASTImporter.h" #include "clang/AST/Attr.h" #include "clang/AST/CXXInheritance.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/Mangle.h" #include "clang/AST/RecordLayout.h" #include "clang/AST/Type.h" #include "clang/AST/VTableBuilder.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/Diagnostic.h" #include "clang/Basic/FileManager.h" #include "clang/Basic/FileSystemOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "clang/Basic/TargetOptions.h" #include "clang/Frontend/FrontendOptions.h" #include "clang/Frontend/LangStandard.h" #ifdef LLDB_DEFINED_NDEBUG_FOR_CLANG #undef NDEBUG #undef LLDB_DEFINED_NDEBUG_FOR_CLANG // Need to re-include assert.h so it is as _we_ would expect it to be (enabled) #include #endif #include "llvm/Support/Signals.h" #include "Plugins/ExpressionParser/Clang/ClangFunctionCaller.h" #include "Plugins/ExpressionParser/Clang/ClangUserExpression.h" #include "Plugins/ExpressionParser/Clang/ClangUtilityFunction.h" #include "lldb/Core/ArchSpec.h" #include "lldb/Core/Flags.h" #include "lldb/Core/Log.h" #include "lldb/Core/Module.h" #include "lldb/Core/PluginManager.h" #include "lldb/Core/RegularExpression.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/StreamFile.h" #include "lldb/Core/ThreadSafeDenseMap.h" #include "lldb/Core/UniqueCStringMap.h" #include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/ClangASTImporter.h" #include "lldb/Symbol/ClangExternalASTSourceCallbacks.h" #include "lldb/Symbol/ClangExternalASTSourceCommon.h" #include "lldb/Symbol/ClangUtil.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/VerifyDecl.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Language.h" #include "lldb/Target/ObjCLanguageRuntime.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "lldb/Utility/LLDBAssert.h" #include "Plugins/SymbolFile/DWARF/DWARFASTParserClang.h" #include "Plugins/SymbolFile/PDB/PDBASTParser.h" #include #include using namespace lldb; using namespace lldb_private; using namespace llvm; using namespace clang; namespace { static inline bool ClangASTContextSupportsLanguage(lldb::LanguageType language) { return language == eLanguageTypeUnknown || // Clang is the default type system Language::LanguageIsC(language) || Language::LanguageIsCPlusPlus(language) || Language::LanguageIsObjC(language) || Language::LanguageIsPascal(language) || // Use Clang for Rust until there is a proper language plugin for it language == eLanguageTypeRust || language == eLanguageTypeExtRenderScript || // Use Clang for D until there is a proper language plugin for it language == eLanguageTypeD; } } typedef lldb_private::ThreadSafeDenseMap ClangASTMap; static ClangASTMap &GetASTMap() { static ClangASTMap *g_map_ptr = nullptr; static std::once_flag g_once_flag; std::call_once(g_once_flag, []() { g_map_ptr = new ClangASTMap(); // leaked on purpose to avoid spins }); return *g_map_ptr; } static bool IsOperator(const char *name, clang::OverloadedOperatorKind &op_kind) { if (name == nullptr || name[0] == '\0') return false; #define OPERATOR_PREFIX "operator" #define OPERATOR_PREFIX_LENGTH (sizeof(OPERATOR_PREFIX) - 1) const char *post_op_name = nullptr; bool no_space = true; if (::strncmp(name, OPERATOR_PREFIX, OPERATOR_PREFIX_LENGTH)) return false; post_op_name = name + OPERATOR_PREFIX_LENGTH; if (post_op_name[0] == ' ') { post_op_name++; no_space = false; } #undef OPERATOR_PREFIX #undef OPERATOR_PREFIX_LENGTH // This is an operator, set the overloaded operator kind to invalid // in case this is a conversion operator... op_kind = clang::NUM_OVERLOADED_OPERATORS; switch (post_op_name[0]) { default: if (no_space) return false; break; case 'n': if (no_space) return false; if (strcmp(post_op_name, "new") == 0) op_kind = clang::OO_New; else if (strcmp(post_op_name, "new[]") == 0) op_kind = clang::OO_Array_New; break; case 'd': if (no_space) return false; if (strcmp(post_op_name, "delete") == 0) op_kind = clang::OO_Delete; else if (strcmp(post_op_name, "delete[]") == 0) op_kind = clang::OO_Array_Delete; break; case '+': if (post_op_name[1] == '\0') op_kind = clang::OO_Plus; else if (post_op_name[2] == '\0') { if (post_op_name[1] == '=') op_kind = clang::OO_PlusEqual; else if (post_op_name[1] == '+') op_kind = clang::OO_PlusPlus; } break; case '-': if (post_op_name[1] == '\0') op_kind = clang::OO_Minus; else if (post_op_name[2] == '\0') { switch (post_op_name[1]) { case '=': op_kind = clang::OO_MinusEqual; break; case '-': op_kind = clang::OO_MinusMinus; break; case '>': op_kind = clang::OO_Arrow; break; } } else if (post_op_name[3] == '\0') { if (post_op_name[2] == '*') op_kind = clang::OO_ArrowStar; break; } break; case '*': if (post_op_name[1] == '\0') op_kind = clang::OO_Star; else if (post_op_name[1] == '=' && post_op_name[2] == '\0') op_kind = clang::OO_StarEqual; break; case '/': if (post_op_name[1] == '\0') op_kind = clang::OO_Slash; else if (post_op_name[1] == '=' && post_op_name[2] == '\0') op_kind = clang::OO_SlashEqual; break; case '%': if (post_op_name[1] == '\0') op_kind = clang::OO_Percent; else if (post_op_name[1] == '=' && post_op_name[2] == '\0') op_kind = clang::OO_PercentEqual; break; case '^': if (post_op_name[1] == '\0') op_kind = clang::OO_Caret; else if (post_op_name[1] == '=' && post_op_name[2] == '\0') op_kind = clang::OO_CaretEqual; break; case '&': if (post_op_name[1] == '\0') op_kind = clang::OO_Amp; else if (post_op_name[2] == '\0') { switch (post_op_name[1]) { case '=': op_kind = clang::OO_AmpEqual; break; case '&': op_kind = clang::OO_AmpAmp; break; } } break; case '|': if (post_op_name[1] == '\0') op_kind = clang::OO_Pipe; else if (post_op_name[2] == '\0') { switch (post_op_name[1]) { case '=': op_kind = clang::OO_PipeEqual; break; case '|': op_kind = clang::OO_PipePipe; break; } } break; case '~': if (post_op_name[1] == '\0') op_kind = clang::OO_Tilde; break; case '!': if (post_op_name[1] == '\0') op_kind = clang::OO_Exclaim; else if (post_op_name[1] == '=' && post_op_name[2] == '\0') op_kind = clang::OO_ExclaimEqual; break; case '=': if (post_op_name[1] == '\0') op_kind = clang::OO_Equal; else if (post_op_name[1] == '=' && post_op_name[2] == '\0') op_kind = clang::OO_EqualEqual; break; case '<': if (post_op_name[1] == '\0') op_kind = clang::OO_Less; else if (post_op_name[2] == '\0') { switch (post_op_name[1]) { case '<': op_kind = clang::OO_LessLess; break; case '=': op_kind = clang::OO_LessEqual; break; } } else if (post_op_name[3] == '\0') { if (post_op_name[2] == '=') op_kind = clang::OO_LessLessEqual; } break; case '>': if (post_op_name[1] == '\0') op_kind = clang::OO_Greater; else if (post_op_name[2] == '\0') { switch (post_op_name[1]) { case '>': op_kind = clang::OO_GreaterGreater; break; case '=': op_kind = clang::OO_GreaterEqual; break; } } else if (post_op_name[1] == '>' && post_op_name[2] == '=' && post_op_name[3] == '\0') { op_kind = clang::OO_GreaterGreaterEqual; } break; case ',': if (post_op_name[1] == '\0') op_kind = clang::OO_Comma; break; case '(': if (post_op_name[1] == ')' && post_op_name[2] == '\0') op_kind = clang::OO_Call; break; case '[': if (post_op_name[1] == ']' && post_op_name[2] == '\0') op_kind = clang::OO_Subscript; break; } return true; } clang::AccessSpecifier ClangASTContext::ConvertAccessTypeToAccessSpecifier(AccessType access) { switch (access) { default: break; case eAccessNone: return AS_none; case eAccessPublic: return AS_public; case eAccessPrivate: return AS_private; case eAccessProtected: return AS_protected; } return AS_none; } static void ParseLangArgs(LangOptions &Opts, InputKind IK, const char *triple) { // FIXME: Cleanup per-file based stuff. // Set some properties which depend solely on the input kind; it would be nice // to move these to the language standard, and have the driver resolve the // input kind + language standard. if (IK == IK_Asm) { Opts.AsmPreprocessor = 1; } else if (IK == IK_ObjC || IK == IK_ObjCXX || IK == IK_PreprocessedObjC || IK == IK_PreprocessedObjCXX) { Opts.ObjC1 = Opts.ObjC2 = 1; } LangStandard::Kind LangStd = LangStandard::lang_unspecified; if (LangStd == LangStandard::lang_unspecified) { // Based on the base language, pick one. switch (IK) { case IK_None: case IK_AST: case IK_LLVM_IR: case IK_RenderScript: llvm_unreachable("Invalid input kind!"); case IK_OpenCL: LangStd = LangStandard::lang_opencl; break; case IK_CUDA: case IK_PreprocessedCuda: LangStd = LangStandard::lang_cuda; break; case IK_Asm: case IK_C: case IK_PreprocessedC: case IK_ObjC: case IK_PreprocessedObjC: LangStd = LangStandard::lang_gnu99; break; case IK_CXX: case IK_PreprocessedCXX: case IK_ObjCXX: case IK_PreprocessedObjCXX: LangStd = LangStandard::lang_gnucxx98; break; } } const LangStandard &Std = LangStandard::getLangStandardForKind(LangStd); Opts.LineComment = Std.hasLineComments(); Opts.C99 = Std.isC99(); Opts.CPlusPlus = Std.isCPlusPlus(); Opts.CPlusPlus11 = Std.isCPlusPlus11(); Opts.Digraphs = Std.hasDigraphs(); Opts.GNUMode = Std.isGNUMode(); Opts.GNUInline = !Std.isC99(); Opts.HexFloats = Std.hasHexFloats(); Opts.ImplicitInt = Std.hasImplicitInt(); Opts.WChar = true; // OpenCL has some additional defaults. if (LangStd == LangStandard::lang_opencl) { Opts.OpenCL = 1; Opts.AltiVec = 1; Opts.CXXOperatorNames = 1; Opts.LaxVectorConversions = 1; } // OpenCL and C++ both have bool, true, false keywords. Opts.Bool = Opts.OpenCL || Opts.CPlusPlus; // if (Opts.CPlusPlus) // Opts.CXXOperatorNames = !Args.hasArg(OPT_fno_operator_names); // // if (Args.hasArg(OPT_fobjc_gc_only)) // Opts.setGCMode(LangOptions::GCOnly); // else if (Args.hasArg(OPT_fobjc_gc)) // Opts.setGCMode(LangOptions::HybridGC); // // if (Args.hasArg(OPT_print_ivar_layout)) // Opts.ObjCGCBitmapPrint = 1; // // if (Args.hasArg(OPT_faltivec)) // Opts.AltiVec = 1; // // if (Args.hasArg(OPT_pthread)) // Opts.POSIXThreads = 1; // // llvm::StringRef Vis = getLastArgValue(Args, OPT_fvisibility, // "default"); // if (Vis == "default") Opts.setValueVisibilityMode(DefaultVisibility); // else if (Vis == "hidden") // Opts.setVisibilityMode(LangOptions::Hidden); // else if (Vis == "protected") // Opts.setVisibilityMode(LangOptions::Protected); // else // Diags.Report(diag::err_drv_invalid_value) // << Args.getLastArg(OPT_fvisibility)->getAsString(Args) << Vis; // Opts.OverflowChecking = Args.hasArg(OPT_ftrapv); // Mimicing gcc's behavior, trigraphs are only enabled if -trigraphs // is specified, or -std is set to a conforming mode. Opts.Trigraphs = !Opts.GNUMode; // if (Args.hasArg(OPT_trigraphs)) // Opts.Trigraphs = 1; // // Opts.DollarIdents = Args.hasFlag(OPT_fdollars_in_identifiers, // OPT_fno_dollars_in_identifiers, // !Opts.AsmPreprocessor); // Opts.PascalStrings = Args.hasArg(OPT_fpascal_strings); // Opts.Microsoft = Args.hasArg(OPT_fms_extensions); // Opts.WritableStrings = Args.hasArg(OPT_fwritable_strings); // if (Args.hasArg(OPT_fno_lax_vector_conversions)) // Opts.LaxVectorConversions = 0; // Opts.Exceptions = Args.hasArg(OPT_fexceptions); // Opts.RTTI = !Args.hasArg(OPT_fno_rtti); // Opts.Blocks = Args.hasArg(OPT_fblocks); Opts.CharIsSigned = ArchSpec(triple).CharIsSignedByDefault(); // Opts.ShortWChar = Args.hasArg(OPT_fshort_wchar); // Opts.Freestanding = Args.hasArg(OPT_ffreestanding); // Opts.NoBuiltin = Args.hasArg(OPT_fno_builtin) || Opts.Freestanding; // Opts.AssumeSaneOperatorNew = // !Args.hasArg(OPT_fno_assume_sane_operator_new); // Opts.HeinousExtensions = Args.hasArg(OPT_fheinous_gnu_extensions); // Opts.AccessControl = Args.hasArg(OPT_faccess_control); // Opts.ElideConstructors = !Args.hasArg(OPT_fno_elide_constructors); // Opts.MathErrno = !Args.hasArg(OPT_fno_math_errno); // Opts.InstantiationDepth = getLastArgIntValue(Args, OPT_ftemplate_depth, // 99, // Diags); // Opts.NeXTRuntime = !Args.hasArg(OPT_fgnu_runtime); // Opts.ObjCConstantStringClass = getLastArgValue(Args, // OPT_fconstant_string_class); // Opts.ObjCNonFragileABI = Args.hasArg(OPT_fobjc_nonfragile_abi); // Opts.CatchUndefined = Args.hasArg(OPT_fcatch_undefined_behavior); // Opts.EmitAllDecls = Args.hasArg(OPT_femit_all_decls); // Opts.PICLevel = getLastArgIntValue(Args, OPT_pic_level, 0, Diags); // Opts.Static = Args.hasArg(OPT_static_define); Opts.OptimizeSize = 0; // FIXME: Eliminate this dependency. // unsigned Opt = // Args.hasArg(OPT_Os) ? 2 : getLastArgIntValue(Args, OPT_O, 0, Diags); // Opts.Optimize = Opt != 0; unsigned Opt = 0; // This is the __NO_INLINE__ define, which just depends on things like the // optimization level and -fno-inline, not actually whether the backend has // inlining enabled. // // FIXME: This is affected by other options (-fno-inline). Opts.NoInlineDefine = !Opt; // unsigned SSP = getLastArgIntValue(Args, OPT_stack_protector, 0, Diags); // switch (SSP) { // default: // Diags.Report(diag::err_drv_invalid_value) // << Args.getLastArg(OPT_stack_protector)->getAsString(Args) << // SSP; // break; // case 0: Opts.setStackProtectorMode(LangOptions::SSPOff); break; // case 1: Opts.setStackProtectorMode(LangOptions::SSPOn); break; // case 2: Opts.setStackProtectorMode(LangOptions::SSPReq); break; // } } ClangASTContext::ClangASTContext(const char *target_triple) : TypeSystem(TypeSystem::eKindClang), m_target_triple(), m_ast_ap(), m_language_options_ap(), m_source_manager_ap(), m_diagnostics_engine_ap(), m_target_options_rp(), m_target_info_ap(), m_identifier_table_ap(), m_selector_table_ap(), m_builtins_ap(), m_callback_tag_decl(nullptr), m_callback_objc_decl(nullptr), m_callback_baton(nullptr), m_pointer_byte_size(0), m_ast_owned(false) { if (target_triple && target_triple[0]) SetTargetTriple(target_triple); } //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- ClangASTContext::~ClangASTContext() { Finalize(); } ConstString ClangASTContext::GetPluginNameStatic() { return ConstString("clang"); } ConstString ClangASTContext::GetPluginName() { return ClangASTContext::GetPluginNameStatic(); } uint32_t ClangASTContext::GetPluginVersion() { return 1; } lldb::TypeSystemSP ClangASTContext::CreateInstance(lldb::LanguageType language, lldb_private::Module *module, Target *target) { if (ClangASTContextSupportsLanguage(language)) { ArchSpec arch; if (module) arch = module->GetArchitecture(); else if (target) arch = target->GetArchitecture(); if (arch.IsValid()) { ArchSpec fixed_arch = arch; // LLVM wants this to be set to iOS or MacOSX; if we're working on // a bare-boards type image, change the triple for llvm's benefit. if (fixed_arch.GetTriple().getVendor() == llvm::Triple::Apple && fixed_arch.GetTriple().getOS() == llvm::Triple::UnknownOS) { if (fixed_arch.GetTriple().getArch() == llvm::Triple::arm || fixed_arch.GetTriple().getArch() == llvm::Triple::aarch64 || fixed_arch.GetTriple().getArch() == llvm::Triple::thumb) { fixed_arch.GetTriple().setOS(llvm::Triple::IOS); } else { fixed_arch.GetTriple().setOS(llvm::Triple::MacOSX); } } if (module) { std::shared_ptr ast_sp(new ClangASTContext); if (ast_sp) { ast_sp->SetArchitecture(fixed_arch); } return ast_sp; } else if (target && target->IsValid()) { std::shared_ptr ast_sp( new ClangASTContextForExpressions(*target)); if (ast_sp) { ast_sp->SetArchitecture(fixed_arch); ast_sp->m_scratch_ast_source_ap.reset( new ClangASTSource(target->shared_from_this())); ast_sp->m_scratch_ast_source_ap->InstallASTContext( ast_sp->getASTContext()); llvm::IntrusiveRefCntPtr proxy_ast_source( ast_sp->m_scratch_ast_source_ap->CreateProxy()); ast_sp->SetExternalSource(proxy_ast_source); return ast_sp; } } } } return lldb::TypeSystemSP(); } void ClangASTContext::EnumerateSupportedLanguages( std::set &languages_for_types, std::set &languages_for_expressions) { static std::vector s_supported_languages_for_types( {lldb::eLanguageTypeC89, lldb::eLanguageTypeC, lldb::eLanguageTypeC11, lldb::eLanguageTypeC_plus_plus, lldb::eLanguageTypeC99, lldb::eLanguageTypeObjC, lldb::eLanguageTypeObjC_plus_plus, lldb::eLanguageTypeC_plus_plus_03, lldb::eLanguageTypeC_plus_plus_11, lldb::eLanguageTypeC11, lldb::eLanguageTypeC_plus_plus_14}); static std::vector s_supported_languages_for_expressions( {lldb::eLanguageTypeC_plus_plus, lldb::eLanguageTypeObjC_plus_plus, lldb::eLanguageTypeC_plus_plus_03, lldb::eLanguageTypeC_plus_plus_11, lldb::eLanguageTypeC_plus_plus_14}); 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 ClangASTContext::Initialize() { PluginManager::RegisterPlugin(GetPluginNameStatic(), "clang base AST context plug-in", CreateInstance, EnumerateSupportedLanguages); } void ClangASTContext::Terminate() { PluginManager::UnregisterPlugin(CreateInstance); } void ClangASTContext::Finalize() { if (m_ast_ap.get()) { GetASTMap().Erase(m_ast_ap.get()); if (!m_ast_owned) m_ast_ap.release(); } m_builtins_ap.reset(); m_selector_table_ap.reset(); m_identifier_table_ap.reset(); m_target_info_ap.reset(); m_target_options_rp.reset(); m_diagnostics_engine_ap.reset(); m_source_manager_ap.reset(); m_language_options_ap.reset(); m_ast_ap.reset(); m_scratch_ast_source_ap.reset(); } void ClangASTContext::Clear() { m_ast_ap.reset(); m_language_options_ap.reset(); m_source_manager_ap.reset(); m_diagnostics_engine_ap.reset(); m_target_options_rp.reset(); m_target_info_ap.reset(); m_identifier_table_ap.reset(); m_selector_table_ap.reset(); m_builtins_ap.reset(); m_pointer_byte_size = 0; } const char *ClangASTContext::GetTargetTriple() { return m_target_triple.c_str(); } void ClangASTContext::SetTargetTriple(const char *target_triple) { Clear(); m_target_triple.assign(target_triple); } void ClangASTContext::SetArchitecture(const ArchSpec &arch) { SetTargetTriple(arch.GetTriple().str().c_str()); } bool ClangASTContext::HasExternalSource() { ASTContext *ast = getASTContext(); if (ast) return ast->getExternalSource() != nullptr; return false; } void ClangASTContext::SetExternalSource( llvm::IntrusiveRefCntPtr &ast_source_ap) { ASTContext *ast = getASTContext(); if (ast) { ast->setExternalSource(ast_source_ap); ast->getTranslationUnitDecl()->setHasExternalLexicalStorage(true); // ast->getTranslationUnitDecl()->setHasExternalVisibleStorage(true); } } void ClangASTContext::RemoveExternalSource() { ASTContext *ast = getASTContext(); if (ast) { llvm::IntrusiveRefCntPtr empty_ast_source_ap; ast->setExternalSource(empty_ast_source_ap); ast->getTranslationUnitDecl()->setHasExternalLexicalStorage(false); // ast->getTranslationUnitDecl()->setHasExternalVisibleStorage(false); } } void ClangASTContext::setASTContext(clang::ASTContext *ast_ctx) { if (!m_ast_owned) { m_ast_ap.release(); } m_ast_owned = false; m_ast_ap.reset(ast_ctx); GetASTMap().Insert(ast_ctx, this); } ASTContext *ClangASTContext::getASTContext() { if (m_ast_ap.get() == nullptr) { m_ast_owned = true; m_ast_ap.reset(new ASTContext(*getLanguageOptions(), *getSourceManager(), *getIdentifierTable(), *getSelectorTable(), *getBuiltinContext())); m_ast_ap->getDiagnostics().setClient(getDiagnosticConsumer(), false); // This can be NULL if we don't know anything about the architecture or if // the // target for an architecture isn't enabled in the llvm/clang that we built TargetInfo *target_info = getTargetInfo(); if (target_info) m_ast_ap->InitBuiltinTypes(*target_info); if ((m_callback_tag_decl || m_callback_objc_decl) && m_callback_baton) { m_ast_ap->getTranslationUnitDecl()->setHasExternalLexicalStorage(); // m_ast_ap->getTranslationUnitDecl()->setHasExternalVisibleStorage(); } GetASTMap().Insert(m_ast_ap.get(), this); llvm::IntrusiveRefCntPtr ast_source_ap( new ClangExternalASTSourceCallbacks( ClangASTContext::CompleteTagDecl, ClangASTContext::CompleteObjCInterfaceDecl, nullptr, ClangASTContext::LayoutRecordType, this)); SetExternalSource(ast_source_ap); } return m_ast_ap.get(); } ClangASTContext *ClangASTContext::GetASTContext(clang::ASTContext *ast) { ClangASTContext *clang_ast = GetASTMap().Lookup(ast); return clang_ast; } Builtin::Context *ClangASTContext::getBuiltinContext() { if (m_builtins_ap.get() == nullptr) m_builtins_ap.reset(new Builtin::Context()); return m_builtins_ap.get(); } IdentifierTable *ClangASTContext::getIdentifierTable() { if (m_identifier_table_ap.get() == nullptr) m_identifier_table_ap.reset( new IdentifierTable(*ClangASTContext::getLanguageOptions(), nullptr)); return m_identifier_table_ap.get(); } LangOptions *ClangASTContext::getLanguageOptions() { if (m_language_options_ap.get() == nullptr) { m_language_options_ap.reset(new LangOptions()); ParseLangArgs(*m_language_options_ap, IK_ObjCXX, GetTargetTriple()); // InitializeLangOptions(*m_language_options_ap, IK_ObjCXX); } return m_language_options_ap.get(); } SelectorTable *ClangASTContext::getSelectorTable() { if (m_selector_table_ap.get() == nullptr) m_selector_table_ap.reset(new SelectorTable()); return m_selector_table_ap.get(); } clang::FileManager *ClangASTContext::getFileManager() { if (m_file_manager_ap.get() == nullptr) { clang::FileSystemOptions file_system_options; m_file_manager_ap.reset(new clang::FileManager(file_system_options)); } return m_file_manager_ap.get(); } clang::SourceManager *ClangASTContext::getSourceManager() { if (m_source_manager_ap.get() == nullptr) m_source_manager_ap.reset( new clang::SourceManager(*getDiagnosticsEngine(), *getFileManager())); return m_source_manager_ap.get(); } clang::DiagnosticsEngine *ClangASTContext::getDiagnosticsEngine() { if (m_diagnostics_engine_ap.get() == nullptr) { llvm::IntrusiveRefCntPtr diag_id_sp(new DiagnosticIDs()); m_diagnostics_engine_ap.reset( new DiagnosticsEngine(diag_id_sp, new DiagnosticOptions())); } return m_diagnostics_engine_ap.get(); } clang::MangleContext *ClangASTContext::getMangleContext() { if (m_mangle_ctx_ap.get() == nullptr) m_mangle_ctx_ap.reset(getASTContext()->createMangleContext()); return m_mangle_ctx_ap.get(); } class NullDiagnosticConsumer : public DiagnosticConsumer { public: NullDiagnosticConsumer() { m_log = lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_EXPRESSIONS); } void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, const clang::Diagnostic &info) { if (m_log) { llvm::SmallVector diag_str(10); info.FormatDiagnostic(diag_str); diag_str.push_back('\0'); m_log->Printf("Compiler diagnostic: %s\n", diag_str.data()); } } DiagnosticConsumer *clone(DiagnosticsEngine &Diags) const { return new NullDiagnosticConsumer(); } private: Log *m_log; }; DiagnosticConsumer *ClangASTContext::getDiagnosticConsumer() { if (m_diagnostic_consumer_ap.get() == nullptr) m_diagnostic_consumer_ap.reset(new NullDiagnosticConsumer); return m_diagnostic_consumer_ap.get(); } std::shared_ptr &ClangASTContext::getTargetOptions() { if (m_target_options_rp.get() == nullptr && !m_target_triple.empty()) { m_target_options_rp = std::make_shared(); if (m_target_options_rp.get() != nullptr) m_target_options_rp->Triple = m_target_triple; } return m_target_options_rp; } TargetInfo *ClangASTContext::getTargetInfo() { // target_triple should be something like "x86_64-apple-macosx" if (m_target_info_ap.get() == nullptr && !m_target_triple.empty()) m_target_info_ap.reset(TargetInfo::CreateTargetInfo(*getDiagnosticsEngine(), getTargetOptions())); return m_target_info_ap.get(); } #pragma mark Basic Types static inline bool QualTypeMatchesBitSize(const uint64_t bit_size, ASTContext *ast, QualType qual_type) { uint64_t qual_type_bit_size = ast->getTypeSize(qual_type); if (qual_type_bit_size == bit_size) return true; return false; } CompilerType ClangASTContext::GetBuiltinTypeForEncodingAndBitSize(Encoding encoding, size_t bit_size) { return ClangASTContext::GetBuiltinTypeForEncodingAndBitSize( getASTContext(), encoding, bit_size); } CompilerType ClangASTContext::GetBuiltinTypeForEncodingAndBitSize( ASTContext *ast, Encoding encoding, uint32_t bit_size) { if (!ast) return CompilerType(); switch (encoding) { case eEncodingInvalid: if (QualTypeMatchesBitSize(bit_size, ast, ast->VoidPtrTy)) return CompilerType(ast, ast->VoidPtrTy); break; case eEncodingUint: if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy)) return CompilerType(ast, ast->UnsignedCharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy)) return CompilerType(ast, ast->UnsignedShortTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedIntTy)) return CompilerType(ast, ast->UnsignedIntTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongTy)) return CompilerType(ast, ast->UnsignedLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongLongTy)) return CompilerType(ast, ast->UnsignedLongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedInt128Ty)) return CompilerType(ast, ast->UnsignedInt128Ty); break; case eEncodingSint: if (QualTypeMatchesBitSize(bit_size, ast, ast->SignedCharTy)) return CompilerType(ast, ast->SignedCharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->ShortTy)) return CompilerType(ast, ast->ShortTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->IntTy)) return CompilerType(ast, ast->IntTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->LongTy)) return CompilerType(ast, ast->LongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->LongLongTy)) return CompilerType(ast, ast->LongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->Int128Ty)) return CompilerType(ast, ast->Int128Ty); break; case eEncodingIEEE754: if (QualTypeMatchesBitSize(bit_size, ast, ast->FloatTy)) return CompilerType(ast, ast->FloatTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->DoubleTy)) return CompilerType(ast, ast->DoubleTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->LongDoubleTy)) return CompilerType(ast, ast->LongDoubleTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->HalfTy)) return CompilerType(ast, ast->HalfTy); break; case eEncodingVector: // Sanity check that bit_size is a multiple of 8's. if (bit_size && !(bit_size & 0x7u)) return CompilerType( ast, ast->getExtVectorType(ast->UnsignedCharTy, bit_size / 8)); break; } return CompilerType(); } lldb::BasicType ClangASTContext::GetBasicTypeEnumeration(const ConstString &name) { if (name) { typedef UniqueCStringMap TypeNameToBasicTypeMap; static TypeNameToBasicTypeMap g_type_map; static std::once_flag g_once_flag; std::call_once(g_once_flag, []() { // "void" g_type_map.Append(ConstString("void").GetStringRef(), eBasicTypeVoid); // "char" g_type_map.Append(ConstString("char").GetStringRef(), eBasicTypeChar); g_type_map.Append(ConstString("signed char").GetStringRef(), eBasicTypeSignedChar); g_type_map.Append(ConstString("unsigned char").GetStringRef(), eBasicTypeUnsignedChar); g_type_map.Append(ConstString("wchar_t").GetStringRef(), eBasicTypeWChar); g_type_map.Append(ConstString("signed wchar_t").GetStringRef(), eBasicTypeSignedWChar); g_type_map.Append(ConstString("unsigned wchar_t").GetStringRef(), eBasicTypeUnsignedWChar); // "short" g_type_map.Append(ConstString("short").GetStringRef(), eBasicTypeShort); g_type_map.Append(ConstString("short int").GetStringRef(), eBasicTypeShort); g_type_map.Append(ConstString("unsigned short").GetStringRef(), eBasicTypeUnsignedShort); g_type_map.Append(ConstString("unsigned short int").GetStringRef(), eBasicTypeUnsignedShort); // "int" g_type_map.Append(ConstString("int").GetStringRef(), eBasicTypeInt); g_type_map.Append(ConstString("signed int").GetStringRef(), eBasicTypeInt); g_type_map.Append(ConstString("unsigned int").GetStringRef(), eBasicTypeUnsignedInt); g_type_map.Append(ConstString("unsigned").GetStringRef(), eBasicTypeUnsignedInt); // "long" g_type_map.Append(ConstString("long").GetStringRef(), eBasicTypeLong); g_type_map.Append(ConstString("long int").GetStringRef(), eBasicTypeLong); g_type_map.Append(ConstString("unsigned long").GetStringRef(), eBasicTypeUnsignedLong); g_type_map.Append(ConstString("unsigned long int").GetStringRef(), eBasicTypeUnsignedLong); // "long long" g_type_map.Append(ConstString("long long").GetStringRef(), eBasicTypeLongLong); g_type_map.Append(ConstString("long long int").GetStringRef(), eBasicTypeLongLong); g_type_map.Append(ConstString("unsigned long long").GetStringRef(), eBasicTypeUnsignedLongLong); g_type_map.Append(ConstString("unsigned long long int").GetStringRef(), eBasicTypeUnsignedLongLong); // "int128" g_type_map.Append(ConstString("__int128_t").GetStringRef(), eBasicTypeInt128); g_type_map.Append(ConstString("__uint128_t").GetStringRef(), eBasicTypeUnsignedInt128); // Miscellaneous g_type_map.Append(ConstString("bool").GetStringRef(), eBasicTypeBool); g_type_map.Append(ConstString("float").GetStringRef(), eBasicTypeFloat); g_type_map.Append(ConstString("double").GetStringRef(), eBasicTypeDouble); g_type_map.Append(ConstString("long double").GetStringRef(), eBasicTypeLongDouble); g_type_map.Append(ConstString("id").GetStringRef(), eBasicTypeObjCID); g_type_map.Append(ConstString("SEL").GetStringRef(), eBasicTypeObjCSel); g_type_map.Append(ConstString("nullptr").GetStringRef(), eBasicTypeNullPtr); g_type_map.Sort(); }); return g_type_map.Find(name.GetStringRef(), eBasicTypeInvalid); } return eBasicTypeInvalid; } CompilerType ClangASTContext::GetBasicType(ASTContext *ast, const ConstString &name) { if (ast) { lldb::BasicType basic_type = ClangASTContext::GetBasicTypeEnumeration(name); return ClangASTContext::GetBasicType(ast, basic_type); } return CompilerType(); } uint32_t ClangASTContext::GetPointerByteSize() { if (m_pointer_byte_size == 0) m_pointer_byte_size = GetBasicType(lldb::eBasicTypeVoid) .GetPointerType() .GetByteSize(nullptr); return m_pointer_byte_size; } CompilerType ClangASTContext::GetBasicType(lldb::BasicType basic_type) { return GetBasicType(getASTContext(), basic_type); } CompilerType ClangASTContext::GetBasicType(ASTContext *ast, lldb::BasicType basic_type) { if (!ast) return CompilerType(); lldb::opaque_compiler_type_t clang_type = GetOpaqueCompilerType(ast, basic_type); if (clang_type) return CompilerType(GetASTContext(ast), clang_type); return CompilerType(); } CompilerType ClangASTContext::GetBuiltinTypeForDWARFEncodingAndBitSize( const char *type_name, uint32_t dw_ate, uint32_t bit_size) { ASTContext *ast = getASTContext(); #define streq(a, b) strcmp(a, b) == 0 assert(ast != nullptr); if (ast) { switch (dw_ate) { default: break; case DW_ATE_address: if (QualTypeMatchesBitSize(bit_size, ast, ast->VoidPtrTy)) return CompilerType(ast, ast->VoidPtrTy); break; case DW_ATE_boolean: if (QualTypeMatchesBitSize(bit_size, ast, ast->BoolTy)) return CompilerType(ast, ast->BoolTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy)) return CompilerType(ast, ast->UnsignedCharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy)) return CompilerType(ast, ast->UnsignedShortTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedIntTy)) return CompilerType(ast, ast->UnsignedIntTy); break; case DW_ATE_lo_user: // This has been seen to mean DW_AT_complex_integer if (type_name) { if (::strstr(type_name, "complex")) { CompilerType complex_int_clang_type = GetBuiltinTypeForDWARFEncodingAndBitSize("int", DW_ATE_signed, bit_size / 2); return CompilerType(ast, ast->getComplexType(ClangUtil::GetQualType( complex_int_clang_type))); } } break; case DW_ATE_complex_float: if (QualTypeMatchesBitSize(bit_size, ast, ast->FloatComplexTy)) return CompilerType(ast, ast->FloatComplexTy); else if (QualTypeMatchesBitSize(bit_size, ast, ast->DoubleComplexTy)) return CompilerType(ast, ast->DoubleComplexTy); else if (QualTypeMatchesBitSize(bit_size, ast, ast->LongDoubleComplexTy)) return CompilerType(ast, ast->LongDoubleComplexTy); else { CompilerType complex_float_clang_type = GetBuiltinTypeForDWARFEncodingAndBitSize("float", DW_ATE_float, bit_size / 2); return CompilerType(ast, ast->getComplexType(ClangUtil::GetQualType( complex_float_clang_type))); } break; case DW_ATE_float: if (streq(type_name, "float") && QualTypeMatchesBitSize(bit_size, ast, ast->FloatTy)) return CompilerType(ast, ast->FloatTy); if (streq(type_name, "double") && QualTypeMatchesBitSize(bit_size, ast, ast->DoubleTy)) return CompilerType(ast, ast->DoubleTy); if (streq(type_name, "long double") && QualTypeMatchesBitSize(bit_size, ast, ast->LongDoubleTy)) return CompilerType(ast, ast->LongDoubleTy); // Fall back to not requiring a name match if (QualTypeMatchesBitSize(bit_size, ast, ast->FloatTy)) return CompilerType(ast, ast->FloatTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->DoubleTy)) return CompilerType(ast, ast->DoubleTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->LongDoubleTy)) return CompilerType(ast, ast->LongDoubleTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->HalfTy)) return CompilerType(ast, ast->HalfTy); break; case DW_ATE_signed: if (type_name) { if (streq(type_name, "wchar_t") && QualTypeMatchesBitSize(bit_size, ast, ast->WCharTy) && (getTargetInfo() && TargetInfo::isTypeSigned(getTargetInfo()->getWCharType()))) return CompilerType(ast, ast->WCharTy); if (streq(type_name, "void") && QualTypeMatchesBitSize(bit_size, ast, ast->VoidTy)) return CompilerType(ast, ast->VoidTy); if (strstr(type_name, "long long") && QualTypeMatchesBitSize(bit_size, ast, ast->LongLongTy)) return CompilerType(ast, ast->LongLongTy); if (strstr(type_name, "long") && QualTypeMatchesBitSize(bit_size, ast, ast->LongTy)) return CompilerType(ast, ast->LongTy); if (strstr(type_name, "short") && QualTypeMatchesBitSize(bit_size, ast, ast->ShortTy)) return CompilerType(ast, ast->ShortTy); if (strstr(type_name, "char")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->CharTy)) return CompilerType(ast, ast->CharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->SignedCharTy)) return CompilerType(ast, ast->SignedCharTy); } if (strstr(type_name, "int")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->IntTy)) return CompilerType(ast, ast->IntTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->Int128Ty)) return CompilerType(ast, ast->Int128Ty); } } // We weren't able to match up a type name, just search by size if (QualTypeMatchesBitSize(bit_size, ast, ast->CharTy)) return CompilerType(ast, ast->CharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->ShortTy)) return CompilerType(ast, ast->ShortTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->IntTy)) return CompilerType(ast, ast->IntTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->LongTy)) return CompilerType(ast, ast->LongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->LongLongTy)) return CompilerType(ast, ast->LongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->Int128Ty)) return CompilerType(ast, ast->Int128Ty); break; case DW_ATE_signed_char: if (ast->getLangOpts().CharIsSigned && type_name && streq(type_name, "char")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->CharTy)) return CompilerType(ast, ast->CharTy); } if (QualTypeMatchesBitSize(bit_size, ast, ast->SignedCharTy)) return CompilerType(ast, ast->SignedCharTy); break; case DW_ATE_unsigned: if (type_name) { if (streq(type_name, "wchar_t")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->WCharTy)) { if (!(getTargetInfo() && TargetInfo::isTypeSigned(getTargetInfo()->getWCharType()))) return CompilerType(ast, ast->WCharTy); } } if (strstr(type_name, "long long")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongLongTy)) return CompilerType(ast, ast->UnsignedLongLongTy); } else if (strstr(type_name, "long")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongTy)) return CompilerType(ast, ast->UnsignedLongTy); } else if (strstr(type_name, "short")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy)) return CompilerType(ast, ast->UnsignedShortTy); } else if (strstr(type_name, "char")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy)) return CompilerType(ast, ast->UnsignedCharTy); } else if (strstr(type_name, "int")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedIntTy)) return CompilerType(ast, ast->UnsignedIntTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedInt128Ty)) return CompilerType(ast, ast->UnsignedInt128Ty); } } // We weren't able to match up a type name, just search by size if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy)) return CompilerType(ast, ast->UnsignedCharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy)) return CompilerType(ast, ast->UnsignedShortTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedIntTy)) return CompilerType(ast, ast->UnsignedIntTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongTy)) return CompilerType(ast, ast->UnsignedLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedLongLongTy)) return CompilerType(ast, ast->UnsignedLongLongTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedInt128Ty)) return CompilerType(ast, ast->UnsignedInt128Ty); break; case DW_ATE_unsigned_char: if (!ast->getLangOpts().CharIsSigned && type_name && streq(type_name, "char")) { if (QualTypeMatchesBitSize(bit_size, ast, ast->CharTy)) return CompilerType(ast, ast->CharTy); } if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedCharTy)) return CompilerType(ast, ast->UnsignedCharTy); if (QualTypeMatchesBitSize(bit_size, ast, ast->UnsignedShortTy)) return CompilerType(ast, ast->UnsignedShortTy); break; case DW_ATE_imaginary_float: break; case DW_ATE_UTF: if (type_name) { if (streq(type_name, "char16_t")) { return CompilerType(ast, ast->Char16Ty); } else if (streq(type_name, "char32_t")) { return CompilerType(ast, ast->Char32Ty); } } break; } } // This assert should fire for anything that we don't catch above so we know // to fix any issues we run into. if (type_name) { Host::SystemLog(Host::eSystemLogError, "error: need to add support for " "DW_TAG_base_type '%s' encoded with " "DW_ATE = 0x%x, bit_size = %u\n", type_name, dw_ate, bit_size); } else { Host::SystemLog(Host::eSystemLogError, "error: need to add support for " "DW_TAG_base_type encoded with " "DW_ATE = 0x%x, bit_size = %u\n", dw_ate, bit_size); } return CompilerType(); } CompilerType ClangASTContext::GetUnknownAnyType(clang::ASTContext *ast) { if (ast) return CompilerType(ast, ast->UnknownAnyTy); return CompilerType(); } CompilerType ClangASTContext::GetCStringType(bool is_const) { ASTContext *ast = getASTContext(); QualType char_type(ast->CharTy); if (is_const) char_type.addConst(); return CompilerType(ast, ast->getPointerType(char_type)); } clang::DeclContext * ClangASTContext::GetTranslationUnitDecl(clang::ASTContext *ast) { return ast->getTranslationUnitDecl(); } clang::Decl *ClangASTContext::CopyDecl(ASTContext *dst_ast, ASTContext *src_ast, clang::Decl *source_decl) { FileSystemOptions file_system_options; FileManager file_manager(file_system_options); ASTImporter importer(*dst_ast, file_manager, *src_ast, file_manager, false); return importer.Import(source_decl); } bool ClangASTContext::AreTypesSame(CompilerType type1, CompilerType type2, bool ignore_qualifiers) { ClangASTContext *ast = llvm::dyn_cast_or_null(type1.GetTypeSystem()); if (!ast || ast != type2.GetTypeSystem()) return false; if (type1.GetOpaqueQualType() == type2.GetOpaqueQualType()) return true; QualType type1_qual = ClangUtil::GetQualType(type1); QualType type2_qual = ClangUtil::GetQualType(type2); if (ignore_qualifiers) { type1_qual = type1_qual.getUnqualifiedType(); type2_qual = type2_qual.getUnqualifiedType(); } return ast->getASTContext()->hasSameType(type1_qual, type2_qual); } CompilerType ClangASTContext::GetTypeForDecl(clang::NamedDecl *decl) { if (clang::ObjCInterfaceDecl *interface_decl = llvm::dyn_cast(decl)) return GetTypeForDecl(interface_decl); if (clang::TagDecl *tag_decl = llvm::dyn_cast(decl)) return GetTypeForDecl(tag_decl); return CompilerType(); } CompilerType ClangASTContext::GetTypeForDecl(TagDecl *decl) { // No need to call the getASTContext() accessor (which can create the AST // if it isn't created yet, because we can't have created a decl in this // AST if our AST didn't already exist... ASTContext *ast = &decl->getASTContext(); if (ast) return CompilerType(ast, ast->getTagDeclType(decl)); return CompilerType(); } CompilerType ClangASTContext::GetTypeForDecl(ObjCInterfaceDecl *decl) { // No need to call the getASTContext() accessor (which can create the AST // if it isn't created yet, because we can't have created a decl in this // AST if our AST didn't already exist... ASTContext *ast = &decl->getASTContext(); if (ast) return CompilerType(ast, ast->getObjCInterfaceType(decl)); return CompilerType(); } #pragma mark Structure, Unions, Classes CompilerType ClangASTContext::CreateRecordType(DeclContext *decl_ctx, AccessType access_type, const char *name, int kind, LanguageType language, ClangASTMetadata *metadata) { ASTContext *ast = getASTContext(); assert(ast != nullptr); if (decl_ctx == nullptr) decl_ctx = ast->getTranslationUnitDecl(); if (language == eLanguageTypeObjC || language == eLanguageTypeObjC_plus_plus) { bool isForwardDecl = true; bool isInternal = false; return CreateObjCClass(name, decl_ctx, isForwardDecl, isInternal, metadata); } // NOTE: Eventually CXXRecordDecl will be merged back into RecordDecl and // we will need to update this code. I was told to currently always use // the CXXRecordDecl class since we often don't know from debug information // if something is struct or a class, so we default to always use the more // complete definition just in case. bool is_anonymous = (!name) || (!name[0]); CXXRecordDecl *decl = CXXRecordDecl::Create( *ast, (TagDecl::TagKind)kind, decl_ctx, SourceLocation(), SourceLocation(), is_anonymous ? nullptr : &ast->Idents.get(name)); if (is_anonymous) decl->setAnonymousStructOrUnion(true); if (decl) { if (metadata) SetMetadata(ast, decl, *metadata); if (access_type != eAccessNone) decl->setAccess(ConvertAccessTypeToAccessSpecifier(access_type)); if (decl_ctx) decl_ctx->addDecl(decl); return CompilerType(ast, ast->getTagDeclType(decl)); } return CompilerType(); } static TemplateParameterList *CreateTemplateParameterList( ASTContext *ast, const ClangASTContext::TemplateParameterInfos &template_param_infos, llvm::SmallVector &template_param_decls) { const bool parameter_pack = false; const bool is_typename = false; const unsigned depth = 0; const size_t num_template_params = template_param_infos.GetSize(); for (size_t i = 0; i < num_template_params; ++i) { const char *name = template_param_infos.names[i]; IdentifierInfo *identifier_info = nullptr; if (name && name[0]) identifier_info = &ast->Idents.get(name); if (template_param_infos.args[i].getKind() == TemplateArgument::Integral) { template_param_decls.push_back(NonTypeTemplateParmDecl::Create( *ast, ast->getTranslationUnitDecl(), // Is this the right decl context?, // SourceLocation StartLoc, SourceLocation(), SourceLocation(), depth, i, identifier_info, template_param_infos.args[i].getIntegralType(), parameter_pack, nullptr)); } else { template_param_decls.push_back(TemplateTypeParmDecl::Create( *ast, ast->getTranslationUnitDecl(), // Is this the right decl context? SourceLocation(), SourceLocation(), depth, i, identifier_info, is_typename, parameter_pack)); } } clang::Expr *const requires_clause = nullptr; // TODO: Concepts TemplateParameterList *template_param_list = TemplateParameterList::Create( *ast, SourceLocation(), SourceLocation(), template_param_decls, SourceLocation(), requires_clause); return template_param_list; } clang::FunctionTemplateDecl *ClangASTContext::CreateFunctionTemplateDecl( clang::DeclContext *decl_ctx, clang::FunctionDecl *func_decl, const char *name, const TemplateParameterInfos &template_param_infos) { // /// \brief Create a function template node. ASTContext *ast = getASTContext(); llvm::SmallVector template_param_decls; TemplateParameterList *template_param_list = CreateTemplateParameterList( ast, template_param_infos, template_param_decls); FunctionTemplateDecl *func_tmpl_decl = FunctionTemplateDecl::Create( *ast, decl_ctx, func_decl->getLocation(), func_decl->getDeclName(), template_param_list, func_decl); for (size_t i = 0, template_param_decl_count = template_param_decls.size(); i < template_param_decl_count; ++i) { // TODO: verify which decl context we should put template_param_decls into.. template_param_decls[i]->setDeclContext(func_decl); } return func_tmpl_decl; } void ClangASTContext::CreateFunctionTemplateSpecializationInfo( FunctionDecl *func_decl, clang::FunctionTemplateDecl *func_tmpl_decl, const TemplateParameterInfos &infos) { TemplateArgumentList template_args(TemplateArgumentList::OnStack, infos.args); func_decl->setFunctionTemplateSpecialization(func_tmpl_decl, &template_args, nullptr); } ClassTemplateDecl *ClangASTContext::CreateClassTemplateDecl( DeclContext *decl_ctx, lldb::AccessType access_type, const char *class_name, int kind, const TemplateParameterInfos &template_param_infos) { ASTContext *ast = getASTContext(); ClassTemplateDecl *class_template_decl = nullptr; if (decl_ctx == nullptr) decl_ctx = ast->getTranslationUnitDecl(); IdentifierInfo &identifier_info = ast->Idents.get(class_name); DeclarationName decl_name(&identifier_info); clang::DeclContext::lookup_result result = decl_ctx->lookup(decl_name); for (NamedDecl *decl : result) { class_template_decl = dyn_cast(decl); if (class_template_decl) return class_template_decl; } llvm::SmallVector template_param_decls; TemplateParameterList *template_param_list = CreateTemplateParameterList( ast, template_param_infos, template_param_decls); CXXRecordDecl *template_cxx_decl = CXXRecordDecl::Create( *ast, (TagDecl::TagKind)kind, decl_ctx, // What decl context do we use here? TU? The actual decl // context? SourceLocation(), SourceLocation(), &identifier_info); for (size_t i = 0, template_param_decl_count = template_param_decls.size(); i < template_param_decl_count; ++i) { template_param_decls[i]->setDeclContext(template_cxx_decl); } // With templated classes, we say that a class is templated with // specializations, but that the bare class has no functions. // template_cxx_decl->startDefinition(); // template_cxx_decl->completeDefinition(); class_template_decl = ClassTemplateDecl::Create( *ast, decl_ctx, // What decl context do we use here? TU? The actual decl // context? - SourceLocation(), decl_name, template_param_list, template_cxx_decl, - nullptr); + SourceLocation(), decl_name, template_param_list, template_cxx_decl); if (class_template_decl) { if (access_type != eAccessNone) class_template_decl->setAccess( ConvertAccessTypeToAccessSpecifier(access_type)); // if (TagDecl *ctx_tag_decl = dyn_cast(decl_ctx)) // CompleteTagDeclarationDefinition(GetTypeForDecl(ctx_tag_decl)); decl_ctx->addDecl(class_template_decl); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(class_template_decl); #endif } return class_template_decl; } ClassTemplateSpecializationDecl * ClangASTContext::CreateClassTemplateSpecializationDecl( DeclContext *decl_ctx, ClassTemplateDecl *class_template_decl, int kind, const TemplateParameterInfos &template_param_infos) { ASTContext *ast = getASTContext(); ClassTemplateSpecializationDecl *class_template_specialization_decl = ClassTemplateSpecializationDecl::Create( *ast, (TagDecl::TagKind)kind, decl_ctx, SourceLocation(), SourceLocation(), class_template_decl, template_param_infos.args, nullptr); class_template_specialization_decl->setSpecializationKind( TSK_ExplicitSpecialization); return class_template_specialization_decl; } CompilerType ClangASTContext::CreateClassTemplateSpecializationType( ClassTemplateSpecializationDecl *class_template_specialization_decl) { if (class_template_specialization_decl) { ASTContext *ast = getASTContext(); if (ast) return CompilerType( ast, ast->getTagDeclType(class_template_specialization_decl)); } return CompilerType(); } static inline bool check_op_param(bool is_method, clang::OverloadedOperatorKind op_kind, bool unary, bool binary, uint32_t num_params) { // Special-case call since it can take any number of operands if (op_kind == OO_Call) return true; // The parameter count doesn't include "this" if (is_method) ++num_params; if (num_params == 1) return unary; if (num_params == 2) return binary; else return false; } bool ClangASTContext::CheckOverloadedOperatorKindParameterCount( bool is_method, clang::OverloadedOperatorKind op_kind, uint32_t num_params) { switch (op_kind) { default: break; // C++ standard allows any number of arguments to new/delete case OO_New: case OO_Array_New: case OO_Delete: case OO_Array_Delete: return true; } #define OVERLOADED_OPERATOR(Name, Spelling, Token, Unary, Binary, MemberOnly) \ case OO_##Name: \ return check_op_param(is_method, op_kind, Unary, Binary, num_params); switch (op_kind) { #include "clang/Basic/OperatorKinds.def" default: break; } return false; } clang::AccessSpecifier ClangASTContext::UnifyAccessSpecifiers(clang::AccessSpecifier lhs, clang::AccessSpecifier rhs) { // Make the access equal to the stricter of the field and the nested field's // access if (lhs == AS_none || rhs == AS_none) return AS_none; if (lhs == AS_private || rhs == AS_private) return AS_private; if (lhs == AS_protected || rhs == AS_protected) return AS_protected; return AS_public; } bool ClangASTContext::FieldIsBitfield(FieldDecl *field, uint32_t &bitfield_bit_size) { return FieldIsBitfield(getASTContext(), field, bitfield_bit_size); } bool ClangASTContext::FieldIsBitfield(ASTContext *ast, FieldDecl *field, uint32_t &bitfield_bit_size) { if (ast == nullptr || field == nullptr) return false; if (field->isBitField()) { Expr *bit_width_expr = field->getBitWidth(); if (bit_width_expr) { llvm::APSInt bit_width_apsint; if (bit_width_expr->isIntegerConstantExpr(bit_width_apsint, *ast)) { bitfield_bit_size = bit_width_apsint.getLimitedValue(UINT32_MAX); return true; } } } return false; } bool ClangASTContext::RecordHasFields(const RecordDecl *record_decl) { if (record_decl == nullptr) return false; if (!record_decl->field_empty()) return true; // No fields, lets check this is a CXX record and check the base classes const CXXRecordDecl *cxx_record_decl = dyn_cast(record_decl); if (cxx_record_decl) { CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { const CXXRecordDecl *base_class_decl = cast( base_class->getType()->getAs()->getDecl()); if (RecordHasFields(base_class_decl)) return true; } } return false; } #pragma mark Objective C Classes CompilerType ClangASTContext::CreateObjCClass(const char *name, DeclContext *decl_ctx, bool isForwardDecl, bool isInternal, ClangASTMetadata *metadata) { ASTContext *ast = getASTContext(); assert(ast != nullptr); assert(name && name[0]); if (decl_ctx == nullptr) decl_ctx = ast->getTranslationUnitDecl(); ObjCInterfaceDecl *decl = ObjCInterfaceDecl::Create( *ast, decl_ctx, SourceLocation(), &ast->Idents.get(name), nullptr, nullptr, SourceLocation(), /*isForwardDecl,*/ isInternal); if (decl && metadata) SetMetadata(ast, decl, *metadata); return CompilerType(ast, ast->getObjCInterfaceType(decl)); } static inline bool BaseSpecifierIsEmpty(const CXXBaseSpecifier *b) { return ClangASTContext::RecordHasFields(b->getType()->getAsCXXRecordDecl()) == false; } uint32_t ClangASTContext::GetNumBaseClasses(const CXXRecordDecl *cxx_record_decl, bool omit_empty_base_classes) { uint32_t num_bases = 0; if (cxx_record_decl) { if (omit_empty_base_classes) { CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { // Skip empty base classes if (omit_empty_base_classes) { if (BaseSpecifierIsEmpty(base_class)) continue; } ++num_bases; } } else num_bases = cxx_record_decl->getNumBases(); } return num_bases; } #pragma mark Namespace Declarations NamespaceDecl * ClangASTContext::GetUniqueNamespaceDeclaration(const char *name, DeclContext *decl_ctx) { NamespaceDecl *namespace_decl = nullptr; ASTContext *ast = getASTContext(); TranslationUnitDecl *translation_unit_decl = ast->getTranslationUnitDecl(); if (decl_ctx == nullptr) decl_ctx = translation_unit_decl; if (name) { IdentifierInfo &identifier_info = ast->Idents.get(name); DeclarationName decl_name(&identifier_info); clang::DeclContext::lookup_result result = decl_ctx->lookup(decl_name); for (NamedDecl *decl : result) { namespace_decl = dyn_cast(decl); if (namespace_decl) return namespace_decl; } namespace_decl = NamespaceDecl::Create(*ast, decl_ctx, false, SourceLocation(), SourceLocation(), &identifier_info, nullptr); decl_ctx->addDecl(namespace_decl); } else { if (decl_ctx == translation_unit_decl) { namespace_decl = translation_unit_decl->getAnonymousNamespace(); if (namespace_decl) return namespace_decl; namespace_decl = NamespaceDecl::Create(*ast, decl_ctx, false, SourceLocation(), SourceLocation(), nullptr, nullptr); translation_unit_decl->setAnonymousNamespace(namespace_decl); translation_unit_decl->addDecl(namespace_decl); assert(namespace_decl == translation_unit_decl->getAnonymousNamespace()); } else { NamespaceDecl *parent_namespace_decl = cast(decl_ctx); if (parent_namespace_decl) { namespace_decl = parent_namespace_decl->getAnonymousNamespace(); if (namespace_decl) return namespace_decl; namespace_decl = NamespaceDecl::Create(*ast, decl_ctx, false, SourceLocation(), SourceLocation(), nullptr, nullptr); parent_namespace_decl->setAnonymousNamespace(namespace_decl); parent_namespace_decl->addDecl(namespace_decl); assert(namespace_decl == parent_namespace_decl->getAnonymousNamespace()); } else { // BAD!!! } } } #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(namespace_decl); #endif return namespace_decl; } NamespaceDecl *ClangASTContext::GetUniqueNamespaceDeclaration( clang::ASTContext *ast, const char *name, clang::DeclContext *decl_ctx) { ClangASTContext *ast_ctx = ClangASTContext::GetASTContext(ast); if (ast_ctx == nullptr) return nullptr; return ast_ctx->GetUniqueNamespaceDeclaration(name, decl_ctx); } clang::BlockDecl * ClangASTContext::CreateBlockDeclaration(clang::DeclContext *ctx) { if (ctx != nullptr) { clang::BlockDecl *decl = clang::BlockDecl::Create(*getASTContext(), ctx, clang::SourceLocation()); ctx->addDecl(decl); return decl; } return nullptr; } clang::DeclContext *FindLCABetweenDecls(clang::DeclContext *left, clang::DeclContext *right, clang::DeclContext *root) { if (root == nullptr) return nullptr; std::set path_left; for (clang::DeclContext *d = left; d != nullptr; d = d->getParent()) path_left.insert(d); for (clang::DeclContext *d = right; d != nullptr; d = d->getParent()) if (path_left.find(d) != path_left.end()) return d; return nullptr; } clang::UsingDirectiveDecl *ClangASTContext::CreateUsingDirectiveDeclaration( clang::DeclContext *decl_ctx, clang::NamespaceDecl *ns_decl) { if (decl_ctx != nullptr && ns_decl != nullptr) { clang::TranslationUnitDecl *translation_unit = (clang::TranslationUnitDecl *)GetTranslationUnitDecl(getASTContext()); clang::UsingDirectiveDecl *using_decl = clang::UsingDirectiveDecl::Create( *getASTContext(), decl_ctx, clang::SourceLocation(), clang::SourceLocation(), clang::NestedNameSpecifierLoc(), clang::SourceLocation(), ns_decl, FindLCABetweenDecls(decl_ctx, ns_decl, translation_unit)); decl_ctx->addDecl(using_decl); return using_decl; } return nullptr; } clang::UsingDecl * ClangASTContext::CreateUsingDeclaration(clang::DeclContext *current_decl_ctx, clang::NamedDecl *target) { if (current_decl_ctx != nullptr && target != nullptr) { clang::UsingDecl *using_decl = clang::UsingDecl::Create( *getASTContext(), current_decl_ctx, clang::SourceLocation(), clang::NestedNameSpecifierLoc(), clang::DeclarationNameInfo(), false); clang::UsingShadowDecl *shadow_decl = clang::UsingShadowDecl::Create( *getASTContext(), current_decl_ctx, clang::SourceLocation(), using_decl, target); using_decl->addShadowDecl(shadow_decl); current_decl_ctx->addDecl(using_decl); return using_decl; } return nullptr; } clang::VarDecl *ClangASTContext::CreateVariableDeclaration( clang::DeclContext *decl_context, const char *name, clang::QualType type) { if (decl_context != nullptr) { clang::VarDecl *var_decl = clang::VarDecl::Create( *getASTContext(), decl_context, clang::SourceLocation(), clang::SourceLocation(), name && name[0] ? &getASTContext()->Idents.getOwn(name) : nullptr, type, nullptr, clang::SC_None); var_decl->setAccess(clang::AS_public); decl_context->addDecl(var_decl); return var_decl; } return nullptr; } lldb::opaque_compiler_type_t ClangASTContext::GetOpaqueCompilerType(clang::ASTContext *ast, lldb::BasicType basic_type) { switch (basic_type) { case eBasicTypeVoid: return ast->VoidTy.getAsOpaquePtr(); case eBasicTypeChar: return ast->CharTy.getAsOpaquePtr(); case eBasicTypeSignedChar: return ast->SignedCharTy.getAsOpaquePtr(); case eBasicTypeUnsignedChar: return ast->UnsignedCharTy.getAsOpaquePtr(); case eBasicTypeWChar: return ast->getWCharType().getAsOpaquePtr(); case eBasicTypeSignedWChar: return ast->getSignedWCharType().getAsOpaquePtr(); case eBasicTypeUnsignedWChar: return ast->getUnsignedWCharType().getAsOpaquePtr(); case eBasicTypeChar16: return ast->Char16Ty.getAsOpaquePtr(); case eBasicTypeChar32: return ast->Char32Ty.getAsOpaquePtr(); case eBasicTypeShort: return ast->ShortTy.getAsOpaquePtr(); case eBasicTypeUnsignedShort: return ast->UnsignedShortTy.getAsOpaquePtr(); case eBasicTypeInt: return ast->IntTy.getAsOpaquePtr(); case eBasicTypeUnsignedInt: return ast->UnsignedIntTy.getAsOpaquePtr(); case eBasicTypeLong: return ast->LongTy.getAsOpaquePtr(); case eBasicTypeUnsignedLong: return ast->UnsignedLongTy.getAsOpaquePtr(); case eBasicTypeLongLong: return ast->LongLongTy.getAsOpaquePtr(); case eBasicTypeUnsignedLongLong: return ast->UnsignedLongLongTy.getAsOpaquePtr(); case eBasicTypeInt128: return ast->Int128Ty.getAsOpaquePtr(); case eBasicTypeUnsignedInt128: return ast->UnsignedInt128Ty.getAsOpaquePtr(); case eBasicTypeBool: return ast->BoolTy.getAsOpaquePtr(); case eBasicTypeHalf: return ast->HalfTy.getAsOpaquePtr(); case eBasicTypeFloat: return ast->FloatTy.getAsOpaquePtr(); case eBasicTypeDouble: return ast->DoubleTy.getAsOpaquePtr(); case eBasicTypeLongDouble: return ast->LongDoubleTy.getAsOpaquePtr(); case eBasicTypeFloatComplex: return ast->FloatComplexTy.getAsOpaquePtr(); case eBasicTypeDoubleComplex: return ast->DoubleComplexTy.getAsOpaquePtr(); case eBasicTypeLongDoubleComplex: return ast->LongDoubleComplexTy.getAsOpaquePtr(); case eBasicTypeObjCID: return ast->getObjCIdType().getAsOpaquePtr(); case eBasicTypeObjCClass: return ast->getObjCClassType().getAsOpaquePtr(); case eBasicTypeObjCSel: return ast->getObjCSelType().getAsOpaquePtr(); case eBasicTypeNullPtr: return ast->NullPtrTy.getAsOpaquePtr(); default: return nullptr; } } #pragma mark Function Types clang::DeclarationName ClangASTContext::GetDeclarationName(const char *name, const CompilerType &function_clang_type) { if (!name || !name[0]) return clang::DeclarationName(); clang::OverloadedOperatorKind op_kind = clang::NUM_OVERLOADED_OPERATORS; if (!IsOperator(name, op_kind) || op_kind == clang::NUM_OVERLOADED_OPERATORS) return DeclarationName(&getASTContext()->Idents.get( name)); // Not operator, but a regular function. // Check the number of operator parameters. Sometimes we have // seen bad DWARF that doesn't correctly describe operators and // if we try to create a method and add it to the class, clang // will assert and crash, so we need to make sure things are // acceptable. clang::QualType method_qual_type(ClangUtil::GetQualType(function_clang_type)); const clang::FunctionProtoType *function_type = llvm::dyn_cast(method_qual_type.getTypePtr()); if (function_type == nullptr) return clang::DeclarationName(); const bool is_method = false; const unsigned int num_params = function_type->getNumParams(); if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount( is_method, op_kind, num_params)) return clang::DeclarationName(); return getASTContext()->DeclarationNames.getCXXOperatorName(op_kind); } FunctionDecl *ClangASTContext::CreateFunctionDeclaration( DeclContext *decl_ctx, const char *name, const CompilerType &function_clang_type, int storage, bool is_inline) { FunctionDecl *func_decl = nullptr; ASTContext *ast = getASTContext(); if (decl_ctx == nullptr) decl_ctx = ast->getTranslationUnitDecl(); const bool hasWrittenPrototype = true; const bool isConstexprSpecified = false; clang::DeclarationName declarationName = GetDeclarationName(name, function_clang_type); func_decl = FunctionDecl::Create( *ast, decl_ctx, SourceLocation(), SourceLocation(), declarationName, ClangUtil::GetQualType(function_clang_type), nullptr, (clang::StorageClass)storage, is_inline, hasWrittenPrototype, isConstexprSpecified); if (func_decl) decl_ctx->addDecl(func_decl); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(func_decl); #endif return func_decl; } CompilerType ClangASTContext::CreateFunctionType( ASTContext *ast, const CompilerType &result_type, const CompilerType *args, unsigned num_args, bool is_variadic, unsigned type_quals) { if (ast == nullptr) return CompilerType(); // invalid AST if (!result_type || !ClangUtil::IsClangType(result_type)) return CompilerType(); // invalid return type std::vector qual_type_args; if (num_args > 0 && args == nullptr) return CompilerType(); // invalid argument array passed in // Verify that all arguments are valid and the right type for (unsigned i = 0; i < num_args; ++i) { if (args[i]) { // Make sure we have a clang type in args[i] and not a type from another // language whose name might match const bool is_clang_type = ClangUtil::IsClangType(args[i]); lldbassert(is_clang_type); if (is_clang_type) qual_type_args.push_back(ClangUtil::GetQualType(args[i])); else return CompilerType(); // invalid argument type (must be a clang type) } else return CompilerType(); // invalid argument type (empty) } // TODO: Detect calling convention in DWARF? FunctionProtoType::ExtProtoInfo proto_info; proto_info.Variadic = is_variadic; proto_info.ExceptionSpec = EST_None; proto_info.TypeQuals = type_quals; proto_info.RefQualifier = RQ_None; return CompilerType(ast, ast->getFunctionType(ClangUtil::GetQualType(result_type), qual_type_args, proto_info)); } ParmVarDecl *ClangASTContext::CreateParameterDeclaration( const char *name, const CompilerType ¶m_type, int storage) { ASTContext *ast = getASTContext(); assert(ast != nullptr); return ParmVarDecl::Create(*ast, ast->getTranslationUnitDecl(), SourceLocation(), SourceLocation(), name && name[0] ? &ast->Idents.get(name) : nullptr, ClangUtil::GetQualType(param_type), nullptr, (clang::StorageClass)storage, nullptr); } void ClangASTContext::SetFunctionParameters(FunctionDecl *function_decl, ParmVarDecl **params, unsigned num_params) { if (function_decl) function_decl->setParams(ArrayRef(params, num_params)); } CompilerType ClangASTContext::CreateBlockPointerType(const CompilerType &function_type) { QualType block_type = m_ast_ap->getBlockPointerType( clang::QualType::getFromOpaquePtr(function_type.GetOpaqueQualType())); return CompilerType(this, block_type.getAsOpaquePtr()); } #pragma mark Array Types CompilerType ClangASTContext::CreateArrayType(const CompilerType &element_type, size_t element_count, bool is_vector) { if (element_type.IsValid()) { ASTContext *ast = getASTContext(); assert(ast != nullptr); if (is_vector) { return CompilerType( ast, ast->getExtVectorType(ClangUtil::GetQualType(element_type), element_count)); } else { llvm::APInt ap_element_count(64, element_count); if (element_count == 0) { return CompilerType(ast, ast->getIncompleteArrayType( ClangUtil::GetQualType(element_type), clang::ArrayType::Normal, 0)); } else { return CompilerType( ast, ast->getConstantArrayType(ClangUtil::GetQualType(element_type), ap_element_count, clang::ArrayType::Normal, 0)); } } } return CompilerType(); } CompilerType ClangASTContext::CreateStructForIdentifier( const ConstString &type_name, const std::initializer_list> &type_fields, bool packed) { CompilerType type; if (!type_name.IsEmpty() && (type = GetTypeForIdentifier(type_name)) .IsValid()) { lldbassert(0 && "Trying to create a type for an existing name"); return type; } type = CreateRecordType(nullptr, lldb::eAccessPublic, type_name.GetCString(), clang::TTK_Struct, lldb::eLanguageTypeC); StartTagDeclarationDefinition(type); for (const auto &field : type_fields) AddFieldToRecordType(type, field.first, field.second, lldb::eAccessPublic, 0); if (packed) SetIsPacked(type); CompleteTagDeclarationDefinition(type); return type; } CompilerType ClangASTContext::GetOrCreateStructForIdentifier( const ConstString &type_name, const std::initializer_list> &type_fields, bool packed) { CompilerType type; if ((type = GetTypeForIdentifier(type_name)).IsValid()) return type; return CreateStructForIdentifier(type_name, type_fields, packed); } #pragma mark Enumeration Types CompilerType ClangASTContext::CreateEnumerationType(const char *name, DeclContext *decl_ctx, const Declaration &decl, const CompilerType &integer_clang_type) { // TODO: Do something intelligent with the Declaration object passed in // like maybe filling in the SourceLocation with it... ASTContext *ast = getASTContext(); // TODO: ask about these... // const bool IsScoped = false; // const bool IsFixed = false; EnumDecl *enum_decl = EnumDecl::Create( *ast, decl_ctx, SourceLocation(), SourceLocation(), name && name[0] ? &ast->Idents.get(name) : nullptr, nullptr, false, // IsScoped false, // IsScopedUsingClassTag false); // IsFixed if (enum_decl) { // TODO: check if we should be setting the promotion type too? enum_decl->setIntegerType(ClangUtil::GetQualType(integer_clang_type)); enum_decl->setAccess(AS_public); // TODO respect what's in the debug info return CompilerType(ast, ast->getTagDeclType(enum_decl)); } return CompilerType(); } // Disable this for now since I can't seem to get a nicely formatted float // out of the APFloat class without just getting the float, double or quad // and then using a formatted print on it which defeats the purpose. We ideally // would like to get perfect string values for any kind of float semantics // so we can support remote targets. The code below also requires a patch to // llvm::APInt. // bool // ClangASTContext::ConvertFloatValueToString (ASTContext *ast, // lldb::opaque_compiler_type_t clang_type, const uint8_t* bytes, size_t // byte_size, int apint_byte_order, std::string &float_str) //{ // uint32_t count = 0; // bool is_complex = false; // if (ClangASTContext::IsFloatingPointType (clang_type, count, is_complex)) // { // unsigned num_bytes_per_float = byte_size / count; // unsigned num_bits_per_float = num_bytes_per_float * 8; // // float_str.clear(); // uint32_t i; // for (i=0; i 0) // { // if (i > 0) // float_str.append(", "); // float_str.append(s); // if (i == 1 && is_complex) // float_str.append(1, 'i'); // } // } // return !float_str.empty(); // } // return false; //} CompilerType ClangASTContext::GetIntTypeFromBitSize(clang::ASTContext *ast, size_t bit_size, bool is_signed) { if (ast) { if (is_signed) { if (bit_size == ast->getTypeSize(ast->SignedCharTy)) return CompilerType(ast, ast->SignedCharTy); if (bit_size == ast->getTypeSize(ast->ShortTy)) return CompilerType(ast, ast->ShortTy); if (bit_size == ast->getTypeSize(ast->IntTy)) return CompilerType(ast, ast->IntTy); if (bit_size == ast->getTypeSize(ast->LongTy)) return CompilerType(ast, ast->LongTy); if (bit_size == ast->getTypeSize(ast->LongLongTy)) return CompilerType(ast, ast->LongLongTy); if (bit_size == ast->getTypeSize(ast->Int128Ty)) return CompilerType(ast, ast->Int128Ty); } else { if (bit_size == ast->getTypeSize(ast->UnsignedCharTy)) return CompilerType(ast, ast->UnsignedCharTy); if (bit_size == ast->getTypeSize(ast->UnsignedShortTy)) return CompilerType(ast, ast->UnsignedShortTy); if (bit_size == ast->getTypeSize(ast->UnsignedIntTy)) return CompilerType(ast, ast->UnsignedIntTy); if (bit_size == ast->getTypeSize(ast->UnsignedLongTy)) return CompilerType(ast, ast->UnsignedLongTy); if (bit_size == ast->getTypeSize(ast->UnsignedLongLongTy)) return CompilerType(ast, ast->UnsignedLongLongTy); if (bit_size == ast->getTypeSize(ast->UnsignedInt128Ty)) return CompilerType(ast, ast->UnsignedInt128Ty); } } return CompilerType(); } CompilerType ClangASTContext::GetPointerSizedIntType(clang::ASTContext *ast, bool is_signed) { if (ast) return GetIntTypeFromBitSize(ast, ast->getTypeSize(ast->VoidPtrTy), is_signed); return CompilerType(); } void ClangASTContext::DumpDeclContextHiearchy(clang::DeclContext *decl_ctx) { if (decl_ctx) { DumpDeclContextHiearchy(decl_ctx->getParent()); clang::NamedDecl *named_decl = llvm::dyn_cast(decl_ctx); if (named_decl) { printf("%20s: %s\n", decl_ctx->getDeclKindName(), named_decl->getDeclName().getAsString().c_str()); } else { printf("%20s\n", decl_ctx->getDeclKindName()); } } } void ClangASTContext::DumpDeclHiearchy(clang::Decl *decl) { if (decl == nullptr) return; DumpDeclContextHiearchy(decl->getDeclContext()); clang::RecordDecl *record_decl = llvm::dyn_cast(decl); if (record_decl) { printf("%20s: %s%s\n", decl->getDeclKindName(), record_decl->getDeclName().getAsString().c_str(), record_decl->isInjectedClassName() ? " (injected class name)" : ""); } else { clang::NamedDecl *named_decl = llvm::dyn_cast(decl); if (named_decl) { printf("%20s: %s\n", decl->getDeclKindName(), named_decl->getDeclName().getAsString().c_str()); } else { printf("%20s\n", decl->getDeclKindName()); } } } bool ClangASTContext::DeclsAreEquivalent(clang::Decl *lhs_decl, clang::Decl *rhs_decl) { if (lhs_decl && rhs_decl) { //---------------------------------------------------------------------- // Make sure the decl kinds match first //---------------------------------------------------------------------- const clang::Decl::Kind lhs_decl_kind = lhs_decl->getKind(); const clang::Decl::Kind rhs_decl_kind = rhs_decl->getKind(); if (lhs_decl_kind == rhs_decl_kind) { //------------------------------------------------------------------ // Now check that the decl contexts kinds are all equivalent // before we have to check any names of the decl contexts... //------------------------------------------------------------------ clang::DeclContext *lhs_decl_ctx = lhs_decl->getDeclContext(); clang::DeclContext *rhs_decl_ctx = rhs_decl->getDeclContext(); if (lhs_decl_ctx && rhs_decl_ctx) { while (1) { if (lhs_decl_ctx && rhs_decl_ctx) { const clang::Decl::Kind lhs_decl_ctx_kind = lhs_decl_ctx->getDeclKind(); const clang::Decl::Kind rhs_decl_ctx_kind = rhs_decl_ctx->getDeclKind(); if (lhs_decl_ctx_kind == rhs_decl_ctx_kind) { lhs_decl_ctx = lhs_decl_ctx->getParent(); rhs_decl_ctx = rhs_decl_ctx->getParent(); if (lhs_decl_ctx == nullptr && rhs_decl_ctx == nullptr) break; } else return false; } else return false; } //-------------------------------------------------------------- // Now make sure the name of the decls match //-------------------------------------------------------------- clang::NamedDecl *lhs_named_decl = llvm::dyn_cast(lhs_decl); clang::NamedDecl *rhs_named_decl = llvm::dyn_cast(rhs_decl); if (lhs_named_decl && rhs_named_decl) { clang::DeclarationName lhs_decl_name = lhs_named_decl->getDeclName(); clang::DeclarationName rhs_decl_name = rhs_named_decl->getDeclName(); if (lhs_decl_name.getNameKind() == rhs_decl_name.getNameKind()) { if (lhs_decl_name.getAsString() != rhs_decl_name.getAsString()) return false; } else return false; } else return false; //-------------------------------------------------------------- // We know that the decl context kinds all match, so now we need // to make sure the names match as well //-------------------------------------------------------------- lhs_decl_ctx = lhs_decl->getDeclContext(); rhs_decl_ctx = rhs_decl->getDeclContext(); while (1) { switch (lhs_decl_ctx->getDeclKind()) { case clang::Decl::TranslationUnit: // We don't care about the translation unit names return true; default: { clang::NamedDecl *lhs_named_decl = llvm::dyn_cast(lhs_decl_ctx); clang::NamedDecl *rhs_named_decl = llvm::dyn_cast(rhs_decl_ctx); if (lhs_named_decl && rhs_named_decl) { clang::DeclarationName lhs_decl_name = lhs_named_decl->getDeclName(); clang::DeclarationName rhs_decl_name = rhs_named_decl->getDeclName(); if (lhs_decl_name.getNameKind() == rhs_decl_name.getNameKind()) { if (lhs_decl_name.getAsString() != rhs_decl_name.getAsString()) return false; } else return false; } else return false; } break; } lhs_decl_ctx = lhs_decl_ctx->getParent(); rhs_decl_ctx = rhs_decl_ctx->getParent(); } } } } return false; } bool ClangASTContext::GetCompleteDecl(clang::ASTContext *ast, clang::Decl *decl) { if (!decl) return false; ExternalASTSource *ast_source = ast->getExternalSource(); if (!ast_source) return false; if (clang::TagDecl *tag_decl = llvm::dyn_cast(decl)) { if (tag_decl->isCompleteDefinition()) return true; if (!tag_decl->hasExternalLexicalStorage()) return false; ast_source->CompleteType(tag_decl); return !tag_decl->getTypeForDecl()->isIncompleteType(); } else if (clang::ObjCInterfaceDecl *objc_interface_decl = llvm::dyn_cast(decl)) { if (objc_interface_decl->getDefinition()) return true; if (!objc_interface_decl->hasExternalLexicalStorage()) return false; ast_source->CompleteType(objc_interface_decl); return !objc_interface_decl->getTypeForDecl()->isIncompleteType(); } else { return false; } } void ClangASTContext::SetMetadataAsUserID(const void *object, user_id_t user_id) { ClangASTMetadata meta_data; meta_data.SetUserID(user_id); SetMetadata(object, meta_data); } void ClangASTContext::SetMetadata(clang::ASTContext *ast, const void *object, ClangASTMetadata &metadata) { ClangExternalASTSourceCommon *external_source = ClangExternalASTSourceCommon::Lookup(ast->getExternalSource()); if (external_source) external_source->SetMetadata(object, metadata); } ClangASTMetadata *ClangASTContext::GetMetadata(clang::ASTContext *ast, const void *object) { ClangExternalASTSourceCommon *external_source = ClangExternalASTSourceCommon::Lookup(ast->getExternalSource()); if (external_source && external_source->HasMetadata(object)) return external_source->GetMetadata(object); else return nullptr; } clang::DeclContext * ClangASTContext::GetAsDeclContext(clang::CXXMethodDecl *cxx_method_decl) { return llvm::dyn_cast(cxx_method_decl); } clang::DeclContext * ClangASTContext::GetAsDeclContext(clang::ObjCMethodDecl *objc_method_decl) { return llvm::dyn_cast(objc_method_decl); } bool ClangASTContext::SetTagTypeKind(clang::QualType tag_qual_type, int kind) const { const clang::Type *clang_type = tag_qual_type.getTypePtr(); if (clang_type) { const clang::TagType *tag_type = llvm::dyn_cast(clang_type); if (tag_type) { clang::TagDecl *tag_decl = llvm::dyn_cast(tag_type->getDecl()); if (tag_decl) { tag_decl->setTagKind((clang::TagDecl::TagKind)kind); return true; } } } return false; } bool ClangASTContext::SetDefaultAccessForRecordFields( clang::RecordDecl *record_decl, int default_accessibility, int *assigned_accessibilities, size_t num_assigned_accessibilities) { if (record_decl) { uint32_t field_idx; clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(), field_idx = 0; field != field_end; ++field, ++field_idx) { // If no accessibility was assigned, assign the correct one if (field_idx < num_assigned_accessibilities && assigned_accessibilities[field_idx] == clang::AS_none) field->setAccess((clang::AccessSpecifier)default_accessibility); } return true; } return false; } clang::DeclContext * ClangASTContext::GetDeclContextForType(const CompilerType &type) { return GetDeclContextForType(ClangUtil::GetQualType(type)); } clang::DeclContext * ClangASTContext::GetDeclContextForType(clang::QualType type) { if (type.isNull()) return nullptr; clang::QualType qual_type = type.getCanonicalType(); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::ObjCInterface: return llvm::cast(qual_type.getTypePtr()) ->getInterface(); case clang::Type::ObjCObjectPointer: return GetDeclContextForType( llvm::cast(qual_type.getTypePtr()) ->getPointeeType()); case clang::Type::Record: return llvm::cast(qual_type)->getDecl(); case clang::Type::Enum: return llvm::cast(qual_type)->getDecl(); case clang::Type::Typedef: return GetDeclContextForType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()); case clang::Type::Auto: return GetDeclContextForType( llvm::cast(qual_type)->getDeducedType()); case clang::Type::Elaborated: return GetDeclContextForType( llvm::cast(qual_type)->getNamedType()); case clang::Type::Paren: return GetDeclContextForType( llvm::cast(qual_type)->desugar()); default: break; } // No DeclContext in this type... return nullptr; } static bool GetCompleteQualType(clang::ASTContext *ast, clang::QualType qual_type, bool allow_completion = true) { const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::ConstantArray: case clang::Type::IncompleteArray: case clang::Type::VariableArray: { const clang::ArrayType *array_type = llvm::dyn_cast(qual_type.getTypePtr()); if (array_type) return GetCompleteQualType(ast, array_type->getElementType(), allow_completion); } break; case clang::Type::Record: { clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { if (cxx_record_decl->hasExternalLexicalStorage()) { const bool is_complete = cxx_record_decl->isCompleteDefinition(); const bool fields_loaded = cxx_record_decl->hasLoadedFieldsFromExternalStorage(); if (is_complete && fields_loaded) return true; if (!allow_completion) return false; // Call the field_begin() accessor to for it to use the external source // to load the fields... clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); if (external_ast_source) { external_ast_source->CompleteType(cxx_record_decl); if (cxx_record_decl->isCompleteDefinition()) { cxx_record_decl->field_begin(); cxx_record_decl->setHasLoadedFieldsFromExternalStorage(true); } } } } const clang::TagType *tag_type = llvm::cast(qual_type.getTypePtr()); return !tag_type->isIncompleteType(); } break; case clang::Type::Enum: { const clang::TagType *tag_type = llvm::dyn_cast(qual_type.getTypePtr()); if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) { if (tag_decl->getDefinition()) return true; if (!allow_completion) return false; if (tag_decl->hasExternalLexicalStorage()) { if (ast) { clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); if (external_ast_source) { external_ast_source->CompleteType(tag_decl); return !tag_type->isIncompleteType(); } } } return false; } } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); // We currently can't complete objective C types through the newly added // ASTContext // because it only supports TagDecl objects right now... if (class_interface_decl) { if (class_interface_decl->getDefinition()) return true; if (!allow_completion) return false; if (class_interface_decl->hasExternalLexicalStorage()) { if (ast) { clang::ExternalASTSource *external_ast_source = ast->getExternalSource(); if (external_ast_source) { external_ast_source->CompleteType(class_interface_decl); return !objc_class_type->isIncompleteType(); } } } return false; } } } break; case clang::Type::Typedef: return GetCompleteQualType(ast, llvm::cast(qual_type) ->getDecl() ->getUnderlyingType(), allow_completion); case clang::Type::Auto: return GetCompleteQualType( ast, llvm::cast(qual_type)->getDeducedType(), allow_completion); case clang::Type::Elaborated: return GetCompleteQualType( ast, llvm::cast(qual_type)->getNamedType(), allow_completion); case clang::Type::Paren: return GetCompleteQualType( ast, llvm::cast(qual_type)->desugar(), allow_completion); case clang::Type::Attributed: return GetCompleteQualType( ast, llvm::cast(qual_type)->getModifiedType(), allow_completion); default: break; } return true; } static clang::ObjCIvarDecl::AccessControl ConvertAccessTypeToObjCIvarAccessControl(AccessType access) { switch (access) { case eAccessNone: return clang::ObjCIvarDecl::None; case eAccessPublic: return clang::ObjCIvarDecl::Public; case eAccessPrivate: return clang::ObjCIvarDecl::Private; case eAccessProtected: return clang::ObjCIvarDecl::Protected; case eAccessPackage: return clang::ObjCIvarDecl::Package; } return clang::ObjCIvarDecl::None; } //---------------------------------------------------------------------- // Tests //---------------------------------------------------------------------- bool ClangASTContext::IsAggregateType(lldb::opaque_compiler_type_t type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::IncompleteArray: case clang::Type::VariableArray: case clang::Type::ConstantArray: case clang::Type::ExtVector: case clang::Type::Vector: case clang::Type::Record: case clang::Type::ObjCObject: case clang::Type::ObjCInterface: return true; case clang::Type::Auto: return IsAggregateType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr()); case clang::Type::Elaborated: return IsAggregateType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr()); case clang::Type::Typedef: return IsAggregateType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr()); case clang::Type::Paren: return IsAggregateType( llvm::cast(qual_type)->desugar().getAsOpaquePtr()); default: break; } // The clang type does have a value return false; } bool ClangASTContext::IsAnonymousType(lldb::opaque_compiler_type_t type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: { if (const clang::RecordType *record_type = llvm::dyn_cast_or_null( qual_type.getTypePtrOrNull())) { if (const clang::RecordDecl *record_decl = record_type->getDecl()) { return record_decl->isAnonymousStructOrUnion(); } } break; } case clang::Type::Auto: return IsAnonymousType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr()); case clang::Type::Elaborated: return IsAnonymousType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr()); case clang::Type::Typedef: return IsAnonymousType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr()); case clang::Type::Paren: return IsAnonymousType( llvm::cast(qual_type)->desugar().getAsOpaquePtr()); default: break; } // The clang type does have a value return false; } bool ClangASTContext::IsArrayType(lldb::opaque_compiler_type_t type, CompilerType *element_type_ptr, uint64_t *size, bool *is_incomplete) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { default: break; case clang::Type::ConstantArray: if (element_type_ptr) element_type_ptr->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getElementType()); if (size) *size = llvm::cast(qual_type) ->getSize() .getLimitedValue(ULLONG_MAX); if (is_incomplete) *is_incomplete = false; return true; case clang::Type::IncompleteArray: if (element_type_ptr) element_type_ptr->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getElementType()); if (size) *size = 0; if (is_incomplete) *is_incomplete = true; return true; case clang::Type::VariableArray: if (element_type_ptr) element_type_ptr->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getElementType()); if (size) *size = 0; if (is_incomplete) *is_incomplete = false; return true; case clang::Type::DependentSizedArray: if (element_type_ptr) element_type_ptr->SetCompilerType( getASTContext(), llvm::cast(qual_type) ->getElementType()); if (size) *size = 0; if (is_incomplete) *is_incomplete = false; return true; case clang::Type::Typedef: return IsArrayType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), element_type_ptr, size, is_incomplete); case clang::Type::Auto: return IsArrayType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), element_type_ptr, size, is_incomplete); case clang::Type::Elaborated: return IsArrayType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), element_type_ptr, size, is_incomplete); case clang::Type::Paren: return IsArrayType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), element_type_ptr, size, is_incomplete); } if (element_type_ptr) element_type_ptr->Clear(); if (size) *size = 0; if (is_incomplete) *is_incomplete = false; return false; } bool ClangASTContext::IsVectorType(lldb::opaque_compiler_type_t type, CompilerType *element_type, uint64_t *size) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Vector: { const clang::VectorType *vector_type = qual_type->getAs(); if (vector_type) { if (size) *size = vector_type->getNumElements(); if (element_type) *element_type = CompilerType(getASTContext(), vector_type->getElementType()); } return true; } break; case clang::Type::ExtVector: { const clang::ExtVectorType *ext_vector_type = qual_type->getAs(); if (ext_vector_type) { if (size) *size = ext_vector_type->getNumElements(); if (element_type) *element_type = CompilerType(getASTContext(), ext_vector_type->getElementType()); } return true; } default: break; } return false; } bool ClangASTContext::IsRuntimeGeneratedType( lldb::opaque_compiler_type_t type) { clang::DeclContext *decl_ctx = ClangASTContext::GetASTContext(getASTContext()) ->GetDeclContextForType(GetQualType(type)); if (!decl_ctx) return false; if (!llvm::isa(decl_ctx)) return false; clang::ObjCInterfaceDecl *result_iface_decl = llvm::dyn_cast(decl_ctx); ClangASTMetadata *ast_metadata = ClangASTContext::GetMetadata(getASTContext(), result_iface_decl); if (!ast_metadata) return false; return (ast_metadata->GetISAPtr() != 0); } bool ClangASTContext::IsCharType(lldb::opaque_compiler_type_t type) { return GetQualType(type).getUnqualifiedType()->isCharType(); } bool ClangASTContext::IsCompleteType(lldb::opaque_compiler_type_t type) { const bool allow_completion = false; return GetCompleteQualType(getASTContext(), GetQualType(type), allow_completion); } bool ClangASTContext::IsConst(lldb::opaque_compiler_type_t type) { return GetQualType(type).isConstQualified(); } bool ClangASTContext::IsCStringType(lldb::opaque_compiler_type_t type, uint32_t &length) { CompilerType pointee_or_element_clang_type; length = 0; Flags type_flags(GetTypeInfo(type, &pointee_or_element_clang_type)); if (!pointee_or_element_clang_type.IsValid()) return false; if (type_flags.AnySet(eTypeIsArray | eTypeIsPointer)) { if (pointee_or_element_clang_type.IsCharType()) { if (type_flags.Test(eTypeIsArray)) { // We know the size of the array and it could be a C string // since it is an array of characters length = llvm::cast( GetCanonicalQualType(type).getTypePtr()) ->getSize() .getLimitedValue(); } return true; } } return false; } bool ClangASTContext::IsFunctionType(lldb::opaque_compiler_type_t type, bool *is_variadic_ptr) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); if (qual_type->isFunctionType()) { if (is_variadic_ptr) { const clang::FunctionProtoType *function_proto_type = llvm::dyn_cast(qual_type.getTypePtr()); if (function_proto_type) *is_variadic_ptr = function_proto_type->isVariadic(); else *is_variadic_ptr = false; } return true; } const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { default: break; case clang::Type::Typedef: return IsFunctionType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), nullptr); case clang::Type::Auto: return IsFunctionType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), nullptr); case clang::Type::Elaborated: return IsFunctionType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), nullptr); case clang::Type::Paren: return IsFunctionType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), nullptr); case clang::Type::LValueReference: case clang::Type::RValueReference: { const clang::ReferenceType *reference_type = llvm::cast(qual_type.getTypePtr()); if (reference_type) return IsFunctionType(reference_type->getPointeeType().getAsOpaquePtr(), nullptr); } break; } } return false; } // Used to detect "Homogeneous Floating-point Aggregates" uint32_t ClangASTContext::IsHomogeneousAggregate(lldb::opaque_compiler_type_t type, CompilerType *base_type_ptr) { if (!type) return 0; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { if (cxx_record_decl->getNumBases() || cxx_record_decl->isDynamicClass()) return 0; } const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); if (record_type) { const clang::RecordDecl *record_decl = record_type->getDecl(); if (record_decl) { // We are looking for a structure that contains only floating point // types clang::RecordDecl::field_iterator field_pos, field_end = record_decl->field_end(); uint32_t num_fields = 0; bool is_hva = false; bool is_hfa = false; clang::QualType base_qual_type; uint64_t base_bitwidth = 0; for (field_pos = record_decl->field_begin(); field_pos != field_end; ++field_pos) { clang::QualType field_qual_type = field_pos->getType(); uint64_t field_bitwidth = getASTContext()->getTypeSize(qual_type); if (field_qual_type->isFloatingType()) { if (field_qual_type->isComplexType()) return 0; else { if (num_fields == 0) base_qual_type = field_qual_type; else { if (is_hva) return 0; is_hfa = true; if (field_qual_type.getTypePtr() != base_qual_type.getTypePtr()) return 0; } } } else if (field_qual_type->isVectorType() || field_qual_type->isExtVectorType()) { if (num_fields == 0) { base_qual_type = field_qual_type; base_bitwidth = field_bitwidth; } else { if (is_hfa) return 0; is_hva = true; if (base_bitwidth != field_bitwidth) return 0; if (field_qual_type.getTypePtr() != base_qual_type.getTypePtr()) return 0; } } else return 0; ++num_fields; } if (base_type_ptr) *base_type_ptr = CompilerType(getASTContext(), base_qual_type); return num_fields; } } } break; case clang::Type::Typedef: return IsHomogeneousAggregate(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), base_type_ptr); case clang::Type::Auto: return IsHomogeneousAggregate(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), base_type_ptr); case clang::Type::Elaborated: return IsHomogeneousAggregate(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), base_type_ptr); default: break; } return 0; } size_t ClangASTContext::GetNumberOfFunctionArguments( lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::FunctionProtoType *func = llvm::dyn_cast(qual_type.getTypePtr()); if (func) return func->getNumParams(); } return 0; } CompilerType ClangASTContext::GetFunctionArgumentAtIndex(lldb::opaque_compiler_type_t type, const size_t index) { if (type) { clang::QualType qual_type(GetQualType(type)); const clang::FunctionProtoType *func = llvm::dyn_cast(qual_type.getTypePtr()); if (func) { if (index < func->getNumParams()) return CompilerType(getASTContext(), func->getParamType(index)); } } return CompilerType(); } bool ClangASTContext::IsFunctionPointerType(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); if (qual_type->isFunctionPointerType()) return true; const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { default: break; case clang::Type::Typedef: return IsFunctionPointerType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr()); case clang::Type::Auto: return IsFunctionPointerType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr()); case clang::Type::Elaborated: return IsFunctionPointerType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr()); case clang::Type::Paren: return IsFunctionPointerType( llvm::cast(qual_type)->desugar().getAsOpaquePtr()); case clang::Type::LValueReference: case clang::Type::RValueReference: { const clang::ReferenceType *reference_type = llvm::cast(qual_type.getTypePtr()); if (reference_type) return IsFunctionPointerType( reference_type->getPointeeType().getAsOpaquePtr()); } break; } } return false; } bool ClangASTContext::IsBlockPointerType( lldb::opaque_compiler_type_t type, CompilerType *function_pointer_type_ptr) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); if (qual_type->isBlockPointerType()) { if (function_pointer_type_ptr) { const clang::BlockPointerType *block_pointer_type = qual_type->getAs(); QualType pointee_type = block_pointer_type->getPointeeType(); QualType function_pointer_type = m_ast_ap->getPointerType(pointee_type); *function_pointer_type_ptr = CompilerType(getASTContext(), function_pointer_type); } return true; } const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { default: break; case clang::Type::Typedef: return IsBlockPointerType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), function_pointer_type_ptr); case clang::Type::Auto: return IsBlockPointerType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), function_pointer_type_ptr); case clang::Type::Elaborated: return IsBlockPointerType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), function_pointer_type_ptr); case clang::Type::Paren: return IsBlockPointerType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), function_pointer_type_ptr); case clang::Type::LValueReference: case clang::Type::RValueReference: { const clang::ReferenceType *reference_type = llvm::cast(qual_type.getTypePtr()); if (reference_type) return IsBlockPointerType( reference_type->getPointeeType().getAsOpaquePtr(), function_pointer_type_ptr); } break; } } return false; } bool ClangASTContext::IsIntegerType(lldb::opaque_compiler_type_t type, bool &is_signed) { if (!type) return false; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::BuiltinType *builtin_type = llvm::dyn_cast(qual_type->getCanonicalTypeInternal()); if (builtin_type) { if (builtin_type->isInteger()) { is_signed = builtin_type->isSignedInteger(); return true; } } return false; } bool ClangASTContext::IsEnumerationType(lldb::opaque_compiler_type_t type, bool &is_signed) { if (type) { const clang::EnumType *enum_type = llvm::dyn_cast( GetCanonicalQualType(type)->getCanonicalTypeInternal()); if (enum_type) { IsIntegerType(enum_type->getDecl()->getIntegerType().getAsOpaquePtr(), is_signed); return true; } } return false; } bool ClangASTContext::IsPointerType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { default: break; case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCClass: return true; } return false; case clang::Type::ObjCObjectPointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type) ->getPointeeType()); return true; case clang::Type::BlockPointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getPointeeType()); return true; case clang::Type::Pointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getPointeeType()); return true; case clang::Type::MemberPointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getPointeeType()); return true; case clang::Type::Typedef: return IsPointerType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), pointee_type); case clang::Type::Auto: return IsPointerType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), pointee_type); case clang::Type::Elaborated: return IsPointerType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), pointee_type); case clang::Type::Paren: return IsPointerType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), pointee_type); default: break; } } if (pointee_type) pointee_type->Clear(); return false; } bool ClangASTContext::IsPointerOrReferenceType( lldb::opaque_compiler_type_t type, CompilerType *pointee_type) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { default: break; case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCClass: return true; } return false; case clang::Type::ObjCObjectPointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type) ->getPointeeType()); return true; case clang::Type::BlockPointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getPointeeType()); return true; case clang::Type::Pointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getPointeeType()); return true; case clang::Type::MemberPointer: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getPointeeType()); return true; case clang::Type::LValueReference: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->desugar()); return true; case clang::Type::RValueReference: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->desugar()); return true; case clang::Type::Typedef: return IsPointerOrReferenceType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), pointee_type); case clang::Type::Auto: return IsPointerOrReferenceType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), pointee_type); case clang::Type::Elaborated: return IsPointerOrReferenceType( llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), pointee_type); case clang::Type::Paren: return IsPointerOrReferenceType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), pointee_type); default: break; } } if (pointee_type) pointee_type->Clear(); return false; } bool ClangASTContext::IsReferenceType(lldb::opaque_compiler_type_t type, CompilerType *pointee_type, bool *is_rvalue) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::LValueReference: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->desugar()); if (is_rvalue) *is_rvalue = false; return true; case clang::Type::RValueReference: if (pointee_type) pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->desugar()); if (is_rvalue) *is_rvalue = true; return true; case clang::Type::Typedef: return IsReferenceType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), pointee_type, is_rvalue); case clang::Type::Auto: return IsReferenceType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), pointee_type, is_rvalue); case clang::Type::Elaborated: return IsReferenceType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), pointee_type, is_rvalue); case clang::Type::Paren: return IsReferenceType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), pointee_type, is_rvalue); default: break; } } if (pointee_type) pointee_type->Clear(); return false; } bool ClangASTContext::IsFloatingPointType(lldb::opaque_compiler_type_t type, uint32_t &count, bool &is_complex) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); if (const clang::BuiltinType *BT = llvm::dyn_cast( qual_type->getCanonicalTypeInternal())) { clang::BuiltinType::Kind kind = BT->getKind(); if (kind >= clang::BuiltinType::Float && kind <= clang::BuiltinType::LongDouble) { count = 1; is_complex = false; return true; } } else if (const clang::ComplexType *CT = llvm::dyn_cast( qual_type->getCanonicalTypeInternal())) { if (IsFloatingPointType(CT->getElementType().getAsOpaquePtr(), count, is_complex)) { count = 2; is_complex = true; return true; } } else if (const clang::VectorType *VT = llvm::dyn_cast( qual_type->getCanonicalTypeInternal())) { if (IsFloatingPointType(VT->getElementType().getAsOpaquePtr(), count, is_complex)) { count = VT->getNumElements(); is_complex = false; return true; } } } count = 0; is_complex = false; return false; } bool ClangASTContext::IsDefined(lldb::opaque_compiler_type_t type) { if (!type) return false; clang::QualType qual_type(GetQualType(type)); const clang::TagType *tag_type = llvm::dyn_cast(qual_type.getTypePtr()); if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) return tag_decl->isCompleteDefinition(); return false; } else { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) return class_interface_decl->getDefinition() != nullptr; return false; } } return true; } bool ClangASTContext::IsObjCClassType(const CompilerType &type) { if (type) { clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type)); const clang::ObjCObjectPointerType *obj_pointer_type = llvm::dyn_cast(qual_type); if (obj_pointer_type) return obj_pointer_type->isObjCClassType(); } return false; } bool ClangASTContext::IsObjCObjectOrInterfaceType(const CompilerType &type) { if (ClangUtil::IsClangType(type)) return ClangUtil::GetCanonicalQualType(type)->isObjCObjectOrInterfaceType(); return false; } bool ClangASTContext::IsClassType(lldb::opaque_compiler_type_t type) { if (!type) return false; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); return (type_class == clang::Type::Record); } bool ClangASTContext::IsEnumType(lldb::opaque_compiler_type_t type) { if (!type) return false; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); return (type_class == clang::Type::Enum); } bool ClangASTContext::IsPolymorphicClass(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); if (record_decl) { const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) return cxx_record_decl->isPolymorphic(); } } break; default: break; } } return false; } bool ClangASTContext::IsPossibleDynamicType(lldb::opaque_compiler_type_t type, CompilerType *dynamic_pointee_type, bool check_cplusplus, bool check_objc) { clang::QualType pointee_qual_type; if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); bool success = false; const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Builtin: if (check_objc && llvm::cast(qual_type)->getKind() == clang::BuiltinType::ObjCId) { if (dynamic_pointee_type) dynamic_pointee_type->SetCompilerType(this, type); return true; } break; case clang::Type::ObjCObjectPointer: if (check_objc) { if (auto objc_pointee_type = qual_type->getPointeeType().getTypePtrOrNull()) { if (auto objc_object_type = llvm::dyn_cast_or_null( objc_pointee_type)) { if (objc_object_type->isObjCClass()) return false; } } if (dynamic_pointee_type) dynamic_pointee_type->SetCompilerType( getASTContext(), llvm::cast(qual_type) ->getPointeeType()); return true; } break; case clang::Type::Pointer: pointee_qual_type = llvm::cast(qual_type)->getPointeeType(); success = true; break; case clang::Type::LValueReference: case clang::Type::RValueReference: pointee_qual_type = llvm::cast(qual_type)->getPointeeType(); success = true; break; case clang::Type::Typedef: return IsPossibleDynamicType(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), dynamic_pointee_type, check_cplusplus, check_objc); case clang::Type::Auto: return IsPossibleDynamicType(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), dynamic_pointee_type, check_cplusplus, check_objc); case clang::Type::Elaborated: return IsPossibleDynamicType(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), dynamic_pointee_type, check_cplusplus, check_objc); case clang::Type::Paren: return IsPossibleDynamicType( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), dynamic_pointee_type, check_cplusplus, check_objc); default: break; } if (success) { // Check to make sure what we are pointing too is a possible dynamic C++ // type // We currently accept any "void *" (in case we have a class that has been // watered down to an opaque pointer) and virtual C++ classes. const clang::Type::TypeClass pointee_type_class = pointee_qual_type.getCanonicalType()->getTypeClass(); switch (pointee_type_class) { case clang::Type::Builtin: switch (llvm::cast(pointee_qual_type)->getKind()) { case clang::BuiltinType::UnknownAny: case clang::BuiltinType::Void: if (dynamic_pointee_type) dynamic_pointee_type->SetCompilerType(getASTContext(), pointee_qual_type); return true; default: break; } break; case clang::Type::Record: if (check_cplusplus) { clang::CXXRecordDecl *cxx_record_decl = pointee_qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { bool is_complete = cxx_record_decl->isCompleteDefinition(); if (is_complete) success = cxx_record_decl->isDynamicClass(); else { ClangASTMetadata *metadata = ClangASTContext::GetMetadata( getASTContext(), cxx_record_decl); if (metadata) success = metadata->GetIsDynamicCXXType(); else { is_complete = CompilerType(getASTContext(), pointee_qual_type) .GetCompleteType(); if (is_complete) success = cxx_record_decl->isDynamicClass(); else success = false; } } if (success) { if (dynamic_pointee_type) dynamic_pointee_type->SetCompilerType(getASTContext(), pointee_qual_type); return true; } } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (check_objc) { if (dynamic_pointee_type) dynamic_pointee_type->SetCompilerType(getASTContext(), pointee_qual_type); return true; } break; default: break; } } } if (dynamic_pointee_type) dynamic_pointee_type->Clear(); return false; } bool ClangASTContext::IsScalarType(lldb::opaque_compiler_type_t type) { if (!type) return false; return (GetTypeInfo(type, nullptr) & eTypeIsScalar) != 0; } bool ClangASTContext::IsTypedefType(lldb::opaque_compiler_type_t type) { if (!type) return false; return GetQualType(type)->getTypeClass() == clang::Type::Typedef; } bool ClangASTContext::IsVoidType(lldb::opaque_compiler_type_t type) { if (!type) return false; return GetCanonicalQualType(type)->isVoidType(); } bool ClangASTContext::SupportsLanguage(lldb::LanguageType language) { return ClangASTContextSupportsLanguage(language); } bool ClangASTContext::GetCXXClassName(const CompilerType &type, std::string &class_name) { if (type) { clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type)); if (!qual_type.isNull()) { clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { class_name.assign(cxx_record_decl->getIdentifier()->getNameStart()); return true; } } } class_name.clear(); return false; } bool ClangASTContext::IsCXXClassType(const CompilerType &type) { if (!type) return false; clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type)); if (!qual_type.isNull() && qual_type->getAsCXXRecordDecl() != nullptr) return true; return false; } bool ClangASTContext::IsBeingDefined(lldb::opaque_compiler_type_t type) { if (!type) return false; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::TagType *tag_type = llvm::dyn_cast(qual_type); if (tag_type) return tag_type->isBeingDefined(); return false; } bool ClangASTContext::IsObjCObjectPointerType(const CompilerType &type, CompilerType *class_type_ptr) { if (!type) return false; clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type)); if (!qual_type.isNull() && qual_type->isObjCObjectPointerType()) { if (class_type_ptr) { if (!qual_type->isObjCClassType() && !qual_type->isObjCIdType()) { const clang::ObjCObjectPointerType *obj_pointer_type = llvm::dyn_cast(qual_type); if (obj_pointer_type == nullptr) class_type_ptr->Clear(); else class_type_ptr->SetCompilerType( type.GetTypeSystem(), clang::QualType(obj_pointer_type->getInterfaceType(), 0) .getAsOpaquePtr()); } } return true; } if (class_type_ptr) class_type_ptr->Clear(); return false; } bool ClangASTContext::GetObjCClassName(const CompilerType &type, std::string &class_name) { if (!type) return false; clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type)); const clang::ObjCObjectType *object_type = llvm::dyn_cast(qual_type); if (object_type) { const clang::ObjCInterfaceDecl *interface = object_type->getInterface(); if (interface) { class_name = interface->getNameAsString(); return true; } } return false; } //---------------------------------------------------------------------- // Type Completion //---------------------------------------------------------------------- bool ClangASTContext::GetCompleteType(lldb::opaque_compiler_type_t type) { if (!type) return false; const bool allow_completion = true; return GetCompleteQualType(getASTContext(), GetQualType(type), allow_completion); } ConstString ClangASTContext::GetTypeName(lldb::opaque_compiler_type_t type) { std::string type_name; if (type) { clang::PrintingPolicy printing_policy(getASTContext()->getPrintingPolicy()); clang::QualType qual_type(GetQualType(type)); printing_policy.SuppressTagKeyword = true; const clang::TypedefType *typedef_type = qual_type->getAs(); if (typedef_type) { const clang::TypedefNameDecl *typedef_decl = typedef_type->getDecl(); type_name = typedef_decl->getQualifiedNameAsString(); } else { type_name = qual_type.getAsString(printing_policy); } } return ConstString(type_name); } uint32_t ClangASTContext::GetTypeInfo(lldb::opaque_compiler_type_t type, CompilerType *pointee_or_element_clang_type) { if (!type) return 0; if (pointee_or_element_clang_type) pointee_or_element_clang_type->Clear(); clang::QualType qual_type(GetQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Builtin: { const clang::BuiltinType *builtin_type = llvm::dyn_cast( qual_type->getCanonicalTypeInternal()); uint32_t builtin_type_flags = eTypeIsBuiltIn | eTypeHasValue; switch (builtin_type->getKind()) { case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCClass: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), getASTContext()->ObjCBuiltinClassTy); builtin_type_flags |= eTypeIsPointer | eTypeIsObjC; break; case clang::BuiltinType::ObjCSel: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType(getASTContext(), getASTContext()->CharTy); builtin_type_flags |= eTypeIsPointer | eTypeIsObjC; break; case clang::BuiltinType::Bool: case clang::BuiltinType::Char_U: case clang::BuiltinType::UChar: case clang::BuiltinType::WChar_U: case clang::BuiltinType::Char16: case clang::BuiltinType::Char32: case clang::BuiltinType::UShort: case clang::BuiltinType::UInt: case clang::BuiltinType::ULong: case clang::BuiltinType::ULongLong: case clang::BuiltinType::UInt128: case clang::BuiltinType::Char_S: case clang::BuiltinType::SChar: case clang::BuiltinType::WChar_S: case clang::BuiltinType::Short: case clang::BuiltinType::Int: case clang::BuiltinType::Long: case clang::BuiltinType::LongLong: case clang::BuiltinType::Int128: case clang::BuiltinType::Float: case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: builtin_type_flags |= eTypeIsScalar; if (builtin_type->isInteger()) { builtin_type_flags |= eTypeIsInteger; if (builtin_type->isSignedInteger()) builtin_type_flags |= eTypeIsSigned; } else if (builtin_type->isFloatingPoint()) builtin_type_flags |= eTypeIsFloat; break; default: break; } return builtin_type_flags; } case clang::Type::BlockPointer: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), qual_type->getPointeeType()); return eTypeIsPointer | eTypeHasChildren | eTypeIsBlock; case clang::Type::Complex: { uint32_t complex_type_flags = eTypeIsBuiltIn | eTypeHasValue | eTypeIsComplex; const clang::ComplexType *complex_type = llvm::dyn_cast( qual_type->getCanonicalTypeInternal()); if (complex_type) { clang::QualType complex_element_type(complex_type->getElementType()); if (complex_element_type->isIntegerType()) complex_type_flags |= eTypeIsFloat; else if (complex_element_type->isFloatingType()) complex_type_flags |= eTypeIsInteger; } return complex_type_flags; } break; case clang::Type::ConstantArray: case clang::Type::DependentSizedArray: case clang::Type::IncompleteArray: case clang::Type::VariableArray: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), llvm::cast(qual_type.getTypePtr()) ->getElementType()); return eTypeHasChildren | eTypeIsArray; case clang::Type::DependentName: return 0; case clang::Type::DependentSizedExtVector: return eTypeHasChildren | eTypeIsVector; case clang::Type::DependentTemplateSpecialization: return eTypeIsTemplate; case clang::Type::Decltype: return 0; case clang::Type::Enum: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), llvm::cast(qual_type)->getDecl()->getIntegerType()); return eTypeIsEnumeration | eTypeHasValue; case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetTypeInfo(pointee_or_element_clang_type); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetTypeInfo(pointee_or_element_clang_type); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetTypeInfo(pointee_or_element_clang_type); case clang::Type::FunctionProto: return eTypeIsFuncPrototype | eTypeHasValue; case clang::Type::FunctionNoProto: return eTypeIsFuncPrototype | eTypeHasValue; case clang::Type::InjectedClassName: return 0; case clang::Type::LValueReference: case clang::Type::RValueReference: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), llvm::cast(qual_type.getTypePtr()) ->getPointeeType()); return eTypeHasChildren | eTypeIsReference | eTypeHasValue; case clang::Type::MemberPointer: return eTypeIsPointer | eTypeIsMember | eTypeHasValue; case clang::Type::ObjCObjectPointer: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), qual_type->getPointeeType()); return eTypeHasChildren | eTypeIsObjC | eTypeIsClass | eTypeIsPointer | eTypeHasValue; case clang::Type::ObjCObject: return eTypeHasChildren | eTypeIsObjC | eTypeIsClass; case clang::Type::ObjCInterface: return eTypeHasChildren | eTypeIsObjC | eTypeIsClass; case clang::Type::Pointer: if (pointee_or_element_clang_type) pointee_or_element_clang_type->SetCompilerType( getASTContext(), qual_type->getPointeeType()); return eTypeHasChildren | eTypeIsPointer | eTypeHasValue; case clang::Type::Record: if (qual_type->getAsCXXRecordDecl()) return eTypeHasChildren | eTypeIsClass | eTypeIsCPlusPlus; else return eTypeHasChildren | eTypeIsStructUnion; break; case clang::Type::SubstTemplateTypeParm: return eTypeIsTemplate; case clang::Type::TemplateTypeParm: return eTypeIsTemplate; case clang::Type::TemplateSpecialization: return eTypeIsTemplate; case clang::Type::Typedef: return eTypeIsTypedef | CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetTypeInfo(pointee_or_element_clang_type); case clang::Type::TypeOfExpr: return 0; case clang::Type::TypeOf: return 0; case clang::Type::UnresolvedUsing: return 0; case clang::Type::ExtVector: case clang::Type::Vector: { uint32_t vector_type_flags = eTypeHasChildren | eTypeIsVector; const clang::VectorType *vector_type = llvm::dyn_cast( qual_type->getCanonicalTypeInternal()); if (vector_type) { if (vector_type->isIntegerType()) vector_type_flags |= eTypeIsFloat; else if (vector_type->isFloatingType()) vector_type_flags |= eTypeIsInteger; } return vector_type_flags; } default: return 0; } return 0; } lldb::LanguageType ClangASTContext::GetMinimumLanguage(lldb::opaque_compiler_type_t type) { if (!type) return lldb::eLanguageTypeC; // If the type is a reference, then resolve it to what it refers to first: clang::QualType qual_type(GetCanonicalQualType(type).getNonReferenceType()); if (qual_type->isAnyPointerType()) { if (qual_type->isObjCObjectPointerType()) return lldb::eLanguageTypeObjC; clang::QualType pointee_type(qual_type->getPointeeType()); if (pointee_type->getPointeeCXXRecordDecl() != nullptr) return lldb::eLanguageTypeC_plus_plus; if (pointee_type->isObjCObjectOrInterfaceType()) return lldb::eLanguageTypeObjC; if (pointee_type->isObjCClassType()) return lldb::eLanguageTypeObjC; if (pointee_type.getTypePtr() == getASTContext()->ObjCBuiltinIdTy.getTypePtr()) return lldb::eLanguageTypeObjC; } else { if (qual_type->isObjCObjectOrInterfaceType()) return lldb::eLanguageTypeObjC; if (qual_type->getAsCXXRecordDecl()) return lldb::eLanguageTypeC_plus_plus; switch (qual_type->getTypeClass()) { default: break; case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { default: case clang::BuiltinType::Void: case clang::BuiltinType::Bool: case clang::BuiltinType::Char_U: case clang::BuiltinType::UChar: case clang::BuiltinType::WChar_U: case clang::BuiltinType::Char16: case clang::BuiltinType::Char32: case clang::BuiltinType::UShort: case clang::BuiltinType::UInt: case clang::BuiltinType::ULong: case clang::BuiltinType::ULongLong: case clang::BuiltinType::UInt128: case clang::BuiltinType::Char_S: case clang::BuiltinType::SChar: case clang::BuiltinType::WChar_S: case clang::BuiltinType::Short: case clang::BuiltinType::Int: case clang::BuiltinType::Long: case clang::BuiltinType::LongLong: case clang::BuiltinType::Int128: case clang::BuiltinType::Float: case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: break; case clang::BuiltinType::NullPtr: return eLanguageTypeC_plus_plus; case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCClass: case clang::BuiltinType::ObjCSel: return eLanguageTypeObjC; case clang::BuiltinType::Dependent: case clang::BuiltinType::Overload: case clang::BuiltinType::BoundMember: case clang::BuiltinType::UnknownAny: break; } break; case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetMinimumLanguage(); } } return lldb::eLanguageTypeC; } lldb::TypeClass ClangASTContext::GetTypeClass(lldb::opaque_compiler_type_t type) { if (!type) return lldb::eTypeClassInvalid; clang::QualType qual_type(GetQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::UnaryTransform: break; case clang::Type::FunctionNoProto: return lldb::eTypeClassFunction; case clang::Type::FunctionProto: return lldb::eTypeClassFunction; case clang::Type::IncompleteArray: return lldb::eTypeClassArray; case clang::Type::VariableArray: return lldb::eTypeClassArray; case clang::Type::ConstantArray: return lldb::eTypeClassArray; case clang::Type::DependentSizedArray: return lldb::eTypeClassArray; case clang::Type::DependentSizedExtVector: return lldb::eTypeClassVector; case clang::Type::ExtVector: return lldb::eTypeClassVector; case clang::Type::Vector: return lldb::eTypeClassVector; case clang::Type::Builtin: return lldb::eTypeClassBuiltin; case clang::Type::ObjCObjectPointer: return lldb::eTypeClassObjCObjectPointer; case clang::Type::BlockPointer: return lldb::eTypeClassBlockPointer; case clang::Type::Pointer: return lldb::eTypeClassPointer; case clang::Type::LValueReference: return lldb::eTypeClassReference; case clang::Type::RValueReference: return lldb::eTypeClassReference; case clang::Type::MemberPointer: return lldb::eTypeClassMemberPointer; case clang::Type::Complex: if (qual_type->isComplexType()) return lldb::eTypeClassComplexFloat; else return lldb::eTypeClassComplexInteger; case clang::Type::ObjCObject: return lldb::eTypeClassObjCObject; case clang::Type::ObjCInterface: return lldb::eTypeClassObjCInterface; case clang::Type::Record: { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); if (record_decl->isUnion()) return lldb::eTypeClassUnion; else if (record_decl->isStruct()) return lldb::eTypeClassStruct; else return lldb::eTypeClassClass; } break; case clang::Type::Enum: return lldb::eTypeClassEnumeration; case clang::Type::Typedef: return lldb::eTypeClassTypedef; case clang::Type::UnresolvedUsing: break; case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetTypeClass(); case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetTypeClass(); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetTypeClass(); case clang::Type::Attributed: break; case clang::Type::TemplateTypeParm: break; case clang::Type::SubstTemplateTypeParm: break; case clang::Type::SubstTemplateTypeParmPack: break; case clang::Type::InjectedClassName: break; case clang::Type::DependentName: break; case clang::Type::DependentTemplateSpecialization: break; case clang::Type::PackExpansion: break; case clang::Type::TypeOfExpr: break; case clang::Type::TypeOf: break; case clang::Type::Decltype: break; case clang::Type::TemplateSpecialization: break; case clang::Type::Atomic: break; case clang::Type::Pipe: break; // pointer type decayed from an array or function type. case clang::Type::Decayed: break; case clang::Type::Adjusted: break; case clang::Type::ObjCTypeParam: break; } // We don't know hot to display this type... return lldb::eTypeClassOther; } unsigned ClangASTContext::GetTypeQualifiers(lldb::opaque_compiler_type_t type) { if (type) return GetQualType(type).getQualifiers().getCVRQualifiers(); return 0; } //---------------------------------------------------------------------- // Creating related types //---------------------------------------------------------------------- CompilerType ClangASTContext::GetArrayElementType(lldb::opaque_compiler_type_t type, uint64_t *stride) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type *array_eletype = qual_type.getTypePtr()->getArrayElementTypeNoTypeQual(); if (!array_eletype) return CompilerType(); CompilerType element_type(getASTContext(), array_eletype->getCanonicalTypeUnqualified()); // TODO: the real stride will be >= this value.. find the real one! if (stride) *stride = element_type.GetByteSize(nullptr); return element_type; } return CompilerType(); } CompilerType ClangASTContext::GetArrayType(lldb::opaque_compiler_type_t type, uint64_t size) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); if (clang::ASTContext *ast_ctx = getASTContext()) { if (size != 0) return CompilerType( ast_ctx, ast_ctx->getConstantArrayType( qual_type, llvm::APInt(64, size), clang::ArrayType::ArraySizeModifier::Normal, 0)); else return CompilerType( ast_ctx, ast_ctx->getIncompleteArrayType( qual_type, clang::ArrayType::ArraySizeModifier::Normal, 0)); } } return CompilerType(); } CompilerType ClangASTContext::GetCanonicalType(lldb::opaque_compiler_type_t type) { if (type) return CompilerType(getASTContext(), GetCanonicalQualType(type)); return CompilerType(); } static clang::QualType GetFullyUnqualifiedType_Impl(clang::ASTContext *ast, clang::QualType qual_type) { if (qual_type->isPointerType()) qual_type = ast->getPointerType( GetFullyUnqualifiedType_Impl(ast, qual_type->getPointeeType())); else qual_type = qual_type.getUnqualifiedType(); qual_type.removeLocalConst(); qual_type.removeLocalRestrict(); qual_type.removeLocalVolatile(); return qual_type; } CompilerType ClangASTContext::GetFullyUnqualifiedType(lldb::opaque_compiler_type_t type) { if (type) return CompilerType( getASTContext(), GetFullyUnqualifiedType_Impl(getASTContext(), GetQualType(type))); return CompilerType(); } int ClangASTContext::GetFunctionArgumentCount( lldb::opaque_compiler_type_t type) { if (type) { const clang::FunctionProtoType *func = llvm::dyn_cast(GetCanonicalQualType(type)); if (func) return func->getNumParams(); } return -1; } CompilerType ClangASTContext::GetFunctionArgumentTypeAtIndex( lldb::opaque_compiler_type_t type, size_t idx) { if (type) { const clang::FunctionProtoType *func = llvm::dyn_cast(GetQualType(type)); if (func) { const uint32_t num_args = func->getNumParams(); if (idx < num_args) return CompilerType(getASTContext(), func->getParamType(idx)); } } return CompilerType(); } CompilerType ClangASTContext::GetFunctionReturnType(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetQualType(type)); const clang::FunctionProtoType *func = llvm::dyn_cast(qual_type.getTypePtr()); if (func) return CompilerType(getASTContext(), func->getReturnType()); } return CompilerType(); } size_t ClangASTContext::GetNumMemberFunctions(lldb::opaque_compiler_type_t type) { size_t num_functions = 0; if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::Record: if (GetCompleteQualType(getASTContext(), qual_type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) num_functions = std::distance(cxx_record_decl->method_begin(), cxx_record_decl->method_end()); } break; case clang::Type::ObjCObjectPointer: { const clang::ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); const clang::ObjCInterfaceType *objc_interface_type = objc_class_type->getInterfaceType(); if (objc_interface_type && GetCompleteType((lldb::opaque_compiler_type_t)objc_interface_type)) { clang::ObjCInterfaceDecl *class_interface_decl = objc_interface_type->getDecl(); if (class_interface_decl) { num_functions = std::distance(class_interface_decl->meth_begin(), class_interface_decl->meth_end()); } } break; } case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) num_functions = std::distance(class_interface_decl->meth_begin(), class_interface_decl->meth_end()); } } break; case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetNumMemberFunctions(); case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetNumMemberFunctions(); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetNumMemberFunctions(); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetNumMemberFunctions(); default: break; } } return num_functions; } TypeMemberFunctionImpl ClangASTContext::GetMemberFunctionAtIndex(lldb::opaque_compiler_type_t type, size_t idx) { std::string name; MemberFunctionKind kind(MemberFunctionKind::eMemberFunctionKindUnknown); CompilerType clang_type; CompilerDecl clang_decl; if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::Record: if (GetCompleteQualType(getASTContext(), qual_type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) { auto method_iter = cxx_record_decl->method_begin(); auto method_end = cxx_record_decl->method_end(); if (idx < static_cast(std::distance(method_iter, method_end))) { std::advance(method_iter, idx); clang::CXXMethodDecl *cxx_method_decl = method_iter->getCanonicalDecl(); if (cxx_method_decl) { name = cxx_method_decl->getDeclName().getAsString(); if (cxx_method_decl->isStatic()) kind = lldb::eMemberFunctionKindStaticMethod; else if (llvm::isa(cxx_method_decl)) kind = lldb::eMemberFunctionKindConstructor; else if (llvm::isa(cxx_method_decl)) kind = lldb::eMemberFunctionKindDestructor; else kind = lldb::eMemberFunctionKindInstanceMethod; clang_type = CompilerType( this, cxx_method_decl->getType().getAsOpaquePtr()); clang_decl = CompilerDecl(this, cxx_method_decl); } } } } break; case clang::Type::ObjCObjectPointer: { const clang::ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); const clang::ObjCInterfaceType *objc_interface_type = objc_class_type->getInterfaceType(); if (objc_interface_type && GetCompleteType((lldb::opaque_compiler_type_t)objc_interface_type)) { clang::ObjCInterfaceDecl *class_interface_decl = objc_interface_type->getDecl(); if (class_interface_decl) { auto method_iter = class_interface_decl->meth_begin(); auto method_end = class_interface_decl->meth_end(); if (idx < static_cast(std::distance(method_iter, method_end))) { std::advance(method_iter, idx); clang::ObjCMethodDecl *objc_method_decl = method_iter->getCanonicalDecl(); if (objc_method_decl) { clang_decl = CompilerDecl(this, objc_method_decl); name = objc_method_decl->getSelector().getAsString(); if (objc_method_decl->isClassMethod()) kind = lldb::eMemberFunctionKindStaticMethod; else kind = lldb::eMemberFunctionKindInstanceMethod; } } } } break; } case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { auto method_iter = class_interface_decl->meth_begin(); auto method_end = class_interface_decl->meth_end(); if (idx < static_cast(std::distance(method_iter, method_end))) { std::advance(method_iter, idx); clang::ObjCMethodDecl *objc_method_decl = method_iter->getCanonicalDecl(); if (objc_method_decl) { clang_decl = CompilerDecl(this, objc_method_decl); name = objc_method_decl->getSelector().getAsString(); if (objc_method_decl->isClassMethod()) kind = lldb::eMemberFunctionKindStaticMethod; else kind = lldb::eMemberFunctionKindInstanceMethod; } } } } } break; case clang::Type::Typedef: return GetMemberFunctionAtIndex(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), idx); case clang::Type::Auto: return GetMemberFunctionAtIndex(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), idx); case clang::Type::Elaborated: return GetMemberFunctionAtIndex( llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), idx); case clang::Type::Paren: return GetMemberFunctionAtIndex( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), idx); default: break; } } if (kind == eMemberFunctionKindUnknown) return TypeMemberFunctionImpl(); else return TypeMemberFunctionImpl(clang_type, clang_decl, name, kind); } CompilerType ClangASTContext::GetNonReferenceType(lldb::opaque_compiler_type_t type) { if (type) return CompilerType(getASTContext(), GetQualType(type).getNonReferenceType()); return CompilerType(); } CompilerType ClangASTContext::CreateTypedefType( const CompilerType &type, const char *typedef_name, const CompilerDeclContext &compiler_decl_ctx) { if (type && typedef_name && typedef_name[0]) { ClangASTContext *ast = llvm::dyn_cast(type.GetTypeSystem()); if (!ast) return CompilerType(); clang::ASTContext *clang_ast = ast->getASTContext(); clang::QualType qual_type(ClangUtil::GetQualType(type)); clang::DeclContext *decl_ctx = ClangASTContext::DeclContextGetAsDeclContext(compiler_decl_ctx); if (decl_ctx == nullptr) decl_ctx = ast->getASTContext()->getTranslationUnitDecl(); clang::TypedefDecl *decl = clang::TypedefDecl::Create( *clang_ast, decl_ctx, clang::SourceLocation(), clang::SourceLocation(), &clang_ast->Idents.get(typedef_name), clang_ast->getTrivialTypeSourceInfo(qual_type)); decl->setAccess(clang::AS_public); // TODO respect proper access specifier // Get a uniqued clang::QualType for the typedef decl type return CompilerType(clang_ast, clang_ast->getTypedefType(decl)); } return CompilerType(); } CompilerType ClangASTContext::GetPointeeType(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetQualType(type)); return CompilerType(getASTContext(), qual_type.getTypePtr()->getPointeeType()); } return CompilerType(); } CompilerType ClangASTContext::GetPointerType(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::ObjCObject: case clang::Type::ObjCInterface: return CompilerType(getASTContext(), getASTContext()->getObjCObjectPointerType(qual_type)); default: return CompilerType(getASTContext(), getASTContext()->getPointerType(qual_type)); } } return CompilerType(); } CompilerType ClangASTContext::GetLValueReferenceType(lldb::opaque_compiler_type_t type) { if (type) return CompilerType(this, getASTContext() ->getLValueReferenceType(GetQualType(type)) .getAsOpaquePtr()); else return CompilerType(); } CompilerType ClangASTContext::GetRValueReferenceType(lldb::opaque_compiler_type_t type) { if (type) return CompilerType(this, getASTContext() ->getRValueReferenceType(GetQualType(type)) .getAsOpaquePtr()); else return CompilerType(); } CompilerType ClangASTContext::AddConstModifier(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType result(GetQualType(type)); result.addConst(); return CompilerType(this, result.getAsOpaquePtr()); } return CompilerType(); } CompilerType ClangASTContext::AddVolatileModifier(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType result(GetQualType(type)); result.addVolatile(); return CompilerType(this, result.getAsOpaquePtr()); } return CompilerType(); } CompilerType ClangASTContext::AddRestrictModifier(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType result(GetQualType(type)); result.addRestrict(); return CompilerType(this, result.getAsOpaquePtr()); } return CompilerType(); } CompilerType ClangASTContext::CreateTypedef(lldb::opaque_compiler_type_t type, const char *typedef_name, const CompilerDeclContext &compiler_decl_ctx) { if (type) { clang::ASTContext *clang_ast = getASTContext(); clang::QualType qual_type(GetQualType(type)); clang::DeclContext *decl_ctx = ClangASTContext::DeclContextGetAsDeclContext(compiler_decl_ctx); if (decl_ctx == nullptr) decl_ctx = getASTContext()->getTranslationUnitDecl(); clang::TypedefDecl *decl = clang::TypedefDecl::Create( *clang_ast, decl_ctx, clang::SourceLocation(), clang::SourceLocation(), &clang_ast->Idents.get(typedef_name), clang_ast->getTrivialTypeSourceInfo(qual_type)); clang::TagDecl *tdecl = nullptr; if (!qual_type.isNull()) { if (const clang::RecordType *rt = qual_type->getAs()) tdecl = rt->getDecl(); if (const clang::EnumType *et = qual_type->getAs()) tdecl = et->getDecl(); } // Check whether this declaration is an anonymous struct, union, or enum, // hidden behind a typedef. If so, we // try to check whether we have a typedef tag to attach to the original // record declaration if (tdecl && !tdecl->getIdentifier() && !tdecl->getTypedefNameForAnonDecl()) tdecl->setTypedefNameForAnonDecl(decl); decl->setAccess(clang::AS_public); // TODO respect proper access specifier // Get a uniqued clang::QualType for the typedef decl type return CompilerType(this, clang_ast->getTypedefType(decl).getAsOpaquePtr()); } return CompilerType(); } CompilerType ClangASTContext::GetTypedefedType(lldb::opaque_compiler_type_t type) { if (type) { const clang::TypedefType *typedef_type = llvm::dyn_cast(GetQualType(type)); if (typedef_type) return CompilerType(getASTContext(), typedef_type->getDecl()->getUnderlyingType()); } return CompilerType(); } //---------------------------------------------------------------------- // Create related types using the current type's AST //---------------------------------------------------------------------- CompilerType ClangASTContext::GetBasicTypeFromAST(lldb::BasicType basic_type) { return ClangASTContext::GetBasicType(getASTContext(), basic_type); } //---------------------------------------------------------------------- // Exploring the type //---------------------------------------------------------------------- uint64_t ClangASTContext::GetBitSize(lldb::opaque_compiler_type_t type, ExecutionContextScope *exe_scope) { if (GetCompleteType(type)) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) return getASTContext()->getTypeSize(qual_type); else return 0; break; case clang::Type::ObjCInterface: case clang::Type::ObjCObject: { ExecutionContext exe_ctx(exe_scope); Process *process = exe_ctx.GetProcessPtr(); if (process) { ObjCLanguageRuntime *objc_runtime = process->GetObjCLanguageRuntime(); if (objc_runtime) { uint64_t bit_size = 0; if (objc_runtime->GetTypeBitSize( CompilerType(getASTContext(), qual_type), bit_size)) return bit_size; } } else { static bool g_printed = false; if (!g_printed) { StreamString s; DumpTypeDescription(type, &s); llvm::outs() << "warning: trying to determine the size of type "; llvm::outs() << s.GetString() << "\n"; llvm::outs() << "without a valid ExecutionContext. this is not " "reliable. please file a bug against LLDB.\n"; llvm::outs() << "backtrace:\n"; llvm::sys::PrintStackTrace(llvm::outs()); llvm::outs() << "\n"; g_printed = true; } } } LLVM_FALLTHROUGH; default: const uint32_t bit_size = getASTContext()->getTypeSize(qual_type); if (bit_size == 0) { if (qual_type->isIncompleteArrayType()) return getASTContext()->getTypeSize( qual_type->getArrayElementTypeNoTypeQual() ->getCanonicalTypeUnqualified()); } if (qual_type->isObjCObjectOrInterfaceType()) return bit_size + getASTContext()->getTypeSize( getASTContext()->ObjCBuiltinClassTy); return bit_size; } } return 0; } size_t ClangASTContext::GetTypeBitAlign(lldb::opaque_compiler_type_t type) { if (GetCompleteType(type)) return getASTContext()->getTypeAlign(GetQualType(type)); return 0; } lldb::Encoding ClangASTContext::GetEncoding(lldb::opaque_compiler_type_t type, uint64_t &count) { if (!type) return lldb::eEncodingInvalid; count = 1; clang::QualType qual_type(GetCanonicalQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::UnaryTransform: break; case clang::Type::FunctionNoProto: case clang::Type::FunctionProto: break; case clang::Type::IncompleteArray: case clang::Type::VariableArray: break; case clang::Type::ConstantArray: break; case clang::Type::ExtVector: case clang::Type::Vector: // TODO: Set this to more than one??? break; case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { case clang::BuiltinType::Void: break; case clang::BuiltinType::Bool: case clang::BuiltinType::Char_S: case clang::BuiltinType::SChar: case clang::BuiltinType::WChar_S: case clang::BuiltinType::Char16: case clang::BuiltinType::Char32: case clang::BuiltinType::Short: case clang::BuiltinType::Int: case clang::BuiltinType::Long: case clang::BuiltinType::LongLong: case clang::BuiltinType::Int128: return lldb::eEncodingSint; case clang::BuiltinType::Char_U: case clang::BuiltinType::UChar: case clang::BuiltinType::WChar_U: case clang::BuiltinType::UShort: case clang::BuiltinType::UInt: case clang::BuiltinType::ULong: case clang::BuiltinType::ULongLong: case clang::BuiltinType::UInt128: return lldb::eEncodingUint; case clang::BuiltinType::Half: case clang::BuiltinType::Float: case clang::BuiltinType::Float128: case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: return lldb::eEncodingIEEE754; case clang::BuiltinType::ObjCClass: case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCSel: return lldb::eEncodingUint; case clang::BuiltinType::NullPtr: return lldb::eEncodingUint; case clang::BuiltinType::Kind::ARCUnbridgedCast: case clang::BuiltinType::Kind::BoundMember: case clang::BuiltinType::Kind::BuiltinFn: case clang::BuiltinType::Kind::Dependent: case clang::BuiltinType::Kind::OCLClkEvent: case clang::BuiltinType::Kind::OCLEvent: case clang::BuiltinType::Kind::OCLImage1dRO: case clang::BuiltinType::Kind::OCLImage1dWO: case clang::BuiltinType::Kind::OCLImage1dRW: case clang::BuiltinType::Kind::OCLImage1dArrayRO: case clang::BuiltinType::Kind::OCLImage1dArrayWO: case clang::BuiltinType::Kind::OCLImage1dArrayRW: case clang::BuiltinType::Kind::OCLImage1dBufferRO: case clang::BuiltinType::Kind::OCLImage1dBufferWO: case clang::BuiltinType::Kind::OCLImage1dBufferRW: case clang::BuiltinType::Kind::OCLImage2dRO: case clang::BuiltinType::Kind::OCLImage2dWO: case clang::BuiltinType::Kind::OCLImage2dRW: case clang::BuiltinType::Kind::OCLImage2dArrayRO: case clang::BuiltinType::Kind::OCLImage2dArrayWO: case clang::BuiltinType::Kind::OCLImage2dArrayRW: case clang::BuiltinType::Kind::OCLImage2dArrayDepthRO: case clang::BuiltinType::Kind::OCLImage2dArrayDepthWO: case clang::BuiltinType::Kind::OCLImage2dArrayDepthRW: case clang::BuiltinType::Kind::OCLImage2dArrayMSAARO: case clang::BuiltinType::Kind::OCLImage2dArrayMSAAWO: case clang::BuiltinType::Kind::OCLImage2dArrayMSAARW: case clang::BuiltinType::Kind::OCLImage2dArrayMSAADepthRO: case clang::BuiltinType::Kind::OCLImage2dArrayMSAADepthWO: case clang::BuiltinType::Kind::OCLImage2dArrayMSAADepthRW: case clang::BuiltinType::Kind::OCLImage2dDepthRO: case clang::BuiltinType::Kind::OCLImage2dDepthWO: case clang::BuiltinType::Kind::OCLImage2dDepthRW: case clang::BuiltinType::Kind::OCLImage2dMSAARO: case clang::BuiltinType::Kind::OCLImage2dMSAAWO: case clang::BuiltinType::Kind::OCLImage2dMSAARW: case clang::BuiltinType::Kind::OCLImage2dMSAADepthRO: case clang::BuiltinType::Kind::OCLImage2dMSAADepthWO: case clang::BuiltinType::Kind::OCLImage2dMSAADepthRW: case clang::BuiltinType::Kind::OCLImage3dRO: case clang::BuiltinType::Kind::OCLImage3dWO: case clang::BuiltinType::Kind::OCLImage3dRW: case clang::BuiltinType::Kind::OCLQueue: case clang::BuiltinType::Kind::OCLNDRange: case clang::BuiltinType::Kind::OCLReserveID: case clang::BuiltinType::Kind::OCLSampler: case clang::BuiltinType::Kind::OMPArraySection: case clang::BuiltinType::Kind::Overload: case clang::BuiltinType::Kind::PseudoObject: case clang::BuiltinType::Kind::UnknownAny: break; } break; // All pointer types are represented as unsigned integer encodings. // We may nee to add a eEncodingPointer if we ever need to know the // difference case clang::Type::ObjCObjectPointer: case clang::Type::BlockPointer: case clang::Type::Pointer: case clang::Type::LValueReference: case clang::Type::RValueReference: case clang::Type::MemberPointer: return lldb::eEncodingUint; case clang::Type::Complex: { lldb::Encoding encoding = lldb::eEncodingIEEE754; if (qual_type->isComplexType()) encoding = lldb::eEncodingIEEE754; else { const clang::ComplexType *complex_type = qual_type->getAsComplexIntegerType(); if (complex_type) encoding = CompilerType(getASTContext(), complex_type->getElementType()) .GetEncoding(count); else encoding = lldb::eEncodingSint; } count = 2; return encoding; } case clang::Type::ObjCInterface: break; case clang::Type::Record: break; case clang::Type::Enum: return lldb::eEncodingSint; case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetEncoding(count); case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetEncoding(count); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetEncoding(count); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetEncoding(count); case clang::Type::DependentSizedArray: case clang::Type::DependentSizedExtVector: case clang::Type::UnresolvedUsing: case clang::Type::Attributed: case clang::Type::TemplateTypeParm: case clang::Type::SubstTemplateTypeParm: case clang::Type::SubstTemplateTypeParmPack: case clang::Type::InjectedClassName: case clang::Type::DependentName: case clang::Type::DependentTemplateSpecialization: case clang::Type::PackExpansion: case clang::Type::ObjCObject: case clang::Type::TypeOfExpr: case clang::Type::TypeOf: case clang::Type::Decltype: case clang::Type::TemplateSpecialization: case clang::Type::Atomic: case clang::Type::Adjusted: case clang::Type::Pipe: break; // pointer type decayed from an array or function type. case clang::Type::Decayed: break; case clang::Type::ObjCTypeParam: break; } count = 0; return lldb::eEncodingInvalid; } lldb::Format ClangASTContext::GetFormat(lldb::opaque_compiler_type_t type) { if (!type) return lldb::eFormatDefault; clang::QualType qual_type(GetCanonicalQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::UnaryTransform: break; case clang::Type::FunctionNoProto: case clang::Type::FunctionProto: break; case clang::Type::IncompleteArray: case clang::Type::VariableArray: break; case clang::Type::ConstantArray: return lldb::eFormatVoid; // no value case clang::Type::ExtVector: case clang::Type::Vector: break; case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { // default: assert(0 && "Unknown builtin type!"); case clang::BuiltinType::UnknownAny: case clang::BuiltinType::Void: case clang::BuiltinType::BoundMember: break; case clang::BuiltinType::Bool: return lldb::eFormatBoolean; case clang::BuiltinType::Char_S: case clang::BuiltinType::SChar: case clang::BuiltinType::WChar_S: case clang::BuiltinType::Char_U: case clang::BuiltinType::UChar: case clang::BuiltinType::WChar_U: return lldb::eFormatChar; case clang::BuiltinType::Char16: return lldb::eFormatUnicode16; case clang::BuiltinType::Char32: return lldb::eFormatUnicode32; case clang::BuiltinType::UShort: return lldb::eFormatUnsigned; case clang::BuiltinType::Short: return lldb::eFormatDecimal; case clang::BuiltinType::UInt: return lldb::eFormatUnsigned; case clang::BuiltinType::Int: return lldb::eFormatDecimal; case clang::BuiltinType::ULong: return lldb::eFormatUnsigned; case clang::BuiltinType::Long: return lldb::eFormatDecimal; case clang::BuiltinType::ULongLong: return lldb::eFormatUnsigned; case clang::BuiltinType::LongLong: return lldb::eFormatDecimal; case clang::BuiltinType::UInt128: return lldb::eFormatUnsigned; case clang::BuiltinType::Int128: return lldb::eFormatDecimal; case clang::BuiltinType::Half: case clang::BuiltinType::Float: case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: return lldb::eFormatFloat; default: return lldb::eFormatHex; } break; case clang::Type::ObjCObjectPointer: return lldb::eFormatHex; case clang::Type::BlockPointer: return lldb::eFormatHex; case clang::Type::Pointer: return lldb::eFormatHex; case clang::Type::LValueReference: case clang::Type::RValueReference: return lldb::eFormatHex; case clang::Type::MemberPointer: break; case clang::Type::Complex: { if (qual_type->isComplexType()) return lldb::eFormatComplex; else return lldb::eFormatComplexInteger; } case clang::Type::ObjCInterface: break; case clang::Type::Record: break; case clang::Type::Enum: return lldb::eFormatEnum; case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetFormat(); case clang::Type::Auto: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetFormat(); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetFormat(); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetFormat(); case clang::Type::DependentSizedArray: case clang::Type::DependentSizedExtVector: case clang::Type::UnresolvedUsing: case clang::Type::Attributed: case clang::Type::TemplateTypeParm: case clang::Type::SubstTemplateTypeParm: case clang::Type::SubstTemplateTypeParmPack: case clang::Type::InjectedClassName: case clang::Type::DependentName: case clang::Type::DependentTemplateSpecialization: case clang::Type::PackExpansion: case clang::Type::ObjCObject: case clang::Type::TypeOfExpr: case clang::Type::TypeOf: case clang::Type::Decltype: case clang::Type::TemplateSpecialization: case clang::Type::Atomic: case clang::Type::Adjusted: case clang::Type::Pipe: break; // pointer type decayed from an array or function type. case clang::Type::Decayed: break; case clang::Type::ObjCTypeParam: break; } // We don't know hot to display this type... return lldb::eFormatBytes; } static bool ObjCDeclHasIVars(clang::ObjCInterfaceDecl *class_interface_decl, bool check_superclass) { while (class_interface_decl) { if (class_interface_decl->ivar_size() > 0) return true; if (check_superclass) class_interface_decl = class_interface_decl->getSuperClass(); else break; } return false; } uint32_t ClangASTContext::GetNumChildren(lldb::opaque_compiler_type_t type, bool omit_empty_base_classes) { if (!type) return 0; uint32_t num_children = 0; clang::QualType qual_type(GetQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { case clang::BuiltinType::ObjCId: // child is Class case clang::BuiltinType::ObjCClass: // child is Class num_children = 1; break; default: break; } break; case clang::Type::Complex: return 0; case clang::Type::Record: if (GetCompleteQualType(getASTContext(), qual_type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) { if (omit_empty_base_classes) { // Check each base classes to see if it or any of its // base classes contain any fields. This can help // limit the noise in variable views by not having to // show base classes that contain no members. clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { const clang::CXXRecordDecl *base_class_decl = llvm::cast( base_class->getType() ->getAs() ->getDecl()); // Skip empty base classes if (ClangASTContext::RecordHasFields(base_class_decl) == false) continue; num_children++; } } else { // Include all base classes num_children += cxx_record_decl->getNumBases(); } } clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field) ++num_children; } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteQualType(getASTContext(), qual_type)) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); if (superclass_interface_decl) { if (omit_empty_base_classes) { if (ObjCDeclHasIVars(superclass_interface_decl, true)) ++num_children; } else ++num_children; } num_children += class_interface_decl->ivar_size(); } } } break; case clang::Type::ObjCObjectPointer: { const clang::ObjCObjectPointerType *pointer_type = llvm::cast(qual_type.getTypePtr()); clang::QualType pointee_type = pointer_type->getPointeeType(); uint32_t num_pointee_children = CompilerType(getASTContext(), pointee_type) .GetNumChildren(omit_empty_base_classes); // If this type points to a simple type, then it has 1 child if (num_pointee_children == 0) num_children = 1; else num_children = num_pointee_children; } break; case clang::Type::Vector: case clang::Type::ExtVector: num_children = llvm::cast(qual_type.getTypePtr())->getNumElements(); break; case clang::Type::ConstantArray: num_children = llvm::cast(qual_type.getTypePtr()) ->getSize() .getLimitedValue(); break; case clang::Type::Pointer: { const clang::PointerType *pointer_type = llvm::cast(qual_type.getTypePtr()); clang::QualType pointee_type(pointer_type->getPointeeType()); uint32_t num_pointee_children = CompilerType(getASTContext(), pointee_type) .GetNumChildren(omit_empty_base_classes); if (num_pointee_children == 0) { // We have a pointer to a pointee type that claims it has no children. // We will want to look at num_children = GetNumPointeeChildren(pointee_type); } else num_children = num_pointee_children; } break; case clang::Type::LValueReference: case clang::Type::RValueReference: { const clang::ReferenceType *reference_type = llvm::cast(qual_type.getTypePtr()); clang::QualType pointee_type = reference_type->getPointeeType(); uint32_t num_pointee_children = CompilerType(getASTContext(), pointee_type) .GetNumChildren(omit_empty_base_classes); // If this type points to a simple type, then it has 1 child if (num_pointee_children == 0) num_children = 1; else num_children = num_pointee_children; } break; case clang::Type::Typedef: num_children = CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetNumChildren(omit_empty_base_classes); break; case clang::Type::Auto: num_children = CompilerType(getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetNumChildren(omit_empty_base_classes); break; case clang::Type::Elaborated: num_children = CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetNumChildren(omit_empty_base_classes); break; case clang::Type::Paren: num_children = CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetNumChildren(omit_empty_base_classes); break; default: break; } return num_children; } CompilerType ClangASTContext::GetBuiltinTypeByName(const ConstString &name) { return GetBasicType(GetBasicTypeEnumeration(name)); } lldb::BasicType ClangASTContext::GetBasicTypeEnumeration(lldb::opaque_compiler_type_t type) { if (type) { clang::QualType qual_type(GetQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); if (type_class == clang::Type::Builtin) { switch (llvm::cast(qual_type)->getKind()) { case clang::BuiltinType::Void: return eBasicTypeVoid; case clang::BuiltinType::Bool: return eBasicTypeBool; case clang::BuiltinType::Char_S: return eBasicTypeSignedChar; case clang::BuiltinType::Char_U: return eBasicTypeUnsignedChar; case clang::BuiltinType::Char16: return eBasicTypeChar16; case clang::BuiltinType::Char32: return eBasicTypeChar32; case clang::BuiltinType::UChar: return eBasicTypeUnsignedChar; case clang::BuiltinType::SChar: return eBasicTypeSignedChar; case clang::BuiltinType::WChar_S: return eBasicTypeSignedWChar; case clang::BuiltinType::WChar_U: return eBasicTypeUnsignedWChar; case clang::BuiltinType::Short: return eBasicTypeShort; case clang::BuiltinType::UShort: return eBasicTypeUnsignedShort; case clang::BuiltinType::Int: return eBasicTypeInt; case clang::BuiltinType::UInt: return eBasicTypeUnsignedInt; case clang::BuiltinType::Long: return eBasicTypeLong; case clang::BuiltinType::ULong: return eBasicTypeUnsignedLong; case clang::BuiltinType::LongLong: return eBasicTypeLongLong; case clang::BuiltinType::ULongLong: return eBasicTypeUnsignedLongLong; case clang::BuiltinType::Int128: return eBasicTypeInt128; case clang::BuiltinType::UInt128: return eBasicTypeUnsignedInt128; case clang::BuiltinType::Half: return eBasicTypeHalf; case clang::BuiltinType::Float: return eBasicTypeFloat; case clang::BuiltinType::Double: return eBasicTypeDouble; case clang::BuiltinType::LongDouble: return eBasicTypeLongDouble; case clang::BuiltinType::NullPtr: return eBasicTypeNullPtr; case clang::BuiltinType::ObjCId: return eBasicTypeObjCID; case clang::BuiltinType::ObjCClass: return eBasicTypeObjCClass; case clang::BuiltinType::ObjCSel: return eBasicTypeObjCSel; default: return eBasicTypeOther; } } } return eBasicTypeInvalid; } void ClangASTContext::ForEachEnumerator( lldb::opaque_compiler_type_t type, std::function const &callback) { const clang::EnumType *enum_type = llvm::dyn_cast(GetCanonicalQualType(type)); if (enum_type) { const clang::EnumDecl *enum_decl = enum_type->getDecl(); if (enum_decl) { CompilerType integer_type(this, enum_decl->getIntegerType().getAsOpaquePtr()); clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos; for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) { ConstString name(enum_pos->getNameAsString().c_str()); if (!callback(integer_type, name, enum_pos->getInitVal())) break; } } } } #pragma mark Aggregate Types uint32_t ClangASTContext::GetNumFields(lldb::opaque_compiler_type_t type) { if (!type) return 0; uint32_t count = 0; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::dyn_cast(qual_type.getTypePtr()); if (record_type) { clang::RecordDecl *record_decl = record_type->getDecl(); if (record_decl) { uint32_t field_idx = 0; clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field) ++field_idx; count = field_idx; } } } break; case clang::Type::Typedef: count = CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetNumFields(); break; case clang::Type::Auto: count = CompilerType(getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetNumFields(); break; case clang::Type::Elaborated: count = CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetNumFields(); break; case clang::Type::Paren: count = CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetNumFields(); break; case clang::Type::ObjCObjectPointer: { const clang::ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); const clang::ObjCInterfaceType *objc_interface_type = objc_class_type->getInterfaceType(); if (objc_interface_type && GetCompleteType((lldb::opaque_compiler_type_t)objc_interface_type)) { clang::ObjCInterfaceDecl *class_interface_decl = objc_interface_type->getDecl(); if (class_interface_decl) { count = class_interface_decl->ivar_size(); } } break; } case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) count = class_interface_decl->ivar_size(); } } break; default: break; } return count; } static lldb::opaque_compiler_type_t GetObjCFieldAtIndex(clang::ASTContext *ast, clang::ObjCInterfaceDecl *class_interface_decl, size_t idx, std::string &name, uint64_t *bit_offset_ptr, uint32_t *bitfield_bit_size_ptr, bool *is_bitfield_ptr) { if (class_interface_decl) { if (idx < (class_interface_decl->ivar_size())) { clang::ObjCInterfaceDecl::ivar_iterator ivar_pos, ivar_end = class_interface_decl->ivar_end(); uint32_t ivar_idx = 0; for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end; ++ivar_pos, ++ivar_idx) { if (ivar_idx == idx) { const clang::ObjCIvarDecl *ivar_decl = *ivar_pos; clang::QualType ivar_qual_type(ivar_decl->getType()); name.assign(ivar_decl->getNameAsString()); if (bit_offset_ptr) { const clang::ASTRecordLayout &interface_layout = ast->getASTObjCInterfaceLayout(class_interface_decl); *bit_offset_ptr = interface_layout.getFieldOffset(ivar_idx); } const bool is_bitfield = ivar_pos->isBitField(); if (bitfield_bit_size_ptr) { *bitfield_bit_size_ptr = 0; if (is_bitfield && ast) { clang::Expr *bitfield_bit_size_expr = ivar_pos->getBitWidth(); llvm::APSInt bitfield_apsint; if (bitfield_bit_size_expr && bitfield_bit_size_expr->EvaluateAsInt(bitfield_apsint, *ast)) { *bitfield_bit_size_ptr = bitfield_apsint.getLimitedValue(); } } } if (is_bitfield_ptr) *is_bitfield_ptr = is_bitfield; return ivar_qual_type.getAsOpaquePtr(); } } } } return nullptr; } CompilerType ClangASTContext::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 (!type) return CompilerType(); clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); uint32_t field_idx = 0; clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++field_idx) { if (idx == field_idx) { // Print the member type if requested // Print the member name and equal sign name.assign(field->getNameAsString()); // Figure out the type byte size (field_type_info.first) and // alignment (field_type_info.second) from the AST context. if (bit_offset_ptr) { const clang::ASTRecordLayout &record_layout = getASTContext()->getASTRecordLayout(record_decl); *bit_offset_ptr = record_layout.getFieldOffset(field_idx); } const bool is_bitfield = field->isBitField(); if (bitfield_bit_size_ptr) { *bitfield_bit_size_ptr = 0; if (is_bitfield) { clang::Expr *bitfield_bit_size_expr = field->getBitWidth(); llvm::APSInt bitfield_apsint; if (bitfield_bit_size_expr && bitfield_bit_size_expr->EvaluateAsInt(bitfield_apsint, *getASTContext())) { *bitfield_bit_size_ptr = bitfield_apsint.getLimitedValue(); } } } if (is_bitfield_ptr) *is_bitfield_ptr = is_bitfield; return CompilerType(getASTContext(), field->getType()); } } } break; case clang::Type::ObjCObjectPointer: { const clang::ObjCObjectPointerType *objc_class_type = qual_type->getAsObjCInterfacePointerType(); const clang::ObjCInterfaceType *objc_interface_type = objc_class_type->getInterfaceType(); if (objc_interface_type && GetCompleteType((lldb::opaque_compiler_type_t)objc_interface_type)) { clang::ObjCInterfaceDecl *class_interface_decl = objc_interface_type->getDecl(); if (class_interface_decl) { return CompilerType( this, GetObjCFieldAtIndex(getASTContext(), class_interface_decl, idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr)); } } break; } case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); return CompilerType( this, GetObjCFieldAtIndex(getASTContext(), class_interface_decl, idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr)); } } break; case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr); case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetFieldAtIndex(idx, name, bit_offset_ptr, bitfield_bit_size_ptr, is_bitfield_ptr); default: break; } return CompilerType(); } uint32_t ClangASTContext::GetNumDirectBaseClasses(lldb::opaque_compiler_type_t type) { uint32_t count = 0; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) count = cxx_record_decl->getNumBases(); } break; case clang::Type::ObjCObjectPointer: count = GetPointeeType(type).GetNumDirectBaseClasses(); break; case clang::Type::ObjCObject: if (GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = qual_type->getAsObjCQualifiedInterfaceType(); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl && class_interface_decl->getSuperClass()) count = 1; } } break; case clang::Type::ObjCInterface: if (GetCompleteType(type)) { const clang::ObjCInterfaceType *objc_interface_type = qual_type->getAs(); if (objc_interface_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_interface_type->getInterface(); if (class_interface_decl && class_interface_decl->getSuperClass()) count = 1; } } break; case clang::Type::Typedef: count = GetNumDirectBaseClasses(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr()); break; case clang::Type::Auto: count = GetNumDirectBaseClasses(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr()); break; case clang::Type::Elaborated: count = GetNumDirectBaseClasses(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr()); break; case clang::Type::Paren: return GetNumDirectBaseClasses( llvm::cast(qual_type)->desugar().getAsOpaquePtr()); default: break; } return count; } uint32_t ClangASTContext::GetNumVirtualBaseClasses(lldb::opaque_compiler_type_t type) { uint32_t count = 0; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) count = cxx_record_decl->getNumVBases(); } break; case clang::Type::Typedef: count = GetNumVirtualBaseClasses(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr()); break; case clang::Type::Auto: count = GetNumVirtualBaseClasses(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr()); break; case clang::Type::Elaborated: count = GetNumVirtualBaseClasses(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr()); break; case clang::Type::Paren: count = GetNumVirtualBaseClasses( llvm::cast(qual_type)->desugar().getAsOpaquePtr()); break; default: break; } return count; } CompilerType ClangASTContext::GetDirectBaseClassAtIndex( lldb::opaque_compiler_type_t type, size_t idx, uint32_t *bit_offset_ptr) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { uint32_t curr_idx = 0; clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class, ++curr_idx) { if (curr_idx == idx) { if (bit_offset_ptr) { const clang::ASTRecordLayout &record_layout = getASTContext()->getASTRecordLayout(cxx_record_decl); const clang::CXXRecordDecl *base_class_decl = llvm::cast( base_class->getType() ->getAs() ->getDecl()); if (base_class->isVirtual()) *bit_offset_ptr = record_layout.getVBaseClassOffset(base_class_decl) .getQuantity() * 8; else *bit_offset_ptr = record_layout.getBaseClassOffset(base_class_decl) .getQuantity() * 8; } return CompilerType(this, base_class->getType().getAsOpaquePtr()); } } } } break; case clang::Type::ObjCObjectPointer: return GetPointeeType(type).GetDirectBaseClassAtIndex(idx, bit_offset_ptr); case clang::Type::ObjCObject: if (idx == 0 && GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = qual_type->getAsObjCQualifiedInterfaceType(); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); if (superclass_interface_decl) { if (bit_offset_ptr) *bit_offset_ptr = 0; return CompilerType(getASTContext(), getASTContext()->getObjCInterfaceType( superclass_interface_decl)); } } } } break; case clang::Type::ObjCInterface: if (idx == 0 && GetCompleteType(type)) { const clang::ObjCObjectType *objc_interface_type = qual_type->getAs(); if (objc_interface_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_interface_type->getInterface(); if (class_interface_decl) { clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); if (superclass_interface_decl) { if (bit_offset_ptr) *bit_offset_ptr = 0; return CompilerType(getASTContext(), getASTContext()->getObjCInterfaceType( superclass_interface_decl)); } } } } break; case clang::Type::Typedef: return GetDirectBaseClassAtIndex(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), idx, bit_offset_ptr); case clang::Type::Auto: return GetDirectBaseClassAtIndex(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), idx, bit_offset_ptr); case clang::Type::Elaborated: return GetDirectBaseClassAtIndex( llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), idx, bit_offset_ptr); case clang::Type::Paren: return GetDirectBaseClassAtIndex( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), idx, bit_offset_ptr); default: break; } return CompilerType(); } CompilerType ClangASTContext::GetVirtualBaseClassAtIndex( lldb::opaque_compiler_type_t type, size_t idx, uint32_t *bit_offset_ptr) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { uint32_t curr_idx = 0; clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->vbases_begin(), base_class_end = cxx_record_decl->vbases_end(); base_class != base_class_end; ++base_class, ++curr_idx) { if (curr_idx == idx) { if (bit_offset_ptr) { const clang::ASTRecordLayout &record_layout = getASTContext()->getASTRecordLayout(cxx_record_decl); const clang::CXXRecordDecl *base_class_decl = llvm::cast( base_class->getType() ->getAs() ->getDecl()); *bit_offset_ptr = record_layout.getVBaseClassOffset(base_class_decl) .getQuantity() * 8; } return CompilerType(this, base_class->getType().getAsOpaquePtr()); } } } } break; case clang::Type::Typedef: return GetVirtualBaseClassAtIndex(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), idx, bit_offset_ptr); case clang::Type::Auto: return GetVirtualBaseClassAtIndex(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), idx, bit_offset_ptr); case clang::Type::Elaborated: return GetVirtualBaseClassAtIndex( llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), idx, bit_offset_ptr); case clang::Type::Paren: return GetVirtualBaseClassAtIndex( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), idx, bit_offset_ptr); default: break; } return CompilerType(); } // If a pointer to a pointee type (the clang_type arg) says that it has no // children, then we either need to trust it, or override it and return a // different result. For example, an "int *" has one child that is an integer, // but a function pointer doesn't have any children. Likewise if a Record type // claims it has no children, then there really is nothing to show. uint32_t ClangASTContext::GetNumPointeeChildren(clang::QualType type) { if (type.isNull()) return 0; clang::QualType qual_type(type.getCanonicalType()); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Builtin: switch (llvm::cast(qual_type)->getKind()) { case clang::BuiltinType::UnknownAny: case clang::BuiltinType::Void: case clang::BuiltinType::NullPtr: case clang::BuiltinType::OCLEvent: case clang::BuiltinType::OCLImage1dRO: case clang::BuiltinType::OCLImage1dWO: case clang::BuiltinType::OCLImage1dRW: case clang::BuiltinType::OCLImage1dArrayRO: case clang::BuiltinType::OCLImage1dArrayWO: case clang::BuiltinType::OCLImage1dArrayRW: case clang::BuiltinType::OCLImage1dBufferRO: case clang::BuiltinType::OCLImage1dBufferWO: case clang::BuiltinType::OCLImage1dBufferRW: case clang::BuiltinType::OCLImage2dRO: case clang::BuiltinType::OCLImage2dWO: case clang::BuiltinType::OCLImage2dRW: case clang::BuiltinType::OCLImage2dArrayRO: case clang::BuiltinType::OCLImage2dArrayWO: case clang::BuiltinType::OCLImage2dArrayRW: case clang::BuiltinType::OCLImage3dRO: case clang::BuiltinType::OCLImage3dWO: case clang::BuiltinType::OCLImage3dRW: case clang::BuiltinType::OCLSampler: return 0; case clang::BuiltinType::Bool: case clang::BuiltinType::Char_U: case clang::BuiltinType::UChar: case clang::BuiltinType::WChar_U: case clang::BuiltinType::Char16: case clang::BuiltinType::Char32: case clang::BuiltinType::UShort: case clang::BuiltinType::UInt: case clang::BuiltinType::ULong: case clang::BuiltinType::ULongLong: case clang::BuiltinType::UInt128: case clang::BuiltinType::Char_S: case clang::BuiltinType::SChar: case clang::BuiltinType::WChar_S: case clang::BuiltinType::Short: case clang::BuiltinType::Int: case clang::BuiltinType::Long: case clang::BuiltinType::LongLong: case clang::BuiltinType::Int128: case clang::BuiltinType::Float: case clang::BuiltinType::Double: case clang::BuiltinType::LongDouble: case clang::BuiltinType::Dependent: case clang::BuiltinType::Overload: case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCClass: case clang::BuiltinType::ObjCSel: case clang::BuiltinType::BoundMember: case clang::BuiltinType::Half: case clang::BuiltinType::ARCUnbridgedCast: case clang::BuiltinType::PseudoObject: case clang::BuiltinType::BuiltinFn: case clang::BuiltinType::OMPArraySection: return 1; default: return 0; } break; case clang::Type::Complex: return 1; case clang::Type::Pointer: return 1; case clang::Type::BlockPointer: return 0; // If block pointers don't have debug info, then no children for // them case clang::Type::LValueReference: return 1; case clang::Type::RValueReference: return 1; case clang::Type::MemberPointer: return 0; case clang::Type::ConstantArray: return 0; case clang::Type::IncompleteArray: return 0; case clang::Type::VariableArray: return 0; case clang::Type::DependentSizedArray: return 0; case clang::Type::DependentSizedExtVector: return 0; case clang::Type::Vector: return 0; case clang::Type::ExtVector: return 0; case clang::Type::FunctionProto: return 0; // When we function pointers, they have no children... case clang::Type::FunctionNoProto: return 0; // When we function pointers, they have no children... case clang::Type::UnresolvedUsing: return 0; case clang::Type::Paren: return GetNumPointeeChildren( llvm::cast(qual_type)->desugar()); case clang::Type::Typedef: return GetNumPointeeChildren(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()); case clang::Type::Auto: return GetNumPointeeChildren( llvm::cast(qual_type)->getDeducedType()); case clang::Type::Elaborated: return GetNumPointeeChildren( llvm::cast(qual_type)->getNamedType()); case clang::Type::TypeOfExpr: return 0; case clang::Type::TypeOf: return 0; case clang::Type::Decltype: return 0; case clang::Type::Record: return 0; case clang::Type::Enum: return 1; case clang::Type::TemplateTypeParm: return 1; case clang::Type::SubstTemplateTypeParm: return 1; case clang::Type::TemplateSpecialization: return 1; case clang::Type::InjectedClassName: return 0; case clang::Type::DependentName: return 1; case clang::Type::DependentTemplateSpecialization: return 1; case clang::Type::ObjCObject: return 0; case clang::Type::ObjCInterface: return 0; case clang::Type::ObjCObjectPointer: return 1; default: break; } return 0; } CompilerType ClangASTContext::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) { if (!type) return CompilerType(); clang::QualType parent_qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass parent_type_class = parent_qual_type->getTypeClass(); child_bitfield_bit_size = 0; child_bitfield_bit_offset = 0; child_is_base_class = false; language_flags = 0; const bool idx_is_valid = idx < GetNumChildren(type, omit_empty_base_classes); uint32_t bit_offset; switch (parent_type_class) { case clang::Type::Builtin: if (idx_is_valid) { switch (llvm::cast(parent_qual_type)->getKind()) { case clang::BuiltinType::ObjCId: case clang::BuiltinType::ObjCClass: child_name = "isa"; child_byte_size = getASTContext()->getTypeSize(getASTContext()->ObjCBuiltinClassTy) / CHAR_BIT; return CompilerType(getASTContext(), getASTContext()->ObjCBuiltinClassTy); default: break; } } break; case clang::Type::Record: if (idx_is_valid && GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast(parent_qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); const clang::ASTRecordLayout &record_layout = getASTContext()->getASTRecordLayout(record_decl); uint32_t child_idx = 0; const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) { // We might have base classes to print out first clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { const clang::CXXRecordDecl *base_class_decl = nullptr; // Skip empty base classes if (omit_empty_base_classes) { base_class_decl = llvm::cast( base_class->getType()->getAs()->getDecl()); if (ClangASTContext::RecordHasFields(base_class_decl) == false) continue; } if (idx == child_idx) { if (base_class_decl == nullptr) base_class_decl = llvm::cast( base_class->getType()->getAs()->getDecl()); if (base_class->isVirtual()) { bool handled = false; if (valobj) { Error err; AddressType addr_type = eAddressTypeInvalid; lldb::addr_t vtable_ptr_addr = valobj->GetCPPVTableAddress(addr_type); if (vtable_ptr_addr != LLDB_INVALID_ADDRESS && addr_type == eAddressTypeLoad) { ExecutionContext exe_ctx(valobj->GetExecutionContextRef()); Process *process = exe_ctx.GetProcessPtr(); if (process) { clang::VTableContextBase *vtable_ctx = getASTContext()->getVTableContext(); if (vtable_ctx) { if (vtable_ctx->isMicrosoft()) { clang::MicrosoftVTableContext *msoft_vtable_ctx = static_cast( vtable_ctx); if (vtable_ptr_addr) { const lldb::addr_t vbtable_ptr_addr = vtable_ptr_addr + record_layout.getVBPtrOffset().getQuantity(); const lldb::addr_t vbtable_ptr = process->ReadPointerFromMemory(vbtable_ptr_addr, err); if (vbtable_ptr != LLDB_INVALID_ADDRESS) { // Get the index into the virtual base table. The // index is the index in uint32_t from vbtable_ptr const unsigned vbtable_index = msoft_vtable_ctx->getVBTableIndex( cxx_record_decl, base_class_decl); const lldb::addr_t base_offset_addr = vbtable_ptr + vbtable_index * 4; const uint32_t base_offset = process->ReadUnsignedIntegerFromMemory( base_offset_addr, 4, UINT32_MAX, err); if (base_offset != UINT32_MAX) { handled = true; bit_offset = base_offset * 8; } } } } else { clang::ItaniumVTableContext *itanium_vtable_ctx = static_cast( vtable_ctx); if (vtable_ptr_addr) { const lldb::addr_t vtable_ptr = process->ReadPointerFromMemory(vtable_ptr_addr, err); if (vtable_ptr != LLDB_INVALID_ADDRESS) { clang::CharUnits base_offset_offset = itanium_vtable_ctx->getVirtualBaseOffsetOffset( cxx_record_decl, base_class_decl); const lldb::addr_t base_offset_addr = vtable_ptr + base_offset_offset.getQuantity(); const uint32_t base_offset_size = process->GetAddressByteSize(); const uint64_t base_offset = process->ReadUnsignedIntegerFromMemory( base_offset_addr, base_offset_size, UINT32_MAX, err); if (base_offset < UINT32_MAX) { handled = true; bit_offset = base_offset * 8; } } } } } } } } if (!handled) bit_offset = record_layout.getVBaseClassOffset(base_class_decl) .getQuantity() * 8; } else bit_offset = record_layout.getBaseClassOffset(base_class_decl) .getQuantity() * 8; // Base classes should be a multiple of 8 bits in size child_byte_offset = bit_offset / 8; CompilerType base_class_clang_type(getASTContext(), base_class->getType()); child_name = base_class_clang_type.GetTypeName().AsCString(""); uint64_t base_class_clang_type_bit_size = base_class_clang_type.GetBitSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); // Base classes bit sizes should be a multiple of 8 bits in size assert(base_class_clang_type_bit_size % 8 == 0); child_byte_size = base_class_clang_type_bit_size / 8; child_is_base_class = true; return base_class_clang_type; } // We don't increment the child index in the for loop since we might // be skipping empty base classes ++child_idx; } } // Make sure index is in range... uint32_t field_idx = 0; clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++field_idx, ++child_idx) { if (idx == child_idx) { // Print the member type if requested // Print the member name and equal sign child_name.assign(field->getNameAsString()); // Figure out the type byte size (field_type_info.first) and // alignment (field_type_info.second) from the AST context. CompilerType field_clang_type(getASTContext(), field->getType()); assert(field_idx < record_layout.getFieldCount()); child_byte_size = field_clang_type.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); const uint32_t child_bit_size = child_byte_size * 8; // Figure out the field offset within the current struct/union/class // type bit_offset = record_layout.getFieldOffset(field_idx); if (ClangASTContext::FieldIsBitfield(getASTContext(), *field, child_bitfield_bit_size)) { child_bitfield_bit_offset = bit_offset % child_bit_size; const uint32_t child_bit_offset = bit_offset - child_bitfield_bit_offset; child_byte_offset = child_bit_offset / 8; } else { child_byte_offset = bit_offset / 8; } return field_clang_type; } } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (idx_is_valid && GetCompleteType(type)) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(parent_qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { uint32_t child_idx = 0; clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { const clang::ASTRecordLayout &interface_layout = getASTContext()->getASTObjCInterfaceLayout(class_interface_decl); clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); if (superclass_interface_decl) { if (omit_empty_base_classes) { CompilerType base_class_clang_type( getASTContext(), getASTContext()->getObjCInterfaceType( superclass_interface_decl)); if (base_class_clang_type.GetNumChildren( omit_empty_base_classes) > 0) { if (idx == 0) { clang::QualType ivar_qual_type( getASTContext()->getObjCInterfaceType( superclass_interface_decl)); child_name.assign( superclass_interface_decl->getNameAsString()); clang::TypeInfo ivar_type_info = getASTContext()->getTypeInfo(ivar_qual_type.getTypePtr()); child_byte_size = ivar_type_info.Width / 8; child_byte_offset = 0; child_is_base_class = true; return CompilerType(getASTContext(), ivar_qual_type); } ++child_idx; } } else ++child_idx; } const uint32_t superclass_idx = child_idx; if (idx < (child_idx + class_interface_decl->ivar_size())) { clang::ObjCInterfaceDecl::ivar_iterator ivar_pos, ivar_end = class_interface_decl->ivar_end(); for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end; ++ivar_pos) { if (child_idx == idx) { clang::ObjCIvarDecl *ivar_decl = *ivar_pos; clang::QualType ivar_qual_type(ivar_decl->getType()); child_name.assign(ivar_decl->getNameAsString()); clang::TypeInfo ivar_type_info = getASTContext()->getTypeInfo(ivar_qual_type.getTypePtr()); child_byte_size = ivar_type_info.Width / 8; // Figure out the field offset within the current // struct/union/class type // For ObjC objects, we can't trust the bit offset we get from // the Clang AST, since // that doesn't account for the space taken up by unbacked // properties, or from // the changing size of base classes that are newer than this // class. // So if we have a process around that we can ask about this // object, do so. child_byte_offset = LLDB_INVALID_IVAR_OFFSET; Process *process = nullptr; if (exe_ctx) process = exe_ctx->GetProcessPtr(); if (process) { ObjCLanguageRuntime *objc_runtime = process->GetObjCLanguageRuntime(); if (objc_runtime != nullptr) { CompilerType parent_ast_type(getASTContext(), parent_qual_type); child_byte_offset = objc_runtime->GetByteOffsetForIvar( parent_ast_type, ivar_decl->getNameAsString().c_str()); } } // Setting this to UINT32_MAX to make sure we don't compute it // twice... bit_offset = UINT32_MAX; if (child_byte_offset == static_cast(LLDB_INVALID_IVAR_OFFSET)) { bit_offset = interface_layout.getFieldOffset(child_idx - superclass_idx); child_byte_offset = bit_offset / 8; } // Note, the ObjC Ivar Byte offset is just that, it doesn't // account for the bit offset // of a bitfield within its containing object. So regardless of // where we get the byte // offset from, we still need to get the bit offset for // bitfields from the layout. if (ClangASTContext::FieldIsBitfield(getASTContext(), ivar_decl, child_bitfield_bit_size)) { if (bit_offset == UINT32_MAX) bit_offset = interface_layout.getFieldOffset( child_idx - superclass_idx); child_bitfield_bit_offset = bit_offset % 8; } return CompilerType(getASTContext(), ivar_qual_type); } ++child_idx; } } } } } break; case clang::Type::ObjCObjectPointer: if (idx_is_valid) { CompilerType pointee_clang_type(GetPointeeType(type)); if (transparent_pointers && pointee_clang_type.IsAggregateType()) { child_is_deref_of_parent = false; bool tmp_child_is_deref_of_parent = false; return pointee_clang_type.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_clang_type.GetCompleteType()) { child_byte_size = pointee_clang_type.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = 0; return pointee_clang_type; } } } break; case clang::Type::Vector: case clang::Type::ExtVector: if (idx_is_valid) { const clang::VectorType *array = llvm::cast(parent_qual_type.getTypePtr()); if (array) { CompilerType element_type(getASTContext(), array->getElementType()); if (element_type.GetCompleteType()) { char element_name[64]; ::snprintf(element_name, sizeof(element_name), "[%" PRIu64 "]", static_cast(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; } } } break; case clang::Type::ConstantArray: case clang::Type::IncompleteArray: if (ignore_array_bounds || idx_is_valid) { const clang::ArrayType *array = GetQualType(type)->getAsArrayTypeUnsafe(); if (array) { CompilerType element_type(getASTContext(), array->getElementType()); if (element_type.GetCompleteType()) { child_name = llvm::formatv("[{0}]", idx); 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; } } } break; case clang::Type::Pointer: { CompilerType pointee_clang_type(GetPointeeType(type)); // Don't dereference "void *" pointers if (pointee_clang_type.IsVoidType()) return CompilerType(); if (transparent_pointers && pointee_clang_type.IsAggregateType()) { child_is_deref_of_parent = false; bool tmp_child_is_deref_of_parent = false; return pointee_clang_type.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) { child_byte_size = pointee_clang_type.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = 0; return pointee_clang_type; } } break; } case clang::Type::LValueReference: case clang::Type::RValueReference: if (idx_is_valid) { const clang::ReferenceType *reference_type = llvm::cast(parent_qual_type.getTypePtr()); CompilerType pointee_clang_type(getASTContext(), reference_type->getPointeeType()); if (transparent_pointers && pointee_clang_type.IsAggregateType()) { child_is_deref_of_parent = false; bool tmp_child_is_deref_of_parent = false; return pointee_clang_type.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 { 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) { child_byte_size = pointee_clang_type.GetByteSize( exe_ctx ? exe_ctx->GetBestExecutionContextScope() : NULL); child_byte_offset = 0; return pointee_clang_type; } } } break; case clang::Type::Typedef: { CompilerType typedefed_clang_type( getASTContext(), llvm::cast(parent_qual_type) ->getDecl() ->getUnderlyingType()); return typedefed_clang_type.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); } break; case clang::Type::Auto: { CompilerType elaborated_clang_type( getASTContext(), llvm::cast(parent_qual_type)->getDeducedType()); return elaborated_clang_type.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); } case clang::Type::Elaborated: { CompilerType elaborated_clang_type( getASTContext(), llvm::cast(parent_qual_type)->getNamedType()); return elaborated_clang_type.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); } case clang::Type::Paren: { CompilerType paren_clang_type( getASTContext(), llvm::cast(parent_qual_type)->desugar()); return paren_clang_type.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); } default: break; } return CompilerType(); } static uint32_t GetIndexForRecordBase(const clang::RecordDecl *record_decl, const clang::CXXBaseSpecifier *base_spec, bool omit_empty_base_classes) { uint32_t child_idx = 0; const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); // const char *super_name = record_decl->getNameAsCString(); // const char *base_name = // base_spec->getType()->getAs()->getDecl()->getNameAsCString(); // printf ("GetIndexForRecordChild (%s, %s)\n", super_name, base_name); // if (cxx_record_decl) { clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { if (omit_empty_base_classes) { if (BaseSpecifierIsEmpty(base_class)) continue; } // printf ("GetIndexForRecordChild (%s, %s) base[%u] = %s\n", // super_name, base_name, // child_idx, // base_class->getType()->getAs()->getDecl()->getNameAsCString()); // // if (base_class == base_spec) return child_idx; ++child_idx; } } return UINT32_MAX; } static uint32_t GetIndexForRecordChild(const clang::RecordDecl *record_decl, clang::NamedDecl *canonical_decl, bool omit_empty_base_classes) { uint32_t child_idx = ClangASTContext::GetNumBaseClasses( llvm::dyn_cast(record_decl), omit_empty_base_classes); clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++child_idx) { if (field->getCanonicalDecl() == canonical_decl) return child_idx; } return UINT32_MAX; } // Look for a child member (doesn't include base classes, but it does include // their members) in the type hierarchy. Returns an index path into "clang_type" // on how to reach the appropriate member. // // class A // { // public: // int m_a; // int m_b; // }; // // class B // { // }; // // class C : // public B, // public A // { // }; // // If we have a clang type that describes "class C", and we wanted to looked // "m_b" in it: // // With omit_empty_base_classes == false we would get an integer array back // with: // { 1, 1 } // The first index 1 is the child index for "class A" within class C // The second index 1 is the child index for "m_b" within class A // // With omit_empty_base_classes == true we would get an integer array back with: // { 0, 1 } // The first index 0 is the child index for "class A" within class C (since // class B doesn't have any members it doesn't count) // The second index 1 is the child index for "m_b" within class A size_t ClangASTContext::GetIndexOfChildMemberWithName( lldb::opaque_compiler_type_t type, const char *name, bool omit_empty_base_classes, std::vector &child_indexes) { if (type && name && name[0]) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); uint32_t child_idx = 0; const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); // Try and find a field that matches NAME clang::RecordDecl::field_iterator field, field_end; llvm::StringRef name_sref(name); for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++child_idx) { llvm::StringRef field_name = field->getName(); if (field_name.empty()) { CompilerType field_type(getASTContext(), field->getType()); child_indexes.push_back(child_idx); if (field_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes)) return child_indexes.size(); child_indexes.pop_back(); } else if (field_name.equals(name_sref)) { // We have to add on the number of base classes to this index! child_indexes.push_back( child_idx + ClangASTContext::GetNumBaseClasses( cxx_record_decl, omit_empty_base_classes)); return child_indexes.size(); } } if (cxx_record_decl) { const clang::RecordDecl *parent_record_decl = cxx_record_decl; // printf ("parent = %s\n", parent_record_decl->getNameAsCString()); // const Decl *root_cdecl = cxx_record_decl->getCanonicalDecl(); // Didn't find things easily, lets let clang do its thang... clang::IdentifierInfo &ident_ref = getASTContext()->Idents.get(name_sref); clang::DeclarationName decl_name(&ident_ref); clang::CXXBasePaths paths; if (cxx_record_decl->lookupInBases( [decl_name](const clang::CXXBaseSpecifier *specifier, clang::CXXBasePath &path) { return clang::CXXRecordDecl::FindOrdinaryMember( specifier, path, decl_name); }, paths)) { clang::CXXBasePaths::const_paths_iterator path, path_end = paths.end(); for (path = paths.begin(); path != path_end; ++path) { const size_t num_path_elements = path->size(); for (size_t e = 0; e < num_path_elements; ++e) { clang::CXXBasePathElement elem = (*path)[e]; child_idx = GetIndexForRecordBase(parent_record_decl, elem.Base, omit_empty_base_classes); if (child_idx == UINT32_MAX) { child_indexes.clear(); return 0; } else { child_indexes.push_back(child_idx); parent_record_decl = llvm::cast( elem.Base->getType() ->getAs() ->getDecl()); } } for (clang::NamedDecl *path_decl : path->Decls) { child_idx = GetIndexForRecordChild( parent_record_decl, path_decl, omit_empty_base_classes); if (child_idx == UINT32_MAX) { child_indexes.clear(); return 0; } else { child_indexes.push_back(child_idx); } } } return child_indexes.size(); } } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteType(type)) { llvm::StringRef name_sref(name); const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { uint32_t child_idx = 0; clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { clang::ObjCInterfaceDecl::ivar_iterator ivar_pos, ivar_end = class_interface_decl->ivar_end(); clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end; ++ivar_pos, ++child_idx) { const clang::ObjCIvarDecl *ivar_decl = *ivar_pos; if (ivar_decl->getName().equals(name_sref)) { if ((!omit_empty_base_classes && superclass_interface_decl) || (omit_empty_base_classes && ObjCDeclHasIVars(superclass_interface_decl, true))) ++child_idx; child_indexes.push_back(child_idx); return child_indexes.size(); } } if (superclass_interface_decl) { // The super class index is always zero for ObjC classes, // so we push it onto the child indexes in case we find // an ivar in our superclass... child_indexes.push_back(0); CompilerType superclass_clang_type( getASTContext(), getASTContext()->getObjCInterfaceType( superclass_interface_decl)); if (superclass_clang_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes)) { // We did find an ivar in a superclass so just // return the results! return child_indexes.size(); } // We didn't find an ivar matching "name" in our // superclass, pop the superclass zero index that // we pushed on above. child_indexes.pop_back(); } } } } break; case clang::Type::ObjCObjectPointer: { CompilerType objc_object_clang_type( getASTContext(), llvm::cast(qual_type.getTypePtr()) ->getPointeeType()); return objc_object_clang_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes); } break; case clang::Type::ConstantArray: { // const clang::ConstantArrayType *array = // llvm::cast(parent_qual_type.getTypePtr()); // const uint64_t element_count = // array->getSize().getLimitedValue(); // // if (idx < element_count) // { // std::pair field_type_info = // ast->getTypeInfo(array->getElementType()); // // char element_name[32]; // ::snprintf (element_name, sizeof (element_name), // "%s[%u]", parent_name ? parent_name : "", idx); // // child_name.assign(element_name); // assert(field_type_info.first % 8 == 0); // child_byte_size = field_type_info.first / 8; // child_byte_offset = idx * child_byte_size; // return array->getElementType().getAsOpaquePtr(); // } } break; // case clang::Type::MemberPointerType: // { // MemberPointerType *mem_ptr_type = // llvm::cast(qual_type.getTypePtr()); // clang::QualType pointee_type = // mem_ptr_type->getPointeeType(); // // if (ClangASTContext::IsAggregateType // (pointee_type.getAsOpaquePtr())) // { // return GetIndexOfChildWithName (ast, // mem_ptr_type->getPointeeType().getAsOpaquePtr(), // name); // } // } // break; // case clang::Type::LValueReference: case clang::Type::RValueReference: { const clang::ReferenceType *reference_type = llvm::cast(qual_type.getTypePtr()); clang::QualType pointee_type(reference_type->getPointeeType()); CompilerType pointee_clang_type(getASTContext(), pointee_type); if (pointee_clang_type.IsAggregateType()) { return pointee_clang_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes); } } break; case clang::Type::Pointer: { CompilerType pointee_clang_type(GetPointeeType(type)); if (pointee_clang_type.IsAggregateType()) { return pointee_clang_type.GetIndexOfChildMemberWithName( name, omit_empty_base_classes, child_indexes); } } break; case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetIndexOfChildMemberWithName(name, omit_empty_base_classes, child_indexes); case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetIndexOfChildMemberWithName(name, omit_empty_base_classes, child_indexes); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetIndexOfChildMemberWithName(name, omit_empty_base_classes, child_indexes); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetIndexOfChildMemberWithName(name, omit_empty_base_classes, child_indexes); default: break; } } return 0; } // Get the index of the child of "clang_type" whose name matches. This function // doesn't descend into the children, but only looks one level deep and name // matches can include base class names. uint32_t ClangASTContext::GetIndexOfChildWithName(lldb::opaque_compiler_type_t type, const char *name, bool omit_empty_base_classes) { if (type && name && name[0]) { clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); uint32_t child_idx = 0; const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) { clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { // Skip empty base classes clang::CXXRecordDecl *base_class_decl = llvm::cast( base_class->getType() ->getAs() ->getDecl()); if (omit_empty_base_classes && ClangASTContext::RecordHasFields(base_class_decl) == false) continue; CompilerType base_class_clang_type(getASTContext(), base_class->getType()); std::string base_class_type_name( base_class_clang_type.GetTypeName().AsCString("")); if (base_class_type_name.compare(name) == 0) return child_idx; ++child_idx; } } // Try and find a field that matches NAME clang::RecordDecl::field_iterator field, field_end; llvm::StringRef name_sref(name); for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++child_idx) { if (field->getName().equals(name_sref)) return child_idx; } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: if (GetCompleteType(type)) { llvm::StringRef name_sref(name); const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { uint32_t child_idx = 0; clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { clang::ObjCInterfaceDecl::ivar_iterator ivar_pos, ivar_end = class_interface_decl->ivar_end(); clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass(); for (ivar_pos = class_interface_decl->ivar_begin(); ivar_pos != ivar_end; ++ivar_pos, ++child_idx) { const clang::ObjCIvarDecl *ivar_decl = *ivar_pos; if (ivar_decl->getName().equals(name_sref)) { if ((!omit_empty_base_classes && superclass_interface_decl) || (omit_empty_base_classes && ObjCDeclHasIVars(superclass_interface_decl, true))) ++child_idx; return child_idx; } } if (superclass_interface_decl) { if (superclass_interface_decl->getName().equals(name_sref)) return 0; } } } } break; case clang::Type::ObjCObjectPointer: { CompilerType pointee_clang_type( getASTContext(), llvm::cast(qual_type.getTypePtr()) ->getPointeeType()); return pointee_clang_type.GetIndexOfChildWithName( name, omit_empty_base_classes); } break; case clang::Type::ConstantArray: { // const clang::ConstantArrayType *array = // llvm::cast(parent_qual_type.getTypePtr()); // const uint64_t element_count = // array->getSize().getLimitedValue(); // // if (idx < element_count) // { // std::pair field_type_info = // ast->getTypeInfo(array->getElementType()); // // char element_name[32]; // ::snprintf (element_name, sizeof (element_name), // "%s[%u]", parent_name ? parent_name : "", idx); // // child_name.assign(element_name); // assert(field_type_info.first % 8 == 0); // child_byte_size = field_type_info.first / 8; // child_byte_offset = idx * child_byte_size; // return array->getElementType().getAsOpaquePtr(); // } } break; // case clang::Type::MemberPointerType: // { // MemberPointerType *mem_ptr_type = // llvm::cast(qual_type.getTypePtr()); // clang::QualType pointee_type = // mem_ptr_type->getPointeeType(); // // if (ClangASTContext::IsAggregateType // (pointee_type.getAsOpaquePtr())) // { // return GetIndexOfChildWithName (ast, // mem_ptr_type->getPointeeType().getAsOpaquePtr(), // name); // } // } // break; // case clang::Type::LValueReference: case clang::Type::RValueReference: { const clang::ReferenceType *reference_type = llvm::cast(qual_type.getTypePtr()); CompilerType pointee_type(getASTContext(), reference_type->getPointeeType()); if (pointee_type.IsAggregateType()) { return pointee_type.GetIndexOfChildWithName(name, omit_empty_base_classes); } } break; case clang::Type::Pointer: { const clang::PointerType *pointer_type = llvm::cast(qual_type.getTypePtr()); CompilerType pointee_type(getASTContext(), pointer_type->getPointeeType()); if (pointee_type.IsAggregateType()) { return pointee_type.GetIndexOfChildWithName(name, omit_empty_base_classes); } else { // if (parent_name) // { // child_name.assign(1, '*'); // child_name += parent_name; // } // // // We have a pointer to an simple type // if (idx == 0) // { // std::pair clang_type_info // = ast->getTypeInfo(pointee_type); // assert(clang_type_info.first % 8 == 0); // child_byte_size = clang_type_info.first / 8; // child_byte_offset = 0; // return pointee_type.getAsOpaquePtr(); // } } } break; case clang::Type::Auto: return CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType()) .GetIndexOfChildWithName(name, omit_empty_base_classes); case clang::Type::Elaborated: return CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType()) .GetIndexOfChildWithName(name, omit_empty_base_classes); case clang::Type::Paren: return CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .GetIndexOfChildWithName(name, omit_empty_base_classes); case clang::Type::Typedef: return CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType()) .GetIndexOfChildWithName(name, omit_empty_base_classes); default: break; } } return UINT32_MAX; } size_t ClangASTContext::GetNumTemplateArguments(lldb::opaque_compiler_type_t type) { if (!type) return 0; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { const clang::ClassTemplateSpecializationDecl *template_decl = llvm::dyn_cast( cxx_record_decl); if (template_decl) return template_decl->getTemplateArgs().size(); } } break; case clang::Type::Typedef: return (CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType())) .GetNumTemplateArguments(); case clang::Type::Auto: return (CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType())) .GetNumTemplateArguments(); case clang::Type::Elaborated: return (CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType())) .GetNumTemplateArguments(); case clang::Type::Paren: return (CompilerType(getASTContext(), llvm::cast(qual_type)->desugar())) .GetNumTemplateArguments(); default: break; } return 0; } CompilerType ClangASTContext::GetTemplateArgument(lldb::opaque_compiler_type_t type, size_t arg_idx, lldb::TemplateArgumentKind &kind) { if (!type) return CompilerType(); clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { const clang::ClassTemplateSpecializationDecl *template_decl = llvm::dyn_cast( cxx_record_decl); if (template_decl && arg_idx < template_decl->getTemplateArgs().size()) { const clang::TemplateArgument &template_arg = template_decl->getTemplateArgs()[arg_idx]; switch (template_arg.getKind()) { case clang::TemplateArgument::Null: kind = eTemplateArgumentKindNull; return CompilerType(); case clang::TemplateArgument::Type: kind = eTemplateArgumentKindType; return CompilerType(getASTContext(), template_arg.getAsType()); case clang::TemplateArgument::Declaration: kind = eTemplateArgumentKindDeclaration; return CompilerType(); case clang::TemplateArgument::Integral: kind = eTemplateArgumentKindIntegral; return CompilerType(getASTContext(), template_arg.getIntegralType()); case clang::TemplateArgument::Template: kind = eTemplateArgumentKindTemplate; return CompilerType(); case clang::TemplateArgument::TemplateExpansion: kind = eTemplateArgumentKindTemplateExpansion; return CompilerType(); case clang::TemplateArgument::Expression: kind = eTemplateArgumentKindExpression; return CompilerType(); case clang::TemplateArgument::Pack: kind = eTemplateArgumentKindPack; return CompilerType(); default: llvm_unreachable("Unhandled clang::TemplateArgument::ArgKind"); } } } } break; case clang::Type::Typedef: return (CompilerType(getASTContext(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType())) .GetTemplateArgument(arg_idx, kind); case clang::Type::Auto: return (CompilerType( getASTContext(), llvm::cast(qual_type)->getDeducedType())) .GetTemplateArgument(arg_idx, kind); case clang::Type::Elaborated: return (CompilerType( getASTContext(), llvm::cast(qual_type)->getNamedType())) .GetTemplateArgument(arg_idx, kind); case clang::Type::Paren: return (CompilerType(getASTContext(), llvm::cast(qual_type)->desugar())) .GetTemplateArgument(arg_idx, kind); default: break; } kind = eTemplateArgumentKindNull; return CompilerType(); } CompilerType ClangASTContext::GetTypeForFormatters(void *type) { if (type) return ClangUtil::RemoveFastQualifiers(CompilerType(this, type)); return CompilerType(); } clang::EnumDecl *ClangASTContext::GetAsEnumDecl(const CompilerType &type) { const clang::EnumType *enutype = llvm::dyn_cast(ClangUtil::GetCanonicalQualType(type)); if (enutype) return enutype->getDecl(); return NULL; } clang::RecordDecl *ClangASTContext::GetAsRecordDecl(const CompilerType &type) { const clang::RecordType *record_type = llvm::dyn_cast(ClangUtil::GetCanonicalQualType(type)); if (record_type) return record_type->getDecl(); return nullptr; } clang::TagDecl *ClangASTContext::GetAsTagDecl(const CompilerType &type) { clang::QualType qual_type = ClangUtil::GetCanonicalQualType(type); if (qual_type.isNull()) return nullptr; else return qual_type->getAsTagDecl(); } clang::CXXRecordDecl * ClangASTContext::GetAsCXXRecordDecl(lldb::opaque_compiler_type_t type) { return GetCanonicalQualType(type)->getAsCXXRecordDecl(); } clang::ObjCInterfaceDecl * ClangASTContext::GetAsObjCInterfaceDecl(const CompilerType &type) { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast( ClangUtil::GetCanonicalQualType(type)); if (objc_class_type) return objc_class_type->getInterface(); return nullptr; } clang::FieldDecl *ClangASTContext::AddFieldToRecordType( const CompilerType &type, const char *name, const CompilerType &field_clang_type, AccessType access, uint32_t bitfield_bit_size) { if (!type.IsValid() || !field_clang_type.IsValid()) return nullptr; ClangASTContext *ast = llvm::dyn_cast_or_null(type.GetTypeSystem()); if (!ast) return nullptr; clang::ASTContext *clang_ast = ast->getASTContext(); clang::FieldDecl *field = nullptr; clang::Expr *bit_width = nullptr; if (bitfield_bit_size != 0) { llvm::APInt bitfield_bit_size_apint( clang_ast->getTypeSize(clang_ast->IntTy), bitfield_bit_size); bit_width = new (*clang_ast) clang::IntegerLiteral(*clang_ast, bitfield_bit_size_apint, clang_ast->IntTy, clang::SourceLocation()); } clang::RecordDecl *record_decl = ast->GetAsRecordDecl(type); if (record_decl) { field = clang::FieldDecl::Create( *clang_ast, record_decl, clang::SourceLocation(), clang::SourceLocation(), name ? &clang_ast->Idents.get(name) : nullptr, // Identifier ClangUtil::GetQualType(field_clang_type), // Field type nullptr, // TInfo * bit_width, // BitWidth false, // Mutable clang::ICIS_NoInit); // HasInit if (!name) { // Determine whether this field corresponds to an anonymous // struct or union. if (const clang::TagType *TagT = field->getType()->getAs()) { if (clang::RecordDecl *Rec = llvm::dyn_cast(TagT->getDecl())) if (!Rec->getDeclName()) { Rec->setAnonymousStructOrUnion(true); field->setImplicit(); } } } if (field) { field->setAccess( ClangASTContext::ConvertAccessTypeToAccessSpecifier(access)); record_decl->addDecl(field); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(field); #endif } } else { clang::ObjCInterfaceDecl *class_interface_decl = ast->GetAsObjCInterfaceDecl(type); if (class_interface_decl) { const bool is_synthesized = false; field_clang_type.GetCompleteType(); field = clang::ObjCIvarDecl::Create( *clang_ast, class_interface_decl, clang::SourceLocation(), clang::SourceLocation(), name ? &clang_ast->Idents.get(name) : nullptr, // Identifier ClangUtil::GetQualType(field_clang_type), // Field type nullptr, // TypeSourceInfo * ConvertAccessTypeToObjCIvarAccessControl(access), bit_width, is_synthesized); if (field) { class_interface_decl->addDecl(field); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(field); #endif } } } return field; } void ClangASTContext::BuildIndirectFields(const CompilerType &type) { if (!type) return; ClangASTContext *ast = llvm::dyn_cast(type.GetTypeSystem()); if (!ast) return; clang::RecordDecl *record_decl = ast->GetAsRecordDecl(type); if (!record_decl) return; typedef llvm::SmallVector IndirectFieldVector; IndirectFieldVector indirect_fields; clang::RecordDecl::field_iterator field_pos; clang::RecordDecl::field_iterator field_end_pos = record_decl->field_end(); clang::RecordDecl::field_iterator last_field_pos = field_end_pos; for (field_pos = record_decl->field_begin(); field_pos != field_end_pos; last_field_pos = field_pos++) { if (field_pos->isAnonymousStructOrUnion()) { clang::QualType field_qual_type = field_pos->getType(); const clang::RecordType *field_record_type = field_qual_type->getAs(); if (!field_record_type) continue; clang::RecordDecl *field_record_decl = field_record_type->getDecl(); if (!field_record_decl) continue; for (clang::RecordDecl::decl_iterator di = field_record_decl->decls_begin(), de = field_record_decl->decls_end(); di != de; ++di) { if (clang::FieldDecl *nested_field_decl = llvm::dyn_cast(*di)) { clang::NamedDecl **chain = new (*ast->getASTContext()) clang::NamedDecl *[2]; chain[0] = *field_pos; chain[1] = nested_field_decl; clang::IndirectFieldDecl *indirect_field = clang::IndirectFieldDecl::Create( *ast->getASTContext(), record_decl, clang::SourceLocation(), nested_field_decl->getIdentifier(), nested_field_decl->getType(), {chain, 2}); indirect_field->setImplicit(); indirect_field->setAccess(ClangASTContext::UnifyAccessSpecifiers( field_pos->getAccess(), nested_field_decl->getAccess())); indirect_fields.push_back(indirect_field); } else if (clang::IndirectFieldDecl *nested_indirect_field_decl = llvm::dyn_cast(*di)) { size_t nested_chain_size = nested_indirect_field_decl->getChainingSize(); clang::NamedDecl **chain = new (*ast->getASTContext()) clang::NamedDecl *[nested_chain_size + 1]; chain[0] = *field_pos; int chain_index = 1; for (clang::IndirectFieldDecl::chain_iterator nci = nested_indirect_field_decl->chain_begin(), nce = nested_indirect_field_decl->chain_end(); nci < nce; ++nci) { chain[chain_index] = *nci; chain_index++; } clang::IndirectFieldDecl *indirect_field = clang::IndirectFieldDecl::Create( *ast->getASTContext(), record_decl, clang::SourceLocation(), nested_indirect_field_decl->getIdentifier(), nested_indirect_field_decl->getType(), {chain, nested_chain_size + 1}); indirect_field->setImplicit(); indirect_field->setAccess(ClangASTContext::UnifyAccessSpecifiers( field_pos->getAccess(), nested_indirect_field_decl->getAccess())); indirect_fields.push_back(indirect_field); } } } } // Check the last field to see if it has an incomplete array type as its // last member and if it does, the tell the record decl about it if (last_field_pos != field_end_pos) { if (last_field_pos->getType()->isIncompleteArrayType()) record_decl->hasFlexibleArrayMember(); } for (IndirectFieldVector::iterator ifi = indirect_fields.begin(), ife = indirect_fields.end(); ifi < ife; ++ifi) { record_decl->addDecl(*ifi); } } void ClangASTContext::SetIsPacked(const CompilerType &type) { if (type) { ClangASTContext *ast = llvm::dyn_cast(type.GetTypeSystem()); if (ast) { clang::RecordDecl *record_decl = GetAsRecordDecl(type); if (!record_decl) return; record_decl->addAttr( clang::PackedAttr::CreateImplicit(*ast->getASTContext())); } } } clang::VarDecl *ClangASTContext::AddVariableToRecordType( const CompilerType &type, const char *name, const CompilerType &var_type, AccessType access) { clang::VarDecl *var_decl = nullptr; if (!type.IsValid() || !var_type.IsValid()) return nullptr; ClangASTContext *ast = llvm::dyn_cast(type.GetTypeSystem()); if (!ast) return nullptr; clang::RecordDecl *record_decl = ast->GetAsRecordDecl(type); if (record_decl) { var_decl = clang::VarDecl::Create( *ast->getASTContext(), // ASTContext & record_decl, // DeclContext * clang::SourceLocation(), // clang::SourceLocation StartLoc clang::SourceLocation(), // clang::SourceLocation IdLoc name ? &ast->getASTContext()->Idents.get(name) : nullptr, // clang::IdentifierInfo * ClangUtil::GetQualType(var_type), // Variable clang::QualType nullptr, // TypeSourceInfo * clang::SC_Static); // StorageClass if (var_decl) { var_decl->setAccess( ClangASTContext::ConvertAccessTypeToAccessSpecifier(access)); record_decl->addDecl(var_decl); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(var_decl); #endif } } return var_decl; } clang::CXXMethodDecl *ClangASTContext::AddMethodToCXXRecordType( lldb::opaque_compiler_type_t type, const char *name, const CompilerType &method_clang_type, lldb::AccessType access, bool is_virtual, bool is_static, bool is_inline, bool is_explicit, bool is_attr_used, bool is_artificial) { if (!type || !method_clang_type.IsValid() || name == nullptr || name[0] == '\0') return nullptr; clang::QualType record_qual_type(GetCanonicalQualType(type)); clang::CXXRecordDecl *cxx_record_decl = record_qual_type->getAsCXXRecordDecl(); if (cxx_record_decl == nullptr) return nullptr; clang::QualType method_qual_type(ClangUtil::GetQualType(method_clang_type)); clang::CXXMethodDecl *cxx_method_decl = nullptr; clang::DeclarationName decl_name(&getASTContext()->Idents.get(name)); const clang::FunctionType *function_type = llvm::dyn_cast(method_qual_type.getTypePtr()); if (function_type == nullptr) return nullptr; const clang::FunctionProtoType *method_function_prototype( llvm::dyn_cast(function_type)); if (!method_function_prototype) return nullptr; unsigned int num_params = method_function_prototype->getNumParams(); clang::CXXDestructorDecl *cxx_dtor_decl(nullptr); clang::CXXConstructorDecl *cxx_ctor_decl(nullptr); if (is_artificial) return nullptr; // skip everything artificial if (name[0] == '~') { cxx_dtor_decl = clang::CXXDestructorDecl::Create( *getASTContext(), cxx_record_decl, clang::SourceLocation(), clang::DeclarationNameInfo( getASTContext()->DeclarationNames.getCXXDestructorName( getASTContext()->getCanonicalType(record_qual_type)), clang::SourceLocation()), method_qual_type, nullptr, is_inline, is_artificial); cxx_method_decl = cxx_dtor_decl; } else if (decl_name == cxx_record_decl->getDeclName()) { cxx_ctor_decl = clang::CXXConstructorDecl::Create( *getASTContext(), cxx_record_decl, clang::SourceLocation(), clang::DeclarationNameInfo( getASTContext()->DeclarationNames.getCXXConstructorName( getASTContext()->getCanonicalType(record_qual_type)), clang::SourceLocation()), method_qual_type, nullptr, // TypeSourceInfo * is_explicit, is_inline, is_artificial, false /*is_constexpr*/); cxx_method_decl = cxx_ctor_decl; } else { clang::StorageClass SC = is_static ? clang::SC_Static : clang::SC_None; clang::OverloadedOperatorKind op_kind = clang::NUM_OVERLOADED_OPERATORS; if (IsOperator(name, op_kind)) { if (op_kind != clang::NUM_OVERLOADED_OPERATORS) { // Check the number of operator parameters. Sometimes we have // seen bad DWARF that doesn't correctly describe operators and // if we try to create a method and add it to the class, clang // will assert and crash, so we need to make sure things are // acceptable. const bool is_method = true; if (!ClangASTContext::CheckOverloadedOperatorKindParameterCount( is_method, op_kind, num_params)) return nullptr; cxx_method_decl = clang::CXXMethodDecl::Create( *getASTContext(), cxx_record_decl, clang::SourceLocation(), clang::DeclarationNameInfo( getASTContext()->DeclarationNames.getCXXOperatorName(op_kind), clang::SourceLocation()), method_qual_type, nullptr, // TypeSourceInfo * SC, is_inline, false /*is_constexpr*/, clang::SourceLocation()); } else if (num_params == 0) { // Conversion operators don't take params... cxx_method_decl = clang::CXXConversionDecl::Create( *getASTContext(), cxx_record_decl, clang::SourceLocation(), clang::DeclarationNameInfo( getASTContext()->DeclarationNames.getCXXConversionFunctionName( getASTContext()->getCanonicalType( function_type->getReturnType())), clang::SourceLocation()), method_qual_type, nullptr, // TypeSourceInfo * is_inline, is_explicit, false /*is_constexpr*/, clang::SourceLocation()); } } if (cxx_method_decl == nullptr) { cxx_method_decl = clang::CXXMethodDecl::Create( *getASTContext(), cxx_record_decl, clang::SourceLocation(), clang::DeclarationNameInfo(decl_name, clang::SourceLocation()), method_qual_type, nullptr, // TypeSourceInfo * SC, is_inline, false /*is_constexpr*/, clang::SourceLocation()); } } clang::AccessSpecifier access_specifier = ClangASTContext::ConvertAccessTypeToAccessSpecifier(access); cxx_method_decl->setAccess(access_specifier); cxx_method_decl->setVirtualAsWritten(is_virtual); if (is_attr_used) cxx_method_decl->addAttr(clang::UsedAttr::CreateImplicit(*getASTContext())); // Populate the method decl with parameter decls llvm::SmallVector params; for (unsigned param_index = 0; param_index < num_params; ++param_index) { params.push_back(clang::ParmVarDecl::Create( *getASTContext(), cxx_method_decl, clang::SourceLocation(), clang::SourceLocation(), nullptr, // anonymous method_function_prototype->getParamType(param_index), nullptr, clang::SC_None, nullptr)); } cxx_method_decl->setParams(llvm::ArrayRef(params)); cxx_record_decl->addDecl(cxx_method_decl); // Sometimes the debug info will mention a constructor (default/copy/move), // destructor, or assignment operator (copy/move) but there won't be any // version of this in the code. So we check if the function was artificially // generated and if it is trivial and this lets the compiler/backend know // that it can inline the IR for these when it needs to and we can avoid a // "missing function" error when running expressions. if (is_artificial) { if (cxx_ctor_decl && ((cxx_ctor_decl->isDefaultConstructor() && cxx_record_decl->hasTrivialDefaultConstructor()) || (cxx_ctor_decl->isCopyConstructor() && cxx_record_decl->hasTrivialCopyConstructor()) || (cxx_ctor_decl->isMoveConstructor() && cxx_record_decl->hasTrivialMoveConstructor()))) { cxx_ctor_decl->setDefaulted(); cxx_ctor_decl->setTrivial(true); } else if (cxx_dtor_decl) { if (cxx_record_decl->hasTrivialDestructor()) { cxx_dtor_decl->setDefaulted(); cxx_dtor_decl->setTrivial(true); } } else if ((cxx_method_decl->isCopyAssignmentOperator() && cxx_record_decl->hasTrivialCopyAssignment()) || (cxx_method_decl->isMoveAssignmentOperator() && cxx_record_decl->hasTrivialMoveAssignment())) { cxx_method_decl->setDefaulted(); cxx_method_decl->setTrivial(true); } } #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(cxx_method_decl); #endif // printf ("decl->isPolymorphic() = %i\n", // cxx_record_decl->isPolymorphic()); // printf ("decl->isAggregate() = %i\n", // cxx_record_decl->isAggregate()); // printf ("decl->isPOD() = %i\n", // cxx_record_decl->isPOD()); // printf ("decl->isEmpty() = %i\n", // cxx_record_decl->isEmpty()); // printf ("decl->isAbstract() = %i\n", // cxx_record_decl->isAbstract()); // printf ("decl->hasTrivialConstructor() = %i\n", // cxx_record_decl->hasTrivialConstructor()); // printf ("decl->hasTrivialCopyConstructor() = %i\n", // cxx_record_decl->hasTrivialCopyConstructor()); // printf ("decl->hasTrivialCopyAssignment() = %i\n", // cxx_record_decl->hasTrivialCopyAssignment()); // printf ("decl->hasTrivialDestructor() = %i\n", // cxx_record_decl->hasTrivialDestructor()); return cxx_method_decl; } #pragma mark C++ Base Classes clang::CXXBaseSpecifier * ClangASTContext::CreateBaseClassSpecifier(lldb::opaque_compiler_type_t type, AccessType access, bool is_virtual, bool base_of_class) { if (type) return new clang::CXXBaseSpecifier( clang::SourceRange(), is_virtual, base_of_class, ClangASTContext::ConvertAccessTypeToAccessSpecifier(access), getASTContext()->getTrivialTypeSourceInfo(GetQualType(type)), clang::SourceLocation()); return nullptr; } void ClangASTContext::DeleteBaseClassSpecifiers( clang::CXXBaseSpecifier **base_classes, unsigned num_base_classes) { for (unsigned i = 0; i < num_base_classes; ++i) { delete base_classes[i]; base_classes[i] = nullptr; } } bool ClangASTContext::SetBaseClassesForClassType( lldb::opaque_compiler_type_t type, clang::CXXBaseSpecifier const *const *base_classes, unsigned num_base_classes) { if (type) { clang::CXXRecordDecl *cxx_record_decl = GetAsCXXRecordDecl(type); if (cxx_record_decl) { cxx_record_decl->setBases(base_classes, num_base_classes); return true; } } return false; } bool ClangASTContext::SetObjCSuperClass( const CompilerType &type, const CompilerType &superclass_clang_type) { ClangASTContext *ast = llvm::dyn_cast_or_null(type.GetTypeSystem()); if (!ast) return false; clang::ASTContext *clang_ast = ast->getASTContext(); if (type && superclass_clang_type.IsValid() && superclass_clang_type.GetTypeSystem() == type.GetTypeSystem()) { clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type); clang::ObjCInterfaceDecl *super_interface_decl = GetAsObjCInterfaceDecl(superclass_clang_type); if (class_interface_decl && super_interface_decl) { class_interface_decl->setSuperClass(clang_ast->getTrivialTypeSourceInfo( clang_ast->getObjCInterfaceType(super_interface_decl))); return true; } } return false; } bool ClangASTContext::AddObjCClassProperty( const CompilerType &type, const char *property_name, const CompilerType &property_clang_type, clang::ObjCIvarDecl *ivar_decl, const char *property_setter_name, const char *property_getter_name, uint32_t property_attributes, ClangASTMetadata *metadata) { if (!type || !property_clang_type.IsValid() || property_name == nullptr || property_name[0] == '\0') return false; ClangASTContext *ast = llvm::dyn_cast(type.GetTypeSystem()); if (!ast) return false; clang::ASTContext *clang_ast = ast->getASTContext(); clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type); if (class_interface_decl) { CompilerType property_clang_type_to_access; if (property_clang_type.IsValid()) property_clang_type_to_access = property_clang_type; else if (ivar_decl) property_clang_type_to_access = CompilerType(clang_ast, ivar_decl->getType()); if (class_interface_decl && property_clang_type_to_access.IsValid()) { clang::TypeSourceInfo *prop_type_source; if (ivar_decl) prop_type_source = clang_ast->getTrivialTypeSourceInfo(ivar_decl->getType()); else prop_type_source = clang_ast->getTrivialTypeSourceInfo( ClangUtil::GetQualType(property_clang_type)); clang::ObjCPropertyDecl *property_decl = clang::ObjCPropertyDecl::Create( *clang_ast, class_interface_decl, clang::SourceLocation(), // Source Location &clang_ast->Idents.get(property_name), clang::SourceLocation(), // Source Location for AT clang::SourceLocation(), // Source location for ( ivar_decl ? ivar_decl->getType() : ClangUtil::GetQualType(property_clang_type), prop_type_source); if (property_decl) { if (metadata) ClangASTContext::SetMetadata(clang_ast, property_decl, *metadata); class_interface_decl->addDecl(property_decl); clang::Selector setter_sel, getter_sel; if (property_setter_name != nullptr) { std::string property_setter_no_colon( property_setter_name, strlen(property_setter_name) - 1); clang::IdentifierInfo *setter_ident = &clang_ast->Idents.get(property_setter_no_colon); setter_sel = clang_ast->Selectors.getSelector(1, &setter_ident); } else if (!(property_attributes & DW_APPLE_PROPERTY_readonly)) { std::string setter_sel_string("set"); setter_sel_string.push_back(::toupper(property_name[0])); setter_sel_string.append(&property_name[1]); clang::IdentifierInfo *setter_ident = &clang_ast->Idents.get(setter_sel_string); setter_sel = clang_ast->Selectors.getSelector(1, &setter_ident); } property_decl->setSetterName(setter_sel); property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_setter); if (property_getter_name != nullptr) { clang::IdentifierInfo *getter_ident = &clang_ast->Idents.get(property_getter_name); getter_sel = clang_ast->Selectors.getSelector(0, &getter_ident); } else { clang::IdentifierInfo *getter_ident = &clang_ast->Idents.get(property_name); getter_sel = clang_ast->Selectors.getSelector(0, &getter_ident); } property_decl->setGetterName(getter_sel); property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_getter); if (ivar_decl) property_decl->setPropertyIvarDecl(ivar_decl); if (property_attributes & DW_APPLE_PROPERTY_readonly) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_readonly); if (property_attributes & DW_APPLE_PROPERTY_readwrite) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_readwrite); if (property_attributes & DW_APPLE_PROPERTY_assign) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_assign); if (property_attributes & DW_APPLE_PROPERTY_retain) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_retain); if (property_attributes & DW_APPLE_PROPERTY_copy) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_copy); if (property_attributes & DW_APPLE_PROPERTY_nonatomic) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_nonatomic); if (property_attributes & clang::ObjCPropertyDecl::OBJC_PR_nullability) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_nullability); if (property_attributes & clang::ObjCPropertyDecl::OBJC_PR_null_resettable) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_null_resettable); if (property_attributes & clang::ObjCPropertyDecl::OBJC_PR_class) property_decl->setPropertyAttributes( clang::ObjCPropertyDecl::OBJC_PR_class); const bool isInstance = (property_attributes & clang::ObjCPropertyDecl::OBJC_PR_class) == 0; if (!getter_sel.isNull() && !(isInstance ? class_interface_decl->lookupInstanceMethod(getter_sel) : class_interface_decl->lookupClassMethod(getter_sel))) { const bool isVariadic = false; const bool isSynthesized = false; const bool isImplicitlyDeclared = true; const bool isDefined = false; const clang::ObjCMethodDecl::ImplementationControl impControl = clang::ObjCMethodDecl::None; const bool HasRelatedResultType = false; clang::ObjCMethodDecl *getter = clang::ObjCMethodDecl::Create( *clang_ast, clang::SourceLocation(), clang::SourceLocation(), getter_sel, ClangUtil::GetQualType(property_clang_type_to_access), nullptr, class_interface_decl, isInstance, isVariadic, isSynthesized, isImplicitlyDeclared, isDefined, impControl, HasRelatedResultType); if (getter && metadata) ClangASTContext::SetMetadata(clang_ast, getter, *metadata); if (getter) { getter->setMethodParams(*clang_ast, llvm::ArrayRef(), llvm::ArrayRef()); class_interface_decl->addDecl(getter); } } if (!setter_sel.isNull() && !(isInstance ? class_interface_decl->lookupInstanceMethod(setter_sel) : class_interface_decl->lookupClassMethod(setter_sel))) { clang::QualType result_type = clang_ast->VoidTy; const bool isVariadic = false; const bool isSynthesized = false; const bool isImplicitlyDeclared = true; const bool isDefined = false; const clang::ObjCMethodDecl::ImplementationControl impControl = clang::ObjCMethodDecl::None; const bool HasRelatedResultType = false; clang::ObjCMethodDecl *setter = clang::ObjCMethodDecl::Create( *clang_ast, clang::SourceLocation(), clang::SourceLocation(), setter_sel, result_type, nullptr, class_interface_decl, isInstance, isVariadic, isSynthesized, isImplicitlyDeclared, isDefined, impControl, HasRelatedResultType); if (setter && metadata) ClangASTContext::SetMetadata(clang_ast, setter, *metadata); llvm::SmallVector params; params.push_back(clang::ParmVarDecl::Create( *clang_ast, setter, clang::SourceLocation(), clang::SourceLocation(), nullptr, // anonymous ClangUtil::GetQualType(property_clang_type_to_access), nullptr, clang::SC_Auto, nullptr)); if (setter) { setter->setMethodParams( *clang_ast, llvm::ArrayRef(params), llvm::ArrayRef()); class_interface_decl->addDecl(setter); } } return true; } } } return false; } bool ClangASTContext::IsObjCClassTypeAndHasIVars(const CompilerType &type, bool check_superclass) { clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type); if (class_interface_decl) return ObjCDeclHasIVars(class_interface_decl, check_superclass); return false; } clang::ObjCMethodDecl *ClangASTContext::AddMethodToObjCObjectType( const CompilerType &type, const char *name, // the full symbol name as seen in the symbol table // (lldb::opaque_compiler_type_t type, "-[NString // stringWithCString:]") const CompilerType &method_clang_type, lldb::AccessType access, bool is_artificial, bool is_variadic) { if (!type || !method_clang_type.IsValid()) return nullptr; clang::ObjCInterfaceDecl *class_interface_decl = GetAsObjCInterfaceDecl(type); if (class_interface_decl == nullptr) return nullptr; ClangASTContext *lldb_ast = llvm::dyn_cast(type.GetTypeSystem()); if (lldb_ast == nullptr) return nullptr; clang::ASTContext *ast = lldb_ast->getASTContext(); const char *selector_start = ::strchr(name, ' '); if (selector_start == nullptr) return nullptr; selector_start++; llvm::SmallVector selector_idents; size_t len = 0; const char *start; // printf ("name = '%s'\n", name); unsigned num_selectors_with_args = 0; for (start = selector_start; start && *start != '\0' && *start != ']'; start += len) { len = ::strcspn(start, ":]"); bool has_arg = (start[len] == ':'); if (has_arg) ++num_selectors_with_args; selector_idents.push_back(&ast->Idents.get(llvm::StringRef(start, len))); if (has_arg) len += 1; } if (selector_idents.size() == 0) return nullptr; clang::Selector method_selector = ast->Selectors.getSelector( num_selectors_with_args ? selector_idents.size() : 0, selector_idents.data()); clang::QualType method_qual_type(ClangUtil::GetQualType(method_clang_type)); // Populate the method decl with parameter decls const clang::Type *method_type(method_qual_type.getTypePtr()); if (method_type == nullptr) return nullptr; const clang::FunctionProtoType *method_function_prototype( llvm::dyn_cast(method_type)); if (!method_function_prototype) return nullptr; bool is_synthesized = false; bool is_defined = false; clang::ObjCMethodDecl::ImplementationControl imp_control = clang::ObjCMethodDecl::None; const unsigned num_args = method_function_prototype->getNumParams(); if (num_args != num_selectors_with_args) return nullptr; // some debug information is corrupt. We are not going to // deal with it. clang::ObjCMethodDecl *objc_method_decl = clang::ObjCMethodDecl::Create( *ast, clang::SourceLocation(), // beginLoc, clang::SourceLocation(), // endLoc, method_selector, method_function_prototype->getReturnType(), nullptr, // TypeSourceInfo *ResultTInfo, ClangASTContext::GetASTContext(ast)->GetDeclContextForType( ClangUtil::GetQualType(type)), name[0] == '-', is_variadic, is_synthesized, true, // is_implicitly_declared; we force this to true because we don't // have source locations is_defined, imp_control, false /*has_related_result_type*/); if (objc_method_decl == nullptr) return nullptr; if (num_args > 0) { llvm::SmallVector params; for (unsigned param_index = 0; param_index < num_args; ++param_index) { params.push_back(clang::ParmVarDecl::Create( *ast, objc_method_decl, clang::SourceLocation(), clang::SourceLocation(), nullptr, // anonymous method_function_prototype->getParamType(param_index), nullptr, clang::SC_Auto, nullptr)); } objc_method_decl->setMethodParams( *ast, llvm::ArrayRef(params), llvm::ArrayRef()); } class_interface_decl->addDecl(objc_method_decl); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(objc_method_decl); #endif return objc_method_decl; } bool ClangASTContext::GetHasExternalStorage(const CompilerType &type) { if (ClangUtil::IsClangType(type)) return false; clang::QualType qual_type(ClangUtil::GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: { clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) return cxx_record_decl->hasExternalLexicalStorage() || cxx_record_decl->hasExternalVisibleStorage(); } break; case clang::Type::Enum: { clang::EnumDecl *enum_decl = llvm::cast(qual_type)->getDecl(); if (enum_decl) return enum_decl->hasExternalLexicalStorage() || enum_decl->hasExternalVisibleStorage(); } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) return class_interface_decl->hasExternalLexicalStorage() || class_interface_decl->hasExternalVisibleStorage(); } } break; case clang::Type::Typedef: return GetHasExternalStorage(CompilerType( type.GetTypeSystem(), llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr())); case clang::Type::Auto: return GetHasExternalStorage(CompilerType( type.GetTypeSystem(), llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr())); case clang::Type::Elaborated: return GetHasExternalStorage(CompilerType( type.GetTypeSystem(), llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr())); case clang::Type::Paren: return GetHasExternalStorage(CompilerType( type.GetTypeSystem(), llvm::cast(qual_type)->desugar().getAsOpaquePtr())); default: break; } return false; } bool ClangASTContext::SetHasExternalStorage(lldb::opaque_compiler_type_t type, bool has_extern) { if (!type) return false; clang::QualType qual_type(GetCanonicalQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: { clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) { cxx_record_decl->setHasExternalLexicalStorage(has_extern); cxx_record_decl->setHasExternalVisibleStorage(has_extern); return true; } } break; case clang::Type::Enum: { clang::EnumDecl *enum_decl = llvm::cast(qual_type)->getDecl(); if (enum_decl) { enum_decl->setHasExternalLexicalStorage(has_extern); enum_decl->setHasExternalVisibleStorage(has_extern); return true; } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { class_interface_decl->setHasExternalLexicalStorage(has_extern); class_interface_decl->setHasExternalVisibleStorage(has_extern); return true; } } } break; case clang::Type::Typedef: return SetHasExternalStorage(llvm::cast(qual_type) ->getDecl() ->getUnderlyingType() .getAsOpaquePtr(), has_extern); case clang::Type::Auto: return SetHasExternalStorage(llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr(), has_extern); case clang::Type::Elaborated: return SetHasExternalStorage(llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr(), has_extern); case clang::Type::Paren: return SetHasExternalStorage( llvm::cast(qual_type)->desugar().getAsOpaquePtr(), has_extern); default: break; } return false; } #pragma mark TagDecl bool ClangASTContext::StartTagDeclarationDefinition(const CompilerType &type) { clang::QualType qual_type(ClangUtil::GetQualType(type)); if (!qual_type.isNull()) { const clang::TagType *tag_type = qual_type->getAs(); if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) { tag_decl->startDefinition(); return true; } } const clang::ObjCObjectType *object_type = qual_type->getAs(); if (object_type) { clang::ObjCInterfaceDecl *interface_decl = object_type->getInterface(); if (interface_decl) { interface_decl->startDefinition(); return true; } } } return false; } bool ClangASTContext::CompleteTagDeclarationDefinition( const CompilerType &type) { clang::QualType qual_type(ClangUtil::GetQualType(type)); if (!qual_type.isNull()) { // Make sure we use the same methodology as // ClangASTContext::StartTagDeclarationDefinition() // as to how we start/end the definition. Previously we were calling const clang::TagType *tag_type = qual_type->getAs(); if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) { clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast_or_null(tag_decl); if (cxx_record_decl) { if (!cxx_record_decl->isCompleteDefinition()) cxx_record_decl->completeDefinition(); cxx_record_decl->setHasLoadedFieldsFromExternalStorage(true); cxx_record_decl->setHasExternalLexicalStorage(false); cxx_record_decl->setHasExternalVisibleStorage(false); return true; } } } const clang::EnumType *enutype = qual_type->getAs(); if (enutype) { clang::EnumDecl *enum_decl = enutype->getDecl(); if (enum_decl) { if (!enum_decl->isCompleteDefinition()) { ClangASTContext *lldb_ast = llvm::dyn_cast(type.GetTypeSystem()); if (lldb_ast == nullptr) return false; clang::ASTContext *ast = lldb_ast->getASTContext(); /// TODO This really needs to be fixed. QualType integer_type(enum_decl->getIntegerType()); if (!integer_type.isNull()) { unsigned NumPositiveBits = 1; unsigned NumNegativeBits = 0; clang::QualType promotion_qual_type; // If the enum integer type is less than an integer in bit width, // then we must promote it to an integer size. if (ast->getTypeSize(enum_decl->getIntegerType()) < ast->getTypeSize(ast->IntTy)) { if (enum_decl->getIntegerType()->isSignedIntegerType()) promotion_qual_type = ast->IntTy; else promotion_qual_type = ast->UnsignedIntTy; } else promotion_qual_type = enum_decl->getIntegerType(); enum_decl->completeDefinition(enum_decl->getIntegerType(), promotion_qual_type, NumPositiveBits, NumNegativeBits); } } return true; } } } return false; } bool ClangASTContext::AddEnumerationValueToEnumerationType( lldb::opaque_compiler_type_t type, const CompilerType &enumerator_clang_type, const Declaration &decl, const char *name, int64_t enum_value, uint32_t enum_value_bit_size) { if (type && enumerator_clang_type.IsValid() && name && name[0]) { clang::QualType enum_qual_type(GetCanonicalQualType(type)); bool is_signed = false; enumerator_clang_type.IsIntegerType(is_signed); const clang::Type *clang_type = enum_qual_type.getTypePtr(); if (clang_type) { const clang::EnumType *enutype = llvm::dyn_cast(clang_type); if (enutype) { llvm::APSInt enum_llvm_apsint(enum_value_bit_size, is_signed); enum_llvm_apsint = enum_value; clang::EnumConstantDecl *enumerator_decl = clang::EnumConstantDecl::Create( *getASTContext(), enutype->getDecl(), clang::SourceLocation(), name ? &getASTContext()->Idents.get(name) : nullptr, // Identifier ClangUtil::GetQualType(enumerator_clang_type), nullptr, enum_llvm_apsint); if (enumerator_decl) { enutype->getDecl()->addDecl(enumerator_decl); #ifdef LLDB_CONFIGURATION_DEBUG VerifyDecl(enumerator_decl); #endif return true; } } } } return false; } CompilerType ClangASTContext::GetEnumerationIntegerType(lldb::opaque_compiler_type_t type) { clang::QualType enum_qual_type(GetCanonicalQualType(type)); const clang::Type *clang_type = enum_qual_type.getTypePtr(); if (clang_type) { const clang::EnumType *enutype = llvm::dyn_cast(clang_type); if (enutype) { clang::EnumDecl *enum_decl = enutype->getDecl(); if (enum_decl) return CompilerType(getASTContext(), enum_decl->getIntegerType()); } } return CompilerType(); } CompilerType ClangASTContext::CreateMemberPointerType(const CompilerType &type, const CompilerType &pointee_type) { if (type && pointee_type.IsValid() && type.GetTypeSystem() == pointee_type.GetTypeSystem()) { ClangASTContext *ast = llvm::dyn_cast(type.GetTypeSystem()); if (!ast) return CompilerType(); return CompilerType(ast->getASTContext(), ast->getASTContext()->getMemberPointerType( ClangUtil::GetQualType(pointee_type), ClangUtil::GetQualType(type).getTypePtr())); } return CompilerType(); } size_t ClangASTContext::ConvertStringToFloatValue(lldb::opaque_compiler_type_t type, const char *s, uint8_t *dst, size_t dst_size) { if (type) { clang::QualType qual_type(GetCanonicalQualType(type)); uint32_t count = 0; bool is_complex = false; if (IsFloatingPointType(type, count, is_complex)) { // TODO: handle complex and vector types if (count != 1) return false; llvm::StringRef s_sref(s); llvm::APFloat ap_float(getASTContext()->getFloatTypeSemantics(qual_type), s_sref); const uint64_t bit_size = getASTContext()->getTypeSize(qual_type); const uint64_t byte_size = bit_size / 8; if (dst_size >= byte_size) { Scalar scalar = ap_float.bitcastToAPInt().zextOrTrunc( llvm::NextPowerOf2(byte_size) * 8); lldb_private::Error get_data_error; if (scalar.GetAsMemoryData(dst, byte_size, lldb_private::endian::InlHostByteOrder(), get_data_error)) return byte_size; } } } return 0; } //---------------------------------------------------------------------- // Dumping types //---------------------------------------------------------------------- #define DEPTH_INCREMENT 2 void ClangASTContext::DumpValue( lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, Stream *s, lldb::Format format, const lldb_private::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 (!type) return; clang::QualType qual_type(GetQualType(type)); switch (qual_type->getTypeClass()) { case clang::Type::Record: if (GetCompleteType(type)) { const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); assert(record_decl); uint32_t field_bit_offset = 0; uint32_t field_byte_offset = 0; const clang::ASTRecordLayout &record_layout = getASTContext()->getASTRecordLayout(record_decl); uint32_t child_idx = 0; const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) { // We might have base classes to print out first clang::CXXRecordDecl::base_class_const_iterator base_class, base_class_end; for (base_class = cxx_record_decl->bases_begin(), base_class_end = cxx_record_decl->bases_end(); base_class != base_class_end; ++base_class) { const clang::CXXRecordDecl *base_class_decl = llvm::cast( base_class->getType()->getAs()->getDecl()); // Skip empty base classes if (verbose == false && ClangASTContext::RecordHasFields(base_class_decl) == false) continue; if (base_class->isVirtual()) field_bit_offset = record_layout.getVBaseClassOffset(base_class_decl) .getQuantity() * 8; else field_bit_offset = record_layout.getBaseClassOffset(base_class_decl) .getQuantity() * 8; field_byte_offset = field_bit_offset / 8; assert(field_bit_offset % 8 == 0); if (child_idx == 0) s->PutChar('{'); else s->PutChar(','); clang::QualType base_class_qual_type = base_class->getType(); std::string base_class_type_name(base_class_qual_type.getAsString()); // Indent and print the base class type name s->Format("\n{0}{1}", llvm::fmt_repeat(" ", depth + DEPTH_INCREMENT), base_class_type_name); clang::TypeInfo base_class_type_info = getASTContext()->getTypeInfo(base_class_qual_type); // Dump the value of the member CompilerType base_clang_type(getASTContext(), base_class_qual_type); base_clang_type.DumpValue( exe_ctx, s, // Stream to dump to base_clang_type .GetFormat(), // The format with which to display the member data, // Data buffer containing all bytes for this type data_byte_offset + field_byte_offset, // Offset into "data" where // to grab value from base_class_type_info.Width / 8, // 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 ++child_idx; } } uint32_t field_idx = 0; clang::RecordDecl::field_iterator field, field_end; for (field = record_decl->field_begin(), field_end = record_decl->field_end(); field != field_end; ++field, ++field_idx, ++child_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 (child_idx == 0) s->PutChar('{'); else s->PutChar(','); // Indent s->Printf("\n%*s", depth + DEPTH_INCREMENT, ""); clang::QualType field_type = field->getType(); // Print the member type if requested // Figure out the type byte size (field_type_info.first) and // alignment (field_type_info.second) from the AST context. clang::TypeInfo field_type_info = getASTContext()->getTypeInfo(field_type); assert(field_idx < record_layout.getFieldCount()); // Figure out the field offset within the current struct/union/class // type field_bit_offset = record_layout.getFieldOffset(field_idx); field_byte_offset = field_bit_offset / 8; uint32_t field_bitfield_bit_size = 0; uint32_t field_bitfield_bit_offset = 0; if (ClangASTContext::FieldIsBitfield(getASTContext(), *field, field_bitfield_bit_size)) field_bitfield_bit_offset = field_bit_offset % 8; if (show_types) { std::string field_type_name(field_type.getAsString()); if (field_bitfield_bit_size > 0) s->Printf("(%s:%u) ", field_type_name.c_str(), field_bitfield_bit_size); else s->Printf("(%s) ", field_type_name.c_str()); } // Print the member name and equal sign s->Printf("%s = ", field->getNameAsString().c_str()); // Dump the value of the member CompilerType field_clang_type(getASTContext(), field_type); field_clang_type.DumpValue( exe_ctx, s, // Stream to dump to field_clang_type .GetFormat(), // The format with which to display the member data, // Data buffer containing all bytes for this type data_byte_offset + field_byte_offset, // Offset into "data" where to // grab value from field_type_info.Width / 8, // Size of this type in bytes field_bitfield_bit_size, // Bitfield bit size field_bitfield_bit_offset, // 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 (child_idx > 0) s->Printf("\n%*s}", depth, ""); } return; case clang::Type::Enum: if (GetCompleteType(type)) { const clang::EnumType *enutype = llvm::cast(qual_type.getTypePtr()); const clang::EnumDecl *enum_decl = enutype->getDecl(); assert(enum_decl); clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos; lldb::offset_t offset = data_byte_offset; const int64_t enum_value = data.GetMaxU64Bitfield( &offset, data_byte_size, bitfield_bit_size, bitfield_bit_offset); for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) { if (enum_pos->getInitVal() == enum_value) { s->Printf("%s", enum_pos->getNameAsString().c_str()); return; } } // If we have gotten here we didn't get find the enumerator in the // enum decl, so just print the integer. s->Printf("%" PRIi64, enum_value); } return; case clang::Type::ConstantArray: { const clang::ConstantArrayType *array = llvm::cast(qual_type.getTypePtr()); bool is_array_of_characters = false; clang::QualType element_qual_type = array->getElementType(); const clang::Type *canonical_type = element_qual_type->getCanonicalTypeInternal().getTypePtr(); if (canonical_type) is_array_of_characters = canonical_type->isCharType(); const uint64_t element_count = array->getSize().getLimitedValue(); clang::TypeInfo field_type_info = getASTContext()->getTypeInfo(element_qual_type); uint32_t element_idx = 0; uint32_t element_offset = 0; uint64_t element_byte_size = field_type_info.Width / 8; uint32_t element_stride = element_byte_size; if (is_array_of_characters) { s->PutChar('"'); data.Dump(s, data_byte_offset, lldb::eFormatChar, element_byte_size, element_count, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); s->PutChar('"'); return; } else { CompilerType element_clang_type(getASTContext(), element_qual_type); lldb::Format element_format = element_clang_type.GetFormat(); for (element_idx = 0; element_idx < element_count; ++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[%u] ", depth + DEPTH_INCREMENT, "", element_idx); // Figure out the field offset within the current struct/union/class // type element_offset = element_idx * element_stride; // 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, ""); } } return; case clang::Type::Typedef: { clang::QualType typedef_qual_type = llvm::cast(qual_type) ->getDecl() ->getUnderlyingType(); CompilerType typedef_clang_type(getASTContext(), typedef_qual_type); lldb::Format typedef_format = typedef_clang_type.GetFormat(); clang::TypeInfo typedef_type_info = getASTContext()->getTypeInfo(typedef_qual_type); uint64_t typedef_byte_size = typedef_type_info.Width / 8; return typedef_clang_type.DumpValue( exe_ctx, s, // Stream to dump to typedef_format, // The format with which to display the element data, // Data buffer containing all bytes for this type data_byte_offset, // Offset into "data" where to grab value from typedef_byte_size, // Size of this type in bytes bitfield_bit_size, // Bitfield bit size bitfield_bit_offset, // 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); // Scope depth for any types that have children } break; case clang::Type::Auto: { clang::QualType elaborated_qual_type = llvm::cast(qual_type)->getDeducedType(); CompilerType elaborated_clang_type(getASTContext(), elaborated_qual_type); lldb::Format elaborated_format = elaborated_clang_type.GetFormat(); clang::TypeInfo elaborated_type_info = getASTContext()->getTypeInfo(elaborated_qual_type); uint64_t elaborated_byte_size = elaborated_type_info.Width / 8; return elaborated_clang_type.DumpValue( exe_ctx, s, // Stream to dump to elaborated_format, // The format with which to display the element data, // Data buffer containing all bytes for this type data_byte_offset, // Offset into "data" where to grab value from elaborated_byte_size, // Size of this type in bytes bitfield_bit_size, // Bitfield bit size bitfield_bit_offset, // 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); // Scope depth for any types that have children } break; case clang::Type::Elaborated: { clang::QualType elaborated_qual_type = llvm::cast(qual_type)->getNamedType(); CompilerType elaborated_clang_type(getASTContext(), elaborated_qual_type); lldb::Format elaborated_format = elaborated_clang_type.GetFormat(); clang::TypeInfo elaborated_type_info = getASTContext()->getTypeInfo(elaborated_qual_type); uint64_t elaborated_byte_size = elaborated_type_info.Width / 8; return elaborated_clang_type.DumpValue( exe_ctx, s, // Stream to dump to elaborated_format, // The format with which to display the element data, // Data buffer containing all bytes for this type data_byte_offset, // Offset into "data" where to grab value from elaborated_byte_size, // Size of this type in bytes bitfield_bit_size, // Bitfield bit size bitfield_bit_offset, // 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); // Scope depth for any types that have children } break; case clang::Type::Paren: { clang::QualType desugar_qual_type = llvm::cast(qual_type)->desugar(); CompilerType desugar_clang_type(getASTContext(), desugar_qual_type); lldb::Format desugar_format = desugar_clang_type.GetFormat(); clang::TypeInfo desugar_type_info = getASTContext()->getTypeInfo(desugar_qual_type); uint64_t desugar_byte_size = desugar_type_info.Width / 8; return desugar_clang_type.DumpValue( exe_ctx, s, // Stream to dump to desugar_format, // The format with which to display the element data, // Data buffer containing all bytes for this type data_byte_offset, // Offset into "data" where to grab value from desugar_byte_size, // Size of this type in bytes bitfield_bit_size, // Bitfield bit size bitfield_bit_offset, // 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); // Scope depth for any types that have children } break; default: // We are down to a scalar type that we just need to display. data.Dump(s, data_byte_offset, format, data_byte_size, 1, UINT32_MAX, LLDB_INVALID_ADDRESS, bitfield_bit_size, bitfield_bit_offset); if (show_summary) DumpSummary(type, exe_ctx, s, data, data_byte_offset, data_byte_size); break; } } bool ClangASTContext::DumpTypeValue( lldb::opaque_compiler_type_t type, Stream *s, lldb::Format format, const lldb_private::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 { clang::QualType qual_type(GetQualType(type)); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Typedef: { clang::QualType typedef_qual_type = llvm::cast(qual_type) ->getDecl() ->getUnderlyingType(); CompilerType typedef_clang_type(getASTContext(), typedef_qual_type); if (format == eFormatDefault) format = typedef_clang_type.GetFormat(); clang::TypeInfo typedef_type_info = getASTContext()->getTypeInfo(typedef_qual_type); uint64_t typedef_byte_size = typedef_type_info.Width / 8; return typedef_clang_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); } break; case clang::Type::Enum: // If our format is enum or default, show the enumeration value as // its enumeration string value, else just display it as requested. if ((format == eFormatEnum || format == eFormatDefault) && GetCompleteType(type)) { const clang::EnumType *enutype = llvm::cast(qual_type.getTypePtr()); const clang::EnumDecl *enum_decl = enutype->getDecl(); assert(enum_decl); clang::EnumDecl::enumerator_iterator enum_pos, enum_end_pos; const bool is_signed = qual_type->isSignedIntegerOrEnumerationType(); lldb::offset_t offset = byte_offset; if (is_signed) { const int64_t enum_svalue = data.GetMaxS64Bitfield( &offset, byte_size, bitfield_bit_size, bitfield_bit_offset); for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) { if (enum_pos->getInitVal().getSExtValue() == enum_svalue) { s->PutCString(enum_pos->getNameAsString()); return true; } } // If we have gotten here we didn't get find the enumerator in the // enum decl, so just print the integer. s->Printf("%" PRIi64, enum_svalue); } else { const uint64_t enum_uvalue = data.GetMaxU64Bitfield( &offset, byte_size, bitfield_bit_size, bitfield_bit_offset); for (enum_pos = enum_decl->enumerator_begin(), enum_end_pos = enum_decl->enumerator_end(); enum_pos != enum_end_pos; ++enum_pos) { if (enum_pos->getInitVal().getZExtValue() == enum_uvalue) { s->PutCString(enum_pos->getNameAsString()); return true; } } // If we have gotten here we didn't get find the enumerator in the // enum decl, so just print the integer. s->Printf("%" PRIu64, enum_uvalue); } return true; } // format was not enum, just fall through and dump the value as // requested.... LLVM_FALLTHROUGH; default: // We are down to a scalar type that we just need to display. { 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 data.Dump(s, byte_offset, format, byte_size, item_count, UINT32_MAX, LLDB_INVALID_ADDRESS, bitfield_bit_size, bitfield_bit_offset, exe_scope); } break; } } return 0; } void ClangASTContext::DumpSummary(lldb::opaque_compiler_type_t type, ExecutionContext *exe_ctx, Stream *s, const lldb_private::DataExtractor &data, lldb::offset_t data_byte_offset, size_t data_byte_size) { uint32_t length = 0; if (IsCStringType(type, length)) { if (exe_ctx) { Process *process = exe_ctx->GetProcessPtr(); if (process) { lldb::offset_t offset = data_byte_offset; lldb::addr_t pointer_address = data.GetMaxU64(&offset, data_byte_size); std::vector buf; if (length > 0) buf.resize(length); else buf.resize(256); lldb_private::DataExtractor cstr_data(&buf.front(), buf.size(), process->GetByteOrder(), 4); buf.back() = '\0'; size_t bytes_read; size_t total_cstr_len = 0; Error error; while ((bytes_read = process->ReadMemory(pointer_address, &buf.front(), buf.size(), error)) > 0) { const size_t len = strlen((const char *)&buf.front()); if (len == 0) break; if (total_cstr_len == 0) s->PutCString(" \""); cstr_data.Dump(s, 0, lldb::eFormatChar, 1, len, UINT32_MAX, LLDB_INVALID_ADDRESS, 0, 0); total_cstr_len += len; if (len < buf.size()) break; pointer_address += total_cstr_len; } if (total_cstr_len > 0) s->PutChar('"'); } } } } void ClangASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type) { StreamFile s(stdout, false); DumpTypeDescription(type, &s); ClangASTMetadata *metadata = ClangASTContext::GetMetadata(getASTContext(), type); if (metadata) { metadata->Dump(&s); } } void ClangASTContext::DumpTypeDescription(lldb::opaque_compiler_type_t type, Stream *s) { if (type) { clang::QualType qual_type(GetQualType(type)); llvm::SmallVector buf; llvm::raw_svector_ostream llvm_ostrm(buf); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { GetCompleteType(type); const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type.getTypePtr()); assert(objc_class_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); if (class_interface_decl) { clang::PrintingPolicy policy = getASTContext()->getPrintingPolicy(); class_interface_decl->print(llvm_ostrm, policy, s->GetIndentLevel()); } } } break; case clang::Type::Typedef: { const clang::TypedefType *typedef_type = qual_type->getAs(); if (typedef_type) { const clang::TypedefNameDecl *typedef_decl = typedef_type->getDecl(); std::string clang_typedef_name( typedef_decl->getQualifiedNameAsString()); if (!clang_typedef_name.empty()) { s->PutCString("typedef "); s->PutCString(clang_typedef_name); } } } break; case clang::Type::Auto: CompilerType(getASTContext(), llvm::cast(qual_type)->getDeducedType()) .DumpTypeDescription(s); return; case clang::Type::Elaborated: CompilerType(getASTContext(), llvm::cast(qual_type)->getNamedType()) .DumpTypeDescription(s); return; case clang::Type::Paren: CompilerType(getASTContext(), llvm::cast(qual_type)->desugar()) .DumpTypeDescription(s); return; case clang::Type::Record: { GetCompleteType(type); const clang::RecordType *record_type = llvm::cast(qual_type.getTypePtr()); const clang::RecordDecl *record_decl = record_type->getDecl(); const clang::CXXRecordDecl *cxx_record_decl = llvm::dyn_cast(record_decl); if (cxx_record_decl) cxx_record_decl->print(llvm_ostrm, getASTContext()->getPrintingPolicy(), s->GetIndentLevel()); else record_decl->print(llvm_ostrm, getASTContext()->getPrintingPolicy(), s->GetIndentLevel()); } break; default: { const clang::TagType *tag_type = llvm::dyn_cast(qual_type.getTypePtr()); if (tag_type) { clang::TagDecl *tag_decl = tag_type->getDecl(); if (tag_decl) tag_decl->print(llvm_ostrm, 0); } else { std::string clang_type_name(qual_type.getAsString()); if (!clang_type_name.empty()) s->PutCString(clang_type_name); } } } if (buf.size() > 0) { s->Write(buf.data(), buf.size()); } } } void ClangASTContext::DumpTypeName(const CompilerType &type) { if (ClangUtil::IsClangType(type)) { clang::QualType qual_type( ClangUtil::GetCanonicalQualType(ClangUtil::RemoveFastQualifiers(type))); const clang::Type::TypeClass type_class = qual_type->getTypeClass(); switch (type_class) { case clang::Type::Record: { const clang::CXXRecordDecl *cxx_record_decl = qual_type->getAsCXXRecordDecl(); if (cxx_record_decl) printf("class %s", cxx_record_decl->getName().str().c_str()); } break; case clang::Type::Enum: { clang::EnumDecl *enum_decl = llvm::cast(qual_type)->getDecl(); if (enum_decl) { printf("enum %s", enum_decl->getName().str().c_str()); } } break; case clang::Type::ObjCObject: case clang::Type::ObjCInterface: { const clang::ObjCObjectType *objc_class_type = llvm::dyn_cast(qual_type); if (objc_class_type) { clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface(); // We currently can't complete objective C types through the newly added // ASTContext // because it only supports TagDecl objects right now... if (class_interface_decl) printf("@class %s", class_interface_decl->getName().str().c_str()); } } break; case clang::Type::Typedef: printf("typedef %s", llvm::cast(qual_type) ->getDecl() ->getName() .str() .c_str()); break; case clang::Type::Auto: printf("auto "); return DumpTypeName(CompilerType(type.GetTypeSystem(), llvm::cast(qual_type) ->getDeducedType() .getAsOpaquePtr())); case clang::Type::Elaborated: printf("elaborated "); return DumpTypeName(CompilerType( type.GetTypeSystem(), llvm::cast(qual_type) ->getNamedType() .getAsOpaquePtr())); case clang::Type::Paren: printf("paren "); return DumpTypeName(CompilerType( type.GetTypeSystem(), llvm::cast(qual_type)->desugar().getAsOpaquePtr())); default: printf("ClangASTContext::DumpTypeName() type_class = %u", type_class); break; } } } clang::ClassTemplateDecl *ClangASTContext::ParseClassTemplateDecl( clang::DeclContext *decl_ctx, lldb::AccessType access_type, const char *parent_name, int tag_decl_kind, const ClangASTContext::TemplateParameterInfos &template_param_infos) { if (template_param_infos.IsValid()) { std::string template_basename(parent_name); template_basename.erase(template_basename.find('<')); return CreateClassTemplateDecl(decl_ctx, access_type, template_basename.c_str(), tag_decl_kind, template_param_infos); } return NULL; } void ClangASTContext::CompleteTagDecl(void *baton, clang::TagDecl *decl) { ClangASTContext *ast = (ClangASTContext *)baton; SymbolFile *sym_file = ast->GetSymbolFile(); if (sym_file) { CompilerType clang_type = GetTypeForDecl(decl); if (clang_type) sym_file->CompleteType(clang_type); } } void ClangASTContext::CompleteObjCInterfaceDecl( void *baton, clang::ObjCInterfaceDecl *decl) { ClangASTContext *ast = (ClangASTContext *)baton; SymbolFile *sym_file = ast->GetSymbolFile(); if (sym_file) { CompilerType clang_type = GetTypeForDecl(decl); if (clang_type) sym_file->CompleteType(clang_type); } } DWARFASTParser *ClangASTContext::GetDWARFParser() { if (!m_dwarf_ast_parser_ap) m_dwarf_ast_parser_ap.reset(new DWARFASTParserClang(*this)); return m_dwarf_ast_parser_ap.get(); } PDBASTParser *ClangASTContext::GetPDBParser() { if (!m_pdb_ast_parser_ap) m_pdb_ast_parser_ap.reset(new PDBASTParser(*this)); return m_pdb_ast_parser_ap.get(); } bool ClangASTContext::LayoutRecordType( void *baton, const clang::RecordDecl *record_decl, uint64_t &bit_size, uint64_t &alignment, llvm::DenseMap &field_offsets, llvm::DenseMap &base_offsets, llvm::DenseMap &vbase_offsets) { ClangASTContext *ast = (ClangASTContext *)baton; DWARFASTParserClang *dwarf_ast_parser = (DWARFASTParserClang *)ast->GetDWARFParser(); return dwarf_ast_parser->GetClangASTImporter().LayoutRecordType( record_decl, bit_size, alignment, field_offsets, base_offsets, vbase_offsets); } //---------------------------------------------------------------------- // CompilerDecl override functions //---------------------------------------------------------------------- ConstString ClangASTContext::DeclGetName(void *opaque_decl) { if (opaque_decl) { clang::NamedDecl *nd = llvm::dyn_cast((clang::Decl *)opaque_decl); if (nd != nullptr) return ConstString(nd->getDeclName().getAsString()); } return ConstString(); } ConstString ClangASTContext::DeclGetMangledName(void *opaque_decl) { if (opaque_decl) { clang::NamedDecl *nd = llvm::dyn_cast((clang::Decl *)opaque_decl); if (nd != nullptr && !llvm::isa(nd)) { clang::MangleContext *mc = getMangleContext(); if (mc && mc->shouldMangleCXXName(nd)) { llvm::SmallVector buf; llvm::raw_svector_ostream llvm_ostrm(buf); if (llvm::isa(nd)) { mc->mangleCXXCtor(llvm::dyn_cast(nd), Ctor_Complete, llvm_ostrm); } else if (llvm::isa(nd)) { mc->mangleCXXDtor(llvm::dyn_cast(nd), Dtor_Complete, llvm_ostrm); } else { mc->mangleName(nd, llvm_ostrm); } if (buf.size() > 0) return ConstString(buf.data(), buf.size()); } } } return ConstString(); } CompilerDeclContext ClangASTContext::DeclGetDeclContext(void *opaque_decl) { if (opaque_decl) return CompilerDeclContext(this, ((clang::Decl *)opaque_decl)->getDeclContext()); else return CompilerDeclContext(); } CompilerType ClangASTContext::DeclGetFunctionReturnType(void *opaque_decl) { if (clang::FunctionDecl *func_decl = llvm::dyn_cast((clang::Decl *)opaque_decl)) return CompilerType(this, func_decl->getReturnType().getAsOpaquePtr()); if (clang::ObjCMethodDecl *objc_method = llvm::dyn_cast((clang::Decl *)opaque_decl)) return CompilerType(this, objc_method->getReturnType().getAsOpaquePtr()); else return CompilerType(); } size_t ClangASTContext::DeclGetFunctionNumArguments(void *opaque_decl) { if (clang::FunctionDecl *func_decl = llvm::dyn_cast((clang::Decl *)opaque_decl)) return func_decl->param_size(); if (clang::ObjCMethodDecl *objc_method = llvm::dyn_cast((clang::Decl *)opaque_decl)) return objc_method->param_size(); else return 0; } CompilerType ClangASTContext::DeclGetFunctionArgumentType(void *opaque_decl, size_t idx) { if (clang::FunctionDecl *func_decl = llvm::dyn_cast((clang::Decl *)opaque_decl)) { if (idx < func_decl->param_size()) { ParmVarDecl *var_decl = func_decl->getParamDecl(idx); if (var_decl) return CompilerType(this, var_decl->getOriginalType().getAsOpaquePtr()); } } else if (clang::ObjCMethodDecl *objc_method = llvm::dyn_cast( (clang::Decl *)opaque_decl)) { if (idx < objc_method->param_size()) return CompilerType( this, objc_method->parameters()[idx]->getOriginalType().getAsOpaquePtr()); } return CompilerType(); } //---------------------------------------------------------------------- // CompilerDeclContext functions //---------------------------------------------------------------------- std::vector ClangASTContext::DeclContextFindDeclByName( void *opaque_decl_ctx, ConstString name, const bool ignore_using_decls) { std::vector found_decls; if (opaque_decl_ctx) { DeclContext *root_decl_ctx = (DeclContext *)opaque_decl_ctx; std::set searched; std::multimap search_queue; SymbolFile *symbol_file = GetSymbolFile(); for (clang::DeclContext *decl_context = root_decl_ctx; decl_context != nullptr && found_decls.empty(); decl_context = decl_context->getParent()) { search_queue.insert(std::make_pair(decl_context, decl_context)); for (auto it = search_queue.find(decl_context); it != search_queue.end(); it++) { if (!searched.insert(it->second).second) continue; symbol_file->ParseDeclsForContext( CompilerDeclContext(this, it->second)); for (clang::Decl *child : it->second->decls()) { if (clang::UsingDirectiveDecl *ud = llvm::dyn_cast(child)) { if (ignore_using_decls) continue; clang::DeclContext *from = ud->getCommonAncestor(); if (searched.find(ud->getNominatedNamespace()) == searched.end()) search_queue.insert( std::make_pair(from, ud->getNominatedNamespace())); } else if (clang::UsingDecl *ud = llvm::dyn_cast(child)) { if (ignore_using_decls) continue; for (clang::UsingShadowDecl *usd : ud->shadows()) { clang::Decl *target = usd->getTargetDecl(); if (clang::NamedDecl *nd = llvm::dyn_cast(target)) { IdentifierInfo *ii = nd->getIdentifier(); if (ii != nullptr && ii->getName().equals(name.AsCString(nullptr))) found_decls.push_back(CompilerDecl(this, nd)); } } } else if (clang::NamedDecl *nd = llvm::dyn_cast(child)) { IdentifierInfo *ii = nd->getIdentifier(); if (ii != nullptr && ii->getName().equals(name.AsCString(nullptr))) found_decls.push_back(CompilerDecl(this, nd)); } } } } } return found_decls; } // Look for child_decl_ctx's lookup scope in frame_decl_ctx and its parents, // and return the number of levels it took to find it, or // LLDB_INVALID_DECL_LEVEL // if not found. If the decl was imported via a using declaration, its name // and/or // type, if set, will be used to check that the decl found in the scope is a // match. // // The optional name is required by languages (like C++) to handle using // declarations // like: // // void poo(); // namespace ns { // void foo(); // void goo(); // } // void bar() { // using ns::foo; // // CountDeclLevels returns 0 for 'foo', 1 for 'poo', and // // LLDB_INVALID_DECL_LEVEL for 'goo'. // } // // The optional type is useful in the case that there's a specific overload // that we're looking for that might otherwise be shadowed, like: // // void foo(int); // namespace ns { // void foo(); // } // void bar() { // using ns::foo; // // CountDeclLevels returns 0 for { 'foo', void() }, // // 1 for { 'foo', void(int) }, and // // LLDB_INVALID_DECL_LEVEL for { 'foo', void(int, int) }. // } // // NOTE: Because file statics are at the TranslationUnit along with globals, a // function at file scope will return the same level as a function at global // scope. // Ideally we'd like to treat the file scope as an additional scope just below // the // global scope. More work needs to be done to recognise that, if the decl // we're // trying to look up is static, we should compare its source file with that of // the // current scope and return a lower number for it. uint32_t ClangASTContext::CountDeclLevels(clang::DeclContext *frame_decl_ctx, clang::DeclContext *child_decl_ctx, ConstString *child_name, CompilerType *child_type) { if (frame_decl_ctx) { std::set searched; std::multimap search_queue; SymbolFile *symbol_file = GetSymbolFile(); // Get the lookup scope for the decl we're trying to find. clang::DeclContext *parent_decl_ctx = child_decl_ctx->getParent(); // Look for it in our scope's decl context and its parents. uint32_t level = 0; for (clang::DeclContext *decl_ctx = frame_decl_ctx; decl_ctx != nullptr; decl_ctx = decl_ctx->getParent()) { if (!decl_ctx->isLookupContext()) continue; if (decl_ctx == parent_decl_ctx) // Found it! return level; search_queue.insert(std::make_pair(decl_ctx, decl_ctx)); for (auto it = search_queue.find(decl_ctx); it != search_queue.end(); it++) { if (searched.find(it->second) != searched.end()) continue; // Currently DWARF has one shared translation unit for all Decls at top // level, so this // would erroneously find using statements anywhere. So don't look at // the top-level // translation unit. // TODO fix this and add a testcase that depends on it. if (llvm::isa(it->second)) continue; searched.insert(it->second); symbol_file->ParseDeclsForContext( CompilerDeclContext(this, it->second)); for (clang::Decl *child : it->second->decls()) { if (clang::UsingDirectiveDecl *ud = llvm::dyn_cast(child)) { clang::DeclContext *ns = ud->getNominatedNamespace(); if (ns == parent_decl_ctx) // Found it! return level; clang::DeclContext *from = ud->getCommonAncestor(); if (searched.find(ns) == searched.end()) search_queue.insert(std::make_pair(from, ns)); } else if (child_name) { if (clang::UsingDecl *ud = llvm::dyn_cast(child)) { for (clang::UsingShadowDecl *usd : ud->shadows()) { clang::Decl *target = usd->getTargetDecl(); clang::NamedDecl *nd = llvm::dyn_cast(target); if (!nd) continue; // Check names. IdentifierInfo *ii = nd->getIdentifier(); if (ii == nullptr || !ii->getName().equals(child_name->AsCString(nullptr))) continue; // Check types, if one was provided. if (child_type) { CompilerType clang_type = ClangASTContext::GetTypeForDecl(nd); if (!AreTypesSame(clang_type, *child_type, /*ignore_qualifiers=*/true)) continue; } // Found it! return level; } } } } } ++level; } } return LLDB_INVALID_DECL_LEVEL; } bool ClangASTContext::DeclContextIsStructUnionOrClass(void *opaque_decl_ctx) { if (opaque_decl_ctx) return ((clang::DeclContext *)opaque_decl_ctx)->isRecord(); else return false; } ConstString ClangASTContext::DeclContextGetName(void *opaque_decl_ctx) { if (opaque_decl_ctx) { clang::NamedDecl *named_decl = llvm::dyn_cast((clang::DeclContext *)opaque_decl_ctx); if (named_decl) return ConstString(named_decl->getName()); } return ConstString(); } ConstString ClangASTContext::DeclContextGetScopeQualifiedName(void *opaque_decl_ctx) { if (opaque_decl_ctx) { clang::NamedDecl *named_decl = llvm::dyn_cast((clang::DeclContext *)opaque_decl_ctx); if (named_decl) return ConstString( llvm::StringRef(named_decl->getQualifiedNameAsString())); } return ConstString(); } bool ClangASTContext::DeclContextIsClassMethod( void *opaque_decl_ctx, lldb::LanguageType *language_ptr, bool *is_instance_method_ptr, ConstString *language_object_name_ptr) { if (opaque_decl_ctx) { clang::DeclContext *decl_ctx = (clang::DeclContext *)opaque_decl_ctx; if (ObjCMethodDecl *objc_method = llvm::dyn_cast(decl_ctx)) { if (is_instance_method_ptr) *is_instance_method_ptr = objc_method->isInstanceMethod(); if (language_ptr) *language_ptr = eLanguageTypeObjC; if (language_object_name_ptr) language_object_name_ptr->SetCString("self"); return true; } else if (CXXMethodDecl *cxx_method = llvm::dyn_cast(decl_ctx)) { if (is_instance_method_ptr) *is_instance_method_ptr = cxx_method->isInstance(); if (language_ptr) *language_ptr = eLanguageTypeC_plus_plus; if (language_object_name_ptr) language_object_name_ptr->SetCString("this"); return true; } else if (clang::FunctionDecl *function_decl = llvm::dyn_cast(decl_ctx)) { ClangASTMetadata *metadata = GetMetadata(&decl_ctx->getParentASTContext(), function_decl); if (metadata && metadata->HasObjectPtr()) { if (is_instance_method_ptr) *is_instance_method_ptr = true; if (language_ptr) *language_ptr = eLanguageTypeObjC; if (language_object_name_ptr) language_object_name_ptr->SetCString(metadata->GetObjectPtrName()); return true; } } } return false; } clang::DeclContext * ClangASTContext::DeclContextGetAsDeclContext(const CompilerDeclContext &dc) { if (dc.IsClang()) return (clang::DeclContext *)dc.GetOpaqueDeclContext(); return nullptr; } ObjCMethodDecl * ClangASTContext::DeclContextGetAsObjCMethodDecl(const CompilerDeclContext &dc) { if (dc.IsClang()) return llvm::dyn_cast( (clang::DeclContext *)dc.GetOpaqueDeclContext()); return nullptr; } CXXMethodDecl * ClangASTContext::DeclContextGetAsCXXMethodDecl(const CompilerDeclContext &dc) { if (dc.IsClang()) return llvm::dyn_cast( (clang::DeclContext *)dc.GetOpaqueDeclContext()); return nullptr; } clang::FunctionDecl * ClangASTContext::DeclContextGetAsFunctionDecl(const CompilerDeclContext &dc) { if (dc.IsClang()) return llvm::dyn_cast( (clang::DeclContext *)dc.GetOpaqueDeclContext()); return nullptr; } clang::NamespaceDecl * ClangASTContext::DeclContextGetAsNamespaceDecl(const CompilerDeclContext &dc) { if (dc.IsClang()) return llvm::dyn_cast( (clang::DeclContext *)dc.GetOpaqueDeclContext()); return nullptr; } ClangASTMetadata * ClangASTContext::DeclContextGetMetaData(const CompilerDeclContext &dc, const void *object) { clang::ASTContext *ast = DeclContextGetClangASTContext(dc); if (ast) return ClangASTContext::GetMetadata(ast, object); return nullptr; } clang::ASTContext * ClangASTContext::DeclContextGetClangASTContext(const CompilerDeclContext &dc) { ClangASTContext *ast = llvm::dyn_cast_or_null(dc.GetTypeSystem()); if (ast) return ast->getASTContext(); return nullptr; } ClangASTContextForExpressions::ClangASTContextForExpressions(Target &target) : ClangASTContext(target.GetArchitecture().GetTriple().getTriple().c_str()), m_target_wp(target.shared_from_this()), m_persistent_variables(new ClangPersistentVariables) {} UserExpression *ClangASTContextForExpressions::GetUserExpression( llvm::StringRef expr, llvm::StringRef prefix, lldb::LanguageType language, Expression::ResultType desired_type, const EvaluateExpressionOptions &options) { TargetSP target_sp = m_target_wp.lock(); if (!target_sp) return nullptr; return new ClangUserExpression(*target_sp.get(), expr, prefix, language, desired_type, options); } FunctionCaller *ClangASTContextForExpressions::GetFunctionCaller( const CompilerType &return_type, const Address &function_address, const ValueList &arg_value_list, const char *name) { TargetSP target_sp = m_target_wp.lock(); if (!target_sp) return nullptr; Process *process = target_sp->GetProcessSP().get(); if (!process) return nullptr; return new ClangFunctionCaller(*process, return_type, function_address, arg_value_list, name); } UtilityFunction * ClangASTContextForExpressions::GetUtilityFunction(const char *text, const char *name) { TargetSP target_sp = m_target_wp.lock(); if (!target_sp) return nullptr; return new ClangUtilityFunction(*target_sp.get(), text, name); } PersistentExpressionState * ClangASTContextForExpressions::GetPersistentExpressionState() { return m_persistent_variables.get(); } Index: vendor/lldb/dist/source/Symbol/Type.cpp =================================================================== --- vendor/lldb/dist/source/Symbol/Type.cpp (revision 312182) +++ vendor/lldb/dist/source/Symbol/Type.cpp (revision 312183) @@ -1,1113 +1,1122 @@ //===-- Type.cpp ------------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes #include // C++ Includes // Other libraries and framework includes // Project includes #include "lldb/Core/DataBufferHeap.h" #include "lldb/Core/DataExtractor.h" #include "lldb/Core/Module.h" #include "lldb/Core/Scalar.h" #include "lldb/Core/StreamString.h" #include "lldb/Symbol/CompilerType.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContextScope.h" #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/TypeList.h" #include "lldb/Symbol/TypeSystem.h" #include "lldb/Target/ExecutionContext.h" #include "lldb/Target/Process.h" #include "lldb/Target/Target.h" #include "llvm/ADT/StringRef.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" using namespace lldb; using namespace lldb_private; void CompilerContext::Dump() const { switch (type) { case CompilerContextKind::Invalid: printf("Invalid"); break; case CompilerContextKind::TranslationUnit: printf("TranslationUnit"); break; case CompilerContextKind::Module: printf("Module"); break; case CompilerContextKind::Namespace: printf("Namespace"); break; case CompilerContextKind::Class: printf("Class"); break; case CompilerContextKind::Structure: printf("Structure"); break; case CompilerContextKind::Union: printf("Union"); break; case CompilerContextKind::Function: printf("Function"); break; case CompilerContextKind::Variable: printf("Variable"); break; case CompilerContextKind::Enumeration: printf("Enumeration"); break; case CompilerContextKind::Typedef: printf("Typedef"); break; } printf("(\"%s\")\n", name.GetCString()); } class TypeAppendVisitor { public: TypeAppendVisitor(TypeListImpl &type_list) : m_type_list(type_list) {} bool operator()(const lldb::TypeSP &type) { m_type_list.Append(TypeImplSP(new TypeImpl(type))); return true; } private: TypeListImpl &m_type_list; }; void TypeListImpl::Append(const lldb_private::TypeList &type_list) { TypeAppendVisitor cb(*this); type_list.ForEach(cb); } SymbolFileType::SymbolFileType(SymbolFile &symbol_file, const lldb::TypeSP &type_sp) : UserID(type_sp ? type_sp->GetID() : LLDB_INVALID_UID), m_symbol_file(symbol_file), m_type_sp(type_sp) {} Type *SymbolFileType::GetType() { if (!m_type_sp) { Type *resolved_type = m_symbol_file.ResolveTypeUID(GetID()); if (resolved_type) m_type_sp = resolved_type->shared_from_this(); } return m_type_sp.get(); } Type::Type(lldb::user_id_t uid, SymbolFile *symbol_file, const ConstString &name, uint64_t byte_size, SymbolContextScope *context, user_id_t encoding_uid, EncodingDataType encoding_uid_type, const Declaration &decl, const CompilerType &compiler_type, ResolveState compiler_type_resolve_state) : std::enable_shared_from_this(), UserID(uid), m_name(name), m_symbol_file(symbol_file), m_context(context), m_encoding_type(nullptr), m_encoding_uid(encoding_uid), m_encoding_uid_type(encoding_uid_type), m_byte_size(byte_size), m_decl(decl), m_compiler_type(compiler_type) { m_flags.compiler_type_resolve_state = (compiler_type ? compiler_type_resolve_state : eResolveStateUnresolved); m_flags.is_complete_objc_class = false; } Type::Type() : std::enable_shared_from_this(), UserID(0), m_name(""), m_symbol_file(nullptr), m_context(nullptr), m_encoding_type(nullptr), m_encoding_uid(LLDB_INVALID_UID), m_encoding_uid_type(eEncodingInvalid), m_byte_size(0), m_decl(), m_compiler_type() { m_flags.compiler_type_resolve_state = eResolveStateUnresolved; m_flags.is_complete_objc_class = false; } Type::Type(const Type &rhs) : std::enable_shared_from_this(rhs), UserID(rhs), m_name(rhs.m_name), m_symbol_file(rhs.m_symbol_file), m_context(rhs.m_context), m_encoding_type(rhs.m_encoding_type), m_encoding_uid(rhs.m_encoding_uid), m_encoding_uid_type(rhs.m_encoding_uid_type), m_byte_size(rhs.m_byte_size), m_decl(rhs.m_decl), m_compiler_type(rhs.m_compiler_type), m_flags(rhs.m_flags) {} const Type &Type::operator=(const Type &rhs) { if (this != &rhs) { } return *this; } void Type::GetDescription(Stream *s, lldb::DescriptionLevel level, bool show_name) { *s << "id = " << (const UserID &)*this; // Call the name accessor to make sure we resolve the type name if (show_name) { const ConstString &type_name = GetName(); if (type_name) { *s << ", name = \"" << type_name << '"'; ConstString qualified_type_name(GetQualifiedName()); if (qualified_type_name != type_name) { *s << ", qualified = \"" << qualified_type_name << '"'; } } } // Call the get byte size accesor so we resolve our byte size if (GetByteSize()) s->Printf(", byte-size = %" PRIu64, m_byte_size); bool show_fullpaths = (level == lldb::eDescriptionLevelVerbose); m_decl.Dump(s, show_fullpaths); if (m_compiler_type.IsValid()) { *s << ", compiler_type = \""; GetForwardCompilerType().DumpTypeDescription(s); *s << '"'; } else if (m_encoding_uid != LLDB_INVALID_UID) { s->Printf(", type_uid = 0x%8.8" PRIx64, m_encoding_uid); switch (m_encoding_uid_type) { case eEncodingInvalid: break; case eEncodingIsUID: s->PutCString(" (unresolved type)"); break; case eEncodingIsConstUID: s->PutCString(" (unresolved const type)"); break; case eEncodingIsRestrictUID: s->PutCString(" (unresolved restrict type)"); break; case eEncodingIsVolatileUID: s->PutCString(" (unresolved volatile type)"); break; case eEncodingIsTypedefUID: s->PutCString(" (unresolved typedef)"); break; case eEncodingIsPointerUID: s->PutCString(" (unresolved pointer)"); break; case eEncodingIsLValueReferenceUID: s->PutCString(" (unresolved L value reference)"); break; case eEncodingIsRValueReferenceUID: s->PutCString(" (unresolved R value reference)"); break; case eEncodingIsSyntheticUID: s->PutCString(" (synthetic type)"); break; } } } void Type::Dump(Stream *s, bool show_context) { s->Printf("%p: ", static_cast(this)); s->Indent(); *s << "Type" << static_cast(*this) << ' '; if (m_name) *s << ", name = \"" << m_name << "\""; if (m_byte_size != 0) s->Printf(", size = %" PRIu64, m_byte_size); if (show_context && m_context != nullptr) { s->PutCString(", context = ( "); m_context->DumpSymbolContext(s); s->PutCString(" )"); } bool show_fullpaths = false; m_decl.Dump(s, show_fullpaths); if (m_compiler_type.IsValid()) { *s << ", compiler_type = " << m_compiler_type.GetOpaqueQualType() << ' '; GetForwardCompilerType().DumpTypeDescription(s); } else if (m_encoding_uid != LLDB_INVALID_UID) { *s << ", type_data = " << (uint64_t)m_encoding_uid; switch (m_encoding_uid_type) { case eEncodingInvalid: break; case eEncodingIsUID: s->PutCString(" (unresolved type)"); break; case eEncodingIsConstUID: s->PutCString(" (unresolved const type)"); break; case eEncodingIsRestrictUID: s->PutCString(" (unresolved restrict type)"); break; case eEncodingIsVolatileUID: s->PutCString(" (unresolved volatile type)"); break; case eEncodingIsTypedefUID: s->PutCString(" (unresolved typedef)"); break; case eEncodingIsPointerUID: s->PutCString(" (unresolved pointer)"); break; case eEncodingIsLValueReferenceUID: s->PutCString(" (unresolved L value reference)"); break; case eEncodingIsRValueReferenceUID: s->PutCString(" (unresolved R value reference)"); break; case eEncodingIsSyntheticUID: s->PutCString(" (synthetic type)"); break; } } // // if (m_access) // s->Printf(", access = %u", m_access); s->EOL(); } const ConstString &Type::GetName() { if (!m_name) m_name = GetForwardCompilerType().GetConstTypeName(); return m_name; } void Type::DumpTypeName(Stream *s) { GetName().Dump(s, ""); } void Type::DumpValue(ExecutionContext *exe_ctx, Stream *s, const DataExtractor &data, uint32_t data_byte_offset, bool show_types, bool show_summary, bool verbose, lldb::Format format) { if (ResolveClangType(eResolveStateForward)) { if (show_types) { s->PutChar('('); if (verbose) s->Printf("Type{0x%8.8" PRIx64 "} ", GetID()); DumpTypeName(s); s->PutCString(") "); } GetForwardCompilerType().DumpValue( exe_ctx, s, format == lldb::eFormatDefault ? GetFormat() : format, data, data_byte_offset, GetByteSize(), 0, // Bitfield bit size 0, // Bitfield bit offset show_types, show_summary, verbose, 0); } } Type *Type::GetEncodingType() { if (m_encoding_type == nullptr && m_encoding_uid != LLDB_INVALID_UID) m_encoding_type = m_symbol_file->ResolveTypeUID(m_encoding_uid); return m_encoding_type; } uint64_t Type::GetByteSize() { if (m_byte_size == 0) { switch (m_encoding_uid_type) { case eEncodingInvalid: case eEncodingIsSyntheticUID: break; case eEncodingIsUID: case eEncodingIsConstUID: case eEncodingIsRestrictUID: case eEncodingIsVolatileUID: case eEncodingIsTypedefUID: { Type *encoding_type = GetEncodingType(); if (encoding_type) m_byte_size = encoding_type->GetByteSize(); if (m_byte_size == 0) m_byte_size = GetLayoutCompilerType().GetByteSize(nullptr); } break; // If we are a pointer or reference, then this is just a pointer size; case eEncodingIsPointerUID: case eEncodingIsLValueReferenceUID: case eEncodingIsRValueReferenceUID: { ArchSpec arch; if (m_symbol_file->GetObjectFile()->GetArchitecture(arch)) m_byte_size = arch.GetAddressByteSize(); } break; } } return m_byte_size; } uint32_t Type::GetNumChildren(bool omit_empty_base_classes) { return GetForwardCompilerType().GetNumChildren(omit_empty_base_classes); } bool Type::IsAggregateType() { return GetForwardCompilerType().IsAggregateType(); } lldb::TypeSP Type::GetTypedefType() { lldb::TypeSP type_sp; if (IsTypedef()) { Type *typedef_type = m_symbol_file->ResolveTypeUID(m_encoding_uid); if (typedef_type) type_sp = typedef_type->shared_from_this(); } return type_sp; } lldb::Format Type::GetFormat() { return GetForwardCompilerType().GetFormat(); } lldb::Encoding Type::GetEncoding(uint64_t &count) { // Make sure we resolve our type if it already hasn't been. return GetForwardCompilerType().GetEncoding(count); } bool Type::DumpValueInMemory(ExecutionContext *exe_ctx, Stream *s, lldb::addr_t address, AddressType address_type, bool show_types, bool show_summary, bool verbose) { if (address != LLDB_INVALID_ADDRESS) { DataExtractor data; Target *target = nullptr; if (exe_ctx) target = exe_ctx->GetTargetPtr(); if (target) data.SetByteOrder(target->GetArchitecture().GetByteOrder()); if (ReadFromMemory(exe_ctx, address, address_type, data)) { DumpValue(exe_ctx, s, data, 0, show_types, show_summary, verbose); return true; } } return false; } bool Type::ReadFromMemory(ExecutionContext *exe_ctx, lldb::addr_t addr, AddressType address_type, DataExtractor &data) { if (address_type == eAddressTypeFile) { // Can't convert a file address to anything valid without more // context (which Module it came from) return false; } const uint64_t byte_size = GetByteSize(); if (data.GetByteSize() < byte_size) { lldb::DataBufferSP data_sp(new DataBufferHeap(byte_size, '\0')); data.SetData(data_sp); } uint8_t *dst = const_cast(data.PeekData(0, byte_size)); if (dst != nullptr) { if (address_type == eAddressTypeHost) { // The address is an address in this process, so just copy it if (addr == 0) return false; memcpy(dst, (uint8_t *)nullptr + addr, byte_size); return true; } else { if (exe_ctx) { Process *process = exe_ctx->GetProcessPtr(); if (process) { Error error; return exe_ctx->GetProcessPtr()->ReadMemory(addr, dst, byte_size, error) == byte_size; } } } } return false; } bool Type::WriteToMemory(ExecutionContext *exe_ctx, lldb::addr_t addr, AddressType address_type, DataExtractor &data) { return false; } TypeList *Type::GetTypeList() { return GetSymbolFile()->GetTypeList(); } const Declaration &Type::GetDeclaration() const { return m_decl; } bool Type::ResolveClangType(ResolveState compiler_type_resolve_state) { // TODO: This needs to consider the correct type system to use. Type *encoding_type = nullptr; if (!m_compiler_type.IsValid()) { encoding_type = GetEncodingType(); if (encoding_type) { switch (m_encoding_uid_type) { case eEncodingIsUID: { CompilerType encoding_compiler_type = encoding_type->GetForwardCompilerType(); if (encoding_compiler_type.IsValid()) { m_compiler_type = encoding_compiler_type; m_flags.compiler_type_resolve_state = encoding_type->m_flags.compiler_type_resolve_state; } } break; case eEncodingIsConstUID: m_compiler_type = encoding_type->GetForwardCompilerType().AddConstModifier(); break; case eEncodingIsRestrictUID: m_compiler_type = encoding_type->GetForwardCompilerType().AddRestrictModifier(); break; case eEncodingIsVolatileUID: m_compiler_type = encoding_type->GetForwardCompilerType().AddVolatileModifier(); break; case eEncodingIsTypedefUID: m_compiler_type = encoding_type->GetForwardCompilerType().CreateTypedef( m_name.AsCString("__lldb_invalid_typedef_name"), GetSymbolFile()->GetDeclContextContainingUID(GetID())); m_name.Clear(); break; case eEncodingIsPointerUID: m_compiler_type = encoding_type->GetForwardCompilerType().GetPointerType(); break; case eEncodingIsLValueReferenceUID: m_compiler_type = encoding_type->GetForwardCompilerType().GetLValueReferenceType(); break; case eEncodingIsRValueReferenceUID: m_compiler_type = encoding_type->GetForwardCompilerType().GetRValueReferenceType(); break; default: llvm_unreachable("Unhandled encoding_data_type."); } } else { // We have no encoding type, return void? TypeSystem *type_system = m_symbol_file->GetTypeSystemForLanguage(eLanguageTypeC); CompilerType void_compiler_type = type_system->GetBasicTypeFromAST(eBasicTypeVoid); switch (m_encoding_uid_type) { case eEncodingIsUID: m_compiler_type = void_compiler_type; break; case eEncodingIsConstUID: m_compiler_type = void_compiler_type.AddConstModifier(); break; case eEncodingIsRestrictUID: m_compiler_type = void_compiler_type.AddRestrictModifier(); break; case eEncodingIsVolatileUID: m_compiler_type = void_compiler_type.AddVolatileModifier(); break; case eEncodingIsTypedefUID: m_compiler_type = void_compiler_type.CreateTypedef( m_name.AsCString("__lldb_invalid_typedef_name"), GetSymbolFile()->GetDeclContextContainingUID(GetID())); break; case eEncodingIsPointerUID: m_compiler_type = void_compiler_type.GetPointerType(); break; case eEncodingIsLValueReferenceUID: m_compiler_type = void_compiler_type.GetLValueReferenceType(); break; case eEncodingIsRValueReferenceUID: m_compiler_type = void_compiler_type.GetRValueReferenceType(); break; default: llvm_unreachable("Unhandled encoding_data_type."); } } // When we have a EncodingUID, our "m_flags.compiler_type_resolve_state" is // set to eResolveStateUnresolved // so we need to update it to say that we now have a forward declaration // since that is what we created // above. if (m_compiler_type.IsValid()) m_flags.compiler_type_resolve_state = eResolveStateForward; } // Check if we have a forward reference to a class/struct/union/enum? if (compiler_type_resolve_state == eResolveStateLayout || compiler_type_resolve_state == eResolveStateFull) { // Check if we have a forward reference to a class/struct/union/enum? if (m_compiler_type.IsValid() && m_flags.compiler_type_resolve_state < compiler_type_resolve_state) { m_flags.compiler_type_resolve_state = eResolveStateFull; if (!m_compiler_type.IsDefined()) { // We have a forward declaration, we need to resolve it to a complete // definition. m_symbol_file->CompleteType(m_compiler_type); } } } // If we have an encoding type, then we need to make sure it is // resolved appropriately. if (m_encoding_uid != LLDB_INVALID_UID) { if (encoding_type == nullptr) encoding_type = GetEncodingType(); if (encoding_type) { ResolveState encoding_compiler_type_resolve_state = compiler_type_resolve_state; if (compiler_type_resolve_state == eResolveStateLayout) { switch (m_encoding_uid_type) { case eEncodingIsPointerUID: case eEncodingIsLValueReferenceUID: case eEncodingIsRValueReferenceUID: encoding_compiler_type_resolve_state = eResolveStateForward; break; default: break; } } encoding_type->ResolveClangType(encoding_compiler_type_resolve_state); } } return m_compiler_type.IsValid(); } uint32_t Type::GetEncodingMask() { uint32_t encoding_mask = 1u << m_encoding_uid_type; Type *encoding_type = GetEncodingType(); assert(encoding_type != this); if (encoding_type) encoding_mask |= encoding_type->GetEncodingMask(); return encoding_mask; } CompilerType Type::GetFullCompilerType() { ResolveClangType(eResolveStateFull); return m_compiler_type; } CompilerType Type::GetLayoutCompilerType() { ResolveClangType(eResolveStateLayout); return m_compiler_type; } CompilerType Type::GetForwardCompilerType() { ResolveClangType(eResolveStateForward); return m_compiler_type; } int Type::Compare(const Type &a, const Type &b) { // Just compare the UID values for now... lldb::user_id_t a_uid = a.GetID(); lldb::user_id_t b_uid = b.GetID(); if (a_uid < b_uid) return -1; if (a_uid > b_uid) return 1; return 0; } ConstString Type::GetQualifiedName() { return GetForwardCompilerType().GetConstTypeName(); } -bool Type::GetTypeScopeAndBasename(const char *&name_cstr, std::string &scope, - std::string &basename, +bool Type::GetTypeScopeAndBasename(const llvm::StringRef& name, + llvm::StringRef &scope, + llvm::StringRef &basename, TypeClass &type_class) { - // Protect against null c string. - type_class = eTypeClassAny; - if (name_cstr && name_cstr[0]) { - llvm::StringRef name_strref(name_cstr); - if (name_strref.startswith("struct ")) { - name_cstr += 7; - type_class = eTypeClassStruct; - } else if (name_strref.startswith("class ")) { - name_cstr += 6; - type_class = eTypeClassClass; - } else if (name_strref.startswith("union ")) { - name_cstr += 6; - type_class = eTypeClassUnion; - } else if (name_strref.startswith("enum ")) { - name_cstr += 5; - type_class = eTypeClassEnumeration; - } else if (name_strref.startswith("typedef ")) { - name_cstr += 8; - type_class = eTypeClassTypedef; - } - const char *basename_cstr = name_cstr; - const char *namespace_separator = ::strstr(basename_cstr, "::"); - if (namespace_separator) { - const char *template_arg_char = ::strchr(basename_cstr, '<'); - while (namespace_separator != nullptr) { - if (template_arg_char && - namespace_separator > template_arg_char) // but namespace'd template - // arguments are still good - // to go - break; - basename_cstr = namespace_separator + 2; - namespace_separator = strstr(basename_cstr, "::"); + if (name.empty()) + return false; + + basename = name; + if (basename.consume_front("struct ")) + type_class = eTypeClassStruct; + else if (basename.consume_front("class ")) + type_class = eTypeClassClass; + else if (basename.consume_front("union ")) + type_class = eTypeClassUnion; + else if (basename.consume_front("enum ")) + type_class = eTypeClassEnumeration; + else if (basename.consume_front("typedef ")) + type_class = eTypeClassTypedef; + + size_t namespace_separator = basename.find("::"); + if (namespace_separator == llvm::StringRef::npos) + return false; + + size_t template_begin = basename.find('<'); + while (namespace_separator != llvm::StringRef::npos) { + if (template_begin != llvm::StringRef::npos && + namespace_separator > template_begin) { + size_t template_depth = 1; + llvm::StringRef template_arg = + basename.drop_front(template_begin + 1); + while (template_depth > 0 && !template_arg.empty()) { + if (template_arg.front() == '<') + template_depth++; + else if (template_arg.front() == '>') + template_depth--; + template_arg = template_arg.drop_front(1); } - if (basename_cstr > name_cstr) { - scope.assign(name_cstr, basename_cstr - name_cstr); - basename.assign(basename_cstr); - return true; - } + if (template_depth != 0) + return false; // We have an invalid type name. Bail out. + if (template_arg.empty()) + break; // The template ends at the end of the full name. + basename = template_arg; + } else { + basename = basename.drop_front(namespace_separator + 2); } + template_begin = basename.find('<'); + namespace_separator = basename.find("::"); + } + if (basename.size() < name.size()) { + scope = name.take_front(name.size() - basename.size()); + return true; } return false; } ModuleSP Type::GetModule() { if (m_symbol_file) return m_symbol_file->GetObjectFile()->GetModule(); return ModuleSP(); } TypeAndOrName::TypeAndOrName() : m_type_pair(), m_type_name() {} TypeAndOrName::TypeAndOrName(TypeSP &in_type_sp) : m_type_pair(in_type_sp) { if (in_type_sp) m_type_name = in_type_sp->GetName(); } TypeAndOrName::TypeAndOrName(const char *in_type_str) : m_type_name(in_type_str) {} TypeAndOrName::TypeAndOrName(const TypeAndOrName &rhs) : m_type_pair(rhs.m_type_pair), m_type_name(rhs.m_type_name) {} TypeAndOrName::TypeAndOrName(ConstString &in_type_const_string) : m_type_name(in_type_const_string) {} TypeAndOrName &TypeAndOrName::operator=(const TypeAndOrName &rhs) { if (this != &rhs) { m_type_name = rhs.m_type_name; m_type_pair = rhs.m_type_pair; } return *this; } bool TypeAndOrName::operator==(const TypeAndOrName &other) const { if (m_type_pair != other.m_type_pair) return false; if (m_type_name != other.m_type_name) return false; return true; } bool TypeAndOrName::operator!=(const TypeAndOrName &other) const { if (m_type_pair != other.m_type_pair) return true; if (m_type_name != other.m_type_name) return true; return false; } ConstString TypeAndOrName::GetName() const { if (m_type_name) return m_type_name; if (m_type_pair) return m_type_pair.GetName(); return ConstString(""); } void TypeAndOrName::SetName(const ConstString &type_name) { m_type_name = type_name; } void TypeAndOrName::SetName(const char *type_name_cstr) { m_type_name.SetCString(type_name_cstr); } void TypeAndOrName::SetTypeSP(lldb::TypeSP type_sp) { m_type_pair.SetType(type_sp); if (m_type_pair) m_type_name = m_type_pair.GetName(); } void TypeAndOrName::SetCompilerType(CompilerType compiler_type) { m_type_pair.SetType(compiler_type); if (m_type_pair) m_type_name = m_type_pair.GetName(); } bool TypeAndOrName::IsEmpty() const { if ((bool)m_type_name || (bool)m_type_pair) return false; else return true; } void TypeAndOrName::Clear() { m_type_name.Clear(); m_type_pair.Clear(); } bool TypeAndOrName::HasName() const { return (bool)m_type_name; } bool TypeAndOrName::HasTypeSP() const { return m_type_pair.GetTypeSP().get() != nullptr; } bool TypeAndOrName::HasCompilerType() const { return m_type_pair.GetCompilerType().IsValid(); } TypeImpl::TypeImpl() : m_module_wp(), m_static_type(), m_dynamic_type() {} TypeImpl::TypeImpl(const TypeImpl &rhs) : m_module_wp(rhs.m_module_wp), m_static_type(rhs.m_static_type), m_dynamic_type(rhs.m_dynamic_type) {} TypeImpl::TypeImpl(const lldb::TypeSP &type_sp) : m_module_wp(), m_static_type(), m_dynamic_type() { SetType(type_sp); } TypeImpl::TypeImpl(const CompilerType &compiler_type) : m_module_wp(), m_static_type(), m_dynamic_type() { SetType(compiler_type); } TypeImpl::TypeImpl(const lldb::TypeSP &type_sp, const CompilerType &dynamic) : m_module_wp(), m_static_type(type_sp), m_dynamic_type(dynamic) { SetType(type_sp, dynamic); } TypeImpl::TypeImpl(const CompilerType &static_type, const CompilerType &dynamic_type) : m_module_wp(), m_static_type(), m_dynamic_type() { SetType(static_type, dynamic_type); } TypeImpl::TypeImpl(const TypePair &pair, const CompilerType &dynamic) : m_module_wp(), m_static_type(), m_dynamic_type() { SetType(pair, dynamic); } void TypeImpl::SetType(const lldb::TypeSP &type_sp) { m_static_type.SetType(type_sp); if (type_sp) m_module_wp = type_sp->GetModule(); else m_module_wp = lldb::ModuleWP(); } void TypeImpl::SetType(const CompilerType &compiler_type) { m_module_wp = lldb::ModuleWP(); m_static_type.SetType(compiler_type); } void TypeImpl::SetType(const lldb::TypeSP &type_sp, const CompilerType &dynamic) { SetType(type_sp); m_dynamic_type = dynamic; } void TypeImpl::SetType(const CompilerType &compiler_type, const CompilerType &dynamic) { m_module_wp = lldb::ModuleWP(); m_static_type.SetType(compiler_type); m_dynamic_type = dynamic; } void TypeImpl::SetType(const TypePair &pair, const CompilerType &dynamic) { m_module_wp = pair.GetModule(); m_static_type = pair; m_dynamic_type = dynamic; } TypeImpl &TypeImpl::operator=(const TypeImpl &rhs) { if (rhs != *this) { m_module_wp = rhs.m_module_wp; m_static_type = rhs.m_static_type; m_dynamic_type = rhs.m_dynamic_type; } return *this; } bool TypeImpl::CheckModule(lldb::ModuleSP &module_sp) const { // Check if we have a module for this type. If we do and the shared pointer is // can be successfully initialized with m_module_wp, return true. Else return // false // if we didn't have a module, or if we had a module and it has been deleted. // Any // functions doing anything with a TypeSP in this TypeImpl class should call // this // function and only do anything with the ivars if this function returns true. // If // we have a module, the "module_sp" will be filled in with a strong reference // to the // module so that the module will at least stay around long enough for the // type // query to succeed. module_sp = m_module_wp.lock(); if (!module_sp) { lldb::ModuleWP empty_module_wp; // If either call to "std::weak_ptr::owner_before(...) value returns true, // this // indicates that m_module_wp once contained (possibly still does) a // reference // to a valid shared pointer. This helps us know if we had a valid reference // to // a section which is now invalid because the module it was in was deleted if (empty_module_wp.owner_before(m_module_wp) || m_module_wp.owner_before(empty_module_wp)) { // m_module_wp had a valid reference to a module, but all strong // references // have been released and the module has been deleted return false; } } // We either successfully locked the module, or didn't have one to begin with return true; } bool TypeImpl::operator==(const TypeImpl &rhs) const { return m_static_type == rhs.m_static_type && m_dynamic_type == rhs.m_dynamic_type; } bool TypeImpl::operator!=(const TypeImpl &rhs) const { return m_static_type != rhs.m_static_type || m_dynamic_type != rhs.m_dynamic_type; } bool TypeImpl::IsValid() const { // just a name is not valid ModuleSP module_sp; if (CheckModule(module_sp)) return m_static_type.IsValid() || m_dynamic_type.IsValid(); return false; } TypeImpl::operator bool() const { return IsValid(); } void TypeImpl::Clear() { m_module_wp = lldb::ModuleWP(); m_static_type.Clear(); m_dynamic_type.Clear(); } ConstString TypeImpl::GetName() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type) return m_dynamic_type.GetTypeName(); return m_static_type.GetName(); } return ConstString(); } ConstString TypeImpl::GetDisplayTypeName() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type) return m_dynamic_type.GetDisplayTypeName(); return m_static_type.GetDisplayTypeName(); } return ConstString(); } TypeImpl TypeImpl::GetPointerType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetPointerType(), m_dynamic_type.GetPointerType()); } return TypeImpl(m_static_type.GetPointerType()); } return TypeImpl(); } TypeImpl TypeImpl::GetPointeeType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetPointeeType(), m_dynamic_type.GetPointeeType()); } return TypeImpl(m_static_type.GetPointeeType()); } return TypeImpl(); } TypeImpl TypeImpl::GetReferenceType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetReferenceType(), m_dynamic_type.GetLValueReferenceType()); } return TypeImpl(m_static_type.GetReferenceType()); } return TypeImpl(); } TypeImpl TypeImpl::GetTypedefedType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetTypedefedType(), m_dynamic_type.GetTypedefedType()); } return TypeImpl(m_static_type.GetTypedefedType()); } return TypeImpl(); } TypeImpl TypeImpl::GetDereferencedType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetDereferencedType(), m_dynamic_type.GetNonReferenceType()); } return TypeImpl(m_static_type.GetDereferencedType()); } return TypeImpl(); } TypeImpl TypeImpl::GetUnqualifiedType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetUnqualifiedType(), m_dynamic_type.GetFullyUnqualifiedType()); } return TypeImpl(m_static_type.GetUnqualifiedType()); } return TypeImpl(); } TypeImpl TypeImpl::GetCanonicalType() const { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { return TypeImpl(m_static_type.GetCanonicalType(), m_dynamic_type.GetCanonicalType()); } return TypeImpl(m_static_type.GetCanonicalType()); } return TypeImpl(); } CompilerType TypeImpl::GetCompilerType(bool prefer_dynamic) { ModuleSP module_sp; if (CheckModule(module_sp)) { if (prefer_dynamic) { if (m_dynamic_type.IsValid()) return m_dynamic_type; } return m_static_type.GetCompilerType(); } return CompilerType(); } TypeSystem *TypeImpl::GetTypeSystem(bool prefer_dynamic) { ModuleSP module_sp; if (CheckModule(module_sp)) { if (prefer_dynamic) { if (m_dynamic_type.IsValid()) return m_dynamic_type.GetTypeSystem(); } return m_static_type.GetCompilerType().GetTypeSystem(); } return NULL; } bool TypeImpl::GetDescription(lldb_private::Stream &strm, lldb::DescriptionLevel description_level) { ModuleSP module_sp; if (CheckModule(module_sp)) { if (m_dynamic_type.IsValid()) { strm.Printf("Dynamic:\n"); m_dynamic_type.DumpTypeDescription(&strm); strm.Printf("\nStatic:\n"); } m_static_type.GetCompilerType().DumpTypeDescription(&strm); } else { strm.PutCString("Invalid TypeImpl module for type has been deleted\n"); } return true; } bool TypeMemberFunctionImpl::IsValid() { return m_type.IsValid() && m_kind != lldb::eMemberFunctionKindUnknown; } ConstString TypeMemberFunctionImpl::GetName() const { return m_name; } ConstString TypeMemberFunctionImpl::GetMangledName() const { return m_decl.GetMangledName(); } CompilerType TypeMemberFunctionImpl::GetType() const { return m_type; } lldb::MemberFunctionKind TypeMemberFunctionImpl::GetKind() const { return m_kind; } bool TypeMemberFunctionImpl::GetDescription(Stream &stream) { switch (m_kind) { case lldb::eMemberFunctionKindUnknown: return false; case lldb::eMemberFunctionKindConstructor: stream.Printf("constructor for %s", m_type.GetTypeName().AsCString("")); break; case lldb::eMemberFunctionKindDestructor: stream.Printf("destructor for %s", m_type.GetTypeName().AsCString("")); break; case lldb::eMemberFunctionKindInstanceMethod: stream.Printf("instance method %s of type %s", m_name.AsCString(), m_decl.GetDeclContext().GetName().AsCString()); break; case lldb::eMemberFunctionKindStaticMethod: stream.Printf("static method %s of type %s", m_name.AsCString(), m_decl.GetDeclContext().GetName().AsCString()); break; } return true; } CompilerType TypeMemberFunctionImpl::GetReturnType() const { if (m_type) return m_type.GetFunctionReturnType(); return m_decl.GetFunctionReturnType(); } size_t TypeMemberFunctionImpl::GetNumArguments() const { if (m_type) return m_type.GetNumberOfFunctionArguments(); else return m_decl.GetNumFunctionArguments(); } CompilerType TypeMemberFunctionImpl::GetArgumentAtIndex(size_t idx) const { if (m_type) return m_type.GetFunctionArgumentAtIndex(idx); else return m_decl.GetFunctionArgumentType(idx); } TypeEnumMemberImpl::TypeEnumMemberImpl(const lldb::TypeImplSP &integer_type_sp, const ConstString &name, const llvm::APSInt &value) : m_integer_type_sp(integer_type_sp), m_name(name), m_value(value), m_valid((bool)name && (bool)integer_type_sp) {} Index: vendor/lldb/dist/source/Symbol/TypeList.cpp =================================================================== --- vendor/lldb/dist/source/Symbol/TypeList.cpp (revision 312182) +++ vendor/lldb/dist/source/Symbol/TypeList.cpp (revision 312183) @@ -1,222 +1,222 @@ //===-- TypeList.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes #include // Other libraries and framework includes #include "llvm/Support/FormattedStream.h" #include "llvm/Support/raw_ostream.h" // Project includes #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/TypeList.h" using namespace lldb; using namespace lldb_private; TypeList::TypeList() : m_types() {} //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- TypeList::~TypeList() {} void TypeList::Insert(const TypeSP &type_sp) { // Just push each type on the back for now. We will worry about uniquing later if (type_sp) m_types.push_back(type_sp); } //---------------------------------------------------------------------- // Find a base type by its unique ID. //---------------------------------------------------------------------- // TypeSP // TypeList::FindType(lldb::user_id_t uid) //{ // iterator pos = m_types.find(uid); // if (pos != m_types.end()) // return pos->second; // return TypeSP(); //} //---------------------------------------------------------------------- // Find a type by name. //---------------------------------------------------------------------- // TypeList // TypeList::FindTypes (const ConstString &name) //{ // // Do we ever need to make a lookup by name map? Here we are doing // // a linear search which isn't going to be fast. // TypeList types(m_ast.getTargetInfo()->getTriple().getTriple().c_str()); // iterator pos, end; // for (pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) // if (pos->second->GetName() == name) // types.Insert (pos->second); // return types; //} void TypeList::Clear() { m_types.clear(); } uint32_t TypeList::GetSize() const { return m_types.size(); } // GetTypeAtIndex isn't used a lot for large type lists, currently only for // type lists that are returned for "image dump -t TYPENAME" commands and other // simple symbol queries that grab the first result... TypeSP TypeList::GetTypeAtIndex(uint32_t idx) { iterator pos, end; uint32_t i = idx; for (pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) { if (i == 0) return *pos; --i; } return TypeSP(); } void TypeList::ForEach( std::function const &callback) const { for (auto pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) { if (!callback(*pos)) break; } } void TypeList::ForEach( std::function const &callback) { for (auto pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) { if (!callback(*pos)) break; } } void TypeList::Dump(Stream *s, bool show_context) { for (iterator pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) { pos->get()->Dump(s, show_context); } } void TypeList::RemoveMismatchedTypes(const char *qualified_typename, bool exact_match) { - std::string type_scope; - std::string type_basename; + llvm::StringRef type_scope; + llvm::StringRef type_basename; TypeClass type_class = eTypeClassAny; if (!Type::GetTypeScopeAndBasename(qualified_typename, type_scope, type_basename, type_class)) { type_basename = qualified_typename; - type_scope.clear(); + type_scope = ""; } return RemoveMismatchedTypes(type_scope, type_basename, type_class, exact_match); } void TypeList::RemoveMismatchedTypes(const std::string &type_scope, const std::string &type_basename, TypeClass type_class, bool exact_match) { // Our "collection" type currently is a std::map which doesn't // have any good way to iterate and remove items from the map // so we currently just make a new list and add all of the matching // types to it, and then swap it into m_types at the end collection matching_types; iterator pos, end = m_types.end(); for (pos = m_types.begin(); pos != end; ++pos) { Type *the_type = pos->get(); bool keep_match = false; TypeClass match_type_class = eTypeClassAny; if (type_class != eTypeClassAny) { match_type_class = the_type->GetForwardCompilerType().GetTypeClass(); if ((match_type_class & type_class) == 0) continue; } ConstString match_type_name_const_str(the_type->GetQualifiedName()); if (match_type_name_const_str) { const char *match_type_name = match_type_name_const_str.GetCString(); - std::string match_type_scope; - std::string match_type_basename; + llvm::StringRef match_type_scope; + llvm::StringRef match_type_basename; if (Type::GetTypeScopeAndBasename(match_type_name, match_type_scope, match_type_basename, match_type_class)) { if (match_type_basename == type_basename) { const size_t type_scope_size = type_scope.size(); const size_t match_type_scope_size = match_type_scope.size(); if (exact_match || (type_scope_size == match_type_scope_size)) { keep_match = match_type_scope == type_scope; } else { if (match_type_scope_size > type_scope_size) { const size_t type_scope_pos = match_type_scope.rfind(type_scope); if (type_scope_pos == match_type_scope_size - type_scope_size) { if (type_scope_pos >= 2) { // Our match scope ends with the type scope we were looking // for, // but we need to make sure what comes before the matching // type scope is a namespace boundary in case we are trying to // match: // type_basename = "d" // type_scope = "b::c::" // We want to match: // match_type_scope "a::b::c::" // But not: // match_type_scope "a::bb::c::" // So below we make sure what comes before "b::c::" in // match_type_scope // is "::", or the namespace boundary if (match_type_scope[type_scope_pos - 1] == ':' && match_type_scope[type_scope_pos - 2] == ':') { keep_match = true; } } } } } } } else { // The type we are currently looking at doesn't exists // in a namespace or class, so it only matches if there // is no type scope... keep_match = type_scope.empty() && type_basename.compare(match_type_name) == 0; } } if (keep_match) { matching_types.push_back(*pos); } } m_types.swap(matching_types); } void TypeList::RemoveMismatchedTypes(TypeClass type_class) { if (type_class == eTypeClassAny) return; // Our "collection" type currently is a std::map which doesn't // have any good way to iterate and remove items from the map // so we currently just make a new list and add all of the matching // types to it, and then swap it into m_types at the end collection matching_types; iterator pos, end = m_types.end(); for (pos = m_types.begin(); pos != end; ++pos) { Type *the_type = pos->get(); TypeClass match_type_class = the_type->GetForwardCompilerType().GetTypeClass(); if (match_type_class & type_class) matching_types.push_back(*pos); } m_types.swap(matching_types); } Index: vendor/lldb/dist/source/Symbol/TypeMap.cpp =================================================================== --- vendor/lldb/dist/source/Symbol/TypeMap.cpp (revision 312182) +++ vendor/lldb/dist/source/Symbol/TypeMap.cpp (revision 312183) @@ -1,266 +1,266 @@ //===-- TypeMap.cpp --------------------------------------------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // C Includes // C++ Includes #include // Other libraries and framework includes #include "clang/AST/ASTConsumer.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclGroup.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/IdentifierTable.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/TargetInfo.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/raw_ostream.h" // Project includes #include "lldb/Symbol/SymbolFile.h" #include "lldb/Symbol/SymbolVendor.h" #include "lldb/Symbol/Type.h" #include "lldb/Symbol/TypeMap.h" using namespace lldb; using namespace lldb_private; using namespace clang; TypeMap::TypeMap() : m_types() {} //---------------------------------------------------------------------- // Destructor //---------------------------------------------------------------------- TypeMap::~TypeMap() {} void TypeMap::Insert(const TypeSP &type_sp) { // Just push each type on the back for now. We will worry about uniquing later if (type_sp) m_types.insert(std::make_pair(type_sp->GetID(), type_sp)); } bool TypeMap::InsertUnique(const TypeSP &type_sp) { if (type_sp) { user_id_t type_uid = type_sp->GetID(); iterator pos, end = m_types.end(); for (pos = m_types.find(type_uid); pos != end && pos->second->GetID() == type_uid; ++pos) { if (pos->second.get() == type_sp.get()) return false; } Insert(type_sp); } return true; } //---------------------------------------------------------------------- // Find a base type by its unique ID. //---------------------------------------------------------------------- // TypeSP // TypeMap::FindType(lldb::user_id_t uid) //{ // iterator pos = m_types.find(uid); // if (pos != m_types.end()) // return pos->second; // return TypeSP(); //} //---------------------------------------------------------------------- // Find a type by name. //---------------------------------------------------------------------- // TypeMap // TypeMap::FindTypes (const ConstString &name) //{ // // Do we ever need to make a lookup by name map? Here we are doing // // a linear search which isn't going to be fast. // TypeMap types(m_ast.getTargetInfo()->getTriple().getTriple().c_str()); // iterator pos, end; // for (pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) // if (pos->second->GetName() == name) // types.Insert (pos->second); // return types; //} void TypeMap::Clear() { m_types.clear(); } uint32_t TypeMap::GetSize() const { return m_types.size(); } bool TypeMap::Empty() const { return m_types.empty(); } // GetTypeAtIndex isn't used a lot for large type lists, currently only for // type lists that are returned for "image dump -t TYPENAME" commands and other // simple symbol queries that grab the first result... TypeSP TypeMap::GetTypeAtIndex(uint32_t idx) { iterator pos, end; uint32_t i = idx; for (pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) { if (i == 0) return pos->second; --i; } return TypeSP(); } void TypeMap::ForEach( std::function const &callback) const { for (auto pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) { if (!callback(pos->second)) break; } } void TypeMap::ForEach( std::function const &callback) { for (auto pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) { if (!callback(pos->second)) break; } } bool TypeMap::Remove(const lldb::TypeSP &type_sp) { if (type_sp) { lldb::user_id_t uid = type_sp->GetID(); for (iterator pos = m_types.find(uid), end = m_types.end(); pos != end && pos->first == uid; ++pos) { if (pos->second == type_sp) { m_types.erase(pos); return true; } } } return false; } void TypeMap::Dump(Stream *s, bool show_context) { for (iterator pos = m_types.begin(), end = m_types.end(); pos != end; ++pos) { pos->second->Dump(s, show_context); } } void TypeMap::RemoveMismatchedTypes(const char *qualified_typename, bool exact_match) { - std::string type_scope; - std::string type_basename; + llvm::StringRef type_scope; + llvm::StringRef type_basename; TypeClass type_class = eTypeClassAny; if (!Type::GetTypeScopeAndBasename(qualified_typename, type_scope, type_basename, type_class)) { type_basename = qualified_typename; - type_scope.clear(); + type_scope = ""; } return RemoveMismatchedTypes(type_scope, type_basename, type_class, exact_match); } void TypeMap::RemoveMismatchedTypes(const std::string &type_scope, const std::string &type_basename, TypeClass type_class, bool exact_match) { // Our "collection" type currently is a std::map which doesn't // have any good way to iterate and remove items from the map // so we currently just make a new list and add all of the matching // types to it, and then swap it into m_types at the end collection matching_types; iterator pos, end = m_types.end(); for (pos = m_types.begin(); pos != end; ++pos) { Type *the_type = pos->second.get(); bool keep_match = false; TypeClass match_type_class = eTypeClassAny; if (type_class != eTypeClassAny) { match_type_class = the_type->GetForwardCompilerType().GetTypeClass(); if ((match_type_class & type_class) == 0) continue; } ConstString match_type_name_const_str(the_type->GetQualifiedName()); if (match_type_name_const_str) { const char *match_type_name = match_type_name_const_str.GetCString(); - std::string match_type_scope; - std::string match_type_basename; + llvm::StringRef match_type_scope; + llvm::StringRef match_type_basename; if (Type::GetTypeScopeAndBasename(match_type_name, match_type_scope, match_type_basename, match_type_class)) { if (match_type_basename == type_basename) { const size_t type_scope_size = type_scope.size(); const size_t match_type_scope_size = match_type_scope.size(); if (exact_match || (type_scope_size == match_type_scope_size)) { keep_match = match_type_scope == type_scope; } else { if (match_type_scope_size > type_scope_size) { const size_t type_scope_pos = match_type_scope.rfind(type_scope); if (type_scope_pos == match_type_scope_size - type_scope_size) { if (type_scope_pos >= 2) { // Our match scope ends with the type scope we were looking // for, // but we need to make sure what comes before the matching // type scope is a namespace boundary in case we are trying to // match: // type_basename = "d" // type_scope = "b::c::" // We want to match: // match_type_scope "a::b::c::" // But not: // match_type_scope "a::bb::c::" // So below we make sure what comes before "b::c::" in // match_type_scope // is "::", or the namespace boundary if (match_type_scope[type_scope_pos - 1] == ':' && match_type_scope[type_scope_pos - 2] == ':') { keep_match = true; } } } } } } } else { // The type we are currently looking at doesn't exists // in a namespace or class, so it only matches if there // is no type scope... keep_match = type_scope.empty() && type_basename.compare(match_type_name) == 0; } } if (keep_match) { matching_types.insert(*pos); } } m_types.swap(matching_types); } void TypeMap::RemoveMismatchedTypes(TypeClass type_class) { if (type_class == eTypeClassAny) return; // Our "collection" type currently is a std::map which doesn't // have any good way to iterate and remove items from the map // so we currently just make a new list and add all of the matching // types to it, and then swap it into m_types at the end collection matching_types; iterator pos, end = m_types.end(); for (pos = m_types.begin(); pos != end; ++pos) { Type *the_type = pos->second.get(); TypeClass match_type_class = the_type->GetForwardCompilerType().GetTypeClass(); if (match_type_class & type_class) matching_types.insert(*pos); } m_types.swap(matching_types); } Index: vendor/lldb/dist/tools/lldb-server/CMakeLists.txt =================================================================== --- vendor/lldb/dist/tools/lldb-server/CMakeLists.txt (revision 312182) +++ vendor/lldb/dist/tools/lldb-server/CMakeLists.txt (revision 312183) @@ -1,190 +1,196 @@ if ( CMAKE_SYSTEM_NAME MATCHES "Linux" ) include_directories( ../../../../llvm/include ../../source/Plugins/Process/Linux ../../source/Plugins/Process/POSIX ) endif () if ( CMAKE_SYSTEM_NAME MATCHES "FreeBSD" ) include_directories( ../../../../llvm/include ../../source/Plugins/Process/FreeBSD ../../source/Plugins/Process/POSIX ) endif () if ( CMAKE_SYSTEM_NAME MATCHES "NetBSD" ) include_directories( ../../../../llvm/include ../../source/Plugins/Process/POSIX ) endif () include_directories(../../source) set( LLDB_USED_LIBS lldbBase lldbBreakpoint lldbCommands lldbDataFormatters lldbHost lldbCore lldbExpression lldbInitialization lldbInterpreter lldbSymbol lldbTarget lldbUtility # Plugins lldbPluginDisassemblerLLVM lldbPluginSymbolFileDWARF lldbPluginSymbolFilePDB lldbPluginSymbolFileSymtab lldbPluginDynamicLoaderPosixDYLD lldbPluginCPlusPlusLanguage lldbPluginGoLanguage lldbPluginJavaLanguage lldbPluginObjCLanguage lldbPluginObjCPlusPlusLanguage lldbPluginOCamlLanguage lldbPluginObjectFileELF lldbPluginObjectFileJIT lldbPluginSymbolVendorELF lldbPluginPlatformPOSIX lldbPluginObjectContainerBSDArchive lldbPluginObjectContainerMachOArchive lldbPluginProcessGDBRemote lldbPluginProcessUtility lldbPluginObjectContainerMachOArchive lldbPluginObjectContainerBSDArchive lldbPluginPlatformMacOSX lldbPluginUnwindAssemblyInstEmulation lldbPluginUnwindAssemblyX86 lldbPluginAppleObjCRuntime lldbPluginCXXItaniumABI lldbPluginInstructionARM lldbPluginInstructionARM64 lldbPluginInstructionMIPS lldbPluginInstructionMIPS64 lldbPluginObjectFilePECOFF lldbPluginExpressionParserClang lldbPluginExpressionParserGo ) # Linux-only libraries if ( CMAKE_SYSTEM_NAME MATCHES "Linux" ) list(APPEND LLDB_USED_LIBS lldbPluginProcessLinux lldbPluginProcessPOSIX ) endif () # Darwin-only libraries if ( CMAKE_SYSTEM_NAME MATCHES "Darwin" ) list(APPEND LLDB_USED_LIBS lldbPluginObjectFileMachO ) endif() set( CLANG_USED_LIBS clangAnalysis clangAST clangBasic clangCodeGen clangDriver clangEdit clangFrontend clangLex clangParse clangRewrite clangRewriteFrontend clangSema clangSerialization ) set(LLDB_SYSTEM_LIBS) if (NOT LLDB_DISABLE_LIBEDIT) list(APPEND LLDB_SYSTEM_LIBS edit) endif() if (NOT LLDB_DISABLE_CURSES) list(APPEND LLDB_SYSTEM_LIBS ${CURSES_LIBRARIES}) if(LLVM_ENABLE_TERMINFO AND HAVE_TERMINFO) list(APPEND LLDB_SYSTEM_LIBS ${TERMINFO_LIBS}) endif() endif() if (NOT HAVE_CXX_ATOMICS64_WITHOUT_LIB ) list(APPEND LLDB_SYSTEM_LIBS atomic) endif() # On FreeBSD/NetBSD backtrace() is provided by libexecinfo, not libc. if (CMAKE_SYSTEM_NAME MATCHES "FreeBSD" OR CMAKE_SYSTEM_NAME MATCHES "NetBSD") list(APPEND LLDB_SYSTEM_LIBS execinfo) endif() if (NOT LLDB_DISABLE_PYTHON AND NOT LLVM_BUILD_STATIC) list(APPEND LLDB_SYSTEM_LIBS ${PYTHON_LIBRARIES}) endif() list(APPEND LLDB_SYSTEM_LIBS ${system_libs}) if (LLVM_BUILD_STATIC) if (NOT LLDB_DISABLE_PYTHON) list(APPEND LLDB_SYSTEM_LIBS python2.7 util) endif() if (NOT LLDB_DISABLE_CURSES) list(APPEND LLDB_SYSTEM_LIBS gpm) endif() endif() set(LLVM_LINK_COMPONENTS ${LLVM_TARGETS_TO_BUILD} interpreter asmparser bitreader bitwriter codegen demangle ipo selectiondag bitreader mc mcjit core mcdisassembler executionengine runtimedyld option support coverage target ) add_lldb_tool(lldb-server INCLUDE_IN_FRAMEWORK Acceptor.cpp lldb-gdbserver.cpp lldb-platform.cpp lldb-server.cpp LLDBServerUtilities.cpp ) # The Darwin linker doesn't understand --start-group/--end-group. if (LLDB_LINKER_SUPPORTS_GROUPS) target_link_libraries(lldb-server -Wl,--start-group ${LLDB_USED_LIBS} -Wl,--end-group) target_link_libraries(lldb-server -Wl,--start-group ${CLANG_USED_LIBS} -Wl,--end-group) else() target_link_libraries(lldb-server ${LLDB_USED_LIBS}) target_link_libraries(lldb-server ${CLANG_USED_LIBS}) endif() -llvm_config(lldb-server ${LLVM_LINK_COMPONENTS}) +if(NOT LLVM_LINK_LLVM_DYLIB) + # This is necessary in !LLVM_LINK_LLVM_DYLIB as LLDB's libs do not track their + # dependencies properly. It is conditional because in a LLVM_LINK_LLVM_DYLIB + # build it would introduce duplicate symbols (add_lldb_tool links to libLLVM, + # and this would add the individual .a files as well). + llvm_config(lldb-server ${LLVM_LINK_COMPONENTS}) +endif() target_link_libraries(lldb-server ${LLDB_SYSTEM_LIBS}) set_target_properties(lldb-server PROPERTIES VERSION ${LLDB_VERSION}) Index: vendor/lldb/dist/unittests/Core/CMakeLists.txt =================================================================== --- vendor/lldb/dist/unittests/Core/CMakeLists.txt (revision 312182) +++ vendor/lldb/dist/unittests/Core/CMakeLists.txt (revision 312183) @@ -1,9 +1,10 @@ add_lldb_unittest(LLDBCoreTests ArchSpecTest.cpp BroadcasterTest.cpp DataExtractorTest.cpp + ErrorTest.cpp ListenerTest.cpp ScalarTest.cpp StructuredDataTest.cpp TimerTest.cpp ) Index: vendor/lldb/dist/unittests/Core/ErrorTest.cpp =================================================================== --- vendor/lldb/dist/unittests/Core/ErrorTest.cpp (nonexistent) +++ vendor/lldb/dist/unittests/Core/ErrorTest.cpp (revision 312183) @@ -0,0 +1,19 @@ +//===-- ErrorTest.cpp -------------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" +#include "lldb/Core/Error.h" + +using namespace lldb_private; + +TEST(ErrorTest, Formatv) { + EXPECT_EQ("", llvm::formatv("{0}", Error()).str()); + EXPECT_EQ("Hello Error", llvm::formatv("{0}", Error("Hello Error")).str()); + EXPECT_EQ("Hello", llvm::formatv("{0:5}", Error("Hello Error")).str()); +} Property changes on: vendor/lldb/dist/unittests/Core/ErrorTest.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property Index: vendor/lldb/dist/unittests/Symbol/CMakeLists.txt =================================================================== --- vendor/lldb/dist/unittests/Symbol/CMakeLists.txt (revision 312182) +++ vendor/lldb/dist/unittests/Symbol/CMakeLists.txt (revision 312183) @@ -1,3 +1,4 @@ add_lldb_unittest(SymbolTests TestClangASTContext.cpp + TestType.cpp ) Index: vendor/lldb/dist/unittests/Symbol/TestType.cpp =================================================================== --- vendor/lldb/dist/unittests/Symbol/TestType.cpp (nonexistent) +++ vendor/lldb/dist/unittests/Symbol/TestType.cpp (revision 312183) @@ -0,0 +1,51 @@ +//===-- TestType.cpp --------------------------------------------*- C++ -*-===// +// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "gtest/gtest.h" + +#include "lldb/Symbol/Type.h" + +using namespace lldb; +using namespace lldb_private; + +namespace { +void TestGetTypeScopeAndBasenameHelper(const char *full_type, + bool expected_is_scoped, + const char *expected_scope, + const char *expected_name) { + llvm::StringRef scope, name; + lldb::TypeClass type_class; + bool is_scoped = + Type::GetTypeScopeAndBasename(full_type, scope, name, type_class); + EXPECT_EQ(is_scoped, expected_is_scoped); + if (expected_is_scoped) { + EXPECT_EQ(scope, expected_scope); + EXPECT_EQ(name, expected_name); + } +} +}; + +TEST(Type, GetTypeScopeAndBasename) { + TestGetTypeScopeAndBasenameHelper("int", false, "", ""); + TestGetTypeScopeAndBasenameHelper("std::string", true, "std::", "string"); + TestGetTypeScopeAndBasenameHelper("std::set", true, "std::", "set"); + TestGetTypeScopeAndBasenameHelper("std::set>", true, + "std::", "set>"); + TestGetTypeScopeAndBasenameHelper("std::string::iterator", true, + "std::string::", "iterator"); + TestGetTypeScopeAndBasenameHelper("std::set::iterator", true, + "std::set::", "iterator"); + TestGetTypeScopeAndBasenameHelper( + "std::set>::iterator", true, + "std::set>::", "iterator"); + TestGetTypeScopeAndBasenameHelper( + "std::set>::iterator", true, + "std::set>::", "iterator"); +} Property changes on: vendor/lldb/dist/unittests/Symbol/TestType.cpp ___________________________________________________________________ Added: svn:eol-style ## -0,0 +1 ## +native \ No newline at end of property Added: svn:keywords ## -0,0 +1 ## +FreeBSD=%H \ No newline at end of property Added: svn:mime-type ## -0,0 +1 ## +text/plain \ No newline at end of property