123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558 |
- /* GNU Objective C Runtime protocol related functions.
- Copyright (C) 2010-2015 Free Software Foundation, Inc.
- Contributed by Nicola Pero
- This file is part of GCC.
- GCC 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 3, or (at your option) any later version.
- GCC 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.
- Under Section 7 of GPL version 3, you are granted additional
- permissions described in the GCC Runtime Library Exception, version
- 3.1, as published by the Free Software Foundation.
- You should have received a copy of the GNU General Public License and
- a copy of the GCC Runtime Library Exception along with this program;
- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
- <http://www.gnu.org/licenses/>. */
- #include "objc-private/common.h"
- #include "objc/runtime.h"
- #include "objc-private/module-abi-8.h" /* For runtime structures */
- #include "objc/thr.h"
- #include "objc-private/runtime.h" /* the kitchen sink */
- #include "objc-private/hash.h" /* For the hash table of protocols. */
- #include "objc-private/protocols.h" /* For __objc_protocols_init() and
- __objc_protocols_add_protocol(). */
- #include <stdlib.h> /* For malloc. */
- /* This is a table that maps a name to a Protocol instance with that
- name. Because there may be multiple Protocol instances with the
- same name (no harm in that) the table records only one
- instance. */
- static cache_ptr __protocols_hashtable;
- /* A mutex protecting the protocol_hashtable. */
- static objc_mutex_t __protocols_hashtable_lock = NULL;
- /* Called at startup by init.c. */
- void
- __objc_protocols_init (void)
- {
- __protocols_hashtable_lock = objc_mutex_allocate ();
- /* The keys in the table are strings, and the values are Protocol
- objects. */
- __protocols_hashtable = objc_hash_new (64, (hash_func_type) objc_hash_string,
- (compare_func_type) objc_compare_strings);
- }
- /* Add a protocol to the hashtable. */
- void
- __objc_protocols_add_protocol (const char *name, struct objc_protocol *object)
- {
- objc_mutex_lock (__protocols_hashtable_lock);
- /* If we find a protocol with the same name already in the
- hashtable, we do not need to add the new one, because it will be
- identical to it. This in the reasonable assumption that two
- protocols with the same name are identical, which is expected in
- any sane program. If we are really paranoid, we would compare
- the protocols and abort if they are not identical.
- Unfortunately, this would slow down the startup of all
- Objective-C programs while trying to catch a problem that has
- never been seen in practice, so we don't do it. */
- if (! objc_hash_is_key_in_hash (__protocols_hashtable, name))
- objc_hash_add (&__protocols_hashtable, name, object);
- objc_mutex_unlock (__protocols_hashtable_lock);
- }
- Protocol *
- objc_getProtocol (const char *name)
- {
- Protocol *protocol;
- if (name == NULL)
- return NULL;
- objc_mutex_lock (__protocols_hashtable_lock);
- protocol = (Protocol *)(objc_hash_value_for_key (__protocols_hashtable, name));
- objc_mutex_unlock (__protocols_hashtable_lock);
- return protocol;
- }
- Protocol **
- objc_copyProtocolList (unsigned int *numberOfReturnedProtocols)
- {
- unsigned int count = 0;
- Protocol **returnValue = NULL;
- node_ptr node;
- objc_mutex_lock (__protocols_hashtable_lock);
- /* Count how many protocols we have. */
- node = objc_hash_next (__protocols_hashtable, NULL);
- while (node)
- {
- count++;
- node = objc_hash_next (__protocols_hashtable, node);
- }
- if (count != 0)
- {
- unsigned int i = 0;
- /* Allocate enough memory to hold them. */
- returnValue = (Protocol **)(malloc (sizeof (Protocol *) * (count + 1)));
-
- /* Copy the protocols. */
- node = objc_hash_next (__protocols_hashtable, NULL);
- while (node)
- {
- returnValue[i] = node->value;
- i++;
- node = objc_hash_next (__protocols_hashtable, node);
- }
- returnValue[i] = NULL;
- }
- objc_mutex_unlock (__protocols_hashtable_lock);
- if (numberOfReturnedProtocols)
- *numberOfReturnedProtocols = count;
- return returnValue;
- }
- BOOL
- class_addProtocol (Class class_, Protocol *protocol)
- {
- struct objc_protocol_list *protocols;
- if (class_ == Nil || protocol == NULL)
- return NO;
- if (class_conformsToProtocol (class_, protocol))
- return NO;
- /* Check that it is a Protocol object before casting it to (struct
- objc_protocol *). */
- if (protocol->class_pointer != objc_lookUpClass ("Protocol"))
- return NO;
- objc_mutex_lock (__objc_runtime_mutex);
- /* Create the objc_protocol_list. */
- protocols = malloc (sizeof (struct objc_protocol_list));
- protocols->count = 1;
- protocols->list[0] = (struct objc_protocol *)protocol;
- /* Attach it to the list of class protocols. */
- protocols->next = class_->protocols;
- class_->protocols = protocols;
- objc_mutex_unlock (__objc_runtime_mutex);
- return YES;
- }
- BOOL
- class_conformsToProtocol (Class class_, Protocol *protocol)
- {
- struct objc_protocol_list* proto_list;
- if (class_ == Nil || protocol == NULL)
- return NO;
- /* Check that it is a Protocol object before casting it to (struct
- objc_protocol *). */
- if (protocol->class_pointer != objc_lookUpClass ("Protocol"))
- return NO;
- /* Acquire the runtime lock because the list of protocols for a
- class may be modified concurrently, for example if another thread
- calls class_addProtocol(), or dynamically loads from a file a
- category of the class. */
- objc_mutex_lock (__objc_runtime_mutex);
- proto_list = class_->protocols;
- while (proto_list)
- {
- size_t i;
- for (i = 0; i < proto_list->count; i++)
- {
- if (proto_list->list[i] == (struct objc_protocol *)protocol
- || protocol_conformsToProtocol ((Protocol *)proto_list->list[i],
- protocol))
- {
- objc_mutex_unlock (__objc_runtime_mutex);
- return YES;
- }
- }
- proto_list = proto_list->next;
- }
-
- objc_mutex_unlock (__objc_runtime_mutex);
- return NO;
- }
- Protocol **
- class_copyProtocolList (Class class_, unsigned int *numberOfReturnedProtocols)
- {
- unsigned int count = 0;
- Protocol **returnValue = NULL;
- struct objc_protocol_list* proto_list;
- if (class_ == Nil)
- {
- if (numberOfReturnedProtocols)
- *numberOfReturnedProtocols = 0;
- return NULL;
- }
- /* Lock the runtime mutex because the class protocols may be
- concurrently modified. */
- objc_mutex_lock (__objc_runtime_mutex);
- /* Count how many protocols we have. */
- proto_list = class_->protocols;
- while (proto_list)
- {
- count = count + proto_list->count;
- proto_list = proto_list->next;
- }
- if (count != 0)
- {
- unsigned int i = 0;
-
- /* Allocate enough memory to hold them. */
- returnValue = (Protocol **)(malloc (sizeof (Protocol *) * (count + 1)));
-
- /* Copy the protocols. */
- proto_list = class_->protocols;
-
- while (proto_list)
- {
- size_t j;
- for (j = 0; j < proto_list->count; j++)
- {
- returnValue[i] = (Protocol *)proto_list->list[j];
- i++;
- }
- proto_list = proto_list->next;
- }
-
- returnValue[i] = NULL;
- }
- objc_mutex_unlock (__objc_runtime_mutex);
- if (numberOfReturnedProtocols)
- *numberOfReturnedProtocols = count;
- return returnValue;
- }
- BOOL
- protocol_conformsToProtocol (Protocol *protocol, Protocol *anotherProtocol)
- {
- struct objc_protocol_list* proto_list;
- if (protocol == NULL || anotherProtocol == NULL)
- return NO;
- if (protocol == anotherProtocol)
- return YES;
-
- /* Check that the objects are Protocol objects before casting them
- to (struct objc_protocol *). */
- if (protocol->class_pointer != anotherProtocol->class_pointer)
- return NO;
-
- if (protocol->class_pointer != objc_lookUpClass ("Protocol"))
- return NO;
- if (strcmp (((struct objc_protocol *)protocol)->protocol_name,
- ((struct objc_protocol *)anotherProtocol)->protocol_name) == 0)
- return YES;
- /* We do not acquire any lock because protocols are currently
- immutable. We can freely iterate over a protocol structure. */
- proto_list = ((struct objc_protocol *)protocol)->protocol_list;
- while (proto_list)
- {
- size_t i;
-
- for (i = 0; i < proto_list->count; i++)
- {
- if (protocol_conformsToProtocol ((Protocol *)proto_list->list[i], anotherProtocol))
- return YES;
- }
- proto_list = proto_list->next;
- }
- return NO;
- }
- BOOL
- protocol_isEqual (Protocol *protocol, Protocol *anotherProtocol)
- {
- if (protocol == anotherProtocol)
- return YES;
- if (protocol == NULL || anotherProtocol == NULL)
- return NO;
-
- /* Check that the objects are Protocol objects before casting them
- to (struct objc_protocol *). */
- if (protocol->class_pointer != anotherProtocol->class_pointer)
- return NO;
-
- if (protocol->class_pointer != objc_lookUpClass ("Protocol"))
- return NO;
- /* Equality between formal protocols is only formal (nothing to do
- with actually checking the list of methods they have!). Two
- formal Protocols are equal if and only if they have the same
- name.
- Please note (for comparisons with other implementations) that
- checking the names is equivalent to checking that Protocol A
- conforms to Protocol B and Protocol B conforms to Protocol A,
- because this happens iff they have the same name. If they have
- different names, A conforms to B if and only if A includes B, but
- the situation where A includes B and B includes A is a circular
- dependency between Protocols which is forbidden by the compiler,
- so A conforms to B and B conforms to A with A and B having
- different names is an impossible case. */
- if (strcmp (((struct objc_protocol *)protocol)->protocol_name,
- ((struct objc_protocol *)anotherProtocol)->protocol_name) == 0)
- return YES;
-
- return NO;
- }
- const char *
- protocol_getName (Protocol *protocol)
- {
- /* Check that it is a Protocol object before casting it to (struct
- objc_protocol *). */
- if (protocol->class_pointer != objc_lookUpClass ("Protocol"))
- return NULL;
- return ((struct objc_protocol *)protocol)->protocol_name;
- }
- struct objc_method_description protocol_getMethodDescription (Protocol *protocol,
- SEL selector,
- BOOL requiredMethod,
- BOOL instanceMethod)
- {
- struct objc_method_description no_result = { NULL, NULL };
- struct objc_method_description_list *methods;
- int i;
- /* TODO: New ABI. */
- /* The current ABI does not have any information on optional protocol methods. */
- if (! requiredMethod)
- return no_result;
- /* Check that it is a Protocol object before casting it to (struct
- objc_protocol *). */
- if (protocol->class_pointer != objc_lookUpClass ("Protocol"))
- return no_result;
- if (instanceMethod)
- methods = ((struct objc_protocol *)protocol)->instance_methods;
- else
- methods = ((struct objc_protocol *)protocol)->class_methods;
- if (methods)
- {
- for (i = 0; i < methods->count; i++)
- {
- if (sel_isEqual (methods->list[i].name, selector))
- return methods->list[i];
- /*
- if (strcmp (sel_getName (methods->list[i].name), selector_name) == 0)
- return methods->list[i];
- */
- }
- }
- return no_result;
- }
- struct objc_method_description *protocol_copyMethodDescriptionList (Protocol *protocol,
- BOOL requiredMethod,
- BOOL instanceMethod,
- unsigned int *numberOfReturnedMethods)
- {
- struct objc_method_description_list *methods;
- unsigned int count = 0;
- struct objc_method_description *returnValue = NULL;
- /* TODO: New ABI */
- /* The current ABI does not have any information on optional protocol methods. */
- if (! requiredMethod)
- {
- if (numberOfReturnedMethods)
- *numberOfReturnedMethods = 0;
- return NULL;
- }
- /* Check that it is a Protocol object before casting it to (struct
- objc_protocol *). */
- if (protocol == NULL || protocol->class_pointer != objc_lookUpClass ("Protocol"))
- {
- if (numberOfReturnedMethods)
- *numberOfReturnedMethods = 0;
- return NULL;
- }
-
- /* We do not acquire any lock because protocols are currently
- immutable. We can freely iterate over a protocol structure. */
- if (instanceMethod)
- methods = ((struct objc_protocol *)protocol)->instance_methods;
- else
- methods = ((struct objc_protocol *)protocol)->class_methods;
- if (methods)
- {
- unsigned int i;
- count = methods->count;
- /* Allocate enough memory to hold them. */
- returnValue = (struct objc_method_description *)(malloc (sizeof (struct objc_method_description) * (count + 1)));
- /* Copy them. */
- for (i = 0; i < count; i++)
- {
- returnValue[i].name = methods->list[i].name;
- returnValue[i].types = methods->list[i].types;
- }
- returnValue[i].name = NULL;
- returnValue[i].types = NULL;
- }
- if (numberOfReturnedMethods)
- *numberOfReturnedMethods = count;
- return returnValue;
- }
- Property protocol_getProperty (Protocol *protocol, const char *propertyName,
- BOOL requiredProperty, BOOL instanceProperty)
- {
- if (protocol == NULL || propertyName == NULL)
- return NULL;
- if (!requiredProperty || !instanceProperty)
- return NULL;
- /* Check that it is a Protocol object before casting it to (struct
- objc_protocol *). */
- if (protocol->class_pointer != objc_lookUpClass ("Protocol"))
- return NULL;
- /* TODO: New ABI. */
- /* The current ABI does not have any information on protocol properties. */
- return NULL;
- }
- Property *protocol_copyPropertyList (Protocol *protocol, unsigned int *numberOfReturnedProperties)
- {
- unsigned int count = 0;
- Property *returnValue = NULL;
- /* Check that it is a Protocol object before casting it to (struct
- objc_protocol *). */
- if (protocol == NULL || protocol->class_pointer != objc_lookUpClass ("Protocol"))
- {
- if (numberOfReturnedProperties)
- *numberOfReturnedProperties = 0;
- return NULL;
- }
-
- /* We do not acquire any lock because protocols are currently
- immutable. We can freely iterate over a protocol structure. */
- /* TODO: New ABI. */
- /* The current ABI does not have any information on protocol properties. */
- if (numberOfReturnedProperties)
- *numberOfReturnedProperties = count;
- return returnValue;
- }
- Protocol **protocol_copyProtocolList (Protocol *protocol, unsigned int *numberOfReturnedProtocols)
- {
- unsigned int count = 0;
- Protocol **returnValue = NULL;
- struct objc_protocol_list* proto_list;
- /* Check that it is a Protocol object before casting it to (struct
- objc_protocol *). */
- if (protocol == NULL || protocol->class_pointer != objc_lookUpClass ("Protocol"))
- {
- if (numberOfReturnedProtocols)
- *numberOfReturnedProtocols = 0;
- return NULL;
- }
-
- /* We do not acquire any lock because protocols are currently
- immutable. We can freely iterate over a protocol structure. */
- /* Count how many protocols we have. */
- proto_list = ((struct objc_protocol *)protocol)->protocol_list;
- while (proto_list)
- {
- count = count + proto_list->count;
- proto_list = proto_list->next;
- }
- if (count != 0)
- {
- unsigned int i = 0;
-
- /* Allocate enough memory to hold them. */
- returnValue = (Protocol **)(malloc (sizeof (Protocol *) * (count + 1)));
-
- /* Copy the protocols. */
- proto_list = ((struct objc_protocol *)protocol)->protocol_list;
-
- while (proto_list)
- {
- size_t j;
- for (j = 0; j < proto_list->count; j++)
- {
- returnValue[i] = (Protocol *)proto_list->list[j];
- i++;
- }
- proto_list = proto_list->next;
- }
- returnValue[i] = NULL;
- }
- if (numberOfReturnedProtocols)
- *numberOfReturnedProtocols = count;
- return returnValue;
- }
|