Add partial C++0x support and recognition of GCC function clone suffixes to
author <hlsyounes@gmail.com> <>
Mon, 7 Feb 2011 14:43:05 +0000 (14:43 +0000)
committer <hlsyounes@gmail.com> <>
Mon, 7 Feb 2011 14:43:05 +0000 (14:43 +0000)
demangle.cc.  Fixes issue 80.

Make svn ignore autom4te.cache.

git-svn-id: https://google-glog.googlecode.com/svn/trunk@91 eb4d4688-79bd-11dd-afb4-1d65580434c0

src/demangle.cc
src/demangle_unittest.cc
src/demangle_unittest.sh

index 46556bf..cc7cebb 100644 (file)
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 //
 // Author: Satoru Takabayashi
+//
+// For reference check out:
+// http://www.codesourcery.com/public/cxx-abi/abi.html#mangling
+//
+// Note that we only have partial C++0x support yet.
 
 #include <stdio.h>  // for NULL
 #include "demangle.h"
@@ -245,6 +250,15 @@ static bool OneOrMore(ParseFunc parse_func, State *state) {
   return false;
 }
 
+// This function is used for handling <non-terminal>* syntax. The function
+// always returns true and must be followed by a termination symbol or a
+// terminating sequence not handled by parse_func (e.g. ParseChar(state, 'E')).
+static bool ZeroOrMore(ParseFunc parse_func, State *state) {
+  while (parse_func(state)) {
+  }
+  return true;
+}
+
 // Append "str" at "out_cur".  If there is an overflow, "overflowed"
 // is set to true for later use.  The output string is ensured to
 // always terminate with '\0' as long as there is no overflow.
@@ -273,6 +287,36 @@ static bool IsAlpha(char c) {
   return ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'));
 }
 
