mirror of git://gcc.gnu.org/git/gcc.git
				
				
				
			
		
			
				
	
	
		
			359 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			359 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C
		
	
	
	
| /* GNU Objective C Runtime class related functions
 | |
|    Copyright (C) 1993, 1995, 1996, 1997 Free Software Foundation, Inc.
 | |
|    Contributed by Kresten Krab Thorup and Dennis Glatting.
 | |
| 
 | |
| This file is part of GNU CC.
 | |
| 
 | |
| GNU CC is free software; you can redistribute it and/or modify it under the
 | |
| terms of the GNU General Public License as published by the Free Software
 | |
| Foundation; either version 2, or (at your option) any later version.
 | |
| 
 | |
| GNU CC is distributed in the hope that it will be useful, but WITHOUT ANY
 | |
| WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 | |
| FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
 | |
| details.
 | |
| 
 | |
| You should have received a copy of the GNU General Public License along with
 | |
| GNU CC; see the file COPYING.  If not, write to the Free Software
 | |
| Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 | |
| 
 | |
| /* As a special exception, if you link this library with files compiled with
 | |
|    GCC to produce an executable, this does not cause the resulting executable
 | |
|    to be covered by the GNU General Public License. This exception does not
 | |
|    however invalidate any other reasons why the executable file might be
 | |
|    covered by the GNU General Public License.  */
 | |
| 
 | |
| #include "runtime.h"		/* the kitchen sink */
 | |
| #include "sarray.h"
 | |
| 
 | |
| /* The table of classname->class.  Used for objc_lookup_class and friends */
 | |
| static cache_ptr __objc_class_hash = 0;                 /* !T:MUTEX */
 | |
| 
 | |
| /* This is a hook which is called by objc_get_class and 
 | |
|    objc_lookup_class if the runtime is not able to find the class.
 | |
|    This may e.g. try to load in the class using dynamic loading */
 | |
| Class (*_objc_lookup_class)(const char* name) = 0;      /* !T:SAFE */
 | |
| 
 | |
| 
 | |
| /* True when class links has been resolved */     
 | |
| BOOL __objc_class_links_resolved = NO;                  /* !T:UNUSED */
 | |
| 
 | |
| 
 | |
| /* Initial number of buckets size of class hash table. */
 | |
| #define CLASS_HASH_SIZE 32
 | |
| 
 | |
| void __objc_init_class_tables()
 | |
| {
 | |
|   /* Allocate the class hash table */
 | |
| 
 | |
|   if(__objc_class_hash)
 | |
|     return;
 | |
| 
 | |
|   objc_mutex_lock(__objc_runtime_mutex);
 | |
| 
 | |
|   __objc_class_hash
 | |
|     =  hash_new (CLASS_HASH_SIZE,
 | |
| 		 (hash_func_type) hash_string,
 | |
| 		 (compare_func_type) compare_strings);
 | |
| 
 | |
|   objc_mutex_unlock(__objc_runtime_mutex);
 | |
| }  
 | |
| 
 | |
| /* This function adds a class to the class hash table, and assigns the 
 | |
|    class a number, unless it's already known */
 | |
| void
 | |
| __objc_add_class_to_hash(Class class)
 | |
| {
 | |
|   Class h_class;
 | |
| 
 | |
|   objc_mutex_lock(__objc_runtime_mutex);
 | |
| 
 | |
|   /* make sure the table is there */
 | |
|   assert(__objc_class_hash);
 | |
| 
 | |
|   /* make sure it's not a meta class */  
 | |
|   assert(CLS_ISCLASS(class));
 | |
| 
 | |
|   /* Check to see if the class is already in the hash table.  */
 | |
|   h_class = hash_value_for_key (__objc_class_hash, class->name);
 | |
|   if (!h_class)
 | |
|     {
 | |
|       /* The class isn't in the hash table.  Add the class and assign a class
 | |
|          number.  */
 | |
|       static unsigned int class_number = 1;
 | |
| 
 | |
|       CLS_SETNUMBER(class, class_number);
 | |
|       CLS_SETNUMBER(class->class_pointer, class_number);
 | |
| 
 | |
|       ++class_number;
 | |
|       hash_add (&__objc_class_hash, class->name, class);
 | |
|     }
 | |
| 
 | |
|   objc_mutex_unlock(__objc_runtime_mutex);
 | |
| }
 | |
| 
 | |
| /* Get the class object for the class named NAME.  If NAME does not
 | |
|    identify a known class, the hook _objc_lookup_class is called.  If
 | |
|    this fails, nil is returned */
 | |
| Class objc_lookup_class (const char* name)
 | |
| {
 | |
|   Class class;
 | |
| 
 | |
|   objc_mutex_lock(__objc_runtime_mutex);
 | |
| 
 | |
|   /* Make sure the class hash table exists.  */
 | |
|   assert (__objc_class_hash);
 | |
| 
 | |
|   class = hash_value_for_key (__objc_class_hash, name);
 | |
| 
 | |
|   objc_mutex_unlock(__objc_runtime_mutex);
 | |
| 
 | |
|   if (class)
 | |
|     return class;
 | |
| 
 | |
|   if (_objc_lookup_class)
 | |
|     return (*_objc_lookup_class)(name);
 | |
|   else
 | |
|     return 0;
 | |
| }
 | |
