diff mbox

ObjC/ObjC++ - demangling of method and function names

Message ID 1287839816.023829417@192.168.2.228
State New
Headers show

Commit Message

Nicola Pero Oct. 23, 2010, 1:16 p.m. UTC
This patch does a few things:

 * it fixes demangling C++ function names when compiling Objective-C++ (eg, __PRETTY_FUNCTION__ inside a C++
member function)

 * it fixes demangling Objective-C/Objective-C++ method names when they have no arguments and contain a '_' 
(not as the last character though).  It's impossible to always get demangling right, but I managed
to at least get "-(id)test_method" to demangle right. ;-)

 * it fixes Objective-C++ error messages to say "In function '-[NSObject release]' ..." instead of 
"In function '_i_NSObject__release' ..." :-)

 * it adds comments about the problems and ideas about how we could do the whole Objective-C/Objective-C++
pretty printing of method names much better (could be for next release).

Three testcases are included, none of which would pass before this patch.  A fourth testcase for the 
"In function '-[NSObject release]' ..." is included but has a TODO for the actual check because that line 
is automatically filtered out by the test suite.  It's a minor TODO and I can fix that in stage 3 or for next release 
maybe (didn't want to spend hours looking at the testsuite right now).  (the testcase would actually pass, it's a
testsuite issue)

Ok to apply to trunk ?

Thanks

PS: This does not fix the "'release' is deprecated" (instead of "'-[NSObject release]' is deprecated") message.  That
may be yet another patch.

In gcc/cp/:
2010-10-23  Nicola Pero  <nicola.pero@meta-innovation.com>

        * tree.c (cxx_printable_name_internal): In Objective-C++, call
        objc_maybe_printable_name.

In gcc/objc/:
2010-10-23  Nicola Pero  <nicola.pero@meta-innovation.com>

        * objc-act.c (OBJC_GEN_METHOD_LABEL): Updated comments.
        (objc_demangle): Return NULL if demangling can not be done because
        the string to demangle is not an Objective-C mangled method name.
        Be smarter in demangling method names so that at least for methods
        with no arguments we are able to almost always demangle '_' correctly.
        Updated comments.
        (objc_maybe_printable_name): New.
        (objc_printable_name): Call objc_maybe_printable_name.  If it
        returns NULL, call cxx_printable_name in Objective-C++.

In gcc/testsuite/:
2010-10-23  Nicola Pero  <nicola.pero@meta-innovation.com>

        * objc.dg/demangle-1.m: New test.
        * obj-c++.dg/demangle-1.mm: New test.
        * obj-c++.dg/demangle-2.mm: New test.
        * obj-c++.dg/demangle-3.mm: New test.   

In gcc/c-family/:
2010-10-23  Nicola Pero  <nicola.pero@meta-innovation.com>

        * c-common.h (objc_maybe_printable_name): New.
        * stub-objc.c (objc_maybe_printable_name): New.

Comments

Mike Stump Oct. 23, 2010, 4:43 p.m. UTC | #1
On Oct 23, 2010, at 6:16 AM, Nicola Pero wrote:
> Ok to apply to trunk ?

Ok.
diff mbox

Patch

Index: c-family/ChangeLog
===================================================================
--- c-family/ChangeLog	(revision 165881)
+++ c-family/ChangeLog	(working copy)
@@ -1,3 +1,8 @@ 
+2010-10-23  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* c-common.h (objc_maybe_printable_name): New.
+	* stub-objc.c (objc_maybe_printable_name): New.
+
 2010-10-22  Artjoms Sinkarovs <artyom.shinakroff@gmail.com>
 	Andrew Pinski <pinskia@gmail.com>
 
Index: c-family/c-common.h
===================================================================
--- c-family/c-common.h	(revision 165881)
+++ c-family/c-common.h	(working copy)
@@ -1048,6 +1048,7 @@  extern tree objc_build_getter_call (tree, tree);
 extern tree objc_build_setter_call (tree, tree);
 extern void objc_add_synthesize_declaration (location_t, tree);
 extern void objc_add_dynamic_declaration (location_t, tree);
+extern const char * objc_maybe_printable_name (tree, int);
 
 /* The following are provided by the C and C++ front-ends, and called by
    ObjC/ObjC++.  */
