Patchwork =?UTF-8?Q?Fix=20for=20PR=20libobjc/16011=20("+load=20is=20not=20called?= =?UTF-8?Q?=20on=20all=20the=20categories")?=

login
register
mail settings
Submitter Nicola Pero
Date Dec. 21, 2010, 3:34 p.m.
Message ID <1292945678.324529842@192.168.2.228>
Download mbox | patch
Permalink /patch/76302/
State New
Headers show

Comments

Nicola Pero - Dec. 21, 2010, 3:34 p.m.
This patch fixes libobjc/16011.  Testcase included.

The testcase is slightly more complex than usual because we need to compile a couple
of different separate Objective-C files, link them then execute the result (we're testing
that +load is invoked correctly on categories compiled in a separate module than the main
class).  I used the objc.dg/special/special.exp which already had a similar case (a similar
testcase for unclaimed categories).

Committed to trunk.

Thanks

Patch

Index: gcc/testsuite/ChangeLog
===================================================================
--- gcc/testsuite/ChangeLog     (revision 168121)
+++ gcc/testsuite/ChangeLog     (working copy)
@@ -1,3 +1,11 @@ 
+2010-12-21  Nicola Pero  <nicola.pero@meta-innovation.com>
+
+       PR libobjc/16110
+       * objc.dg/special/special.exp: Added new test.
+       * objc.dg/special/load-category-1.m: New.
+       * objc.dg/special/load-category-1a.m: New.
+       * objc.dg/special/load-category-1.h: New.       
+       
 2010-12-21  Steven Bosscher  <steven@gcc.gnu.org>
 
        PR middle-end/45310
Index: gcc/testsuite/objc.dg/special/load-category-1.m
===================================================================
--- gcc/testsuite/objc.dg/special/load-category-1.m     (revision 0)
+++ gcc/testsuite/objc.dg/special/load-category-1.m     (revision 0)
@@ -0,0 +1,40 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+/* { dg-do run } */
+/* { dg-xfail-run-if "Needs OBJC2 ABI" { *-*-darwin* && { lp64 && { ! objc2 } } } { "-fnext-runtime" } { "" } } */
+
+#include <stdlib.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+#include "load-category-1.h"
+
+@implementation TestClass1
++ initialize { return self; }
++ load
+{
+  increase_load_count ();
+}
+@end
+
+@implementation TestClass2 (Category)
++ load
+{
+  increase_load_count ();
+}
+@end
+
+
+static int load_count = 0;
+
+int increase_load_count (void)
+{
+  load_count++;
+}
+
+int main (void)
+{
+  if (load_count != 4)
+    abort ();
+
+  return 0;
+}
Index: gcc/testsuite/objc.dg/special/load-category-1a.m
===================================================================
--- gcc/testsuite/objc.dg/special/load-category-1a.m    (revision 0)
+++ gcc/testsuite/objc.dg/special/load-category-1a.m    (revision 0)
@@ -0,0 +1,22 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+
+#include <stdlib.h>
+#include <objc/objc.h>
+#include <objc/runtime.h>
+
+#include "load-category-1.h"
+
+@implementation TestClass2
++ initialize { return self; }
++ load
+{
+  increase_load_count ();
+}
+@end
+
+@implementation TestClass1 (Category)
++ load
+{
+  increase_load_count ();
+}
+@end
Index: gcc/testsuite/objc.dg/special/special.exp
===================================================================
--- gcc/testsuite/objc.dg/special/special.exp   (revision 168121)
+++ gcc/testsuite/objc.dg/special/special.exp   (working copy)
@@ -1,5 +1,5 @@ 
 # GCC Objective-C testsuite that uses the `dg.exp' driver.
-#   Copyright (C) 1997, 2001, 2007 Free Software Foundation, Inc.
+#   Copyright (C) 1997, 2001, 2007, 2010 Free Software Foundation, Inc.
 
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -56,6 +56,33 @@  if ![string match "" $lines] then {
 }
 }
 
