summaryrefslogtreecommitdiff
path: root/gcc/objc/objc-act.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/objc/objc-act.c')
-rw-r--r--gcc/objc/objc-act.c507
1 files changed, 210 insertions, 297 deletions
diff --git a/gcc/objc/objc-act.c b/gcc/objc/objc-act.c
index 72a486d9c83..396d748849f 100644
--- a/gcc/objc/objc-act.c
+++ b/gcc/objc/objc-act.c
@@ -40,6 +40,7 @@ along with GCC; see the file COPYING3. If not see
#include "flags.h"
#include "langhooks.h"
#include "objc-act.h"
+#include "objc-map.h"
#include "input.h"
#include "function.h"
#include "output.h"
@@ -157,27 +158,25 @@ static void objc_generate_cxx_cdtors (void);
static void objc_decl_method_attributes (tree*, tree, int);
static tree build_keyword_selector (tree);
-/* Hash tables to manage the global pool of method prototypes. */
static void hash_init (void);
-hash *nst_method_hash_list = 0;
-hash *cls_method_hash_list = 0;
+/* Hash tables to manage the global pool of method prototypes. Each
+ of these maps map a method name (selector) identifier to either a
+ single tree (for methods with a single method prototype) or a
+ TREE_VEC (for methods with multiple method prototypes). */
+static GTY(()) objc_map_t instance_method_map = 0;
+static GTY(()) objc_map_t class_method_map = 0;
/* Hash tables to manage the global pool of class names. */
-hash *cls_name_hash_list = 0;
-hash *als_name_hash_list = 0;
+static GTY(()) objc_map_t class_name_map = 0;
+static GTY(()) objc_map_t alias_name_map = 0;
-hash *ivar_offset_hash_list = 0;
-
-static void hash_class_name_enter (hash *, tree, tree);
-static hash hash_class_name_lookup (hash *, tree);
-
-static hash hash_lookup (hash *, tree);
static tree lookup_method (tree, tree);
static tree lookup_method_static (tree, tree, int);
-static tree add_class (tree, tree);
+static void interface_hash_init (void);
+static tree add_interface (tree, tree);
static void add_category (tree, tree);
static inline tree lookup_category (tree, tree);
@@ -207,7 +206,7 @@ static void generate_struct_by_value_array (void) ATTRIBUTE_NORETURN;
static void mark_referenced_methods (void);
static bool objc_type_valid_for_messaging (tree type, bool allow_classes);
-static tree check_duplicates (hash, int, int);
+static tree check_duplicates (tree, int, int);
/*** Private Interface (data) ***/
/* Flags for lookup_method_static(). */
@@ -380,6 +379,7 @@ objc_init (void)
/* Set up stuff used by FE parser and all runtimes. */
errbuf = XNEWVEC (char, 1024 * 10);
+ interface_hash_init ();
hash_init ();
objc_encoding_init ();
/* ... and then check flags and set-up for the selected runtime ... */
@@ -418,19 +418,15 @@ objc_write_global_declarations (void)
if (warn_selector)
{
- int slot;
- hash hsh;
+ objc_map_iterator_t i;
- /* Run through the selector hash tables and print a warning for any
- selector which has multiple methods. */
+ objc_map_iterator_initialize (class_method_map, &i);
+ while (objc_map_iterator_move_to_next (class_method_map, &i))
+ check_duplicates (objc_map_iterator_current_value (class_method_map, i), 0, 1);
- for (slot = 0; slot < SIZEHASHTABLE; slot++)
- {
- for (hsh = cls_method_hash_list[slot]; hsh; hsh = hsh->next)
- check_duplicates (hsh, 0, 1);
- for (hsh = nst_method_hash_list[slot]; hsh; hsh = hsh->next)
- check_duplicates (hsh, 0, 0);
- }
+ objc_map_iterator_initialize (instance_method_map, &i);
+ while (objc_map_iterator_move_to_next (instance_method_map, &i))
+ check_duplicates (objc_map_iterator_current_value (instance_method_map, i), 0, 0);
}
/* TODO: consider an early exit here if either errorcount or sorrycount
@@ -3351,8 +3347,7 @@ objc_declare_alias (tree alias_ident, tree class_ident)
#ifdef OBJCPLUS
pop_lang_context ();
#endif
- hash_class_name_enter (als_name_hash_list, alias_ident,
- underlying_class);
+ objc_map_put (alias_name_map, alias_ident, underlying_class);
}
}
@@ -3392,15 +3387,13 @@ objc_declare_class (tree identifier)
the TYPE_OBJC_INTERFACE. If later an @interface is found,
we'll replace the ident with the interface. */
TYPE_OBJC_INTERFACE (record) = identifier;
- hash_class_name_enter (cls_name_hash_list, identifier, NULL_TREE);
+ objc_map_put (class_name_map, identifier, NULL_TREE);
}
}
tree
objc_is_class_name (tree ident)
{
- hash target;
-
if (ident && TREE_CODE (ident) == IDENTIFIER_NODE)
{
tree t = identifier_global_value (ident);
@@ -3428,16 +3421,17 @@ objc_is_class_name (tree ident)
if (lookup_interface (ident))
return ident;
- target = hash_class_name_lookup (cls_name_hash_list, ident);
- if (target)
- return target->key;
+ {
+ tree target;
- target = hash_class_name_lookup (als_name_hash_list, ident);
- if (target)
- {
- gcc_assert (target->list && target->list->value);
- return target->list->value;
- }
+ target = objc_map_get (class_name_map, ident);
+ if (target != OBJC_MAP_NOT_FOUND)
+ return ident;
+
+ target = objc_map_get (alias_name_map, ident);
+ if (target != OBJC_MAP_NOT_FOUND)
+ return target;
+ }
return 0;
}
@@ -3761,25 +3755,30 @@ objc_generate_write_barrier (tree lhs, enum tree_code modifycode, tree rhs)
return result;
}
-struct GTY(()) interface_tuple {
- tree id;
- tree class_name;
-};
+/* Implementation of the table mapping a class name (as an identifier)
+ to a class node. The two public functions for it are
+ lookup_interface() and add_interface(). add_interface() is only
+ used in this file, so we can make it static. */
-static GTY ((param_is (struct interface_tuple))) htab_t interface_htab;
+static GTY(()) objc_map_t interface_map;
-static hashval_t
-hash_interface (const void *p)
+static void
+interface_hash_init (void)
{
- const struct interface_tuple *d = (const struct interface_tuple *) p;
- return IDENTIFIER_HASH_VALUE (d->id);
+ interface_map = objc_map_alloc_ggc (200);
}
-static int
-eq_interface (const void *p1, const void *p2)
+static tree
+add_interface (tree class_name, tree name)
{
- const struct interface_tuple *d = (const struct interface_tuple *) p1;
- return d->id == p2;
+ /* Put interfaces on list in reverse order. */
+ TREE_CHAIN (class_name) = interface_chain;
+ interface_chain = class_name;
+
+ /* Add it to the map. */
+ objc_map_put (interface_map, name, class_name);
+
+ return interface_chain;
}
tree
@@ -3794,19 +3793,12 @@ lookup_interface (tree ident)
return NULL_TREE;
{
- struct interface_tuple **slot;
- tree i = NULL_TREE;
+ tree interface = objc_map_get (interface_map, ident);
- if (interface_htab)
- {
- slot = (struct interface_tuple **)
- htab_find_slot_with_hash (interface_htab, ident,
- IDENTIFIER_HASH_VALUE (ident),
- NO_INSERT);
- if (slot && *slot)
- i = (*slot)->class_name;
- }
- return i;
+ if (interface == OBJC_MAP_NOT_FOUND)
+ return NULL_TREE;
+ else
+ return interface;
}
}
@@ -5052,71 +5044,75 @@ build_function_type_for_method (tree return_type, tree method,
return ftype;
}
+/* The 'method' argument is a tree; this tree could either be a single
+ method, which is returned, or could be a TREE_VEC containing a list
+ of methods. In that case, the first one is returned, and warnings
+ are issued as appropriate. */
static tree
-check_duplicates (hash hsh, int methods, int is_class)
+check_duplicates (tree method, int methods, int is_class)
{
- tree meth = NULL_TREE;
-
- if (hsh)
- {
- meth = hsh->key;
-
- if (hsh->list)
- {
- /* We have two or more methods with the same name but
- different types. */
- attr loop;
-
- /* But just how different are those types? If
- -Wno-strict-selector-match is specified, we shall not
- complain if the differences are solely among types with
- identical size and alignment. */
- if (!warn_strict_selector_match)
- {
- for (loop = hsh->list; loop; loop = loop->next)
- if (!comp_proto_with_proto (meth, loop->value, 0))
- goto issue_warning;
-
- return meth;
- }
-
- issue_warning:
- if (methods)
- {
- bool type = TREE_CODE (meth) == INSTANCE_METHOD_DECL;
-
- warning_at (input_location, 0,
- "multiple methods named %<%c%E%> found",
- (is_class ? '+' : '-'),
- METHOD_SEL_NAME (meth));
- inform (DECL_SOURCE_LOCATION (meth), "using %<%c%s%>",
- (type ? '-' : '+'),
- identifier_to_locale (gen_method_decl (meth)));
- }
- else
- {
- bool type = TREE_CODE (meth) == INSTANCE_METHOD_DECL;
+ tree first_method;
+ size_t i;
- warning_at (input_location, 0,
- "multiple selectors named %<%c%E%> found",
- (is_class ? '+' : '-'),
- METHOD_SEL_NAME (meth));
- inform (DECL_SOURCE_LOCATION (meth), "found %<%c%s%>",
- (type ? '-' : '+'),
- identifier_to_locale (gen_method_decl (meth)));
- }
-
- for (loop = hsh->list; loop; loop = loop->next)
- {
- bool type = TREE_CODE (loop->value) == INSTANCE_METHOD_DECL;
+ if (method == NULL_TREE)
+ return NULL_TREE;
- inform (DECL_SOURCE_LOCATION (loop->value), "also found %<%c%s%>",
- (type ? '-' : '+'),
- identifier_to_locale (gen_method_decl (loop->value)));
- }
- }
+ if (TREE_CODE (method) != TREE_VEC)
+ return method;
+
+ /* We have two or more methods with the same name but different
+ types. */
+ first_method = TREE_VEC_ELT (method, 0);
+
+ /* But just how different are those types? If
+ -Wno-strict-selector-match is specified, we shall not complain if
+ the differences are solely among types with identical size and
+ alignment. */
+ if (!warn_strict_selector_match)
+ {
+ for (i = 0; i < TREE_VEC_LENGTH (method); i++)
+ if (!comp_proto_with_proto (first_method, TREE_VEC_ELT (method, i), 0))
+ goto issue_warning;
+
+ return first_method;
+ }
+
+ issue_warning:
+ if (methods)
+ {
+ bool type = TREE_CODE (first_method) == INSTANCE_METHOD_DECL;
+
+ warning_at (input_location, 0,
+ "multiple methods named %<%c%E%> found",
+ (is_class ? '+' : '-'),
+ METHOD_SEL_NAME (first_method));
+ inform (DECL_SOURCE_LOCATION (first_method), "using %<%c%s%>",
+ (type ? '-' : '+'),
+ identifier_to_locale (gen_method_decl (first_method)));
}
- return meth;
+ else
+ {
+ bool type = TREE_CODE (first_method) == INSTANCE_METHOD_DECL;
+
+ warning_at (input_location, 0,
+ "multiple selectors named %<%c%E%> found",
+ (is_class ? '+' : '-'),
+ METHOD_SEL_NAME (first_method));
+ inform (DECL_SOURCE_LOCATION (first_method), "found %<%c%s%>",
+ (type ? '-' : '+'),
+ identifier_to_locale (gen_method_decl (first_method)));
+ }
+
+ for (i = 0; i < TREE_VEC_LENGTH (method); i++)
+ {
+ bool type = TREE_CODE (TREE_VEC_ELT (method, i)) == INSTANCE_METHOD_DECL;
+
+ inform (DECL_SOURCE_LOCATION (TREE_VEC_ELT (method, i)), "also found %<%c%s%>",
+ (type ? '-' : '+'),
+ identifier_to_locale (gen_method_decl (TREE_VEC_ELT (method, i))));
+ }
+
+ return first_method;
}
/* If RECEIVER is a class reference, return the identifier node for
@@ -5294,17 +5290,18 @@ objc_build_message_expr (tree receiver, tree message_args)
static tree
lookup_method_in_hash_lists (tree sel_name, int is_class)
{
- hash method_prototype = NULL;
+ tree method_prototype = OBJC_MAP_NOT_FOUND;
if (!is_class)
- method_prototype = hash_lookup (nst_method_hash_list,
- sel_name);
-
- if (!method_prototype)
+ method_prototype = objc_map_get (instance_method_map, sel_name);
+
+ if (method_prototype == OBJC_MAP_NOT_FOUND)
{
- method_prototype = hash_lookup (cls_method_hash_list,
- sel_name);
+ method_prototype = objc_map_get (class_method_map, sel_name);
is_class = 1;
+
+ if (method_prototype == OBJC_MAP_NOT_FOUND)
+ return NULL_TREE;
}
return check_duplicates (method_prototype, 1, is_class);
@@ -5714,21 +5711,19 @@ objc_build_selector_expr (location_t loc, tree selnamelist)
/* Look the selector up in the list of all known class and
instance methods (up to this line) to check that the selector
exists. */
- hash hsh;
+ tree method;
/* First try with instance methods. */
- hsh = hash_lookup (nst_method_hash_list, selname);
+ method = objc_map_get (instance_method_map, selname);
/* If not found, try with class methods. */
- if (!hsh)
+ if (method == OBJC_MAP_NOT_FOUND)
{
- hsh = hash_lookup (cls_method_hash_list, selname);
- }
+ method = objc_map_get (class_method_map, selname);
- /* If still not found, print out a warning. */
- if (!hsh)
- {
- warning (0, "undeclared selector %qE", selname);
+ /* If still not found, print out a warning. */
+ if (method == OBJC_MAP_NOT_FOUND)
+ warning (0, "undeclared selector %qE", selname);
}
}
@@ -5761,131 +5756,99 @@ build_ivar_reference (tree id)
return (*runtime.build_ivar_reference) (input_location, base, id);
}
-/* Compute a hash value for a given method SEL_NAME. */
-
-static size_t
-hash_func (tree sel_name)
-{
- const unsigned char *s
- = (const unsigned char *)IDENTIFIER_POINTER (sel_name);
- size_t h = 0;
-
- while (*s)
- h = h * 67 + *s++ - 113;
- return h;
-}
-
static void
hash_init (void)
{
- nst_method_hash_list = ggc_alloc_cleared_vec_hash (SIZEHASHTABLE);
- cls_method_hash_list = ggc_alloc_cleared_vec_hash (SIZEHASHTABLE);
-
- cls_name_hash_list = ggc_alloc_cleared_vec_hash (SIZEHASHTABLE);
- als_name_hash_list = ggc_alloc_cleared_vec_hash (SIZEHASHTABLE);
+ instance_method_map = objc_map_alloc_ggc (1000);
+ class_method_map = objc_map_alloc_ggc (1000);
- ivar_offset_hash_list = ggc_alloc_cleared_vec_hash (SIZEHASHTABLE);
+ class_name_map = objc_map_alloc_ggc (200);
+ alias_name_map = objc_map_alloc_ggc (200);
/* Initialize the hash table used to hold the constant string objects. */
string_htab = htab_create_ggc (31, string_hash,
string_eq, NULL);
}
-/* This routine adds sel_name to the hash list. sel_name is a class or alias
- name for the class. If alias name, then value is its underlying class.
- If class, the value is NULL_TREE. */
-
+/* Use the following to add a method to class_method_map or
+ instance_method_map. It will add the method, keyed by the
+ METHOD_SEL_NAME. If the method already exists, but with one or
+ more different prototypes, it will store a TREE_VEC in the map,
+ with the method prototypes in the vector. */
static void
-hash_class_name_enter (hash *hashlist, tree sel_name, tree value)
+insert_method_into_method_map (bool class_method, tree method)
{
- hash obj;
- int slot = hash_func (sel_name) % SIZEHASHTABLE;
+ tree method_name = METHOD_SEL_NAME (method);
+ tree existing_entry;
+ objc_map_t map;
- obj = ggc_alloc_hashed_entry ();
- if (value != NULL_TREE)
- {
- /* Save the underlying class for the 'alias' in the hash table */
- attr obj_attr = ggc_alloc_hashed_attribute ();
- obj_attr->value = value;
- obj->list = obj_attr;
- }
+ if (class_method)
+ map = class_method_map;
else
- obj->list = 0;
- obj->next = hashlist[slot];
- obj->key = sel_name;
-
- hashlist[slot] = obj; /* append to front */
+ map = instance_method_map;
-}
+ /* Check if the method already exists in the map. */
+ existing_entry = objc_map_get (map, method_name);
-/*
- Searches in the hash table looking for a match for class or alias name.
-*/
-
-static hash
-hash_class_name_lookup (hash *hashlist, tree sel_name)
-{
- hash target;
-
- target = hashlist[hash_func (sel_name) % SIZEHASHTABLE];
-
- while (target)
+ /* If not, we simply add it to the map. */
+ if (existing_entry == OBJC_MAP_NOT_FOUND)
+ objc_map_put (map, method_name, method);
+ else
{
- if (sel_name == target->key)
- return target;
-
- target = target->next;
- }
- return 0;
-}
-
-/* WARNING!!!! hash_enter is called with a method, and will peek
- inside to find its selector! But hash_lookup is given a selector
- directly, and looks for the selector that's inside the found
- entry's key (method) for comparison. */
-
-static void
-hash_enter (hash *hashlist, tree method)
-{
- hash obj;
- int slot = hash_func (METHOD_SEL_NAME (method)) % SIZEHASHTABLE;
-
- obj = ggc_alloc_hashed_entry ();
- obj->list = 0;
- obj->next = hashlist[slot];
- obj->key = method;
-
- hashlist[slot] = obj; /* append to front */
-}
-
-static hash
-hash_lookup (hash *hashlist, tree sel_name)
-{
- hash target;
+ tree new_entry;
+
+ /* If an entry already exists, it's more complicated. We'll
+ have to check whether the method prototype is the same or
+ not. */
+ if (TREE_CODE (existing_entry) != TREE_VEC)
+ {
+ /* If the method prototypes are the same, there is nothing
+ to do. */
+ if (comp_proto_with_proto (method, existing_entry, 1))
+ return;
- target = hashlist[hash_func (sel_name) % SIZEHASHTABLE];
+ /* If not, create a vector to store both the method already
+ in the map, and the new one that we are adding. */
+ new_entry = make_tree_vec (2);
+
+ TREE_VEC_ELT (new_entry, 0) = existing_entry;
+ TREE_VEC_ELT (new_entry, 1) = method;
+ }
+ else
+ {
+ /* An entry already exists, and it's already a vector. This
+ means that at least 2 different method prototypes were
+ already found, and we're considering registering yet
+ another one. */
+ size_t i;
+
+ /* Check all the existing prototypes. If any matches the
+ one we need to add, there is nothing to do because it's
+ already there. */
+ for (i = 0; i < TREE_VEC_LENGTH (existing_entry); i++)
+ if (comp_proto_with_proto (method, TREE_VEC_ELT (existing_entry, i), 1))
+ return;
- while (target)
- {
- if (sel_name == METHOD_SEL_NAME (target->key))
- return target;
+ /* Else, create a new, bigger vector and add the new method
+ at the end of it. This is inefficient but extremely
+ rare; in any sane program most methods have a single
+ prototype, and very few, if any, will have more than
+ 2! */
+ new_entry = make_tree_vec (TREE_VEC_LENGTH (existing_entry) + 1);
+
+ /* Copy the methods from the existing vector. */
+ for (i = 0; i < TREE_VEC_LENGTH (existing_entry); i++)
+ TREE_VEC_ELT (new_entry, i) = TREE_VEC_ELT (existing_entry, i);
+
+ /* Add the new method at the end. */
+ TREE_VEC_ELT (new_entry, i) = method;
+ }
- target = target->next;
+ /* Store the new vector in the map. */
+ objc_map_put (map, method_name, new_entry);
}
- return 0;
}
-static void
-hash_add_attr (hash entry, tree value)
-{
- attr obj;
-
- obj = ggc_alloc_hashed_attribute ();
- obj->next = entry->list;
- obj->value = value;
-
- entry->list = obj; /* append to front */
-}
static tree
lookup_method (tree mchain, tree method)
@@ -5988,31 +5951,6 @@ lookup_method_static (tree interface, tree ident, int flags)
}
}
-/* Add the method to the hash list if it doesn't contain an identical
- method already. */
-
-static void
-add_method_to_hash_list (hash *hash_list, tree method)
-{
- hash hsh;
-
- if (!(hsh = hash_lookup (hash_list, METHOD_SEL_NAME (method))))
- {
- /* Install on a global chain. */
- hash_enter (hash_list, method);
- }
- else
- {
- /* Check types against those; if different, add to a list. */
- attr loop;
- int already_there = comp_proto_with_proto (method, hsh->key, 1);
- for (loop = hsh->list; !already_there && loop; loop = loop->next)
- already_there |= comp_proto_with_proto (method, loop->value, 1);
- if (!already_there)
- hash_add_attr (hsh, method);
- }
-}
-
static tree
objc_add_method (tree klass, tree method, int is_class, bool is_optional)
{
@@ -6135,10 +6073,10 @@ objc_add_method (tree klass, tree method, int is_class, bool is_optional)
}
if (is_class)
- add_method_to_hash_list (cls_method_hash_list, method);
+ insert_method_into_method_map (true, method);
else
{
- add_method_to_hash_list (nst_method_hash_list, method);
+ insert_method_into_method_map (false, method);
/* Instance methods in root classes (and categories thereof)
may act as class methods as a last resort. We also add
@@ -6151,37 +6089,12 @@ objc_add_method (tree klass, tree method, int is_class, bool is_optional)
if (TREE_CODE (klass) == PROTOCOL_INTERFACE_TYPE
|| !CLASS_SUPER_NAME (klass))
- add_method_to_hash_list (cls_method_hash_list, method);
+ insert_method_into_method_map (true, method);
}
return method;
}
-static tree
-add_class (tree class_name, tree name)
-{
- struct interface_tuple **slot;
-
- /* Put interfaces on list in reverse order. */
- TREE_CHAIN (class_name) = interface_chain;
- interface_chain = class_name;
-
- if (interface_htab == NULL)
- interface_htab = htab_create_ggc (31, hash_interface, eq_interface, NULL);
- slot = (struct interface_tuple **)
- htab_find_slot_with_hash (interface_htab, name,
- IDENTIFIER_HASH_VALUE (name),
- INSERT);
- if (!*slot)
- {
- *slot = ggc_alloc_cleared_interface_tuple ();
- (*slot)->id = name;
- }
- (*slot)->class_name = class_name;
-
- return interface_chain;
-}
-
static void
add_category (tree klass, tree category)
{
@@ -6951,8 +6864,8 @@ start_class (enum tree_code code, tree class_name, tree super_name,
{
warning (0, "cannot find interface declaration for %qE",
class_name);
- add_class (implementation_template = objc_implementation_context,
- class_name);
+ add_interface (implementation_template = objc_implementation_context,
+ class_name);
}
/* If a super class has been specified in the implementation,
@@ -6985,7 +6898,7 @@ start_class (enum tree_code code, tree class_name, tree super_name,
warning (0, "duplicate interface declaration for class %qE", class_name);
#endif
else
- add_class (klass, class_name);
+ add_interface (klass, class_name);
if (protocol_list)
CLASS_PROTOCOL_LIST (klass)