llvm-project/lldb/source/Core/FastDemangle.cpp
Jason Molenda 62e0681afb Add -Wimplicit-fallthrough command line option to clang in
the xcode project file to catch switch statements that have a
case that falls through unintentionally.

Define LLVM_FALLTHROUGH to indicate instances where a case has code
and intends to fall through.  This should be in llvm/Support/Compiler.h;
Peter Collingbourne originally checked in there (r237766), then
reverted (r237941) because he didn't have time to mark up all the
'case' statements that were intended to fall through.  I put together
a patch to get this back in llvm http://reviews.llvm.org/D17063 but
it hasn't been approved in the past week.  I added a new
lldb-private-defines.h to hold the definition for now.

Every place in lldb where there is a comment that the fall-through
is intentional, I added LLVM_FALLTHROUGH to silence the warning.
I haven't tried to identify whether the fallthrough is a bug or
not in the other places.

I haven't tried to add this to the cmake option build flags.
This warning will only work for clang.

This build cleanly (with some new warnings) on macosx with clang
under xcodebuild, but if this causes problems for people on other
configurations, I'll back it out.

llvm-svn: 260930
2016-02-16 04:14:33 +00:00

2419 lines
78 KiB
C++

//===-- FastDemangle.cpp ----------------------------------------*- C++ -*-===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "lldb/lldb-private.h"
//#define DEBUG_FAILURES 1
//#define DEBUG_SUBSTITUTIONS 1
//#define DEBUG_TEMPLATE_ARGS 1
//#define DEBUG_HIGHWATER 1
//#define DEBUG_REORDERING 1
namespace {
/// @brief Represents the collection of qualifiers on a type
enum Qualifiers
{
QualifierNone = 0,
QualifierConst = 1,
QualifierRestrict = 2,
QualifierVolatile = 4,
QualifierReference = 8,
QualifierRValueReference = 16,
QualifierPointer = 32
};
/// @brief Categorizes the recognized operators
enum class OperatorKind
{
Unary,
Postfix,
Binary,
Ternary,
Other,
ConversionOperator,
Vendor,
NoMatch
};
/// @brief Represents one of the recognized two-character operator
/// abbreviations used when parsing operators as names and expressions
struct Operator
{
const char *name;
OperatorKind kind;
};
/// @brief Represents a range of characters in the output buffer, typically for
/// use with RewriteRange()
struct BufferRange
{
int offset;
int length;
};
/// @brief Transient state required while parsing a name
struct NameState
{
bool parse_function_params;
bool is_last_generic;
bool has_no_return_type;
BufferRange last_name_range;
};
/// @brief LLDB's fast C++ demangler
///
/// This is an incomplete implementation designed to speed up the demangling
/// process that is often a bottleneck when LLDB stops a process for the first
/// time. Where the implementation doesn't know how to demangle a symbol it
/// fails gracefully to allow the caller to fall back to the existing demangler.
///
/// Over time the full mangling spec should be supported without compromising
/// performance for the most common cases.
class SymbolDemangler
{
public:
//----------------------------------------------------
// Public API
//----------------------------------------------------
/// @brief Create a SymbolDemangler
///
/// The newly created demangler allocates and owns scratch memory sufficient
/// for demangling typical symbols. Additional memory will be allocated if
/// needed and managed by the demangler instance.
SymbolDemangler()
{
m_buffer = (char *) malloc(8192);
m_buffer_end = m_buffer + 8192;
m_owns_buffer = true;
m_rewrite_ranges = (BufferRange *) malloc(128 * sizeof (BufferRange));
m_rewrite_ranges_size = 128;
m_owns_m_rewrite_ranges = true;
}
/// @brief Create a SymbolDemangler that uses provided scratch memory
///
/// The provided memory is not owned by the demangler. It will be
/// overwritten during calls to GetDemangledCopy() but can be used for
/// other purposes between calls. The provided memory will not be freed
/// when this instance is destroyed.
///
/// If demangling a symbol requires additional space it will be allocated
/// and managed by the demangler instance.
///
/// @param storage_ptr Valid pointer to at least storage_size bytes of
/// space that the SymbolDemangler can use during demangling
///
/// @param storage_size Number of bytes of space available scratch memory
/// referenced by storage_ptr
SymbolDemangler(void *storage_ptr, int storage_size)
{
// Use up to 1/8th of the provided space for rewrite ranges
m_rewrite_ranges_size = (storage_size >> 3) / sizeof (BufferRange);
m_rewrite_ranges = (BufferRange *) storage_ptr;
m_owns_m_rewrite_ranges = false;
// Use the rest for the character buffer
m_buffer = (char *) storage_ptr + m_rewrite_ranges_size * sizeof (BufferRange);
m_buffer_end = (const char *)storage_ptr + storage_size;
m_owns_buffer = false;
}
/// @brief Destroys the SymbolDemangler and deallocates any scratch
/// memory that it owns
~SymbolDemangler()
{
if (m_owns_buffer)
free(m_buffer);
if (m_owns_m_rewrite_ranges)
free(m_rewrite_ranges);
}
#ifdef DEBUG_HIGHWATER
int highwater_store = 0;
int highwater_buffer = 0;
#endif
/// @brief Parses the provided mangled name and returns a newly allocated
/// demangling
///
/// @param mangled_name Valid null-terminated C++ mangled name following
/// the Itanium C++ ABI mangling specification as implemented by Clang
///
/// @result Newly allocated null-terminated demangled name when demangling
/// is successful, and nullptr when demangling fails. The caller is
/// responsible for freeing the allocated memory.
char *
GetDemangledCopy(const char *mangled_name,
long mangled_name_length = 0)
{
if (!ParseMangling(mangled_name, mangled_name_length))
return nullptr;
#ifdef DEBUG_HIGHWATER
int rewrite_count = m_next_substitute_index +
(m_rewrite_ranges_size - 1 - m_next_template_arg_index);
int buffer_size = (int)(m_write_ptr - m_buffer);
if (rewrite_count > highwater_store)
highwater_store = rewrite_count;
if (buffer_size > highwater_buffer)
highwater_buffer = buffer_size;
#endif
int length = (int)(m_write_ptr - m_buffer);
char *copy = (char *)malloc(length + 1);
memcpy(copy, m_buffer, length);
copy[length] = '\0';
return copy;
}
private:
//----------------------------------------------------
// Grow methods
//
// Manage the storage used during demangling
//----------------------------------------------------
void GrowBuffer(long min_growth = 0)
{
// By default, double the size of the buffer
long growth = m_buffer_end - m_buffer;
// Avoid growing by more than 1MB at a time
if (growth > 1 << 20)
growth = 1 << 20;
// ... but never grow by less than requested,
// or 1K, whichever is greater
if (min_growth < 1024)
min_growth = 1024;
if (growth < min_growth)
growth = min_growth;
// Allocate the new m_buffer and migrate content
long new_size = (m_buffer_end - m_buffer) + growth;
char *new_buffer = (char *) malloc(new_size);
memcpy(new_buffer, m_buffer, m_write_ptr - m_buffer);
if (m_owns_buffer)
free(m_buffer);
m_owns_buffer = true;
// Update references to the new buffer
m_write_ptr = new_buffer + (m_write_ptr - m_buffer);
m_buffer = new_buffer;
m_buffer_end = m_buffer + new_size;
}
void
GrowRewriteRanges()
{
// By default, double the size of the array
int growth = m_rewrite_ranges_size;
// Apply reasonable minimum and maximum sizes for growth
if (growth > 128)
growth = 128;
if (growth < 16)
growth = 16;
// Allocate the new array and migrate content
int bytes = (m_rewrite_ranges_size + growth) * sizeof (BufferRange);
BufferRange *new_ranges = (BufferRange *) malloc (bytes);
for (int index = 0; index < m_next_substitute_index; index++)
{
new_ranges[index] = m_rewrite_ranges[index];
}
for (int index = m_rewrite_ranges_size - 1;
index > m_next_template_arg_index; index--)
{
new_ranges[index + growth] = m_rewrite_ranges[index];
}
if (m_owns_m_rewrite_ranges)
free(m_rewrite_ranges);
m_owns_m_rewrite_ranges = true;
// Update references to the new array
m_rewrite_ranges = new_ranges;
m_rewrite_ranges_size += growth;
m_next_template_arg_index += growth;
}
//----------------------------------------------------
// Range and state management
//----------------------------------------------------
int
GetStartCookie()
{
return (int)(m_write_ptr - m_buffer);
}
BufferRange
EndRange(int start_cookie)
{
return { start_cookie, (int)(m_write_ptr - (m_buffer + start_cookie)) };
}
void
ReorderRange(BufferRange source_range, int insertion_point_cookie)
{
// Ensure there's room the preserve the source range
if (m_write_ptr + source_range.length > m_buffer_end)
{
GrowBuffer(m_write_ptr + source_range.length - m_buffer_end);
}
// Reorder the content
memcpy(m_write_ptr, m_buffer + source_range.offset, source_range.length);
memmove(m_buffer + insertion_point_cookie + source_range.length,
m_buffer + insertion_point_cookie,
source_range.offset - insertion_point_cookie);
memcpy(m_buffer + insertion_point_cookie, m_write_ptr, source_range.length);
// Fix up rewritable ranges, covering both substitutions and templates
int index = 0;
while (true)
{
if (index == m_next_substitute_index)
index = m_next_template_arg_index + 1;
if (index == m_rewrite_ranges_size)
break;
// Affected ranges are either shuffled forward when after the
// insertion but before the source, or backward when inside the
// source
int candidate_offset = m_rewrite_ranges[index].offset;
if (candidate_offset >= insertion_point_cookie)
{
if (candidate_offset < source_range.offset)
{
m_rewrite_ranges[index].offset += source_range.length;
}
else if (candidate_offset >= source_range.offset)
{
m_rewrite_ranges[index].offset -= (source_range.offset - insertion_point_cookie);
}
}
++index;
}
}
void
EndSubstitution(int start_cookie)
{
if (m_next_substitute_index == m_next_template_arg_index)
GrowRewriteRanges();
int index = m_next_substitute_index++;
m_rewrite_ranges[index] = EndRange(start_cookie);
#ifdef DEBUG_SUBSTITUTIONS
printf("Saved substitution # %d = %.*s\n", index,
m_rewrite_ranges[index].length, m_buffer + start_cookie);
#endif
}
void
EndTemplateArg(int start_cookie)
{
if (m_next_substitute_index == m_next_template_arg_index)
GrowRewriteRanges();
int index = m_next_template_arg_index--;
m_rewrite_ranges[index] = EndRange(start_cookie);
#ifdef DEBUG_TEMPLATE_ARGS
printf("Saved template arg # %d = %.*s\n",
m_rewrite_ranges_size - index - 1,
m_rewrite_ranges[index].length, m_buffer + start_cookie);
#endif
}
void
ResetTemplateArgs()
{
//TODO: this works, but is it the right thing to do?
// Should we push/pop somehow at the call sites?
m_next_template_arg_index = m_rewrite_ranges_size - 1;
}
//----------------------------------------------------
// Write methods
//
// Appends content to the existing output buffer
//----------------------------------------------------
void
Write(char character)
{
if (m_write_ptr == m_buffer_end)
GrowBuffer();
*m_write_ptr++ = character;
}
void
Write(const char *content)
{
Write(content, strlen(content));
}
void
Write(const char *content, long content_length)
{
char *end_m_write_ptr = m_write_ptr + content_length;
if (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);
m_write_ptr = end_m_write_ptr;
}
#define WRITE(x) Write(x, sizeof (x) - 1)
void
WriteTemplateStart()
{
Write('<');
}
void
WriteTemplateEnd()
{
// Put a space between terminal > characters when nesting templates
if (m_write_ptr != m_buffer && *(m_write_ptr - 1) == '>')
WRITE(" >");
else Write('>');
}
void
WriteCommaSpace()
{
WRITE(", ");
}
void
WriteNamespaceSeparator()
{
WRITE("::");
}
void
WriteStdPrefix()
{
WRITE("std::");
}
void
WriteQualifiers(int qualifiers, bool space_before_reference = true)
{
if (qualifiers & QualifierPointer)
Write('*');
if (qualifiers & QualifierConst)
WRITE(" const");
if (qualifiers & QualifierVolatile)
WRITE(" volatile");
if (qualifiers & QualifierRestrict)
WRITE(" restrict");
if (qualifiers & QualifierReference)
{
if (space_before_reference)
WRITE(" &");
else Write('&');
}
if (qualifiers & QualifierRValueReference)
{
if (space_before_reference)
WRITE(" &&");
else WRITE("&&");
}
}
//----------------------------------------------------
// Rewrite methods
//
// Write another copy of content already present
// earlier in the output buffer
//----------------------------------------------------
void
RewriteRange(BufferRange range)
{
Write(m_buffer + range.offset, range.length);
}
bool
RewriteSubstitution(int index)
{
if (index < 0 || index >= m_next_substitute_index)
{
#ifdef DEBUG_FAILURES
printf("*** Invalid substitution #%d\n", index);
#endif
return false;
}
RewriteRange(m_rewrite_ranges[index]);
return true;
}
bool
RewriteTemplateArg(int template_index)
{
int index = m_rewrite_ranges_size - 1 - template_index;
if (template_index < 0 || index <= m_next_template_arg_index)
{
#ifdef DEBUG_FAILURES
printf("*** Invalid template arg reference #%d\n", template_index);
#endif
return false;
}
RewriteRange(m_rewrite_ranges[index]);
return true;
}
//----------------------------------------------------
// TryParse methods
//
// Provide information with return values instead of
// writing to the output buffer
//
// Values indicating failure guarantee that the pre-
// call m_read_ptr is unchanged
//----------------------------------------------------
int
TryParseNumber()
{
unsigned char digit = *m_read_ptr - '0';
if (digit > 9)
return -1;
int count = digit;
while (true)
{
digit = *++m_read_ptr - '0';
if (digit > 9)
break;
count = count * 10 + digit;
}
return count;
}
int
TryParseBase36Number()
{
char digit = *m_read_ptr;
int count;
if (digit >= '0' && digit <= '9')
count = digit -= '0';
else if (digit >= 'A' && digit <= 'Z')
count = digit -= ('A' - 10);
else return -1;
while (true)
{
digit = *++m_read_ptr;
if (digit >= '0' && digit <= '9')
digit -= '0';
else if (digit >= 'A' && digit <= 'Z')
digit -= ('A' - 10);
else break;
count = count * 36 + digit;
}
return count;
}
// <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
const char *
TryParseBuiltinType()
{
switch (*m_read_ptr++)
{
case 'v': return "void";
case 'w': return "wchar_t";
case 'b': return "bool";
case 'c': return "char";
case 'a': return "signed char";
case 'h': return "unsigned char";
case 's': return "short";
case 't': return "unsigned short";
case 'i': return "int";
case 'j': return "unsigned int";
case 'l': return "long";
case 'm': return "unsigned long";
case 'x': return "long long";
case 'y': return "unsigned long long";
case 'n': return "__int128";
case 'o': return "unsigned __int128";
case 'f': return "float";
case 'd': return "double";
case 'e': return "long double";
case 'g': return "__float128";
case 'z': return "...";
case 'D':
{
switch (*m_read_ptr++)
{
case 'd': return "decimal64";
case 'e': return "decimal128";
case 'f': return "decimal32";
case 'h': return "decimal16";
case 'i': return "char32_t";
case 's': return "char16_t";
case 'a': return "auto";
case 'c': return "decltype(auto)";
case 'n': return "std::nullptr_t";
default:
--m_read_ptr;
}
}
}
--m_read_ptr;
return nullptr;
}
// <operator-name>
// ::= aa # &&
// ::= ad # & (unary)
// ::= an # &
// ::= aN # &=
// ::= aS # =
// ::= cl # ()
// ::= cm # ,
// ::= co # ~
// ::= da # delete[]
// ::= de # * (unary)
// ::= dl # delete
// ::= dv # /
// ::= dV # /=
// ::= eo # ^
// ::= eO # ^=
// ::= eq # ==
// ::= ge # >=
// ::= gt # >
// ::= ix # []
// ::= le # <=
// ::= ls # <<
// ::= lS # <<=
// ::= lt # <
// ::= mi # -
// ::= mI # -=
// ::= ml # *
// ::= mL # *=
// ::= mm # -- (postfix in <expression> context)
// ::= na # new[]
// ::= ne # !=
// ::= ng # - (unary)
// ::= nt # !
// ::= nw # new
// ::= oo # ||
// ::= or # |
// ::= oR # |=
// ::= pm # ->*
// ::= pl # +
// ::= pL # +=
// ::= pp # ++ (postfix in <expression> context)
// ::= ps # + (unary)
// ::= pt # ->
// ::= qu # ?
// ::= rm # %
// ::= rM # %=
// ::= rs # >>
// ::= rS # >>=
// ::= cv <type> # (cast)
// ::= v <digit> <source-name> # vendor extended operator
Operator
TryParseOperator()
{
switch (*m_read_ptr++)
{
case 'a':
switch (*m_read_ptr++)
{
case 'a': return { "&&", OperatorKind::Binary };
case 'd': return { "&", OperatorKind::Unary };
case 'n': return { "&", OperatorKind::Binary };
case 'N': return { "&=", OperatorKind::Binary };
case 'S': return { "=", OperatorKind::Binary };
}
--m_read_ptr;
break;
case 'c':
switch (*m_read_ptr++)
{
case 'l': return { "()", OperatorKind::Other };
case 'm': return { ",", OperatorKind::Other };
case 'o': return { "~", OperatorKind::Unary };
case 'v': return { nullptr, OperatorKind::ConversionOperator };
}
--m_read_ptr;
break;
case 'd':
switch (*m_read_ptr++)
{
case 'a': return { " delete[]", OperatorKind::Other };
case 'e': return { "*", OperatorKind::Unary };
case 'l': return { " delete", OperatorKind::Other };
case 'v': return { "/", OperatorKind::Binary };
case 'V': return { "/=", OperatorKind::Binary };
}
--m_read_ptr;
break;
case 'e':
switch (*m_read_ptr++)
{
case 'o': return { "^", OperatorKind::Binary };
case 'O': return { "^=", OperatorKind::Binary };
case 'q': return { "==", OperatorKind::Binary };
}
--m_read_ptr;
break;
case 'g':
switch (*m_read_ptr++)
{
case 'e': return { ">=", OperatorKind::Binary };
case 't': return { ">", OperatorKind::Binary };
}
--m_read_ptr;
break;
case 'i':
switch (*m_read_ptr++)
{
case 'x': return { "[]", OperatorKind::Other };
}
--m_read_ptr;
break;
case 'l':
switch (*m_read_ptr++)
{
case 'e': return { "<=", OperatorKind::Binary };
case 's': return { "<<", OperatorKind::Binary };
case 'S': return { "<<=", OperatorKind::Binary };
case 't': return { "<", OperatorKind::Binary };
// case 'i': return { "?", OperatorKind::Binary };
}
--m_read_ptr;
break;
case 'm':
switch (*m_read_ptr++)
{
case 'i': return { "-", OperatorKind::Binary };
case 'I': return { "-=", OperatorKind::Binary };
case 'l': return { "*", OperatorKind::Binary };
case 'L': return { "*=", OperatorKind::Binary };
case 'm': return { "--", OperatorKind::Postfix };
}
--m_read_ptr;
break;
case 'n':
switch (*m_read_ptr++)
{
case 'a': return { " new[]", OperatorKind::Other };
case 'e': return { "!=", OperatorKind::Binary };
case 'g': return { "-", OperatorKind::Unary };
case 't': return { "!", OperatorKind::Unary };
case 'w': return { " new", OperatorKind::Other };
}
--m_read_ptr;
break;
case 'o':
switch (*m_read_ptr++)
{
case 'o': return { "||", OperatorKind::Binary };
case 'r': return { "|", OperatorKind::Binary };
case 'R': return { "|=", OperatorKind::Binary };
}
--m_read_ptr;
break;
case 'p':
switch (*m_read_ptr++)
{
case 'm': return { "->*", OperatorKind::Binary };
case 's': return { "+", OperatorKind::Unary };
case 'l': return { "+", OperatorKind::Binary };
case 'L': return { "+=", OperatorKind::Binary };
case 'p': return { "++", OperatorKind::Postfix };
case 't': return { "->", OperatorKind::Binary };
}
--m_read_ptr;
break;
case 'q':
switch (*m_read_ptr++)
{
case 'u': return { "?", OperatorKind::Ternary };
}
--m_read_ptr;
break;
case 'r':
switch (*m_read_ptr++)
{
case 'm': return { "%", OperatorKind::Binary };
case 'M': return { "%=", OperatorKind::Binary };
case 's': return { ">>", OperatorKind::Binary };
case 'S': return { ">=", OperatorKind::Binary };
}
--m_read_ptr;
break;
case 'v':
char digit = *m_read_ptr;
if (digit >= '0' && digit <= '9')
{
m_read_ptr++;
return { nullptr, OperatorKind::Vendor };
}
--m_read_ptr;
break;
}
--m_read_ptr;
return { nullptr, OperatorKind::NoMatch };
}
// <CV-qualifiers> ::= [r] [V] [K]
// <ref-qualifier> ::= R # & ref-qualifier
// <ref-qualifier> ::= O # && ref-qualifier
int
TryParseQualifiers(bool allow_cv, bool allow_ro)
{
int qualifiers = QualifierNone;
char next = *m_read_ptr;
if (allow_cv)
{
if (next == 'r') // restrict
{
qualifiers |= QualifierRestrict;
next = *++m_read_ptr;
}
if (next == 'V') // volatile
{
qualifiers |= QualifierVolatile;
next = *++m_read_ptr;
}
if (next == 'K') // const
{
qualifiers |= QualifierConst;
next = *++m_read_ptr;
}
}
if (allow_ro)
{
if (next == 'R')
{
++m_read_ptr;
qualifiers |= QualifierReference;
}
else if (next =='O')
{
++m_read_ptr;
qualifiers |= QualifierRValueReference;
}
}
return qualifiers;
}
// <discriminator> := _ <non-negative number> # when number < 10
// := __ <non-negative number> _ # when number >= 10
// extension := decimal-digit+
int
TryParseDiscriminator()
{
const char *discriminator_start = m_read_ptr;
// Test the extension first, since it's what Clang uses
int discriminator_value = TryParseNumber();
if (discriminator_value != -1)
return discriminator_value;
char next = *m_read_ptr;
if (next == '_')
{
next = *++m_read_ptr;
if (next == '_')
{
++m_read_ptr;
discriminator_value = TryParseNumber();
if (discriminator_value != -1 && *m_read_ptr++ != '_')
{
return discriminator_value;
}
}
else if (next >= '0' && next <= '9')
{
++m_read_ptr;
return next - '0';
}
}
// Not a valid discriminator
m_read_ptr = discriminator_start;
return -1;
}
//----------------------------------------------------
// Parse methods
//
// Consume input starting from m_read_ptr and produce
// buffered output at m_write_ptr
//
// Failures return false and may leave m_read_ptr in an
// indeterminate state
//----------------------------------------------------
bool
Parse(char character)
{
if (*m_read_ptr++ == character)
return true;
#ifdef DEBUG_FAILURES
printf("*** Expected '%c'\n", character);
#endif
return false;
}
// <number> ::= [n] <non-negative decimal integer>
bool
ParseNumber(bool allow_negative = false)
{
if (allow_negative && *m_read_ptr == 'n')
{
Write('-');
++m_read_ptr;
}
const char *before_digits = m_read_ptr;
while (true)
{
unsigned char digit = *m_read_ptr - '0';
if (digit > 9)
break;
++m_read_ptr;
}
if (int digit_count = (int)(m_read_ptr - before_digits))
{
Write(before_digits, digit_count);
return true;
}
#ifdef DEBUG_FAILURES
printf("*** Expected number\n");
#endif
return false;
}
// <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> >
bool
ParseSubstitution()
{
const char *substitution;
switch (*m_read_ptr)
{
case 'a': substitution = "std::allocator"; break;
case 'b': substitution = "std::basic_string"; break;
case 's': substitution = "std::string"; break;
case 'i': substitution = "std::istream"; break;
case 'o': substitution = "std::ostream"; break;
case 'd': substitution = "std::iostream"; break;
default:
// A failed attempt to parse a number will return -1 which turns out to be
// perfect here as S_ is the first substitution, S0_ the next and so forth
int substitution_index = TryParseBase36Number();
if (*m_read_ptr++ != '_')
{
#ifdef DEBUG_FAILURES
printf("*** Expected terminal _ in substitution\n");
#endif
return false;
}
return RewriteSubstitution (substitution_index + 1);
}
Write(substitution);
++m_read_ptr;
return true;
}
// <function-type> ::= F [Y] <bare-function-type> [<ref-qualifier>] E
//
// <bare-function-type> ::= <signature type>+ # types are possible return type, then parameter types
bool
ParseFunctionType (int inner_qualifiers = QualifierNone)
{
#ifdef DEBUG_FAILURES
printf("*** Function types not supported\n");
#endif
//TODO: first steps toward an implementation follow, but they're far
// from complete. Function types tend to bracket other types eg:
// int (*)() when used as the type for "name" becomes int (*name)().
// This makes substitution et al ... interesting.
return false;
#if 0 // TODO
if (*m_read_ptr == 'Y')
++m_read_ptr;
int return_type_start_cookie = GetStartCookie();
if (!ParseType())
return false;
Write(' ');
int insert_cookie = GetStartCookie();
Write('(');
bool first_param = true;
int qualifiers = QualifierNone;
while (true)
{
switch (*m_read_ptr)
{
case 'E':
++m_read_ptr;
Write(')');
break;
case 'v':
++m_read_ptr;
continue;
case 'R':
case 'O':
if (*(m_read_ptr + 1) == 'E')
{
qualifiers = TryParseQualifiers (false, true);
Parse('E');
break;
}
// fallthrough
default:
{
if (first_param)
first_param = false;
else WriteCommaSpace();
if (!ParseType())
return false;
continue;
}
}
break;
}
if (qualifiers)
{
WriteQualifiers (qualifiers);
EndSubstitution (return_type_start_cookie);
}
if (inner_qualifiers)
{
int qualifier_start_cookie = GetStartCookie();
Write ('(');
WriteQualifiers (inner_qualifiers);
Write (')');
ReorderRange (EndRange (qualifier_start_cookie), insert_cookie);
}
return true;
#endif // TODO
}
// <array-type> ::= A <positive dimension number> _ <element type>
// ::= A [<dimension expression>] _ <element type>
bool
ParseArrayType(int qualifiers = QualifierNone)
{
#ifdef DEBUG_FAILURES
printf("*** Array type unsupported\n");
#endif
//TODO: We fail horribly when recalling these as substitutions or
// templates and trying to constify them eg:
// _ZN4llvm2cl5applyIA28_cNS0_3optIbLb0ENS0_6parserIbEEEEEEvRKT_PT0_
//
//TODO: Chances are we don't do any better with references and pointers
// that should be type (&) [] instead of type & []
return false;
#if 0 // TODO
if (*m_read_ptr == '_')
{
++m_read_ptr;
if (!ParseType())
return false;
if (qualifiers)
WriteQualifiers(qualifiers);
WRITE(" []");
return true;
}
else
{
const char *before_digits = m_read_ptr;
if (TryParseNumber() != -1)
{
const char *after_digits = m_read_ptr;
if (!Parse('_'))
return false;
if (!ParseType())
return false;
if (qualifiers)
WriteQualifiers(qualifiers);
Write(' ');
Write('[');
Write(before_digits, after_digits - before_digits);
}
else
{
int type_insertion_cookie = GetStartCookie();
if (!ParseExpression())
return false;
if (!Parse('_'))
return false;
int type_start_cookie = GetStartCookie();
if (!ParseType())
return false;
if (qualifiers)
WriteQualifiers(qualifiers);
Write(' ');
Write('[');
ReorderRange (EndRange (type_start_cookie), type_insertion_cookie);
}
Write(']');
return true;
}
#endif // TODO
}
// <pointer-to-member-type> ::= M <class type> <member type>
//TODO: Determine how to handle pointers to function members correctly,
// currently not an issue because we don't have function types at all...
bool
ParsePointerToMemberType()
{
int insertion_cookie = GetStartCookie();
Write(' ');
if (!ParseType())
return false;
WRITE("::*");
int type_cookie = GetStartCookie();
if (!ParseType())
return false;
ReorderRange (EndRange (type_cookie), insertion_cookie);
return true;
}
// <template-param> ::= T_ # first template parameter
// ::= T <parameter-2 non-negative number> _
bool
ParseTemplateParam()
{
int count = TryParseNumber();
if (!Parse('_'))
return false;
// When no number is present we get -1, which is convenient since
// T_ is the zeroth element T0_ is element 1, and so on
return RewriteTemplateArg (count + 1);
}
// <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>
bool
ParseType()
{
#ifdef DEBUG_FAILURES
const char *failed_type = m_read_ptr;
#endif
int type_start_cookie = GetStartCookie();
bool suppress_substitution = false;
int qualifiers = TryParseQualifiers (true, false);
switch (*m_read_ptr)
{
case 'D':
++m_read_ptr;
switch (*m_read_ptr++)
{
case 'p':
if (!ParseType())
return false;
break;
case 'T':
case 't':
case 'v':
default:
#ifdef DEBUG_FAILURES
printf("*** Unsupported type: %.3s\n", failed_type);
#endif
return false;
}
break;
case 'T':
++m_read_ptr;
if (!ParseTemplateParam())
return false;
break;
case 'M':
++m_read_ptr;
if (!ParsePointerToMemberType())
return false;
break;
case 'A':
++m_read_ptr;
if (!ParseArrayType())
return false;
break;
case 'F':
++m_read_ptr;
if (!ParseFunctionType())
return false;
break;
case 'S':
if (*++m_read_ptr == 't')
{
++m_read_ptr;
WriteStdPrefix();
if (!ParseName())
return false;
}
else
{
suppress_substitution = true;
if (!ParseSubstitution())
return false;
}
break;
case 'P':
{
switch (*++m_read_ptr)
{
case 'F':
++m_read_ptr;
if (!ParseFunctionType(QualifierPointer))
return false;
break;
default:
if (!ParseType())
return false;
Write('*');
break;
}
break;
}
case 'R':
{
++m_read_ptr;
if (!ParseType())
return false;
Write('&');
break;
}
case 'O':
{
++m_read_ptr;
if (!ParseType())
return false;
Write('&');
Write('&');
break;
}
case 'C':
case 'G':
case 'U':
#ifdef DEBUG_FAILURES
printf("*** Unsupported type: %.3s\n", failed_type);
#endif
return false;
// Test for common cases to avoid TryParseBuiltinType() overhead
case 'N':
case 'Z':
case 'L':
if (!ParseName())
return false;
break;
default:
if (const char *builtin = TryParseBuiltinType())
{
Write(builtin);
suppress_substitution = true;
}
else
{
if (!ParseName())
return false;
}
break;
}
// Allow base substitutions to be suppressed, but always record
// substitutions for the qualified variant
if (!suppress_substitution)
EndSubstitution(type_start_cookie);
if (qualifiers)
{
WriteQualifiers(qualifiers, false);
EndSubstitution(type_start_cookie);
}
return true;
}
// <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
bool
ParseUnnamedTypeName(NameState & name_state)
{
switch (*m_read_ptr++)
{
case 't':
{
int cookie = GetStartCookie();
WRITE("'unnamed");
const char *before_digits = m_read_ptr;
if (TryParseNumber() != -1) Write (before_digits,
m_read_ptr - before_digits);
if (!Parse('_'))
return false;
Write('\'');
name_state.last_name_range = EndRange (cookie);
return true;
}
case 'b':
{
int cookie = GetStartCookie();
WRITE("'block");
const char *before_digits = m_read_ptr;
if (TryParseNumber() != -1) Write (before_digits,
m_read_ptr - before_digits);
if (!Parse('_'))
return false;
Write('\'');
name_state.last_name_range = EndRange (cookie);
return true;
}
case 'l':
#ifdef DEBUG_FAILURES
printf("*** Lambda type names unsupported\n");
#endif
return false;
}
#ifdef DEBUG_FAILURES
printf("*** Unknown unnamed type %.3s\n", m_read_ptr - 2);
#endif
return false;
}
// <ctor-dtor-name> ::= C1 # complete object constructor
// ::= C2 # base object constructor
// ::= C3 # complete object allocating constructor
bool
ParseCtor(NameState & name_state)
{
char next = *m_read_ptr;
if (next == '1' || next == '2' || next == '3' || next == '5')
{
RewriteRange (name_state.last_name_range);
name_state.has_no_return_type = true;
++m_read_ptr;
return true;
}
#ifdef DEBUG_FAILURES
printf("*** Broken constructor\n");
#endif
return false;
}
// <ctor-dtor-name> ::= D0 # deleting destructor
// ::= D1 # complete object destructor
// ::= D2 # base object destructor
bool
ParseDtor(NameState & name_state)
{
char next = *m_read_ptr;
if (next == '0' || next == '1' || next == '2' || next == '5')
{
Write('~');
RewriteRange(name_state.last_name_range);
name_state.has_no_return_type = true;
++m_read_ptr;
return true;
}
#ifdef DEBUG_FAILURES
printf("*** Broken destructor\n");
#endif
return false;
}
// See TryParseOperator()
bool
ParseOperatorName(NameState & name_state)
{
#ifdef DEBUG_FAILURES
const char *operator_ptr = m_read_ptr;
#endif
Operator parsed_operator = TryParseOperator();
if (parsed_operator.name)
{
WRITE("operator");
Write(parsed_operator.name);
return true;
}
// Handle special operators
switch (parsed_operator.kind)
{
case OperatorKind::Vendor:
WRITE("operator ");
return ParseSourceName();
case OperatorKind::ConversionOperator:
ResetTemplateArgs();
name_state.has_no_return_type = true;
WRITE("operator ");
return ParseType();
default:
#ifdef DEBUG_FAILURES
printf("*** Unknown operator: %.2s\n", operator_ptr);
#endif
return false;
}
}
// <source-name> ::= <positive length number> <identifier>
bool
ParseSourceName()
{
int count = TryParseNumber();
if (count == -1)
{
#ifdef DEBUG_FAILURES
printf("*** Malformed source name, missing length count\n");
#endif
return false;
}
const char *next_m_read_ptr = m_read_ptr + count;
if (next_m_read_ptr > m_read_end)
{
#ifdef DEBUG_FAILURES
printf("*** Malformed source name, premature termination\n");
#endif
return false;
}
if (count >= 10 && strncmp(m_read_ptr, "_GLOBAL__N", 10) == 0)
WRITE("(anonymous namespace)");
else Write(m_read_ptr, count);
m_read_ptr = next_m_read_ptr;
return true;
}
// <unqualified-name> ::= <operator-name>
// ::= <ctor-dtor-name>
// ::= <source-name>
// ::= <unnamed-type-name>
bool
ParseUnqualifiedName(NameState & name_state)
{
// Note that these are detected directly in ParseNestedName for
// performance rather than switching on the same options twice
char next = *m_read_ptr;
switch (next)
{
case 'C':
++m_read_ptr;
return ParseCtor(name_state);
case 'D':
++m_read_ptr;
return ParseDtor(name_state);
case 'U':
++m_read_ptr;
return ParseUnnamedTypeName(name_state);
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
int name_start_cookie = GetStartCookie();
if (!ParseSourceName())
return false;
name_state.last_name_range = EndRange(name_start_cookie);
return true;
}
default:
return ParseOperatorName(name_state);
};
}
// <unscoped-name> ::= <unqualified-name>
// ::= St <unqualified-name> # ::std::
// extension ::= StL<unqualified-name>
bool
ParseUnscopedName(NameState & name_state)
{
if (*m_read_ptr == 'S' && *(m_read_ptr + 1) == 't')
{
WriteStdPrefix();
if (*(m_read_ptr += 2) == 'L')
++m_read_ptr;
}
return ParseUnqualifiedName(name_state);
}
bool
ParseIntegerLiteral(const char *prefix, const char *suffix,
bool allow_negative)
{
if (prefix)
Write(prefix);
if (!ParseNumber(allow_negative))
return false;
if (suffix)
Write(suffix);
return Parse('E');
}
bool
ParseBooleanLiteral()
{
switch (*m_read_ptr++)
{
case '0': WRITE("false"); break;
case '1': WRITE("true"); break;
default:
#ifdef DEBUG_FAILURES
printf("*** Boolean literal not 0 or 1\n");
#endif
return false;
}
return Parse('E');
}
// <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
bool
ParseExpressionPrimary()
{
switch (*m_read_ptr++)
{
case 'b': return ParseBooleanLiteral();
case 'x': return ParseIntegerLiteral(nullptr, "ll", true);
case 'l': return ParseIntegerLiteral(nullptr, "l", true);
case 'i': return ParseIntegerLiteral(nullptr, nullptr, true);
case 'n': return ParseIntegerLiteral("(__int128)", nullptr, true);
case 'j': return ParseIntegerLiteral(nullptr, "u", false);
case 'm': return ParseIntegerLiteral(nullptr, "ul", false);
case 'y': return ParseIntegerLiteral(nullptr, "ull", false);
case 'o': return ParseIntegerLiteral("(unsigned __int128)",
nullptr, false);
case '_':
if (*m_read_ptr++ == 'Z')
{
if (!ParseEncoding())
return false;
return Parse('E');
}
--m_read_ptr;
LLVM_FALLTHROUGH;
case 'w':
case 'c':
case 'a':
case 'h':
case 's':
case 't':
case 'f':
case 'd':
case 'e':
#ifdef DEBUG_FAILURES
printf("*** Unsupported primary expression %.5s\n", m_read_ptr - 1);
#endif
return false;
case 'T':
// Invalid mangled name per
// http://sourcerytools.com/pipermail/cxx-abi-dev/2011-August/002422.html
#ifdef DEBUG_FAILURES
printf("*** Invalid primary expr encoding\n");
#endif
return false;
default:
--m_read_ptr;
Write('(');
if (!ParseType())
return false;
Write(')');
if (!ParseNumber())
return false;
return Parse('E');
}
}
// <unresolved-type> ::= <template-param>
// ::= <decltype>
// ::= <substitution>
bool
ParseUnresolvedType()
{
int type_start_cookie = GetStartCookie();
switch (*m_read_ptr++)
{
case 'T':
if (!ParseTemplateParam())
return false;
EndSubstitution(type_start_cookie);
return true;
case 'S':
{
if (*m_read_ptr != 't')
return ParseSubstitution();
++m_read_ptr;
WriteStdPrefix();
NameState type_name = {};
if (!ParseUnqualifiedName(type_name))
return false;
EndSubstitution(type_start_cookie);
return true;
}
case 'D':
default:
#ifdef DEBUG_FAILURES
printf("*** Unsupported unqualified type: %3s\n", m_read_ptr - 1);
#endif
return false;
}
}
// <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>
bool
ParseBaseUnresolvedName()
{
#ifdef DEBUG_FAILURES
printf("*** Base unresolved name unsupported\n");
#endif
return false;
}
// <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>
bool
ParseUnresolvedName()
{
#ifdef DEBUG_FAILURES
printf("*** Unresolved names not supported\n");
#endif
//TODO: grammar for all of this seems unclear...
return false;
#if 0 // TODO
if (*m_read_ptr == 'g' && *(m_read_ptr + 1) == 's')
{
m_read_ptr += 2;
WriteNamespaceSeparator();
}
#endif // TODO
}
// <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>
bool
ParseExpression()
{
Operator expression_operator = TryParseOperator();
switch (expression_operator.kind)
{
case OperatorKind::Unary:
Write(expression_operator.name);
Write('(');
if (!ParseExpression())
return false;
Write(')');
return true;
case OperatorKind::Binary:
if (!ParseExpression())
return false;
Write(expression_operator.name);
return ParseExpression();
case OperatorKind::Ternary:
if (!ParseExpression())
return false;
Write('?');
if (!ParseExpression())
return false;
Write(':');
return ParseExpression();
case OperatorKind::NoMatch:
break;
case OperatorKind::Other:
default:
#ifdef DEBUG_FAILURES
printf("*** Unsupported operator: %s\n", expression_operator.name);
#endif
return false;
}
switch (*m_read_ptr++)
{
case 'T': return ParseTemplateParam();
case 'L': return ParseExpressionPrimary();
case 's':
if (*m_read_ptr++ == 'r')
return ParseUnresolvedName();
--m_read_ptr;
LLVM_FALLTHROUGH;
default:
return ParseExpressionPrimary();
}
}
// <template-arg> ::= <type> # type or template
// ::= X <expression> E # expression
// ::= <expr-primary> # simple expressions
// ::= J <template-arg>* E # argument pack
// ::= LZ <encoding> E # extension
bool
ParseTemplateArg()
{
switch (*m_read_ptr) {
case 'J':
#ifdef DEBUG_FAILURES
printf("*** Template argument packs unsupported\n");
#endif
return false;
case 'X':
++m_read_ptr;
if (!ParseExpression())
return false;
return Parse('E');
case 'L':
++m_read_ptr;
return ParseExpressionPrimary();
default:
return ParseType();
}
}
// <template-args> ::= I <template-arg>* E
// extension, the abi says <template-arg>+
bool
ParseTemplateArgs(bool record_template_args = false)
{
if (record_template_args)
ResetTemplateArgs();
bool first_arg = true;
while (*m_read_ptr != 'E')
{
if (first_arg)
first_arg = false;
else WriteCommaSpace();
int template_start_cookie = GetStartCookie();
if (!ParseTemplateArg())
return false;
if (record_template_args)
EndTemplateArg(template_start_cookie);
}
++m_read_ptr;
return true;
}
// <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>
//
// <unqualified-name> ::= <operator-name>
// ::= <ctor-dtor-name>
// ::= <source-name>
// ::= <unnamed-type-name>
bool
ParseNestedName(NameState & name_state, bool parse_discriminator = false)
{
int qualifiers = TryParseQualifiers(true, true);
bool first_part = true;
bool suppress_substitution = true;
int name_start_cookie = GetStartCookie();
while (true)
{
char next = *m_read_ptr;
if (next == 'E')
{
++m_read_ptr;
break;
}
// Record a substitution candidate for all prefixes, but not the full name
if (suppress_substitution)
suppress_substitution = false;
else EndSubstitution(name_start_cookie);
if (next == 'I')
{
++m_read_ptr;
name_state.is_last_generic = true;
WriteTemplateStart();
if (!ParseTemplateArgs(name_state.parse_function_params))
return false;
WriteTemplateEnd();
continue;
}
if (first_part)
first_part = false;
else WriteNamespaceSeparator();
name_state.is_last_generic = false;
switch (next)
{
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
{
int name_start_cookie = GetStartCookie();
if (!ParseSourceName())
return false;
name_state.last_name_range = EndRange(name_start_cookie);
continue;
}
case 'S':
if (*++m_read_ptr == 't')
{
WriteStdPrefix();
++m_read_ptr;
if (!ParseUnqualifiedName(name_state))
return false;
}
else
{
if (!ParseSubstitution())
return false;
suppress_substitution = true;
}
continue;
case 'T':
++m_read_ptr;
if (!ParseTemplateParam())
return false;
continue;
case 'C':
++m_read_ptr;
if (!ParseCtor(name_state))
return false;
continue;
case 'D':
{
switch (*(m_read_ptr + 1))
{
case 't':
case 'T':
#ifdef DEBUG_FAILURES
printf("*** Decltype unsupported\n");
#endif
return false;
}
++m_read_ptr;
if (!ParseDtor(name_state))
return false;
continue;
}
case 'U':
++m_read_ptr;
if (!ParseUnnamedTypeName(name_state))
return false;
continue;
case 'L':
++m_read_ptr;
if (!ParseUnqualifiedName(name_state))
return false;
continue;
default:
if (!ParseOperatorName(name_state))
return false;
}
}
if (parse_discriminator)
TryParseDiscriminator();
if (name_state.parse_function_params
&& !ParseFunctionArgs(name_state, name_start_cookie))
{
return false;
}
if (qualifiers)
WriteQualifiers(qualifiers);
return true;
}
// <local-name> := Z <function encoding> E <entity name> [<discriminator>]
// := Z <function encoding> E s [<discriminator>]
// := Z <function encoding> Ed [ <parameter number> ] _ <entity name>
bool
ParseLocalName(bool parse_function_params)
{
if (!ParseEncoding())
return false;
if (!Parse('E'))
return false;
switch (*m_read_ptr)
{
case 's':
++m_read_ptr;
TryParseDiscriminator(); // Optional and ignored
WRITE("::string literal");
break;
case 'd':
++m_read_ptr;
TryParseNumber(); // Optional and ignored
if (!Parse('_'))
return false;
WriteNamespaceSeparator();
if (!ParseName())
return false;
break;
default:
WriteNamespaceSeparator();
if (!ParseName(parse_function_params, true))
return false;
TryParseDiscriminator(); // Optional and ignored
}
return true;
}
// <name> ::= <nested-name>
// ::= <local-name>
// ::= <unscoped-template-name> <template-args>
// ::= <unscoped-name>
// <unscoped-template-name> ::= <unscoped-name>
// ::= <substitution>
bool
ParseName(bool parse_function_params = false,
bool parse_discriminator = false)
{
NameState name_state = { parse_function_params, false, false, {0, 0}};
int name_start_cookie = GetStartCookie();
switch (*m_read_ptr)
{
case 'N':
++m_read_ptr;
return ParseNestedName(name_state, parse_discriminator);
case 'Z':
{
++m_read_ptr;
if (!ParseLocalName(parse_function_params))
return false;
break;
}
case 'L':
++m_read_ptr;
LLVM_FALLTHROUGH;
default:
{
if (!ParseUnscopedName(name_state))
return false;
if (*m_read_ptr == 'I')
{
EndSubstitution(name_start_cookie);
++m_read_ptr;
name_state.is_last_generic = true;
WriteTemplateStart();
if (!ParseTemplateArgs(parse_function_params))
return false;
WriteTemplateEnd();
}
break;
}
}
if (parse_discriminator)
TryParseDiscriminator();
if (parse_function_params &&
!ParseFunctionArgs(name_state, name_start_cookie))
{
return false;
}
return true;
}
// <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
bool
ParseCallOffset()
{
switch (*m_read_ptr++)
{
case 'h':
if (*m_read_ptr == 'n')
++m_read_ptr;
if (TryParseNumber() == -1 || *m_read_ptr++ != '_')
break;
return true;
case 'v':
if (*m_read_ptr == 'n')
++m_read_ptr;
if (TryParseNumber() == -1 || *m_read_ptr++ != '_')
break;
if (*m_read_ptr == 'n')
++m_read_ptr;
if (TryParseNumber() == -1 || *m_read_ptr++ != '_')
break;
return true;
}
#ifdef DEBUG_FAILURES
printf("*** Malformed call offset\n");
#endif
return false;
}
// <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
// extension ::= TC <first type> <number> _ <second type> # construction vtable for second-in-first
bool
ParseSpecialNameT()
{
switch (*m_read_ptr++)
{
case 'V':
WRITE("vtable for ");
return ParseType();
case 'T':
WRITE("VTT for ");
return ParseType();
case 'I':
WRITE("typeinfo for ");
return ParseType();
case 'S':
WRITE("typeinfo name for ");
return ParseType();
case 'c':
case 'C':
#ifdef DEBUG_FAILURES
printf("*** Unsupported thunk or construction vtable name: %.3s\n", m_read_ptr - 1);
#endif
return false;
default:
if (*--m_read_ptr == 'v')
{
WRITE("virtual thunk to ");
}
else
{
WRITE("non-virtual thunk to ");
}
if (!ParseCallOffset())
return false;
return ParseEncoding();
}
}
// <special-name> ::= GV <object name> # Guard variable for one-time initialization
// # No <type>
// extension ::= GR <object name> # reference temporary for object
bool
ParseSpecialNameG()
{
switch (*m_read_ptr++)
{
case 'V':
WRITE("guard variable for ");
if (!ParseName(true))
return false;
break;
case 'R':
WRITE("reference temporary for ");
if (!ParseName(true))
return false;
break;
default:
#ifdef DEBUG_FAILURES
printf("*** Unknown G encoding\n");
#endif
return false;
}
return true;
}
// <bare-function-type> ::= <signature type>+ # types are possible return type, then parameter types
bool
ParseFunctionArgs(NameState & name_state, int return_insert_cookie)
{
char next = *m_read_ptr;
if (next == 'E' || next == '\0' || next == '.')
return true;
// Clang has a bad habit of making unique manglings by just sticking numbers on the end of a symbol,
// which is ambiguous with malformed source name manglings
const char *before_clang_uniquing_test = m_read_ptr;
if (TryParseNumber())
{
if (*m_read_ptr == '\0')
return true;
m_read_ptr = before_clang_uniquing_test;
}
if (name_state.is_last_generic && !name_state.has_no_return_type)
{
int return_type_start_cookie = GetStartCookie();
if (!ParseType())
return false;
Write(' ');
ReorderRange(EndRange(return_type_start_cookie),
return_insert_cookie);
}
Write('(');
bool first_param = true;
while (true)
{
switch (*m_read_ptr)
{
case '\0':
case 'E':
case '.':
break;
case 'v':
++m_read_ptr;
continue;
case '_':
// Not a formal part of the mangling specification, but clang emits suffixes starting with _block_invoke
if (strncmp(m_read_ptr, "_block_invoke", 13) == 0)
{
m_read_ptr += strlen(m_read_ptr);
break;
}
LLVM_FALLTHROUGH;
default:
if (first_param)
first_param = false;
else WriteCommaSpace();
if (!ParseType())
return false;
continue;
}
break;
}
Write(')');
return true;
}
// <encoding> ::= <function name> <bare-function-type>
// ::= <data name>
// ::= <special-name>
bool
ParseEncoding()
{
switch (*m_read_ptr)
{
case 'T':
++m_read_ptr;
if (!ParseSpecialNameT())
return false;
break;
case 'G':
++m_read_ptr;
if (!ParseSpecialNameG())
return false;
break;
default:
if (!ParseName(true))
return false;
break;
}
return true;
}
bool
ParseMangling(const char *mangled_name, long mangled_name_length = 0)
{
if (!mangled_name_length)
mangled_name_length = strlen(mangled_name);
m_read_end = mangled_name + mangled_name_length;
m_read_ptr = mangled_name;
m_write_ptr = m_buffer;
m_next_substitute_index = 0;
m_next_template_arg_index = m_rewrite_ranges_size - 1;
if (*m_read_ptr++ != '_' || *m_read_ptr++ != 'Z')
{
#ifdef DEBUG_FAILURES
printf("*** Missing _Z prefix\n");
#endif
return false;
}
if (!ParseEncoding())
return false;
switch (*m_read_ptr)
{
case '.':
Write(' ');
Write('(');
Write(m_read_ptr, m_read_end - m_read_ptr);
Write(')');
case '\0':
return true;
default:
#ifdef DEBUG_FAILURES
printf("*** Unparsed mangled content\n");
#endif
return false;
}
}
private:
// External scratch storage used during demanglings
char *m_buffer;
const char *m_buffer_end;
BufferRange *m_rewrite_ranges;
int m_rewrite_ranges_size;
bool m_owns_buffer;
bool m_owns_m_rewrite_ranges;
// Internal state used during demangling
const char *m_read_ptr;
const char *m_read_end;
char *m_write_ptr;
int m_next_template_arg_index;
int m_next_substitute_index;
};
} // Anonymous namespace
// Public entry points referenced from Mangled.cpp
namespace lldb_private
{
char *
FastDemangle(const char *mangled_name)
{
char buffer[16384];
SymbolDemangler demangler(buffer, sizeof (buffer));
return demangler.GetDemangledCopy(mangled_name);
}
char *
FastDemangle(const char *mangled_name, long mangled_name_length)
{
char buffer[16384];
SymbolDemangler demangler(buffer, sizeof (buffer));
return demangler.GetDemangledCopy(mangled_name, mangled_name_length);
}
} // lldb_private namespace