diff options
Diffstat (limited to 'source/Core')
45 files changed, 6538 insertions, 5707 deletions
diff --git a/source/Core/Address.cpp b/source/Core/Address.cpp index d449d0b214382..1cd7a7447b7f0 100644 --- a/source/Core/Address.cpp +++ b/source/Core/Address.cpp @@ -468,6 +468,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum case DumpStyleResolvedDescription: case DumpStyleResolvedDescriptionNoModule: case DumpStyleResolvedDescriptionNoFunctionArguments: + case DumpStyleNoFunctionName: if (IsSectionOffset()) { uint32_t pointer_size = 4; @@ -500,7 +501,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum if (symbol_name) { s->PutCString(symbol_name); - addr_t delta = file_Addr - symbol->GetAddress().GetFileAddress(); + addr_t delta = file_Addr - symbol->GetAddressRef().GetFileAddress(); if (delta) s->Printf(" + %" PRIu64, delta); showed_info = true; @@ -553,7 +554,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum #endif Address cstr_addr(*this); cstr_addr.SetOffset(cstr_addr.GetOffset() + pointer_size); - func_sc.DumpStopContext(s, exe_scope, so_addr, true, true, false, true); + func_sc.DumpStopContext(s, exe_scope, so_addr, true, true, false, true, true); if (ReadAddress (exe_scope, cstr_addr, pointer_size, so_addr)) { #if VERBOSE_OUTPUT @@ -636,7 +637,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum if (pointer_sc.function || pointer_sc.symbol) { s->PutCString(": "); - pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false, false, true); + pointer_sc.DumpStopContext(s, exe_scope, so_addr, true, false, false, true, true); } } } @@ -662,12 +663,13 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum const bool show_fullpaths = false; const bool show_inlined_frames = true; const bool show_function_arguments = (style != DumpStyleResolvedDescriptionNoFunctionArguments); + const bool show_function_name = (style != DumpStyleNoFunctionName); if (sc.function == NULL && sc.symbol != NULL) { // If we have just a symbol make sure it is in the right section if (sc.symbol->ValueIsAddress()) { - if (sc.symbol->GetAddress().GetSection() != GetSection()) + if (sc.symbol->GetAddressRef().GetSection() != GetSection()) { // don't show the module if the symbol is a trampoline symbol show_stop_context = false; @@ -684,7 +686,8 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum show_fullpaths, show_module, show_inlined_frames, - show_function_arguments); + show_function_arguments, + show_function_name); } else { @@ -719,7 +722,7 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum // as our address. If it isn't, then we might have just found // the last symbol that came before the address that we are // looking up that has nothing to do with our address lookup. - if (sc.symbol->ValueIsAddress() && sc.symbol->GetAddress().GetSection() != GetSection()) + if (sc.symbol->ValueIsAddress() && sc.symbol->GetAddressRef().GetSection() != GetSection()) sc.symbol = NULL; } sc.GetDescription(s, eDescriptionLevelBrief, target); @@ -742,10 +745,15 @@ Address::Dump (Stream *s, ExecutionContextScope *exe_scope, DumpStyle style, Dum if (var && var->LocationIsValidForAddress (*this)) { s->Indent(); - s->Printf (" Variable: id = {0x%8.8" PRIx64 "}, name = \"%s\", type= \"%s\", location =", + s->Printf (" Variable: id = {0x%8.8" PRIx64 "}, name = \"%s\"", var->GetID(), - var->GetName().GetCString(), - var->GetType()->GetName().GetCString()); + var->GetName().GetCString()); + Type *type = var->GetType(); + if (type) + s->Printf(", type = \"%s\"", type->GetName().GetCString()); + else + s->PutCString(", type = <unknown>"); + s->PutCString(", location = "); var->DumpLocationForAddress(s, *this); s->PutCString(", decl = "); var->GetDeclaration().DumpStopContext(s, false); diff --git a/source/Core/AddressResolver.cpp b/source/Core/AddressResolver.cpp index 5369d960f2513..aa457015b4fd5 100644 --- a/source/Core/AddressResolver.cpp +++ b/source/Core/AddressResolver.cpp @@ -20,7 +20,6 @@ #include "lldb/Core/StreamString.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Target/Target.h" -#include "lldb/lldb-private-log.h" using namespace lldb_private; diff --git a/source/Core/AddressResolverFileLine.cpp b/source/Core/AddressResolverFileLine.cpp index 6089abd76cbcc..e45076e9bfc6b 100644 --- a/source/Core/AddressResolverFileLine.cpp +++ b/source/Core/AddressResolverFileLine.cpp @@ -14,7 +14,6 @@ #include "lldb/Core/StreamString.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/SymbolContext.h" -#include "lldb/lldb-private-log.h" using namespace lldb; using namespace lldb_private; diff --git a/source/Core/AddressResolverName.cpp b/source/Core/AddressResolverName.cpp index 9f3b3f506fe00..1c6205fa6bdfd 100644 --- a/source/Core/AddressResolverName.cpp +++ b/source/Core/AddressResolverName.cpp @@ -17,7 +17,6 @@ #include "lldb/Symbol/Function.h" #include "lldb/Symbol/SymbolContext.h" #include "lldb/Symbol/Symbol.h" -#include "lldb/lldb-private-log.h" using namespace lldb; using namespace lldb_private; @@ -166,7 +165,7 @@ AddressResolverName::SearchCallback { if (symbol_sc.symbol && symbol_sc.symbol->ValueIsAddress()) { - if (sc.function->GetAddressRange().GetBaseAddress() == symbol_sc.symbol->GetAddress()) + if (sc.function->GetAddressRange().GetBaseAddress() == symbol_sc.symbol->GetAddressRef()) { sym_list.RemoveContextAtIndex(j); continue; // Don't increment j @@ -212,7 +211,7 @@ AddressResolverName::SearchCallback { if (sc.symbol && sc.symbol->ValueIsAddress()) { - func_addr = sc.symbol->GetAddress(); + func_addr = sc.symbol->GetAddressRef(); addr_t byte_size = sc.symbol->GetByteSize(); if (skip_prologue) diff --git a/source/Core/ArchSpec.cpp b/source/Core/ArchSpec.cpp index 015f76bffbb53..33cbede9ce14a 100644 --- a/source/Core/ArchSpec.cpp +++ b/source/Core/ArchSpec.cpp @@ -18,7 +18,7 @@ #include "llvm/Support/COFF.h" #include "llvm/Support/ELF.h" #include "llvm/Support/Host.h" -#include "lldb/Utility/SafeMachO.h" + #include "lldb/Core/RegularExpression.h" #include "lldb/Core/StringList.h" #include "lldb/Host/Endian.h" @@ -27,6 +27,8 @@ #include "lldb/Target/Process.h" #include "lldb/Target/RegisterContext.h" #include "lldb/Target/Thread.h" +#include "lldb/Utility/NameMatches.h" +#include "lldb/Utility/SafeMachO.h" #include "Plugins/Process/Utility/ARMDefines.h" #include "Plugins/Process/Utility/InstructionUtils.h" @@ -87,7 +89,29 @@ static const CoreDefinition g_core_definitions[] = { eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, ArchSpec::eCore_arm_armv8 , "armv8" }, { eByteOrderLittle, 8, 4, 4, llvm::Triple::aarch64, ArchSpec::eCore_arm_aarch64 , "aarch64" }, - { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64 , "mips64" }, + // mips32, mips32r2, mips32r3, mips32r5, mips32r6 + { eByteOrderBig , 4, 4, 4, llvm::Triple::mips , ArchSpec::eCore_mips32 , "mips" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::mips , ArchSpec::eCore_mips32r2 , "mipsr2" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::mips , ArchSpec::eCore_mips32r3 , "mipsr3" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::mips , ArchSpec::eCore_mips32r5 , "mipsr5" }, + { eByteOrderBig , 4, 4, 4, llvm::Triple::mips , ArchSpec::eCore_mips32r6 , "mipsr6" }, + { eByteOrderLittle, 4, 4, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32el , "mipsel" }, + { eByteOrderLittle, 4, 4, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r2el , "mipsr2el" }, + { eByteOrderLittle, 4, 4, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r3el , "mipsr3el" }, + { eByteOrderLittle, 4, 4, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r5el , "mipsr5el" }, + { eByteOrderLittle, 4, 4, 4, llvm::Triple::mipsel, ArchSpec::eCore_mips32r6el , "mipsr6el" }, + + // mips64, mips64r2, mips64r3, mips64r5, mips64r6 + { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64 , "mips64" }, + { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64r2 , "mips64r2" }, + { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64r3 , "mips64r3" }, + { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64r5 , "mips64r5" }, + { eByteOrderBig , 8, 4, 4, llvm::Triple::mips64 , ArchSpec::eCore_mips64r6 , "mips64r6" }, + { eByteOrderLittle, 8, 4, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64el , "mips64el" }, + { eByteOrderLittle, 8, 4, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r2el , "mips64r2el" }, + { eByteOrderLittle, 8, 4, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r3el , "mips64r3el" }, + { eByteOrderLittle, 8, 4, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r5el , "mips64r5el" }, + { eByteOrderLittle, 8, 4, 4, llvm::Triple::mips64el, ArchSpec::eCore_mips64r6el , "mips64r6el" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_generic , "powerpc" }, { eByteOrderBig , 4, 4, 4, llvm::Triple::ppc , ArchSpec::eCore_ppc_ppc601 , "ppc601" }, @@ -259,14 +283,25 @@ static const ArchDefinitionEntry g_elf_arch_entries[] = { { ArchSpec::eCore_sparc_generic , llvm::ELF::EM_SPARC , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Sparc { ArchSpec::eCore_x86_32_i386 , llvm::ELF::EM_386 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Intel 80386 - { ArchSpec::eCore_x86_32_i486 , llvm::ELF::EM_486 , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Intel 486 (deprecated) + { ArchSpec::eCore_x86_32_i486 , llvm::ELF::EM_IAMCU , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // Intel MCU // FIXME: is this correct? { ArchSpec::eCore_ppc_generic , llvm::ELF::EM_PPC , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // PowerPC { 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_arm_aarch64 , llvm::ELF::EM_AARCH64, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // ARM64 { 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_mips64 , llvm::ELF::EM_MIPS , LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // MIPS + { ArchSpec::eCore_mips32 , llvm::ELF::EM_MIPS , ArchSpec::eMIPSSubType_mips32, 0xFFFFFFFFu, 0xFFFFFFFFu }, // mips32 + { ArchSpec::eCore_mips32r2 , llvm::ELF::EM_MIPS , ArchSpec::eMIPSSubType_mips32r2, 0xFFFFFFFFu, 0xFFFFFFFFu }, // mips32r2 + { ArchSpec::eCore_mips32r6 , llvm::ELF::EM_MIPS , ArchSpec::eMIPSSubType_mips32r6, 0xFFFFFFFFu, 0xFFFFFFFFu }, // mips32r6 + { ArchSpec::eCore_mips32el , llvm::ELF::EM_MIPS , ArchSpec::eMIPSSubType_mips32el, 0xFFFFFFFFu, 0xFFFFFFFFu }, // mips32el + { ArchSpec::eCore_mips32r2el , llvm::ELF::EM_MIPS , ArchSpec::eMIPSSubType_mips32r2el, 0xFFFFFFFFu, 0xFFFFFFFFu }, // mips32r2el + { ArchSpec::eCore_mips32r6el , llvm::ELF::EM_MIPS , ArchSpec::eMIPSSubType_mips32r6el, 0xFFFFFFFFu, 0xFFFFFFFFu }, // mips32r6el + { ArchSpec::eCore_mips64 , llvm::ELF::EM_MIPS , ArchSpec::eMIPSSubType_mips64, 0xFFFFFFFFu, 0xFFFFFFFFu }, // mips64 + { ArchSpec::eCore_mips64r2 , llvm::ELF::EM_MIPS , ArchSpec::eMIPSSubType_mips64r2, 0xFFFFFFFFu, 0xFFFFFFFFu }, // mips64r2 + { ArchSpec::eCore_mips64r6 , llvm::ELF::EM_MIPS , ArchSpec::eMIPSSubType_mips64r6, 0xFFFFFFFFu, 0xFFFFFFFFu }, // mips64r6 + { ArchSpec::eCore_mips64el , llvm::ELF::EM_MIPS , ArchSpec::eMIPSSubType_mips64el, 0xFFFFFFFFu, 0xFFFFFFFFu }, // mips64el + { ArchSpec::eCore_mips64r2el , llvm::ELF::EM_MIPS , ArchSpec::eMIPSSubType_mips64r2el, 0xFFFFFFFFu, 0xFFFFFFFFu }, // mips64r2el + { ArchSpec::eCore_mips64r6el , llvm::ELF::EM_MIPS , ArchSpec::eMIPSSubType_mips64r6el, 0xFFFFFFFFu, 0xFFFFFFFFu }, // mips64r6el { ArchSpec::eCore_hexagon_generic , llvm::ELF::EM_HEXAGON, LLDB_INVALID_CPUTYPE, 0xFFFFFFFFu, 0xFFFFFFFFu }, // HEXAGON { ArchSpec::eCore_kalimba3 , llvm::ELF::EM_CSR_KALIMBA, llvm::Triple::KalimbaSubArch_v3, 0xFFFFFFFFu, 0xFFFFFFFFu }, // KALIMBA { ArchSpec::eCore_kalimba4 , llvm::ELF::EM_CSR_KALIMBA, llvm::Triple::KalimbaSubArch_v4, 0xFFFFFFFFu, 0xFFFFFFFFu }, // KALIMBA @@ -573,6 +608,32 @@ ArchSpec::GetDefaultEndian () const return eByteOrderInvalid; } +bool +ArchSpec::CharIsSignedByDefault () const +{ + switch (m_triple.getArch()) { + default: + return true; + + case llvm::Triple::aarch64: + case llvm::Triple::aarch64_be: + case llvm::Triple::arm: + case llvm::Triple::armeb: + case llvm::Triple::thumb: + case llvm::Triple::thumbeb: + return m_triple.isOSDarwin() || m_triple.isOSWindows(); + + case llvm::Triple::ppc: + case llvm::Triple::ppc64: + return m_triple.isOSDarwin(); + + case llvm::Triple::ppc64le: + case llvm::Triple::systemz: + case llvm::Triple::xcore: + return false; + } +} + lldb::ByteOrder ArchSpec::GetByteOrder () const { @@ -779,7 +840,7 @@ ArchSpec::MergeFrom(const ArchSpec &other) } bool -ArchSpec::SetArchitecture (ArchitectureType arch_type, uint32_t cpu, uint32_t sub) +ArchSpec::SetArchitecture (ArchitectureType arch_type, uint32_t cpu, uint32_t sub, uint32_t os) { m_core = kCore_invalid; bool update_triple = true; @@ -800,7 +861,6 @@ ArchSpec::SetArchitecture (ArchitectureType arch_type, uint32_t cpu, uint32_t su if (arch_type == eArchTypeMachO) { m_triple.setVendor (llvm::Triple::Apple); - switch (core_def->machine) { case llvm::Triple::aarch64: @@ -825,10 +885,22 @@ ArchSpec::SetArchitecture (ArchitectureType arch_type, uint32_t cpu, uint32_t su break; } } - else + else if (arch_type == eArchTypeELF) { + llvm::Triple::OSType ostype; + switch (os) + { + case llvm::ELF::ELFOSABI_AIX: ostype = llvm::Triple::OSType::AIX; break; + case llvm::ELF::ELFOSABI_FREEBSD: ostype = llvm::Triple::OSType::FreeBSD; break; + case llvm::ELF::ELFOSABI_GNU: ostype = llvm::Triple::OSType::Linux; break; + case llvm::ELF::ELFOSABI_NETBSD: ostype = llvm::Triple::OSType::NetBSD; break; + case llvm::ELF::ELFOSABI_OPENBSD: ostype = llvm::Triple::OSType::OpenBSD; break; + case llvm::ELF::ELFOSABI_SOLARIS: ostype = llvm::Triple::OSType::Solaris; break; + default: + ostype = llvm::Triple::OSType::UnknownOS; + } + m_triple.setOS (ostype); m_triple.setVendor (llvm::Triple::UnknownVendor); - m_triple.setOS (llvm::Triple::UnknownOS); } // Fall back onto setting the machine type if the arch by name failed... if (m_triple.getArch () == llvm::Triple::UnknownArch) @@ -1019,6 +1091,8 @@ cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_in try_inverse = false; if (core2 == ArchSpec::eCore_arm_armv7) return true; + if (core2 == ArchSpec::eCore_arm_armv6m) + return true; } break; @@ -1027,8 +1101,36 @@ cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_in return true; break; - case ArchSpec::eCore_arm_armv7m: case ArchSpec::eCore_arm_armv7em: + if (!enforce_exact_match) + { + if (core2 == ArchSpec::eCore_arm_generic) + return true; + if (core2 == ArchSpec::eCore_arm_armv7m) + return true; + if (core2 == ArchSpec::eCore_arm_armv6m) + return true; + if (core2 == ArchSpec::eCore_arm_armv7) + return true; + try_inverse = true; + } + break; + + case ArchSpec::eCore_arm_armv7m: + if (!enforce_exact_match) + { + if (core2 == ArchSpec::eCore_arm_generic) + return true; + if (core2 == ArchSpec::eCore_arm_armv6m) + return true; + if (core2 == ArchSpec::eCore_arm_armv7) + return true; + if (core2 == ArchSpec::eCore_arm_armv7em) + return true; + try_inverse = true; + } + break; + case ArchSpec::eCore_arm_armv7f: case ArchSpec::eCore_arm_armv7k: case ArchSpec::eCore_arm_armv7s: @@ -1084,6 +1186,36 @@ cores_match (const ArchSpec::Core core1, const ArchSpec::Core core2, bool try_in } break; + case ArchSpec::eCore_mips64: + case ArchSpec::eCore_mips64r2: + case ArchSpec::eCore_mips64r3: + case ArchSpec::eCore_mips64r5: + case ArchSpec::eCore_mips64r6: + if (!enforce_exact_match) + { + if (core2 >= ArchSpec::kCore_mips32_first && core2 <= (core1 - 10)) + return true; + if (core2 >= ArchSpec::kCore_mips64_first && core2 <= (core1 - 1)) + return true; + try_inverse = false; + } + break; + + case ArchSpec::eCore_mips64el: + case ArchSpec::eCore_mips64r2el: + case ArchSpec::eCore_mips64r3el: + case ArchSpec::eCore_mips64r5el: + case ArchSpec::eCore_mips64r6el: + if (!enforce_exact_match) + { + if (core2 >= ArchSpec::kCore_mips32el_first && core2 <= (core1 - 10)) + return true; + if (core2 >= ArchSpec::kCore_mips64el_first && core2 <= (core1 - 1)) + return true; + try_inverse = false; + } + break; + default: break; } diff --git a/source/Core/Broadcaster.cpp b/source/Core/Broadcaster.cpp index 1cbbde20e7f6a..351487401f6b3 100644 --- a/source/Core/Broadcaster.cpp +++ b/source/Core/Broadcaster.cpp @@ -16,7 +16,6 @@ #include "lldb/Core/Log.h" #include "lldb/Core/Event.h" #include "lldb/Core/StreamString.h" -#include "lldb/lldb-private-log.h" using namespace lldb; using namespace lldb_private; diff --git a/source/Core/Communication.cpp b/source/Core/Communication.cpp index ea84843fe0b30..ae579d1b00ae5 100644 --- a/source/Core/Communication.cpp +++ b/source/Core/Communication.cpp @@ -11,7 +11,6 @@ // C++ Includes // Other libraries and framework includes // Project includes -#include "lldb/lldb-private-log.h" #include "lldb/Core/Communication.h" #include "lldb/Core/Connection.h" #include "lldb/Core/Log.h" @@ -39,9 +38,11 @@ Communication::Communication(const char *name) : Broadcaster (NULL, name), m_connection_sp (), m_read_thread_enabled (false), + m_read_thread_did_exit (false), m_bytes(), m_bytes_mutex (Mutex::eMutexTypeRecursive), m_write_mutex (Mutex::eMutexTypeNormal), + m_synchronize_mutex (Mutex::eMutexTypeNormal), m_callback (NULL), m_callback_baton (NULL), m_close_on_eof (true) @@ -56,6 +57,7 @@ Communication::Communication(const char *name) : SetEventName (eBroadcastBitReadThreadDidExit, "read thread did exit"); SetEventName (eBroadcastBitReadThreadShouldExit, "read thread should exit"); SetEventName (eBroadcastBitPacketAvailable, "packet available"); + SetEventName (eBroadcastBitNoMorePendingInput, "no more pending input"); CheckInWithManager(); } @@ -182,7 +184,8 @@ Communication::Read (void *dst, size_t dst_len, uint32_t timeout_usec, Connectio if (event_type & eBroadcastBitReadThreadDidExit) { - Disconnect (NULL); + if (GetCloseOnEOF ()) + Disconnect (NULL); break; } } @@ -244,6 +247,7 @@ Communication::StartReadThread (Error *error_ptr) snprintf(thread_name, sizeof(thread_name), "<lldb.comm.%s>", m_broadcaster_name.AsCString()); m_read_thread_enabled = true; + m_read_thread_did_exit = false; m_read_thread = ThreadLauncher::LaunchThread(thread_name, Communication::ReadThread, this, error_ptr); if (!m_read_thread.IsJoinable()) m_read_thread_enabled = false; @@ -376,9 +380,8 @@ Communication::ReadThread (lldb::thread_arg_t p) break; case eConnectionStatusEndOfFile: - if (comm->GetCloseOnEOF()) - done = true; - break; + done = true; + break; case eConnectionStatusError: // Check GetError() for details if (error.GetType() == eErrorTypePOSIX && error.GetError() == EIO) { @@ -392,9 +395,13 @@ Communication::ReadThread (lldb::thread_arg_t p) p, Communication::ConnectionStatusAsCString (status)); break; + case eConnectionStatusInterrupted: // Synchronization signal from SynchronizeWithReadThread() + // The connection returns eConnectionStatusInterrupted only when there is no + // input pending to be read, so we can signal that. + comm->BroadcastEvent (eBroadcastBitNoMorePendingInput); + break; case eConnectionStatusNoConnection: // No connection case eConnectionStatusLostConnection: // Lost connection while connected to a valid connection - case eConnectionStatusInterrupted: // Interrupted done = true; // Fall through... case eConnectionStatusTimedOut: // Request timed out @@ -410,7 +417,9 @@ Communication::ReadThread (lldb::thread_arg_t p) if (log) log->Printf ("%p Communication::ReadThread () thread exiting...", p); + comm->m_read_thread_did_exit = true; // Let clients know that this thread is exiting + comm->BroadcastEvent (eBroadcastBitNoMorePendingInput); comm->BroadcastEvent (eBroadcastBitReadThreadDidExit); return NULL; } @@ -427,6 +436,28 @@ Communication::SetReadThreadBytesReceivedCallback } void +Communication::SynchronizeWithReadThread () +{ + // Only one thread can do the synchronization dance at a time. + Mutex::Locker locker(m_synchronize_mutex); + + // First start listening for the synchronization event. + Listener listener("Communication::SyncronizeWithReadThread"); + listener.StartListeningForEvents(this, eBroadcastBitNoMorePendingInput); + + // If the thread is not running, there is no point in synchronizing. + if (!m_read_thread_enabled || m_read_thread_did_exit) + return; + + // Notify the read thread. + m_connection_sp->InterruptRead(); + + // Wait for the synchronization event. + EventSP event_sp; + listener.WaitForEvent(NULL, event_sp); +} + +void Communication::SetConnection (Connection *connection) { Disconnect (NULL); diff --git a/source/Core/ConnectionMachPort.cpp b/source/Core/ConnectionMachPort.cpp index fe29814be4202..e04c48905570f 100644 --- a/source/Core/ConnectionMachPort.cpp +++ b/source/Core/ConnectionMachPort.cpp @@ -17,7 +17,6 @@ // C++ Includes // Other libraries and framework includes // Project includes -#include "lldb/lldb-private-log.h" #include "lldb/Core/Communication.h" #include "lldb/Core/Log.h" @@ -69,7 +68,7 @@ ConnectionMachPort::Connect (const char *s, Error *error_ptr) ConnectionStatus status = eConnectionStatusError; - if (strncmp (s, "bootstrap-checkin://", strlen("bootstrap-checkin://"))) + if (0 == strncmp (s, "bootstrap-checkin://", strlen("bootstrap-checkin://"))) { s += strlen("bootstrap-checkin://"); @@ -83,7 +82,7 @@ ConnectionMachPort::Connect (const char *s, Error *error_ptr) error_ptr->SetErrorString ("bootstrap port name is empty"); } } - else if (strncmp (s, "bootstrap-lookup://", strlen("bootstrap-lookup://"))) + else if (0 == strncmp (s, "bootstrap-lookup://", strlen("bootstrap-lookup://"))) { s += strlen("bootstrap-lookup://"); if (*s) diff --git a/source/Core/ConnectionSharedMemory.cpp b/source/Core/ConnectionSharedMemory.cpp index d3dfa3cd8d564..77daeb14840d0 100644 --- a/source/Core/ConnectionSharedMemory.cpp +++ b/source/Core/ConnectionSharedMemory.cpp @@ -26,7 +26,6 @@ // Other libraries and framework includes // Project includes #include "llvm/Support/MathExtras.h" -#include "lldb/lldb-private-log.h" #include "lldb/Core/Communication.h" #include "lldb/Core/Log.h" diff --git a/source/Core/CxaDemangle.cpp b/source/Core/CxaDemangle.cpp new file mode 100644 index 0000000000000..bcc52ff39fe4e --- /dev/null +++ b/source/Core/CxaDemangle.cpp @@ -0,0 +1,5011 @@ +//---------------------------------------------------------------------- +// Inlined copy of: +// http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_demangle.cpp +// revision 238263. +// +// Changes include: +// - Renamed the "__cxxabiv1" namespace to "lldb_private" +// - Stripped GCC attributes() +// - Removed extern "C" from the cxa_demangle function +// - Added "#undef _LIBCPP_EXTERN_TEMPLATE" to avoid warning +// - Implemented missing rebind, construct, destroy in malloc_alloc +// - Replaced noexcept, constexpr, alignas with their LLVM_* equivalents +// - Included win32.h for snprintf implementation for MSVC +// - Removed constexpr member initialization for MSVC +// - Changed argument to alignas() to a literal for MSVC +//---------------------------------------------------------------------- + +#if defined(_MSC_VER) +#include "lldb/Host/windows/win32.h" // snprintf +#endif +#include "llvm/Support/Compiler.h" // LLVM_{NOEXCEPT, CONSTEXPR, ALIGNAS} +#undef _LIBCPP_EXTERN_TEMPLATE // Avoid warning below + +//===-------------------------- cxa_demangle.cpp --------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is dual licensed under the MIT and the University of Illinois Open +// Source Licenses. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#define _LIBCPP_EXTERN_TEMPLATE(...) +#define _LIBCPP_NO_EXCEPTIONS + +#include <vector> +#include <algorithm> +#include <string> +#include <numeric> +#include <cstdlib> +#include <cstring> +#include <cctype> + +namespace lldb_private +{ + +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, + bool* ends_with_template_args = 0); +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) +{ + fprintf(stderr, "---------\n"); + fprintf(stderr, "names:\n"); + for (auto& s : db.names) + fprintf(stderr, "{%s#%s}\n", s.first.c_str(), s.second.c_str()); + int i = -1; + fprintf(stderr, "subs:\n"); + for (auto& v : db.subs) + { + if (i >= 0) + fprintf(stderr, "S%i_ = {", i); + else + fprintf(stderr, "S_ = {"); + for (auto& s : v) + fprintf(stderr, "{%s#%s}", s.first.c_str(), s.second.c_str()); + fprintf(stderr, "}\n"); + ++i; + } + fprintf(stderr, "template_param:\n"); + for (auto& t : db.template_param) + { + fprintf(stderr, "--\n"); + i = -1; + for (auto& v : t) + { + if (i >= 0) + fprintf(stderr, "T%i_ = {", i); + else + fprintf(stderr, "T_ = {"); + for (auto& s : v) + fprintf(stderr, "{%s#%s}", s.first.c_str(), s.second.c_str()); + fprintf(stderr, "}\n"); + ++i; + } + } + fprintf(stderr, "---------\n\n"); +} + +template <class C> +void +print_state(const char* msg, const char* first, const char* last, const C& db) +{ + fprintf(stderr, "%s: ", msg); + for (; first != last; ++first) + fprintf(stderr, "%c", *first); + fprintf(stderr, "\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 const char* spec; +}; + +const char* float_data<float>::spec = "%af"; + +template <> +struct float_data<double> +{ + static const size_t mangled_size = 16; + static const size_t max_demangled_size = 32; + static const char* spec; +}; + +const char* float_data<double>::spec = "%a"; + +template <> +struct float_data<long double> +{ +#if defined(__arm__) + static const size_t mangled_size = 16; +#else + static const size_t mangled_size = 20; // May need to be adjusted to 16 or 24 on other platforms +#endif + static const size_t max_demangled_size = 40; + static const char* spec; +}; + +const char* float_data<long double>::spec = "%LaL"; + +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 __BYTE_ORDER__ == __ORDER_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) +// ::= Dc # decltype(auto) +// ::= 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 'c': + db.names.push_back("decltype(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.empty()) + return first; + 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 != '_' || db.template_param.empty()) + 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.empty()) + return 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.size() < 2) + return first; + 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 || db.names.size() < 2) + 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) + { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + 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) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "::"); + } + while (*t != 'E') + { + t1 = parse_unresolved_qualifier_level(t, last, db); + if (t1 == t || t1 == last || db.names.size() < 2) + 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) + { + if (!db.names.empty()) + db.names.pop_back(); + return first; + } + if (db.names.size() < 2) + 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) + { + if (db.names.size() < 2) + return first; + 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; + if (db.names.empty()) + 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; + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + if (!first_expr) + { + db.names.back().first.append(", "); + first_expr = false; + } + db.names.back().first.append(tmp); + } + t = t1; + } + ++t; + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + init_list = db.names.back().move_full(); + db.names.pop_back(); + } + if (db.names.empty()) + return first; + auto type = db.names.back().move_full(); + db.names.pop_back(); + typename C::String expr_list; + if (has_expr_list) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + if (!tmp.empty()) + { + if (db.names.empty()) + return first; + db.names.back().first.append(", "); + db.names.back().first.append(tmp); + first_expr = false; + } + } + t = t1; + } + } + ++t; + } + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (*t == 'Y') + { + /* extern "C" */ + 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; + } + if (db.names.empty()) + return first; + 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) + { + if (db.names.size() < 2) + return first; + auto func = std::move(db.names.back()); + db.names.pop_back(); + auto class_type = std::move(db.names.back()); + if (!func.second.empty() && 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.empty()) + return first; + 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.empty()) + return first; + 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) + { + if (db.names.size() < 2) + return first; + 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') + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return 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.empty() && + 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.empty() && + 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.empty() && + 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.empty()) + return 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) + { + if (db.names.size() < 2) + return 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) + { + if (db.names.empty()) + return 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) + { + if (db.names.empty()) + return 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) + { + if (db.names.empty()) + return 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 # <= +// ::= li <source-name> # operator "" +// ::= 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) + { + if (db.names.empty()) + return first; + 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 'i': + { + const char* t = parse_source_name(first+2, last, db); + if (t != first+2) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "operator\"\" "); + first = t; + } + } + 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) + { + if (db.names.empty()) + return first; + 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') + { + if (db.names.empty()) + return first; + 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': + if (db.names.empty()) + return first; + 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': + if (db.names.empty()) + return first; + 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; + } + if (db.names.size() < 2) + 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; + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return 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) + { + if (db.names.empty()) + return 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.size() < 2) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.size() < 3) + return first; + 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, + bool* ends_with_template_args) +{ + 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; + bool component_ends_with_template_args = false; + while (*t0 != 'E') + { + component_ends_with_template_args = false; + 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; + component_ends_with_template_args = true; + } + 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(); + if (ends_with_template_args) + *ends_with_template_args = component_ends_with_template_args; + } + 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, + bool* ends_with_template_args) +{ + 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); + if (db.names.empty()) + return first; + 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, + ends_with_template_args); + if (t1 != t) + { + if (db.names.size() < 2) + return first; + 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, + ends_with_template_args); + if (t1 != t) + { + // parse but ignore discriminator + first = parse_discriminator(t1, last); + if (db.names.size() < 2) + return first; + 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, + bool* ends_with_template_args) +{ + 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, + ends_with_template_args); + if (t1 != t0) + first = t1; + break; + } + case 'Z': + { + const char* t1 = parse_local_name(t0, last, db, + ends_with_template_args); + 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> + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += tmp; + first = t1; + if (ends_with_template_args) + *ends_with_template_args = true; + } + } + 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) + { + if (db.names.size() < 2) + return first; + auto tmp = db.names.back().move_full(); + db.names.pop_back(); + db.names.back().first += tmp; + first = t1; + if (ends_with_template_args) + *ends_with_template_args = true; + } + } + } + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.size() < 2) + return first; + 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 (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + 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) + { + if (db.names.empty()) + return first; + db.names.back().first.insert(0, "reference temporary for "); + first = t; + } + break; + } + break; + } + } + return first; +} + +template <class T> +class save_value +{ + T& restore_; + T original_value_; +public: + save_value(T& restore) + : restore_(restore), + original_value_(restore) + {} + + ~save_value() + { + restore_ = std::move(original_value_); + } + + save_value(const save_value&) = delete; + save_value& operator=(const save_value&) = delete; +}; + +// <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) + { + save_value<decltype(db.encoding_depth)> su(db.encoding_depth); + ++db.encoding_depth; + save_value<decltype(db.tag_templates)> sb(db.tag_templates); + if (db.encoding_depth > 1) + db.tag_templates = true; + switch (*first) + { + case 'G': + case 'T': + first = parse_special_name(first, last, db); + break; + default: + { + bool ends_with_template_args = false; + const char* t = parse_name(first, last, db, + &ends_with_template_args); + unsigned cv = db.cv; + unsigned ref = db.ref; + if (t != first) + { + if (t != last && *t != 'E' && *t != '.') + { + save_value<bool> sb2(db.tag_templates); + db.tag_templates = false; + const char* t2; + typename C::String ret2; + if (db.names.empty()) + return first; + const typename C::String& nm = db.names.back().first; + if (nm.empty()) + return first; + if (!db.parsed_ctor_dtor_cv && ends_with_template_args) + { + t2 = parse_type(t, last, db); + if (t2 == t) + return first; + if (db.names.size() < 2) + 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 (db.names.empty()) + return first; + if (!first_arg) + db.names.back().first += ", "; + else + first_arg = false; + db.names.back().first += tmp; + } + } + t = t2; + } + } + if (db.names.empty()) + return first; + 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; + } + 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; + } + if (db.names.empty()) + return first; + 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 == '.') + { + if (db.names.empty()) + return 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; + LLVM_ALIGNAS(16) char buf_[N]; + char* ptr_; + + std::size_t + align_up(std::size_t n) LLVM_NOEXCEPT + {return (n + (alignment-1)) & ~(alignment-1);} + + bool + pointer_in_buffer(char* p) LLVM_NOEXCEPT + {return buf_ <= p && p <= buf_ + N;} + +public: + arena() LLVM_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) LLVM_NOEXCEPT; + + static LLVM_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) LLVM_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) LLVM_NOEXCEPT : a_(a) {} + template <class U> + short_alloc(const short_alloc<U, N>& a) LLVM_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) LLVM_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) LLVM_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) LLVM_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) LLVM_NOEXCEPT +{ + return !(x == y); +} + +template <class T> +class malloc_alloc +{ +public: + typedef T value_type; + typedef std::size_t size_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef const T* const_pointer; + typedef T& reference; + typedef const T& const_reference; + + malloc_alloc() = default; + template <class U> malloc_alloc(const malloc_alloc<U>&) LLVM_NOEXCEPT {} + + T* allocate(std::size_t n) + { + return static_cast<T*>(std::malloc(n*sizeof(T))); + } + void deallocate(T* p, std::size_t) LLVM_NOEXCEPT + { + std::free(p); + } + template<class Other> + struct rebind + { + typedef malloc_alloc<Other> other; + }; + void construct(T *p) + { + ::new (p) T(); + } + void construct(T *p, const T& t) + { + ::new (p) T(t); + } + void destroy(T *p) + { + p->~T(); + } +}; + +template <class T, class U> +inline +bool +operator==(const malloc_alloc<T>&, const malloc_alloc<U>&) LLVM_NOEXCEPT +{ + return true; +} + +template <class T, class U> +inline +bool +operator!=(const malloc_alloc<T>& x, const malloc_alloc<U>& y) LLVM_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>>; + +template <class StrT> +struct string_pair +{ + StrT first; + StrT second; + + string_pair() = default; + string_pair(StrT f) : first(std::move(f)) {} + string_pair(StrT f, StrT 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();} + StrT full() const {return first + second;} + StrT move_full() {return std::move(first) + std::move(second);} +}; + +struct Db +{ + typedef std::basic_string<char, std::char_traits<char>, + malloc_alloc<char>> String; + typedef Vector<string_pair<String>> sub_type; + typedef Vector<sub_type> template_param_type; + sub_type names; + template_param_type subs; + Vector<template_param_type> template_param; + unsigned cv; + unsigned ref; + unsigned encoding_depth; + 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) + {} +}; + +} // unnamed namespace + +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.encoding_depth = 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; +} + +} // lldb_private diff --git a/source/Core/DataBufferHeap.cpp b/source/Core/DataBufferHeap.cpp index 984b36e54153e..ba1314448bbd8 100644 --- a/source/Core/DataBufferHeap.cpp +++ b/source/Core/DataBufferHeap.cpp @@ -106,7 +106,7 @@ DataBufferHeap::CopyData (const void *src, uint64_t src_len) void DataBufferHeap::AppendData (const void *src, uint64_t src_len) { - m_data.insert(m_data.end(), (uint8_t *)src, (uint8_t *)src + src_len); + m_data.insert(m_data.end(), (const uint8_t *)src, (const uint8_t *)src + src_len); } void diff --git a/source/Core/DataBufferMemoryMap.cpp b/source/Core/DataBufferMemoryMap.cpp index 4ca43b89eef1f..b3211b35af85a 100644 --- a/source/Core/DataBufferMemoryMap.cpp +++ b/source/Core/DataBufferMemoryMap.cpp @@ -26,7 +26,6 @@ #include "lldb/Host/FileSpec.h" #include "lldb/Host/HostInfo.h" #include "lldb/Core/Log.h" -#include "lldb/lldb-private-log.h" using namespace lldb; using namespace lldb_private; @@ -91,7 +90,8 @@ DataBufferMemoryMap::Clear() { Log *log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_MMAP)); if (log) - log->Printf("DataBufferMemoryMap::Clear() m_mmap_addr = %p, m_mmap_size = %" PRIu64 "", m_mmap_addr, (uint64_t)m_mmap_size); + log->Printf("DataBufferMemoryMap::Clear() m_mmap_addr = %p, m_mmap_size = %" PRIu64 "", (void *)m_mmap_addr, + (uint64_t)m_mmap_size); #ifdef _WIN32 UnmapViewOfFile(m_mmap_addr); #else @@ -284,9 +284,9 @@ DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd, // Save the actual mmap'ed size m_mmap_size = length + page_offset; - // Our data is at an offset into the the mapped data + // Our data is at an offset into the mapped data m_data = m_mmap_addr + page_offset; - // Our pretend size is the size that was requestd + // Our pretend size is the size that was requested m_size = length; } } @@ -307,8 +307,10 @@ DataBufferMemoryMap::MemoryMapFromFileDescriptor (int fd, if (log) { - log->Printf("DataBufferMemoryMap::MemoryMapFromFileSpec() m_mmap_addr = %p, m_mmap_size = %" PRIu64 ", error = %s", - m_mmap_addr, (uint64_t)m_mmap_size, error.AsCString()); + log->Printf( + "DataBufferMemoryMap::MemoryMapFromFileSpec() m_mmap_addr = %p, m_mmap_size = %" PRIu64 + ", error = %s", + (void *)m_mmap_addr, (uint64_t)m_mmap_size, error.AsCString()); } } } diff --git a/source/Core/DataEncoder.cpp b/source/Core/DataEncoder.cpp index 92a9104acc3a2..d21ca423892f7 100644 --- a/source/Core/DataEncoder.cpp +++ b/source/Core/DataEncoder.cpp @@ -21,36 +21,36 @@ using namespace lldb; using namespace lldb_private; static inline void -WriteInt16(const unsigned char* ptr, unsigned offset, uint16_t value) +WriteInt16(unsigned char* ptr, unsigned offset, uint16_t value) { *(uint16_t *)(ptr + offset) = value; } static inline void -WriteInt32 (const unsigned char* ptr, unsigned offset, uint32_t value) +WriteInt32 (unsigned char* ptr, unsigned offset, uint32_t value) { *(uint32_t *)(ptr + offset) = value; } static inline void -WriteInt64(const unsigned char* ptr, unsigned offset, uint64_t value) +WriteInt64(unsigned char* ptr, unsigned offset, uint64_t value) { *(uint64_t *)(ptr + offset) = value; } static inline void -WriteSwappedInt16(const unsigned char* ptr, unsigned offset, uint16_t value) +WriteSwappedInt16(unsigned char* ptr, unsigned offset, uint16_t value) { *(uint16_t *)(ptr + offset) = llvm::ByteSwap_16(value); } static inline void -WriteSwappedInt32 (const unsigned char* ptr, unsigned offset, uint32_t value) +WriteSwappedInt32 (unsigned char* ptr, unsigned offset, uint32_t value) { *(uint32_t *)(ptr + offset) = llvm::ByteSwap_32(value); } static inline void -WriteSwappedInt64(const unsigned char* ptr, unsigned offset, uint64_t value) +WriteSwappedInt64(unsigned char* ptr, unsigned offset, uint64_t value) { *(uint64_t *)(ptr + offset) = llvm::ByteSwap_64(value); } @@ -153,7 +153,7 @@ DataEncoder::GetSharedDataOffset () const // any data extracted will be endian swapped. //---------------------------------------------------------------------- uint32_t -DataEncoder::SetData (const void *bytes, uint32_t length, ByteOrder endian) +DataEncoder::SetData (void *bytes, uint32_t length, ByteOrder endian) { m_byte_order = endian; m_data_sp.reset(); diff --git a/source/Core/DataExtractor.cpp b/source/Core/DataExtractor.cpp index 6e1d63095cf31..b4b43ed97780a 100644 --- a/source/Core/DataExtractor.cpp +++ b/source/Core/DataExtractor.cpp @@ -1710,7 +1710,7 @@ DataExtractor::Dump (Stream *s, { size_t complex_int_byte_size = item_byte_size / 2; - if (complex_int_byte_size <= 8) + if (complex_int_byte_size > 0 && complex_int_byte_size <= 8) { s->Printf("%" PRIu64, GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0)); s->Printf(" + %" PRIu64 "i", GetMaxU64Bitfield(&offset, complex_int_byte_size, 0, 0)); diff --git a/source/Core/Debugger.cpp b/source/Core/Debugger.cpp index f25a3f41825f7..cd41e5d651036 100644 --- a/source/Core/Debugger.cpp +++ b/source/Core/Debugger.cpp @@ -7,8 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - #include "lldb/Core/Debugger.h" #include <map> @@ -39,6 +37,7 @@ #include "lldb/Host/Terminal.h" #include "lldb/Host/ThreadLauncher.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/OptionValueSInt64.h" #include "lldb/Interpreter/OptionValueString.h" #include "lldb/Symbol/ClangASTContext.h" @@ -63,7 +62,6 @@ using namespace lldb; using namespace lldb_private; -static uint32_t g_shared_debugger_refcount = 0; static lldb::user_id_t g_unique_id = 1; static size_t g_debugger_event_thread_stack_bytes = 8 * 1024 * 1024; @@ -126,7 +124,21 @@ g_language_enumerators[] = FILE_AND_LINE\ "\\n" -#define DEFAULT_DISASSEMBLY_FORMAT "${current-pc-arrow}${addr-file-or-load}{ <${function.name-without-args}${function.concrete-only-addr-offset-no-padding}>}: " +// Three parts to this disassembly format specification: +// 1. If this is a new function/symbol (no previous symbol/function), print +// dylib`funcname:\n +// 2. If this is a symbol context change (different from previous symbol/function), print +// dylib`funcname:\n +// 3. print +// address <+offset>: +#define DEFAULT_DISASSEMBLY_FORMAT "{${function.initial-function}{${module.file.basename}`}{${function.name-without-args}}:\n}{${function.changed}\n{${module.file.basename}`}{${function.name-without-args}}:\n}{${current-pc-arrow} }${addr-file-or-load}{ <${function.concrete-only-addr-offset-no-padding}>}: " + +// gdb's disassembly format can be emulated with +// ${current-pc-arrow}${addr-file-or-load}{ <${function.name-without-args}${function.concrete-only-addr-offset-no-padding}>}: + +// lldb's original format for disassembly would look like this format string - +// {${function.initial-function}{${module.file.basename}`}{${function.name-without-args}}:\n}{${function.changed}\n{${module.file.basename}`}{${function.name-without-args}}:\n}{${current-pc-arrow} }{${addr-file-or-load}}: + static PropertyDefinition g_properties[] = @@ -170,7 +182,7 @@ enum ePropertyEscapeNonPrintables }; -Debugger::LoadPluginCallbackType Debugger::g_load_plugin_callback = NULL; +LoadPluginCallbackType Debugger::g_load_plugin_callback = NULL; Error Debugger::SetPropertyValue (const ExecutionContext *exe_ctx, @@ -391,36 +403,24 @@ Debugger::GetEscapeNonPrintables () const //} // -int -Debugger::TestDebuggerRefCount () -{ - return g_shared_debugger_refcount; -} - +static bool lldb_initialized = false; void -Debugger::Initialize (LoadPluginCallbackType load_plugin_callback) +Debugger::Initialize(LoadPluginCallbackType load_plugin_callback) { + assert(!lldb_initialized && "Debugger::Initialize called more than once!"); + + lldb_initialized = true; g_load_plugin_callback = load_plugin_callback; - if (g_shared_debugger_refcount++ == 0) - lldb_private::Initialize(); } void Debugger::Terminate () { - if (g_shared_debugger_refcount > 0) - { - g_shared_debugger_refcount--; - if (g_shared_debugger_refcount == 0) - { - lldb_private::WillTerminate(); - lldb_private::Terminate(); + assert(lldb_initialized && "Debugger::Terminate called without a matching Debugger::Initialize!"); - // Clear our master list of debugger objects - Mutex::Locker locker (GetDebuggerListMutex ()); - GetDebuggerList().clear(); - } - } + // Clear our master list of debugger objects + Mutex::Locker locker (GetDebuggerListMutex ()); + GetDebuggerList().clear(); } void @@ -553,7 +553,7 @@ DebuggerSP Debugger::CreateInstance (lldb::LogOutputCallback log_callback, void *baton) { DebuggerSP debugger_sp (new Debugger(log_callback, baton)); - if (g_shared_debugger_refcount > 0) + if (lldb_initialized) { Mutex::Locker locker (GetDebuggerListMutex ()); GetDebuggerList().push_back(debugger_sp); @@ -570,7 +570,7 @@ Debugger::Destroy (DebuggerSP &debugger_sp) debugger_sp->Clear(); - if (g_shared_debugger_refcount > 0) + if (lldb_initialized) { Mutex::Locker locker (GetDebuggerListMutex ()); DebuggerList &debugger_list = GetDebuggerList (); @@ -590,7 +590,7 @@ DebuggerSP Debugger::FindDebuggerWithInstanceName (const ConstString &instance_name) { DebuggerSP debugger_sp; - if (g_shared_debugger_refcount > 0) + if (lldb_initialized) { Mutex::Locker locker (GetDebuggerListMutex ()); DebuggerList &debugger_list = GetDebuggerList(); @@ -612,7 +612,7 @@ TargetSP Debugger::FindTargetWithProcessID (lldb::pid_t pid) { TargetSP target_sp; - if (g_shared_debugger_refcount > 0) + if (lldb_initialized) { Mutex::Locker locker (GetDebuggerListMutex ()); DebuggerList &debugger_list = GetDebuggerList(); @@ -631,7 +631,7 @@ TargetSP Debugger::FindTargetWithProcess (Process *process) { TargetSP target_sp; - if (g_shared_debugger_refcount > 0) + if (lldb_initialized) { Mutex::Locker locker (GetDebuggerListMutex ()); DebuggerList &debugger_list = GetDebuggerList(); @@ -682,6 +682,10 @@ Debugger::Debugger(lldb::LogOutputCallback log_callback, void *baton) : ConstString("Settings specify to debugging targets."), true, Target::GetGlobalProperties()->GetValueProperties()); + m_collection_sp->AppendProperty (ConstString("platform"), + ConstString("Platform settings."), + true, + Platform::GetGlobalPlatformProperties()->GetValueProperties()); if (m_command_interpreter_ap.get()) { m_collection_sp->AppendProperty (ConstString("interpreter"), @@ -879,34 +883,27 @@ Debugger::ClearIOHandlers () { IOHandlerSP reader_sp (m_input_reader_stack.Top()); if (reader_sp) - { - m_input_reader_stack.Pop(); - reader_sp->SetIsDone(true); - reader_sp->Cancel(); - } + PopIOHandler (reader_sp); } } void -Debugger::ExecuteIOHanders() +Debugger::ExecuteIOHandlers() { - while (1) { IOHandlerSP reader_sp(m_input_reader_stack.Top()); if (!reader_sp) break; - reader_sp->Activate(); reader_sp->Run(); - reader_sp->Deactivate(); // Remove all input readers that are done from the top of the stack while (1) { IOHandlerSP top_reader_sp = m_input_reader_stack.Top(); if (top_reader_sp && top_reader_sp->GetIsDone()) - m_input_reader_stack.Pop(); + PopIOHandler (top_reader_sp); else break; } @@ -920,6 +917,12 @@ Debugger::IsTopIOHandler (const lldb::IOHandlerSP& reader_sp) return m_input_reader_stack.IsTop (reader_sp); } +void +Debugger::PrintAsync (const char *s, size_t len, bool is_stdout) +{ + lldb::StreamFileSP stream = is_stdout ? GetOutputFile() : GetErrorFile(); + m_input_reader_stack.PrintAsync(stream.get(), s, len); +} ConstString Debugger::GetTopIOHandlerControlSequence(char ch) @@ -943,25 +946,23 @@ void Debugger::RunIOHandler (const IOHandlerSP& reader_sp) { PushIOHandler (reader_sp); - + IOHandlerSP top_reader_sp = reader_sp; while (top_reader_sp) { - top_reader_sp->Activate(); top_reader_sp->Run(); - top_reader_sp->Deactivate(); - + if (top_reader_sp.get() == reader_sp.get()) { if (PopIOHandler (reader_sp)) break; } - + while (1) { top_reader_sp = m_input_reader_stack.Top(); if (top_reader_sp && top_reader_sp->GetIsDone()) - m_input_reader_stack.Pop(); + PopIOHandler (top_reader_sp); else break; } @@ -1024,93 +1025,73 @@ Debugger::PushIOHandler (const IOHandlerSP& reader_sp) if (!reader_sp) return; - // Got the current top input reader... + Mutex::Locker locker (m_input_reader_stack.GetMutex()); + + // Get the current top input reader... IOHandlerSP top_reader_sp (m_input_reader_stack.Top()); // Don't push the same IO handler twice... - if (reader_sp.get() != top_reader_sp.get()) - { - // Push our new input reader - m_input_reader_stack.Push (reader_sp); + if (reader_sp == top_reader_sp) + return; - // Interrupt the top input reader to it will exit its Run() function - // and let this new input reader take over - if (top_reader_sp) - top_reader_sp->Deactivate(); + // Push our new input reader + m_input_reader_stack.Push (reader_sp); + reader_sp->Activate(); + + // Interrupt the top input reader to it will exit its Run() function + // and let this new input reader take over + if (top_reader_sp) + { + top_reader_sp->Deactivate(); + top_reader_sp->Cancel(); } } bool Debugger::PopIOHandler (const IOHandlerSP& pop_reader_sp) { - bool result = false; - + if (! pop_reader_sp) + return false; + Mutex::Locker locker (m_input_reader_stack.GetMutex()); // The reader on the stop of the stack is done, so let the next // read on the stack refresh its prompt and if there is one... - if (!m_input_reader_stack.IsEmpty()) - { - IOHandlerSP reader_sp(m_input_reader_stack.Top()); - - if (!pop_reader_sp || pop_reader_sp.get() == reader_sp.get()) - { - reader_sp->Deactivate(); - reader_sp->Cancel(); - m_input_reader_stack.Pop (); - - reader_sp = m_input_reader_stack.Top(); - if (reader_sp) - reader_sp->Activate(); - - result = true; - } - } - return result; -} - -bool -Debugger::HideTopIOHandler() -{ - Mutex::Locker locker; - - if (locker.TryLock(m_input_reader_stack.GetMutex())) - { - IOHandlerSP reader_sp(m_input_reader_stack.Top()); - if (reader_sp) - reader_sp->Hide(); - return true; - } - return false; -} + if (m_input_reader_stack.IsEmpty()) + return false; -void -Debugger::RefreshTopIOHandler() -{ IOHandlerSP reader_sp(m_input_reader_stack.Top()); + + if (pop_reader_sp != reader_sp) + return false; + + reader_sp->Deactivate(); + reader_sp->Cancel(); + m_input_reader_stack.Pop (); + + reader_sp = m_input_reader_stack.Top(); if (reader_sp) - reader_sp->Refresh(); -} + reader_sp->Activate(); + return true; +} StreamSP Debugger::GetAsyncOutputStream () { - return StreamSP (new StreamAsynchronousIO (GetCommandInterpreter(), - CommandInterpreter::eBroadcastBitAsynchronousOutputData)); + return StreamSP (new StreamAsynchronousIO (*this, true)); } StreamSP Debugger::GetAsyncErrorStream () { - return StreamSP (new StreamAsynchronousIO (GetCommandInterpreter(), - CommandInterpreter::eBroadcastBitAsynchronousErrorData)); + return StreamSP (new StreamAsynchronousIO (*this, false)); } size_t Debugger::GetNumDebuggers() { - if (g_shared_debugger_refcount > 0) + if (lldb_initialized) { Mutex::Locker locker (GetDebuggerListMutex ()); return GetDebuggerList().size(); @@ -1123,7 +1104,7 @@ Debugger::GetDebuggerAtIndex (size_t index) { DebuggerSP debugger_sp; - if (g_shared_debugger_refcount > 0) + if (lldb_initialized) { Mutex::Locker locker (GetDebuggerListMutex ()); DebuggerList &debugger_list = GetDebuggerList(); @@ -1140,7 +1121,7 @@ Debugger::FindDebuggerWithID (lldb::user_id_t id) { DebuggerSP debugger_sp; - if (g_shared_debugger_refcount > 0) + if (lldb_initialized) { Mutex::Locker locker (GetDebuggerListMutex ()); DebuggerList &debugger_list = GetDebuggerList(); @@ -1292,8 +1273,6 @@ Debugger::SetLoggingCallback (lldb::LogOutputCallback log_callback, void *baton) bool Debugger::EnableLog (const char *channel, const char **categories, const char *log_file, uint32_t log_options, Stream &error_stream) { - Log::Callbacks log_callbacks; - StreamSP log_stream_sp; if (m_log_callback_stream_sp) { @@ -1312,7 +1291,12 @@ Debugger::EnableLog (const char *channel, const char **categories, const char *l log_stream_sp = pos->second.lock(); if (!log_stream_sp) { - log_stream_sp.reset (new StreamFile (log_file)); + uint32_t options = File::eOpenOptionWrite | File::eOpenOptionCanCreate + | File::eOpenOptionCloseOnExec | File::eOpenOptionAppend; + if (! (log_options & LLDB_LOG_OPTION_APPEND)) + options |= File::eOpenOptionTruncate; + + log_stream_sp.reset (new StreamFile (log_file, options)); m_log_streams[log_file] = log_stream_sp; } } @@ -1321,33 +1305,7 @@ Debugger::EnableLog (const char *channel, const char **categories, const char *l if (log_options == 0) log_options = LLDB_LOG_OPTION_PREPEND_THREAD_NAME | LLDB_LOG_OPTION_THREADSAFE; - if (Log::GetLogChannelCallbacks (ConstString(channel), log_callbacks)) - { - log_callbacks.enable (log_stream_sp, log_options, categories, &error_stream); - return true; - } - else - { - LogChannelSP log_channel_sp (LogChannel::FindPlugin (channel)); - if (log_channel_sp) - { - if (log_channel_sp->Enable (log_stream_sp, log_options, &error_stream, categories)) - { - return true; - } - else - { - error_stream.Printf ("Invalid log channel '%s'.\n", channel); - return false; - } - } - else - { - error_stream.Printf ("Invalid log channel '%s'.\n", channel); - return false; - } - } - return false; + return Log::EnableLogChannel(log_stream_sp, log_options, channel, categories, error_stream); } SourceManager & @@ -1385,14 +1343,14 @@ Debugger::HandleBreakpointEvent (const EventSP &event_sp) if (num_new_locations > 0) { BreakpointSP breakpoint = Breakpoint::BreakpointEventData::GetBreakpointFromEvent(event_sp); - StreamFileSP output_sp (GetOutputFile()); + StreamSP output_sp (GetAsyncOutputStream()); if (output_sp) { output_sp->Printf("%d location%s added to breakpoint %d\n", num_new_locations, num_new_locations == 1 ? "" : "s", breakpoint->GetID()); - RefreshTopIOHandler(); + output_sp->Flush(); } } } @@ -1479,8 +1437,8 @@ Debugger::HandleProcessEvent (const EventSP &event_sp) const uint32_t event_type = event_sp->GetType(); ProcessSP process_sp = Process::ProcessEventData::GetProcessFromEvent(event_sp.get()); - StreamString output_stream; - StreamString error_stream; + StreamSP output_stream_sp = GetAsyncOutputStream(); + StreamSP error_stream_sp = GetAsyncErrorStream(); const bool gui_enabled = IsForwardingEvents(); if (!gui_enabled) @@ -1488,47 +1446,43 @@ Debugger::HandleProcessEvent (const EventSP &event_sp) bool pop_process_io_handler = false; assert (process_sp); - if (event_type & Process::eBroadcastBitSTDOUT || event_type & Process::eBroadcastBitStateChanged) + bool state_is_stopped = false; + const bool got_state_changed = (event_type & Process::eBroadcastBitStateChanged) != 0; + const bool got_stdout = (event_type & Process::eBroadcastBitSTDOUT) != 0; + const bool got_stderr = (event_type & Process::eBroadcastBitSTDERR) != 0; + if (got_state_changed) { - GetProcessSTDOUT (process_sp.get(), &output_stream); + StateType event_state = Process::ProcessEventData::GetStateFromEvent (event_sp.get()); + state_is_stopped = StateIsStoppedState(event_state, false); } - if (event_type & Process::eBroadcastBitSTDERR || event_type & Process::eBroadcastBitStateChanged) + // Display running state changes first before any STDIO + if (got_state_changed && !state_is_stopped) { - GetProcessSTDERR (process_sp.get(), &error_stream); + Process::HandleProcessStateChangedEvent (event_sp, output_stream_sp.get(), pop_process_io_handler); } - if (event_type & Process::eBroadcastBitStateChanged) + // Now display and STDOUT + if (got_stdout || got_state_changed) { - Process::HandleProcessStateChangedEvent (event_sp, &output_stream, pop_process_io_handler); + GetProcessSTDOUT (process_sp.get(), output_stream_sp.get()); } - if (output_stream.GetSize() || error_stream.GetSize()) + // Now display and STDERR + if (got_stderr || got_state_changed) { - StreamFileSP error_stream_sp (GetOutputFile()); - bool top_io_handler_hid = false; - - if (process_sp->ProcessIOHandlerIsActive() == false) - top_io_handler_hid = HideTopIOHandler(); - - if (output_stream.GetSize()) - { - StreamFileSP output_stream_sp (GetOutputFile()); - if (output_stream_sp) - output_stream_sp->Write (output_stream.GetData(), output_stream.GetSize()); - } - - if (error_stream.GetSize()) - { - StreamFileSP error_stream_sp (GetErrorFile()); - if (error_stream_sp) - error_stream_sp->Write (error_stream.GetData(), error_stream.GetSize()); - } + GetProcessSTDERR (process_sp.get(), error_stream_sp.get()); + } - if (top_io_handler_hid) - RefreshTopIOHandler(); + // Now display any stopped state changes after any STDIO + if (got_state_changed && state_is_stopped) + { + Process::HandleProcessStateChangedEvent (event_sp, output_stream_sp.get(), pop_process_io_handler); } + output_stream_sp->Flush(); + error_stream_sp->Flush(); + if (pop_process_io_handler) process_sp->PopProcessIOHandler(); } @@ -1547,10 +1501,7 @@ Debugger::HandleThreadEvent (const EventSP &event_sp) ThreadSP thread_sp (Thread::ThreadEventData::GetThreadFromEvent (event_sp.get())); if (thread_sp) { - HideTopIOHandler(); - StreamFileSP stream_sp (GetOutputFile()); - thread_sp->GetStatus(*stream_sp, 0, 1, 1); - RefreshTopIOHandler(); + thread_sp->GetStatus(*GetAsyncOutputStream(), 0, 1, 1); } } } @@ -1644,13 +1595,11 @@ Debugger::DefaultEventHandler() const char *data = reinterpret_cast<const char *>(EventDataBytes::GetBytesFromEvent (event_sp.get())); if (data && data[0]) { - StreamFileSP error_sp (GetErrorFile()); + StreamSP error_sp (GetAsyncErrorStream()); if (error_sp) { - HideTopIOHandler(); error_sp->PutCString(data); error_sp->Flush(); - RefreshTopIOHandler(); } } } @@ -1659,13 +1608,11 @@ Debugger::DefaultEventHandler() const char *data = reinterpret_cast<const char *>(EventDataBytes::GetBytesFromEvent (event_sp.get())); if (data && data[0]) { - StreamFileSP output_sp (GetOutputFile()); + StreamSP output_sp (GetAsyncOutputStream()); if (output_sp) { - HideTopIOHandler(); output_sp->PutCString(data); output_sp->Flush(); - RefreshTopIOHandler(); } } } @@ -1729,7 +1676,7 @@ lldb::thread_result_t Debugger::IOHandlerThread (lldb::thread_arg_t arg) { Debugger *debugger = (Debugger *)arg; - debugger->ExecuteIOHanders(); + debugger->ExecuteIOHandlers(); debugger->StopEventHandlerThread(); return NULL; } diff --git a/source/Core/Disassembler.cpp b/source/Core/Disassembler.cpp index 14fbee149a527..f96232fcceebd 100644 --- a/source/Core/Disassembler.cpp +++ b/source/Core/Disassembler.cpp @@ -7,8 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - #include "lldb/Core/Disassembler.h" // C Includes @@ -421,6 +419,47 @@ Disassembler::PrintInstructions } const uint32_t scope = eSymbolContextLineEntry | eSymbolContextFunction | eSymbolContextSymbol; const bool use_inline_block_range = false; + + const FormatEntity::Entry *disassembly_format = NULL; + FormatEntity::Entry format; + if (exe_ctx.HasTargetScope()) + { + disassembly_format = exe_ctx.GetTargetRef().GetDebugger().GetDisassemblyFormat (); + } + else + { + FormatEntity::Parse("${addr}: ", format); + disassembly_format = &format; + } + + // First pass: step through the list of instructions, + // find how long the initial addresses strings are, insert padding + // in the second pass so the opcodes all line up nicely. + size_t address_text_size = 0; + for (size_t i = 0; i < num_instructions_found; ++i) + { + Instruction *inst = disasm_ptr->GetInstructionList().GetInstructionAtIndex (i).get(); + if (inst) + { + const Address &addr = inst->GetAddress(); + ModuleSP module_sp (addr.GetModule()); + if (module_sp) + { + const uint32_t resolve_mask = eSymbolContextFunction | eSymbolContextSymbol; + uint32_t resolved_mask = module_sp->ResolveSymbolContextForAddress(addr, resolve_mask, sc); + if (resolved_mask) + { + StreamString strmstr; + Debugger::FormatDisassemblerAddress (disassembly_format, &sc, NULL, &exe_ctx, &addr, strmstr); + size_t cur_line = strmstr.GetSizeOfLastLine(); + if (cur_line > address_text_size) + address_text_size = cur_line; + } + sc.Clear(false); + } + } + } + for (size_t i = 0; i < num_instructions_found; ++i) { Instruction *inst = disasm_ptr->GetInstructionList().GetInstructionAtIndex (i).get(); @@ -448,7 +487,7 @@ Disassembler::PrintInstructions if (offset != 0) strm.EOL(); - sc.DumpStopContext(&strm, exe_ctx.GetProcessPtr(), addr, false, true, false, false); + sc.DumpStopContext(&strm, exe_ctx.GetProcessPtr(), addr, false, true, false, false, true); strm.EOL(); if (sc.comp_unit && sc.line_entry.IsValid()) @@ -471,7 +510,7 @@ Disassembler::PrintInstructions } const bool show_bytes = (options & eOptionShowBytes) != 0; - inst->Dump (&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx, &sc, &prev_sc, NULL); + inst->Dump (&strm, max_opcode_byte_size, true, show_bytes, &exe_ctx, &sc, &prev_sc, NULL, address_text_size); strm.EOL(); } else @@ -509,7 +548,7 @@ Disassembler::Disassemble } else if (sc.symbol && sc.symbol->ValueIsAddress()) { - range.GetBaseAddress() = sc.symbol->GetAddress(); + range.GetBaseAddress() = sc.symbol->GetAddressRef(); range.SetByteSize (sc.symbol->GetByteSize()); } else @@ -561,7 +600,8 @@ Instruction::Dump (lldb_private::Stream *s, const ExecutionContext* exe_ctx, const SymbolContext *sym_ctx, const SymbolContext *prev_sym_ctx, - const FormatEntity::Entry *disassembly_addr_format) + const FormatEntity::Entry *disassembly_addr_format, + size_t max_address_text_size) { size_t opcode_column_width = 7; const size_t operand_column_width = 25; @@ -573,6 +613,7 @@ Instruction::Dump (lldb_private::Stream *s, if (show_address) { Debugger::FormatDisassemblerAddress (disassembly_addr_format, sym_ctx, prev_sym_ctx, exe_ctx, &m_address, ss); + ss.FillLastLineToColumn (max_address_text_size, ' '); } if (show_bytes) @@ -683,7 +724,7 @@ Instruction::ReadArray (FILE *in_file, Stream *out_stream, OptionValue::Type dat { case OptionValue::eTypeUInt64: data_value_sp.reset (new OptionValueUInt64 (0, 0)); - data_value_sp->SetValueFromCString (value.c_str()); + data_value_sp->SetValueFromString (value); break; // Other types can be added later as needed. default: @@ -791,7 +832,7 @@ Instruction::ReadDictionary (FILE *in_file, Stream *out_stream) else if ((value[0] == '0') && (value[1] == 'x')) { value_sp.reset (new OptionValueUInt64 (0, 0)); - value_sp->SetValueFromCString (value.c_str()); + value_sp->SetValueFromString (value); } else { @@ -999,7 +1040,7 @@ InstructionList::Dump (Stream *s, { if (pos != begin) s->EOL(); - (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx, NULL, NULL, disassembly_format); + (*pos)->Dump(s, max_opcode_byte_size, show_address, show_bytes, exe_ctx, NULL, NULL, disassembly_format, 0); } } @@ -1018,12 +1059,13 @@ InstructionList::Append (lldb::InstructionSP &inst_sp) } uint32_t -InstructionList::GetIndexOfNextBranchInstruction(uint32_t start) const +InstructionList::GetIndexOfNextBranchInstruction(uint32_t start, Target &target) const { size_t num_instructions = m_instructions.size(); uint32_t next_branch = UINT32_MAX; - for (size_t i = start; i < num_instructions; i++) + size_t i; + for (i = start; i < num_instructions; i++) { if (m_instructions[i]->DoesBranch()) { @@ -1031,6 +1073,52 @@ InstructionList::GetIndexOfNextBranchInstruction(uint32_t start) const break; } } + + // Hexagon needs the first instruction of the packet with the branch. + // Go backwards until we find an instruction marked end-of-packet, or + // until we hit start. + if (target.GetArchitecture().GetTriple().getArch() == llvm::Triple::hexagon) + { + // If we didn't find a branch, find the last packet start. + if (next_branch == UINT32_MAX) + { + i = num_instructions - 1; + } + + while (i > start) + { + --i; + + Error error; + uint32_t inst_bytes; + bool prefer_file_cache = false; // Read from process if process is running + lldb::addr_t load_addr = LLDB_INVALID_ADDRESS; + target.ReadMemory(m_instructions[i]->GetAddress(), + prefer_file_cache, + &inst_bytes, + sizeof(inst_bytes), + error, + &load_addr); + // If we have an error reading memory, return start + if (!error.Success()) + return start; + // check if this is the last instruction in a packet + // bits 15:14 will be 11b or 00b for a duplex + if (((inst_bytes & 0xC000) == 0xC000) || + ((inst_bytes & 0xC000) == 0x0000)) + { + // instruction after this should be the start of next packet + next_branch = i + 1; + break; + } + } + + if (next_branch == UINT32_MAX) + { + // We couldn't find the previous packet, so return start + next_branch = start; + } + } return next_branch; } diff --git a/source/Core/DynamicLoader.cpp b/source/Core/DynamicLoader.cpp index 1f545b727a1eb..ffd7425f5231d 100644 --- a/source/Core/DynamicLoader.cpp +++ b/source/Core/DynamicLoader.cpp @@ -186,6 +186,24 @@ DynamicLoader::LoadModuleAtAddress(const FileSpec &file, addr_t link_map_addr, a { UpdateLoadedSections(module_sp, link_map_addr, base_addr); } + else + { + // Try to fetch the load address of the file from the process. It can be different from the + // address reported by the linker in case of a file with fixed load address because the + // linker reports the bias between the load address specified in the file and the actual + // load address it loaded the file. + bool is_loaded; + lldb::addr_t load_addr; + Error error = m_process->GetFileLoadAddress(file, is_loaded, load_addr); + if (error.Fail() || !is_loaded) + load_addr = base_addr; + + if ((module_sp = m_process->ReadModuleFromMemory(file, load_addr))) + { + UpdateLoadedSections(module_sp, link_map_addr, base_addr); + target.GetImages().AppendIfNeeded(module_sp); + } + } return module_sp; } diff --git a/source/Core/Error.cpp b/source/Core/Error.cpp index 5b0bbe273603d..ce055826af2c8 100644 --- a/source/Core/Error.cpp +++ b/source/Core/Error.cpp @@ -21,10 +21,6 @@ #include <cerrno> #include <cstdarg> -#if (defined (__arm__) || defined (__arm64__) || defined (__aarch64__)) && defined (__APPLE__) -#include <SpringBoardServices/SpringBoardServer.h> -#endif - using namespace lldb; using namespace lldb_private; diff --git a/source/Core/FastDemangle.cpp b/source/Core/FastDemangle.cpp index 53e8972e80485..0f12af2783db6 100644 --- a/source/Core/FastDemangle.cpp +++ b/source/Core/FastDemangle.cpp @@ -383,10 +383,19 @@ private: char *end_m_write_ptr = m_write_ptr + content_length; if (end_m_write_ptr > m_buffer_end) { - GrowBuffer(end_m_write_ptr - m_buffer_end); + if (content >= m_buffer && content < m_buffer_end) + { + long offset = content - m_buffer; + GrowBuffer (end_m_write_ptr - m_buffer_end); + content = m_buffer + offset; + } + else + { + GrowBuffer (end_m_write_ptr - m_buffer_end); + } end_m_write_ptr = m_write_ptr + content_length; } - memcpy(m_write_ptr, content, content_length); + memcpy (m_write_ptr, content, content_length); m_write_ptr = end_m_write_ptr; } #define WRITE(x) Write(x, sizeof (x) - 1) diff --git a/source/Core/FileLineResolver.cpp b/source/Core/FileLineResolver.cpp index b1346bbdd812d..e8ef87f009d94 100644 --- a/source/Core/FileLineResolver.cpp +++ b/source/Core/FileLineResolver.cpp @@ -10,7 +10,6 @@ #include "lldb/Core/FileLineResolver.h" // Project includes -#include "lldb/lldb-private-log.h" #include "lldb/Core/Log.h" #include "lldb/Core/StreamString.h" #include "lldb/Symbol/CompileUnit.h" diff --git a/source/Core/FormatEntity.cpp b/source/Core/FormatEntity.cpp index 48b2c2ddbf728..2ebe95747bc02 100644 --- a/source/Core/FormatEntity.cpp +++ b/source/Core/FormatEntity.cpp @@ -13,6 +13,7 @@ #include "lldb/Core/Address.h" #include "lldb/Core/Debugger.h" +#include "lldb/Core/Language.h" #include "lldb/Core/Module.h" #include "lldb/Core/Stream.h" #include "lldb/Core/StreamString.h" @@ -20,6 +21,7 @@ #include "lldb/Core/ValueObjectVariable.h" #include "lldb/DataFormatters/DataVisualization.h" #include "lldb/DataFormatters/FormatManager.h" +#include "lldb/Expression/ClangExpressionVariable.h" #include "lldb/Host/FileSpec.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Symbol/Block.h" @@ -94,7 +96,9 @@ static FormatEntity::Entry::Definition g_function_child_entries[] = ENTRY ("addr-offset" , FunctionAddrOffset , UInt64), ENTRY ("concrete-only-addr-offset-no-padding", FunctionAddrOffsetConcrete, UInt64), ENTRY ("line-offset" , FunctionLineOffset , UInt64), - ENTRY ("pc-offset" , FunctionPCOffset , UInt64) + ENTRY ("pc-offset" , FunctionPCOffset , UInt64), + ENTRY ("initial-function" , FunctionInitial , None), + ENTRY ("changed" , FunctionChanged , None) }; static FormatEntity::Entry::Definition g_line_child_entries[] = @@ -207,6 +211,7 @@ static FormatEntity::Entry::Definition g_top_level_entries[] = ENTRY_CHILDREN ("ansi" , Invalid , None , g_ansi_entries), ENTRY ("current-pc-arrow" , CurrentPCArrow , CString ), ENTRY_CHILDREN ("file" , File , CString , g_file_child_entries), + ENTRY ("language" , Lang , CString), ENTRY_CHILDREN ("frame" , Invalid , None , g_frame_child_entries), ENTRY_CHILDREN ("function" , Invalid , None , g_function_child_entries), ENTRY_CHILDREN ("line" , Invalid , None , g_line_child_entries), @@ -318,6 +323,7 @@ FormatEntity::Entry::TypeToCString (Type t) ENUM_TO_CSTR(ScriptTarget); ENUM_TO_CSTR(ModuleFile); ENUM_TO_CSTR(File); + ENUM_TO_CSTR(Lang); ENUM_TO_CSTR(FrameIndex); ENUM_TO_CSTR(FrameRegisterPC); ENUM_TO_CSTR(FrameRegisterSP); @@ -335,6 +341,8 @@ FormatEntity::Entry::TypeToCString (Type t) ENUM_TO_CSTR(FunctionAddrOffsetConcrete); ENUM_TO_CSTR(FunctionLineOffset); ENUM_TO_CSTR(FunctionPCOffset); + ENUM_TO_CSTR(FunctionInitial); + ENUM_TO_CSTR(FunctionChanged); ENUM_TO_CSTR(LineEntryFile); ENUM_TO_CSTR(LineEntryLineNumber); ENUM_TO_CSTR(LineEntryStartAddress); @@ -444,7 +452,8 @@ DumpAddressOffsetFromFunction (Stream &s, const ExecutionContext *exe_ctx, const Address &format_addr, bool concrete_only, - bool no_padding) + bool no_padding, + bool print_zero_offsets) { if (format_addr.IsValid()) { @@ -468,7 +477,7 @@ DumpAddressOffsetFromFunction (Stream &s, } } else if (sc->symbol && sc->symbol->ValueIsAddress()) - func_addr = sc->symbol->GetAddress(); + func_addr = sc->symbol->GetAddressRef(); } if (func_addr.IsValid()) @@ -479,10 +488,15 @@ DumpAddressOffsetFromFunction (Stream &s, { addr_t func_file_addr = func_addr.GetFileAddress(); addr_t addr_file_addr = format_addr.GetFileAddress(); - if (addr_file_addr > func_file_addr) + if (addr_file_addr > func_file_addr + || (addr_file_addr == func_file_addr && print_zero_offsets)) + { s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding, addr_file_addr - func_file_addr); + } else if (addr_file_addr < func_file_addr) + { s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding, func_file_addr - addr_file_addr); + } return true; } else @@ -492,10 +506,15 @@ DumpAddressOffsetFromFunction (Stream &s, { addr_t func_load_addr = func_addr.GetLoadAddress (target); addr_t addr_load_addr = format_addr.GetLoadAddress (target); - if (addr_load_addr > func_load_addr) + if (addr_load_addr > func_load_addr + || (addr_load_addr == func_load_addr && print_zero_offsets)) + { s.Printf("%s+%s%" PRIu64, addr_offset_padding, addr_offset_padding, addr_load_addr - func_load_addr); + } else if (addr_load_addr < func_load_addr) + { s.Printf("%s-%s%" PRIu64, addr_offset_padding, addr_offset_padding, func_load_addr - addr_load_addr); + } return true; } } @@ -750,7 +769,7 @@ DumpValue (Stream &s, ValueObject::ExpressionPathAftermath what_next = (do_deref_pointer ? ValueObject::eExpressionPathAftermathDereference : ValueObject::eExpressionPathAftermathNothing); ValueObject::GetValueForExpressionPathOptions options; - options.DontCheckDotVsArrowSyntax().DoAllowBitfieldSyntax().DoAllowFragileIVar().DoAllowSyntheticChildren(); + options.DontCheckDotVsArrowSyntax().DoAllowBitfieldSyntax().DoAllowFragileIVar().SetSyntheticChildrenTraversal(ValueObject::GetValueForExpressionPathOptions::SyntheticChildrenTraversal::Both); ValueObject* target = NULL; const char* var_name_final_if_array_range = NULL; size_t close_bracket_index = llvm::StringRef::npos; @@ -935,11 +954,11 @@ DumpValue (Stream &s, return false; if (log) log->Printf("[Debugger::FormatPrompt] handle as array"); + StreamString special_directions_stream; llvm::StringRef special_directions; if (close_bracket_index != llvm::StringRef::npos && subpath.size() > close_bracket_index) { ConstString additional_data (subpath.drop_front(close_bracket_index+1)); - StreamString special_directions_stream; special_directions_stream.Printf("${%svar%s", do_deref_pointer ? "*" : "", additional_data.GetCString()); @@ -1283,13 +1302,11 @@ FormatEntity::Format (const Entry &entry, // Watch for the special "tid" format... if (entry.printf_format == "tid") { - bool handled = false; Target &target = thread->GetProcess()->GetTarget(); ArchSpec arch (target.GetArchitecture ()); llvm::Triple::OSType ostype = arch.IsValid() ? arch.GetTriple().getOS() : llvm::Triple::UnknownOS; if ((ostype == llvm::Triple::FreeBSD) || (ostype == llvm::Triple::Linux)) { - handled = true; format = "%" PRIu64; } } @@ -1501,6 +1518,23 @@ FormatEntity::Format (const Entry &entry, } return false; + case Entry::Type::Lang: + if (sc) + { + CompileUnit *cu = sc->comp_unit; + if (cu) + { + Language lang(cu->GetLanguage()); + const char *lang_name = lang.AsCString(); + if (lang_name) + { + s.PutCString(lang_name); + return true; + } + } + } + return false; + case Entry::Type::FrameIndex: if (exe_ctx) { @@ -1803,7 +1837,7 @@ FormatEntity::Format (const Entry &entry, case Entry::Type::FunctionAddrOffset: if (addr) { - if (DumpAddressOffsetFromFunction (s, sc, exe_ctx, *addr, false, false)) + if (DumpAddressOffsetFromFunction (s, sc, exe_ctx, *addr, false, false, false)) return true; } return false; @@ -1811,13 +1845,13 @@ FormatEntity::Format (const Entry &entry, case Entry::Type::FunctionAddrOffsetConcrete: if (addr) { - if (DumpAddressOffsetFromFunction (s, sc, exe_ctx, *addr, true, true)) + if (DumpAddressOffsetFromFunction (s, sc, exe_ctx, *addr, true, true, true)) return true; } return false; case Entry::Type::FunctionLineOffset: - if (DumpAddressOffsetFromFunction (s, sc, exe_ctx, sc->line_entry.range.GetBaseAddress(), false, false)) + if (DumpAddressOffsetFromFunction (s, sc, exe_ctx, sc->line_entry.range.GetBaseAddress(), false, false, false)) return true; return false; @@ -1827,12 +1861,18 @@ FormatEntity::Format (const Entry &entry, StackFrame *frame = exe_ctx->GetFramePtr(); if (frame) { - if (DumpAddressOffsetFromFunction (s, sc, exe_ctx, frame->GetFrameCodeAddress(), false, false)) + if (DumpAddressOffsetFromFunction (s, sc, exe_ctx, frame->GetFrameCodeAddress(), false, false, false)) return true; } } return false; + case Entry::Type::FunctionChanged: + return function_changed == true; + + case Entry::Type::FunctionInitial: + return initial_function == true; + case Entry::Type::LineEntryFile: if (sc && sc->line_entry.IsValid()) { diff --git a/source/Core/IOHandler.cpp b/source/Core/IOHandler.cpp index 747fd4411d4d1..0246778b06545 100644 --- a/source/Core/IOHandler.cpp +++ b/source/Core/IOHandler.cpp @@ -8,8 +8,6 @@ //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - #include <string> #include "lldb/Breakpoint/BreakpointLocation.h" @@ -169,6 +167,17 @@ IOHandler::WaitForPop () m_popped.WaitForValueEqualTo(true); } +void +IOHandlerStack::PrintAsync (Stream *stream, const char *s, size_t len) +{ + if (stream) + { + Mutex::Locker locker (m_mutex); + if (m_top) + m_top->PrintAsync (stream, s, len); + } +} + IOHandlerConfirm::IOHandlerConfirm (Debugger &debugger, const char *prompt, bool default_response) : @@ -380,7 +389,8 @@ IOHandlerEditline::IOHandlerEditline (Debugger &debugger, m_curr_line_idx (UINT32_MAX), m_multi_line (multi_line), m_color_prompts (color_prompts), - m_interrupt_exits (true) + m_interrupt_exits (true), + m_editing (false) { SetPrompt(prompt); @@ -474,6 +484,7 @@ IOHandlerEditline::GetLine (std::string &line, bool &interrupted) char buffer[256]; bool done = false; bool got_line = false; + m_editing = true; while (!done) { if (fgets(buffer, sizeof(buffer), in) == NULL) @@ -508,6 +519,7 @@ IOHandlerEditline::GetLine (std::string &line, bool &interrupted) line.append(buffer, buffer_len); } } + m_editing = false; // We might have gotten a newline on a line by itself // make sure to return true in this case. return got_line; @@ -737,47 +749,11 @@ IOHandlerEditline::Run () } void -IOHandlerEditline::Hide () -{ -#ifndef LLDB_DISABLE_LIBEDIT - if (m_editline_ap) - m_editline_ap->Hide(); -#endif -} - - -void -IOHandlerEditline::Refresh () -{ -#ifndef LLDB_DISABLE_LIBEDIT - if (m_editline_ap) - { - m_editline_ap->Refresh(); - } - else - { -#endif - const char *prompt = GetPrompt(); - if (prompt && prompt[0]) - { - FILE *out = GetOutputFILE(); - if (out) - { - ::fprintf(out, "%s", prompt); - ::fflush(out); - } - } -#ifndef LLDB_DISABLE_LIBEDIT - } -#endif -} - -void IOHandlerEditline::Cancel () { #ifndef LLDB_DISABLE_LIBEDIT if (m_editline_ap) - m_editline_ap->Interrupt (); + m_editline_ap->Cancel (); #endif } @@ -804,6 +780,17 @@ IOHandlerEditline::GotEOF() #endif } +void +IOHandlerEditline::PrintAsync (Stream *stream, const char *s, size_t len) +{ +#ifndef LLDB_DISABLE_LIBEDIT + if (m_editline_ap) + m_editline_ap->PrintAsync(stream, s, len); + else +#endif + IOHandler::PrintAsync(stream, s, len); +} + // we may want curses to be disabled for some builds // for instance, windows #ifndef LLDB_DISABLE_CURSES @@ -1080,7 +1067,7 @@ type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect ~WindowDelegate() { } - + virtual bool WindowDelegateDraw (Window &window, bool force) { @@ -1112,14 +1099,13 @@ type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect public: HelpDialogDelegate (const char *text, KeyHelp *key_help_array); - virtual - ~HelpDialogDelegate(); + ~HelpDialogDelegate() override; - virtual bool - WindowDelegateDraw (Window &window, bool force); + bool + WindowDelegateDraw (Window &window, bool force) override; - virtual HandleCharResult - WindowDelegateHandleChar (Window &window, int key); + HandleCharResult + WindowDelegateHandleChar (Window &window, int key) override; size_t GetNumLines() const @@ -1787,8 +1773,7 @@ type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect int key_value, uint64_t identifier); - virtual ~ - Menu () + ~Menu () override { } @@ -1816,11 +1801,11 @@ type summary add -s "${var.origin%S} ${var.size%S}" curses::Rect void DrawMenuTitle (Window &window, bool highlight); - virtual bool - WindowDelegateDraw (Window &window, bool force); + bool + WindowDelegateDraw (Window &window, bool force) override; - virtual HandleCharResult - WindowDelegateHandleChar (Window &window, int key); + HandleCharResult + WindowDelegateHandleChar (Window &window, int key) override; MenuActionResult ActionPrivate (Menu &menu) @@ -2924,8 +2909,6 @@ public: return this; if (m_children.empty()) return NULL; - if (static_cast<uint32_t>(m_children.back().m_row_idx) < row_idx) - return NULL; if (IsExpanded()) { for (auto &item : m_children) @@ -3005,8 +2988,8 @@ public: return m_max_y - m_min_y; } - virtual bool - WindowDelegateDraw (Window &window, bool force) + bool + WindowDelegateDraw (Window &window, bool force) override { ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext()); Process *process = exe_ctx.GetProcessPtr(); @@ -3071,14 +3054,14 @@ public: } - virtual const char * - WindowDelegateGetHelpText () + const char * + WindowDelegateGetHelpText () override { return "Thread window keyboard shortcuts:"; } - virtual KeyHelp * - WindowDelegateGetKeyHelp () + KeyHelp * + WindowDelegateGetKeyHelp () override { static curses::KeyHelp g_source_view_key_help[] = { { KEY_UP, "Select previous item" }, @@ -3096,8 +3079,8 @@ public: return g_source_view_key_help; } - virtual HandleCharResult - WindowDelegateHandleChar (Window &window, int c) + HandleCharResult + WindowDelegateHandleChar (Window &window, int c) override { switch(c) { @@ -3221,12 +3204,12 @@ public: m_format); } - virtual ~FrameTreeDelegate() + ~FrameTreeDelegate() override { } - virtual void - TreeDelegateDrawTreeItem (TreeItem &item, Window &window) + void + TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override { Thread* thread = (Thread*)item.GetUserData(); if (thread) @@ -3246,14 +3229,14 @@ public: } } } - virtual void - TreeDelegateGenerateChildren (TreeItem &item) + void + TreeDelegateGenerateChildren (TreeItem &item) override { // No children for frames yet... } - virtual bool - TreeDelegateItemSelected (TreeItem &item) + bool + TreeDelegateItemSelected (TreeItem &item) override { Thread* thread = (Thread*)item.GetUserData(); if (thread) @@ -3282,8 +3265,7 @@ public: m_format); } - virtual - ~ThreadTreeDelegate() + ~ThreadTreeDelegate() override { } @@ -3302,8 +3284,8 @@ public: return ThreadSP(); } - virtual void - TreeDelegateDrawTreeItem (TreeItem &item, Window &window) + void + TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override { ThreadSP thread_sp = GetThread (item); if (thread_sp) @@ -3317,8 +3299,8 @@ public: } } } - virtual void - TreeDelegateGenerateChildren (TreeItem &item) + void + TreeDelegateGenerateChildren (TreeItem &item) override { ProcessSP process_sp = GetProcess (); if (process_sp && process_sp->IsAlive()) @@ -3355,8 +3337,8 @@ public: item.ClearChildren(); } - virtual bool - TreeDelegateItemSelected (TreeItem &item) + bool + TreeDelegateItemSelected (TreeItem &item) override { ProcessSP process_sp = GetProcess (); if (process_sp && process_sp->IsAlive()) @@ -3403,8 +3385,7 @@ public: m_format); } - virtual - ~ThreadsTreeDelegate() + ~ThreadsTreeDelegate() override { } @@ -3414,8 +3395,8 @@ public: return m_debugger.GetCommandInterpreter().GetExecutionContext().GetProcessSP(); } - virtual void - TreeDelegateDrawTreeItem (TreeItem &item, Window &window) + void + TreeDelegateDrawTreeItem (TreeItem &item, Window &window) override { ProcessSP process_sp = GetProcess (); if (process_sp && process_sp->IsAlive()) @@ -3430,8 +3411,8 @@ public: } } - virtual void - TreeDelegateGenerateChildren (TreeItem &item) + void + TreeDelegateGenerateChildren (TreeItem &item) override { ProcessSP process_sp = GetProcess (); if (process_sp && process_sp->IsAlive()) @@ -3468,8 +3449,8 @@ public: item.ClearChildren(); } - virtual bool - TreeDelegateItemSelected (TreeItem &item) + bool + TreeDelegateItemSelected (TreeItem &item) override { return false; } @@ -3510,8 +3491,7 @@ public: SetValues (valobj_list); } - virtual - ~ValueObjectListDelegate() + ~ValueObjectListDelegate() override { } @@ -3529,8 +3509,8 @@ public: m_rows.push_back(Row(m_valobj_list.GetValueObjectAtIndex(i), NULL)); } - virtual bool - WindowDelegateDraw (Window &window, bool force) + bool + WindowDelegateDraw (Window &window, bool force) override { m_num_rows = 0; m_min_x = 2; @@ -3572,8 +3552,8 @@ public: return true; // Drawing handled } - virtual KeyHelp * - WindowDelegateGetKeyHelp () + KeyHelp * + WindowDelegateGetKeyHelp () override { static curses::KeyHelp g_source_view_key_help[] = { { KEY_UP, "Select previous item" }, @@ -3607,8 +3587,8 @@ public: } - virtual HandleCharResult - WindowDelegateHandleChar (Window &window, int c) + HandleCharResult + WindowDelegateHandleChar (Window &window, int c) override { switch(c) { @@ -3911,19 +3891,18 @@ public: { } - virtual - ~FrameVariablesWindowDelegate() + ~FrameVariablesWindowDelegate() override { } - virtual const char * - WindowDelegateGetHelpText () + const char * + WindowDelegateGetHelpText () override { return "Frame variable window keyboard shortcuts:"; } - virtual bool - WindowDelegateDraw (Window &window, bool force) + bool + WindowDelegateDraw (Window &window, bool force) override { ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext()); Process *process = exe_ctx.GetProcessPtr(); @@ -3944,6 +3923,7 @@ public: return true; // Don't do any updating when we are running } } + ValueObjectList local_values; if (frame_block) @@ -3959,7 +3939,18 @@ public: const DynamicValueType use_dynamic = eDynamicDontRunTarget; const size_t num_locals = locals->GetSize(); for (size_t i=0; i<num_locals; ++i) - local_values.Append(frame->GetValueObjectForFrameVariable (locals->GetVariableAtIndex(i), use_dynamic)); + { + ValueObjectSP value_sp = frame->GetValueObjectForFrameVariable (locals->GetVariableAtIndex(i), use_dynamic); + if (value_sp) + { + ValueObjectSP synthetic_value_sp = value_sp->GetSyntheticValue(); + if (synthetic_value_sp) + local_values.Append(synthetic_value_sp); + else + local_values.Append(value_sp); + + } + } // Update the values SetValues(local_values); } @@ -3990,20 +3981,19 @@ public: m_debugger (debugger) { } - - virtual + ~RegistersWindowDelegate() { } - virtual const char * - WindowDelegateGetHelpText () + const char * + WindowDelegateGetHelpText () override { return "Register window keyboard shortcuts:"; } - virtual bool - WindowDelegateDraw (Window &window, bool force) + bool + WindowDelegateDraw (Window &window, bool force) override { ExecutionContext exe_ctx (m_debugger.GetCommandInterpreter().GetExecutionContext()); StackFrame *frame = exe_ctx.GetFramePtr(); @@ -4309,18 +4299,18 @@ public: { } - virtual ~ApplicationDelegate () { } - virtual bool - WindowDelegateDraw (Window &window, bool force) + + bool + WindowDelegateDraw (Window &window, bool force) override { return false; // Drawing not handled, let standard window drawing happen } - virtual HandleCharResult - WindowDelegateHandleChar (Window &window, int key) + HandleCharResult + WindowDelegateHandleChar (Window &window, int key) override { switch (key) { @@ -4342,8 +4332,8 @@ public: } - virtual const char * - WindowDelegateGetHelpText () + const char * + WindowDelegateGetHelpText () override { return "Welcome to the LLDB curses GUI.\n\n" "Press the TAB key to change the selected view.\n" @@ -4351,8 +4341,8 @@ public: "Common key bindings for all views:"; } - virtual KeyHelp * - WindowDelegateGetKeyHelp () + KeyHelp * + WindowDelegateGetKeyHelp () override { static curses::KeyHelp g_source_view_key_help[] = { { '\t', "Select next view" }, @@ -4370,8 +4360,8 @@ public: return g_source_view_key_help; } - virtual MenuActionResult - MenuDelegateAction (Menu &menu) + MenuActionResult + MenuDelegateAction (Menu &menu) override { switch (menu.GetIdentifier()) { @@ -4430,7 +4420,7 @@ public: { Process *process = exe_ctx.GetProcessPtr(); if (process && process->IsAlive()) - process->Destroy(); + process->Destroy(false); } } return MenuActionResult::Handled; @@ -4647,12 +4637,12 @@ public: m_format); } - virtual ~StatusBarWindowDelegate () { } - virtual bool - WindowDelegateDraw (Window &window, bool force) + + bool + WindowDelegateDraw (Window &window, bool force) override { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); Process *process = exe_ctx.GetProcessPtr(); @@ -4723,8 +4713,7 @@ public: { } - virtual - ~SourceFileWindowDelegate() + ~SourceFileWindowDelegate() override { } @@ -4740,14 +4729,14 @@ public: return m_max_y - m_min_y; } - virtual const char * - WindowDelegateGetHelpText () + const char * + WindowDelegateGetHelpText () override { return "Source/Disassembly window keyboard shortcuts:"; } - virtual KeyHelp * - WindowDelegateGetKeyHelp () + KeyHelp * + WindowDelegateGetKeyHelp () override { static curses::KeyHelp g_source_view_key_help[] = { { KEY_RETURN, "Run to selected line with one shot breakpoint" }, @@ -4773,8 +4762,8 @@ public: return g_source_view_key_help; } - virtual bool - WindowDelegateDraw (Window &window, bool force) + bool + WindowDelegateDraw (Window &window, bool force) override { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); Process *process = exe_ctx.GetProcessPtr(); @@ -5259,8 +5248,8 @@ public: return 0; } - virtual HandleCharResult - WindowDelegateHandleChar (Window &window, int c) + HandleCharResult + WindowDelegateHandleChar (Window &window, int c) override { const uint32_t num_visible_lines = NumVisibleLines(); const size_t num_lines = GetNumLines (); @@ -5324,7 +5313,8 @@ public: eLazyBoolCalculate, // Check inlines using global setting eLazyBoolCalculate, // Skip prologue using global setting, false, // internal - false); // request_hardware + false, // request_hardware + eLazyBoolCalculate); // move_to_nearest_code // Make breakpoint one shot bp_sp->GetOptions()->SetOneShot(true); exe_ctx.GetProcessRef().Resume(); @@ -5359,7 +5349,8 @@ public: eLazyBoolCalculate, // Check inlines using global setting eLazyBoolCalculate, // Skip prologue using global setting, false, // internal - false); // request_hardware + false, // request_hardware + eLazyBoolCalculate); // move_to_nearest_code } } else if (m_selected_line < GetNumDisassemblyLines()) @@ -5390,7 +5381,7 @@ public: { ExecutionContext exe_ctx = m_debugger.GetCommandInterpreter().GetExecutionContext(); if (exe_ctx.HasProcessScope()) - exe_ctx.GetProcessRef().Destroy(); + exe_ctx.GetProcessRef().Destroy(false); } return eKeyHandled; @@ -5611,17 +5602,6 @@ IOHandlerCursesGUI::~IOHandlerCursesGUI () } void -IOHandlerCursesGUI::Hide () -{ -} - - -void -IOHandlerCursesGUI::Refresh () -{ -} - -void IOHandlerCursesGUI::Cancel () { } diff --git a/source/Core/Language.cpp b/source/Core/Language.cpp index 1ffd5aadc526c..1cc4b8a0dc9cf 100644 --- a/source/Core/Language.cpp +++ b/source/Core/Language.cpp @@ -46,7 +46,25 @@ g_languages[] = { { "objc++" , NULL , "Objective-C++" } }, { { "upc" , NULL , "Unified Parallel C" } }, { { "d" , NULL , "D" } }, - { { "python" , NULL , "Python" } } + { { "python" , NULL , "Python" } }, + { { "opencl" , "OpenCL" , "OpenCL" } }, + { { "go" , "Go" , "Go" } }, + { { "modula3" , "Modula3" , "Modula 3" } }, + { { "haskell" , "Haskell" , "Haskell" } }, + { { "c++03" , "C_plus_plus_03" , "ISO C++:2003" } }, + { { "c++11" , "C_plus_plus_11" , "ISO C++:2011" } }, + { { "ocaml" , "OCaml" , "OCaml" } }, + { { "rust" , "Rust" , "Rust" } }, + { { "c11" , "C11" , "ISO C:2011" } }, + { { "swift" , "Swift" , "Swift" } }, + { { "julia" , "Julia" , "Julia" } }, + { { "dylan" , "Dylan" , "Dylan" } }, + { { "c++14" , "C_plus_plus_14" , "ISO C++:2014" } }, + { { "f03" , "Fortran03" , "ISO Fortran 2003" } }, + { { "f08" , "Fortran08" , "ISO Fortran 2008" } }, + // Vendor Extensions + { { "mipsassem" , "Mips_Assembler" , "Mips Assembler" } }, + { { "renderscript" , "RenderScript" , "RenderScript" } } }; static const size_t g_num_languages = llvm::array_lengthof(g_languages); diff --git a/source/Core/Listener.cpp b/source/Core/Listener.cpp index f71d766e0a1c5..34475498d48ef 100644 --- a/source/Core/Listener.cpp +++ b/source/Core/Listener.cpp @@ -18,7 +18,6 @@ #include "lldb/Core/StreamString.h" #include "lldb/Core/Event.h" #include "lldb/Host/TimeValue.h" -#include "lldb/lldb-private-log.h" #include <algorithm> using namespace lldb; diff --git a/source/Core/Log.cpp b/source/Core/Log.cpp index fe4cfb366c887..6acd50eec76a2 100644 --- a/source/Core/Log.cpp +++ b/source/Core/Log.cpp @@ -27,8 +27,11 @@ #include "lldb/Host/ThisThread.h" #include "lldb/Host/TimeValue.h" #include "lldb/Interpreter/Args.h" +#include "lldb/Utility/NameMatches.h" #include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Signals.h" using namespace lldb; using namespace lldb_private; @@ -74,6 +77,23 @@ Log::GetMask() const return m_mask_bits; } +void +Log::PutCString(const char *cstr) +{ + Printf("%s", cstr); +} + +//---------------------------------------------------------------------- +// Simple variable argument logging with flags. +//---------------------------------------------------------------------- +void +Log::Printf(const char *format, ...) +{ + va_list args; + va_start(args, format); + VAPrintf(format, args); + va_end(args); +} //---------------------------------------------------------------------- // All logging eventually boils down to this function call. If we have @@ -81,7 +101,7 @@ Log::GetMask() const // a valid file handle, we also log to the file. //---------------------------------------------------------------------- void -Log::PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args) +Log::VAPrintf(const char *format, va_list args) { // Make a copy of our stream shared pointer in case someone disables our // log while we are logging and releases the stream @@ -90,10 +110,6 @@ Log::PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args) { static uint32_t g_sequence_id = 0; StreamString header; - // Enabling the thread safe logging actually deadlocks right now. - // Need to fix this at some point. -// static Mutex g_LogThreadedMutex(Mutex::eMutexTypeRecursive); -// Mutex::Locker locker (g_LogThreadedMutex); // Add a sequence ID if requested if (m_options.Test (LLDB_LOG_OPTION_PREPEND_SEQUENCE)) @@ -103,7 +119,7 @@ Log::PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args) if (m_options.Test (LLDB_LOG_OPTION_PREPEND_TIMESTAMP)) { TimeValue now = TimeValue::Now(); - header.Printf ("%9d.%6.6d ", now.seconds(), now.nanoseconds()); + header.Printf ("%9d.%09.9d ", now.seconds(), now.nanoseconds()); } // Add the process and thread if requested @@ -120,51 +136,29 @@ Log::PrintfWithFlagsVarArg (uint32_t flags, const char *format, va_list args) } header.PrintfVarArg (format, args); - stream_sp->Printf("%s\n", header.GetData()); - - if (m_options.Test (LLDB_LOG_OPTION_BACKTRACE)) - Host::Backtrace (*stream_sp, 1024); - stream_sp->Flush(); - } -} - - -void -Log::PutCString (const char *cstr) -{ - Printf ("%s", cstr); -} - - -//---------------------------------------------------------------------- -// Simple variable argument logging with flags. -//---------------------------------------------------------------------- -void -Log::Printf(const char *format, ...) -{ - va_list args; - va_start (args, format); - PrintfWithFlagsVarArg (0, format, args); - va_end (args); -} - -void -Log::VAPrintf (const char *format, va_list args) -{ - PrintfWithFlagsVarArg (0, format, args); -} + header.PutCString("\n"); + if (m_options.Test(LLDB_LOG_OPTION_BACKTRACE)) + { + std::string back_trace; + llvm::raw_string_ostream stream(back_trace); + llvm::sys::PrintStackTrace(stream); + header.PutCString(back_trace.c_str()); + } -//---------------------------------------------------------------------- -// Simple variable argument logging with flags. -//---------------------------------------------------------------------- -void -Log::PrintfWithFlags (uint32_t flags, const char *format, ...) -{ - va_list args; - va_start (args, format); - PrintfWithFlagsVarArg (flags, format, args); - va_end (args); + if (m_options.Test(LLDB_LOG_OPTION_THREADSAFE)) + { + static Mutex g_LogThreadedMutex(Mutex::eMutexTypeRecursive); + Mutex::Locker locker(g_LogThreadedMutex); + stream_sp->PutCString(header.GetString().c_str()); + stream_sp->Flush(); + } + else + { + stream_sp->PutCString(header.GetString().c_str()); + stream_sp->Flush(); + } + } } //---------------------------------------------------------------------- @@ -172,15 +166,15 @@ Log::PrintfWithFlags (uint32_t flags, const char *format, ...) // a non-zero value. //---------------------------------------------------------------------- void -Log::Debug (const char *format, ...) +Log::Debug(const char *format, ...) { - if (GetOptions().Test(LLDB_LOG_OPTION_DEBUG)) - { - va_list args; - va_start (args, format); - PrintfWithFlagsVarArg (LLDB_LOG_FLAG_DEBUG, format, args); - va_end (args); - } + if (!GetOptions().Test(LLDB_LOG_OPTION_DEBUG)) + return; + + va_list args; + va_start(args, format); + VAPrintf(format, args); + va_end(args); } @@ -189,15 +183,15 @@ Log::Debug (const char *format, ...) // a non-zero value. //---------------------------------------------------------------------- void -Log::DebugVerbose (const char *format, ...) +Log::DebugVerbose(const char *format, ...) { - if (GetOptions().AllSet (LLDB_LOG_OPTION_DEBUG | LLDB_LOG_OPTION_VERBOSE)) - { - va_list args; - va_start (args, format); - PrintfWithFlagsVarArg (LLDB_LOG_FLAG_DEBUG | LLDB_LOG_FLAG_VERBOSE, format, args); - va_end (args); - } + if (!GetOptions().AllSet(LLDB_LOG_OPTION_DEBUG | LLDB_LOG_OPTION_VERBOSE)) + return; + + va_list args; + va_start(args, format); + VAPrintf(format, args); + va_end(args); } @@ -205,55 +199,63 @@ Log::DebugVerbose (const char *format, ...) // Log only if all of the bits are set //---------------------------------------------------------------------- void -Log::LogIf (uint32_t bits, const char *format, ...) +Log::LogIf(uint32_t bits, const char *format, ...) { - if (m_options.AllSet (bits)) - { - va_list args; - va_start (args, format); - PrintfWithFlagsVarArg (0, format, args); - va_end (args); - } + if (!m_options.AllSet(bits)) + return; + + va_list args; + va_start(args, format); + VAPrintf(format, args); + va_end(args); } //---------------------------------------------------------------------- // Printing of errors that are not fatal. //---------------------------------------------------------------------- void -Log::Error (const char *format, ...) +Log::Error(const char *format, ...) { - char *arg_msg = NULL; va_list args; - va_start (args, format); - ::vasprintf (&arg_msg, format, args); - va_end (args); + va_start(args, format); + VAError(format, args); + va_end(args); +} - if (arg_msg != NULL) - { - PrintfWithFlags (LLDB_LOG_FLAG_ERROR, "error: %s", arg_msg); - free (arg_msg); - } + +void +Log::VAError(const char *format, va_list args) +{ + char *arg_msg = nullptr; + ::vasprintf(&arg_msg, format, args); + + if (arg_msg == nullptr) + return; + + Printf("error: %s", arg_msg); + free(arg_msg); } + //---------------------------------------------------------------------- // Printing of errors that ARE fatal. Exit with ERR exit code // immediately. //---------------------------------------------------------------------- void -Log::FatalError (int err, const char *format, ...) +Log::FatalError(int err, const char *format, ...) { - char *arg_msg = NULL; + char *arg_msg = nullptr; va_list args; - va_start (args, format); - ::vasprintf (&arg_msg, format, args); - va_end (args); + va_start(args, format); + ::vasprintf(&arg_msg, format, args); + va_end(args); - if (arg_msg != NULL) + if (arg_msg != nullptr) { - PrintfWithFlags (LLDB_LOG_FLAG_ERROR | LLDB_LOG_FLAG_FATAL, "error: %s", arg_msg); - ::free (arg_msg); + Printf("error: %s", arg_msg); + ::free(arg_msg); } - ::exit (err); + ::exit(err); } @@ -262,15 +264,15 @@ Log::FatalError (int err, const char *format, ...) // enabled. //---------------------------------------------------------------------- void -Log::Verbose (const char *format, ...) +Log::Verbose(const char *format, ...) { - if (m_options.Test(LLDB_LOG_OPTION_VERBOSE)) - { - va_list args; - va_start (args, format); - PrintfWithFlagsVarArg (LLDB_LOG_FLAG_VERBOSE, format, args); - va_end (args); - } + if (!m_options.Test(LLDB_LOG_OPTION_VERBOSE)) + return; + + va_list args; + va_start(args, format); + VAPrintf(format, args); + va_end(args); } //---------------------------------------------------------------------- @@ -278,40 +280,40 @@ Log::Verbose (const char *format, ...) // enabled. //---------------------------------------------------------------------- void -Log::WarningVerbose (const char *format, ...) +Log::WarningVerbose(const char *format, ...) { - if (m_options.Test(LLDB_LOG_OPTION_VERBOSE)) - { - char *arg_msg = NULL; - va_list args; - va_start (args, format); - ::vasprintf (&arg_msg, format, args); - va_end (args); + if (!m_options.Test(LLDB_LOG_OPTION_VERBOSE)) + return; - if (arg_msg != NULL) - { - PrintfWithFlags (LLDB_LOG_FLAG_WARNING | LLDB_LOG_FLAG_VERBOSE, "warning: %s", arg_msg); - free (arg_msg); - } - } + char *arg_msg = nullptr; + va_list args; + va_start(args, format); + ::vasprintf(&arg_msg, format, args); + va_end(args); + + if (arg_msg == nullptr) + return; + + Printf("warning: %s", arg_msg); + free(arg_msg); } //---------------------------------------------------------------------- // Printing of warnings that are not fatal. //---------------------------------------------------------------------- void -Log::Warning (const char *format, ...) +Log::Warning(const char *format, ...) { - char *arg_msg = NULL; + char *arg_msg = nullptr; va_list args; - va_start (args, format); - ::vasprintf (&arg_msg, format, args); - va_end (args); + va_start(args, format); + ::vasprintf(&arg_msg, format, args); + va_end(args); - if (arg_msg != NULL) - { - PrintfWithFlags (LLDB_LOG_FLAG_WARNING, "warning: %s", arg_msg); - free (arg_msg); - } + if (arg_msg == nullptr) + return; + + Printf("warning: %s", arg_msg); + free(arg_msg); } typedef std::map <ConstString, Log::Callbacks> CallbackMap; @@ -363,6 +365,40 @@ Log::GetLogChannelCallbacks (const ConstString &channel, Log::Callbacks &log_cal return false; } +bool +Log::EnableLogChannel(lldb::StreamSP &log_stream_sp, + uint32_t log_options, + const char *channel, + const char **categories, + Stream &error_stream) +{ + Log::Callbacks log_callbacks; + if (Log::GetLogChannelCallbacks (ConstString(channel), log_callbacks)) + { + log_callbacks.enable (log_stream_sp, log_options, categories, &error_stream); + return true; + } + + LogChannelSP log_channel_sp (LogChannel::FindPlugin (channel)); + if (log_channel_sp) + { + if (log_channel_sp->Enable (log_stream_sp, log_options, &error_stream, categories)) + { + return true; + } + else + { + error_stream.Printf ("Invalid log channel '%s'.\n", channel); + return false; + } + } + else + { + error_stream.Printf ("Invalid log channel '%s'.\n", channel); + return false; + } +} + void Log::EnableAllLogChannels ( diff --git a/source/Core/Logging.cpp b/source/Core/Logging.cpp new file mode 100644 index 0000000000000..412536adbb34e --- /dev/null +++ b/source/Core/Logging.cpp @@ -0,0 +1,277 @@ +//===-- lldb-log.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/Logging.h" + +// C Includes +// C++ Includes +#include <atomic> +// Other libraries and framework includes +// Project includes +#include "lldb/Interpreter/Args.h" +#include "lldb/Core/Log.h" +#include "lldb/Core/StreamFile.h" +#include <string.h> + +using namespace lldb; +using namespace lldb_private; + + +// We want to avoid global constructors where code needs to be run so here we +// control access to our static g_log_sp by hiding it in a singleton function +// that will construct the static g_lob_sp the first time this function is +// called. + +static std::atomic<bool> g_log_enabled {false}; +static Log * g_log = NULL; +static Log * +GetLog () +{ + if (!g_log_enabled) + return NULL; + return g_log; +} + +uint32_t +lldb_private::GetLogMask () +{ + Log *log(GetLog ()); + if (log) + return log->GetMask().Get(); + return 0; +} + +bool +lldb_private::IsLogVerbose () +{ + uint32_t mask = GetLogMask(); + return (mask & LIBLLDB_LOG_VERBOSE); +} + +Log * +lldb_private::GetLogIfAllCategoriesSet (uint32_t mask) +{ + Log *log(GetLog ()); + if (log && mask) + { + uint32_t log_mask = log->GetMask().Get(); + if ((log_mask & mask) != mask) + return NULL; + } + return log; +} + +void +lldb_private::LogIfAllCategoriesSet (uint32_t mask, const char *format, ...) +{ + Log *log(GetLogIfAllCategoriesSet (mask)); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} + +void +lldb_private::LogIfAnyCategoriesSet (uint32_t mask, const char *format, ...) +{ + Log *log(GetLogIfAnyCategoriesSet (mask)); + if (log) + { + va_list args; + va_start (args, format); + log->VAPrintf (format, args); + va_end (args); + } +} + +Log * +lldb_private::GetLogIfAnyCategoriesSet (uint32_t mask) +{ + Log *log(GetLog ()); + if (log && mask && (mask & log->GetMask().Get())) + return log; + return NULL; +} + +void +lldb_private::DisableLog (const char **categories, Stream *feedback_strm) +{ + Log *log(GetLog ()); + + if (log) + { + uint32_t flag_bits = 0; + if (categories[0] != NULL) + { + flag_bits = log->GetMask().Get(); + for (size_t i = 0; categories[i] != NULL; ++i) + { + const char *arg = categories[i]; + + if (0 == ::strcasecmp(arg, "all")) flag_bits &= ~LIBLLDB_LOG_ALL; + else if (0 == ::strcasecmp(arg, "api")) flag_bits &= ~LIBLLDB_LOG_API; + else if (0 == ::strncasecmp(arg, "break", 5)) flag_bits &= ~LIBLLDB_LOG_BREAKPOINTS; + else if (0 == ::strcasecmp(arg, "commands")) flag_bits &= ~LIBLLDB_LOG_COMMANDS; + else if (0 == ::strcasecmp(arg, "default")) flag_bits &= ~LIBLLDB_LOG_DEFAULT; + else if (0 == ::strcasecmp(arg, "dyld")) flag_bits &= ~LIBLLDB_LOG_DYNAMIC_LOADER; + else if (0 == ::strncasecmp(arg, "event", 5)) flag_bits &= ~LIBLLDB_LOG_EVENTS; + else if (0 == ::strncasecmp(arg, "expr", 4)) flag_bits &= ~LIBLLDB_LOG_EXPRESSIONS; + else if (0 == ::strncasecmp(arg, "object", 6)) flag_bits &= ~LIBLLDB_LOG_OBJECT; + else if (0 == ::strcasecmp(arg, "process")) flag_bits &= ~LIBLLDB_LOG_PROCESS; + else if (0 == ::strcasecmp(arg, "platform")) flag_bits &= ~LIBLLDB_LOG_PLATFORM; + else if (0 == ::strcasecmp(arg, "script")) flag_bits &= ~LIBLLDB_LOG_SCRIPT; + else if (0 == ::strcasecmp(arg, "state")) flag_bits &= ~LIBLLDB_LOG_STATE; + else if (0 == ::strcasecmp(arg, "step")) flag_bits &= ~LIBLLDB_LOG_STEP; + else if (0 == ::strcasecmp(arg, "thread")) flag_bits &= ~LIBLLDB_LOG_THREAD; + else if (0 == ::strcasecmp(arg, "target")) flag_bits &= ~LIBLLDB_LOG_TARGET; + else if (0 == ::strcasecmp(arg, "verbose")) flag_bits &= ~LIBLLDB_LOG_VERBOSE; + else if (0 == ::strncasecmp(arg, "watch", 5)) flag_bits &= ~LIBLLDB_LOG_WATCHPOINTS; + else if (0 == ::strncasecmp(arg, "temp", 4)) flag_bits &= ~LIBLLDB_LOG_TEMPORARY; + else if (0 == ::strncasecmp(arg, "comm", 4)) flag_bits &= ~LIBLLDB_LOG_COMMUNICATION; + else if (0 == ::strncasecmp(arg, "conn", 4)) flag_bits &= ~LIBLLDB_LOG_CONNECTION; + else if (0 == ::strncasecmp(arg, "host", 4)) flag_bits &= ~LIBLLDB_LOG_HOST; + else if (0 == ::strncasecmp(arg, "unwind", 6)) flag_bits &= ~LIBLLDB_LOG_UNWIND; + else if (0 == ::strncasecmp(arg, "types", 5)) flag_bits &= ~LIBLLDB_LOG_TYPES; + else if (0 == ::strncasecmp(arg, "symbol", 6)) flag_bits &= ~LIBLLDB_LOG_SYMBOLS; + else if (0 == ::strcasecmp(arg, "system-runtime")) flag_bits &= ~LIBLLDB_LOG_SYSTEM_RUNTIME; + else if (0 == ::strncasecmp(arg, "module", 6)) flag_bits &= ~LIBLLDB_LOG_MODULES; + else if (0 == ::strncasecmp(arg, "mmap", 4)) flag_bits &= ~LIBLLDB_LOG_MMAP; + else if (0 == ::strcasecmp(arg, "os")) flag_bits &= ~LIBLLDB_LOG_OS; + else if (0 == ::strcasecmp(arg, "jit")) flag_bits &= ~LIBLLDB_LOG_JIT_LOADER; + else if (0 == ::strcasecmp(arg, "language")) flag_bits &= ~LIBLLDB_LOG_LANGUAGE; + else + { + feedback_strm->Printf ("error: unrecognized log category '%s'\n", arg); + ListLogCategories (feedback_strm); + return; + } + } + } + log->GetMask().Reset (flag_bits); + if (flag_bits == 0) + { + log->SetStream(lldb::StreamSP()); + g_log_enabled = false; + } + } + + return; +} + +Log * +lldb_private::EnableLog (StreamSP &log_stream_sp, uint32_t log_options, const char **categories, Stream *feedback_strm) +{ + // Try see if there already is a log - that way we can reuse its settings. + // We could reuse the log in toto, but we don't know that the stream is the same. + uint32_t flag_bits; + if (g_log) + flag_bits = g_log->GetMask().Get(); + else + flag_bits = 0; + + // Now make a new log with this stream if one was provided + if (log_stream_sp) + { + if (g_log) + g_log->SetStream(log_stream_sp); + else + g_log = new Log(log_stream_sp); + } + + if (g_log) + { + for (size_t i=0; categories[i] != NULL; ++i) + { + const char *arg = categories[i]; + + if (0 == ::strcasecmp(arg, "all")) flag_bits |= LIBLLDB_LOG_ALL; + else if (0 == ::strcasecmp(arg, "api")) flag_bits |= LIBLLDB_LOG_API; + else if (0 == ::strncasecmp(arg, "break", 5)) flag_bits |= LIBLLDB_LOG_BREAKPOINTS; + else if (0 == ::strcasecmp(arg, "commands")) flag_bits |= LIBLLDB_LOG_COMMANDS; + else if (0 == ::strncasecmp(arg, "commu", 5)) flag_bits |= LIBLLDB_LOG_COMMUNICATION; + else if (0 == ::strncasecmp(arg, "conn", 4)) flag_bits |= LIBLLDB_LOG_CONNECTION; + else if (0 == ::strcasecmp(arg, "default")) flag_bits |= LIBLLDB_LOG_DEFAULT; + else if (0 == ::strcasecmp(arg, "dyld")) flag_bits |= LIBLLDB_LOG_DYNAMIC_LOADER; + else if (0 == ::strncasecmp(arg, "event", 5)) flag_bits |= LIBLLDB_LOG_EVENTS; + else if (0 == ::strncasecmp(arg, "expr", 4)) flag_bits |= LIBLLDB_LOG_EXPRESSIONS; + else if (0 == ::strncasecmp(arg, "host", 4)) flag_bits |= LIBLLDB_LOG_HOST; + else if (0 == ::strncasecmp(arg, "mmap", 4)) flag_bits |= LIBLLDB_LOG_MMAP; + else if (0 == ::strncasecmp(arg, "module", 6)) flag_bits |= LIBLLDB_LOG_MODULES; + else if (0 == ::strncasecmp(arg, "object", 6)) flag_bits |= LIBLLDB_LOG_OBJECT; + else if (0 == ::strcasecmp(arg, "os")) flag_bits |= LIBLLDB_LOG_OS; + else if (0 == ::strcasecmp(arg, "platform")) flag_bits |= LIBLLDB_LOG_PLATFORM; + else if (0 == ::strcasecmp(arg, "process")) flag_bits |= LIBLLDB_LOG_PROCESS; + else if (0 == ::strcasecmp(arg, "script")) flag_bits |= LIBLLDB_LOG_SCRIPT; + else if (0 == ::strcasecmp(arg, "state")) flag_bits |= LIBLLDB_LOG_STATE; + else if (0 == ::strcasecmp(arg, "step")) flag_bits |= LIBLLDB_LOG_STEP; + else if (0 == ::strncasecmp(arg, "symbol", 6)) flag_bits |= LIBLLDB_LOG_SYMBOLS; + else if (0 == ::strcasecmp(arg, "system-runtime")) flag_bits |= LIBLLDB_LOG_SYSTEM_RUNTIME; + else if (0 == ::strcasecmp(arg, "target")) flag_bits |= LIBLLDB_LOG_TARGET; + else if (0 == ::strncasecmp(arg, "temp", 4)) flag_bits |= LIBLLDB_LOG_TEMPORARY; + else if (0 == ::strcasecmp(arg, "thread")) flag_bits |= LIBLLDB_LOG_THREAD; + else if (0 == ::strncasecmp(arg, "types", 5)) flag_bits |= LIBLLDB_LOG_TYPES; + else if (0 == ::strncasecmp(arg, "unwind", 6)) flag_bits |= LIBLLDB_LOG_UNWIND; + else if (0 == ::strcasecmp(arg, "verbose")) flag_bits |= LIBLLDB_LOG_VERBOSE; + else if (0 == ::strncasecmp(arg, "watch", 5)) flag_bits |= LIBLLDB_LOG_WATCHPOINTS; + else if (0 == ::strcasecmp(arg, "jit")) flag_bits |= LIBLLDB_LOG_JIT_LOADER; + else if (0 == ::strcasecmp(arg, "language")) flag_bits |= LIBLLDB_LOG_LANGUAGE; + else + { + feedback_strm->Printf("error: unrecognized log category '%s'\n", arg); + ListLogCategories (feedback_strm); + return g_log; + } + } + + g_log->GetMask().Reset(flag_bits); + g_log->GetOptions().Reset(log_options); + } + g_log_enabled = true; + return g_log; +} + + +void +lldb_private::ListLogCategories (Stream *strm) +{ + strm->Printf("Logging categories for 'lldb':\n" + " all - turn on all available logging categories\n" + " api - enable logging of API calls and return values\n" + " break - log breakpoints\n" + " commands - log command argument parsing\n" + " communication - log communication activities\n" + " connection - log connection details\n" + " default - enable the default set of logging categories for liblldb\n" + " dyld - log shared library related activities\n" + " events - log broadcaster, listener and event queue activities\n" + " expr - log expressions\n" + " host - log host activities\n" + " jit - log JIT events in the target\n" + " language - log language runtime events\n" + " mmap - log mmap related activities\n" + " module - log module activities such as when modules are created, destroyed, replaced, and more\n" + " object - log object construction/destruction for important objects\n" + " os - log OperatingSystem plugin related activities\n" + " platform - log platform events and activities\n" + " process - log process events and activities\n" + " script - log events about the script interpreter\n" + " state - log private and public process state changes\n" + " step - log step related activities\n" + " symbol - log symbol related issues and warnings\n" + " system-runtime - log system runtime events\n" + " target - log target events and activities\n" + " thread - log thread events and activities\n" + " types - log type system related activities\n" + " unwind - log stack unwind activities\n" + " verbose - enable verbose logging\n" + " watch - log watchpoint related activities\n"); +} diff --git a/source/Core/Mangled.cpp b/source/Core/Mangled.cpp index e1598d30e4e5e..3e1a8bb891394 100644 --- a/source/Core/Mangled.cpp +++ b/source/Core/Mangled.cpp @@ -13,6 +13,8 @@ #if defined(_MSC_VER) #include "lldb/Host/windows/windows.h" #include <Dbghelp.h> +#pragma comment(lib, "dbghelp.lib") +#define LLDB_USE_BUILTIN_DEMANGLER #elif defined (__FreeBSD__) #define LLDB_USE_BUILTIN_DEMANGLER #else @@ -23,4960 +25,9 @@ // Provide a fast-path demangler implemented in FastDemangle.cpp until it can // replace the existing C++ demangler with a complete implementation -namespace lldb_private -{ - extern char * FastDemangle (const char * mangled_name, - long mangled_name_length); -} - -//---------------------------------------------------------------------- -// Inlined copy of: -// http://llvm.org/svn/llvm-project/libcxxabi/trunk/src/cxa_demangle.cpp -// revision 199944. -// -// 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. -// - Added "#undef _LIBCPP_EXTERN_TEMPLATE" to avoid warning -//---------------------------------------------------------------------- - -#undef _LIBCPP_EXTERN_TEMPLATE // Avoid warning below - -//===-------------------------- cxa_demangle.cpp --------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is dual licensed under the MIT and the University of Illinois Open -// Source Licenses. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// - -#define _LIBCPP_EXTERN_TEMPLATE(...) -#define _LIBCPP_NO_EXCEPTIONS +#include "lldb/Core/FastDemangle.h" +#include "lldb/Core/CxaDemangle.h" -#include <vector> -#include <algorithm> -#include <string> -#include <numeric> -#include <cstdlib> -#include <cstring> -#include <cctype> - - -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.empty()) - return first; - 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 != '_' || db.template_param.empty()) - 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.empty()) - return 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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 || db.names.size() < 2) - 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) - { - if (!db.names.empty()) - db.names.pop_back(); - return first; - } - if (db.names.size() < 2) - 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (!db.names.empty()) - db.names.pop_back(); - return first; - } - if (db.names.size() < 2) - 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) - { - if (db.names.empty()) - return first; - db.names.back().first.insert(0, "::"); - } - while (*t != 'E') - { - t1 = parse_unresolved_qualifier_level(t, last, db); - if (t1 == t || t1 == last || db.names.size() < 2) - 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) - { - if (!db.names.empty()) - db.names.pop_back(); - return first; - } - if (db.names.size() < 2) - 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) - { - if (db.names.size() < 2) - return first; - 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; - if (db.names.empty()) - 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; - if (db.names.empty()) - return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - if (!tmp.empty()) - { - if (db.names.empty()) - return first; - if (!first_expr) - { - db.names.back().first.append(", "); - first_expr = false; - } - db.names.back().first.append(tmp); - } - t = t1; - } - ++t; - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - if (!tmp.empty()) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - if (!tmp.empty()) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - init_list = db.names.back().move_full(); - db.names.pop_back(); - } - if (db.names.empty()) - return first; - auto type = db.names.back().move_full(); - db.names.pop_back(); - typename C::String expr_list; - if (has_expr_list) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - auto tmp = db.names.back().move_full(); - db.names.pop_back(); - if (!tmp.empty()) - { - if (db.names.empty()) - return first; - db.names.back().first.append(", "); - db.names.back().first.append(tmp); - first_expr = false; - } - } - t = t1; - } - } - ++t; - } - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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; - } - if (db.names.empty()) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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.empty()) - return first; - 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.empty()) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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') - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.empty()) - return 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) - { - if (db.names.size() < 2) - return 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) - { - if (db.names.empty()) - return 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) - { - if (db.names.empty()) - return 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) - { - if (db.names.empty()) - return 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 # <= -// ::= li <source-name> # operator "" -// ::= 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) - { - if (db.names.empty()) - return first; - 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 'i': - { - const char* t = parse_source_name(first+2, last, db); - if (t != first+2) - { - if (db.names.empty()) - return first; - db.names.back().first.insert(0, "operator\"\" "); - first = t; - } - } - 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) - { - if (db.names.empty()) - return first; - 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') - { - if (db.names.empty()) - return first; - 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': - if (db.names.empty()) - return first; - 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': - if (db.names.empty()) - return first; - 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; - } - if (db.names.size() < 2) - 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; - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return 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) - { - if (db.names.empty()) - return 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.size() < 3) - return first; - 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; - unsigned ref = 0; - if (*t0 == 'R') - { - ref = 1; - ++t0; - } - else if (*t0 == 'O') - { - 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.ref = ref; - 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); - if (db.names.empty()) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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); - if (db.names.size() < 2) - return first; - 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> - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.size() < 2) - return first; - 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 (db.names.empty()) - return first; - if (first[1] == '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) - { - if (db.names.empty()) - return first; - 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) - { - if (db.names.empty()) - return first; - db.names.back().first.insert(0, "reference temporary for "); - first = t; - } - break; - } - break; - } - } - return first; -} - -template <class T> -class save_value -{ - T& restore_; - T original_value_; -public: - save_value(T& restore) - : restore_(restore), - original_value_(restore) - {} - - ~save_value() - { - restore_ = std::move(original_value_); - } - - save_value(const save_value&) = delete; - save_value& operator=(const save_value&) = delete; -}; - -// <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) - { - save_value<decltype(db.encoding_depth)> su(db.encoding_depth); - ++db.encoding_depth; - save_value<decltype(db.tag_templates)> sb(db.tag_templates); - if (db.encoding_depth > 1) - db.tag_templates = true; - 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 != '.') - { - save_value<bool> sb2(db.tag_templates); - db.tag_templates = false; - const char* t2; - typename C::String ret2; - if (db.names.empty()) - return first; - const typename C::String& nm = db.names.back().first; - if (nm.empty()) - return 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; - if (db.names.size() < 2) - 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 (db.names.empty()) - return first; - if (!first_arg) - db.names.back().first += ", "; - else - first_arg = false; - db.names.back().first += tmp; - } - } - t = t2; - } - } - if (db.names.empty()) - return first; - 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; - } - 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; - } - if (db.names.empty()) - return first; - 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 == '.') - { - if (db.names.empty()) - return 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; - unsigned encoding_depth; - 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.encoding_depth = 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; -} - -} #endif @@ -4995,16 +46,23 @@ __cxa_demangle(const char* mangled_name, char* buf, size_t* n, int* status) using namespace lldb_private; -static inline bool -cstring_is_mangled (const char *s) +static inline Mangled::ManglingScheme +cstring_mangling_scheme(const char *s) { if (s) -#if defined(_MSC_VER) - return (s[0] == '?'); -#else - return (s[0] == '_' && s[1] == 'Z'); -#endif - return false; + { + if (s[0] == '?') + return Mangled::eManglingSchemeMSVC; + if (s[0] == '_' && s[1] == 'Z') + return Mangled::eManglingSchemeItanium; + } + return Mangled::eManglingSchemeNone; +} + +static inline bool +cstring_is_mangled(const char *s) +{ + return cstring_mangling_scheme(s) != Mangled::eManglingSchemeNone; } static const ConstString & @@ -5134,7 +192,7 @@ Mangled::Clear () //---------------------------------------------------------------------- -// Compare the the string values. +// Compare the string values. //---------------------------------------------------------------------- int Mangled::Compare (const Mangled& a, const Mangled& b) @@ -5215,45 +273,58 @@ Mangled::GetDemangledName () const m_mangled.GetCString()); // Don't bother running anything that isn't mangled - const char *mangled_cstr = m_mangled.GetCString(); - if (cstring_is_mangled(mangled_cstr)) + const char *mangled_name = m_mangled.GetCString(); + ManglingScheme mangling_scheme{cstring_mangling_scheme(mangled_name)}; + if (mangling_scheme != eManglingSchemeNone && + !m_mangled.GetMangledCounterpart(m_demangled)) { - if (!m_mangled.GetMangledCounterpart(m_demangled)) + // We didn't already mangle this name, demangle it and if all goes well + // add it to our map. + char *demangled_name = nullptr; + switch (mangling_scheme) { - // We didn't already mangle this name, demangle it and if all goes well - // add it to our map. -#ifdef LLDB_USE_BUILTIN_DEMANGLER - // Try to use the fast-path demangler first for the - // performance win, falling back to the full demangler only - // when necessary - char *demangled_name = FastDemangle (mangled_cstr, - m_mangled.GetLength()); - if (!demangled_name) - demangled_name = __cxa_demangle (mangled_cstr, NULL, NULL, NULL); -#elif defined(_MSC_VER) - char *demangled_name = (char *)::malloc(1024); - ::ZeroMemory(demangled_name, 1024); - DWORD result = ::UnDecorateSymbolName(mangled_cstr, demangled_name, 1023, - UNDNAME_NO_ACCESS_SPECIFIERS | // Strip public, private, protected keywords - UNDNAME_NO_ALLOCATION_LANGUAGE | // Strip __thiscall, __stdcall, etc keywords - UNDNAME_NO_THROW_SIGNATURES | // Strip throw() specifications - UNDNAME_NO_MEMBER_TYPE | // Strip virtual, static, etc specifiers - UNDNAME_NO_MS_KEYWORDS // Strip all MS extension keywords - ); - if (result == 0) + case eManglingSchemeMSVC: { - free (demangled_name); - demangled_name = nullptr; +#if defined(_MSC_VER) + const size_t demangled_length = 2048; + demangled_name = static_cast<char *>(::malloc(demangled_length)); + ::ZeroMemory(demangled_name, demangled_length); + DWORD result = ::UnDecorateSymbolName(mangled_name, demangled_name, demangled_length, + UNDNAME_NO_ACCESS_SPECIFIERS | // Strip public, private, protected keywords + UNDNAME_NO_ALLOCATION_LANGUAGE | // Strip __thiscall, __stdcall, etc keywords + UNDNAME_NO_THROW_SIGNATURES | // Strip throw() specifications + UNDNAME_NO_MEMBER_TYPE | // Strip virtual, static, etc specifiers + UNDNAME_NO_MS_KEYWORDS // Strip all MS extension keywords + ); + if (result == 0) + { + free(demangled_name); + demangled_name = nullptr; + } +#endif + break; } + case eManglingSchemeItanium: + { +#ifdef LLDB_USE_BUILTIN_DEMANGLER + // Try to use the fast-path demangler first for the + // performance win, falling back to the full demangler only + // when necessary + demangled_name = FastDemangle(mangled_name, m_mangled.GetLength()); + if (!demangled_name) + demangled_name = __cxa_demangle(mangled_name, NULL, NULL, NULL); #else - char *demangled_name = abi::__cxa_demangle (mangled_cstr, NULL, NULL, NULL); + demangled_name = abi::__cxa_demangle(mangled_name, NULL, NULL, NULL); #endif - - if (demangled_name) - { - m_demangled.SetCStringWithMangledCounterpart(demangled_name, m_mangled); - free (demangled_name); + break; } + case eManglingSchemeNone: + break; + } + if (demangled_name) + { + m_demangled.SetCStringWithMangledCounterpart(demangled_name, m_mangled); + free(demangled_name); } } if (!m_demangled) @@ -5353,7 +424,7 @@ Mangled::MemorySize () const } lldb::LanguageType -Mangled::GetLanguage () +Mangled::GuessLanguage () const { ConstString mangled = GetMangledName(); if (mangled) diff --git a/source/Core/Module.cpp b/source/Core/Module.cpp index 891bd87a20d49..eb0359d02d5be 100644 --- a/source/Core/Module.cpp +++ b/source/Core/Module.cpp @@ -7,8 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - #include "lldb/Core/AddressResolverFileLine.h" #include "lldb/Core/Error.h" #include "lldb/Core/Module.h" @@ -25,7 +23,7 @@ #include "lldb/Host/Symbols.h" #include "lldb/Interpreter/CommandInterpreter.h" #include "lldb/Interpreter/ScriptInterpreter.h" -#include "lldb/lldb-private-log.h" +#include "lldb/Symbol/ClangASTContext.h" #include "lldb/Symbol/CompileUnit.h" #include "lldb/Symbol/ObjectFile.h" #include "lldb/Symbol/SymbolContext.h" @@ -39,6 +37,9 @@ #include "Plugins/ObjectFile/JIT/ObjectFileJIT.h" +#include "llvm/Support/raw_os_ostream.h" +#include "llvm/Support/Signals.h" + using namespace lldb; using namespace lldb_private; @@ -145,7 +146,7 @@ Module::Module (const ModuleSpec &module_spec) : m_object_mod_time (), m_objfile_sp (), m_symfile_ap (), - m_ast (), + m_ast (new ClangASTContext), m_source_mappings (), m_sections_ap(), m_did_load_objfile (false), @@ -249,7 +250,7 @@ Module::Module(const FileSpec& file_spec, m_object_mod_time (), m_objfile_sp (), m_symfile_ap (), - m_ast (), + m_ast (new ClangASTContext), m_source_mappings (), m_sections_ap(), m_did_load_objfile (false), @@ -295,7 +296,7 @@ Module::Module () : m_object_mod_time (), m_objfile_sp (), m_symfile_ap (), - m_ast (), + m_ast (new ClangASTContext), m_source_mappings (), m_sections_ap(), m_did_load_objfile (false), @@ -440,10 +441,10 @@ Module::GetClangASTContext () object_arch.GetTriple().setOS(llvm::Triple::MacOSX); } } - m_ast.SetArchitecture (object_arch); + m_ast->SetArchitecture (object_arch); } } - return m_ast; + return *m_ast; } void @@ -901,10 +902,9 @@ Module::FindFunctions (const RegularExpression& regex, { sc.symbol = symtab->SymbolAtIndex(symbol_indexes[i]); SymbolType sym_type = sc.symbol->GetType(); - if (sc.symbol && (sym_type == eSymbolTypeCode || - sym_type == eSymbolTypeResolver)) + if (sc.symbol && sc.symbol->ValueIsAddress() && (sym_type == eSymbolTypeCode || sym_type == eSymbolTypeResolver)) { - FileAddrToIndexMap::const_iterator pos = file_addr_to_index.find(sc.symbol->GetAddress().GetFileAddress()); + FileAddrToIndexMap::const_iterator pos = file_addr_to_index.find(sc.symbol->GetAddressRef().GetFileAddress()); if (pos == end) sc_list.Append(sc); else @@ -1233,7 +1233,12 @@ Module::LogMessageVerboseBacktrace (Log *log, const char *format, ...) log_message.PrintfVarArg (format, args); va_end (args); if (log->GetVerbose()) - Host::Backtrace (log_message, 1024); + { + std::string back_trace; + llvm::raw_string_ostream stream(back_trace); + llvm::sys::PrintStackTrace(stream); + log_message.PutCString(back_trace.c_str()); + } log->PutCString(log_message.GetString().c_str()); } } @@ -1461,9 +1466,11 @@ Module::FindSymbolsMatchingRegExAndType (const RegularExpression ®ex, SymbolT void Module::SetSymbolFileFileSpec (const FileSpec &file) { - // Remove any sections in the unified section list that come from the current symbol vendor. + if (!file.Exists()) + return; if (m_symfile_ap) { + // Remove any sections in the unified section list that come from the current symbol vendor. SectionList *section_list = GetSectionList(); SymbolFile *symbol_file = m_symfile_ap->GetSymbolFile(); if (section_list && symbol_file) @@ -1471,21 +1478,49 @@ Module::SetSymbolFileFileSpec (const FileSpec &file) ObjectFile *obj_file = symbol_file->GetObjectFile(); // Make sure we have an object file and that the symbol vendor's objfile isn't // the same as the module's objfile before we remove any sections for it... - if (obj_file && obj_file != m_objfile_sp.get()) + if (obj_file) { - size_t num_sections = section_list->GetNumSections (0); - for (size_t idx = num_sections; idx > 0; --idx) + // Check to make sure we aren't trying to specify the file we already have + if (obj_file->GetFileSpec() == file) { - lldb::SectionSP section_sp (section_list->GetSectionAtIndex (idx - 1)); - if (section_sp->GetObjectFile() == obj_file) + // We are being told to add the exact same file that we already have + // we don't have to do anything. + return; + } + + // The symbol file might be a directory bundle ("/tmp/a.out.dSYM") instead + // of a full path to the symbol file within the bundle + // ("/tmp/a.out.dSYM/Contents/Resources/DWARF/a.out"). So we need to check this + + if (file.IsDirectory()) + { + std::string new_path(file.GetPath()); + std::string old_path(obj_file->GetFileSpec().GetPath()); + if (old_path.find(new_path) == 0) + { + // We specified the same bundle as the symbol file that we already have + return; + } + } + + if (obj_file != m_objfile_sp.get()) + { + size_t num_sections = section_list->GetNumSections (0); + for (size_t idx = num_sections; idx > 0; --idx) { - section_list->DeleteSection (idx - 1); + lldb::SectionSP section_sp (section_list->GetSectionAtIndex (idx - 1)); + if (section_sp->GetObjectFile() == obj_file) + { + section_list->DeleteSection (idx - 1); + } } } } } + // Keep all old symbol files around in case there are any lingering type references in + // any SBValue objects that might have been handed out. + m_old_symfiles.push_back(std::move(m_symfile_ap)); } - m_symfile_spec = file; m_symfile_ap.reset(); m_did_load_symbol_vendor = false; @@ -1608,7 +1643,7 @@ Module::SetArchitecture (const ArchSpec &new_arch) m_arch = new_arch; return true; } - return m_arch.IsExactMatch(new_arch); + return m_arch.IsCompatibleMatch(new_arch); } bool diff --git a/source/Core/ModuleList.cpp b/source/Core/ModuleList.cpp index 879eb9bd18223..669b3d9274cc7 100644 --- a/source/Core/ModuleList.cpp +++ b/source/Core/ModuleList.cpp @@ -978,14 +978,46 @@ ModuleList::GetSharedModule if (module_sp) return error; + + module_sp.reset (new Module (module_spec)); + // Make sure there are a module and an object file since we can specify + // a valid file path with an architecture that might not be in that file. + // By getting the object file we can guarantee that the architecture matches + if (module_sp->GetObjectFile()) + { + // If we get in here we got the correct arch, now we just need + // to verify the UUID if one was given + if (uuid_ptr && *uuid_ptr != module_sp->GetUUID()) + module_sp.reset(); + else + { + if (did_create_ptr) + *did_create_ptr = true; + + shared_module_list.ReplaceEquivalent(module_sp); + return error; + } + } else + module_sp.reset(); + + if (module_search_paths_ptr) { - module_sp.reset (new Module (module_spec)); - // Make sure there are a module and an object file since we can specify - // a valid file path with an architecture that might not be in that file. - // By getting the object file we can guarantee that the architecture matches - if (module_sp) + const auto num_directories = module_search_paths_ptr->GetSize(); + for (size_t idx = 0; idx < num_directories; ++idx) { + auto search_path_spec = module_search_paths_ptr->GetFileSpecAtIndex(idx); + if (!search_path_spec.ResolvePath()) + continue; + if (!search_path_spec.Exists() || !search_path_spec.IsDirectory()) + continue; + search_path_spec.AppendPathComponent(module_spec.GetFileSpec().GetFilename().AsCString()); + if (!search_path_spec.Exists()) + continue; + + auto resolved_module_spec(module_spec); + resolved_module_spec.GetFileSpec() = search_path_spec; + module_sp.reset (new Module (resolved_module_spec)); if (module_sp->GetObjectFile()) { // If we get in here we got the correct arch, now we just need @@ -998,7 +1030,7 @@ ModuleList::GetSharedModule *did_create_ptr = true; shared_module_list.ReplaceEquivalent(module_sp); - return error; + return Error(); } } else @@ -1162,3 +1194,15 @@ ModuleList::LoadScriptingResourcesInTarget (Target *target, } return errors.size() == 0; } + +void +ModuleList::ForEach (std::function <bool (const ModuleSP &module_sp)> const &callback) const +{ + Mutex::Locker locker(m_modules_mutex); + for (const auto &module : m_modules) + { + // If the callback returns false, then stop iterating and break out + if (!callback (module)) + break; + } +} diff --git a/source/Core/PluginManager.cpp b/source/Core/PluginManager.cpp index 95574cb2deafd..e34b7fc3b17dd 100644 --- a/source/Core/PluginManager.cpp +++ b/source/Core/PluginManager.cpp @@ -7,8 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - #include "lldb/Core/PluginManager.h" #include <limits.h> @@ -885,6 +883,7 @@ struct LanguageRuntimeInstance ConstString name; std::string description; LanguageRuntimeCreateInstance create_callback; + LanguageRuntimeGetCommandObject command_callback; }; typedef std::vector<LanguageRuntimeInstance> LanguageRuntimeInstances; @@ -908,7 +907,8 @@ PluginManager::RegisterPlugin ( const ConstString &name, const char *description, - LanguageRuntimeCreateInstance create_callback + LanguageRuntimeCreateInstance create_callback, + LanguageRuntimeGetCommandObject command_callback ) { if (create_callback) @@ -919,6 +919,7 @@ PluginManager::RegisterPlugin if (description && description[0]) instance.description = description; instance.create_callback = create_callback; + instance.command_callback = command_callback; Mutex::Locker locker (GetLanguageRuntimeMutex ()); GetLanguageRuntimeInstances ().push_back (instance); } @@ -956,6 +957,16 @@ PluginManager::GetLanguageRuntimeCreateCallbackAtIndex (uint32_t idx) return NULL; } +LanguageRuntimeGetCommandObject +PluginManager::GetLanguageRuntimeGetCommandObjectAtIndex (uint32_t idx) +{ + Mutex::Locker locker (GetLanguageRuntimeMutex ()); + LanguageRuntimeInstances &instances = GetLanguageRuntimeInstances (); + if (idx < instances.size()) + return instances[idx].command_callback; + return NULL; +} + LanguageRuntimeCreateInstance PluginManager::GetLanguageRuntimeCreateCallbackForPluginName (const ConstString &name) { diff --git a/source/Core/Scalar.cpp b/source/Core/Scalar.cpp index 0e9b98dc4ab6a..0022c348bfeec 100644 --- a/source/Core/Scalar.cpp +++ b/source/Core/Scalar.cpp @@ -1900,7 +1900,7 @@ Scalar::SetValueFromData (DataExtractor &data, lldb::Encoding encoding, size_t b break; case lldb::eEncodingUint: { - lldb::offset_t offset; + lldb::offset_t offset = 0; switch (byte_size) { @@ -1916,7 +1916,7 @@ Scalar::SetValueFromData (DataExtractor &data, lldb::Encoding encoding, size_t b break; case lldb::eEncodingSint: { - lldb::offset_t offset; + lldb::offset_t offset = 0; switch (byte_size) { @@ -1932,7 +1932,7 @@ Scalar::SetValueFromData (DataExtractor &data, lldb::Encoding encoding, size_t b break; case lldb::eEncodingIEEE754: { - lldb::offset_t offset; + lldb::offset_t offset = 0; if (byte_size == sizeof (float)) operator=((float)data.GetFloat(&offset)); diff --git a/source/Core/SearchFilter.cpp b/source/Core/SearchFilter.cpp index 9c3412a422d0e..f54bbe8d4adf4 100644 --- a/source/Core/SearchFilter.cpp +++ b/source/Core/SearchFilter.cpp @@ -816,7 +816,7 @@ SearchFilterByModuleListAndCU::GetDescription (Stream *s) } else if (num_modules > 0) { - s->Printf (", modules(%zd) = ", num_modules); + s->Printf (", modules(%" PRIu64 ") = ", static_cast<uint64_t>(num_modules)); for (size_t i = 0; i < num_modules; i++) { if (s->GetVerbose()) diff --git a/source/Core/Section.cpp b/source/Core/Section.cpp index ea33a62b775f2..9a3f220beb445 100644 --- a/source/Core/Section.cpp +++ b/source/Core/Section.cpp @@ -12,6 +12,7 @@ #include "lldb/Symbol/ObjectFile.h" #include "lldb/Target/SectionLoadList.h" #include "lldb/Target/Target.h" +#include "lldb/Utility/ConvertEnum.h" #include <limits> diff --git a/source/Core/SourceManager.cpp b/source/Core/SourceManager.cpp index 44ab50197c197..324ed34bfaacb 100644 --- a/source/Core/SourceManager.cpp +++ b/source/Core/SourceManager.cpp @@ -7,8 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - #include "lldb/Core/SourceManager.h" // C Includes @@ -18,6 +16,7 @@ #include "lldb/Core/DataBuffer.h" #include "lldb/Core/Debugger.h" #include "lldb/Core/Module.h" +#include "lldb/Core/RegularExpression.h" #include "lldb/Core/Stream.h" #include "lldb/Symbol/ClangNamespaceDecl.h" #include "lldb/Symbol/CompileUnit.h" diff --git a/source/Core/StreamAsynchronousIO.cpp b/source/Core/StreamAsynchronousIO.cpp index 257982ab8b292..ccfde0c9a0119 100644 --- a/source/Core/StreamAsynchronousIO.cpp +++ b/source/Core/StreamAsynchronousIO.cpp @@ -7,22 +7,20 @@ // //===----------------------------------------------------------------------===// -#include <stdio.h> +#include "lldb/Core/StreamAsynchronousIO.h" #include "lldb/lldb-private.h" -#include "lldb/Core/Broadcaster.h" -#include "lldb/Core/Event.h" -#include "lldb/Core/StreamAsynchronousIO.h" +#include "lldb/Core/Debugger.h" using namespace lldb; using namespace lldb_private; -StreamAsynchronousIO::StreamAsynchronousIO (Broadcaster &broadcaster, uint32_t broadcast_event_type) : +StreamAsynchronousIO::StreamAsynchronousIO (Debugger &debugger, bool for_stdout) : Stream (0, 4, eByteOrderBig), - m_broadcaster (broadcaster), - m_broadcast_event_type (broadcast_event_type), - m_accumulated_data () + m_debugger (debugger), + m_data (), + m_for_stdout (for_stdout) { } @@ -35,19 +33,16 @@ StreamAsynchronousIO::~StreamAsynchronousIO () void StreamAsynchronousIO::Flush () { - if (!m_accumulated_data.empty()) + if (!m_data.empty()) { - std::unique_ptr<EventDataBytes> data_bytes_ap (new EventDataBytes); - // Let's swap the bytes to avoid LARGE string copies. - data_bytes_ap->SwapBytes (m_accumulated_data); - EventSP new_event_sp (new Event (m_broadcast_event_type, data_bytes_ap.release())); - m_broadcaster.BroadcastEvent (new_event_sp); + m_debugger.PrintAsync (m_data.data(), m_data.size(), m_for_stdout); + m_data = std::move(std::string()); } } size_t StreamAsynchronousIO::Write (const void *s, size_t length) { - m_accumulated_data.append ((const char *)s, length); + m_data.append ((const char *)s, length); return length; } diff --git a/source/Core/StreamFile.cpp b/source/Core/StreamFile.cpp index 9f8dd629e3d44..8c9700c6dcba2 100644 --- a/source/Core/StreamFile.cpp +++ b/source/Core/StreamFile.cpp @@ -54,6 +54,13 @@ StreamFile::StreamFile (const char *path) : { } +StreamFile::StreamFile (const char *path, + uint32_t options, + uint32_t permissions) : + Stream(), + m_file(path, options, permissions) +{ +} StreamFile::~StreamFile() { diff --git a/source/Core/StringList.cpp b/source/Core/StringList.cpp index d2fa8cfb4a880..4e07ba4a4579a 100644 --- a/source/Core/StringList.cpp +++ b/source/Core/StringList.cpp @@ -69,6 +69,12 @@ StringList::AppendString (const char *str, size_t str_len) } void +StringList::AppendString(llvm::StringRef str) +{ + m_strings.push_back(str.str()); +} + +void StringList::AppendList (const char **strv, int strc) { for (int i = 0; i < strc; ++i) diff --git a/source/Core/StructuredData.cpp b/source/Core/StructuredData.cpp index 3c43e41f3c94e..a2c440948af15 100644 --- a/source/Core/StructuredData.cpp +++ b/source/Core/StructuredData.cpp @@ -13,6 +13,8 @@ #include <stdlib.h> #include <inttypes.h> +#include "lldb/Core/StreamString.h" + using namespace lldb_private; @@ -296,6 +298,10 @@ StructuredData::ParseJSON (std::string json_text) { object_sp = read_json_object (&c); } + else if (*c == '[') + { + object_sp = read_json_array (&c); + } else { // We have bad characters here, this is likely an illegal JSON string. @@ -350,16 +356,32 @@ StructuredData::Object::GetObjectForDotSeparatedPath (llvm::StringRef path) } void -StructuredData::Array::Dump (Stream &s) const +StructuredData::Object::DumpToStdout() const { - s << "["; - const size_t arrsize = m_items.size(); - for (size_t i = 0; i < arrsize; ++i) + StreamString stream; + Dump(stream); + printf("%s\n", stream.GetString().c_str()); +} + +void +StructuredData::Array::Dump(Stream &s) const +{ + bool first = true; + s << "[\n"; + s.IndentMore(); + for (const auto &item_sp : m_items) { - m_items[i]->Dump(s); - if (i + 1 < arrsize) - s << ","; + if (first) + first = false; + else + s << ",\n"; + + s.Indent(); + item_sp->Dump(s); } + s.IndentLess(); + s.EOL(); + s.Indent(); s << "]"; } @@ -404,21 +426,22 @@ StructuredData::String::Dump (Stream &s) const void StructuredData::Dictionary::Dump (Stream &s) const { - bool have_printed_one_elem = false; - s << "{"; - for (collection::const_iterator iter = m_dict.begin(); iter != m_dict.end(); ++iter) + bool first = true; + s << "{\n"; + s.IndentMore(); + for (const auto &pair : m_dict) { - if (have_printed_one_elem == false) - { - have_printed_one_elem = true; - } + if (first) + first = false; else - { - s << ","; - } - s << "\"" << iter->first.AsCString() << "\":"; - iter->second->Dump(s); + s << ",\n"; + s.Indent(); + s << "\"" << pair.first.AsCString() << "\" : "; + pair.second->Dump(s); } + s.IndentLess(); + s.EOL(); + s.Indent(); s << "}"; } @@ -427,3 +450,9 @@ StructuredData::Null::Dump (Stream &s) const { s << "null"; } + +void +StructuredData::Generic::Dump(Stream &s) const +{ + s << "0x" << m_object; +} diff --git a/source/Core/UUID.cpp b/source/Core/UUID.cpp index c1b3eb13d6563..88290d137943a 100644 --- a/source/Core/UUID.cpp +++ b/source/Core/UUID.cpp @@ -234,7 +234,10 @@ UUID::SetFromCString (const char *cstr, uint32_t num_uuid_bytes) // If we successfully decoded a UUID, return the amount of characters that // were consumed if (uuid_byte_idx == num_uuid_bytes) + { + m_num_uuid_bytes = num_uuid_bytes; return p - cstr; + } // Else return zero to indicate we were not able to parse a UUID value return 0; diff --git a/source/Core/UserSettingsController.cpp b/source/Core/UserSettingsController.cpp index 63a5dd9ed51fa..837ff18721e20 100644 --- a/source/Core/UserSettingsController.cpp +++ b/source/Core/UserSettingsController.cpp @@ -7,8 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - #include <string.h> #include <algorithm> @@ -18,6 +16,7 @@ #include "lldb/Core/Stream.h" #include "lldb/Core/StreamString.h" #include "lldb/Interpreter/CommandInterpreter.h" +#include "lldb/Interpreter/OptionValueProperties.h" #include "lldb/Interpreter/OptionValueString.h" using namespace lldb; diff --git a/source/Core/ValueObject.cpp b/source/Core/ValueObject.cpp index b72e5c3f18c64..8718b37b95e08 100644 --- a/source/Core/ValueObject.cpp +++ b/source/Core/ValueObject.cpp @@ -7,8 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - #include "lldb/Core/ValueObject.h" // C Includes @@ -43,7 +41,6 @@ #include "lldb/Host/Endian.h" #include "lldb/Interpreter/CommandInterpreter.h" -#include "lldb/Interpreter/ScriptInterpreterPython.h" #include "lldb/Symbol/ClangASTType.h" #include "lldb/Symbol/ClangASTContext.h" @@ -198,7 +195,7 @@ ValueObject::UpdateValueIfNeeded (bool update_format) bool first_update = IsChecksumEmpty(); - if (m_update_point.NeedsUpdating()) + if (NeedsUpdating()) { m_update_point.SetUpdated(); @@ -973,7 +970,7 @@ ValueObject::GetPointeeData (DataExtractor& data, ExecutionContext exe_ctx (GetExecutionContextRef()); - const uint64_t item_type_size = pointee_or_element_clang_type.GetByteSize(&exe_ctx); + const uint64_t item_type_size = pointee_or_element_clang_type.GetByteSize(exe_ctx.GetBestExecutionContextScope()); const uint64_t bytes = item_count * item_type_size; const uint64_t offset = item_idx * item_type_size; @@ -1049,10 +1046,13 @@ ValueObject::GetPointeeData (DataExtractor& data, break; case eAddressTypeHost: { - const uint64_t max_bytes = GetClangType().GetByteSize(&exe_ctx); + const uint64_t max_bytes = GetClangType().GetByteSize(exe_ctx.GetBestExecutionContextScope()); if (max_bytes > offset) { size_t bytes_read = std::min<uint64_t>(max_bytes - offset, bytes); + addr = m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); + if (addr == LLDB_INVALID_ADDRESS) + break; heap_buf_ptr->CopyData((uint8_t*)(addr + offset), bytes_read); data.SetData(data_sp); return bytes_read; @@ -1821,13 +1821,19 @@ ValueObject::GetAddressOf (bool scalar_is_load_address, AddressType *address_typ case Value::eValueTypeLoadAddress: case Value::eValueTypeFileAddress: - case Value::eValueTypeHostAddress: { if(address_type) *address_type = m_value.GetValueAddressType (); return m_value.GetScalar().ULongLong(LLDB_INVALID_ADDRESS); } break; + case Value::eValueTypeHostAddress: + { + if(address_type) + *address_type = m_value.GetValueAddressType (); + return LLDB_INVALID_ADDRESS; + } + break; } if (address_type) *address_type = eAddressTypeInvalid; @@ -2064,6 +2070,21 @@ ValueObject::IsPossibleDynamicType () } bool +ValueObject::IsRuntimeSupportValue () +{ + Process *process(GetProcessSP().get()); + if (process) + { + LanguageRuntime *runtime = process->GetLanguageRuntime(GetObjectRuntimeLanguage()); + if (!runtime) + runtime = process->GetObjCLanguageRuntime(); + if (runtime) + return runtime->IsRuntimeSupportValue(*this); + } + return false; +} + +bool ValueObject::IsObjCNil () { const uint32_t mask = eTypeIsObjC | eTypeIsPointer; @@ -2075,52 +2096,6 @@ ValueObject::IsObjCNil () return canReadValue && isZero; } -ValueObjectSP -ValueObject::GetSyntheticArrayMember (size_t index, bool can_create) -{ - const uint32_t type_info = GetTypeInfo (); - if (type_info & eTypeIsArray) - return GetSyntheticArrayMemberFromArray(index, can_create); - - if (type_info & eTypeIsPointer) - return GetSyntheticArrayMemberFromPointer(index, can_create); - - return ValueObjectSP(); - -} - -ValueObjectSP -ValueObject::GetSyntheticArrayMemberFromPointer (size_t index, bool can_create) -{ - ValueObjectSP synthetic_child_sp; - if (IsPointerType ()) - { - char index_str[64]; - snprintf(index_str, sizeof(index_str), "[%" PRIu64 "]", (uint64_t)index); - ConstString index_const_str(index_str); - // Check if we have already created a synthetic array member in this - // valid object. If we have we will re-use it. - synthetic_child_sp = GetSyntheticChild (index_const_str); - if (!synthetic_child_sp) - { - ValueObject *synthetic_child; - // We haven't made a synthetic array member for INDEX yet, so - // lets make one and cache it for any future reference. - synthetic_child = CreateChildAtIndex(0, true, index); - - // Cache the value if we got one back... - if (synthetic_child) - { - AddSyntheticChild(index_const_str, synthetic_child); - synthetic_child_sp = synthetic_child->GetSP(); - synthetic_child_sp->SetName(ConstString(index_str)); - synthetic_child_sp->m_is_array_item_for_pointer = true; - } - } - } - return synthetic_child_sp; -} - // This allows you to create an array member using and index // that doesn't not fall in the normal bounds of the array. // Many times structure can be defined as: @@ -2133,10 +2108,10 @@ ValueObject::GetSyntheticArrayMemberFromPointer (size_t index, bool can_create) // there are more items in "item_array". ValueObjectSP -ValueObject::GetSyntheticArrayMemberFromArray (size_t index, bool can_create) +ValueObject::GetSyntheticArrayMember (size_t index, bool can_create) { ValueObjectSP synthetic_child_sp; - if (IsArrayType ()) + if (IsPointerType () || IsArrayType()) { char index_str[64]; snprintf(index_str, sizeof(index_str), "[%" PRIu64 "]", (uint64_t)index); @@ -2150,7 +2125,7 @@ ValueObject::GetSyntheticArrayMemberFromArray (size_t index, bool can_create) // We haven't made a synthetic array member for INDEX yet, so // lets make one and cache it for any future reference. synthetic_child = CreateChildAtIndex(0, true, index); - + // Cache the value if we got one back... if (synthetic_child) { @@ -2229,7 +2204,7 @@ ValueObject::GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type ValueObjectChild *synthetic_child = new ValueObjectChild(*this, type, name_const_str, - type.GetByteSize(&exe_ctx), + type.GetByteSize(exe_ctx.GetBestExecutionContextScope()), offset, 0, 0, @@ -2272,7 +2247,7 @@ ValueObject::GetSyntheticBase (uint32_t offset, const ClangASTType& type, bool c ValueObjectChild *synthetic_child = new ValueObjectChild(*this, type, name_const_str, - type.GetByteSize(&exe_ctx), + type.GetByteSize(exe_ctx.GetBestExecutionContextScope()), offset, 0, 0, @@ -2320,7 +2295,7 @@ ValueObject::GetSyntheticExpressionPathChild(const char* expression, bool can_cr // lets make one and cache it for any future reference. synthetic_child_sp = GetValueForExpressionPath(expression, NULL, NULL, NULL, - GetValueForExpressionPathOptions().DontAllowSyntheticChildren()); + GetValueForExpressionPathOptions().SetSyntheticChildrenTraversal(GetValueForExpressionPathOptions::SyntheticChildrenTraversal::None)); // Cache the value if we got one back... if (synthetic_child_sp.get()) @@ -2851,19 +2826,43 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, *final_result = ValueObject::eExpressionPathEndResultTypePlain; return child_valobj_sp; } - else if (options.m_no_synthetic_children == false) // let's try with synthetic children + else { - if (root->IsSynthetic()) + switch (options.m_synthetic_children_traversal) { - *first_unparsed = expression_cstr; - *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchSyntheticChild; - *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; - return ValueObjectSP(); + case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::None: + break; + case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::FromSynthetic: + if (root->IsSynthetic()) + { + child_valobj_sp = root->GetNonSyntheticValue(); + if (child_valobj_sp.get()) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + break; + case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::ToSynthetic: + if (!root->IsSynthetic()) + { + child_valobj_sp = root->GetSyntheticValue(); + if (child_valobj_sp.get()) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + break; + case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::Both: + if (root->IsSynthetic()) + { + child_valobj_sp = root->GetNonSyntheticValue(); + if (child_valobj_sp.get()) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + else + { + child_valobj_sp = root->GetSyntheticValue(); + if (child_valobj_sp.get()) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + break; } - - child_valobj_sp = root->GetSyntheticValue(); - if (child_valobj_sp.get()) - child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); } // if we are here and options.m_no_synthetic_children is true, child_valobj_sp is going to be a NULL SP, @@ -2894,19 +2893,43 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, *final_result = ValueObject::eExpressionPathEndResultTypePlain; continue; } - else if (options.m_no_synthetic_children == false) // let's try with synthetic children + else { - if (root->IsSynthetic()) + switch (options.m_synthetic_children_traversal) { - *first_unparsed = expression_cstr; - *reason_to_stop = ValueObject::eExpressionPathScanEndReasonNoSuchChild; - *final_result = ValueObject::eExpressionPathEndResultTypeInvalid; - return ValueObjectSP(); + case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::None: + break; + case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::FromSynthetic: + if (root->IsSynthetic()) + { + child_valobj_sp = root->GetNonSyntheticValue(); + if (child_valobj_sp.get()) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + break; + case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::ToSynthetic: + if (!root->IsSynthetic()) + { + child_valobj_sp = root->GetSyntheticValue(); + if (child_valobj_sp.get()) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + break; + case GetValueForExpressionPathOptions::SyntheticChildrenTraversal::Both: + if (root->IsSynthetic()) + { + child_valobj_sp = root->GetNonSyntheticValue(); + if (child_valobj_sp.get()) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + else + { + child_valobj_sp = root->GetSyntheticValue(); + if (child_valobj_sp.get()) + child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); + } + break; } - - child_valobj_sp = root->GetSyntheticValue(true); - if (child_valobj_sp) - child_valobj_sp = child_valobj_sp->GetChildMemberWithName(child_name, true); } // if we are here and options.m_no_synthetic_children is true, child_valobj_sp is going to be a NULL SP, @@ -2934,7 +2957,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, { if (!root_clang_type_info.Test(eTypeIsScalar)) // if this is not even a scalar... { - if (options.m_no_synthetic_children) // ...only chance left is synthetic + if (options.m_synthetic_children_traversal == GetValueForExpressionPathOptions::SyntheticChildrenTraversal::None) // ...only chance left is synthetic { *first_unparsed = expression_cstr; *reason_to_stop = ValueObject::eExpressionPathScanEndReasonRangeOperatorInvalid; @@ -3009,7 +3032,7 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, { ValueObjectSP child_valobj_sp = root->GetChildAtIndex(index, true); if (!child_valobj_sp) - child_valobj_sp = root->GetSyntheticArrayMemberFromArray(index, true); + child_valobj_sp = root->GetSyntheticArrayMember(index, true); if (!child_valobj_sp) if (root->HasSyntheticValue() && root->GetSyntheticValue()->GetNumChildren() > index) child_valobj_sp = root->GetSyntheticValue()->GetChildAtIndex(index, true); @@ -3053,12 +3076,13 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, if (root->GetClangType().GetMinimumLanguage() == eLanguageTypeObjC && pointee_clang_type_info.AllClear(eTypeIsPointer) && root->HasSyntheticValue() - && options.m_no_synthetic_children == false) + && (options.m_synthetic_children_traversal == GetValueForExpressionPathOptions::SyntheticChildrenTraversal::ToSynthetic || + options.m_synthetic_children_traversal == GetValueForExpressionPathOptions::SyntheticChildrenTraversal::Both)) { root = root->GetSyntheticValue()->GetChildAtIndex(index, true); } else - root = root->GetSyntheticArrayMemberFromPointer(index, true); + root = root->GetSyntheticArrayMember(index, true); if (!root.get()) { *first_unparsed = expression_cstr; @@ -3109,7 +3133,8 @@ ValueObject::GetValueForExpressionPath_Impl(const char* expression_cstr, continue; } } - else if (options.m_no_synthetic_children == false) + else if (options.m_synthetic_children_traversal == GetValueForExpressionPathOptions::SyntheticChildrenTraversal::ToSynthetic || + options.m_synthetic_children_traversal == GetValueForExpressionPathOptions::SyntheticChildrenTraversal::Both) { if (root->HasSyntheticValue()) root = root->GetSyntheticValue(); @@ -3401,7 +3426,7 @@ ValueObject::ExpandArraySliceExpression(const char* expression_cstr, } else { - root = root->GetSyntheticArrayMemberFromPointer(index, true); + root = root->GetSyntheticArrayMember(index, true); if (!root.get()) { *first_unparsed = expression_cstr; @@ -3535,7 +3560,7 @@ void ValueObject::LogValueObject (Log *log) { if (log) - return LogValueObject (log, DumpValueObjectOptions::DefaultOptions()); + return LogValueObject (log, DumpValueObjectOptions(*this)); } void @@ -3553,7 +3578,7 @@ ValueObject::LogValueObject (Log *log, const DumpValueObjectOptions& options) void ValueObject::Dump (Stream &s) { - Dump (s, DumpValueObjectOptions::DefaultOptions()); + Dump (s, DumpValueObjectOptions(*this)); } void @@ -3766,7 +3791,7 @@ ValueObject::AddressOf (Error &error) const bool scalar_is_load_address = false; addr_t addr = GetAddressOf (scalar_is_load_address, &address_type); error.Clear(); - if (addr != LLDB_INVALID_ADDRESS) + if (addr != LLDB_INVALID_ADDRESS && address_type != eAddressTypeHost) { switch (address_type) { @@ -3780,7 +3805,6 @@ ValueObject::AddressOf (Error &error) case eAddressTypeFile: case eAddressTypeLoad: - case eAddressTypeHost: { ClangASTType clang_type = GetClangType(); if (clang_type) @@ -3797,6 +3821,8 @@ ValueObject::AddressOf (Error &error) } } break; + default: + break; } } else @@ -3923,9 +3949,8 @@ ValueObject::EvaluationPoint::~EvaluationPoint () // exe_scope will be set to the current execution context scope. bool -ValueObject::EvaluationPoint::SyncWithProcessState() +ValueObject::EvaluationPoint::SyncWithProcessState(bool accept_invalid_exe_ctx) { - // Start with the target, if it is NULL, then we're obviously not going to get any further: const bool thread_and_frame_only_if_stopped = true; ExecutionContext exe_ctx(m_exe_ctx_ref.Lock(thread_and_frame_only_if_stopped)); @@ -3968,30 +3993,33 @@ ValueObject::EvaluationPoint::SyncWithProcessState() // That way we'll be sure to return a valid exe_scope. // If we used to have a thread or a frame but can't find it anymore, then mark ourselves as invalid. - if (m_exe_ctx_ref.HasThreadRef()) + if (!accept_invalid_exe_ctx) { - ThreadSP thread_sp (m_exe_ctx_ref.GetThreadSP()); - if (thread_sp) + if (m_exe_ctx_ref.HasThreadRef()) { - if (m_exe_ctx_ref.HasFrameRef()) + ThreadSP thread_sp (m_exe_ctx_ref.GetThreadSP()); + if (thread_sp) { - StackFrameSP frame_sp (m_exe_ctx_ref.GetFrameSP()); - if (!frame_sp) + if (m_exe_ctx_ref.HasFrameRef()) { - // We used to have a frame, but now it is gone - SetInvalid(); - changed = was_valid; + StackFrameSP frame_sp (m_exe_ctx_ref.GetFrameSP()); + if (!frame_sp) + { + // We used to have a frame, but now it is gone + SetInvalid(); + changed = was_valid; + } } } + else + { + // We used to have a thread, but now it is gone + SetInvalid(); + changed = was_valid; + } } - else - { - // We used to have a thread, but now it is gone - SetInvalid(); - changed = was_valid; - } - } + return changed; } diff --git a/source/Core/ValueObjectChild.cpp b/source/Core/ValueObjectChild.cpp index 2b3c8344af8f6..c1e45e1f48de8 100644 --- a/source/Core/ValueObjectChild.cpp +++ b/source/Core/ValueObjectChild.cpp @@ -110,6 +110,14 @@ ValueObjectChild::GetDisplayTypeName() } bool +ValueObjectChild::CanUpdateWithInvalidExecutionContext () +{ + if (m_parent) + return m_parent->CanUpdateWithInvalidExecutionContext(); + return this->ValueObject::CanUpdateWithInvalidExecutionContext(); +} + +bool ValueObjectChild::UpdateValue () { m_error.Clear(); diff --git a/source/Core/ValueObjectConstResult.cpp b/source/Core/ValueObjectConstResult.cpp index e8b477a74a526..b4e6303064691 100644 --- a/source/Core/ValueObjectConstResult.cpp +++ b/source/Core/ValueObjectConstResult.cpp @@ -259,7 +259,7 @@ ValueObjectConstResult::GetByteSize() ExecutionContext exe_ctx(GetExecutionContextRef()); if (m_byte_size == 0) - SetByteSize(GetClangType().GetByteSize(&exe_ctx)); + SetByteSize(GetClangType().GetByteSize(exe_ctx.GetBestExecutionContextScope())); return m_byte_size; } diff --git a/source/Core/ValueObjectSyntheticFilter.cpp b/source/Core/ValueObjectSyntheticFilter.cpp index e266267981b8b..867b520162982 100644 --- a/source/Core/ValueObjectSyntheticFilter.cpp +++ b/source/Core/ValueObjectSyntheticFilter.cpp @@ -7,8 +7,6 @@ // //===----------------------------------------------------------------------===// -#include "lldb/lldb-python.h" - #include "lldb/Core/ValueObjectSyntheticFilter.h" // C Includes @@ -304,3 +302,15 @@ ValueObjectSynthetic::SetValueFromCString (const char *value_str, Error& error) { return m_parent->SetValueFromCString(value_str, error); } + +void +ValueObjectSynthetic::SetFormat (lldb::Format format) +{ + if (m_parent) + { + m_parent->ClearUserVisibleData(eClearUserVisibleDataItemsAll); + m_parent->SetFormat(format); + } + this->ValueObject::SetFormat(format); + this->ClearUserVisibleData(eClearUserVisibleDataItemsAll); +} diff --git a/source/Core/ValueObjectVariable.cpp b/source/Core/ValueObjectVariable.cpp index ed2aeb3d9634c..c86bece42eb89 100644 --- a/source/Core/ValueObjectVariable.cpp +++ b/source/Core/ValueObjectVariable.cpp @@ -112,7 +112,7 @@ ValueObjectVariable::GetByteSize() if (!type.IsValid()) return 0; - return type.GetByteSize(&exe_ctx); + return type.GetByteSize(exe_ctx.GetBestExecutionContextScope()); } lldb::ValueType |