| 
 | |
| /* Get the class object for the class named NAME.  If NAME does not
 | |
|    identify a known class, the hook _objc_lookup_class is called.  If
 | |
|    this fails,  an error message is issued and the system aborts */
 | |
| Class
 | |
| objc_get_class (const char *name)
 | |
| {
 | |
|   Class class;
 | |
| 
 | |
|   objc_mutex_lock(__objc_runtime_mutex);
 | |
| 
 | |
|   /* Make sure the class hash table exists.  */
 | |
|   assert (__objc_class_hash);
 | |
| 
 | |
|   class = hash_value_for_key (__objc_class_hash, name);
 | |
| 
 | |
|   objc_mutex_unlock(__objc_runtime_mutex);
 | |
| 
 | |
|   if (class)
 | |
|     return class;
 | |
| 
 | |
|   if (_objc_lookup_class)
 | |
|     class = (*_objc_lookup_class)(name);
 | |
| 
 | |
|   if(class)
 | |
|     return class;
 | |
|   
 | |
|   objc_error(nil, OBJC_ERR_BAD_CLASS, 
 | |
| 	     "objc runtime: cannot find class %s\n", name);
 | |
|   return 0;
 | |
| }
 | |
| 
 | |
| MetaClass
 | |
| objc_get_meta_class(const char *name)
 | |
| {
 | |
|   return objc_get_class(name)->class_pointer;
 | |
| }
 | |
| 
 | |
| /* This function provides a way to enumerate all the classes in the
 | |
|    executable.  Pass *ENUM_STATE == NULL to start the enumeration.  The
 | |
|    function will return 0 when there are no more classes.  
 | |
|    For example: 
 | |
|        id class; 
 | |
|        void *es = NULL;
 | |
|        while ((class = objc_next_class(&es)))
 | |
|          ... do something with class; 
 | |
| */
 | |
| Class
 | |
| objc_next_class(void **enum_state)
 | |
| {
 | |
|   objc_mutex_lock(__objc_runtime_mutex);
 | |
| 
 | |
|   /* make sure the table is there */
 | |
|   assert(__objc_class_hash);
 | |
| 
 | |
|   *(node_ptr*)enum_state = 
 | |
|     hash_next(__objc_class_hash, *(node_ptr*)enum_state);
 | |
| 
 | |
|   objc_mutex_unlock(__objc_runtime_mutex);
 | |
| 
 | |
|   if (*(node_ptr*)enum_state)
 | |
|     return (*(node_ptr*)enum_state)->value;
 | |
|   return (Class)0;
 | |
| }
 | |
| 
 | |
| /* Resolve super/subclass links for all classes.  The only thing we 
 | |
|    can be sure of is that the class_pointer for class objects point 
 | |
|    to the right meta class objects */
 | |
| void __objc_resolve_class_links()
 | |