Index: c-family/stub-objc.c
===================================================================
--- c-family/stub-objc.c	(revision 165881)
+++ c-family/stub-objc.c	(working copy)
@@ -359,6 +359,13 @@  objc_add_dynamic_declaration (location_t ARG_UNUSE
 {
 }
 
+const char *
+objc_maybe_printable_name (tree ARG_UNUSED (decl), 
+			   int ARG_UNUSED (v))
+{
+  return NULL;
+}
+
 tree
 objc_build_throw_stmt (location_t ARG_UNUSED (loc), tree ARG_UNUSED (expr))
 {
Index: objc/objc-act.c
===================================================================
--- objc/objc-act.c	(revision 165881)
+++ objc/objc-act.c	(working copy)
@@ -75,9 +75,14 @@  bool in_late_binary_op = false;
 #endif  /* OBJCPLUS */
 
 /* This is the default way of generating a method name.  */
-/* I am not sure it is really correct.
-   Perhaps there's a danger that it will make name conflicts
-   if method names contain underscores. -- rms.  */
+/* This has the problem that "test_method:argument:" and
+   "test:method_argument:" will generate the same name
+   ("_i_Test__test_method_argument_" for an instance method of the
+   class "Test"), so you can't have them both in the same class!
+   Moreover, the demangling (going from
+   "_i_Test__test_method_argument" back to the original name) is
+   undefined because there are two correct ways of demangling the
+   name.  */
 #ifndef OBJC_GEN_METHOD_LABEL
 #define OBJC_GEN_METHOD_LABEL(BUF, IS_INST, CLASS_NAME, CAT_NAME, SEL_NAME, NUM) \
   do {					    \
@@ -10615,8 +10620,65 @@  dump_interface (FILE *fp, tree chain)
   fprintf (fp, "@end\n");
 }
 
-/* Demangle function for Objective-C */
+#if 0
+/* Produce the pretty printing for an Objective-C method.  This is
+   currently unused, but could be handy while reorganizing the pretty
+   printing to be more robust.  */
 static const char *
+objc_pretty_print_method (bool is_class_method,
+			  const char *class_name,
+			  const char *category_name,
+			  const char *selector)
+{
+  if (category_name)
+    {
+      char *result = XNEWVEC (char, strlen (class_name) + strlen (category_name) 
+			      + strlen (selector) + 7);
+
+      if (is_class_method)
+	sprintf (result, "+[%s(%s) %s]", class_name, category_name, selector);
+      else
+	sprintf (result, "-[%s(%s) %s]", class_name, category_name, selector);
+
+      return result;
+    }
+  else
+    {
+      char *result = XNEWVEC (char, strlen (class_name)
+			      + strlen (selector) + 5);
+
+      if (is_class_method)
+	sprintf (result, "+[%s %s]", class_name, selector);
+      else
+	sprintf (result, "-[%s %s]", class_name, selector);
+
+      return result;      
+    }
+}
+#endif
+
+/* Demangle function for Objective-C.  Attempt to demangle the
+   function name associated with a method (eg, going from
+   "_i_NSObject__class" to "-[NSObject class]"); usually for the
+   purpose of pretty printing or error messages.  Return the demangled
+   name, or NULL if the string is not an Objective-C mangled method
+   name.
+
+   Because of how the mangling is done, any method that has a '_' in
+   its original name is at risk of being demangled incorrectly.  In
+   some cases there are multiple valid ways to demangle a method name
+   and there is no way we can decide.
+
+   TODO: objc_demangle() can't always get it right; the right way to
+   get this correct for all method names would be to store the
+   Objective-C method name somewhere in the function decl.  Then,
+   there is no demangling to do; we'd just pull the method name out of
+   the decl.  As an additional bonus, when printing error messages we
+   could check for such a method name, and if we find it, we know the
+   function is actually an Objective-C method and we could print error
+   messages saying "In method '+[NSObject class]" instead of "In
+   function '+[NSObject class]" as we do now.  */
+static const char *
 objc_demangle (const char *mangled)
 {
   char *demangled, *cp;
@@ -10638,7 +10700,7 @@  objc_demangle (const char *mangled)
       if (cp == NULL)
 	{
 	  free(demangled);      /* not mangled name */
-	  return mangled;
+	  return NULL;
 	}
       if (cp[1] == '_')  /* easy case: no category name */
 	{
@@ -10652,31 +10714,105 @@  objc_demangle (const char *mangled)
 	  if (cp == 0)
 	    {
 	      free(demangled);    /* not mangled name */
-	      return mangled;
+	      return NULL;
 	    }
 	  *cp++ = ')';
 	  *cp++ = ' ';            /* overwriting 1st char of method name... */
 	  strcpy(cp, mangled + (cp - demangled)); /* get it back */
 	}
+      /* Now we have the method name.  We need to generally replace
+	 '_' with ':' but trying to preserve '_' if it could only have
+	 been in the mangled string because it was already in the
+	 original name.  In cases where it's ambiguous, we assume that
+	 any '_' originated from a ':'.  */
+
+      /* Initial '_'s in method name can't have been generating by
+	 converting ':'s.  Skip them.  */
       while (*cp && *cp == '_')
-	cp++;                   /* skip any initial underbars in method name */
-      for (; *cp; cp++)
-	if (*cp == '_')
-	  *cp = ':';            /* replace remaining '_' with ':' */
+	cp++;
+
+      /* If the method name does not end with '_', then it has no
+	 arguments and there was no replacement of '_'s with ':'s
+	 during mangling.  Check for that case, and skip any
+	 replacement if so.  This at least guarantees that methods
+	 with no arguments are always demangled correctly (unless the
+	 original name ends with '_').  */
+      if (*(mangled + strlen (mangled) - 1) != '_')
+	{
+	  /* Skip to the end.  */
+	  for (; *cp; cp++)
+	    ;
+	}
+      else
+	{
+	  /* Replace remaining '_' with ':'.  This may get it wrong if
+	     there were '_'s in the original name.  In most cases it
+	     is impossible to disambiguate.  */
+	  for (; *cp; cp++)
+	    if (*cp == '_')
+	      *cp = ':';         
+	}
       *cp++ = ']';              /* closing right brace */
       *cp++ = 0;                /* string terminator */
       return demangled;
     }
   else
-    return mangled;             /* not an objc mangled name */
+    return NULL;             /* not an objc mangled name */
 }
 
+/* Try to pretty-print a decl.  If the 'decl' is an Objective-C specific decl,
+   it return the printable name for it.  If not, return NULL.  */
 const char *
-objc_printable_name (tree decl, int kind ATTRIBUTE_UNUSED)
+objc_maybe_printable_name (tree decl, int v ATTRIBUTE_UNUSED)
 {
-  return objc_demangle (IDENTIFIER_POINTER (DECL_NAME (decl)));
+  const char *decl_name = IDENTIFIER_POINTER (DECL_NAME (decl));  
+
+  switch (TREE_CODE (decl))
+    {
+    case FUNCTION_DECL:
+      return objc_demangle (decl_name);
+      break;
+      /* This unusual case (INSTANCE_METHOD_DECL and
+	 CLASS_METHOD_DECL) seems to happen only in ObjC++ and to be a
+	 by-product of the method attribute changes.  It would be nice
+	 to be able to print "-[NSObject autorelease] is deprecated",
+	 but to do that, we'd need to store the class and method name
+	 in the method decl, which we currently don't do.  For now,
+	 just return the name of the method.  We don't return NULL,
+	 because that may trigger further attempts to pretty-print the
+	 decl in C/C++, but they wouldn't know how to pretty-print
+	 it.  */
+    case INSTANCE_METHOD_DECL:
+    case CLASS_METHOD_DECL:
+      return decl_name;
+      break;
+    default:
+      return NULL;
+      break;
+    }
 }
 
+/* Return a printable name for 'decl'.  This first tries
+   objc_maybe_printable_name(), and if that fails, it hands it back to
+   C/C++.  'v' is the verbosity level, as this is a
+   LANG_HOOKS_DECL_PRINTABLE_NAME.  */
+const char *
+objc_printable_name (tree decl, int v)
+{
+  const char *demangled_name = objc_maybe_printable_name (decl, v);
+
+  if (demangled_name != NULL)
+    return demangled_name;
+  else
+    {
+#ifdef OBJCPLUS
+      return cxx_printable_name (decl, v);
+#else
+      return IDENTIFIER_POINTER (DECL_NAME (decl));
+#endif
+    }
+}
+
 static void
 init_objc (void)
 {
Index: objc/ChangeLog
===================================================================
--- objc/ChangeLog	(revision 165881)
+++ objc/ChangeLog	(working copy)
@@ -1,3 +1,15 @@ 
+2010-10-23  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc-act.c (OBJC_GEN_METHOD_LABEL): Updated comments.
+	(objc_demangle): Return NULL if demangling can not be done because
+	the string to demangle is not an Objective-C mangled method name.
+	Be smarter in demangling method names so that at least for methods
+	with no arguments we are able to almost always demangle '_' correctly.
+	Updated comments.
+	(objc_maybe_printable_name): New.
+	(objc_printable_name): Call objc_maybe_printable_name.  If it
+	returns NULL, call cxx_printable_name in Objective-C++.
+
 2010-10-21  Iain Sandoe  <iains@gcc.gnu.org>
 
 	Based on the CFString implementation in FSF apple/trunk branch.
Index: testsuite/ChangeLog
===================================================================
--- testsuite/ChangeLog	(revision 165881)
+++ testsuite/ChangeLog	(working copy)
@@ -1,3 +1,10 @@ 
+2010-10-23  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* objc.dg/demangle-1.m: New test.
+	* obj-c++.dg/demangle-1.mm: New test.
+	* obj-c++.dg/demangle-2.mm: New test.
+	* obj-c++.dg/demangle-3.mm: New test.	
+
 2010-10-23  Jie Zhang  <jie@codesourcery.com>
 
 	PR rtl-optimization/37360
Index: testsuite/objc.dg/demangle-1.m
===================================================================
--- testsuite/objc.dg/demangle-1.m	(revision 0)
+++ testsuite/objc.dg/demangle-1.m	(revision 0)
@@ -0,0 +1,56 @@ 
+/* Test demangling an Objective-C method.  */
+/* { dg-do run } */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <objc/objc.h>
+
+@interface DemangleTest
+{
+  Class isa;
+}
++ (int) testFunction1;
++ (int) test_function2;
++ (int) __testFunction3: (int)unused  andArgument: (char)unused2;
+@end
+
+@implementation DemangleTest
++ (int) testFunction1
+{
+  printf ("%s\n", __PRETTY_FUNCTION__);
+  return strcmp (__PRETTY_FUNCTION__, "+[DemangleTest testFunction1]");
+}
+/* Note that in general, due to how mangling is done, it's impossible
+   to get the demangling right for all functions containing '_' in the
+   name.  But at least we should be able to get that right for single
+   argument ones that don't end with '_', such as the following
+   one.  */
++ (int) test_function2
+{
+  printf ("%s\n", __PRETTY_FUNCTION__);
+  return strcmp (__PRETTY_FUNCTION__, "+[DemangleTest test_function2]");
+}
++ (int) __testFunction3: (int)unused   andArgument: (char)unused2
+{
+  printf ("%s\n", __PRETTY_FUNCTION__);
+  return strcmp (__PRETTY_FUNCTION__, "+[DemangleTest __testFunction3:andArgument:]");
+}
+@end
+
+int main ()
+{
+  if ([DemangleTest testFunction1] != 0)
+      abort ();
+
+  if ([DemangleTest test_function2] != 0)
+    abort ();
+
+  if ([DemangleTest __testFunction3:0 andArgument: 'c'] != 0)
+      abort ();
+
+  return 0;
+}
+
+
Index: testsuite/obj-c++.dg/demangle-1.mm
===================================================================
--- testsuite/obj-c++.dg/demangle-1.mm	(revision 0)
+++ testsuite/obj-c++.dg/demangle-1.mm	(revision 0)
@@ -0,0 +1,35 @@ 
+/* Test demangling a C++ function.  */
+/* { dg-do run } */
+
+#include <cstring>
+#include <cstdlib>
+#include <iostream>
+
+class demangle_test
+{
+public:
+  /* Return 0 if the demangling test succeeds.  */
+  static int test_function1 ()
+  {
+    std::cout << __PRETTY_FUNCTION__ << "\n";
+    return std::strcmp (__PRETTY_FUNCTION__, "static int demangle_test::test_function1()");
+  }
+
+  /* Return 0 if the demangling test succeeds.  */
+  static int test_function2 (int ignored)
+  {
+    std::cout << __PRETTY_FUNCTION__ << "\n";
+    return std::strcmp (__PRETTY_FUNCTION__, "static int demangle_test::test_function2(int)");
+  }
+};
+
+int main ()
+{
+  if (demangle_test::test_function1 () != 0)
+    abort ();
+
+  if (demangle_test::test_function2 (0) != 0)
+    abort ();
+  
+  return 0;
+}
Index: testsuite/obj-c++.dg/demangle-2.mm
===================================================================
--- testsuite/obj-c++.dg/demangle-2.mm	(revision 0)
+++ testsuite/obj-c++.dg/demangle-2.mm	(revision 0)
@@ -0,0 +1,50 @@ 
+/* Test demangling an Objective-C method.  */
+/* { dg-do run } */
+
+#include <cstring>
+#include <cstdlib>
+#include <iostream>
+#include <objc/objc.h>
+
+@interface DemangleTest
+{
+  Class isa;
+}
++ (int) testFunction1;
++ (int) test_function2;
++ (int) __testFunction3: (int)unused  andArgument: (char)unused2;
+@end
+
+@implementation DemangleTest
++ (int) testFunction1
+{
+  std::cout << __PRETTY_FUNCTION__ << "\n";
+  return std::strcmp (__PRETTY_FUNCTION__, "+[DemangleTest testFunction1]");
+}
++ (int) test_function2
+{
+  std::cout << __PRETTY_FUNCTION__ << "\n";
+  return std::strcmp (__PRETTY_FUNCTION__, "+[DemangleTest test_function2]");
+}
++ (int) __testFunction3: (int)unused   andArgument: (char)unused2
+{
+  std::cout << __PRETTY_FUNCTION__ << "\n";
+  return std::strcmp (__PRETTY_FUNCTION__, "+[DemangleTest __testFunction3:andArgument:]");
+}
+@end
+
+int main ()
+{
+  if ([DemangleTest testFunction1] != 0)
+      abort ();
+
+  if ([DemangleTest test_function2] != 0)
+      abort ();
+
+  if ([DemangleTest __testFunction3:0 andArgument: 'c'] != 0)
+      abort ();
+  
+  return 0;
+}
+
+
Index: testsuite/obj-c++.dg/demangle-3.mm
===================================================================
--- testsuite/obj-c++.dg/demangle-3.mm	(revision 0)
+++ testsuite/obj-c++.dg/demangle-3.mm	(revision 0)
@@ -0,0 +1,21 @@ 
+/* Test demangling an Objective-C method in error messages.  */
+/* { dg-do compile } */
+
+#include <objc/objc.h>
+
+@interface DemangleTest
+{
+  Class isa;
+}
++ (int) testFunction1;
+@end
+
+@implementation DemangleTest
++ (int) testFunction1
+{
+  /* TODO: Hack the testsuite so we can test that we get 
+     dg-error "In function .+[DemangleTest testFunction1]."
+     At the moment, the message is filtered out.  */
+  z; /* { dg-error "was not declared" } */
+}
+@end
Index: cp/tree.c
===================================================================
--- cp/tree.c	(revision 165881)
+++ cp/tree.c	(working copy)
@@ -1409,6 +1409,15 @@  cxx_printable_name_internal (tree decl, int v, boo
   static int ring_counter;
   int i;
 
+  /* If doing Objective-C++, give Objective-C a chance to demangle
+     Objective-C method names.  */
+  if (c_dialect_objc ())
+    {
+      const char *demangled = objc_maybe_printable_name (decl, v);
+      if (demangled)
+	return demangled;
+    }
+
   /* Only cache functions.  */
   if (v < 2
       || TREE_CODE (decl) != FUNCTION_DECL
Index: cp/ChangeLog
===================================================================
--- cp/ChangeLog	(revision 165881)
+++ cp/ChangeLog	(working copy)
@@ -1,3 +1,8 @@ 
+2010-10-23  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+	* tree.c (cxx_printable_name_internal): In Objective-C++, call
+	objc_maybe_printable_name.
+
 2010-10-22  Jason Merrill  <jason@redhat.com>
 
 	PR c++/46129