+#
+# load-category-1 test
+#
+# This test is similar to the one above.  We compile load-category-1.m
+# and load-category-1a.m, link them together, and execute the result.
+set add_flags "additional_flags=-I${srcdir}/../../libobjc"
+lappend add_flags "additional_flags=-fgnu-runtime"
+set lines [objc_target_compile "$srcdir/$subdir/load-category-1a.m" "load-category-1a.o" object $add_flags ]
+if ![string match "" $lines] then {
+    fail "load-category-1a.o"
+} else {
+    dg-runtest "$srcdir/$subdir/load-category-1.m" "load-category-1a.o" "-I${srcdir}/../../libobjc -fgnu-runtime"
+    file delete load-category-1a.o
+}
+
+if [istarget "*-*-darwin*" ] {
+set add_flags ""
+lappend add_flags "additional_flags=-fnext-runtime"
+set lines [objc_target_compile "$srcdir/$subdir/load-category-1a.m" "load-category-1a.o" object $add_flags ]
+if ![string match "" $lines] then {
+    fail "load-category-1a.o"
+} else {
+    dg-runtest "$srcdir/$subdir/load-category-1.m" "load-category-1a.o" "-fnext-runtime"
+    file delete load-category-1a.o
+}
+}
+
 # All done.
 dg-finish
 
Index: gcc/testsuite/objc.dg/special/load-category-1.h
===================================================================
--- gcc/testsuite/objc.dg/special/load-category-1.h     (revision 0)
+++ gcc/testsuite/objc.dg/special/load-category-1.h     (revision 0)
@@ -0,0 +1,20 @@ 
+/* Contributed by Nicola Pero <nicola.pero@meta-innovation.com>, December 2010.  */
+
+/* Test that +load works when a category is defined in a different
+   module than the main class.  */
+
+/* This function should be called any time +load is invoked, so we can
+   keep the count.  */
+extern int increase_load_count (void);
+
+@interface TestClass1
+{
+  id isa;
+}
+@end
+
+@interface TestClass2
+{
+  id isa;
+}
+@end
Index: libobjc/init.c
===================================================================
--- libobjc/init.c      (revision 168119)
+++ libobjc/init.c      (working copy)
@@ -44,8 +44,8 @@  see the files COPYING3 and COPYING.RUNTIME respect
 #define OBJC_VERSION 8
 #define PROTOCOL_VERSION 2
 
-/* This list contains all modules currently loaded into the
-   runtime.  */
+/* This list contains modules currently loaded into the runtime and
+   for which the +load method has not been called yet.  */
 static struct objc_list *__objc_module_list = 0;       /* !T:MUTEX */
 
 /* This list contains all proto_list's not yet assigned class
@@ -359,37 +359,49 @@  __objc_tree_print (objc_class_tree *tree, int leve
 #endif
 
 /* Walks on a linked list of methods in the reverse order and executes
-   all the methods corresponding to `op' selector. Walking in the
-   reverse order assures the +load of class is executed first and then
-   +load of categories because of the way in which categories are
-   added to the class methods.  */
+   all the methods corresponding to the `+load' selector.  Walking in
+   the reverse order assures the +load of class is executed first and
+   then +load of categories because of the way in which categories are
+   added to the class methods.  This function needs to be called with
+   the objc_runtime_mutex locked.  */
 static void
-__objc_send_message_in_list (struct objc_method_list *method_list, Class class, SEL op)
+__objc_send_load_using_method_list (struct objc_method_list *method_list, Class class)
 {
+  static SEL load_selector = 0;
   int i;
 
-  if (! method_list)
+  if (!method_list)
     return;
 
-  /* First execute the `op' message in the following method lists.  */
-  __objc_send_message_in_list (method_list->method_next, class, op);
+  /* This needs no lock protection because we are called with the
+     objc_runtime_mutex locked.  */
+  if (!load_selector)
+    load_selector = sel_registerName ("load");
 