| {
 | |
|   node_ptr node;
 | |
|   Class object_class = objc_get_class ("Object");
 | |
| 
 | |
|   assert(object_class);
 | |
| 
 | |
|   objc_mutex_lock(__objc_runtime_mutex);
 | |
| 
 | |
|   /* Assign subclass links */
 | |
|   for (node = hash_next (__objc_class_hash, NULL); node;
 | |
|        node = hash_next (__objc_class_hash, node))
 | |
|     {
 | |
|       Class class1 = node->value;
 | |
| 
 | |
|       /* Make sure we have what we think we have.  */
 | |
|       assert (CLS_ISCLASS(class1));
 | |
|       assert (CLS_ISMETA(class1->class_pointer));
 | |
| 
 | |
|       /* The class_pointer of all meta classes point to Object's meta class. */
 | |
|       class1->class_pointer->class_pointer = object_class->class_pointer;
 | |
| 
 | |
|       if (!(CLS_ISRESOLV(class1)))
 | |
|         {
 | |
|           CLS_SETRESOLV(class1);
 | |
|           CLS_SETRESOLV(class1->class_pointer);
 | |
|               
 | |
|           if(class1->super_class)
 | |
|             {   
 | |
|               Class a_super_class 
 | |
|                 = objc_get_class ((char *) class1->super_class);
 | |
|               
 | |
|               assert (a_super_class);
 | |
|               
 | |
|               DEBUG_PRINTF ("making class connections for: %s\n",
 | |
|                             class1->name);
 | |
|               
 | |
|               /* assign subclass links for superclass */
 | |
|               class1->sibling_class = a_super_class->subclass_list;
 | |
|               a_super_class->subclass_list = class1;
 | |
|               
 | |
|               /* Assign subclass links for meta class of superclass */
 | |
|               if (a_super_class->class_pointer)
 | |
|                 {
 | |
|                   class1->class_pointer->sibling_class
 | |
|                     = a_super_class->class_pointer->subclass_list;
 | |
|                   a_super_class->class_pointer->subclass_list 
 | |
|                     = class1->class_pointer;
 | |
|                 }
 | |
|             }
 | |
|           else                  /* a root class, make its meta object */
 | |
|                                 /* be a subclass of Object */
 | |
|             {
 | |
|               class1->class_pointer->sibling_class 
 | |
|                 = object_class->subclass_list;
 | |
|               object_class->subclass_list = class1->class_pointer;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   /* Assign superclass links */
 | |
|   for (node = hash_next (__objc_class_hash, NULL); node;
 | |
|        node = hash_next (__objc_class_hash, node))
 | |
|     {
 | |
|       Class class1 = node->value;
 | |
|       Class sub_class;
 | |
|       for (sub_class = class1->subclass_list; sub_class;
 | |
|            sub_class = sub_class->sibling_class)
 | |
|         {
 | |
|           sub_class->super_class = class1;
 | |
|           if(CLS_ISCLASS(sub_class))
 | |
|             sub_class->class_pointer->super_class = class1->class_pointer;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   objc_mutex_unlock(__objc_runtime_mutex);
 | |
| }
 | |
| 
 | |
| 
 | |
| 
 | |
| #define CLASSOF(c) ((c)->class_pointer)
 | |
| 
 | |
| Class
 | |
| class_pose_as (Class impostor, Class super_class)
 | |
| {
 | |
|   node_ptr node;
 | |
|   Class class1;
 | |
| 
 | |
|   if (!CLS_ISRESOLV (impostor))
 | |
|     __objc_resolve_class_links ();
 | |
| 
 | |
|   /* preconditions */
 | |
|   assert (impostor);
 | |
|   assert (super_class);
 | |
|   assert (impostor->super_class == super_class);
 | |
|   assert (CLS_ISCLASS (impostor));
 | |
|   assert (CLS_ISCLASS (super_class));
 | |
|   assert (impostor->instance_size == super_class->instance_size);
 | |
| 
 | |
|   {
 | |
|     Class *subclass = &(super_class->subclass_list);
 | |
| 
 | |
|     /* move subclasses of super_class to impostor */
 | |
|     while (*subclass)
 | |
|       {
 | |
| 	Class nextSub = (*subclass)->sibling_class;
 | |
| 
 | |
| 	if (*subclass != impostor)
 | |
| 	  {
 | |
| 	    Class sub = *subclass;
 | |
| 
 | |
| 	    /* classes */
 | |
| 	    sub->sibling_class = impostor->subclass_list;
 | |
| 	    sub->super_class = impostor;
 | |
| 	    impostor->subclass_list = sub;
 | |
| 
 | |
| 	    /* It will happen that SUB is not a class object if it is 
 | |
| 	       the top of the meta class hierarchy chain.  (root
 | |
| 	       meta-class objects inherit their class object)  If that is
 | |
| 	       the case... don't mess with the meta-meta class. */ 
 | |
| 	    if (CLS_ISCLASS (sub))
 | |
| 	      {
 | |
| 		/* meta classes */
 | |
| 		CLASSOF (sub)->sibling_class = 
 | |
| 		  CLASSOF (impostor)->subclass_list;
 | |
| 		CLASSOF (sub)->super_class = CLASSOF (impostor);
 | |
| 		CLASSOF (impostor)->subclass_list = CLASSOF (sub);
 | |
| 	      }
 | |
| 	  }
 | |
| 
 | |
| 	*subclass = nextSub;
 | |
|       }
 | |
| 
 | |
|     /* set subclasses of superclass to be impostor only */
 | |
|     super_class->subclass_list = impostor;
 | |
|     CLASSOF (super_class)->subclass_list = CLASSOF (impostor);
 | |
|     
 | |
|     /* set impostor to have no sibling classes */
 | |
|     impostor->sibling_class = 0;
 | |
|     CLASSOF (impostor)->sibling_class = 0;
 | |
|   }
 | |
|   
 | |
|   /* check relationship of impostor and super_class is kept. */
 | |
|   assert (impostor->super_class == super_class);
 | |
|   assert (CLASSOF (impostor)->super_class == CLASSOF (super_class));
 | |
| 
 | |
|   /* This is how to update the lookup table. Regardless of
 | |
|      what the keys of the hashtable is, change all values that are
 | |
|      superclass into impostor. */
 | |
| 
 | |
|   objc_mutex_lock(__objc_runtime_mutex);
 | |
| 
 | |
|   for (node = hash_next (__objc_class_hash, NULL); node;
 | |
|        node = hash_next (__objc_class_hash, node))
 | |
|     {
 | |
|       class1 = (Class)node->value;
 | |
|       if (class1 == super_class)
 | |
| 	{
 | |
| 	  node->value = impostor; /* change hash table value */
 | |
| 	}
 | |
|     }      
 | |
| 
 | |
|   objc_mutex_unlock(__objc_runtime_mutex);
 | |
| 
 | |
|   /* next, we update the dispatch tables... */
 | |
|   __objc_update_dispatch_table_for_class (CLASSOF (impostor));
 | |
|   __objc_update_dispatch_table_for_class (impostor);
 | |
| 
 | |
|   return impostor;
 | |
| }
 | |
|   
 | |
| 
 |