diff options
Diffstat (limited to 'source/Core')
28 files changed, 5676 insertions, 558 deletions
diff --git a/source/Core/Address.cpp b/source/Core/Address.cpp index 8d599d80ad4f..1e79f332ffc8 100644 --- a/source/Core/Address.cpp +++ b/source/Core/Address.cpp @@ -12,7 +12,6 @@ #include "lldb/Core/Section.h" #include "lldb/Symbol/Block.h" #include "lldb/Symbol/ObjectFile.h" -#include "lldb/Symbol/Type.h" #include "lldb/Symbol/Variable.h" #include "lldb/Symbol/VariableList.h" #include "lldb/Target/ExecutionContext.h" @@ -281,6 +280,12 @@ Address::GetFileAddress () const // address by adding the file base address to our offset return sect_file_addr + m_offset; } + else if (SectionWasDeleted()) + { + // Used to have a valid section but it got deleted so the + // offset doesn't mean anything without the section + return LLDB_INVALID_ADDRESS; + } // No section, we just return the offset since it is the value in this case return m_offset; } @@ -289,25 +294,33 @@ addr_t Address::GetLoadAddress (Target *target) const { SectionSP section_sp (GetSection()); - if (!section_sp) - { - // No section, we just return the offset since it is the value in this case - return m_offset; - } - - if (target) + if (section_sp) { - addr_t sect_load_addr = section_sp->GetLoadBaseAddress (target); - - if (sect_load_addr != LLDB_INVALID_ADDRESS) + if (target) { - // We have a valid file range, so we can return the file based - // address by adding the file base address to our offset - return sect_load_addr + m_offset; + addr_t sect_load_addr = section_sp->GetLoadBaseAddress (target); + + if (sect_load_addr != LLDB_INVALID_ADDRESS) + { + // We have a valid file range, so we can return the file based + // address by adding the file base address to our offset + return sect_load_addr + m_offset; + } } } - // The section isn't resolved or no process was supplied so we can't - // return a valid file address. + else if (SectionWasDeleted()) + { + // Used to have a valid section but it got deleted so the + // offset doesn't mean anything without the section + return LLDB_INVALID_ADDRESS; + } + else + { + // We don't have a section so the offset is the load address + return m_offset; + } + // The section isn't resolved or an invalid target was passed in + // so we can't return a valid load address. return LLDB_INVALID_ADDRESS; } @@ -767,6 +780,19 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum return true; } +bool +Address::SectionWasDeleted() const +{ + lldb::SectionWP empty_section_wp; + + // If either call to "std::weak_ptr::owner_before(...) value returns true, this + // indicates that m_section_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 unloaded/deleted, + // or if the address doesn't have a valid reference to a section. + return empty_section_wp.owner_before(m_section_wp) || m_section_wp.owner_before(empty_section_wp); +} + uint32_t Address::CalculateSymbolContext (SymbolContext *sc, uint32_t resolve_scope) const { diff --git a/source/Core/ArchSpec.cpp b/source/Core/ArchSpec.cpp index 27d62c358bbf..f2eb3751a4b5 100644 --- a/source/Core/ArchSpec.cpp +++ b/source/Core/ArchSpec.cpp @@ -14,6 +14,7 @@ #include <string> +#include "llvm/Support/COFF.h" #include "llvm/Support/ELF.h" #include "llvm/Support/Host.h" #include "llvm/Support/MachO.h" @@ -55,6 +56,7 @@ static const CoreDefinition g_core_definitions[ArchSpec::kNumCores] = { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv5e , "armv5e" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv5t , "armv5t" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv6 , "armv6" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv6m , "armv6m" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7 , "armv7" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7f , "armv7f" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::arm , ArchSpec::eCore_arm_armv7s , "armv7s" }, @@ -67,13 +69,15 @@ static const CoreDefinition g_core_definitions[ArchSpec::kNumCores] = { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv5 , "thumbv5" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv5e , "thumbv5e" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv6 , "thumbv6" }, + { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv6m , "thumbv6m" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7 , "thumbv7" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7f , "thumbv7f" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7s , "thumbv7s" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7k , "thumbv7k" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7m , "thumbv7m" }, { eByteOrderLittle, 4, 2, 4, llvm::Triple::thumb , ArchSpec::eCore_thumbv7em , "thumbv7em" }, - + + { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64 , "mips64" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_generic , "ppc" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc601 , "ppc601" }, @@ -154,58 +158,60 @@ ArchSpec::AutoComplete (const char *name, StringList &matches) #define SUBTYPE_MASK 0x00FFFFFFu static const ArchDefinitionEntry g_macho_arch_entries[] = { - { ArchSpec::eCore_arm_generic , llvm::MachO::CPUTypeARM , CPU_ANY, UINT32_MAX , UINT32_MAX }, - { ArchSpec::eCore_arm_generic , llvm::MachO::CPUTypeARM , 0 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv4 , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv4t , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv6 , llvm::MachO::CPUTypeARM , 6 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv5 , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv5e , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv5t , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_xscale , llvm::MachO::CPUTypeARM , 8 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv7 , llvm::MachO::CPUTypeARM , 9 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv7f , llvm::MachO::CPUTypeARM , 10 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv7s , llvm::MachO::CPUTypeARM , 11 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv7k , llvm::MachO::CPUTypeARM , 12 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv7m , llvm::MachO::CPUTypeARM , 15 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_arm_armv7em , llvm::MachO::CPUTypeARM , 16 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumb , llvm::MachO::CPUTypeARM , 0 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv4t , llvm::MachO::CPUTypeARM , 5 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv5 , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv5e , llvm::MachO::CPUTypeARM , 7 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv6 , llvm::MachO::CPUTypeARM , 6 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv7 , llvm::MachO::CPUTypeARM , 9 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv7f , llvm::MachO::CPUTypeARM , 10 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv7s , llvm::MachO::CPUTypeARM , 11 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv7k , llvm::MachO::CPUTypeARM , 12 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv7m , llvm::MachO::CPUTypeARM , 15 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_thumbv7em , llvm::MachO::CPUTypeARM , 16 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_generic , llvm::MachO::CPUTypePowerPC , CPU_ANY, UINT32_MAX , UINT32_MAX }, - { ArchSpec::eCore_ppc_generic , llvm::MachO::CPUTypePowerPC , 0 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc601 , llvm::MachO::CPUTypePowerPC , 1 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc602 , llvm::MachO::CPUTypePowerPC , 2 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc603 , llvm::MachO::CPUTypePowerPC , 3 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc603e , llvm::MachO::CPUTypePowerPC , 4 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc603ev , llvm::MachO::CPUTypePowerPC , 5 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc604 , llvm::MachO::CPUTypePowerPC , 6 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc604e , llvm::MachO::CPUTypePowerPC , 7 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc620 , llvm::MachO::CPUTypePowerPC , 8 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc750 , llvm::MachO::CPUTypePowerPC , 9 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc7400 , llvm::MachO::CPUTypePowerPC , 10 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc7450 , llvm::MachO::CPUTypePowerPC , 11 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc_ppc970 , llvm::MachO::CPUTypePowerPC , 100 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc64_generic , llvm::MachO::CPUTypePowerPC64 , 0 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_ppc64_ppc970_64 , llvm::MachO::CPUTypePowerPC64 , 100 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPUTypeI386 , 3 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_32_i486 , llvm::MachO::CPUTypeI386 , 4 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_32_i486sx , llvm::MachO::CPUTypeI386 , 0x84 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPUTypeI386 , CPU_ANY, UINT32_MAX , UINT32_MAX }, - { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , 3 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , 4 , UINT32_MAX , SUBTYPE_MASK }, - { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPUTypeX86_64 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_arm_generic , llvm::MachO::CPU_TYPE_ARM , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_arm_generic , llvm::MachO::CPU_TYPE_ARM , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv4 , llvm::MachO::CPU_TYPE_ARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv4t , llvm::MachO::CPU_TYPE_ARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv6 , llvm::MachO::CPU_TYPE_ARM , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv6m , llvm::MachO::CPU_TYPE_ARM , 14 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5 , llvm::MachO::CPU_TYPE_ARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5e , llvm::MachO::CPU_TYPE_ARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv5t , llvm::MachO::CPU_TYPE_ARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_xscale , llvm::MachO::CPU_TYPE_ARM , 8 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7 , llvm::MachO::CPU_TYPE_ARM , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7f , llvm::MachO::CPU_TYPE_ARM , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7s , llvm::MachO::CPU_TYPE_ARM , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7k , llvm::MachO::CPU_TYPE_ARM , 12 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7m , llvm::MachO::CPU_TYPE_ARM , 15 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_arm_armv7em , llvm::MachO::CPU_TYPE_ARM , 16 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumb , llvm::MachO::CPU_TYPE_ARM , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv4t , llvm::MachO::CPU_TYPE_ARM , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv5 , llvm::MachO::CPU_TYPE_ARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv5e , llvm::MachO::CPU_TYPE_ARM , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv6 , llvm::MachO::CPU_TYPE_ARM , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv6m , llvm::MachO::CPU_TYPE_ARM , 14 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7 , llvm::MachO::CPU_TYPE_ARM , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7f , llvm::MachO::CPU_TYPE_ARM , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7s , llvm::MachO::CPU_TYPE_ARM , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7k , llvm::MachO::CPU_TYPE_ARM , 12 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7m , llvm::MachO::CPU_TYPE_ARM , 15 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_thumbv7em , llvm::MachO::CPU_TYPE_ARM , 16 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_generic , llvm::MachO::CPU_TYPE_POWERPC , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_ppc_generic , llvm::MachO::CPU_TYPE_POWERPC , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc601 , llvm::MachO::CPU_TYPE_POWERPC , 1 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc602 , llvm::MachO::CPU_TYPE_POWERPC , 2 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603 , llvm::MachO::CPU_TYPE_POWERPC , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603e , llvm::MachO::CPU_TYPE_POWERPC , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc603ev , llvm::MachO::CPU_TYPE_POWERPC , 5 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc604 , llvm::MachO::CPU_TYPE_POWERPC , 6 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc604e , llvm::MachO::CPU_TYPE_POWERPC , 7 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc620 , llvm::MachO::CPU_TYPE_POWERPC , 8 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc750 , llvm::MachO::CPU_TYPE_POWERPC , 9 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc7400 , llvm::MachO::CPU_TYPE_POWERPC , 10 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc7450 , llvm::MachO::CPU_TYPE_POWERPC , 11 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc_ppc970 , llvm::MachO::CPU_TYPE_POWERPC , 100 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc64_generic , llvm::MachO::CPU_TYPE_POWERPC64 , 0 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_ppc64_ppc970_64 , llvm::MachO::CPU_TYPE_POWERPC64 , 100 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPU_TYPE_I386 , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i486 , llvm::MachO::CPU_TYPE_I386 , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i486sx , llvm::MachO::CPU_TYPE_I386 , 0x84 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_32_i386 , llvm::MachO::CPU_TYPE_I386 , CPU_ANY, UINT32_MAX , UINT32_MAX }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , 3 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , 4 , UINT32_MAX , SUBTYPE_MASK }, + { ArchSpec::eCore_x86_64_x86_64 , llvm::MachO::CPU_TYPE_X86_64 , CPU_ANY, UINT32_MAX , UINT32_MAX }, // Catch any unknown mach architectures so we can always use the object and symbol mach-o files - { ArchSpec::eCore_uknownMach32 , 0 , 0 , 0xFF000000u, 0x00000000u }, - { ArchSpec::eCore_uknownMach64 , llvm::MachO::CPUArchABI64 , 0 , 0xFF000000u, 0x00000000u } + { ArchSpec::eCore_uknownMach32 , 0 , 0 , 0xFF000000u, 0x00000000u }, + { ArchSpec::eCore_uknownMach64 , llvm::MachO::CPU_ARCH_ABI64 , 0 , 0xFF000000u, 0x00000000u } }; static const ArchDefinition g_macho_arch_def = { eArchTypeMachO, @@ -228,7 +234,8 @@ static const ArchDefinitionEntry g_elf_arch_entries[] = { ArchSpec::eCore_ppc64_generic , llvm::ELF::EM_PPC64 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC64 { ArchSpec::eCore_arm_generic , llvm::ELF::EM_ARM , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARM { ArchSpec::eCore_sparc9_generic , llvm::ELF::EM_SPARCV9, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // SPARC V9 - { ArchSpec::eCore_x86_64_x86_64 , llvm::ELF::EM_X86_64 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu } // AMD64 + { ArchSpec::eCore_x86_64_x86_64 , llvm::ELF::EM_X86_64 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // AMD64 + { ArchSpec::eCore_mips64 , llvm::ELF::EM_MIPS , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu } // MIPS }; static const ArchDefinition g_elf_arch_def = { @@ -238,11 +245,30 @@ static const ArchDefinition g_elf_arch_def = { "elf", }; +static const ArchDefinitionEntry g_coff_arch_entries[] = +{ + { ArchSpec::eCore_x86_32_i386 , llvm::COFF::IMAGE_FILE_MACHINE_I386 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Intel 80386 + { ArchSpec::eCore_ppc_generic , llvm::COFF::IMAGE_FILE_MACHINE_POWERPC , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC + { ArchSpec::eCore_ppc_generic , llvm::COFF::IMAGE_FILE_MACHINE_POWERPCFP, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC (with FPU) + { ArchSpec::eCore_arm_generic , llvm::COFF::IMAGE_FILE_MACHINE_ARM , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARM + { ArchSpec::eCore_arm_armv7 , llvm::COFF::IMAGE_FILE_MACHINE_ARMV7 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARMv7 + { ArchSpec::eCore_thumb , llvm::COFF::IMAGE_FILE_MACHINE_THUMB , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARMv7 + { ArchSpec::eCore_x86_64_x86_64, llvm::COFF::IMAGE_FILE_MACHINE_AMD64 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu } // AMD64 +}; + +static const ArchDefinition g_coff_arch_def = { + eArchTypeCOFF, + sizeof(g_coff_arch_entries)/sizeof(g_coff_arch_entries[0]), + g_coff_arch_entries, + "pe-coff", +}; + //===----------------------------------------------------------------------===// // Table of all ArchDefinitions static const ArchDefinition *g_arch_definitions[] = { &g_macho_arch_def, - &g_elf_arch_def + &g_elf_arch_def, + &g_coff_arch_def }; static const size_t k_num_arch_definitions = @@ -863,6 +889,15 @@ cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_in return true; break; + case ArchSpec::eCore_arm_armv6m: + if (!enforce_exact_match) + { + try_inverse = false; + if (core2 == ArchSpec::eCore_arm_armv7) + return true; + } + break; + case ArchSpec::eCore_arm_armv7m: case ArchSpec::eCore_arm_armv7em: case ArchSpec::eCore_arm_armv7f: diff --git a/source/Core/Communication.cpp b/source/Core/Communication.cpp index 7f40e6520204..6ea7a11426be 100644 --- a/source/Core/Communication.cpp +++ b/source/Core/Communication.cpp @@ -333,8 +333,8 @@ Communication::ReadThreadIsRunning () return m_read_thread_enabled; } -void * -Communication::ReadThread (void *p) +lldb::thread_result_t +Communication::ReadThread (lldb::thread_arg_t p) { Communication *comm = (Communication *)p; diff --git a/source/Core/ConnectionFileDescriptor.cpp b/source/Core/ConnectionFileDescriptor.cpp index e320bda2fcdc..8e80543b857b 100644 --- a/source/Core/ConnectionFileDescriptor.cpp +++ b/source/Core/ConnectionFileDescriptor.cpp @@ -15,10 +15,16 @@ #endif #include "lldb/Core/ConnectionFileDescriptor.h" +#include "lldb/Host/Config.h" +#include "lldb/Host/SocketAddress.h" // C Includes #include <errno.h> #include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <sys/types.h> +#ifndef LLDB_DISABLE_POSIX #include <arpa/inet.h> #include <netdb.h> #include <netinet/in.h> @@ -26,13 +32,17 @@ #include <sys/socket.h> #include <sys/un.h> #include <termios.h> -#include <sys/types.h> -#include <string.h> -#include <stdlib.h> #include <unistd.h> +#endif +#ifdef _WIN32 +#include "lldb/Host/windows/windows.h" +#include <winsock2.h> +#include <WS2tcpip.h> +#endif // C++ Includes // Other libraries and framework includes +#include "llvm/Support/ErrorHandling.h" #if defined(__APPLE__) #include "llvm/ADT/SmallVector.h" #endif @@ -84,7 +94,7 @@ ConnectionFileDescriptor::ConnectionFileDescriptor () : m_fd_recv (-1), m_fd_send_type (eFDTypeFile), m_fd_recv_type (eFDTypeFile), - m_udp_send_sockaddr (), + m_udp_send_sockaddr (new SocketAddress()), m_should_close_fd (false), m_socket_timeout_usec(0), m_pipe_read(-1), @@ -103,7 +113,7 @@ ConnectionFileDescriptor::ConnectionFileDescriptor (int fd, bool owns_fd) : m_fd_recv (fd), m_fd_send_type (eFDTypeFile), m_fd_recv_type (eFDTypeFile), - m_udp_send_sockaddr (), + m_udp_send_sockaddr (new SocketAddress()), m_should_close_fd (owns_fd), m_socket_timeout_usec(0), m_pipe_read(-1), @@ -135,11 +145,15 @@ ConnectionFileDescriptor::OpenCommandPipe () Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); // Make the command file descriptor here: int filedes[2]; +#ifndef LLDB_DISABLE_POSIX int result = pipe (filedes); +#else + int result = -1; +#endif if (result != 0) { if (log) - log->Printf ("%p ConnectionFileDescriptor::ConnectionFileDescriptor () - could not make pipe: %s", + log->Printf ("%p ConnectionFileDescriptor::OpenCommandPipe () - could not make pipe: %s", this, strerror(errno)); } @@ -147,21 +161,39 @@ ConnectionFileDescriptor::OpenCommandPipe () { m_pipe_read = filedes[0]; m_pipe_write = filedes[1]; + if (log) + log->Printf ("%p ConnectionFileDescriptor::OpenCommandPipe() - success readfd=%d writefd=%d", + this, + m_pipe_read, + m_pipe_write); } } void ConnectionFileDescriptor::CloseCommandPipe () { + Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); + if (log) + log->Printf ("%p ConnectionFileDescriptor::CloseCommandPipe()", + this); + if (m_pipe_read != -1) { +#ifdef _MSC_VER + llvm_unreachable("pipe close unsupported in MSVC"); +#else close (m_pipe_read); +#endif m_pipe_read = -1; } if (m_pipe_write != -1) { +#ifdef _MSC_VER + llvm_unreachable("pipe close unsupported in MSVC"); +#else close (m_pipe_write); +#endif m_pipe_write = -1; } } @@ -223,7 +255,11 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) // get the flags from the file descriptor and making sure it // isn't a bad fd. errno = 0; +#ifndef LLDB_DISABLE_POSIX int flags = ::fcntl (m_fd_send, F_GETFL, 0); +#else + int flags = -1; +#endif if (flags == -1 || errno == EBADF) { if (error_ptr) @@ -262,6 +298,7 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) { // file:///PATH const char *path = s + strlen("file://"); +#ifndef LLDB_DISABLE_POSIX do { m_fd_send = m_fd_recv = ::open (path, O_RDWR); @@ -304,6 +341,9 @@ ConnectionFileDescriptor::Connect (const char *s, Error *error_ptr) } m_should_close_fd = true; return eConnectionStatusSuccess; +#else + return eConnectionStatusError; +#endif } if (error_ptr) error_ptr->SetErrorStringWithFormat ("unsupported connection URL: '%s'", s); @@ -344,27 +384,30 @@ ConnectionFileDescriptor::Disconnect (Error *error_ptr) { if (m_pipe_write != -1 ) { - write (m_pipe_write, "q", 1); - close (m_pipe_write); - m_pipe_write = -1; + int result; + result = write (m_pipe_write, "q", 1); + if (log) + log->Printf ("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, sent 'q' to %d, result = %d.", this, m_pipe_write, result); } + else if (log) + log->Printf ("%p ConnectionFileDescriptor::Disconnect(): Couldn't get the lock, but no command pipe is available.", this); locker.Lock (m_mutex); } - + if (m_should_close_fd == true) { if (m_fd_send == m_fd_recv) { - status = Close (m_fd_send, error_ptr); + status = Close (m_fd_send, m_fd_send_type, error_ptr); } else { // File descriptors are the different, close both if needed if (m_fd_send >= 0) - status = Close (m_fd_send, error_ptr); + status = Close (m_fd_send, m_fd_send_type, error_ptr); if (m_fd_recv >= 0) { - ConnectionStatus recv_status = Close (m_fd_recv, error_ptr); + ConnectionStatus recv_status = Close (m_fd_recv, m_fd_recv_type, error_ptr); if (status == eConnectionStatusSuccess) status = recv_status; } @@ -420,7 +463,21 @@ ConnectionFileDescriptor::Read (void *dst, { do { +#ifndef LLDB_DISABLE_POSIX bytes_read = ::read (m_fd_recv, dst, dst_len); +#else + switch (m_fd_send_type) { + case eFDTypeSocket: + case eFDTypeSocketUDP: + bytes_read = ::recv (m_fd_recv, (char*)dst, dst_len, 0); + break; + default: + bytes_read = -1; + break; + + } + +#endif } while (bytes_read < 0 && errno == EINTR); } @@ -494,6 +551,13 @@ ConnectionFileDescriptor::Read (void *dst, case ETIMEDOUT: // A transmission timeout occurs during a read attempt on a socket. status = eConnectionStatusTimedOut; return 0; + + default: + if (log) + log->Printf("%p ConnectionFileDescriptor::Read (), unexpected error: %s", this, strerror(error_value)); + status = eConnectionStatusError; + break; // Break to close.... + } return 0; @@ -523,30 +587,31 @@ ConnectionFileDescriptor::Write (const void *src, size_t src_len, ConnectionStat switch (m_fd_send_type) { +#ifndef LLDB_DISABLE_POSIX case eFDTypeFile: // Other FD requireing read/write do { bytes_sent = ::write (m_fd_send, src, src_len); } while (bytes_sent < 0 && errno == EINTR); break; - +#endif case eFDTypeSocket: // Socket requiring send/recv do { - bytes_sent = ::send (m_fd_send, src, src_len, 0); + bytes_sent = ::send (m_fd_send, (char*)src, src_len, 0); } while (bytes_sent < 0 && errno == EINTR); break; case eFDTypeSocketUDP: // Unconnected UDP socket requiring sendto/recvfrom - assert (m_udp_send_sockaddr.GetFamily() != 0); + assert (m_udp_send_sockaddr->GetFamily() != 0); do { bytes_sent = ::sendto (m_fd_send, - src, + (char*)src, src_len, 0, - m_udp_send_sockaddr, - m_udp_send_sockaddr.GetLength()); + *m_udp_send_sockaddr, + m_udp_send_sockaddr->GetLength()); } while (bytes_sent < 0 && errno == EINTR); break; } @@ -656,7 +721,8 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt { TimeValue time_value; time_value.OffsetWithMicroSeconds (timeout_usec); - tv = time_value.GetAsTimeVal(); + tv.tv_sec = time_value.seconds(); + tv.tv_usec = time_value.microseconds(); tv_ptr = &tv; } @@ -806,7 +872,8 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt { TimeValue time_value; time_value.OffsetWithMicroSeconds (timeout_usec); - tv = time_value.GetAsTimeVal(); + tv.tv_sec = time_value.seconds(); + tv.tv_usec = time_value.microseconds(); tv_ptr = &tv; } @@ -821,7 +888,9 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt // If this assert fires off on MacOSX, we will need to switch to using // libdispatch to read from file descriptors because poll() is causing // kernel panics and if we exceed FD_SETSIZE we will have no choice... +#ifndef _MSC_VER assert (data_fd < FD_SETSIZE); +#endif const bool have_pipe_fd = pipe_fd >= 0; @@ -1077,7 +1146,7 @@ ConnectionFileDescriptor::BytesAvailable (uint32_t timeout_usec, Error *error_pt #endif ConnectionStatus -ConnectionFileDescriptor::Close (int& fd, Error *error_ptr) +ConnectionFileDescriptor::Close (int& fd, FDType type, Error *error_ptr) { if (error_ptr) error_ptr->Clear(); @@ -1093,7 +1162,11 @@ ConnectionFileDescriptor::Close (int& fd, Error *error_ptr) Log *log(lldb_private::GetLogIfAnyCategoriesSet (LIBLLDB_LOG_CONNECTION)); if (log) log->Printf ("%p ConnectionFileDescriptor::Close (fd = %i)", this,fd); - +#if _WIN32 + if (type != eFDTypeFile) + success = closesocket(fd) == 0; + else +#endif success = ::close (fd) == 0; // A reference to a FD was passed in, set it to an invalid value fd = -1; @@ -1115,6 +1188,7 @@ ConnectionFileDescriptor::Close (int& fd, Error *error_ptr) ConnectionStatus ConnectionFileDescriptor::NamedSocketAccept (const char *socket_name, Error *error_ptr) { +#ifndef LLDB_DISABLE_POSIX ConnectionStatus result = eConnectionStatusError; struct sockaddr_un saddr_un; @@ -1131,7 +1205,7 @@ ConnectionFileDescriptor::NamedSocketAccept (const char *socket_name, Error *err saddr_un.sun_family = AF_UNIX; ::strncpy(saddr_un.sun_path, socket_name, sizeof(saddr_un.sun_path) - 1); saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; -#if defined(__APPLE__) || defined(__FreeBSD__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) saddr_un.sun_len = SUN_LEN (&saddr_un); #endif @@ -1157,13 +1231,17 @@ ConnectionFileDescriptor::NamedSocketAccept (const char *socket_name, Error *err error_ptr->SetErrorToErrno(); } // We are done with the listen port - Close (listen_socket, NULL); + Close (listen_socket, eFDTypeSocket, NULL); return result; +#else + return eConnectionStatusError; +#endif } ConnectionStatus ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *error_ptr) { +#ifndef LLDB_DISABLE_POSIX Disconnect (NULL); m_fd_send_type = m_fd_recv_type = eFDTypeSocket; @@ -1180,7 +1258,7 @@ ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *er saddr_un.sun_family = AF_UNIX; ::strncpy(saddr_un.sun_path, socket_name, sizeof(saddr_un.sun_path) - 1); saddr_un.sun_path[sizeof(saddr_un.sun_path) - 1] = '\0'; -#if defined(__APPLE__) || defined(__FreeBSD__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) saddr_un.sun_len = SUN_LEN (&saddr_un); #endif @@ -1194,6 +1272,9 @@ ConnectionFileDescriptor::NamedSocketConnect (const char *socket_name, Error *er if (error_ptr) error_ptr->Clear(); return eConnectionStatusSuccess; +#else + return eConnectionStatusError; +#endif } ConnectionStatus @@ -1224,7 +1305,7 @@ ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_p { if (error_ptr) error_ptr->SetErrorToErrno(); - Close (listen_port, NULL); + Close (listen_port, eFDTypeSocket, NULL); return eConnectionStatusError; } @@ -1233,7 +1314,7 @@ ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_p { if (error_ptr) error_ptr->SetErrorToErrno(); - Close (listen_port, NULL); + Close (listen_port, eFDTypeSocket, NULL); return eConnectionStatusError; } @@ -1242,13 +1323,13 @@ ConnectionFileDescriptor::SocketListen (uint16_t listen_port_num, Error *error_p { if (error_ptr) error_ptr->SetErrorToErrno(); - Close (listen_port, NULL); + Close (listen_port, eFDTypeSocket, NULL); return eConnectionStatusError; } } // We are done with the listen port - Close (listen_port, NULL); + Close (listen_port, eFDTypeSocket, NULL); m_should_close_fd = true; @@ -1408,7 +1489,7 @@ ConnectionFileDescriptor::ConnectUDP (const char *host_and_port, Error *error_pt if (m_fd_send != -1) { - m_udp_send_sockaddr = service_info_ptr; + *m_udp_send_sockaddr = service_info_ptr; break; } else @@ -1430,18 +1511,18 @@ ConnectionFileDescriptor::ConnectUDP (const char *host_and_port, Error *error_pt return eConnectionStatusSuccess; } -#if defined(__MINGW32__) || defined(__MINGW64__) +#if defined(_WIN32) typedef const char * set_socket_option_arg_type; typedef char * get_socket_option_arg_type; -#else // #if defined(__MINGW32__) || defined(__MINGW64__) +#else // #if defined(_WIN32) typedef const void * set_socket_option_arg_type; typedef void * get_socket_option_arg_type; -#endif // #if defined(__MINGW32__) || defined(__MINGW64__) +#endif // #if defined(_WIN32) int ConnectionFileDescriptor::GetSocketOption(int fd, int level, int option_name, int &option_value) { - get_socket_option_arg_type option_value_p = static_cast<get_socket_option_arg_type>(&option_value); + get_socket_option_arg_type option_value_p = reinterpret_cast<get_socket_option_arg_type>(&option_value); socklen_t option_value_size = sizeof(int); return ::getsockopt(fd, level, option_name, option_value_p, &option_value_size); } @@ -1449,7 +1530,7 @@ ConnectionFileDescriptor::GetSocketOption(int fd, int level, int option_name, in int ConnectionFileDescriptor::SetSocketOption(int fd, int level, int option_name, int option_value) { - set_socket_option_arg_type option_value_p = static_cast<get_socket_option_arg_type>(&option_value); + set_socket_option_arg_type option_value_p = reinterpret_cast<get_socket_option_arg_type>(&option_value); return ::setsockopt(fd, level, option_name, option_value_p, sizeof(option_value)); } @@ -1487,7 +1568,7 @@ ConnectionFileDescriptor::SetSocketReceiveTimeout (uint32_t timeout_usec) timeout.tv_sec = timeout_usec / TimeValue::MicroSecPerSec; timeout.tv_usec = timeout_usec % TimeValue::MicroSecPerSec; } - if (::setsockopt (m_fd_recv, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) == 0) + if (::setsockopt (m_fd_recv, SOL_SOCKET, SO_RCVTIMEO, reinterpret_cast<get_socket_option_arg_type>(&timeout), sizeof(timeout)) == 0) { m_socket_timeout_usec = timeout_usec; return true; diff --git a/source/Core/ConnectionMachPort.cpp b/source/Core/ConnectionMachPort.cpp index ca818d405a20..4a090dbe5ec8 100644 --- a/source/Core/ConnectionMachPort.cpp +++ b/source/Core/ConnectionMachPort.cpp @@ -11,6 +11,7 @@ #include "lldb/Core/ConnectionMachPort.h" // C Includes +#include <mach/mach.h> #include <servers/bootstrap.h> // C++ Includes diff --git a/source/Core/ConnectionSharedMemory.cpp b/source/Core/ConnectionSharedMemory.cpp index 625f17a0985b..cd708c4868c9 100644 --- a/source/Core/ConnectionSharedMemory.cpp +++ b/source/Core/ConnectionSharedMemory.cpp @@ -11,12 +11,15 @@ // C Includes #include <errno.h> -#include <pthread.h> #include <stdlib.h> +#ifdef _WIN32 +#include "lldb/Host/windows/windows.h" +#else #include <sys/file.h> #include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> +#endif // C++ Includes // Other libraries and framework includes @@ -73,7 +76,12 @@ ConnectionSharedMemory::Disconnect (Error *error_ptr) m_mmap.Clear(); if (!m_name.empty()) { +#ifdef _WIN32 + close(m_fd); + m_fd = -1; +#else shm_unlink (m_name.c_str()); +#endif m_name.clear(); } return eConnectionStatusSuccess; @@ -114,6 +122,16 @@ ConnectionSharedMemory::Open (bool create, const char *name, size_t size, Error } m_name.assign (name); + +#ifdef _WIN32 + HANDLE handle; + if (create) + handle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, (DWORD)(size >> 32), (DWORD)(size), name); + else + handle = OpenFileMapping(FILE_MAP_ALL_ACCESS, FALSE, name); + + m_fd = _open_osfhandle((intptr_t)handle, 0); +#else int oflag = O_RDWR; if (create) oflag |= O_CREAT; @@ -121,6 +139,7 @@ ConnectionSharedMemory::Open (bool create, const char *name, size_t size, Error if (create) ::ftruncate (m_fd, size); +#endif if (m_mmap.MemoryMapFromFileDescriptor(m_fd, 0, size, true, false) == size) return eConnectionStatusSuccess; diff --git a/source/Core/ConstString.cpp b/source/Core/ConstString.cpp index 875169428d2a..ce6e51108db5 100644 --- a/source/Core/ConstString.cpp +++ b/source/Core/ConstString.cpp @@ -259,8 +259,8 @@ int ConstString::Compare (const ConstString& lhs, const ConstString& rhs) { // If the iterators are the same, this is the same string - register const char *lhs_cstr = lhs.m_string; - register const char *rhs_cstr = rhs.m_string; + const char *lhs_cstr = lhs.m_string; + const char *rhs_cstr = rhs.m_string; if (lhs_cstr == rhs_cstr) return 0; if (lhs_cstr && rhs_cstr) @@ -319,7 +319,7 @@ bool ConstString::GetMangledCounterpart (ConstString &counterpart) const { counterpart.m_string = StringPool().GetMangledCounterpart(m_string); - return counterpart; + return (bool)counterpart; } void diff --git a/source/Core/DataBufferMemoryMap.cpp b/source/Core/DataBufferMemoryMap.cpp index a4382a0c67e1..008b736ef726 100644 --- a/source/Core/DataBufferMemoryMap.cpp +++ b/source/Core/DataBufferMemoryMap.cpp @@ -12,7 +12,11 @@ #include <fcntl.h> #include <limits.h> #include <sys/stat.h> +#ifdef _WIN32 +#include "lldb/Host/windows/windows.h" +#else #include <sys/mman.h> +#endif #include "lldb/Core/DataBufferMemoryMap.h" #include "lldb/Core/Error.h" @@ -86,7 +90,11 @@ DataBufferMemoryMap::Clear() Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP)); if (log) log->Printf("DataBufferMemoryMap::Clear() m_mmap_addr = %p, m_mmap_size = %zu", m_mmap_addr, m_mmap_size); +#ifdef _WIN32 + UnmapViewOfFile(m_mmap_addr); +#else ::munmap((void *)m_mmap_addr, m_mmap_size); +#endif m_mmap_addr = NULL; m_mmap_size = 0; m_data = NULL; @@ -139,7 +147,17 @@ DataBufferMemoryMap::MemoryMapFromFileSpec (const FileSpec* filespec, Clear(); return 0; } - +
+
+#ifdef _WIN32
+static size_t win32memmapalignment = 0;
+void LoadWin32MemMapAlignment ()
+{
+ SYSTEM_INFO data;
+ GetSystemInfo(&data);
+ win32memmapalignment = data.dwAllocationGranularity;
+}
+#endif //---------------------------------------------------------------------- // The file descriptor FD is assumed to already be opened as read only @@ -166,13 +184,59 @@ DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd, Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP|LIBLLDB_LOG_VERBOSE)); if (log) { +#ifdef _WIN32 + log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(fd=%p, offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i, fd_is_file=%i)", +#else log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec(fd=%i, offset=0x%" PRIx64 ", length=0x%" PRIx64 ", writeable=%i, fd_is_file=%i)", +#endif fd, offset, length, writeable, fd_is_file); } +#ifdef _WIN32 + HANDLE handle = (HANDLE)_get_osfhandle(fd); + DWORD file_size_low, file_size_high; + file_size_low = GetFileSize(handle, &file_size_high); + const size_t file_size = (file_size_high << 32) | file_size_low; + const size_t max_bytes_available = file_size - offset; + if (length == SIZE_MAX) + { + length = max_bytes_available; + } + else if (length > max_bytes_available) + { + // Cap the length if too much data was requested + length = max_bytes_available; + } + + if (length > 0) + { + HANDLE fileMapping = CreateFileMapping(handle, NULL, writeable ? PAGE_READWRITE : PAGE_READONLY, file_size_high, file_size_low, NULL); + if (fileMapping != NULL) + { + if (win32memmapalignment == 0) LoadWin32MemMapAlignment();
+ lldb::offset_t realoffset = offset;
+ lldb::offset_t delta = 0;
+ if (realoffset % win32memmapalignment != 0) {
+ realoffset = realoffset / win32memmapalignment * win32memmapalignment;
+ delta = offset - realoffset;
+ } +
+ LPVOID data = MapViewOfFile(fileMapping, writeable ? FILE_MAP_WRITE : FILE_MAP_READ, 0, realoffset, length + delta);
+ m_mmap_addr = (uint8_t *)data;
+ if (!data) {
+ Error error;
+ error.SetErrorToErrno ();
+ } else { + m_data = m_mmap_addr + delta;
+ m_size = length;
+ }
+ CloseHandle(fileMapping); + } + } +#else struct stat stat; if (::fstat(fd, &stat) == 0) { @@ -253,6 +317,7 @@ DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd, } } } +#endif } return GetByteSize (); } diff --git a/source/Core/DataExtractor.cpp b/source/Core/DataExtractor.cpp index 518faeb71ea7..d1f3c09c2305 100644 --- a/source/Core/DataExtractor.cpp +++ b/source/Core/DataExtractor.cpp @@ -935,6 +935,10 @@ DataExtractor::ExtractBytes (offset_t offset, offset_t length, ByteOrder dst_byt { if (dst_byte_order != GetByteOrder()) { + // Validate that only a word- or register-sized dst is byte swapped + assert (length == 1 || length == 2 || length == 4 || length == 8 || + length == 10 || length == 16 || length == 32); + for (uint32_t i=0; i<length; ++i) ((uint8_t*)dst)[i] = src[length - i - 1]; } @@ -945,6 +949,21 @@ DataExtractor::ExtractBytes (offset_t offset, offset_t length, ByteOrder dst_byt return 0; } +// Extract data as it exists in target memory +lldb::offset_t +DataExtractor::CopyData (offset_t offset, + offset_t length, + void *dst) const +{ + const uint8_t *src = PeekData (offset, length); + if (src) + { + ::memcpy (dst, src, length); + return length; + } + return 0; +} + // Extract data and swap if needed when doing the copy lldb::offset_t DataExtractor::CopyByteOrderedData (offset_t src_offset, @@ -963,7 +982,12 @@ DataExtractor::CopyByteOrderedData (offset_t src_offset, assert (dst_void_ptr != NULL); assert (dst_len > 0); assert (dst_byte_order == eByteOrderBig || dst_byte_order == eByteOrderLittle); - + + // Validate that only a word- or register-sized dst is byte swapped + assert (dst_byte_order == m_byte_order || dst_len == 1 || dst_len == 2 || + dst_len == 4 || dst_len == 8 || dst_len == 10 || dst_len == 16 || + dst_len == 32); + // Must have valid byte orders set in this object and for destination if (!(dst_byte_order == eByteOrderBig || dst_byte_order == eByteOrderLittle) || !(m_byte_order == eByteOrderBig || m_byte_order == eByteOrderLittle)) @@ -1330,18 +1354,22 @@ DumpAPInt (Stream *s, const DataExtractor &data, lldb::offset_t offset, lldb::of static float half2float (uint16_t half) { +#ifdef _MSC_VER + llvm_unreachable("half2float not implemented for MSVC"); +#else union{ float f; uint32_t u;}u; int32_t v = (int16_t) half; if( 0 == (v & 0x7c00)) { u.u = v & 0x80007FFFU; - return u.f * 0x1.0p125f; + return u.f * ldexpf(1, 125); } v <<= 13; u.u = v | 0x70000000U; - return u.f * 0x1.0p-112f; + return u.f * ldexpf(1, -112); +#endif } lldb::offset_t @@ -1703,29 +1731,35 @@ DataExtractor::Dump (Stream *s, case eFormatHexUppercase: { bool wantsuppercase = (item_format == eFormatHexUppercase); - if (item_byte_size <= 8) + switch (item_byte_size) { + case 1: + case 2: + case 4: + case 8: s->Printf(wantsuppercase ? "0x%*.*" PRIX64 : "0x%*.*" PRIx64, (int)(2 * item_byte_size), (int)(2 * item_byte_size), GetMaxU64Bitfield(&offset, item_byte_size, item_bit_size, item_bit_offset)); - } - else - { - assert (item_bit_size == 0 && item_bit_offset == 0); - s->PutCString("0x"); - const uint8_t *bytes = (const uint8_t* )GetData(&offset, item_byte_size); - if (bytes) + break; + default: { - uint32_t idx; - if (m_byte_order == eByteOrderBig) - { - for (idx = 0; idx < item_byte_size; ++idx) - s->Printf(wantsuppercase ? "%2.2X" : "%2.2x", bytes[idx]); - } - else + assert (item_bit_size == 0 && item_bit_offset == 0); + const uint8_t *bytes = (const uint8_t* )GetData(&offset, item_byte_size); + if (bytes) { - for (idx = 0; idx < item_byte_size; ++idx) - s->Printf(wantsuppercase ? "%2.2X" : "%2.2x", bytes[item_byte_size - 1 - idx]); + s->PutCString("0x"); + uint32_t idx; + if (m_byte_order == eByteOrderBig) + { + for (idx = 0; idx < item_byte_size; ++idx) + s->Printf(wantsuppercase ? "%2.2X" : "%2.2x", bytes[idx]); + } + else + { + for (idx = 0; idx < item_byte_size; ++idx) + s->Printf(wantsuppercase ? "%2.2X" : "%2.2x", bytes[item_byte_size - 1 - idx]); + } } } + break; } } break; diff --git a/source/Core/Debugger.cpp b/source/Core/Debugger.cpp index d1d2ebb0550b..3941d82d47b0 100644 --- a/source/Core/Debugger.cpp +++ b/source/Core/Debugger.cpp @@ -132,6 +132,7 @@ g_properties[] = { "thread-format", OptionValue::eTypeString , true, 0 , DEFAULT_THREAD_FORMAT, NULL, "The default thread format string to use when displaying thread information." }, { "use-external-editor", OptionValue::eTypeBoolean, true, false, NULL, NULL, "Whether to use an external editor or not." }, { "use-color", OptionValue::eTypeBoolean, true, true , NULL, NULL, "Whether to use Ansi color codes or not." }, +{ "auto-one-line-summaries", OptionValue::eTypeBoolean, true, true, NULL, NULL, "If true, LLDB will automatically display small structs in one-liner format (default: true)." }, { NULL, OptionValue::eTypeInvalid, true, 0 , NULL, NULL, NULL } }; @@ -151,6 +152,7 @@ enum ePropertyThreadFormat, ePropertyUseExternalEditor, ePropertyUseColor, + ePropertyAutoOneLineSummaries }; // @@ -347,6 +349,14 @@ Debugger::GetDisassemblyLineCount () const return m_collection_sp->GetPropertyAtIndexAsSInt64 (NULL, idx, g_properties[idx].default_uint_value); } +bool +Debugger::GetAutoOneLineSummaries () const +{ + const uint32_t idx = ePropertyAutoOneLineSummaries; + return m_collection_sp->GetPropertyAtIndexAsBoolean (NULL, idx, true); + +} + #pragma mark Debugger //const DebuggerPropertiesSP & @@ -821,7 +831,6 @@ Debugger::GetSelectedExecutionContext () } } return exe_ctx; - } InputReaderSP @@ -1720,7 +1729,6 @@ FormatPromptRecurse do_deref_pointer = false; } - // <rdar://problem/11338654> // we do not want to use the summary for a bitfield of type T:n // if we were originally dealing with just a T - that would get // us into an endless recursion @@ -1902,12 +1910,12 @@ FormatPromptRecurse if (var_name_begin[0] == 'n' || var_name_begin[5] == 'f') { format_file_spec.GetFilename() = exe_module->GetFileSpec().GetFilename(); - var_success = format_file_spec; + var_success = (bool)format_file_spec; } else { format_file_spec = exe_module->GetFileSpec(); - var_success = format_file_spec; + var_success = (bool)format_file_spec; } } } @@ -1983,7 +1991,7 @@ FormatPromptRecurse ValueObjectSP return_valobj_sp = StopInfo::GetReturnValueObject (stop_info_sp); if (return_valobj_sp) { - ValueObject::DumpValueObject (s, return_valobj_sp.get()); + return_valobj_sp->Dump(s); var_success = true; } } @@ -2067,12 +2075,12 @@ FormatPromptRecurse if (IsToken (var_name_begin, "basename}")) { format_file_spec.GetFilename() = module->GetFileSpec().GetFilename(); - var_success = format_file_spec; + var_success = (bool)format_file_spec; } else if (IsToken (var_name_begin, "fullpath}")) { format_file_spec = module->GetFileSpec(); - var_success = format_file_spec; + var_success = (bool)format_file_spec; } } } @@ -2091,12 +2099,12 @@ FormatPromptRecurse if (IsToken (var_name_begin, "basename}")) { format_file_spec.GetFilename() = sc->comp_unit->GetFilename(); - var_success = format_file_spec; + var_success = (bool)format_file_spec; } else if (IsToken (var_name_begin, "fullpath}")) { format_file_spec = *sc->comp_unit; - var_success = format_file_spec; + var_success = (bool)format_file_spec; } } } @@ -2355,12 +2363,12 @@ FormatPromptRecurse if (IsToken (var_name_begin, "basename}")) { format_file_spec.GetFilename() = sc->line_entry.file.GetFilename(); - var_success = format_file_spec; + var_success = (bool)format_file_spec; } else if (IsToken (var_name_begin, "fullpath}")) { format_file_spec = sc->line_entry.file; - var_success = format_file_spec; + var_success = (bool)format_file_spec; } } else if (IsTokenWithFormat (var_name_begin, "number", token_format, "%" PRIu64, exe_ctx, sc)) diff --git a/source/Core/Disassembler.cpp b/source/Core/Disassembler.cpp index e80e92c91b51..7f830acba1f7 100644 --- a/source/Core/Disassembler.cpp +++ b/source/Core/Disassembler.cpp @@ -235,7 +235,8 @@ Disassembler::DisassembleRange const char *plugin_name, const char *flavor, const ExecutionContext &exe_ctx, - const AddressRange &range + const AddressRange &range, + bool prefer_file_cache ) { lldb::DisassemblerSP disasm_sp; @@ -245,7 +246,6 @@ Disassembler::DisassembleRange if (disasm_sp) { - const bool prefer_file_cache = false; size_t bytes_disassembled = disasm_sp->ParseInstructions (&exe_ctx, range, NULL, prefer_file_cache); if (bytes_disassembled == 0) disasm_sp.reset(); @@ -611,8 +611,8 @@ Instruction::Dump (lldb_private::Stream *s, } else { - // Else, we have ARM which can show up to a uint32_t 0x00000000 (10 spaces) - // plus two for padding... + // Else, we have ARM or MIPS which can show up to a uint32_t + // 0x00000000 (10 spaces) plus two for padding... if (max_opcode_byte_size > 0) m_opcode.Dump (&ss, max_opcode_byte_size * 3 + 1); else diff --git a/source/Core/Error.cpp b/source/Core/Error.cpp index e29f12f0b2cd..7aabe5b386d4 100644 --- a/source/Core/Error.cpp +++ b/source/Core/Error.cpp @@ -8,7 +8,9 @@ //===----------------------------------------------------------------------===// // C Includes -#include <errno.h> +#ifdef __APPLE__ +#include <mach/mach.h> +#endif // C++ Includes // Other libraries and framework includes @@ -16,9 +18,8 @@ #include "lldb/Core/Error.h" #include "lldb/Core/Log.h" #include "llvm/ADT/SmallVector.h" +#include <cerrno> #include <cstdarg> -#include <cstdlib> -#include <cstring> #if defined (__arm__) && defined (__APPLE__) #include <SpringBoardServices/SpringBoardServer.h> @@ -51,12 +52,16 @@ Error::Error (const Error &rhs) : { } -Error::Error (const char* err_str): +Error::Error (const char* format, ...): m_code (0), m_type (eErrorTypeInvalid), m_string () { - SetErrorString(err_str); + va_list args; + va_start (args, format); + SetErrorToGenericError (); + SetErrorStringWithVarArg (format, args); + va_end (args); } //---------------------------------------------------------------------- @@ -238,7 +243,7 @@ Error::LogIfError (Log *log, const char *format, ...) if (err_str == NULL) err_str = "???"; - SetErrorStringWithFormat("error: %s err = %s (0x%8.8x)", arg_msg, err_str, m_code); + SetErrorStringWithFormat("%s err = %s (0x%8.8x)", arg_msg, err_str, m_code); if (log) log->Error("%s", m_string.c_str()); diff --git a/source/Core/FileLineResolver.cpp b/source/Core/FileLineResolver.cpp index 15cbbe6ff9e2..b1346bbdd812 100644 --- a/source/Core/FileLineResolver.cpp +++ b/source/Core/FileLineResolver.cpp @@ -50,7 +50,7 @@ FileLineResolver::SearchCallback { CompileUnit *cu = context.comp_unit; - if (m_inlines || m_file_spec.Compare(*cu, m_file_spec, m_file_spec.GetDirectory())) + if (m_inlines || m_file_spec.Compare(*cu, m_file_spec, (bool)m_file_spec.GetDirectory())) { uint32_t start_file_idx = 0; uint32_t file_idx = cu->GetSupportFiles().FindFileIndex(start_file_idx, m_file_spec, false); diff --git a/source/Core/Log.cpp b/source/Core/Log.cpp index d73ab15f697a..8d659cfd7522 100644 --- a/source/Core/Log.cpp +++ b/source/Core/Log.cpp @@ -10,11 +10,9 @@ #include "lldb/lldb-python.h" // C Includes -#include <pthread.h> #include <stdio.h> #include <stdarg.h> #include <stdlib.h> -#include <unistd.h> // C++ Includes #include <map> @@ -101,8 +99,8 @@ Log::PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args) // Timestamp if requested if (m_options.Test (LLDB_LOG_OPTION_PREPEND_TIMESTAMP)) { - struct timeval tv = TimeValue::Now().GetAsTimeVal(); - header.Printf ("%9ld.%6.6d ", tv.tv_sec, (int32_t)tv.tv_usec); + TimeValue now = TimeValue::Now(); + header.Printf ("%9d.%6.6d ", now.seconds(), now.nanoseconds()); } // Add the process and thread if requested diff --git a/source/Core/Mangled.cpp b/source/Core/Mangled.cpp index 4655eb131a6c..189c3bc5531d 100644 --- a/source/Core/Mangled.cpp +++ b/source/Core/Mangled.cpp @@ -10,7 +10,4696 @@ // FreeBSD9-STABLE requires this to know about size_t in cxxabi.h #include <cstddef> +#if defined(_MSC_VER) || defined (__FreeBSD__) +#define LLDB_USE_BUILTIN_DEMANGLER +#else #include <cxxabi.h> +#endif + +#ifdef LLDB_USE_BUILTIN_DEMANGLER + +#include <vector> +#include <algorithm> +#include <string> +#include <numeric> +#include <cstdlib> +#include <cstring> +#include <cctype> +//---------------------------------------------------------------------- +// Inlined copy of: +// http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_demangle.cpp +// revision 193704. +// +// Changes include: +// - remove the "__cxxabiv1" namespace +// - stripped GCC attributes() +// - removed extern "C" from the cxa_demangle function +// - Changed the scope of the unnamed namespace to include cxa_demangle +// function. +//---------------------------------------------------------------------- + +namespace +{ + +enum +{ + unknown_error = -4, + invalid_args = -3, + invalid_mangled_name, + memory_alloc_failure, + success +}; + +template <class C> + const char* parse_type(const char* first, const char* last, C& db); +template <class C> + const char* parse_encoding(const char* first, const char* last, C& db); +template <class C> + const char* parse_name(const char* first, const char* last, C& db); +template <class C> + const char* parse_expression(const char* first, const char* last, C& db); +template <class C> + const char* parse_template_args(const char* first, const char* last, C& db); +template <class C> + const char* parse_operator_name(const char* first, const char* last, C& db); +template <class C> + const char* parse_unqualified_name(const char* first, const char* last, C& db); +template <class C> + const char* parse_decltype(const char* first, const char* last, C& db); + +template <class C> +void +print_stack(const C& db) +{ + printf("---------\n"); + printf("names:\n"); + for (auto& s : db.names) + printf("{%s#%s}\n", s.first.c_str(), s.second.c_str()); + int i = -1; + printf("subs:\n"); + for (auto& v : db.subs) + { + if (i >= 0) + printf("S%i_ = {", i); + else + printf("S_ = {"); + for (auto& s : v) + printf("{%s#%s}", s.first.c_str(), s.second.c_str()); + printf("}\n"); + ++i; + } + printf("template_param:\n"); + for (auto& t : db.template_param) + { + printf("--\n"); + i = -1; + for (auto& v : t) + { + if (i >= 0) + printf("T%i_ = {", i); + else + printf("T_ = {"); + for (auto& s : v) + printf("{%s#%s}", s.first.c_str(), s.second.c_str()); + printf("}\n"); + ++i; + } + } + printf("---------\n\n"); +} + +template <class C> +void +print_state(const char* msg, const char* first, const char* last, const C& db) +{ + printf("%s: ", msg); + for (; first != last; ++first) + printf("%c", *first); + printf("\n"); + print_stack(db); +} + +// <number> ::= [n] <non-negative decimal integer> + +const char* +parse_number(const char* first, const char* last) +{ + if (first != last) + { + const char* t = first; + if (*t == 'n') + ++t; + if (t != last) + { + if (*t == '0') + { + first = t+1; + } + else if ('1' <= *t && *t <= '9') + { + first = t+1; + while (first != last && std::isdigit(*first)) + ++first; + } + } + } + return first; +} + +template <class Float> +struct float_data; + +template <> +struct float_data<float> +{ + static const size_t mangled_size = 8; + static const size_t max_demangled_size = 24; + static constexpr const char* spec = "%af"; +}; + +constexpr const char* float_data<float>::spec; + +template <> +struct float_data<double> +{ + static const size_t mangled_size = 16; + static const size_t max_demangled_size = 32; + static constexpr const char* spec = "%a"; +}; + +constexpr const char* float_data<double>::spec; + +template <> +struct float_data<long double> +{ + static const size_t mangled_size = 20; // May need to be adjusted to 16 or 24 on other platforms + static const size_t max_demangled_size = 40; + static constexpr const char* spec = "%LaL"; +}; + +constexpr const char* float_data<long double>::spec; + +template <class Float, class C> +const char* +parse_floating_number(const char* first, const char* last, C& db) +{ + const size_t N = float_data<Float>::mangled_size; + if (static_cast<std::size_t>(last - first) > N) + { + last = first + N; + union + { + Float value; + char buf[sizeof(Float)]; + }; + const char* t = first; + char* e = buf; + for (; t != last; ++t, ++e) + { + if (!isxdigit(*t)) + return first; + unsigned d1 = isdigit(*t) ? static_cast<unsigned>(*t - '0') : + static_cast<unsigned>(*t - 'a' + 10); + ++t; + unsigned d0 = isdigit(*t) ? static_cast<unsigned>(*t - '0') : + static_cast<unsigned>(*t - 'a' + 10); + *e = static_cast<char>((d1 << 4) + d0); + } + if (*t == 'E') + { +#if __LITTLE_ENDIAN__ + std::reverse(buf, e); +#endif + char num[float_data<Float>::max_demangled_size] = {0}; + int n = snprintf(num, sizeof(num), float_data<Float>::spec, value); + if (static_cast<std::size_t>(n) >= sizeof(num)) + return first; + db.names.push_back(typename C::String(num, static_cast<std::size_t>(n))); + first = t+1; + } + } + return first; +} + +// <source-name> ::= <positive length number> <identifier> + +template <class C> +const char* +parse_source_name(const char* first, const char* last, C& db) +{ + if (first != last) + { + char c = *first; + if (isdigit(c) && first+1 != last) + { + const char* t = first+1; + size_t n = static_cast<size_t>(c - '0'); + for (c = *t; isdigit(c); c = *t) + { + n = n * 10 + static_cast<size_t>(c - '0'); + if (++t == last) + return first; + } + if (static_cast<size_t>(last - t) >= n) + { + typename C::String r(t, n); + if (r.substr(0, 10) == "_GLOBAL__N") + db.names.push_back("(anonymous namespace)"); + else + db.names.push_back(std::move(r)); + first = t + n; + } + } + } + return first; +} + +// <substitution> ::= S <seq-id> _ +// ::= S_ +// <substitution> ::= Sa # ::std::allocator +// <substitution> ::= Sb # ::std::basic_string +// <substitution> ::= Ss # ::std::basic_string < char, +// ::std::char_traits<char>, +// ::std::allocator<char> > +// <substitution> ::= Si # ::std::basic_istream<char, std::char_traits<char> > +// <substitution> ::= So # ::std::basic_ostream<char, std::char_traits<char> > +// <substitution> ::= Sd # ::std::basic_iostream<char, std::char_traits<char> > + +template <class C> +const char* +parse_substitution(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + if (*first == 'S') + { + switch (first[1]) + { + case 'a': + db.names.push_back("std::allocator"); + first += 2; + break; + case 'b': + db.names.push_back("std::basic_string"); + first += 2; + break; + case 's': + db.names.push_back("std::string"); + first += 2; + break; + case 'i': + db.names.push_back("std::istream"); + first += 2; + break; + case 'o': + db.names.push_back("std::ostream"); + first += 2; + break; + case 'd': + db.names.push_back("std::iostream"); + first += 2; + break; + case '_': + if (!db.subs.empty()) + { + for (const auto& n : db.subs.front()) + db.names.push_back(n); + first += 2; + } + break; + default: + if (std::isdigit(first[1]) || std::isupper(first[1])) + { + size_t sub = 0; + const char* t = first+1; + if (std::isdigit(*t)) + sub = static_cast<size_t>(*t - '0'); + else + sub = static_cast<size_t>(*t - 'A') + 10; + for (++t; t != last && (std::isdigit(*t) || std::isupper(*t)); ++t) + { + sub *= 36; + if (std::isdigit(*t)) + sub += static_cast<size_t>(*t - '0'); + else + sub += static_cast<size_t>(*t - 'A') + 10; + } + if (t == last || *t != '_') + return first; + ++sub; + if (sub < db.subs.size()) + { + for (const auto& n : db.subs[sub]) + db.names.push_back(n); + first = t+1; + } + } + break; + } + } + } + return first; +} + +// <builtin-type> ::= v # void +// ::= w # wchar_t +// ::= b # bool +// ::= c # char +// ::= a # signed char +// ::= h # unsigned char +// ::= s # short +// ::= t # unsigned short +// ::= i # int +// ::= j # unsigned int +// ::= l # long +// ::= m # unsigned long +// ::= x # long long, __int64 +// ::= y # unsigned long long, __int64 +// ::= n # __int128 +// ::= o # unsigned __int128 +// ::= f # float +// ::= d # double +// ::= e # long double, __float80 +// ::= g # __float128 +// ::= z # ellipsis +// ::= Dd # IEEE 754r decimal floating point (64 bits) +// ::= De # IEEE 754r decimal floating point (128 bits) +// ::= Df # IEEE 754r decimal floating point (32 bits) +// ::= Dh # IEEE 754r half-precision floating point (16 bits) +// ::= Di # char32_t +// ::= Ds # char16_t +// ::= Da # auto (in dependent new-expressions) +// ::= Dn # std::nullptr_t (i.e., decltype(nullptr)) +// ::= u <source-name> # vendor extended type + +template <class C> +const char* +parse_builtin_type(const char* first, const char* last, C& db) +{ + if (first != last) + { + switch (*first) + { + case 'v': + db.names.push_back("void"); + ++first; + break; + case 'w': + db.names.push_back("wchar_t"); + ++first; + break; + case 'b': + db.names.push_back("bool"); + ++first; + break; + case 'c': + db.names.push_back("char"); + ++first; + break; + case 'a': + db.names.push_back("signed char"); + ++first; + break; + case 'h': + db.names.push_back("unsigned char"); + ++first; + break; + case 's': + db.names.push_back("short"); + ++first; + break; + case 't': + db.names.push_back("unsigned short"); + ++first; + break; + case 'i': + db.names.push_back("int"); + ++first; + break; + case 'j': + db.names.push_back("unsigned int"); + ++first; + break; + case 'l': + db.names.push_back("long"); + ++first; + break; + case 'm': + db.names.push_back("unsigned long"); + ++first; + break; + case 'x': + db.names.push_back("long long"); + ++first; + break; + case 'y': + db.names.push_back("unsigned long long"); + ++first; + break; + case 'n': + db.names.push_back("__int128"); + ++first; + break; + case 'o': + db.names.push_back("unsigned __int128"); + ++first; + break; + case 'f': + db.names.push_back("float"); + ++first; + break; + case 'd': + db.names.push_back("double"); + ++first; + break; + case 'e': + db.names.push_back("long double"); + ++first; + break; + case 'g': + db.names.push_back("__float128"); + ++first; + break; + case 'z': + db.names.push_back("..."); + ++first; + break; + case 'u': + { + const char*t = parse_source_name(first+1, last, db); + if (t != first+1) + first = t; + } + break; + case 'D': + if (first+1 != last) + { + switch (first[1]) + { + case 'd': + db.names.push_back("decimal64"); + first += 2; + break; + case 'e': + db.names.push_back("decimal128"); + first += 2; + break; + case 'f': + db.names.push_back("decimal32"); + first += 2; + break; + case 'h': + db.names.push_back("decimal16"); + first += 2; + break; + case 'i': + db.names.push_back("char32_t"); + first += 2; + break; + case 's': + db.names.push_back("char16_t"); + first += 2; + break; + case 'a': + db.names.push_back("auto"); + first += 2; + break; + case 'n': + db.names.push_back("std::nullptr_t"); + first += 2; + break; + } + } + break; + } + } + return first; +} + +// <CV-qualifiers> ::= [r] [V] [K] + +const char* +parse_cv_qualifiers(const char* first, const char* last, unsigned& cv) +{ + cv = 0; + if (first != last) + { + if (*first == 'r') + { + cv |= 4; + ++first; + } + if (*first == 'V') + { + cv |= 2; + ++first; + } + if (*first == 'K') + { + cv |= 1; + ++first; + } + } + return first; +} + +// <template-param> ::= T_ # first template parameter +// ::= T <parameter-2 non-negative number> _ + +template <class C> +const char* +parse_template_param(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + if (*first == 'T') + { + if (first[1] == '_') + { + if (!db.template_param.back().empty()) + { + for (auto& t : db.template_param.back().front()) + db.names.push_back(t); + first += 2; + } + else + { + db.names.push_back("T_"); + first += 2; + db.fix_forward_references = true; + } + } + else if (isdigit(first[1])) + { + const char* t = first+1; + size_t sub = static_cast<size_t>(*t - '0'); + for (++t; t != last && isdigit(*t); ++t) + { + sub *= 10; + sub += static_cast<size_t>(*t - '0'); + } + if (t == last || *t != '_') + return first; + ++sub; + if (sub < db.template_param.back().size()) + { + for (auto& temp : db.template_param.back()[sub]) + db.names.push_back(temp); + first = t+1; + } + else + { + db.names.push_back(typename C::String(first, t+1)); + first = t+1; + db.fix_forward_references = true; + } + } + } + } + return first; +} + +// cc <type> <expression> # const_cast<type> (expression) + +template <class C> +const char* +parse_const_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'c' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "const_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// dc <type> <expression> # dynamic_cast<type> (expression) + +template <class C> +const char* +parse_dynamic_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "dynamic_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// rc <type> <expression> # reinterpret_cast<type> (expression) + +template <class C> +const char* +parse_reinterpret_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'r' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "reinterpret_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// sc <type> <expression> # static_cast<type> (expression) + +template <class C> +const char* +parse_static_cast_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'c') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "static_cast<" + db.names.back().move_full() + ">(" + expr + ")"; + first = t1; + } + } + } + return first; +} + +// sp <expression> # pack expansion + +template <class C> +const char* +parse_pack_expansion(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'p') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + first = t; + } + return first; +} + +// st <type> # sizeof (a type) + +template <class C> +const char* +parse_sizeof_type_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 't') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// sz <expr> # sizeof (a expression) + +template <class C> +const char* +parse_sizeof_expr_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'z') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + db.names.back() = "sizeof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// sZ <template-param> # size of a parameter pack + +template <class C> +const char* +parse_sizeof_param_pack_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'T') + { + size_t k0 = db.names.size(); + const char* t = parse_template_param(first+2, last, db); + size_t k1 = db.names.size(); + if (t != first+2) + { + typename C::String tmp("sizeof...("); + size_t k = k0; + if (k != k1) + { + tmp += db.names[k].move_full(); + for (++k; k != k1; ++k) + tmp += ", " + db.names[k].move_full(); + } + tmp += ")"; + for (; k1 != k0; --k1) + db.names.pop_back(); + db.names.push_back(std::move(tmp)); + first = t; + } + } + return first; +} + +// <function-param> ::= fp <top-level CV-qualifiers> _ # L == 0, first parameter +// ::= fp <top-level CV-qualifiers> <parameter-2 non-negative number> _ # L == 0, second and later parameters +// ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> _ # L > 0, first parameter +// ::= fL <L-1 non-negative number> p <top-level CV-qualifiers> <parameter-2 non-negative number> _ # L > 0, second and later parameters + +template <class C> +const char* +parse_function_param(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && *first == 'f') + { + if (first[1] == 'p') + { + unsigned cv; + const char* t = parse_cv_qualifiers(first+2, last, cv); + const char* t1 = parse_number(t, last); + if (t1 != last && *t1 == '_') + { + db.names.push_back("fp" + typename C::String(t, t1)); + first = t1+1; + } + } + else if (first[1] == 'L') + { + unsigned cv; + const char* t0 = parse_number(first+2, last); + if (t0 != last && *t0 == 'p') + { + ++t0; + const char* t = parse_cv_qualifiers(t0, last, cv); + const char* t1 = parse_number(t, last); + if (t1 != last && *t1 == '_') + { + db.names.push_back("fp" + typename C::String(t, t1)); + first = t1+1; + } + } + } + } + return first; +} + +// sZ <function-param> # size of a function parameter pack + +template <class C> +const char* +parse_sizeof_function_param_pack_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 's' && first[1] == 'Z' && first[2] == 'f') + { + const char* t = parse_function_param(first+2, last, db); + if (t != first+2) + { + db.names.back() = "sizeof...(" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// te <expression> # typeid (expression) +// ti <type> # typeid (type) + +template <class C> +const char* +parse_typeid_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 't' && (first[1] == 'e' || first[1] == 'i')) + { + const char* t; + if (first[1] == 'e') + t = parse_expression(first+2, last, db); + else + t = parse_type(first+2, last, db); + if (t != first+2) + { + db.names.back() = "typeid(" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// tw <expression> # throw expression + +template <class C> +const char* +parse_throw_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 't' && first[1] == 'w') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + db.names.back() = "throw " + db.names.back().move_full(); + first = t; + } + } + return first; +} + +// ds <expression> <expression> # expr.*expr + +template <class C> +const char* +parse_dot_star_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 's') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + auto expr = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += ".*" + expr; + first = t1; + } + } + } + return first; +} + +// <simple-id> ::= <source-name> [ <template-args> ] + +template <class C> +const char* +parse_simple_id(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t = parse_source_name(first, last, db); + if (t != first) + { + const char* t1 = parse_template_args(t, last, db); + if (t1 != t) + { + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + } + first = t1; + } + else + first = t; + } + return first; +} + +// <unresolved-type> ::= <template-param> +// ::= <decltype> +// ::= <substitution> + +template <class C> +const char* +parse_unresolved_type(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t = first; + switch (*first) + { + case 'T': + { + size_t k0 = db.names.size(); + t = parse_template_param(first, last, db); + size_t k1 = db.names.size(); + if (t != first && k1 == k0 + 1) + { + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + else + { + for (; k1 != k0; --k1) + db.names.pop_back(); + } + break; + } + case 'D': + t = parse_decltype(first, last, db); + if (t != first) + { + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + break; + case 'S': + t = parse_substitution(first, last, db); + if (t != first) + first = t; + else + { + if (last - first > 2 && first[1] == 't') + { + t = parse_unqualified_name(first+2, last, db); + if (t != first+2) + { + db.names.back().first.insert(0, "std::"); + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + } + break; + } + } + return first; +} + +// <destructor-name> ::= <unresolved-type> # e.g., ~T or ~decltype(f()) +// ::= <simple-id> # e.g., ~A<2*N> + +template <class C> +const char* +parse_destructor_name(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t = parse_unresolved_type(first, last, db); + if (t == first) + t = parse_simple_id(first, last, db); + if (t != first) + { + db.names.back().first.insert(0, "~"); + first = t; + } + } + return first; +} + +// <base-unresolved-name> ::= <simple-id> # unresolved name +// extension ::= <operator-name> # unresolved operator-function-id +// extension ::= <operator-name> <template-args> # unresolved operator template-id +// ::= on <operator-name> # unresolved operator-function-id +// ::= on <operator-name> <template-args> # unresolved operator template-id +// ::= dn <destructor-name> # destructor or pseudo-destructor; +// # e.g. ~X or ~X<N-1> + +template <class C> +const char* +parse_base_unresolved_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + if ((first[0] == 'o' || first[0] == 'd') && first[1] == 'n') + { + if (first[0] == 'o') + { + const char* t = parse_operator_name(first+2, last, db); + if (t != first+2) + { + first = parse_template_args(t, last, db); + if (first != t) + { + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + } + } + } + else + { + const char* t = parse_destructor_name(first+2, last, db); + if (t != first+2) + first = t; + } + } + else + { + const char* t = parse_simple_id(first, last, db); + if (t == first) + { + t = parse_operator_name(first, last, db); + if (t != first) + { + first = parse_template_args(t, last, db); + if (first != t) + { + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + } + } + } + else + first = t; + } + } + return first; +} + +// <unresolved-qualifier-level> ::= <simple-id> + +template <class C> +const char* +parse_unresolved_qualifier_level(const char* first, const char* last, C& db) +{ + return parse_simple_id(first, last, db); +} + +// <unresolved-name> +// extension ::= srN <unresolved-type> [<template-args>] <unresolved-qualifier-level>* E <base-unresolved-name> +// ::= [gs] <base-unresolved-name> # x or (with "gs") ::x +// ::= [gs] sr <unresolved-qualifier-level>+ E <base-unresolved-name> +// # A::x, N::y, A<T>::z; "gs" means leading "::" +// ::= sr <unresolved-type> <base-unresolved-name> # T::x / decltype(p)::x +// extension ::= sr <unresolved-type> <template-args> <base-unresolved-name> +// # T::N::x /decltype(p)::N::x +// (ignored) ::= srN <unresolved-type> <unresolved-qualifier-level>+ E <base-unresolved-name> + +template <class C> +const char* +parse_unresolved_name(const char* first, const char* last, C& db) +{ + if (last - first > 2) + { + const char* t = first; + bool global = false; + if (t[0] == 'g' && t[1] == 's') + { + global = true; + t += 2; + } + const char* t2 = parse_base_unresolved_name(t, last, db); + if (t2 != t) + { + if (global) + db.names.back().first.insert(0, "::"); + first = t2; + } + else if (last - t > 2 && t[0] == 's' && t[1] == 'r') + { + if (t[2] == 'N') + { + t += 3; + const char* t1 = parse_unresolved_type(t, last, db); + if (t1 == t || t1 == last) + return first; + t = t1; + t1 = parse_template_args(t, last, db); + if (t1 != t) + { + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + t = t1; + if (t == last) + { + db.names.pop_back(); + return first; + } + } + while (*t != 'E') + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + t = t1; + } + ++t; + t1 = parse_base_unresolved_name(t, last, db); + if (t1 == t) + { + db.names.pop_back(); + return first; + } + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + first = t1; + } + else + { + t += 2; + const char* t1 = parse_unresolved_type(t, last, db); + if (t1 != t) + { + t = t1; + t1 = parse_template_args(t, last, db); + if (t1 != t) + { + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + t = t1; + } + t1 = parse_base_unresolved_name(t, last, db); + if (t1 == t) + { + db.names.pop_back(); + return first; + } + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + first = t1; + } + else + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last) + return first; + t = t1; + if (global) + db.names.back().first.insert(0, "::"); + while (*t != 'E') + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last) + return first; + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + t = t1; + } + ++t; + t1 = parse_base_unresolved_name(t, last, db); + if (t1 == t) + { + db.names.pop_back(); + return first; + } + auto s = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "::" + std::move(s); + first = t1; + } + } + } + } + return first; +} + +// dt <expression> <unresolved-name> # expr.name + +template <class C> +const char* +parse_dot_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'd' && first[1] == 't') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_unresolved_name(t, last, db); + if (t1 != t) + { + auto name = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "." + name; + first = t1; + } + } + } + return first; +} + +// cl <expression>+ E # call + +template <class C> +const char* +parse_call_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 4 && first[0] == 'c' && first[1] == 'l') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + if (t == last) + return first; + db.names.back().first += db.names.back().second; + db.names.back().second = typename C::String(); + db.names.back().first.append("("); + bool first_expr = true; + while (*t != 'E') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (!first_expr) + { + db.names.back().first.append(", "); + first_expr = false; + } + db.names.back().first.append(tmp); + } + t = t1; + } + ++t; + db.names.back().first.append(")"); + first = t; + } + } + return first; +} + +// [gs] nw <expression>* _ <type> E # new (expr-list) type +// [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) +// [gs] na <expression>* _ <type> E # new[] (expr-list) type +// [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) +// <initializer> ::= pi <expression>* E # parenthesized initialization + +template <class C> +const char* +parse_new_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 4) + { + const char* t = first; + bool parsed_gs = false; + if (t[0] == 'g' && t[1] == 's') + { + t += 2; + parsed_gs = true; + } + if (t[0] == 'n' && (t[1] == 'w' || t[1] == 'a')) + { + bool is_array = t[1] == 'a'; + t += 2; + if (t == last) + return first; + bool has_expr_list = false; + bool first_expr = true; + while (*t != '_') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + has_expr_list = true; + if (!first_expr) + { + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + ++t; + const char* t1 = parse_type(t, last, db); + if (t1 == t || t1 == last) + return first; + t = t1; + bool has_init = false; + if (last - t >= 3 && t[0] == 'p' && t[1] == 'i') + { + t += 2; + has_init = true; + first_expr = true; + while (*t != 'E') + { + t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + if (!first_expr) + { + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + } + if (*t != 'E') + return first; + typename C::String init_list; + if (has_init) + { + init_list = db.names.back().move_full(); + db.names.pop_back(); + } + auto type = db.names.back().move_full(); + db.names.pop_back(); + typename C::String expr_list; + if (has_expr_list) + { + expr_list = db.names.back().move_full(); + db.names.pop_back(); + } + typename C::String r; + if (parsed_gs) + r = "::"; + if (is_array) + r += "[] "; + else + r += " "; + if (has_expr_list) + r += "(" + expr_list + ") "; + r += type; + if (has_init) + r += " (" + init_list + ")"; + db.names.push_back(std::move(r)); + first = t+1; + } + } + return first; +} + +// cv <type> <expression> # conversion with one argument +// cv <type> _ <expression>* E # conversion with a different number of arguments + +template <class C> +const char* +parse_conversion_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'c' && first[1] == 'v') + { + bool try_to_parse_template_args = db.try_to_parse_template_args; + db.try_to_parse_template_args = false; + const char* t = parse_type(first+2, last, db); + db.try_to_parse_template_args = try_to_parse_template_args; + if (t != first+2 && t != last) + { + if (*t != '_') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t) + return first; + t = t1; + } + else + { + ++t; + if (t == last) + return first; + if (*t == 'E') + db.names.emplace_back(); + else + { + bool first_expr = true; + while (*t != 'E') + { + const char* t1 = parse_expression(t, last, db); + if (t1 == t || t1 == last) + return first; + if (!first_expr) + { + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + } + ++t; + } + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back() = "(" + db.names.back().move_full() + ")(" + tmp + ")"; + first = t; + } + } + return first; +} + +// pt <expression> <expression> # expr->name + +template <class C> +const char* +parse_arrow_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'p' && first[1] == 't') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + const char* t1 = parse_expression(t, last, db); + if (t1 != t) + { + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += "->"; + db.names.back().first += tmp; + first = t1; + } + } + } + return first; +} + +// <ref-qualifier> ::= R # & ref-qualifier +// <ref-qualifier> ::= O # && ref-qualifier + +// <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E + +template <class C> +const char* +parse_function_type(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'F') + { + const char* t = first+1; + if (t != last) + { + bool externC = false; + if (*t == 'Y') + { + externC = true; + if (++t == last) + return first; + } + const char* t1 = parse_type(t, last, db); + if (t1 != t) + { + t = t1; + typename C::String sig("("); + int ref_qual = 0; + while (true) + { + if (t == last) + { + db.names.pop_back(); + return first; + } + if (*t == 'E') + { + ++t; + break; + } + if (*t == 'v') + { + ++t; + continue; + } + if (*t == 'R' && t+1 != last && t[1] == 'E') + { + ref_qual = 1; + ++t; + continue; + } + if (*t == 'O' && t+1 != last && t[1] == 'E') + { + ref_qual = 2; + ++t; + continue; + } + size_t k0 = db.names.size(); + t1 = parse_type(t, last, db); + size_t k1 = db.names.size(); + if (t1 == t || t1 == last) + return first; + for (size_t k = k0; k < k1; ++k) + { + if (sig.size() > 1) + sig += ", "; + sig += db.names[k].move_full(); + } + for (size_t k = k0; k < k1; ++k) + db.names.pop_back(); + t = t1; + } + sig += ")"; + switch (ref_qual) + { + case 1: + sig += " &"; + break; + case 2: + sig += " &&"; + break; + } + db.names.back().first += " "; + db.names.back().second.insert(0, sig); + first = t; + } + } + } + return first; +} + +// <pointer-to-member-type> ::= M <class type> <member type> + +template <class C> +const char* +parse_pointer_to_member_type(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'M') + { + const char* t = parse_type(first+1, last, db); + if (t != first+1) + { + const char* t2 = parse_type(t, last, db); + if (t2 != t) + { + auto func = std::move(db.names.back()); + db.names.pop_back(); + auto class_type = std::move(db.names.back()); + if (func.second.front() == '(') + { + db.names.back().first = std::move(func.first) + "(" + class_type.move_full() + "::*"; + db.names.back().second = ")" + std::move(func.second); + } + else + { + db.names.back().first = std::move(func.first) + " " + class_type.move_full() + "::*"; + db.names.back().second = std::move(func.second); + } + first = t2; + } + } + } + return first; +} + +// <array-type> ::= A <positive dimension number> _ <element type> +// ::= A [<dimension expression>] _ <element type> + +template <class C> +const char* +parse_array_type(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'A' && first+1 != last) + { + if (first[1] == '_') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + if (db.names.back().second.substr(0, 2) == " [") + db.names.back().second.erase(0, 1); + db.names.back().second.insert(0, " []"); + first = t; + } + } + else if ('1' <= first[1] && first[1] <= '9') + { + const char* t = parse_number(first+1, last); + if (t != last && *t == '_') + { + const char* t2 = parse_type(t+1, last, db); + if (t2 != t+1) + { + if (db.names.back().second.substr(0, 2) == " [") + db.names.back().second.erase(0, 1); + db.names.back().second.insert(0, " [" + typename C::String(first+1, t) + "]"); + first = t2; + } + } + } + else + { + const char* t = parse_expression(first+1, last, db); + if (t != first+1 && t != last && *t == '_') + { + const char* t2 = parse_type(++t, last, db); + if (t2 != t) + { + auto type = std::move(db.names.back()); + db.names.pop_back(); + auto expr = std::move(db.names.back()); + db.names.back().first = std::move(type.first); + if (type.second.substr(0, 2) == " [") + type.second.erase(0, 1); + db.names.back().second = " [" + expr.move_full() + "]" + std::move(type.second); + first = t2; + } + } + } + } + return first; +} + +// <decltype> ::= Dt <expression> E # decltype of an id-expression or class member access (C++0x) +// ::= DT <expression> E # decltype of an expression (C++0x) + +template <class C> +const char* +parse_decltype(const char* first, const char* last, C& db) +{ + if (last - first >= 4 && first[0] == 'D') + { + switch (first[1]) + { + case 't': + case 'T': + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2 && t != last && *t == 'E') + { + db.names.back() = "decltype(" + db.names.back().move_full() + ")"; + first = t+1; + } + } + break; + } + } + return first; +} + +// extension: +// <vector-type> ::= Dv <positive dimension number> _ +// <extended element type> +// ::= Dv [<dimension expression>] _ <element type> +// <extended element type> ::= <element type> +// ::= p # AltiVec vector pixel + +template <class C> +const char* +parse_vector_type(const char* first, const char* last, C& db) +{ + if (last - first > 3 && first[0] == 'D' && first[1] == 'v') + { + if ('1' <= first[2] && first[2] <= '9') + { + const char* t = parse_number(first+2, last); + if (t == last || *t != '_') + return first; + const char* num = first + 2; + size_t sz = static_cast<size_t>(t - num); + if (++t != last) + { + if (*t != 'p') + { + const char* t1 = parse_type(t, last, db); + if (t1 != t) + { + db.names.back().first += " vector[" + typename C::String(num, sz) + "]"; + first = t1; + } + } + else + { + ++t; + db.names.push_back("pixel vector[" + typename C::String(num, sz) + "]"); + first = t; + } + } + } + else + { + typename C::String num; + const char* t1 = first+2; + if (*t1 != '_') + { + const char* t = parse_expression(t1, last, db); + if (t != t1) + { + num = db.names.back().move_full(); + db.names.pop_back(); + t1 = t; + } + } + if (t1 != last && *t1 == '_' && ++t1 != last) + { + const char* t = parse_type(t1, last, db); + if (t != t1) + { + db.names.back().first += " vector[" + num + "]"; + first = t; + } + } + } + } + return first; +} + +// <type> ::= <builtin-type> +// ::= <function-type> +// ::= <class-enum-type> +// ::= <array-type> +// ::= <pointer-to-member-type> +// ::= <template-param> +// ::= <template-template-param> <template-args> +// ::= <decltype> +// ::= <substitution> +// ::= <CV-qualifiers> <type> +// ::= P <type> # pointer-to +// ::= R <type> # reference-to +// ::= O <type> # rvalue reference-to (C++0x) +// ::= C <type> # complex pair (C 2000) +// ::= G <type> # imaginary (C 2000) +// ::= Dp <type> # pack expansion (C++0x) +// ::= U <source-name> <type> # vendor extended type qualifier +// extension := U <objc-name> <objc-type> # objc-type<identifier> +// extension := <vector-type> # <vector-type> starts with Dv + +// <objc-name> ::= <k0 number> objcproto <k1 number> <identifier> # k0 = 9 + <number of digits in k1> + k1 +// <objc-type> := <source-name> # PU<11+>objcproto 11objc_object<source-name> 11objc_object -> id<source-name> + +template <class C> +const char* +parse_type(const char* first, const char* last, C& db) +{ + if (first != last) + { + switch (*first) + { + case 'r': + case 'V': + case 'K': + { + unsigned cv = 0; + const char* t = parse_cv_qualifiers(first, last, cv); + if (t != first) + { + bool is_function = *t == 'F'; + size_t k0 = db.names.size(); + const char* t1 = parse_type(t, last, db); + size_t k1 = db.names.size(); + if (t1 != t) + { + if (is_function) + db.subs.pop_back(); + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (is_function) + { + size_t p = db.names[k].second.size(); + if (db.names[k].second[p-2] == '&') + p -= 3; + else if (db.names[k].second.back() == '&') + p -= 2; + if (cv & 1) + { + db.names[k].second.insert(p, " const"); + p += 6; + } + if (cv & 2) + { + db.names[k].second.insert(p, " volatile"); + p += 9; + } + if (cv & 4) + db.names[k].second.insert(p, " restrict"); + } + else + { + if (cv & 1) + db.names[k].first.append(" const"); + if (cv & 2) + db.names[k].first.append(" volatile"); + if (cv & 4) + db.names[k].first.append(" restrict"); + } + db.subs.back().push_back(db.names[k]); + } + first = t1; + } + } + } + break; + default: + { + const char* t = parse_builtin_type(first, last, db); + if (t != first) + { + first = t; + } + else + { + switch (*first) + { + case 'A': + t = parse_array_type(first, last, db); + if (t != first) + { + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'C': + t = parse_type(first+1, last, db); + if (t != first+1) + { + db.names.back().first.append(" complex"); + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'F': + t = parse_function_type(first, last, db); + if (t != first) + { + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'G': + t = parse_type(first+1, last, db); + if (t != first+1) + { + db.names.back().first.append(" imaginary"); + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'M': + t = parse_pointer_to_member_type(first, last, db); + if (t != first) + { + first = t; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + break; + case 'O': + { + size_t k0 = db.names.size(); + t = parse_type(first+1, last, db); + size_t k1 = db.names.size(); + if (t != first+1) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (db.names[k].second.substr(0, 2) == " [") + { + db.names[k].first += " ("; + db.names[k].second.insert(0, ")"); + } + else if (db.names[k].second.front() == '(') + { + db.names[k].first += "("; + db.names[k].second.insert(0, ")"); + } + db.names[k].first.append("&&"); + db.subs.back().push_back(db.names[k]); + } + first = t; + } + break; + } + case 'P': + { + size_t k0 = db.names.size(); + t = parse_type(first+1, last, db); + size_t k1 = db.names.size(); + if (t != first+1) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (db.names[k].second.substr(0, 2) == " [") + { + db.names[k].first += " ("; + db.names[k].second.insert(0, ")"); + } + else if (db.names[k].second.front() == '(') + { + db.names[k].first += "("; + db.names[k].second.insert(0, ")"); + } + if (first[1] != 'U' || db.names[k].first.substr(0, 12) != "objc_object<") + { + db.names[k].first.append("*"); + } + else + { + db.names[k].first.replace(0, 11, "id"); + } + db.subs.back().push_back(db.names[k]); + } + first = t; + } + break; + } + case 'R': + { + size_t k0 = db.names.size(); + t = parse_type(first+1, last, db); + size_t k1 = db.names.size(); + if (t != first+1) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + { + if (db.names[k].second.substr(0, 2) == " [") + { + db.names[k].first += " ("; + db.names[k].second.insert(0, ")"); + } + else if (db.names[k].second.front() == '(') + { + db.names[k].first += "("; + db.names[k].second.insert(0, ")"); + } + db.names[k].first.append("&"); + db.subs.back().push_back(db.names[k]); + } + first = t; + } + break; + } + case 'T': + { + size_t k0 = db.names.size(); + t = parse_template_param(first, last, db); + size_t k1 = db.names.size(); + if (t != first) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + db.subs.back().push_back(db.names[k]); + if (db.try_to_parse_template_args && k1 == k0+1) + { + const char* t1 = parse_template_args(t, last, db); + if (t1 != t) + { + auto args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += std::move(args); + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + t = t1; + } + } + first = t; + } + break; + } + case 'U': + if (first+1 != last) + { + t = parse_source_name(first+1, last, db); + if (t != first+1) + { + const char* t2 = parse_type(t, last, db); + if (t2 != t) + { + auto type = db.names.back().move_full(); + db.names.pop_back(); + if (db.names.back().first.substr(0, 9) != "objcproto") + { + db.names.back() = type + " " + db.names.back().move_full(); + } + else + { + auto proto = db.names.back().move_full(); + db.names.pop_back(); + t = parse_source_name(proto.data() + 9, proto.data() + proto.size(), db); + if (t != proto.data() + 9) + { + db.names.back() = type + "<" + db.names.back().move_full() + ">"; + } + else + { + db.names.push_back(type + " " + proto); + } + } + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t2; + } + } + } + break; + case 'S': + if (first+1 != last && first[1] == 't') + { + t = parse_name(first, last, db); + if (t != first) + { + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + else + { + t = parse_substitution(first, last, db); + if (t != first) + { + first = t; + // Parsed a substitution. If the substitution is a + // <template-param> it might be followed by <template-args>. + t = parse_template_args(first, last, db); + if (t != first) + { + auto template_args = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += template_args; + // Need to create substitution for <template-template-param> <template-args> + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + } + break; + case 'D': + if (first+1 != last) + { + switch (first[1]) + { + case 'p': + { + size_t k0 = db.names.size(); + t = parse_type(first+2, last, db); + size_t k1 = db.names.size(); + if (t != first+2) + { + db.subs.emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + db.subs.back().push_back(db.names[k]); + first = t; + return first; + } + break; + } + case 't': + case 'T': + t = parse_decltype(first, last, db); + if (t != first) + { + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + return first; + } + break; + case 'v': + t = parse_vector_type(first, last, db); + if (t != first) + { + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + return first; + } + break; + } + } + // drop through + default: + // must check for builtin-types before class-enum-types to avoid + // ambiguities with operator-names + t = parse_builtin_type(first, last, db); + if (t != first) + { + first = t; + } + else + { + t = parse_name(first, last, db); + if (t != first) + { + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + first = t; + } + } + break; + } + } + break; + } + } + } + return first; +} + +// <operator-name> +// ::= aa # && +// ::= ad # & (unary) +// ::= an # & +// ::= aN # &= +// ::= aS # = +// ::= cl # () +// ::= cm # , +// ::= co # ~ +// ::= cv <type> # (cast) +// ::= da # delete[] +// ::= de # * (unary) +// ::= dl # delete +// ::= dv # / +// ::= dV # /= +// ::= eo # ^ +// ::= eO # ^= +// ::= eq # == +// ::= ge # >= +// ::= gt # > +// ::= ix # [] +// ::= le # <= +// ::= ls # << +// ::= lS # <<= +// ::= lt # < +// ::= mi # - +// ::= mI # -= +// ::= ml # * +// ::= mL # *= +// ::= mm # -- (postfix in <expression> context) +// ::= na # new[] +// ::= ne # != +// ::= ng # - (unary) +// ::= nt # ! +// ::= nw # new +// ::= oo # || +// ::= or # | +// ::= oR # |= +// ::= pm # ->* +// ::= pl # + +// ::= pL # += +// ::= pp # ++ (postfix in <expression> context) +// ::= ps # + (unary) +// ::= pt # -> +// ::= qu # ? +// ::= rm # % +// ::= rM # %= +// ::= rs # >> +// ::= rS # >>= +// ::= v <digit> <source-name> # vendor extended operator + +template <class C> +const char* +parse_operator_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + switch (first[0]) + { + case 'a': + switch (first[1]) + { + case 'a': + db.names.push_back("operator&&"); + first += 2; + break; + case 'd': + case 'n': + db.names.push_back("operator&"); + first += 2; + break; + case 'N': + db.names.push_back("operator&="); + first += 2; + break; + case 'S': + db.names.push_back("operator="); + first += 2; + break; + } + break; + case 'c': + switch (first[1]) + { + case 'l': + db.names.push_back("operator()"); + first += 2; + break; + case 'm': + db.names.push_back("operator,"); + first += 2; + break; + case 'o': + db.names.push_back("operator~"); + first += 2; + break; + case 'v': + { + bool try_to_parse_template_args = db.try_to_parse_template_args; + db.try_to_parse_template_args = false; + const char* t = parse_type(first+2, last, db); + db.try_to_parse_template_args = try_to_parse_template_args; + if (t != first+2) + { + db.names.back().first.insert(0, "operator "); + db.parsed_ctor_dtor_cv = true; + first = t; + } + } + break; + } + break; + case 'd': + switch (first[1]) + { + case 'a': + db.names.push_back("operator delete[]"); + first += 2; + break; + case 'e': + db.names.push_back("operator*"); + first += 2; + break; + case 'l': + db.names.push_back("operator delete"); + first += 2; + break; + case 'v': + db.names.push_back("operator/"); + first += 2; + break; + case 'V': + db.names.push_back("operator/="); + first += 2; + break; + } + break; + case 'e': + switch (first[1]) + { + case 'o': + db.names.push_back("operator^"); + first += 2; + break; + case 'O': + db.names.push_back("operator^="); + first += 2; + break; + case 'q': + db.names.push_back("operator=="); + first += 2; + break; + } + break; + case 'g': + switch (first[1]) + { + case 'e': + db.names.push_back("operator>="); + first += 2; + break; + case 't': + db.names.push_back("operator>"); + first += 2; + break; + } + break; + case 'i': + if (first[1] == 'x') + { + db.names.push_back("operator[]"); + first += 2; + } + break; + case 'l': + switch (first[1]) + { + case 'e': + db.names.push_back("operator<="); + first += 2; + break; + case 's': + db.names.push_back("operator<<"); + first += 2; + break; + case 'S': + db.names.push_back("operator<<="); + first += 2; + break; + case 't': + db.names.push_back("operator<"); + first += 2; + break; + } + break; + case 'm': + switch (first[1]) + { + case 'i': + db.names.push_back("operator-"); + first += 2; + break; + case 'I': + db.names.push_back("operator-="); + first += 2; + break; + case 'l': + db.names.push_back("operator*"); + first += 2; + break; + case 'L': + db.names.push_back("operator*="); + first += 2; + break; + case 'm': + db.names.push_back("operator--"); + first += 2; + break; + } + break; + case 'n': + switch (first[1]) + { + case 'a': + db.names.push_back("operator new[]"); + first += 2; + break; + case 'e': + db.names.push_back("operator!="); + first += 2; + break; + case 'g': + db.names.push_back("operator-"); + first += 2; + break; + case 't': + db.names.push_back("operator!"); + first += 2; + break; + case 'w': + db.names.push_back("operator new"); + first += 2; + break; + } + break; + case 'o': + switch (first[1]) + { + case 'o': + db.names.push_back("operator||"); + first += 2; + break; + case 'r': + db.names.push_back("operator|"); + first += 2; + break; + case 'R': + db.names.push_back("operator|="); + first += 2; + break; + } + break; + case 'p': + switch (first[1]) + { + case 'm': + db.names.push_back("operator->*"); + first += 2; + break; + case 'l': + db.names.push_back("operator+"); + first += 2; + break; + case 'L': + db.names.push_back("operator+="); + first += 2; + break; + case 'p': + db.names.push_back("operator++"); + first += 2; + break; + case 's': + db.names.push_back("operator+"); + first += 2; + break; + case 't': + db.names.push_back("operator->"); + first += 2; + break; + } + break; + case 'q': + if (first[1] == 'u') + { + db.names.push_back("operator?"); + first += 2; + } + break; + case 'r': + switch (first[1]) + { + case 'm': + db.names.push_back("operator%"); + first += 2; + break; + case 'M': + db.names.push_back("operator%="); + first += 2; + break; + case 's': + db.names.push_back("operator>>"); + first += 2; + break; + case 'S': + db.names.push_back("operator>>="); + first += 2; + break; + } + break; + case 'v': + if (std::isdigit(first[1])) + { + const char* t = parse_source_name(first+2, last, db); + if (t != first+2) + { + db.names.back().first.insert(0, "operator "); + first = t; + } + } + break; + } + } + return first; +} + +template <class C> +const char* +parse_integer_literal(const char* first, const char* last, const typename C::String& lit, C& db) +{ + const char* t = parse_number(first, last); + if (t != first && t != last && *t == 'E') + { + if (lit.size() > 3) + db.names.push_back("(" + lit + ")"); + else + db.names.emplace_back(); + if (*first == 'n') + { + db.names.back().first += '-'; + ++first; + } + db.names.back().first.append(first, t); + if (lit.size() <= 3) + db.names.back().first += lit; + first = t+1; + } + return first; +} + +// <expr-primary> ::= L <type> <value number> E # integer literal +// ::= L <type> <value float> E # floating literal +// ::= L <string type> E # string literal +// ::= L <nullptr type> E # nullptr literal (i.e., "LDnE") +// ::= L <type> <real-part float> _ <imag-part float> E # complex floating point literal (C 2000) +// ::= L <mangled-name> E # external name + +template <class C> +const char* +parse_expr_primary(const char* first, const char* last, C& db) +{ + if (last - first >= 4 && *first == 'L') + { + switch (first[1]) + { + case 'w': + { + const char* t = parse_integer_literal(first+2, last, "wchar_t", db); + if (t != first+2) + first = t; + } + break; + case 'b': + if (first[3] == 'E') + { + switch (first[2]) + { + case '0': + db.names.push_back("false"); + first += 4; + break; + case '1': + db.names.push_back("true"); + first += 4; + break; + } + } + break; + case 'c': + { + const char* t = parse_integer_literal(first+2, last, "char", db); + if (t != first+2) + first = t; + } + break; + case 'a': + { + const char* t = parse_integer_literal(first+2, last, "signed char", db); + if (t != first+2) + first = t; + } + break; + case 'h': + { + const char* t = parse_integer_literal(first+2, last, "unsigned char", db); + if (t != first+2) + first = t; + } + break; + case 's': + { + const char* t = parse_integer_literal(first+2, last, "short", db); + if (t != first+2) + first = t; + } + break; + case 't': + { + const char* t = parse_integer_literal(first+2, last, "unsigned short", db); + if (t != first+2) + first = t; + } + break; + case 'i': + { + const char* t = parse_integer_literal(first+2, last, "", db); + if (t != first+2) + first = t; + } + break; + case 'j': + { + const char* t = parse_integer_literal(first+2, last, "u", db); + if (t != first+2) + first = t; + } + break; + case 'l': + { + const char* t = parse_integer_literal(first+2, last, "l", db); + if (t != first+2) + first = t; + } + break; + case 'm': + { + const char* t = parse_integer_literal(first+2, last, "ul", db); + if (t != first+2) + first = t; + } + break; + case 'x': + { + const char* t = parse_integer_literal(first+2, last, "ll", db); + if (t != first+2) + first = t; + } + break; + case 'y': + { + const char* t = parse_integer_literal(first+2, last, "ull", db); + if (t != first+2) + first = t; + } + break; + case 'n': + { + const char* t = parse_integer_literal(first+2, last, "__int128", db); + if (t != first+2) + first = t; + } + break; + case 'o': + { + const char* t = parse_integer_literal(first+2, last, "unsigned __int128", db); + if (t != first+2) + first = t; + } + break; + case 'f': + { + const char* t = parse_floating_number<float>(first+2, last, db); + if (t != first+2) + first = t; + } + break; + case 'd': + { + const char* t = parse_floating_number<double>(first+2, last, db); + if (t != first+2) + first = t; + } + break; + case 'e': + { + const char* t = parse_floating_number<long double>(first+2, last, db); + if (t != first+2) + first = t; + } + break; + case '_': + if (first[2] == 'Z') + { + const char* t = parse_encoding(first+3, last, db); + if (t != first+3 && t != last && *t == 'E') + first = t+1; + } + break; + case 'T': + // Invalid mangled name per + // http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html + break; + default: + { + // might be named type + const char* t = parse_type(first+1, last, db); + if (t != first+1 && t != last) + { + if (*t != 'E') + { + const char* n = t; + for (; n != last && isdigit(*n); ++n) + ; + if (n != t && n != last && *n == 'E') + { + db.names.back() = "(" + db.names.back().move_full() + ")" + typename C::String(t, n); + first = n+1; + break; + } + } + else + { + first = t+1; + break; + } + } + } + } + } + return first; +} + +template <class String> +String +base_name(String& s) +{ + if (s.empty()) + return s; + if (s == "std::string") + { + s = "std::basic_string<char, std::char_traits<char>, std::allocator<char> >"; + return "basic_string"; + } + if (s == "std::istream") + { + s = "std::basic_istream<char, std::char_traits<char> >"; + return "basic_istream"; + } + if (s == "std::ostream") + { + s = "std::basic_ostream<char, std::char_traits<char> >"; + return "basic_ostream"; + } + if (s == "std::iostream") + { + s = "std::basic_iostream<char, std::char_traits<char> >"; + return "basic_iostream"; + } + const char* const pf = s.data(); + const char* pe = pf + s.size(); + if (pe[-1] == '>') + { + unsigned c = 1; + while (true) + { + if (--pe == pf) + return String(); + if (pe[-1] == '<') + { + if (--c == 0) + { + --pe; + break; + } + } + else if (pe[-1] == '>') + ++c; + } + } + const char* p0 = pe - 1; + for (; p0 != pf; --p0) + { + if (*p0 == ':') + { + ++p0; + break; + } + } + return String(p0, pe); +} + +// <ctor-dtor-name> ::= C1 # complete object constructor +// ::= C2 # base object constructor +// ::= C3 # complete object allocating constructor +// extension ::= C5 # ? +// ::= D0 # deleting destructor +// ::= D1 # complete object destructor +// ::= D2 # base object destructor +// extension ::= D5 # ? + +template <class C> +const char* +parse_ctor_dtor_name(const char* first, const char* last, C& db) +{ + if (last-first >= 2 && !db.names.empty()) + { + switch (first[0]) + { + case 'C': + switch (first[1]) + { + case '1': + case '2': + case '3': + case '5': + db.names.push_back(base_name(db.names.back().first)); + first += 2; + db.parsed_ctor_dtor_cv = true; + break; + } + break; + case 'D': + switch (first[1]) + { + case '0': + case '1': + case '2': + case '5': + db.names.push_back("~" + base_name(db.names.back().first)); + first += 2; + db.parsed_ctor_dtor_cv = true; + break; + } + break; + } + } + return first; +} + +// <unnamed-type-name> ::= Ut [ <nonnegative number> ] _ +// ::= <closure-type-name> +// +// <closure-type-name> ::= Ul <lambda-sig> E [ <nonnegative number> ] _ +// +// <lambda-sig> ::= <parameter type>+ # Parameter types or "v" if the lambda has no parameters + +template <class C> +const char* +parse_unnamed_type_name(const char* first, const char* last, C& db) +{ + if (last - first > 2 && first[0] == 'U') + { + char type = first[1]; + switch (type) + { + case 't': + { + db.names.push_back(typename C::String("'unnamed")); + const char* t0 = first+2; + if (t0 == last) + { + db.names.pop_back(); + return first; + } + if (std::isdigit(*t0)) + { + const char* t1 = t0 + 1; + while (t1 != last && std::isdigit(*t1)) + ++t1; + db.names.back().first.append(t0, t1); + t0 = t1; + } + db.names.back().first.push_back('\''); + if (t0 == last || *t0 != '_') + { + db.names.pop_back(); + return first; + } + first = t0 + 1; + } + break; + case 'l': + { + db.names.push_back(typename C::String("'lambda'(")); + const char* t0 = first+2; + if (first[2] == 'v') + { + db.names.back().first += ')'; + ++t0; + } + else + { + const char* t1 = parse_type(t0, last, db); + if (t1 == t0) + { + db.names.pop_back(); + return first; + } + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first.append(tmp); + t0 = t1; + while (true) + { + t1 = parse_type(t0, last, db); + if (t1 == t0) + break; + tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + } + t0 = t1; + } + db.names.back().first.append(")"); + } + if (t0 == last || *t0 != 'E') + { + db.names.pop_back(); + return first; + } + ++t0; + if (t0 == last) + { + db.names.pop_back(); + return first; + } + if (std::isdigit(*t0)) + { + const char* t1 = t0 + 1; + while (t1 != last && std::isdigit(*t1)) + ++t1; + db.names.back().first.insert(db.names.back().first.begin()+7, t0, t1); + t0 = t1; + } + if (t0 == last || *t0 != '_') + { + db.names.pop_back(); + return first; + } + first = t0 + 1; + } + break; + } + } + return first; +} + +// <unqualified-name> ::= <operator-name> +// ::= <ctor-dtor-name> +// ::= <source-name> +// ::= <unnamed-type-name> + +template <class C> +const char* +parse_unqualified_name(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t; + switch (*first) + { + case 'C': + case 'D': + t = parse_ctor_dtor_name(first, last, db); + if (t != first) + first = t; + break; + case 'U': + t = parse_unnamed_type_name(first, last, db); + if (t != first) + first = t; + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + t = parse_source_name(first, last, db); + if (t != first) + first = t; + break; + default: + t = parse_operator_name(first, last, db); + if (t != first) + first = t; + break; + }; + } + return first; +} + +// <unscoped-name> ::= <unqualified-name> +// ::= St <unqualified-name> # ::std:: +// extension ::= StL<unqualified-name> + +template <class C> +const char* +parse_unscoped_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + const char* t0 = first; + bool St = false; + if (first[0] == 'S' && first[1] == 't') + { + t0 += 2; + St = true; + if (t0 != last && *t0 == 'L') + ++t0; + } + const char* t1 = parse_unqualified_name(t0, last, db); + if (t1 != t0) + { + if (St) + db.names.back().first.insert(0, "std::"); + first = t1; + } + } + return first; +} + +// at <type> # alignof (a type) + +template <class C> +const char* +parse_alignof_type(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'a' && first[1] == 't') + { + const char* t = parse_type(first+2, last, db); + if (t != first+2) + { + db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +// az <expression> # alignof (a expression) + +template <class C> +const char* +parse_alignof_expr(const char* first, const char* last, C& db) +{ + if (last - first >= 3 && first[0] == 'a' && first[1] == 'z') + { + const char* t = parse_expression(first+2, last, db); + if (t != first+2) + { + db.names.back().first = "alignof (" + db.names.back().move_full() + ")"; + first = t; + } + } + return first; +} + +template <class C> +const char* +parse_noexcept_expression(const char* first, const char* last, C& db) +{ + const char* t1 = parse_expression(first, last, db); + if (t1 != first) + { + db.names.back().first = "noexcept (" + db.names.back().move_full() + ")"; + first = t1; + } + return first; +} + +template <class C> +const char* +parse_prefix_expression(const char* first, const char* last, const typename C::String& op, C& db) +{ + const char* t1 = parse_expression(first, last, db); + if (t1 != first) + { + db.names.back().first = op + "(" + db.names.back().move_full() + ")"; + first = t1; + } + return first; +} + +template <class C> +const char* +parse_binary_expression(const char* first, const char* last, const typename C::String& op, C& db) +{ + const char* t1 = parse_expression(first, last, db); + if (t1 != first) + { + const char* t2 = parse_expression(t1, last, db); + if (t2 != t1) + { + auto op2 = db.names.back().move_full(); + db.names.pop_back(); + auto op1 = db.names.back().move_full(); + auto& nm = db.names.back().first; + nm.clear(); + if (op == ">") + nm += '('; + nm += "(" + op1 + ") " + op + " (" + op2 + ")"; + if (op == ">") + nm += ')'; + first = t2; + } + else + db.names.pop_back(); + } + return first; +} + +// <expression> ::= <unary operator-name> <expression> +// ::= <binary operator-name> <expression> <expression> +// ::= <ternary operator-name> <expression> <expression> <expression> +// ::= cl <expression>+ E # call +// ::= cv <type> <expression> # conversion with one argument +// ::= cv <type> _ <expression>* E # conversion with a different number of arguments +// ::= [gs] nw <expression>* _ <type> E # new (expr-list) type +// ::= [gs] nw <expression>* _ <type> <initializer> # new (expr-list) type (init) +// ::= [gs] na <expression>* _ <type> E # new[] (expr-list) type +// ::= [gs] na <expression>* _ <type> <initializer> # new[] (expr-list) type (init) +// ::= [gs] dl <expression> # delete expression +// ::= [gs] da <expression> # delete[] expression +// ::= pp_ <expression> # prefix ++ +// ::= mm_ <expression> # prefix -- +// ::= ti <type> # typeid (type) +// ::= te <expression> # typeid (expression) +// ::= dc <type> <expression> # dynamic_cast<type> (expression) +// ::= sc <type> <expression> # static_cast<type> (expression) +// ::= cc <type> <expression> # const_cast<type> (expression) +// ::= rc <type> <expression> # reinterpret_cast<type> (expression) +// ::= st <type> # sizeof (a type) +// ::= sz <expression> # sizeof (an expression) +// ::= at <type> # alignof (a type) +// ::= az <expression> # alignof (an expression) +// ::= nx <expression> # noexcept (expression) +// ::= <template-param> +// ::= <function-param> +// ::= dt <expression> <unresolved-name> # expr.name +// ::= pt <expression> <unresolved-name> # expr->name +// ::= ds <expression> <expression> # expr.*expr +// ::= sZ <template-param> # size of a parameter pack +// ::= sZ <function-param> # size of a function parameter pack +// ::= sp <expression> # pack expansion +// ::= tw <expression> # throw expression +// ::= tr # throw with no operand (rethrow) +// ::= <unresolved-name> # f(p), N::f(p), ::f(p), +// # freestanding dependent name (e.g., T::x), +// # objectless nonstatic member reference +// ::= <expr-primary> + +template <class C> +const char* +parse_expression(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + const char* t = first; + bool parsed_gs = false; + if (last - first >= 4 && t[0] == 'g' && t[1] == 's') + { + t += 2; + parsed_gs = true; + } + switch (*t) + { + case 'L': + first = parse_expr_primary(first, last, db); + break; + case 'T': + first = parse_template_param(first, last, db); + break; + case 'f': + first = parse_function_param(first, last, db); + break; + case 'a': + switch (t[1]) + { + case 'a': + t = parse_binary_expression(first+2, last, "&&", db); + if (t != first+2) + first = t; + break; + case 'd': + t = parse_prefix_expression(first+2, last, "&", db); + if (t != first+2) + first = t; + break; + case 'n': + t = parse_binary_expression(first+2, last, "&", db); + if (t != first+2) + first = t; + break; + case 'N': + t = parse_binary_expression(first+2, last, "&=", db); + if (t != first+2) + first = t; + break; + case 'S': + t = parse_binary_expression(first+2, last, "=", db); + if (t != first+2) + first = t; + break; + case 't': + first = parse_alignof_type(first, last, db); + break; + case 'z': + first = parse_alignof_expr(first, last, db); + break; + } + break; + case 'c': + switch (t[1]) + { + case 'c': + first = parse_const_cast_expr(first, last, db); + break; + case 'l': + first = parse_call_expr(first, last, db); + break; + case 'm': + t = parse_binary_expression(first+2, last, ",", db); + if (t != first+2) + first = t; + break; + case 'o': + t = parse_prefix_expression(first+2, last, "~", db); + if (t != first+2) + first = t; + break; + case 'v': + first = parse_conversion_expr(first, last, db); + break; + } + break; + case 'd': + switch (t[1]) + { + case 'a': + { + const char* t1 = parse_expression(t+2, last, db); + if (t1 != t+2) + { + db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + + "delete[] " + db.names.back().move_full(); + first = t1; + } + } + break; + case 'c': + first = parse_dynamic_cast_expr(first, last, db); + break; + case 'e': + t = parse_prefix_expression(first+2, last, "*", db); + if (t != first+2) + first = t; + break; + case 'l': + { + const char* t1 = parse_expression(t+2, last, db); + if (t1 != t+2) + { + db.names.back().first = (parsed_gs ? typename C::String("::") : typename C::String()) + + "delete " + db.names.back().move_full(); + first = t1; + } + } + break; + case 'n': + return parse_unresolved_name(first, last, db); + case 's': + first = parse_dot_star_expr(first, last, db); + break; + case 't': + first = parse_dot_expr(first, last, db); + break; + case 'v': + t = parse_binary_expression(first+2, last, "/", db); + if (t != first+2) + first = t; + break; + case 'V': + t = parse_binary_expression(first+2, last, "/=", db); + if (t != first+2) + first = t; + break; + } + break; + case 'e': + switch (t[1]) + { + case 'o': + t = parse_binary_expression(first+2, last, "^", db); + if (t != first+2) + first = t; + break; + case 'O': + t = parse_binary_expression(first+2, last, "^=", db); + if (t != first+2) + first = t; + break; + case 'q': + t = parse_binary_expression(first+2, last, "==", db); + if (t != first+2) + first = t; + break; + } + break; + case 'g': + switch (t[1]) + { + case 'e': + t = parse_binary_expression(first+2, last, ">=", db); + if (t != first+2) + first = t; + break; + case 't': + t = parse_binary_expression(first+2, last, ">", db); + if (t != first+2) + first = t; + break; + } + break; + case 'i': + if (t[1] == 'x') + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + const char* t2 = parse_expression(t1, last, db); + if (t2 != t1) + { + auto op2 = db.names.back().move_full(); + db.names.pop_back(); + auto op1 = db.names.back().move_full(); + db.names.back() = "(" + op1 + ")[" + op2 + "]"; + first = t2; + } + else + db.names.pop_back(); + } + } + break; + case 'l': + switch (t[1]) + { + case 'e': + t = parse_binary_expression(first+2, last, "<=", db); + if (t != first+2) + first = t; + break; + case 's': + t = parse_binary_expression(first+2, last, "<<", db); + if (t != first+2) + first = t; + break; + case 'S': + t = parse_binary_expression(first+2, last, "<<=", db); + if (t != first+2) + first = t; + break; + case 't': + t = parse_binary_expression(first+2, last, "<", db); + if (t != first+2) + first = t; + break; + } + break; + case 'm': + switch (t[1]) + { + case 'i': + t = parse_binary_expression(first+2, last, "-", db); + if (t != first+2) + first = t; + break; + case 'I': + t = parse_binary_expression(first+2, last, "-=", db); + if (t != first+2) + first = t; + break; + case 'l': + t = parse_binary_expression(first+2, last, "*", db); + if (t != first+2) + first = t; + break; + case 'L': + t = parse_binary_expression(first+2, last, "*=", db); + if (t != first+2) + first = t; + break; + case 'm': + if (first+2 != last && first[2] == '_') + { + t = parse_prefix_expression(first+3, last, "--", db); + if (t != first+3) + first = t; + } + else + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + db.names.back() = "(" + db.names.back().move_full() + ")--"; + first = t1; + } + } + break; + } + break; + case 'n': + switch (t[1]) + { + case 'a': + case 'w': + first = parse_new_expr(first, last, db); + break; + case 'e': + t = parse_binary_expression(first+2, last, "!=", db); + if (t != first+2) + first = t; + break; + case 'g': + t = parse_prefix_expression(first+2, last, "-", db); + if (t != first+2) + first = t; + break; + case 't': + t = parse_prefix_expression(first+2, last, "!", db); + if (t != first+2) + first = t; + break; + case 'x': + t = parse_noexcept_expression(first+2, last, db); + if (t != first+2) + first = t; + break; + } + break; + case 'o': + switch (t[1]) + { + case 'n': + return parse_unresolved_name(first, last, db); + case 'o': + t = parse_binary_expression(first+2, last, "||", db); + if (t != first+2) + first = t; + break; + case 'r': + t = parse_binary_expression(first+2, last, "|", db); + if (t != first+2) + first = t; + break; + case 'R': + t = parse_binary_expression(first+2, last, "|=", db); + if (t != first+2) + first = t; + break; + } + break; + case 'p': + switch (t[1]) + { + case 'm': + t = parse_binary_expression(first+2, last, "->*", db); + if (t != first+2) + first = t; + break; + case 'l': + t = parse_binary_expression(first+2, last, "+", db); + if (t != first+2) + first = t; + break; + case 'L': + t = parse_binary_expression(first+2, last, "+=", db); + if (t != first+2) + first = t; + break; + case 'p': + if (first+2 != last && first[2] == '_') + { + t = parse_prefix_expression(first+3, last, "++", db); + if (t != first+3) + first = t; + } + else + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + db.names.back() = "(" + db.names.back().move_full() + ")++"; + first = t1; + } + } + break; + case 's': + t = parse_prefix_expression(first+2, last, "+", db); + if (t != first+2) + first = t; + break; + case 't': + first = parse_arrow_expr(first, last, db); + break; + } + break; + case 'q': + if (t[1] == 'u') + { + const char* t1 = parse_expression(first+2, last, db); + if (t1 != first+2) + { + const char* t2 = parse_expression(t1, last, db); + if (t2 != t1) + { + const char* t3 = parse_expression(t2, last, db); + if (t3 != t2) + { + auto op3 = db.names.back().move_full(); + db.names.pop_back(); + auto op2 = db.names.back().move_full(); + db.names.pop_back(); + auto op1 = db.names.back().move_full(); + db.names.back() = "(" + op1 + ") ? (" + op2 + ") : (" + op3 + ")"; + first = t3; + } + else + { + db.names.pop_back(); + db.names.pop_back(); + } + } + else + db.names.pop_back(); + } + } + break; + case 'r': + switch (t[1]) + { + case 'c': + first = parse_reinterpret_cast_expr(first, last, db); + break; + case 'm': + t = parse_binary_expression(first+2, last, "%", db); + if (t != first+2) + first = t; + break; + case 'M': + t = parse_binary_expression(first+2, last, "%=", db); + if (t != first+2) + first = t; + break; + case 's': + t = parse_binary_expression(first+2, last, ">>", db); + if (t != first+2) + first = t; + break; + case 'S': + t = parse_binary_expression(first+2, last, ">>=", db); + if (t != first+2) + first = t; + break; + } + break; + case 's': + switch (t[1]) + { + case 'c': + first = parse_static_cast_expr(first, last, db); + break; + case 'p': + first = parse_pack_expansion(first, last, db); + break; + case 'r': + return parse_unresolved_name(first, last, db); + case 't': + first = parse_sizeof_type_expr(first, last, db); + break; + case 'z': + first = parse_sizeof_expr_expr(first, last, db); + break; + case 'Z': + if (last - t >= 3) + { + switch (t[2]) + { + case 'T': + first = parse_sizeof_param_pack_expr(first, last, db); + break; + case 'f': + first = parse_sizeof_function_param_pack_expr(first, last, db); + break; + } + } + break; + } + break; + case 't': + switch (t[1]) + { + case 'e': + case 'i': + first = parse_typeid_expr(first, last, db); + break; + case 'r': + db.names.push_back("throw"); + first += 2; + break; + case 'w': + first = parse_throw_expr(first, last, db); + break; + } + break; + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + return parse_unresolved_name(first, last, db); + } + } + return first; +} + +// <template-arg> ::= <type> # type or template +// ::= X <expression> E # expression +// ::= <expr-primary> # simple expressions +// ::= J <template-arg>* E # argument pack +// ::= LZ <encoding> E # extension + +template <class C> +const char* +parse_template_arg(const char* first, const char* last, C& db) +{ + if (first != last) + { + const char* t; + switch (*first) + { + case 'X': + t = parse_expression(first+1, last, db); + if (t != first+1) + { + if (t != last && *t == 'E') + first = t+1; + } + break; + case 'J': + t = first+1; + if (t == last) + return first; + while (*t != 'E') + { + const char* t1 = parse_template_arg(t, last, db); + if (t1 == t) + return first; + t = t1; + } + first = t+1; + break; + case 'L': + // <expr-primary> or LZ <encoding> E + if (first+1 != last && first[1] == 'Z') + { + t = parse_encoding(first+2, last, db); + if (t != first+2 && t != last && *t == 'E') + first = t+1; + } + else + first = parse_expr_primary(first, last, db); + break; + default: + // <type> + first = parse_type(first, last, db); + break; + } + } + return first; +} + +// <template-args> ::= I <template-arg>* E +// extension, the abi says <template-arg>+ + +template <class C> +const char* +parse_template_args(const char* first, const char* last, C& db) +{ + if (last - first >= 2 && *first == 'I') + { + if (db.tag_templates) + db.template_param.back().clear(); + const char* t = first+1; + typename C::String args("<"); + while (*t != 'E') + { + if (db.tag_templates) + db.template_param.emplace_back(db.names.get_allocator()); + size_t k0 = db.names.size(); + const char* t1 = parse_template_arg(t, last, db); + size_t k1 = db.names.size(); + if (db.tag_templates) + db.template_param.pop_back(); + if (t1 == t || t1 == last) + return first; + if (db.tag_templates) + { + db.template_param.back().emplace_back(db.names.get_allocator()); + for (size_t k = k0; k < k1; ++k) + db.template_param.back().back().push_back(db.names[k]); + } + for (size_t k = k0; k < k1; ++k) + { + if (args.size() > 1) + args += ", "; + args += db.names[k].move_full(); + } + for (; k1 != k0; --k1) + db.names.pop_back(); + t = t1; + } + first = t + 1; + if (args.back() != '>') + args += ">"; + else + args += " >"; + db.names.push_back(std::move(args)); + + } + return first; +} + +// <nested-name> ::= N [<CV-qualifiers>] [<ref-qualifier>] <prefix> <unqualified-name> E +// ::= N [<CV-qualifiers>] [<ref-qualifier>] <template-prefix> <template-args> E +// +// <prefix> ::= <prefix> <unqualified-name> +// ::= <template-prefix> <template-args> +// ::= <template-param> +// ::= <decltype> +// ::= # empty +// ::= <substitution> +// ::= <prefix> <data-member-prefix> +// extension ::= L +// +// <template-prefix> ::= <prefix> <template unqualified-name> +// ::= <template-param> +// ::= <substitution> + +template <class C> +const char* +parse_nested_name(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'N') + { + unsigned cv; + const char* t0 = parse_cv_qualifiers(first+1, last, cv); + if (t0 == last) + return first; + db.ref = 0; + if (*t0 == 'R') + { + db.ref = 1; + ++t0; + } + else if (*t0 == 'O') + { + db.ref = 2; + ++t0; + } + db.names.emplace_back(); + if (last - t0 >= 2 && t0[0] == 'S' && t0[1] == 't') + { + t0 += 2; + db.names.back().first = "std"; + } + if (t0 == last) + { + db.names.pop_back(); + return first; + } + bool pop_subs = false; + while (*t0 != 'E') + { + const char* t1; + switch (*t0) + { + case 'S': + if (t0 + 1 != last && t0[1] == 't') + goto do_parse_unqualified_name; + t1 = parse_substitution(t0, last, db); + if (t1 != t0 && t1 != last) + { + auto name = db.names.back().move_full(); + db.names.pop_back(); + if (!db.names.back().first.empty()) + { + db.names.back().first += "::" + name; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + } + else + db.names.back().first = name; + pop_subs = true; + t0 = t1; + } + else + return first; + break; + case 'T': + t1 = parse_template_param(t0, last, db); + if (t1 != t0 && t1 != last) + { + auto name = db.names.back().move_full(); + db.names.pop_back(); + if (!db.names.back().first.empty()) + db.names.back().first += "::" + name; + else + db.names.back().first = name; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + pop_subs = true; + t0 = t1; + } + else + return first; + break; + case 'D': + if (t0 + 1 != last && t0[1] != 't' && t0[1] != 'T') + goto do_parse_unqualified_name; + t1 = parse_decltype(t0, last, db); + if (t1 != t0 && t1 != last) + { + auto name = db.names.back().move_full(); + db.names.pop_back(); + if (!db.names.back().first.empty()) + db.names.back().first += "::" + name; + else + db.names.back().first = name; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + pop_subs = true; + t0 = t1; + } + else + return first; + break; + case 'I': + t1 = parse_template_args(t0, last, db); + if (t1 != t0 && t1 != last) + { + auto name = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += name; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + t0 = t1; + } + else + return first; + break; + case 'L': + if (++t0 == last) + return first; + break; + default: + do_parse_unqualified_name: + t1 = parse_unqualified_name(t0, last, db); + if (t1 != t0 && t1 != last) + { + auto name = db.names.back().move_full(); + db.names.pop_back(); + if (!db.names.back().first.empty()) + db.names.back().first += "::" + name; + else + db.names.back().first = name; + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + pop_subs = true; + t0 = t1; + } + else + return first; + } + } + first = t0 + 1; + db.cv = cv; + if (pop_subs && !db.subs.empty()) + db.subs.pop_back(); + } + return first; +} + +// <discriminator> := _ <non-negative number> # when number < 10 +// := __ <non-negative number> _ # when number >= 10 +// extension := decimal-digit+ + +const char* +parse_discriminator(const char* first, const char* last) +{ + // parse but ignore discriminator + if (first != last) + { + if (*first == '_') + { + const char* t1 = first+1; + if (t1 != last) + { + if (std::isdigit(*t1)) + first = t1+1; + else if (*t1 == '_') + { + for (++t1; t1 != last && std::isdigit(*t1); ++t1) + ; + if (t1 != last && *t1 == '_') + first = t1 + 1; + } + } + } + else if (std::isdigit(*first)) + { + const char* t1 = first+1; + for (; t1 != last && std::isdigit(*t1); ++t1) + ; + first = t1; + } + } + return first; +} + +// <local-name> := Z <function encoding> E <entity name> [<discriminator>] +// := Z <function encoding> E s [<discriminator>] +// := Z <function encoding> Ed [ <parameter number> ] _ <entity name> + +template <class C> +const char* +parse_local_name(const char* first, const char* last, C& db) +{ + if (first != last && *first == 'Z') + { + const char* t = parse_encoding(first+1, last, db); + if (t != first+1 && t != last && *t == 'E' && ++t != last) + { + switch (*t) + { + case 's': + first = parse_discriminator(t+1, last); + db.names.back().first.append("::string literal"); + break; + case 'd': + if (++t != last) + { + const char* t1 = parse_number(t, last); + if (t1 != last && *t1 == '_') + { + t = t1 + 1; + t1 = parse_name(t, last, db); + if (t1 != t) + { + auto name = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first.append("::"); + db.names.back().first.append(name); + first = t1; + } + else + db.names.pop_back(); + } + } + break; + default: + { + const char* t1 = parse_name(t, last, db); + if (t1 != t) + { + // parse but ignore discriminator + first = parse_discriminator(t1, last); + auto name = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first.append("::"); + db.names.back().first.append(name); + } + else + db.names.pop_back(); + } + break; + } + } + } + return first; +} + +// <name> ::= <nested-name> // N +// ::= <local-name> # See Scope Encoding below // Z +// ::= <unscoped-template-name> <template-args> +// ::= <unscoped-name> + +// <unscoped-template-name> ::= <unscoped-name> +// ::= <substitution> + +template <class C> +const char* +parse_name(const char* first, const char* last, C& db) +{ + if (last - first >= 2) + { + const char* t0 = first; + // extension: ignore L here + if (*t0 == 'L') + ++t0; + switch (*t0) + { + case 'N': + { + const char* t1 = parse_nested_name(t0, last, db); + if (t1 != t0) + first = t1; + break; + } + case 'Z': + { + const char* t1 = parse_local_name(t0, last, db); + if (t1 != t0) + first = t1; + break; + } + default: + { + const char* t1 = parse_unscoped_name(t0, last, db); + if (t1 != t0) + { + if (t1 != last && *t1 == 'I') // <unscoped-template-name> <template-args> + { + db.subs.push_back(typename C::sub_type(1, db.names.back(), db.names.get_allocator())); + t0 = t1; + t1 = parse_template_args(t0, last, db); + if (t1 != t0) + { + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += tmp; + first = t1; + } + } + else // <unscoped-name> + first = t1; + } + else + { // try <substitution> <template-args> + t1 = parse_substitution(t0, last, db); + if (t1 != t0 && t1 != last && *t1 == 'I') + { + t0 = t1; + t1 = parse_template_args(t0, last, db); + if (t1 != t0) + { + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += tmp; + first = t1; + } + } + } + break; + } + } + } + return first; +} + +// <call-offset> ::= h <nv-offset> _ +// ::= v <v-offset> _ +// +// <nv-offset> ::= <offset number> +// # non-virtual base override +// +// <v-offset> ::= <offset number> _ <virtual offset number> +// # virtual base override, with vcall offset + +const char* +parse_call_offset(const char* first, const char* last) +{ + if (first != last) + { + switch (*first) + { + case 'h': + { + const char* t = parse_number(first + 1, last); + if (t != first + 1 && t != last && *t == '_') + first = t + 1; + } + break; + case 'v': + { + const char* t = parse_number(first + 1, last); + if (t != first + 1 && t != last && *t == '_') + { + const char* t2 = parse_number(++t, last); + if (t2 != t && t2 != last && *t2 == '_') + first = t2 + 1; + } + } + break; + } + } + return first; +} + +// <special-name> ::= TV <type> # virtual table +// ::= TT <type> # VTT structure (construction vtable index) +// ::= TI <type> # typeinfo structure +// ::= TS <type> # typeinfo name (null-terminated byte string) +// ::= Tc <call-offset> <call-offset> <base encoding> +// # base is the nominal target function of thunk +// # first call-offset is 'this' adjustment +// # second call-offset is result adjustment +// ::= T <call-offset> <base encoding> +// # base is the nominal target function of thunk +// ::= GV <object name> # Guard variable for one-time initialization +// # No <type> +// extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first +// extension ::= GR <object name> # reference temporary for object + +template <class C> +const char* +parse_special_name(const char* first, const char* last, C& db) +{ + if (last - first > 2) + { + const char* t; + switch (*first) + { + case 'T': + switch (first[1]) + { + case 'V': + // TV <type> # virtual table + t = parse_type(first+2, last, db); + if (t != first+2) + { + db.names.back().first.insert(0, "vtable for "); + first = t; + } + break; + case 'T': + // TT <type> # VTT structure (construction vtable index) + t = parse_type(first+2, last, db); + if (t != first+2) + { + db.names.back().first.insert(0, "VTT for "); + first = t; + } + break; + case 'I': + // TI <type> # typeinfo structure + t = parse_type(first+2, last, db); + if (t != first+2) + { + db.names.back().first.insert(0, "typeinfo for "); + first = t; + } + break; + case 'S': + // TS <type> # typeinfo name (null-terminated byte string) + t = parse_type(first+2, last, db); + if (t != first+2) + { + db.names.back().first.insert(0, "typeinfo name for "); + first = t; + } + break; + case 'c': + // Tc <call-offset> <call-offset> <base encoding> + { + const char* t0 = parse_call_offset(first+2, last); + if (t0 == first+2) + break; + const char* t1 = parse_call_offset(t0, last); + if (t1 == t0) + break; + t = parse_encoding(t1, last, db); + if (t != t1) + { + db.names.back().first.insert(0, "covariant return thunk to "); + first = t; + } + } + break; + case 'C': + // extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first + t = parse_type(first+2, last, db); + if (t != first+2) + { + const char* t0 = parse_number(t, last); + if (t0 != t && t0 != last && *t0 == '_') + { + const char* t1 = parse_type(++t0, last, db); + if (t1 != t0) + { + auto left = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first = "construction vtable for " + + std::move(left) + "-in-" + + db.names.back().move_full(); + first = t1; + } + } + } + break; + default: + // T <call-offset> <base encoding> + { + const char* t0 = parse_call_offset(first+1, last); + if (t0 == first+1) + break; + t = parse_encoding(t0, last, db); + if (t != t0) + { + if (first[2] == 'v') + { + db.names.back().first.insert(0, "virtual thunk to "); + first = t; + } + else + { + db.names.back().first.insert(0, "non-virtual thunk to "); + first = t; + } + } + } + break; + } + break; + case 'G': + switch (first[1]) + { + case 'V': + // GV <object name> # Guard variable for one-time initialization + t = parse_name(first+2, last, db); + if (t != first+2) + { + db.names.back().first.insert(0, "guard variable for "); + first = t; + } + break; + case 'R': + // extension ::= GR <object name> # reference temporary for object + t = parse_name(first+2, last, db); + if (t != first+2) + { + db.names.back().first.insert(0, "reference temporary for "); + first = t; + } + break; + } + break; + } + } + return first; +} + +// <encoding> ::= <function name> <bare-function-type> +// ::= <data name> +// ::= <special-name> + +template <class C> +const char* +parse_encoding(const char* first, const char* last, C& db) +{ + if (first != last) + { + switch (*first) + { + case 'G': + case 'T': + first = parse_special_name(first, last, db); + break; + default: + { + const char* t = parse_name(first, last, db); + unsigned cv = db.cv; + unsigned ref = db.ref; + if (t != first) + { + if (t != last && *t != 'E' && *t != '.') + { + bool tag_templates = db.tag_templates; + db.tag_templates = false; + const char* t2; + typename C::String ret2; + const typename C::String& nm = db.names.back().first; + if (!db.parsed_ctor_dtor_cv && nm.back() == '>' && nm[nm.size()-2] != '-' + && nm[nm.size()-2] != '>') + { + t2 = parse_type(t, last, db); + if (t2 == t) + return first; + auto ret1 = std::move(db.names.back().first); + ret2 = std::move(db.names.back().second); + if (ret2.empty()) + ret1 += ' '; + db.names.pop_back(); + db.names.back().first.insert(0, ret1); + t = t2; + } + db.names.back().first += '('; + if (t != last && *t == 'v') + { + ++t; + } + else + { + bool first_arg = true; + while (true) + { + size_t k0 = db.names.size(); + t2 = parse_type(t, last, db); + size_t k1 = db.names.size(); + if (t2 == t) + break; + if (k1 > k0) + { + typename C::String tmp; + for (size_t k = k0; k < k1; ++k) + { + if (!tmp.empty()) + tmp += ", "; + tmp += db.names[k].move_full(); + } + for (size_t k = k0; k < k1; ++k) + db.names.pop_back(); + if (!tmp.empty()) + { + if (!first_arg) + db.names.back().first += ", "; + else + first_arg = false; + db.names.back().first += tmp; + } + } + t = t2; + } + } + db.names.back().first += ')'; + if (cv & 1) + db.names.back().first.append(" const"); + if (cv & 2) + db.names.back().first.append(" volatile"); + if (cv & 4) + db.names.back().first.append(" restrict"); + if (ref == 1) + db.names.back().first.append(" &"); + else if (ref == 2) + db.names.back().first.append(" &&"); + db.names.back().first += ret2; + first = t; + db.tag_templates = tag_templates; + } + else + first = t; + } + break; + } + } + } + return first; +} + +// _block_invoke +// _block_invoke<decimal-digit>+ +// _block_invoke_<decimal-digit>+ + +template <class C> +const char* +parse_block_invoke(const char* first, const char* last, C& db) +{ + if (last - first >= 13) + { + const char test[] = "_block_invoke"; + const char* t = first; + for (int i = 0; i < 13; ++i, ++t) + { + if (*t != test[i]) + return first; + } + if (t != last) + { + if (*t == '_') + { + // must have at least 1 decimal digit + if (++t == last || !std::isdigit(*t)) + return first; + ++t; + } + // parse zero or more digits + while (t != last && isdigit(*t)) + ++t; + } + db.names.back().first.insert(0, "invocation function for block in "); + first = t; + } + return first; +} + +// extension +// <dot-suffix> := .<anything and everything> + +template <class C> +const char* +parse_dot_suffix(const char* first, const char* last, C& db) +{ + if (first != last && *first == '.') + { + db.names.back().first += " (" + typename C::String(first, last) + ")"; + first = last; + } + return first; +} + +// <block-involcaton-function> ___Z<encoding>_block_invoke +// <block-involcaton-function> ___Z<encoding>_block_invoke<decimal-digit>+ +// <block-involcaton-function> ___Z<encoding>_block_invoke_<decimal-digit>+ +// <mangled-name> ::= _Z<encoding> +// ::= <type> + +template <class C> +void +demangle(const char* first, const char* last, C& db, int& status) +{ + if (first >= last) + { + status = invalid_mangled_name; + return; + } + if (*first == '_') + { + if (last - first >= 4) + { + if (first[1] == 'Z') + { + const char* t = parse_encoding(first+2, last, db); + if (t != first+2 && t != last && *t == '.') + t = parse_dot_suffix(t, last, db); + if (t != last) + status = invalid_mangled_name; + } + else if (first[1] == '_' && first[2] == '_' && first[3] == 'Z') + { + const char* t = parse_encoding(first+4, last, db); + if (t != first+4 && t != last) + { + const char* t1 = parse_block_invoke(t, last, db); + if (t1 != last) + status = invalid_mangled_name; + } + else + status = invalid_mangled_name; + } + else + status = invalid_mangled_name; + } + else + status = invalid_mangled_name; + } + else + { + const char* t = parse_type(first, last, db); + if (t != last) + status = invalid_mangled_name; + } + if (status == success && db.names.empty()) + status = invalid_mangled_name; +} + +template <std::size_t N> +class arena +{ + static const std::size_t alignment = 16; + alignas(alignment) char buf_[N]; + char* ptr_; + + std::size_t + align_up(std::size_t n) noexcept + {return n + (alignment-1) & ~(alignment-1);} + + bool + pointer_in_buffer(char* p) noexcept + {return buf_ <= p && p <= buf_ + N;} + +public: + arena() noexcept : ptr_(buf_) {} + ~arena() {ptr_ = nullptr;} + arena(const arena&) = delete; + arena& operator=(const arena&) = delete; + + char* allocate(std::size_t n); + void deallocate(char* p, std::size_t n) noexcept; + + static constexpr std::size_t size() {return N;} + std::size_t used() const {return static_cast<std::size_t>(ptr_ - buf_);} + void reset() {ptr_ = buf_;} +}; + +template <std::size_t N> +char* +arena<N>::allocate(std::size_t n) +{ + n = align_up(n); + if (static_cast<std::size_t>(buf_ + N - ptr_) >= n) + { + char* r = ptr_; + ptr_ += n; + return r; + } + return static_cast<char*>(std::malloc(n)); +} + +template <std::size_t N> +void +arena<N>::deallocate(char* p, std::size_t n) noexcept +{ + if (pointer_in_buffer(p)) + { + n = align_up(n); + if (p + n == ptr_) + ptr_ = p; + } + else + std::free(p); +} + +template <class T, std::size_t N> +class short_alloc +{ + arena<N>& a_; +public: + typedef T value_type; + +public: + template <class _Up> struct rebind {typedef short_alloc<_Up, N> other;}; + + short_alloc(arena<N>& a) noexcept : a_(a) {} + template <class U> + short_alloc(const short_alloc<U, N>& a) noexcept + : a_(a.a_) {} + short_alloc(const short_alloc&) = default; + short_alloc& operator=(const short_alloc&) = delete; + + T* allocate(std::size_t n) + { + return reinterpret_cast<T*>(a_.allocate(n*sizeof(T))); + } + void deallocate(T* p, std::size_t n) noexcept + { + a_.deallocate(reinterpret_cast<char*>(p), n*sizeof(T)); + } + + template <class T1, std::size_t N1, class U, std::size_t M> + friend + bool + operator==(const short_alloc<T1, N1>& x, const short_alloc<U, M>& y) noexcept; + + template <class U, std::size_t M> friend class short_alloc; +}; + +template <class T, std::size_t N, class U, std::size_t M> +inline +bool +operator==(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept +{ + return N == M && &x.a_ == &y.a_; +} + +template <class T, std::size_t N, class U, std::size_t M> +inline +bool +operator!=(const short_alloc<T, N>& x, const short_alloc<U, M>& y) noexcept +{ + return !(x == y); +} + +template <class T> +class malloc_alloc +{ +public: + typedef T value_type; + + malloc_alloc() = default; + template <class U> malloc_alloc(const malloc_alloc<U>&) noexcept {} + + T* allocate(std::size_t n) + { + return static_cast<T*>(std::malloc(n*sizeof(T))); + } + void deallocate(T* p, std::size_t) noexcept + { + std::free(p); + } +}; + +template <class T, class U> +inline +bool +operator==(const malloc_alloc<T>&, const malloc_alloc<U>&) noexcept +{ + return true; +} + +template <class T, class U> +inline +bool +operator!=(const malloc_alloc<T>& x, const malloc_alloc<U>& y) noexcept +{ + return !(x == y); +} + +const size_t bs = 4 * 1024; +template <class T> using Alloc = short_alloc<T, bs>; +template <class T> using Vector = std::vector<T, Alloc<T>>; +using String = std::basic_string<char, std::char_traits<char>, malloc_alloc<char>>; + +struct string_pair +{ + String first; + String second; + + string_pair() = default; + string_pair(String f) : first(std::move(f)) {} + string_pair(String f, String s) + : first(std::move(f)), second(std::move(s)) {} + template <size_t N> + string_pair(const char (&s)[N]) : first(s, N-1) {} + + size_t size() const {return first.size() + second.size();} + String full() const {return first + second;} + String move_full() {return std::move(first) + std::move(second);} +}; + +struct Db +{ + typedef String String; + typedef Vector<string_pair> sub_type; + typedef Vector<sub_type> template_param_type; + Vector<string_pair> names; + Vector<sub_type> subs; + Vector<template_param_type> template_param; + unsigned cv; + unsigned ref; + bool parsed_ctor_dtor_cv; + bool tag_templates; + bool fix_forward_references; + bool try_to_parse_template_args; + + template <size_t N> + Db(arena<N>& ar) : + names(ar), + subs(0, names, ar), + template_param(0, subs, ar) + {} +}; + +char* +__cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status) +{ + if (mangled_name == nullptr || (buf != nullptr && n == nullptr)) + { + if (status) + *status = invalid_args; + return nullptr; + } + size_t internal_size = buf != nullptr ? *n : 0; + arena<bs> a; + Db db(a); + db.cv = 0; + db.ref = 0; + db.parsed_ctor_dtor_cv = false; + db.tag_templates = true; + db.template_param.emplace_back(a); + db.fix_forward_references = false; + db.try_to_parse_template_args = true; + int internal_status = success; + size_t len = std::strlen(mangled_name); + demangle(mangled_name, mangled_name + len, db, + internal_status); + if (internal_status == success && db.fix_forward_references && + !db.template_param.empty() && !db.template_param.front().empty()) + { + db.fix_forward_references = false; + db.tag_templates = false; + db.names.clear(); + db.subs.clear(); + demangle(mangled_name, mangled_name + len, db, internal_status); + if (db.fix_forward_references) + internal_status = invalid_mangled_name; + } + if (internal_status == success) + { + size_t sz = db.names.back().size() + 1; + if (sz > internal_size) + { + char* newbuf = static_cast<char*>(std::realloc(buf, sz)); + if (newbuf == nullptr) + { + internal_status = memory_alloc_failure; + buf = nullptr; + } + else + { + buf = newbuf; + if (n != nullptr) + *n = sz; + } + } + if (buf != nullptr) + { + db.names.back().first += db.names.back().second; + std::memcpy(buf, db.names.back().first.data(), sz-1); + buf[sz-1] = char(0); + } + } + else + buf = nullptr; + if (status) + *status = internal_status; + return buf; +} + +} // unnamed namespace + +#endif #include "llvm/ADT/DenseMap.h" @@ -199,7 +4888,11 @@ Mangled::GetDemangledName () const { // We didn't already mangle this name, demangle it and if all goes well // add it to our map. +#ifdef LLDB_USE_BUILTIN_DEMANGLER + char *demangled_name = __cxa_demangle (mangled_cstr, NULL, NULL, NULL); +#else char *demangled_name = abi::__cxa_demangle (mangled_cstr, NULL, NULL, NULL); +#endif if (demangled_name) { diff --git a/source/Core/Module.cpp b/source/Core/Module.cpp index 4252ed4cb6c6..3f3be9360efa 100644 --- a/source/Core/Module.cpp +++ b/source/Core/Module.cpp @@ -9,6 +9,7 @@ #include "lldb/lldb-python.h" +#include "lldb/Core/AddressResolverFileLine.h" #include "lldb/Core/Error.h" #include "lldb/Core/Module.h" #include "lldb/Core/DataBuffer.h" @@ -448,7 +449,8 @@ Module::ResolveFileAddress (lldb::addr_t vm_addr, Address& so_addr) } uint32_t -Module::ResolveSymbolContextForAddress (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc) +Module::ResolveSymbolContextForAddress (const Address& so_addr, uint32_t resolve_scope, SymbolContext& sc, + bool resolve_tail_call_address) { Mutex::Locker locker (m_mutex); uint32_t resolved_flags = 0; @@ -467,6 +469,10 @@ Module::ResolveSymbolContextForAddress (const Address& so_addr, uint32_t resolve 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 || @@ -474,25 +480,92 @@ Module::ResolveSymbolContextForAddress (const Address& so_addr, uint32_t resolve resolve_scope & eSymbolContextBlock || resolve_scope & eSymbolContextLineEntry ) { - SymbolVendor *symbols = GetSymbolVendor (); - if (symbols) - resolved_flags |= symbols->ResolveSymbolContext (so_addr, resolve_scope, sc); + 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)) { - SymbolVendor* sym_vendor = GetSymbolVendor(); - if (sym_vendor) + Symtab *symtab = sym_vendor->GetSymtab(); + if (symtab && so_addr.IsSectionOffset()) { - Symtab *symtab = sym_vendor->GetSymtab(); - if (symtab) + sc.symbol = symtab->FindSymbolContainingFileAddress(so_addr.GetFileAddress()); + 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 (so_addr.IsSectionOffset()) + if (sc.symbol->IsSynthetic()) { - sc.symbol = symtab->FindSymbolContainingFileAddress(so_addr.GetFileAddress()); - if (sc.symbol) - resolved_flags |= eSymbolContextSymbol; + // 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. } } } @@ -573,7 +646,7 @@ Module::FindCompileUnits (const FileSpec &path, const size_t num_compile_units = GetNumCompileUnits(); SymbolContext sc; sc.module_sp = shared_from_this(); - const bool compare_directory = path.GetDirectory(); + const bool compare_directory = (bool)path.GetDirectory(); for (size_t i=0; i<num_compile_units; ++i) { sc.comp_unit = GetCompileUnitAtIndex(i).get(); @@ -752,6 +825,27 @@ Module::FindFunctions (const RegularExpression& regex, return sc_list.GetSize() - start_size; } +void +Module::FindAddressesForLine (const lldb::TargetSP target_sp, + const FileSpec &file, uint32_t line, + Function *function, + std::vector<Address> &output_local, std::vector<Address> &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, @@ -1447,14 +1541,14 @@ Module::MatchesModuleSpec (const ModuleSpec &module_ref) const FileSpec &file_spec = module_ref.GetFileSpec(); if (file_spec) { - if (!FileSpec::Equal (file_spec, m_file, file_spec.GetDirectory())) + if (!FileSpec::Equal (file_spec, m_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 (), platform_file_spec.GetDirectory())) + if (!FileSpec::Equal (platform_file_spec, GetPlatformFileSpec (), (bool)platform_file_spec.GetDirectory())) return false; } @@ -1606,4 +1700,4 @@ Module::PrepareForFunctionNameLookup (const ConstString &name, lookup_name = name; match_name_after_lookup = false; } -}
\ No newline at end of file +} diff --git a/source/Core/ModuleList.cpp b/source/Core/ModuleList.cpp index ebc6702d3a90..215611e42429 100644 --- a/source/Core/ModuleList.cpp +++ b/source/Core/ModuleList.cpp @@ -663,7 +663,19 @@ ModuleList::FindSourceFile (const FileSpec &orig_spec, FileSpec &new_spec) const return false; } - +void +ModuleList::FindAddressesForLine (const lldb::TargetSP target_sp, + const FileSpec &file, uint32_t line, + Function *function, + std::vector<Address> &output_local, std::vector<Address> &output_extern) +{ + Mutex::Locker locker(m_modules_mutex); + collection::const_iterator pos, end = m_modules.end(); + for (pos = m_modules.begin(); pos != end; ++pos) + { + (*pos)->FindAddressesForLine(target_sp, file, line, function, output_local, output_extern); + } +} ModuleSP ModuleList::FindFirstModule (const ModuleSpec &module_spec) const diff --git a/source/Core/Opcode.cpp b/source/Core/Opcode.cpp index d9878656e3f1..978a110150f9 100644 --- a/source/Core/Opcode.cpp +++ b/source/Core/Opcode.cpp @@ -31,18 +31,18 @@ Opcode::Dump (Stream *s, uint32_t min_byte_width) int bytes_written = 0; switch (m_type) { - case Opcode::eTypeInvalid: - bytes_written = s->PutCString ("<invalid>"); + case Opcode::eTypeInvalid: + bytes_written = s->PutCString ("<invalid>"); break; - case Opcode::eType8: - bytes_written = s->Printf ("0x%2.2x", m_data.inst8); + case Opcode::eType8: + bytes_written = s->Printf ("0x%2.2x", m_data.inst8); break; case Opcode::eType16: - bytes_written = s->Printf ("0x%4.4x", m_data.inst16); + bytes_written = s->Printf ("0x%4.4x", m_data.inst16); break; case Opcode::eType16_2: case Opcode::eType32: - bytes_written = s->Printf ("0x%8.8x", m_data.inst32); + bytes_written = s->Printf ("0x%8.8x", m_data.inst32); break; case Opcode::eType64: @@ -55,12 +55,12 @@ Opcode::Dump (Stream *s, uint32_t min_byte_width) { if (i > 0) bytes_written += s->PutChar (' '); - bytes_written += s->Printf ("%2.2x", m_data.inst.bytes[i]); + bytes_written += s->Printf ("%2.2x", m_data.inst.bytes[i]); } } break; } - + // Add spaces to make sure bytes dispay comes out even in case opcodes // aren't all the same size if (bytes_written < min_byte_width) @@ -89,7 +89,7 @@ uint32_t Opcode::GetData (DataExtractor &data) const { uint32_t byte_size = GetByteSize (); - + DataBufferSP buffer_sp; if (byte_size > 0) { @@ -97,7 +97,7 @@ Opcode::GetData (DataExtractor &data) const { case Opcode::eTypeInvalid: break; - + case Opcode::eType8: buffer_sp.reset (new DataBufferHeap (&m_data.inst8, byte_size)); break; case Opcode::eType16: buffer_sp.reset (new DataBufferHeap (&m_data.inst16, byte_size)); break; case Opcode::eType16_2: @@ -119,7 +119,7 @@ Opcode::GetData (DataExtractor &data) const break; } } - + if (buffer_sp) { data.SetByteOrder(GetDataByteOrder()); diff --git a/source/Core/PluginManager.cpp b/source/Core/PluginManager.cpp index 7a2d3772e6c3..813cec227525 100644 --- a/source/Core/PluginManager.cpp +++ b/source/Core/PluginManager.cpp @@ -854,6 +854,111 @@ PluginManager::GetLanguageRuntimeCreateCallbackForPluginName (const ConstString return NULL; } +#pragma mark SystemRuntime + + +struct SystemRuntimeInstance +{ + SystemRuntimeInstance() : + name(), + description(), + create_callback(NULL) + { + } + + ConstString name; + std::string description; + SystemRuntimeCreateInstance create_callback; +}; + +typedef std::vector<SystemRuntimeInstance> SystemRuntimeInstances; + +static Mutex & +GetSystemRuntimeMutex () +{ + static Mutex g_instances_mutex (Mutex::eMutexTypeRecursive); + return g_instances_mutex; +} + +static SystemRuntimeInstances & +GetSystemRuntimeInstances () +{ + static SystemRuntimeInstances g_instances; + return g_instances; +} + +bool +PluginManager::RegisterPlugin +( + const ConstString &name, + const char *description, + SystemRuntimeCreateInstance create_callback +) +{ + if (create_callback) + { + SystemRuntimeInstance instance; + assert ((bool)name); + instance.name = name; + if (description && description[0]) + instance.description = description; + instance.create_callback = create_callback; + Mutex::Locker locker (GetSystemRuntimeMutex ()); + GetSystemRuntimeInstances ().push_back (instance); + } + return false; +} + +bool +PluginManager::UnregisterPlugin (SystemRuntimeCreateInstance create_callback) +{ + if (create_callback) + { + Mutex::Locker locker (GetSystemRuntimeMutex ()); + SystemRuntimeInstances &instances = GetSystemRuntimeInstances (); + + SystemRuntimeInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (pos->create_callback == create_callback) + { + instances.erase(pos); + return true; + } + } + } + return false; +} + +SystemRuntimeCreateInstance +PluginManager::GetSystemRuntimeCreateCallbackAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetSystemRuntimeMutex ()); + SystemRuntimeInstances &instances = GetSystemRuntimeInstances (); + if (idx < instances.size()) + return instances[idx].create_callback; + return NULL; +} + +SystemRuntimeCreateInstance +PluginManager::GetSystemRuntimeCreateCallbackForPluginName (const ConstString &name) +{ + if (name) + { + Mutex::Locker locker (GetSystemRuntimeMutex ()); + SystemRuntimeInstances &instances = GetSystemRuntimeInstances (); + + SystemRuntimeInstances::iterator pos, end = instances.end(); + for (pos = instances.begin(); pos != end; ++ pos) + { + if (name == pos->name) + return pos->create_callback; + } + } + return NULL; +} + + #pragma mark ObjectFile struct ObjectFileInstance diff --git a/source/Core/SearchFilter.cpp b/source/Core/SearchFilter.cpp index 54937c0afeca..64b5a838d3de 100644 --- a/source/Core/SearchFilter.cpp +++ b/source/Core/SearchFilter.cpp @@ -361,7 +361,7 @@ bool SearchFilterByModule::ModulePasses (const FileSpec &spec) { // Do a full match only if "spec" has a directory - const bool full_match = spec.GetDirectory(); + const bool full_match = (bool)spec.GetDirectory(); return FileSpec::Equal(spec, m_module_spec, full_match); } @@ -409,7 +409,7 @@ SearchFilterByModule::Search (Searcher &searcher) for (size_t i = 0; i < num_modules; i++) { Module* module = target_modules.GetModulePointerAtIndexUnlocked(i); - const bool full_match = m_module_spec.GetDirectory(); + const bool full_match = (bool)m_module_spec.GetDirectory(); if (FileSpec::Equal (m_module_spec, module->GetFileSpec(), full_match)) { SymbolContext matchingContext(m_target_sp, module->shared_from_this()); diff --git a/source/Core/SourceManager.cpp b/source/Core/SourceManager.cpp index 9f289348fd91..940034625c0a 100644 --- a/source/Core/SourceManager.cpp +++ b/source/Core/SourceManager.cpp @@ -567,15 +567,15 @@ SourceManager::File::CalculateLineOffsets (uint32_t line) // Push a 1 at index zero to indicate the file has been completely indexed. m_offsets.push_back(UINT32_MAX); - register const char *s; + const char *s; for (s = start; s < end; ++s) { - register char curr_ch = *s; + char curr_ch = *s; if (is_newline_char (curr_ch)) { if (s + 1 < end) { - register char next_ch = s[1]; + char next_ch = s[1]; if (is_newline_char (next_ch)) { if (curr_ch != next_ch) diff --git a/source/Core/StreamGDBRemote.cpp b/source/Core/StreamGDBRemote.cpp new file mode 100644 index 000000000000..46cb99ce98a5 --- /dev/null +++ b/source/Core/StreamGDBRemote.cpp @@ -0,0 +1,54 @@ +//===-- StreamGDBRemote.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/StreamGDBRemote.h" +#include <stdio.h> + +using namespace lldb; +using namespace lldb_private; + +StreamGDBRemote::StreamGDBRemote () : +StreamString () +{ +} + +StreamGDBRemote::StreamGDBRemote(uint32_t flags, uint32_t addr_size, ByteOrder byte_order) : +StreamString (flags, addr_size, byte_order) +{ +} + +StreamGDBRemote::~StreamGDBRemote() +{ +} + + +int +StreamGDBRemote::PutEscapedBytes (const void* s, + size_t src_len) +{ + int bytes_written = 0; + const uint8_t *src = (const uint8_t *)s; + bool binary_is_set = m_flags.Test(eBinary); + m_flags.Clear(eBinary); + while (src_len) + { + uint8_t byte = *src; + src++; src_len--; + if (byte == 0x23 || byte == 0x24 || byte == 0x7d || byte == 0x2a) + { + bytes_written += PutChar(0x7d); + byte ^= 0x20; + } + bytes_written += PutChar(byte); + }; + if (binary_is_set) + m_flags.Set(eBinary); + return bytes_written; +} + diff --git a/source/Core/Timer.cpp b/source/Core/Timer.cpp index b1416bdaf62e..bbd990056ba0 100644 --- a/source/Core/Timer.cpp +++ b/source/Core/Timer.cpp @@ -14,6 +14,7 @@ #include "lldb/Core/Stream.h" #include "lldb/Host/Mutex.h" +#include "lldb/Host/Host.h" #include <stdio.h> @@ -26,7 +27,7 @@ uint32_t Timer::g_display_depth = 0; FILE * Timer::g_file = NULL; typedef std::vector<Timer *> TimerStack; typedef std::map<const char *, uint64_t> TimerCategoryMap; -static pthread_key_t g_key; +static lldb::thread_key_t g_key; static Mutex & GetCategoryMutex() @@ -46,11 +47,11 @@ GetCategoryMap() static TimerStack * GetTimerStackForCurrentThread () { - void *timer_stack = ::pthread_getspecific (g_key); + void *timer_stack = Host::ThreadLocalStorageGet(g_key); if (timer_stack == NULL) { - ::pthread_setspecific (g_key, new TimerStack); - timer_stack = ::pthread_getspecific (g_key); + Host::ThreadLocalStorageSet(g_key, new TimerStack); + timer_stack = Host::ThreadLocalStorageGet(g_key); } return (TimerStack *)timer_stack; } @@ -71,8 +72,7 @@ void Timer::Initialize () { Timer::g_file = stdout; - ::pthread_key_create (&g_key, ThreadSpecificCleanup); - + g_key = Host::ThreadLocalStorageCreate(ThreadSpecificCleanup); } Timer::Timer (const char *category, const char *format, ...) : diff --git a/source/Core/Value.cpp b/source/Core/Value.cpp index 3fe75d3d4ce5..9dd72c7546bf 100644 --- a/source/Core/Value.cpp +++ b/source/Core/Value.cpp @@ -339,16 +339,30 @@ Value::GetValueAsData (ExecutionContext *exe_ctx, break; case eValueTypeScalar: - data.SetByteOrder (lldb::endian::InlHostByteOrder()); - if (ast_type.IsValid()) - data.SetAddressByteSize (ast_type.GetPointerByteSize()); - else - data.SetAddressByteSize(sizeof(void *)); - if (m_value.GetData (data)) - return error; // Success; - error.SetErrorStringWithFormat("extracting data from value failed"); - break; + { + data.SetByteOrder (lldb::endian::InlHostByteOrder()); + if (ast_type.IsValid()) + data.SetAddressByteSize (ast_type.GetPointerByteSize()); + else + data.SetAddressByteSize(sizeof(void *)); + uint32_t limit_byte_size = UINT32_MAX; + + if (ast_type.IsValid() && ast_type.IsScalarType()) + { + uint64_t type_encoding_count = 0; + lldb::Encoding type_encoding = ast_type.GetEncoding(type_encoding_count); + + if (type_encoding == eEncodingUint || type_encoding == eEncodingSint) + limit_byte_size = ast_type.GetByteSize(); + } + + if (m_value.GetData (data, limit_byte_size)) + return error; // Success; + + error.SetErrorStringWithFormat("extracting data from value failed"); + break; + } case eValueTypeLoadAddress: if (exe_ctx == NULL) { diff --git a/source/Core/ValueObject.cpp b/source/Core/ValueObject.cpp index a30cc1306c6b..d39d21a2a0bf 100644 --- a/source/Core/ValueObject.cpp +++ b/source/Core/ValueObject.cpp @@ -34,6 +34,7 @@ #include "lldb/Core/ValueObjectSyntheticFilter.h" #include "lldb/DataFormatters/DataVisualization.h" +#include "lldb/DataFormatters/ValueObjectPrinter.h" #include "lldb/Host/Endian.h" @@ -170,13 +171,12 @@ ValueObject::UpdateValueIfNeeded (bool update_format) // we have an error or not if (GetIsConstant()) { - // if you were asked to update your formatters, but did not get a chance to do it - // clear your own values (this serves the purpose of faking a stop-id for frozen - // objects (which are regarded as constant, but could have changes behind their backs - // because of the frozen-pointer depth limit) - // TODO: decouple summary from value and then remove this code and only force-clear the summary + // if you are constant, things might still have changed behind your back + // (e.g. you are a frozen object and things have changed deeper than you cared to freeze-dry yourself) + // in this case, your value has not changed, but "computed" entries might have, so you might now have + // a different summary, or a different object description. clear these so we will recompute them if (update_format && !did_change_formats) - ClearUserVisibleData(eClearUserVisibleDataItemsSummary); + ClearUserVisibleData(eClearUserVisibleDataItemsSummary | eClearUserVisibleDataItemsDescription); return m_error.Success(); } @@ -245,7 +245,7 @@ ValueObject::UpdateFormatsIfNeeded() if ( (m_last_format_mgr_revision != DataVisualization::GetCurrentRevision())) { - SetValueFormat(DataVisualization::ValueFormats::GetFormat (*this, eNoDynamicValues)); + SetValueFormat(DataVisualization::GetFormat (*this, eNoDynamicValues)); SetSummaryFormat(DataVisualization::GetSummaryFormat (*this, GetDynamicValueType())); #ifndef LLDB_DISABLE_PYTHON SetSyntheticChildren(DataVisualization::GetSyntheticChildren (*this, GetDynamicValueType())); @@ -272,6 +272,7 @@ ValueObject::SetNeedsUpdate () void ValueObject::ClearDynamicTypeInformation () { + m_children_count_valid = false; m_did_calculate_complete_objc_class_type = false; m_last_format_mgr_revision = 0; m_override_type = ClangASTType(); @@ -359,6 +360,12 @@ ValueObject::GetClangType () return MaybeCalculateCompleteType(); } +TypeImpl +ValueObject::GetTypeImpl () +{ + return TypeImpl(GetClangType()); +} + DataExtractor & ValueObject::GetDataExtractor () { @@ -595,6 +602,86 @@ ValueObject::GetChildAtIndexPath (const std::vector< std::pair<size_t, bool> > & return root; } +lldb::ValueObjectSP +ValueObject::GetChildAtNamePath (const std::initializer_list<ConstString> &names, + ConstString* name_of_error) +{ + if (names.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (ConstString name : names) + { + root = root->GetChildMemberWithName(name, true); + if (!root) + { + if (name_of_error) + *name_of_error = name; + return root; + } + } + return root; +} + +lldb::ValueObjectSP +ValueObject::GetChildAtNamePath (const std::vector<ConstString> &names, + ConstString* name_of_error) +{ + if (names.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (ConstString name : names) + { + root = root->GetChildMemberWithName(name, true); + if (!root) + { + if (name_of_error) + *name_of_error = name; + return root; + } + } + return root; +} + +lldb::ValueObjectSP +ValueObject::GetChildAtNamePath (const std::initializer_list< std::pair<ConstString, bool> > &names, + ConstString* name_of_error) +{ + if (names.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (std::pair<ConstString, bool> name : names) + { + root = root->GetChildMemberWithName(name.first, name.second); + if (!root) + { + if (name_of_error) + *name_of_error = name.first; + return root; + } + } + return root; +} + +lldb::ValueObjectSP +ValueObject::GetChildAtNamePath (const std::vector< std::pair<ConstString, bool> > &names, + ConstString* name_of_error) +{ + if (names.size() == 0) + return GetSP(); + ValueObjectSP root(GetSP()); + for (std::pair<ConstString, bool> name : names) + { + root = root->GetChildMemberWithName(name.first, name.second); + if (!root) + { + if (name_of_error) + *name_of_error = name.first; + return root; + } + } + return root; +} + size_t ValueObject::GetIndexOfChildWithName (const ConstString &name) { @@ -943,7 +1030,7 @@ ValueObject::GetPointeeData (DataExtractor& data, { heap_buf_ptr->SetByteSize(bytes); size_t bytes_read = process->ReadMemory(addr + offset, heap_buf_ptr->GetBytes(), bytes, error); - if (error.Success()) + if (error.Success() || bytes_read > 0) { data.SetData(data_sp); return bytes_read; @@ -1455,6 +1542,27 @@ ValueObject::GetValueAsUnsigned (uint64_t fail_value, bool *success) return fail_value; } +int64_t +ValueObject::GetValueAsSigned (int64_t fail_value, bool *success) +{ + // If our byte size is zero this is an aggregate type that has children + if (!GetClangType().IsAggregateType()) + { + Scalar scalar; + if (ResolveValue (scalar)) + { + if (success) + *success = true; + return scalar.SLongLong(fail_value); + } + // fallthrough, otherwise... + } + + if (success) + *success = false; + return fail_value; +} + // if any more "special cases" are added to ValueObject::DumpPrintableRepresentation() please keep // this call up to date by returning true for your new special cases. We will eventually move // to checking this call result before trying to display special cases @@ -2211,7 +2319,7 @@ ValueObject::CalculateSyntheticValue (bool use_synthetic) return; TargetSP target_sp(GetTargetSP()); - if (target_sp && (target_sp->GetEnableSyntheticValue() == false || target_sp->GetSuppressSyntheticValue() == true)) + if (target_sp && target_sp->GetEnableSyntheticValue() == false) { m_synthetic_value = NULL; return; @@ -3342,329 +3450,39 @@ ValueObject::ExpandArraySliceExpression(const char* expression_cstr, } } -static void -DumpValueObject_Impl (Stream &s, - ValueObject *valobj, - const ValueObject::DumpValueObjectOptions& options, - uint32_t ptr_depth, - uint32_t curr_depth) -{ - if (valobj) - { - bool update_success = valobj->UpdateValueIfNeeded (true); - - const char *root_valobj_name = - options.m_root_valobj_name.empty() ? - valobj->GetName().AsCString() : - options.m_root_valobj_name.c_str(); - - if (update_success && options.m_use_dynamic != eNoDynamicValues) - { - ValueObject *dynamic_value = valobj->GetDynamicValue(options.m_use_dynamic).get(); - if (dynamic_value) - valobj = dynamic_value; - } - - ClangASTType clang_type = valobj->GetClangType(); - const Flags type_flags (clang_type.GetTypeInfo ()); - const char *err_cstr = NULL; - const bool has_children = type_flags.Test (ClangASTType::eTypeHasChildren); - const bool has_value = type_flags.Test (ClangASTType::eTypeHasValue); - - const bool print_valobj = options.m_flat_output == false || has_value; - - if (print_valobj) - { - if (options.m_show_location) - { - s.Printf("%s: ", valobj->GetLocationAsCString()); - } - - s.Indent(); - - bool show_type = true; - // if we are at the root-level and been asked to hide the root's type, then hide it - if (curr_depth == 0 && options.m_hide_root_type) - show_type = false; - else - // otherwise decide according to the usual rules (asked to show types - always at the root level) - show_type = options.m_show_types || (curr_depth == 0 && !options.m_flat_output); - - if (show_type) - { - // Some ValueObjects don't have types (like registers sets). Only print - // the type if there is one to print - ConstString qualified_type_name(valobj->GetQualifiedTypeName()); - if (qualified_type_name) - s.Printf("(%s) ", qualified_type_name.GetCString()); - } - - if (options.m_flat_output) - { - // If we are showing types, also qualify the C++ base classes - const bool qualify_cxx_base_classes = options.m_show_types; - if (!options.m_hide_name) - { - valobj->GetExpressionPath(s, qualify_cxx_base_classes); - s.PutCString(" ="); - } - } - else if (!options.m_hide_name) - { - const char *name_cstr = root_valobj_name ? root_valobj_name : valobj->GetName().AsCString(""); - s.Printf ("%s =", name_cstr); - } - - if (!options.m_scope_already_checked && !valobj->IsInScope()) - { - err_cstr = "out of scope"; - } - } - - std::string summary_str; - std::string value_str; - const char *val_cstr = NULL; - const char *sum_cstr = NULL; - TypeSummaryImpl* entry = options.m_summary_sp ? options.m_summary_sp.get() : valobj->GetSummaryFormat().get(); - - if (options.m_omit_summary_depth > 0) - entry = NULL; - - bool is_nil = valobj->IsObjCNil(); - - if (err_cstr == NULL) - { - if (options.m_format != eFormatDefault && options.m_format != valobj->GetFormat()) - { - valobj->GetValueAsCString(options.m_format, - value_str); - } - else - { - val_cstr = valobj->GetValueAsCString(); - if (val_cstr) - value_str = val_cstr; - } - err_cstr = valobj->GetError().AsCString(); - } - - if (err_cstr) - { - s.Printf (" <%s>\n", err_cstr); - } - else - { - const bool is_ref = type_flags.Test (ClangASTType::eTypeIsReference); - if (print_valobj) - { - if (is_nil) - sum_cstr = "nil"; - else if (options.m_omit_summary_depth == 0) - { - if (options.m_summary_sp) - { - valobj->GetSummaryAsCString(entry, summary_str); - sum_cstr = summary_str.c_str(); - } - else - sum_cstr = valobj->GetSummaryAsCString(); - } - - // Make sure we have a value and make sure the summary didn't - // specify that the value should not be printed - and do not print - // the value if this thing is nil - // (but show the value if the user passes a format explicitly) - if (!is_nil && !value_str.empty() && (entry == NULL || (entry->DoesPrintValue() || options.m_format != eFormatDefault) || sum_cstr == NULL) && !options.m_hide_value) - s.Printf(" %s", value_str.c_str()); - - if (sum_cstr) - s.Printf(" %s", sum_cstr); - - // let's avoid the overly verbose no description error for a nil thing - if (options.m_use_objc && !is_nil) - { - if (!options.m_hide_value || !options.m_hide_name) - s.Printf(" "); - const char *object_desc = valobj->GetObjectDescription(); - if (object_desc) - s.Printf("%s\n", object_desc); - else - s.Printf ("[no Objective-C description available]\n"); - return; - } - } - - if (curr_depth < options.m_max_depth) - { - // We will show children for all concrete types. We won't show - // pointer contents unless a pointer depth has been specified. - // We won't reference contents unless the reference is the - // root object (depth of zero). - bool print_children = true; - - // Use a new temporary pointer depth in case we override the - // current pointer depth below... - uint32_t curr_ptr_depth = ptr_depth; - - const bool is_ptr = type_flags.Test (ClangASTType::eTypeIsPointer); - if (is_ptr || is_ref) - { - // We have a pointer or reference whose value is an address. - // Make sure that address is not NULL - AddressType ptr_address_type; - if (valobj->GetPointerValue (&ptr_address_type) == 0) - print_children = false; - - else if (is_ref && curr_depth == 0) - { - // If this is the root object (depth is zero) that we are showing - // and it is a reference, and no pointer depth has been supplied - // print out what it references. Don't do this at deeper depths - // otherwise we can end up with infinite recursion... - curr_ptr_depth = 1; - } - - if (curr_ptr_depth == 0) - print_children = false; - } - - if (print_children && (!entry || entry->DoesPrintChildren() || !sum_cstr)) - { - ValueObjectSP synth_valobj_sp = valobj->GetSyntheticValue (options.m_use_synthetic); - ValueObject* synth_valobj = (synth_valobj_sp ? synth_valobj_sp.get() : valobj); - - size_t num_children = synth_valobj->GetNumChildren(); - bool print_dotdotdot = false; - if (num_children) - { - if (options.m_flat_output) - { - if (print_valobj) - s.EOL(); - } - else - { - if (print_valobj) - s.PutCString(is_ref ? ": {\n" : " {\n"); - s.IndentMore(); - } - - const size_t max_num_children = valobj->GetTargetSP()->GetMaximumNumberOfChildrenToDisplay(); - - if (num_children > max_num_children && !options.m_ignore_cap) - { - num_children = max_num_children; - print_dotdotdot = true; - } - - ValueObject::DumpValueObjectOptions child_options(options); - child_options.SetFormat(options.m_format).SetSummary().SetRootValueObjectName(); - child_options.SetScopeChecked(true).SetHideName(options.m_hide_name).SetHideValue(options.m_hide_value) - .SetOmitSummaryDepth(child_options.m_omit_summary_depth > 1 ? child_options.m_omit_summary_depth - 1 : 0); - for (size_t idx=0; idx<num_children; ++idx) - { - ValueObjectSP child_sp(synth_valobj->GetChildAtIndex(idx, true)); - if (child_sp.get()) - { - DumpValueObject_Impl (s, - child_sp.get(), - child_options, - (is_ptr || is_ref) ? curr_ptr_depth - 1 : curr_ptr_depth, - curr_depth + 1); - } - } - - if (!options.m_flat_output) - { - if (print_dotdotdot) - { - ExecutionContext exe_ctx (valobj->GetExecutionContextRef()); - Target *target = exe_ctx.GetTargetPtr(); - if (target) - target->GetDebugger().GetCommandInterpreter().ChildrenTruncated(); - s.Indent("...\n"); - } - s.IndentLess(); - s.Indent("}\n"); - } - } - else if (has_children) - { - // Aggregate, no children... - if (print_valobj) - s.PutCString(" {}\n"); - } - else - { - if (print_valobj) - s.EOL(); - } - - } - else - { - s.EOL(); - } - } - else - { - if (has_children && print_valobj) - { - s.PutCString("{...}\n"); - } - } - } - } -} - void -ValueObject::LogValueObject (Log *log, - ValueObject *valobj) +ValueObject::LogValueObject (Log *log) { - if (log && valobj) - return LogValueObject (log, valobj, DumpValueObjectOptions::DefaultOptions()); + if (log) + return LogValueObject (log, DumpValueObjectOptions::DefaultOptions()); } void -ValueObject::LogValueObject (Log *log, - ValueObject *valobj, - const DumpValueObjectOptions& options) +ValueObject::LogValueObject (Log *log, const DumpValueObjectOptions& options) { - if (log && valobj) + if (log) { StreamString s; - ValueObject::DumpValueObject (s, valobj, options); + Dump (s, options); if (s.GetSize()) log->PutCString(s.GetData()); } } void -ValueObject::DumpValueObject (Stream &s, - ValueObject *valobj) +ValueObject::Dump (Stream &s) { - if (!valobj) - return; - - DumpValueObject_Impl(s, - valobj, - DumpValueObjectOptions::DefaultOptions(), - 0, - 0); + ValueObjectPrinter printer(this,&s,DumpValueObjectOptions::DefaultOptions()); + printer.PrintValueObject(); } void -ValueObject::DumpValueObject (Stream &s, - ValueObject *valobj, - const DumpValueObjectOptions& options) +ValueObject::Dump (Stream &s, + const DumpValueObjectOptions& options) { - DumpValueObject_Impl(s, - valobj, - options, - options.m_max_ptr_depth, // max pointer depth allowed, we will go down from here - 0 // current object depth is 0 since we are just starting - ); + ValueObjectPrinter printer(this,&s,options); + printer.PrintValueObject(); } ValueObjectSP diff --git a/source/Core/ValueObjectDynamicValue.cpp b/source/Core/ValueObjectDynamicValue.cpp index 977cc4cd3132..47e781e71156 100644 --- a/source/Core/ValueObjectDynamicValue.cpp +++ b/source/Core/ValueObjectDynamicValue.cpp @@ -52,10 +52,15 @@ ValueObjectDynamicValue::~ValueObjectDynamicValue() ClangASTType ValueObjectDynamicValue::GetClangTypeImpl () { - if (m_dynamic_type_info.HasTypeSP()) - return m_value.GetClangType(); - else - return m_parent->GetClangType(); + const bool success = UpdateValueIfNeeded(false); + if (success) + { + if (m_dynamic_type_info.HasType()) + return m_value.GetClangType(); + else + return m_parent->GetClangType(); + } + return m_parent->GetClangType(); } ConstString @@ -64,24 +69,35 @@ ValueObjectDynamicValue::GetTypeName() const bool success = UpdateValueIfNeeded(false); if (success) { - if (m_dynamic_type_info.HasTypeSP()) - return GetClangType().GetConstTypeName(); if (m_dynamic_type_info.HasName()) return m_dynamic_type_info.GetName(); + if (m_dynamic_type_info.HasType()) + return GetClangType().GetConstTypeName(); } return m_parent->GetTypeName(); } +TypeImpl +ValueObjectDynamicValue::GetTypeImpl () +{ + const bool success = UpdateValueIfNeeded(false); + if (success && m_type_impl.IsValid()) + { + return m_type_impl; + } + return m_parent->GetTypeImpl(); +} + ConstString ValueObjectDynamicValue::GetQualifiedTypeName() { const bool success = UpdateValueIfNeeded(false); if (success) { - if (m_dynamic_type_info.HasTypeSP()) - return GetClangType().GetConstQualifiedTypeName (); if (m_dynamic_type_info.HasName()) return m_dynamic_type_info.GetName(); + if (m_dynamic_type_info.HasType()) + return GetClangType().GetConstQualifiedTypeName (); } return m_parent->GetTypeName(); } @@ -90,7 +106,7 @@ size_t ValueObjectDynamicValue::CalculateNumChildren() { const bool success = UpdateValueIfNeeded(false); - if (success && m_dynamic_type_info.HasTypeSP()) + if (success && m_dynamic_type_info.HasType()) return GetClangType().GetNumChildren (true); else return m_parent->GetNumChildren(); @@ -100,7 +116,7 @@ uint64_t ValueObjectDynamicValue::GetByteSize() { const bool success = UpdateValueIfNeeded(false); - if (success && m_dynamic_type_info.HasTypeSP()) + if (success && m_dynamic_type_info.HasType()) return m_value.GetValueByteSize(NULL); else return m_parent->GetByteSize(); @@ -112,6 +128,40 @@ ValueObjectDynamicValue::GetValueType() const return m_parent->GetValueType(); } + +static TypeAndOrName +FixupTypeAndOrName (const TypeAndOrName& type_andor_name, + ValueObject& parent) +{ + TypeAndOrName ret(type_andor_name); + if (type_andor_name.HasType()) + { + // The type will always be the type of the dynamic object. If our parent's type was a pointer, + // then our type should be a pointer to the type of the dynamic object. If a reference, then the original type + // should be okay... + ClangASTType orig_type = type_andor_name.GetClangASTType(); + ClangASTType corrected_type = orig_type; + if (parent.IsPointerType()) + corrected_type = orig_type.GetPointerType (); + else if (parent.IsPointerOrReferenceType()) + corrected_type = orig_type.GetLValueReferenceType (); + ret.SetClangASTType(corrected_type); + } + else /*if (m_dynamic_type_info.HasName())*/ + { + // If we are here we need to adjust our dynamic type name to include the correct & or * symbol + std::string corrected_name (type_andor_name.GetName().GetCString()); + if (parent.IsPointerType()) + corrected_name.append(" *"); + else if (parent.IsPointerOrReferenceType()) + corrected_name.append(" &"); + // the parent type should be a correctly pointer'ed or referenc'ed type + ret.SetClangASTType(parent.GetClangType()); + ret.SetName(corrected_name.c_str()); + } + return ret; +} + bool ValueObjectDynamicValue::UpdateValue () { @@ -176,6 +226,31 @@ ValueObjectDynamicValue::UpdateValue () // don't... m_update_point.SetUpdated(); + + if (found_dynamic_type) + { + if (class_type_or_name.HasType()) + { + // TypeSP are always generated from debug info + if (!class_type_or_name.HasTypeSP() && class_type_or_name.GetClangASTType().IsRuntimeGeneratedType()) + { + m_type_impl = TypeImpl(m_parent->GetClangType(),FixupTypeAndOrName(class_type_or_name, *m_parent).GetClangASTType()); + class_type_or_name.SetClangASTType(ClangASTType()); + } + else + { + m_type_impl = TypeImpl(FixupTypeAndOrName(class_type_or_name, *m_parent).GetClangASTType()); + } + } + else + { + m_type_impl.Clear(); + } + } + else + { + m_type_impl.Clear(); + } // If we don't have a dynamic type, then make ourselves just a echo of our parent. // Or we could return false, and make ourselves an echo of our parent? @@ -224,33 +299,10 @@ ValueObjectDynamicValue::UpdateValue () m_value.GetScalar() = load_address; } - ClangASTType corrected_type; - if (m_dynamic_type_info.HasTypeSP()) - { - // The type will always be the type of the dynamic object. If our parent's type was a pointer, - // then our type should be a pointer to the type of the dynamic object. If a reference, then the original type - // should be okay... - ClangASTType orig_type = m_dynamic_type_info.GetTypeSP()->GetClangForwardType(); - corrected_type = orig_type; - if (m_parent->IsPointerType()) - corrected_type = orig_type.GetPointerType (); - else if (m_parent->IsPointerOrReferenceType()) - corrected_type = orig_type.GetLValueReferenceType (); - } - else /*if (m_dynamic_type_info.HasName())*/ - { - // If we are here we need to adjust our dynamic type name to include the correct & or * symbol - std::string type_name_buf (m_dynamic_type_info.GetName().GetCString()); - if (m_parent->IsPointerType()) - type_name_buf.append(" *"); - else if (m_parent->IsPointerOrReferenceType()) - type_name_buf.append(" &"); - corrected_type = m_parent->GetClangType(); - m_dynamic_type_info.SetName(type_name_buf.c_str()); - } + m_dynamic_type_info = FixupTypeAndOrName(m_dynamic_type_info, *m_parent); //m_value.SetContext (Value::eContextTypeClangType, corrected_type); - m_value.SetClangType (corrected_type); + m_value.SetClangType (m_dynamic_type_info.GetClangASTType()); // Our address is the location of the dynamic type stored in memory. It isn't a load address, // because we aren't pointing to the LOCATION that stores the pointer to us, we're pointing to us... diff --git a/source/Core/ValueObjectSyntheticFilter.cpp b/source/Core/ValueObjectSyntheticFilter.cpp index 522ca082a691..5767466f509d 100644 --- a/source/Core/ValueObjectSyntheticFilter.cpp +++ b/source/Core/ValueObjectSyntheticFilter.cpp @@ -30,19 +30,19 @@ public: size_t CalculateNumChildren() { - return 0; + return m_backend.GetNumChildren(); } lldb::ValueObjectSP GetChildAtIndex (size_t idx) { - return lldb::ValueObjectSP(); + return m_backend.GetChildAtIndex(idx, true); } size_t GetIndexOfChildWithName (const ConstString &name) { - return UINT32_MAX; + return m_backend.GetIndexOfChildWithName(name); } bool diff --git a/source/Core/ValueObjectVariable.cpp b/source/Core/ValueObjectVariable.cpp index 38c0d91324ad..3d8b07a36948 100644 --- a/source/Core/ValueObjectVariable.cpp +++ b/source/Core/ValueObjectVariable.cpp @@ -156,6 +156,10 @@ ValueObjectVariable::UpdateValue () { m_resolved_value = m_value; m_value.SetContext(Value::eContextTypeVariable, variable); + + ClangASTType clang_type = GetClangType(); + if (clang_type.IsValid()) + m_value.SetClangType(clang_type); Value::ValueType value_type = m_value.GetValueType(); |