+static bool IsDigit(char c) {
+  return c >= '0' && c <= '9';
+}
+
+// Returns true if "str" is a function clone suffix.  These suffixes are used
+// by GCC 4.5.x and later versions to indicate functions which have been
+// cloned during optimization.  We treat any sequence (.<alpha>+.<digit>+)+ as
+// a function clone suffix.
+static bool IsFunctionCloneSuffix(const char *str) {
+  size_t i = 0;
+  while (str[i] != '\0') {
+    // Consume a single .<alpha>+.<digit>+ sequence.
+    if (str[i] != '.' || !IsAlpha(str[i + 1])) {
+      return false;
+    }
+    i += 2;
+    while (IsAlpha(str[i])) {
+      ++i;
+    }
+    if (str[i] != '.' || !IsDigit(str[i + 1])) {
+      return false;
+    }
+    i += 2;
+    while (IsDigit(str[i])) {
+      ++i;
+    }
+  }
+  return true;  // Consumed everything in "str".
+}
+
 // Append "str" with some tweaks, iff "append" state is true.
 // Returns true so that it can be placed in "if" conditions.
 static void MaybeAppendWithLength(State *state, const char * const str,
@@ -429,6 +473,10 @@ static bool ParseSubstitution(State *state);
 // <mangled-name> ::= _Z <encoding>
 static bool ParseMangledName(State *state) {
   if (ParseTwoChar(state, "_Z") && ParseEncoding(state)) {
+    // Drop trailing function clone suffix, if any.
+    if (IsFunctionCloneSuffix(state->mangled_cur)) {
+      state->mangled_cur = state->mangled_end;
+    }
     // Append trailing version suffix if any.
     // ex. _Z3foo@@GLIBCXX_3.4
     if (state->mangled_cur < state->mangled_end &&
@@ -596,7 +644,7 @@ static bool ParseNumber(State *state) {
   const char *p = state->mangled_cur;
   int number = 0;
   for (;p < state->mangled_end; ++p) {
-    if ((*p >= '0' && *p <= '9')) {
+    if (IsDigit(*p)) {
       number = number * 10 + (*p - '0');
     } else {
       break;
@@ -616,7 +664,7 @@ static bool ParseFloatNumber(State *state) {
   const char *p = state->mangled_cur;
   int number = 0;
   for (;p < state->mangled_end; ++p) {
-    if ((*p >= '0' && *p <= '9')) {
+    if (IsDigit(*p)) {
       number = number * 16 + (*p - '0');
     } else if (*p >= 'a' && *p <= 'f') {
       number = number * 16 + (*p - 'a' + 10);
@@ -638,7 +686,7 @@ static bool ParseSeqId(State *state) {
   const char *p = state->mangled_cur;
   int number = 0;
   for (;p < state->mangled_end; ++p) {
-    if ((*p >= '0' && *p <= '9')) {
+    if (IsDigit(*p)) {
       number = number * 36 + (*p - '0');
     } else if (*p >= 'A' && *p <= 'Z') {
       number = number * 36 + (*p - 'A' + 10);
@@ -858,11 +906,12 @@ static bool ParseCtorDtorName(State *state) {
 }
 
 // <type> ::= <CV-qualifiers> <type>
-//        ::= P <type>
-//        ::= R <type>
-//        ::= C <type>
-//        ::= G <type>
-//        ::= U <source-name> <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)
+//        ::= U <source-name> <type>  # vendor extended type qualifier
 //        ::= <builtin-type>
 //        ::= <function-type>
 //        ::= <class-enum-type>
@@ -871,6 +920,11 @@ static bool ParseCtorDtorName(State *state) {
 //        ::= <template-template-param> <template-args>
 //        ::= <template-param>
 //        ::= <substitution>
+//        ::= Dp <type>          # pack expansion of (C++0x)
+//        ::= Dt <expression> E  # decltype of an id-expression or class
+//                               # member access (C++0x)
+//        ::= DT <expression> E  # decltype of an expression (C++0x)
+//
 static bool ParseType(State *state) {
   // We should check CV-qualifers, and PRGC things first.
   State copy = *state;
@@ -879,7 +933,18 @@ static bool ParseType(State *state) {
   }
   *state = copy;
 
-  if (ParseCharClass(state, "PRCG") && ParseType(state)) {
+  if (ParseCharClass(state, "OPRCG") && ParseType(state)) {
+    return true;
+  }
+  *state = copy;
+
+  if (ParseTwoChar(state, "Dp") && ParseType(state)) {
+    return true;
+  }
+  *state = copy;
+
+  if (ParseChar(state, 'D') && ParseCharClass(state, "tT") &&
+      ParseExpression(state) && ParseChar(state, 'E')) {
     return true;
   }
   *state = copy;
@@ -1045,14 +1110,23 @@ static bool ParseTemplateArgs(State *state) {
 
 // <template-arg>  ::= <type>
 //                 ::= <expr-primary>
+//                 ::= I <template-arg>* E        # argument pack
 //                 ::= X <expression> E
 static bool ParseTemplateArg(State *state) {
+  State copy = *state;
+  if (ParseChar(state, 'I') &&
+      ZeroOrMore(ParseTemplateArg, state) &&
+      ParseChar(state, 'E')) {
+    return true;
+  }
+  *state = copy;
+
   if (ParseType(state) ||
       ParseExprPrimary(state)) {
     return true;
   }
+  *state = copy;
 
-  State copy = *state;
   if (ParseChar(state, 'X') && ParseExpression(state) &&
       ParseChar(state, 'E')) {
     return true;
index c07332a..9d219e6 100644 (file)
@@ -71,6 +71,32 @@ TEST(Demangle, CornerCases) {
   EXPECT_FALSE(Demangle("_Z6foobarv", NULL, 0));  // Should not cause SEGV.
 }
 
+// Test handling of functions suffixed with .clone.N, which is used by GCC
+// 4.5.x, and .constprop.N and .isra.N, which are used by GCC 4.6.x.  These
+// suffixes are used to indicate functions which have been cloned during
+// optimization.  We ignore these suffixes.
+TEST(Demangle, Clones) {
+  char tmp[20];
+  EXPECT_TRUE(Demangle("_ZL3Foov", tmp, sizeof(tmp)));
+  EXPECT_STREQ("Foo()", tmp);
+  EXPECT_TRUE(Demangle("_ZL3Foov.clone.3", tmp, sizeof(tmp)));
+  EXPECT_STREQ("Foo()", tmp);
+  EXPECT_TRUE(Demangle("_ZL3Foov.constprop.80", tmp, sizeof(tmp)));
+  EXPECT_STREQ("Foo()", tmp);
+  EXPECT_TRUE(Demangle("_ZL3Foov.isra.18", tmp, sizeof(tmp)));
+  EXPECT_STREQ("Foo()", tmp);
+  EXPECT_TRUE(Demangle("_ZL3Foov.isra.2.constprop.18", tmp, sizeof(tmp)));
+  EXPECT_STREQ("Foo()", tmp);
+  // Invalid (truncated), should not demangle.
+  EXPECT_FALSE(Demangle("_ZL3Foov.clo", tmp, sizeof(tmp)));
+  // Invalid (.clone. not followed by number), should not demangle.
+  EXPECT_FALSE(Demangle("_ZL3Foov.clone.", tmp, sizeof(tmp)));
+  // Invalid (.clone. followed by non-number), should not demangle.
+  EXPECT_FALSE(Demangle("_ZL3Foov.clone.foo", tmp, sizeof(tmp)));
+  // Invalid (.constprop. not followed by number), should not demangle.
+  EXPECT_FALSE(Demangle("_ZL3Foov.isra.2.constprop.", tmp, sizeof(tmp)));
+}
+
 TEST(Demangle, FromFile) {
   string test_file = FLAGS_test_srcdir + "/src/demangle_unittest.txt";
   ifstream f(test_file.c_str());  // The file should exist.
index 4e79538..91deee2 100755 (executable)
@@ -84,7 +84,10 @@ fi
 
 # Check if mangled symbols exist.  They must not exist.
 if grep --quiet '^_Z' "$DM_OUTPUT"; then
-    die "Mangled symbols found in $DM_OUTPUT"
+    MANGLED=`grep '^_Z' "$DM_OUTPUT" | wc -l | awk '{ print \$1 }'`
+    echo "Mangled symbols ($MANGLED out of $NM_LINES) found in $DM_OUTPUT:"
+    grep '^_Z' "$DM_OUTPUT"
+    die "Mangled symbols ($MANGLED out of $NM_LINES) found in $DM_OUTPUT"
 fi
 
 # All C++ symbols are demangled successfully.