From patchwork Fri Dec 24 17:00:40 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Nicola Pero X-Patchwork-Id: 76633 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id 075F8B708B for ; Sat, 25 Dec 2010 04:01:15 +1100 (EST) Received: (qmail 7454 invoked by alias); 24 Dec 2010 17:01:13 -0000 Received: (qmail 7434 invoked by uid 22791); 24 Dec 2010 17:01:10 -0000 X-SWARE-Spam-Status: No, hits=-0.0 required=5.0 tests=AWL, BAYES_50, TW_BJ, T_RP_MATCHES_RCVD X-Spam-Check-By: sourceware.org Received: from fencepost.gnu.org (HELO fencepost.gnu.org) (140.186.70.10) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Fri, 24 Dec 2010 17:01:02 +0000 Received: from eggs.gnu.org ([140.186.70.92]:42706) by fencepost.gnu.org with esmtps (TLS-1.0:RSA_AES_256_CBC_SHA1:32) (Exim 4.69) (envelope-from ) id 1PWB0j-0005Jb-7M for gcc-patches@gnu.org; Fri, 24 Dec 2010 12:00:57 -0500 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1PWB0X-0006h3-6w for gcc-patches@gnu.org; Fri, 24 Dec 2010 12:00:59 -0500 Received: from smtp201.iad.emailsrvr.com ([207.97.245.201]:52463) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1PWB0W-0006gh-Vw for gcc-patches@gnu.org; Fri, 24 Dec 2010 12:00:45 -0500 Received: from localhost (localhost.localdomain [127.0.0.1]) by smtp50.relay.iad1a.emailsrvr.com (SMTP Server) with ESMTP id 530443709C1 for ; Fri, 24 Dec 2010 12:00:44 -0500 (EST) Received: by smtp50.relay.iad1a.emailsrvr.com (Authenticated sender: nicola.pero-AT-meta-innovation.com) with ESMTPA id A6F023709BE for ; Fri, 24 Dec 2010 12:00:43 -0500 (EST) Message-Id: <409E9DE2-BD6E-4247-A05A-3B018527E5FE@meta-innovation.com> From: Nicola Pero To: gcc-patches@gnu.org Mime-Version: 1.0 (Apple Message framework v936) Subject: libobjc: review and complete the "Modern" typed selector API Date: Fri, 24 Dec 2010 18:00:40 +0100 X-detected-operating-system: by eggs.gnu.org: GNU/Linux 2.6 (newer, 3) X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org This patch is a review of libobjc's "Modern" API for typed selectors. We can't be compatible with the Apple runtime because the Apple runtime doesn't have typed selectors, but we also can't really leave in place the "Traditional" GNU Objective-C API with sel_get_typed_uid() etc. as it feels completely out of place in the context of the "Modern" GNU Objective-C API. This patch: * renames the GNU-specific 'sel_getType()' function to 'sel_getTypeEncoding()' to be consistent with the rest of the Modern API, which has 'method_getTypeEncoding()' and 'ivar_getTypeEncoding()' * adds 'sel_copyTypedSelectorList()' which is very general and allows you to build any query you want on typed selectors on top of it. It is consistent with the rest of the 'Modern' API which has got class_copyIvarList(), class_copyMethodList() etc. to get access to raw lists of things. Of course, like these other functions, it is relatively slow as it required a malloc, but it's always good to have as a fallback. * adds 'sel_getTypedSelector()' which is similar to sel_get_any_typed_uid() or sel_get_typed_uid() etc. in the Traditional API. It is a fast way to do the most likely query with typed selectors, which is to get a typed selector by name (sel_registerName() doesn't do it as it would register a new selector with no types; sel_registerTypedName() wouldn't do it either). With these changes, I think we can safely deprecate the old Traditional API for typed selectors. :-) Committed to trunk. Thanks /** Implementation: the following functions are in objects.c. */ Index: ChangeLog =================================================================== --- ChangeLog (revision 168211) +++ ChangeLog (working copy) @@ -1,3 +1,11 @@ +2010-12-24 Nicola Pero + + * objc.dg/gnu-api-2-sel.m: Updated for renaming of sel_getType to + sel_getTypeEncoding. Test that sel_getTypeEncoding returns NULL + when called with a NULL argument. Added test for + sel_copyTypedSelectorList and sel_getTypedSelector. + * obj-c++.dg/gnu-api-2-sel.mm: Same changes. + 2010-12-22 Sebastian Pop PR tree-optimization/46758 Index: objc.dg/gnu-api-2-sel.m =================================================================== --- objc.dg/gnu-api-2-sel.m (revision 168228) +++ objc.dg/gnu-api-2-sel.m (working copy) @@ -35,11 +35,13 @@ { id variable_ivar; } - (void) setVariable: (id)value; - (id) variable; +- (void) method; @end @implementation MySubClass - (void) setVariable: (id)value { variable_ivar = value; } - (id) variable { return variable_ivar; } +- (void) method { return; } @end @@ -47,6 +49,30 @@ { /* Functions are tested in alphabetical order. */ + printf ("Testing sel_copyTypedSelectorList ()...\n"); + { + unsigned int count; + SEL * list = sel_copyTypedSelectorList ("method", &count); + + /* There should only be two, since 'method' is referenced twice, + once with types and once without (in this very test). */ + if (count != 2) + abort (); + + /* Check that both selectors are not-NULL, and have the correct + name. We use @selector() here, which wouldn't really be + needed, just to register a second, untyped selector with name + 'method'. */ + if (strcmp (sel_getName (list[0]), sel_getName (@selector (method))) != 0) + abort (); + + if (strcmp (sel_getName (list[1]), sel_getName (@selector (method))) != 0) + abort (); + + if (list[2] != NULL) + abort (); + } + printf ("Testing sel_getName () ...\n"); { if (strcmp (sel_getName (@selector (variable)), "variable") != 0) @@ -56,17 +82,53 @@ abort (); } - printf ("Testing sel_getType () ...\n"); + printf ("Testing sel_getTypeEncoding () ...\n"); { /* Get a selector from a real class, so it has interesting types. */ Method method = class_getInstanceMethod (objc_getClass ("MySubClass"), @selector (variable)); - if (strcmp (sel_getType (method_getName (method)), method_getTypeEncoding (method)) != 0) + if (strcmp (sel_getTypeEncoding (method_getName (method)), + method_getTypeEncoding (method)) != 0) abort (); + + if (sel_getTypeEncoding (NULL) != NULL) + abort (); } + printf ("Testing sel_getTypedSelector () ...\n"); + { + /* First try with a selector where we know that a typed one has + been registered. */ + SEL selector = sel_getTypedSelector ("variable"); + + if (selector == NULL) + abort (); + + if (sel_getTypeEncoding (selector) == NULL) + abort (); + + /* Now try a selector which was never registered. */ + selector = sel_getTypedSelector ("not_registered"); + + if (selector != NULL) + abort (); + + /* Now try registering a selector with no types. The following + line is just a way to have an unused '@selector()' expression + without the compiler complaining. */ + if (@selector (registered_with_no_types) == NULL) + abort (); + + /* Try getting it. Nothing should be returned because it is + untyped. */ + selector = sel_getTypedSelector ("registered_with_no_types"); + + if (selector != NULL) + abort (); + } + printf ("Testing sel_getUid () ...\n"); { if (strcmp (sel_getName (sel_getUid ("myMethod")), "myMethod") ! = 0) @@ -95,7 +157,7 @@ if (strcmp (sel_getName (selector), "aMethod") != 0) abort (); - if (strcmp (sel_getType (selector), types) != 0) + if (strcmp (sel_getTypeEncoding (selector), types) != 0) abort (); } Index: obj-c++.dg/gnu-api-2-sel.mm =================================================================== --- obj-c++.dg/gnu-api-2-sel.mm (revision 168211) +++ obj-c++.dg/gnu-api-2-sel.mm (working copy) @@ -35,11 +35,13 @@ { id variable_ivar; } - (void) setVariable: (id)value; - (id) variable; +- (void) method; @end @implementation MySubClass - (void) setVariable: (id)value { variable_ivar = value; } - (id) variable { return variable_ivar; } +- (void) method { return; } @end @@ -47,6 +49,30 @@ { /* Functions are tested in alphabetical order. */ + std::cout << "Testing sel_copyTypedSelectorList ()...\n"; + { + unsigned int count; + SEL * list = sel_copyTypedSelectorList ("method", &count); + + /* There should only be two, since 'method' is referenced twice, + once with types and once without (in this very test). */ + if (count != 2) + abort (); + + /* Check that both selectors are not-NULL, and have the correct + name. We use @selector() here, which wouldn't really be + needed, just to register a second, untyped selector with name + 'method'. */ + if (std::strcmp (sel_getName (list[0]), sel_getName (@selector (method))) != 0) + abort (); + + if (std::strcmp (sel_getName (list[1]), sel_getName (@selector (method))) != 0) + abort (); + + if (list[2] != NULL) + abort (); + } + std::cout << "Testing sel_getName () ...\n"; { if (std::strcmp (sel_getName (@selector (variable)), "variable") != 0) @@ -56,17 +82,53 @@ abort (); } - std::cout << "Testing sel_getType () ...\n"; + std::cout << "Testing sel_getTypeEncoding () ...\n"; { /* Get a selector from a real class, so it has interesting types. */ Method method = class_getInstanceMethod (objc_getClass ("MySubClass"), @selector (variable)); - if (std::strcmp (sel_getType (method_getName (method)), method_getTypeEncoding (method)) != 0) + if (std::strcmp (sel_getTypeEncoding (method_getName (method)), + method_getTypeEncoding (method)) != 0) abort (); + + if (sel_getTypeEncoding (NULL) != NULL) + abort (); } + std::cout << "Testing sel_getTypedSelector () ...\n"; + { + /* First try with a selector where we know that a typed one has + been registered. */ + SEL selector = sel_getTypedSelector ("variable"); + + if (selector == NULL) + abort (); + + if (sel_getTypeEncoding (selector) == NULL) + abort (); + + /* Now try a selector which was never registered. */ + selector = sel_getTypedSelector ("not_registered"); + + if (selector != NULL) + abort (); + + /* Now try registering a selector with no types. The following + line is just a wayx to have an unused '@selector()' expression + without the compiler complaining. */ + if (@selector (registered_with_no_types) == NULL) + abort (); + + /* Try getting it. Nothing should be returned because it is + untyped. */ + selector = sel_getTypedSelector ("registered_with_no_types"); + + if (selector != NULL) + abort (); + } + std::cout << "Testing sel_getUid () ...\n"; { if (std::strcmp (sel_getName (sel_getUid ("myMethod")), "myMethod") != 0) @@ -91,11 +153,11 @@ (objc_getClass ("MySubClass"), @selector (variable))); SEL selector = sel_registerTypedName ("aMethod", types); - + if (std::strcmp (sel_getName (selector), "aMethod") != 0) abort (); - if (std::strcmp (sel_getType (selector), types) != 0) + if (std::strcmp (sel_getTypeEncoding (selector), types) != 0) abort (); } Index: selector.c =================================================================== --- selector.c (revision 168211) +++ selector.c (working copy) @@ -31,6 +31,7 @@ see the files COPYING3 and COPYING.RUNTI #include "objc-private/runtime.h" #include "objc-private/sarray.h" #include "objc-private/selector.h" +#include /* For malloc. */ /* Initial selector hash table size. Value doesn't matter much. */ #define SELECTOR_HASH_SIZE 128 @@ -250,7 +251,11 @@ sel_types_match (const char *t1, const c return NO; } -/* Return selector representing name. */ +/* Return selector representing name. In the Modern API, you'd + normally use sel_registerTypedName() for this, which does the same + but would register the selector with the runtime if not registered + yet (if you only want to check for selectors without registering, + use sel_copyTypedSelectorList()). */ SEL sel_get_typed_uid (const char *name, const char *types) { @@ -290,7 +295,8 @@ sel_get_typed_uid (const char *name, con } /* Return selector representing name; prefer a selector with non-NULL - type. */ + type. In the Modern API, sel_getTypedSelector() is similar but + returns NULL if a typed selector couldn't be found. */ SEL sel_get_any_typed_uid (const char *name) { @@ -347,6 +353,91 @@ sel_get_any_uid (const char *name) return (SEL) l->head; } +SEL +sel_getTypedSelector (const char *name) +{ + sidx i; + objc_mutex_lock (__objc_runtime_mutex); + + /* Look for a typed selector. */ + i = (sidx) objc_hash_value_for_key (__objc_selector_hash, name); + if (i != 0) + { + struct objc_list *l; + + for (l = (struct objc_list *) sarray_get_safe (__objc_selector_array, i); + l; l = l->tail) + { + SEL s = (SEL) l->head; + if (s->sel_types) + { + objc_mutex_unlock (__objc_runtime_mutex); + return s; + } + } + } + + /* No typed selector found. Return NULL. */ + objc_mutex_unlock (__objc_runtime_mutex); + return 0; +} + +SEL * +sel_copyTypedSelectorList (const char *name, unsigned int *numberOfReturnedSelectors) +{ + unsigned int count = 0; + SEL *returnValue = NULL; + sidx i; + + if (name == NULL) + { + if (numberOfReturnedSelectors) + *numberOfReturnedSelectors = 0; + return NULL; + } + + objc_mutex_lock (__objc_runtime_mutex); + + /* Count how many selectors we have. */ + i = (sidx) objc_hash_value_for_key (__objc_selector_hash, name); + if (i != 0) + { + struct objc_list *selector_list = NULL; + selector_list = (struct objc_list *) sarray_get_safe (__objc_selector_array, i); + + /* Count how many selectors we have. */ + { + struct objc_list *l; + for (l = selector_list; l; l = l->tail) + count++; + } + + if (count != 0) + { + /* Allocate enough memory to hold them. */ + returnValue = (SEL *)(malloc (sizeof (SEL) * (count + 1))); + + /* Copy the selectors. */ + { + unsigned int j; + for (j = 0; j < count; j++) + { + returnValue[j] = (SEL)(selector_list->head); + selector_list = selector_list->tail; + } + returnValue[j] = NULL; + } + } + } + + objc_mutex_unlock (__objc_runtime_mutex); + + if (numberOfReturnedSelectors) + *numberOfReturnedSelectors = count; + + return returnValue; +} + /* Get the name of a selector. If the selector is unknown, the empty string "" is returned. */ const char *sel_getName (SEL selector) @@ -382,7 +473,7 @@ sel_is_mapped (SEL selector) return ((idx > 0) && (idx <= __objc_selector_max_index)); } -const char *sel_getType (SEL selector) +const char *sel_getTypeEncoding (SEL selector) { if (selector) return selector->sel_types; @@ -393,7 +484,7 @@ const char *sel_getType (SEL selector) /* Traditional GNU Objective-C Runtime API. */ const char *sel_get_type (SEL selector) { - return sel_getType (selector); + return sel_getTypeEncoding (selector); } /* The uninstalled dispatch table. */ Index: ChangeLog =================================================================== --- ChangeLog (revision 168215) +++ ChangeLog (working copy) @@ -1,3 +1,13 @@ +2010-12-24 Nicola Pero + + * objc/runtime.h (sel_getType): Renamed to sel_getTypeEncoding to + be consistent with method_getTypeEncoding and + ivar_getTypeEncoding. + (sel_copyTypedSelectorList, sel_getTypedSelector): New. + * selector.c (sel_getType): Renamed to sel_getTypeEncoding. + (sel_copyTypedSelectorList, sel_getTypedSelector): New. + (sel_get_type): Updated call to sel_getType. + 2010-12-23 Nicola Pero * init.c (create_tree_of_subclasses_inherited_from): Updated Index: objc/runtime.h =================================================================== --- objc/runtime.h (revision 168211) +++ objc/runtime.h (working copy) @@ -175,12 +175,13 @@ object_getClass (id object) "". */ objc_EXPORT const char *sel_getName (SEL selector); -/* Return the type of a given selector. +/* Return the type of a given selector. Return NULL if selector is + NULL. Compatibility Note: the Apple/NeXT runtime has untyped selectors, so it does not have this function, which is specific to the GNU Runtime. */ -objc_EXPORT const char *sel_getType (SEL selector); +objc_EXPORT const char *sel_getTypeEncoding (SEL selector); /* This is the same as sel_registerName (). Please use sel_registerName () instead. */ @@ -188,11 +189,16 @@ objc_EXPORT SEL sel_getUid (const char * /* Register a selector with a given name (but unspecified types). If you know the types, it is better to call sel_registerTypedName(). - If a selector with this name already exists, it is returned. */ + If a selector with this name and no types already exists, it is + returned. Note that this function should really be called + 'objc_registerSelector'. */ objc_EXPORT SEL sel_registerName (const char *name); /* Register a selector with a given name and types. If a selector - with this name and types already exists, it is returned. + with this name and types already exists, it is returned. Note that + this function should really be called 'objc_registerTypedSelector', + and it's called 'sel_registerTypedName' only for consistency with + 'sel_registerName'. Compatibility Note: the Apple/NeXT runtime has untyped selectors, so it does not have this function, which is specific to the GNU @@ -203,6 +209,37 @@ objc_EXPORT SEL sel_registerTypedName (c if not. */ objc_EXPORT BOOL sel_isEqual (SEL first_selector, SEL second_selector); +/* Return all the selectors with the supplied name. In the GNU + runtime, selectors are typed and there may be multiple selectors + with the same name but a different type. The return value of the + function is a pointer to an area, allocated with malloc(), that + contains all the selectors with the supplier name known to the + runtime. The list is terminated by NULL. Optionally, if you pass + a non-NULL 'numberOfReturnedSelectors' pointer, the unsigned int + that it points to will be filled with the number of selectors + returned. + + Compatibility Note: the Apple/NeXT runtime has untyped selectors, + so it does not have this function, which is specific to the GNU + Runtime. */ +objc_EXPORT SEL * sel_copyTypedSelectorList (const char *name, + unsigned int *numberOfReturnedSelectors); + +/* Return a selector with name 'name' and a non-zero type encoding, if + any such selector is registered with the runtime. If there is no + such selector, NULL is returned. + + This is useful if you have the name of the selector, and would + really like to get a selector for it that includes the type + encoding. Unfortunately, if the program contains multiple selector + with the same name but different types, sel_getTypedSelector + returns a random one of them, which may not be the right one. + + Compatibility Note: the Apple/NeXT runtime has untyped selectors, + so it does not have this function, which is specific to the GNU + Runtime. */ +objc_EXPORT SEL sel_getTypedSelector (const char *name); +