This patch implements the change discussed in libobjc/50428.  Traditionally, 
the Apple runtime "inherits" +initialize,
meaning that if a class doesn't implement it, but the superclass does, the 
superclass's implementation gets executed
the first time a method of the class is invoked.  Instead, the GNU runtime 
traditionally doesn't "inherit" +initialize,
meaning in that situation, no +initialize is executed.  The idea is that with 
the GNU runtime you wouldn't have to worry
about protecting your +initialize against multiple executions.  But, in 
practice, people seem to protect +initialize methods all
the same even with the GNU runtime, and they supply the following two 
explanations of why:

 * It is generally considered good practice to protect +initialize methods 
against multiple exclusions because it allows
the code to run without changes with the Apple/NeXT runtime.

 * The runtime doesn't prevent programmers from invoking +initialize directly.  
You are not supposed to do it, but some beginners
do, which is why a number of frameworks (including GNUstep) protect all 
+initialize methods against multiple executions all the
same even when using the GNU runtime and even ignoring the existence of the 
Apple/NeXT runtime (!!).

Because of these two reasons, it seems that everyone's really protecting their 
+initialize methods against multiple executions
all the same!  So the difference in behaviour between the GNU runtime and the 
Apple/NeXT one is not really helping anyone;
but it makes it harder to port code written for the Apple/NeXT runtime to the 
GNU runtime.  Programmers writing code for the
Apple/NeXT runtime assume that +initialize methods are "inherited", so they may 
write code which expects such methods to
be called multiple times, once per subclass.  That code would compile on the 
GNU runtime but not work.  Fixing the code so
that it works with both runtimes is not necessarily trivial in some situations, 
for example if the classes are dynamically
created at runtime.  So the difference in behaviour is not really helping 
anyone, but it's making life hard for some people who
are porting software to run on the GNU runtime.

This patch (originally submitted by Richard Frith-Macdonald in a private email, 
and revised by me) changes the behaviour of the
GNU runtime to match the one of the Apple/NeXT one.  This makes it easier to 
port software to use the GNU runtime.

A testcase is included; I tested the patch on an Apple Mac OS X 10.6.8, making 
sure that the testcase behaves in the same way
with the Apple/NeXT and GNU runtimes.  Committed to trunk.

Thanks

PS: This change absolutely deserves a mention in the GCC 4.7 release notes.  
But I'll work on the release notes separately,
once we are in stage 2 or so. :-)

Index: gcc/doc/objc.texi
===================================================================
--- gcc/doc/objc.texi   (revision 179710)
+++ gcc/doc/objc.texi   (working copy)
@@ -635,7 +635,8 @@ following class does this:
 
 + (void)initialize
 @{
-  class_ivar_set_gcinvisible (self, "weakPointer", YES);
+  if (self == objc_lookUpClass ("WeakPointer"))
+    class_ivar_set_gcinvisible (self, "weakPointer", YES);
 @}
 
 - initWithPointer:(const void*)p
Index: gcc/ChangeLog
===================================================================
--- gcc/ChangeLog       (revision 179710)
+++ gcc/ChangeLog       (working copy)
@@ -1,3 +1,9 @@
+2011-10-08  Nicola Pero  <nicola.p...@meta-innovation.com>
+
+       PR libobjc/50428
+       * doc/objc.texi (Garbage Collection): Updated example to protect
+       +initialize against execution in subclasses.
+
 2011-10-07  Richard Henderson  <r...@redhat.com>
 
        * doc/extend.texi (__builtin_shuffle): Improve the description to
Index: gcc/testsuite/ChangeLog
===================================================================
--- gcc/testsuite/ChangeLog     (revision 179710)
+++ gcc/testsuite/ChangeLog     (working copy)
@@ -1,3 +1,8 @@
+2011-10-08  Nicola Pero  <nicola.p...@meta-innovation.com>
+
+       PR libobjc/50428        
+       * objc/execute/initialize-1.m: New test.
+
 2011-10-08  Paul Thomas  <pa...@gcc.gnu.org>
 
        PR fortran/47844
Index: gcc/testsuite/objc/execute/initialize-1.m
===================================================================
--- gcc/testsuite/objc/execute/initialize-1.m   (revision 0)
+++ gcc/testsuite/objc/execute/initialize-1.m   (revision 0)
@@ -0,0 +1,47 @@
+/* Contributed by Nicola Pero - Sat  8 Oct 2011 16:47:48 BST */
+#include <objc/objc.h>
+
+/* Test that if a class has no +initialize method, the superclass
+   implementation is called.  */
+
+static int class_variable = 0;
+
+@interface TestClass
+{
+  Class isa;
+}
++ (void) initialize;
++ (int) classVariable;
+@end
+
+@implementation TestClass
++ (void) initialize
+{
+  class_variable++;
+}
++ (int) classVariable
+{
+  return class_variable;
+}
+@end
+
+@interface TestSubClass : TestClass
+@end
+
+@implementation TestSubClass
+@end
+
+int main (void)
+{
+  if ([TestClass classVariable] != 1)
+    {
+      abort ();
+    }
+
+  if ([TestSubClass classVariable] != 2)
+    {
+      abort ();
+    }
+
+  return 0;
+}
Index: libobjc/sendmsg.c
===================================================================
--- libobjc/sendmsg.c   (revision 179710)
+++ libobjc/sendmsg.c   (working copy)
@@ -516,34 +516,13 @@ __objc_send_initialize (Class class)
 
       {
        SEL op = sel_registerName ("initialize");
-       IMP imp = 0;
-        struct objc_method_list * method_list = class->class_pointer->methods;
-       
-        while (method_list)
+        struct objc_method *method = search_for_method_in_hierarchy 
(class->class_pointer, 
+                                                                    op);
+
+       if (method)
          {
-           int i;
-           struct objc_method * method;
-           
-           for (i = 0; i < method_list->method_count; i++)
-             {
-               method = &(method_list->method_list[i]);
-               if (method->method_name
-                   && method->method_name->sel_id == op->sel_id)
-                 {
-                   imp = method->method_imp;
-                   break;
-                 }
-             }
-           
-           if (imp)
-             break;
-           
-           method_list = method_list->method_next;
-         }
-       if (imp)
-         {
            DEBUG_PRINTF (" begin of [%s +initialize]\n", class->name);
-           (*imp) ((id) class, op);
+           (*method->method_imp) ((id)class, op);
            DEBUG_PRINTF (" end of [%s +initialize]\n", class->name);
          }
 #ifdef DEBUG
Index: libobjc/ChangeLog
===================================================================
--- libobjc/ChangeLog   (revision 179710)
+++ libobjc/ChangeLog   (working copy)
@@ -1,3 +1,14 @@
+2011-10-08  Richard Frith-Macdonald <r...@gnu.org>
+            Nicola Pero  <nicola.p...@meta-innovation.com>
+
+       PR libobjc/50428
+       * sendmsg.c (__objc_send_initialize): If a class does not have an
+       +initialize method, search for an +initialize method in the
+       superclass and in the ancestor classes and execute the first one
+       that is found.  This makes the GNU runtime behave in the same way
+       as the Apple/NeXT runtime with respect to +initialize methods and
+       subclassing.
+
 2011-08-06  Nicola Pero  <nicola.p...@meta-innovation.com>
 
        PR libobjc/50002


Reply via email to