+  /* method_list is a linked list of method lists; since we're
+     executing in reverse order, we need to do the next list before we
+     do this one.  */
+  __objc_send_load_using_method_list (method_list->method_next, class);
+
   /* Search the method list.  */
   for (i = 0; i < method_list->method_count; i++)
     {
       struct objc_method *mth = &method_list->method_list[i];
 
-      if (mth->method_name && sel_eq (mth->method_name, op)
+      /* We are searching for +load methods that we haven't executed
+        yet.  */
+      if (mth->method_name && sel_eq (mth->method_name, load_selector)
          && ! objc_hash_is_key_in_hash (__objc_load_methods, mth->method_imp))
        {
-         /* Add this method into the +load hash table.  */
+         /* Add this method into the +load hash table, so we won't
+            execute it again next time.  */
          objc_hash_add (&__objc_load_methods,
                         mth->method_imp,
                         mth->method_imp);
          
          DEBUG_PRINTF ("sending +load in class: %s\n", class->name);
          
-         /* The method was found and wasn't previously executed.  */
+         /* Call +load.  */
          (*mth->method_imp) ((id)class, mth->method_name);
 
          break;
@@ -397,18 +409,16 @@  static void
     }
 }
 
+/* This function needs to be called with the objc_runtime_mutex
+   locked.  */
 static void
 __objc_send_load (objc_class_tree *tree,
                  int level __attribute__ ((__unused__)))
 {
-  static SEL load_sel = 0;
   Class class = tree->class;
   struct objc_method_list *method_list = class->class_pointer->methods;
 
-  if (! load_sel)
-    load_sel = sel_registerName ("load");
-
-  __objc_send_message_in_list (method_list, class, load_sel);
+  __objc_send_load_using_method_list (method_list, class);
 }
 
 static void
@@ -580,8 +590,8 @@  __objc_exec_class (struct objc_module *module)
       previous_constructors = 1;
     }
 
-  /* Save the module pointer for later processing. (not currently
-     used).  */
+  /* Save the module pointer so that later we remember to call +load
+     on all classes and categories on it.  */
   objc_mutex_lock (__objc_runtime_mutex);
   __objc_module_list = list_cons (module, __objc_module_list);
 
@@ -717,14 +727,16 @@  __objc_exec_class (struct objc_module *module)
   objc_mutex_unlock (__objc_runtime_mutex);
 }
 
+/* This function needs to be called with the objc_runtime_mutex
+   locked.  */
 static void
 objc_send_load (void)
 {
-  if (! __objc_module_list)
+  if (!__objc_module_list)
     return;
  
   /* Try to find out if all the classes loaded so far also have their
-     superclasses known to the runtime. We suppose that the objects
+     superclasses known to the runtime.  We suppose that the objects
      that are allocated in the +load method are in general of a class
      declared in the same module.  */
   if (unresolved_classes)
@@ -742,7 +754,7 @@  objc_send_load (void)
 
       /* If we still have classes for whom we don't have yet their
          super classes known to the runtime we don't send the +load
-         messages.  */
+         messages yet.  */
       if (unresolved_classes)
        return;
     }
@@ -791,6 +803,25 @@  __objc_create_classes_tree (struct objc_module *mo
 
       objc_tree_insert_class (class);
     }
+
+  /* Now iterate over "claimed" categories too (ie, categories that
+     extend a class that has already been loaded by the runtime), and
+     insert them in the classes tree hiearchy too.  Otherwise, if you
+     add a category, its +load method would not be called if the class
+     is already loaded in the runtime.  It the category is
+     "unclaimed", ie, we haven't loaded the main class yet, postpone
+     sending +load as we want to execute +load from the class before
+     we execute the one from the category.  */
+  for (i = 0; i < symtab->cat_def_cnt; ++i)
+    {
+      struct objc_category *category = symtab->defs[i + symtab->cls_def_cnt];
+      Class class = objc_getClass (category->class_name);
+      
+      /* If the class for the category exists then append its
+        methods.  */
+      if (class)
+       objc_tree_insert_class (class);
+    }
 }
 
 static void
Index: libobjc/ChangeLog
===================================================================
--- libobjc/ChangeLog   (revision 168119)
+++ libobjc/ChangeLog   (working copy)
@@ -1,5 +1,17 @@ 
 2010-12-21  Nicola Pero  <nicola.pero@meta-innovation.com>
 
+       PR libobjc/16110
+       * init.c (__objc_send_message_in_list): Renamed to
+       __objc_send_load_using_method_list.  Do not take an 'op' argument.
+       Register the 'load' selector if needed.
+       (__objc_send_load): Do not register the 'load' selector.  Updated
+       call to __objc_send_message_in_list.
+       (__objc_create_classes_tree): Add the class of any claimed
+       category that was loaded in the module to the list of classes for
+       which we try to execute +load.
+       
+2010-12-21  Nicola Pero  <nicola.pero@meta-innovation.com>
+
        * objc-private/common.h: When DEBUG is defined, include <stdio.h>.
        Updated comments.
        * init.c (__objc_tree_insert_class): Use %p, not %x, when printing