Improve protocol method metadata.

Methods now include a selector and extended type encoding, rather than a
method name and lgacy type encoding.  Older ones are auto-upgraded.

Expose the extended type encoding via a function that JavaScriptCore
expects to exist.
main
David Chisnall 8 years ago
parent 65b34a196f
commit ab84589b5d

@ -20,6 +20,7 @@ set(TESTS
ManyManySelectors.m ManyManySelectors.m
NestedExceptions.m NestedExceptions.m
PropertyAttributeTest.m PropertyAttributeTest.m
ProtocolExtendedProperties.m
PropertyIntrospectionTest.m PropertyIntrospectionTest.m
PropertyIntrospectionTest2_arc.m PropertyIntrospectionTest2_arc.m
ProtocolCreation.m ProtocolCreation.m
@ -82,9 +83,9 @@ function(addtest_flags TEST_NAME FLAGS TEST_SOURCE)
endfunction(addtest_flags) endfunction(addtest_flags)
function(addtest_variants TEST TEST_SOURCE LEGACY) function(addtest_variants TEST TEST_SOURCE LEGACY)
addtest_flags(${TEST} "-O0 -fobjc-runtime=gnustep-2.0 -UNDEBUG" "${TEST_SOURCE}") addtest_flags(${TEST} "-O0 -fobjc-runtime=gnustep-2.0 -UNDEBUG -DGS_RUNTIME_V2" "${TEST_SOURCE}")
target_sources(${TEST} PRIVATE $<TARGET_OBJECTS:test_runtime>) target_sources(${TEST} PRIVATE $<TARGET_OBJECTS:test_runtime>)
addtest_flags("${TEST}_optimised" "-O3 -fobjc-runtime=gnustep-2.0 -UNDEBUG" "${TEST_SOURCE}") addtest_flags("${TEST}_optimised" "-O3 -fobjc-runtime=gnustep-2.0 -UNDEBUG -DGS_RUNTIME_V2" "${TEST_SOURCE}")
target_sources("${TEST}_optimised" PRIVATE $<TARGET_OBJECTS:test_runtime>) target_sources("${TEST}_optimised" PRIVATE $<TARGET_OBJECTS:test_runtime>)
if (LEGACY) if (LEGACY)
addtest_flags("${TEST}_legacy" "-O0 -fobjc-runtime=gnustep-1.7 -UNDEBUG" "${TEST_SOURCE}") addtest_flags("${TEST}_legacy" "-O0 -fobjc-runtime=gnustep-1.7 -UNDEBUG" "${TEST_SOURCE}")

@ -0,0 +1,19 @@
#include "Test.h"
#include <string.h>
@class NSString;
@protocol Foo
- (NSString*)aMethod: (void(^)(int))aBlock;
@end
int main(void)
{
const char *encoding = _protocol_getMethodTypeEncoding(@protocol(Foo), @selector(aMethod:), YES, YES);
#ifdef GS_RUNTIME_V2
// We expect something like this (LP64): @"NSString"24@0:8@?<v@?i>16
assert(strstr(encoding, "@\"NSString\"") == encoding);
assert(strstr(encoding, "@?<v@?i>") != NULL);
#else
assert(strstr(encoding, "@?") != NULL);
#endif
}

@ -283,6 +283,26 @@ PRIVATE struct objc_category *objc_upgrade_category(struct objc_category_legacy
return cat; return cat;
} }
static struct objc_protocol_method_description_list*
upgrade_protocol_method_list_gcc(struct objc_protocol_method_description_list_gcc *l)
{
if ((l == NULL) || (l->count == 0))
{
return NULL;
}
struct objc_protocol_method_description_list *n =
malloc(sizeof(struct objc_protocol_method_description_list) +
l->count * sizeof(struct objc_protocol_method_description));
n->count = l->count;
n->size = sizeof(struct objc_protocol_method_description);
for (int i=0 ; i<n->count ; i++)
{
n->methods[i].selector = sel_registerTypedName_np(l->methods[i].name, l->methods[i].types);
n->methods[i].types = l->methods[i].types;
}
return n;
}
PRIVATE struct objc_protocol *objc_upgrade_protocol_gcc(struct objc_protocol_gcc *p) PRIVATE struct objc_protocol *objc_upgrade_protocol_gcc(struct objc_protocol_gcc *p)
{ {
// If the protocol has already been upgraded, the don't try to upgrade it twice. // If the protocol has already been upgraded, the don't try to upgrade it twice.
@ -297,8 +317,8 @@ PRIVATE struct objc_protocol *objc_upgrade_protocol_gcc(struct objc_protocol_gcc
proto->name = p->name; proto->name = p->name;
// Aliasing this means that when this returns these will all be updated. // Aliasing this means that when this returns these will all be updated.
proto->protocol_list = p->protocol_list; proto->protocol_list = p->protocol_list;
proto->instance_methods = p->instance_methods; proto->instance_methods = upgrade_protocol_method_list_gcc(p->instance_methods);
proto->class_methods = p->class_methods; proto->class_methods = upgrade_protocol_method_list_gcc(p->class_methods);
assert(proto->isa); assert(proto->isa);
return proto; return proto;
} }
@ -310,14 +330,12 @@ PRIVATE struct objc_protocol *objc_upgrade_protocol_gsv1(struct objc_protocol_gs
{ {
return (struct objc_protocol*)p; return (struct objc_protocol*)p;
} }
if (p->properties) // We do in-place upgrading of these, because they might be referenced
{ // directly
p->properties = (struct objc_property_list_gsv1*)upgradePropertyList(p->properties); p->instance_methods = (struct objc_protocol_method_description_list_gcc*)upgrade_protocol_method_list_gcc(p->instance_methods);
} p->class_methods = (struct objc_protocol_method_description_list_gcc*)upgrade_protocol_method_list_gcc(p->class_methods);
if (p->optional_properties) p->properties = (struct objc_property_list_gsv1*)upgradePropertyList(p->properties);
{ p->optional_properties = (struct objc_property_list_gsv1*)upgradePropertyList(p->optional_properties);
p->optional_properties = (struct objc_property_list_gsv1*)upgradePropertyList(p->optional_properties);
}
p->isa = objc_getClass("Protocol"); p->isa = objc_getClass("Protocol");
assert(p->isa); assert(p->isa);
return (struct objc_protocol*)p; return (struct objc_protocol*)p;

@ -8,14 +8,11 @@ struct objc_method
*/ */
IMP imp; IMP imp;
/** /**
* Selector used to send messages to this method. The type encoding of * Selector used to send messages to this method.
* this method should match the types field.
*/ */
SEL selector; SEL selector;
/** /**
* The type encoding for this selector. Used only for introspection, and * The extended type encoding for this method.
* only required because of the stupid selector handling in the old GNU
* runtime. In future, this field may be reused for something else.
*/ */
const char *types; const char *types;
}; };

@ -740,6 +740,16 @@ Protocol *__unsafe_unretained*objc_copyProtocolList(unsigned int *outCount);
struct objc_method_description protocol_getMethodDescription(Protocol *p, struct objc_method_description protocol_getMethodDescription(Protocol *p,
SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod); SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod);
/**
* Returns the extended type encoding of the specified method.
*
* Note: This function is used by JavaScriptCore but is not public in Apple's
* implementation and so its semantics may change in the future and this
* runtime may diverge from Apple's.
*/
const char *_protocol_getMethodTypeEncoding(Protocol *p, SEL aSel,
BOOL isRequiredMethod, BOOL isInstanceMethod);
/** /**
* Returns the name of the specified protocol. * Returns the name of the specified protocol.
*/ */

@ -316,12 +316,12 @@ BOOL class_conformsToProtocol(Class cls, Protocol *protocol)
return NO; return NO;
} }
static struct objc_method_description_list * static struct objc_protocol_method_description_list *
get_method_list(Protocol *p, get_method_list(Protocol *p,
BOOL isRequiredMethod, BOOL isRequiredMethod,
BOOL isInstanceMethod) BOOL isInstanceMethod)
{ {
struct objc_method_description_list *list; struct objc_protocol_method_description_list *list;
if (isRequiredMethod) if (isRequiredMethod)
{ {
if (isInstanceMethod) if (isInstanceMethod)
@ -353,7 +353,7 @@ struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p,
BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *count) BOOL isRequiredMethod, BOOL isInstanceMethod, unsigned int *count)
{ {
if ((NULL == p) || (NULL == count)){ return NULL; } if ((NULL == p) || (NULL == count)){ return NULL; }
struct objc_method_description_list *list = struct objc_protocol_method_description_list *list =
get_method_list(p, isRequiredMethod, isInstanceMethod); get_method_list(p, isRequiredMethod, isInstanceMethod);
*count = 0; *count = 0;
if (NULL == list || list->count == 0) { return NULL; } if (NULL == list || list->count == 0) { return NULL; }
@ -363,9 +363,8 @@ struct objc_method_description *protocol_copyMethodDescriptionList(Protocol *p,
calloc(sizeof(struct objc_method_description), list->count); calloc(sizeof(struct objc_method_description), list->count);
for (int i=0 ; i < (list->count) ; i++) for (int i=0 ; i < (list->count) ; i++)
{ {
out[i].name = sel_registerTypedName_np(list->methods[i].name, out[i].name = list->methods[i].selector;
list->methods[i].types); out[i].types = sel_getType_np(list->methods[i].selector);
out[i].types = list->methods[i].types;
} }
return out; return out;
} }
@ -461,34 +460,61 @@ objc_property_t protocol_getProperty(Protocol *p,
return NULL; return NULL;
} }
static struct objc_protocol_method_description *
struct objc_method_description get_method_description(Protocol *p,
protocol_getMethodDescription(Protocol *p, SEL aSel,
SEL aSel, BOOL isRequiredMethod,
BOOL isRequiredMethod, BOOL isInstanceMethod)
BOOL isInstanceMethod)
{ {
struct objc_method_description d = {0,0}; struct objc_protocol_method_description_list *list =
struct objc_method_description_list *list =
get_method_list(p, isRequiredMethod, isInstanceMethod); get_method_list(p, isRequiredMethod, isInstanceMethod);
if (NULL == list) if (NULL == list)
{ {
return d; return NULL;
} }
// TODO: We could make this much more efficient if
for (int i=0 ; i<list->count ; i++) for (int i=0 ; i<list->count ; i++)
{ {
SEL s = sel_registerTypedName_np(list->methods[i].name, 0); SEL s = list->methods[i].selector;
if (sel_isEqual(s, aSel)) if (sel_isEqual(s, aSel))
{ {
d.name = s; return &list->methods[i];
d.types = list->methods[i].types;
break;
} }
} }
return NULL;
}
struct objc_method_description
protocol_getMethodDescription(Protocol *p,
SEL aSel,
BOOL isRequiredMethod,
BOOL isInstanceMethod)
{
struct objc_method_description d = {0,0};
struct objc_protocol_method_description *m =
get_method_description(p, aSel, isRequiredMethod, isInstanceMethod);
if (m != NULL)
{
SEL s = m->selector;
d.name = s;
d.types = sel_getType_np(s);
}
return d; return d;
} }
const char *_protocol_getMethodTypeEncoding(Protocol *p,
SEL aSel,
BOOL isRequiredMethod,
BOOL isInstanceMethod)
{
struct objc_protocol_method_description *m =
get_method_description(p, aSel, isRequiredMethod, isInstanceMethod);
if (m != NULL)
{
return m->types;
}
return NULL;
}
const char *protocol_getName(Protocol *p) const char *protocol_getName(Protocol *p)
{ {
@ -569,7 +595,7 @@ void protocol_addMethodDescription(Protocol *aProtocol,
{ {
if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; } if ((NULL == aProtocol) || (NULL == name) || (NULL == types)) { return; }
if (incompleteProtocolClass() != aProtocol->isa) { return; } if (incompleteProtocolClass() != aProtocol->isa) { return; }
struct objc_method_description_list **listPtr; struct objc_protocol_method_description_list **listPtr;
if (isInstanceMethod) if (isInstanceMethod)
{ {
if (isRequiredMethod) if (isRequiredMethod)
@ -594,19 +620,22 @@ void protocol_addMethodDescription(Protocol *aProtocol,
} }
if (NULL == *listPtr) if (NULL == *listPtr)
{ {
*listPtr = calloc(1, sizeof(struct objc_method_description_list) + sizeof(struct objc_method_description)); // FIXME: Factor this out, we do the same thing in multiple places.
*listPtr = calloc(1, sizeof(struct objc_protocol_method_description_list) +
sizeof(struct objc_protocol_method_description));
(*listPtr)->count = 1; (*listPtr)->count = 1;
(*listPtr)->size = sizeof(struct objc_protocol_method_description);
} }
else else
{ {
(*listPtr)->count++; (*listPtr)->count++;
*listPtr = realloc(*listPtr, sizeof(struct objc_method_description_list) + *listPtr = realloc(*listPtr, sizeof(struct objc_protocol_method_description_list) +
sizeof(struct objc_method_description) * (*listPtr)->count); sizeof(struct objc_protocol_method_description) * (*listPtr)->count);
} }
struct objc_method_description_list *list = *listPtr; struct objc_protocol_method_description_list *list = *listPtr;
int index = list->count-1; int index = list->count-1;
list->methods[index].name = sel_getName(name); list->methods[index].selector = sel_registerTypedName_np(sel_getName(name), types);
list->methods[index].types= types; list->methods[index].types = types;
} }
void protocol_addProtocol(Protocol *aProtocol, Protocol *addition) void protocol_addProtocol(Protocol *aProtocol, Protocol *addition)
{ {

@ -4,7 +4,7 @@
#include "selector.h" #include "selector.h"
#include <stdlib.h> #include <stdlib.h>
struct objc_method_description_list struct objc_protocol_method_description_list_gcc
{ {
/** /**
* Number of method descriptions in this list. * Number of method descriptions in this list.
@ -18,6 +18,37 @@ struct objc_method_description_list
struct objc_selector methods[]; struct objc_selector methods[];
}; };
/**
* A description of a method in a protocol.
*/
struct objc_protocol_method_description
{
/**
* The selector for this method, includes traditional type encoding.
*/
SEL selector;
/**
* The extended type encoding.
*/
const char *types;
};
struct objc_protocol_method_description_list
{
/**
* Number of method descriptions in this list.
*/
int count;
/**
* Size of `struct objc_method_description`
*/
int size;
/**
* Methods in this list. `count` elements long.
*/
struct objc_protocol_method_description methods[];
};
struct objc_protocol struct objc_protocol
{ {
/** /**
@ -26,16 +57,16 @@ struct objc_protocol
id isa; id isa;
char *name; char *name;
struct objc_protocol_list *protocol_list; struct objc_protocol_list *protocol_list;
struct objc_method_description_list *instance_methods; struct objc_protocol_method_description_list *instance_methods;
struct objc_method_description_list *class_methods; struct objc_protocol_method_description_list *class_methods;
/** /**
* Instance methods that are declared as optional for this protocol. * Instance methods that are declared as optional for this protocol.
*/ */
struct objc_method_description_list *optional_instance_methods; struct objc_protocol_method_description_list *optional_instance_methods;
/** /**
* Class methods that are declared as optional for this protocol. * Class methods that are declared as optional for this protocol.
*/ */
struct objc_method_description_list *optional_class_methods; struct objc_protocol_method_description_list *optional_class_methods;
/** /**
* Properties that are required by this protocol. * Properties that are required by this protocol.
*/ */
@ -63,11 +94,11 @@ struct objc_protocol_gcc
/** /**
* List of instance methods required by this protocol. * List of instance methods required by this protocol.
*/ */
struct objc_method_description_list *instance_methods; struct objc_protocol_method_description_list_gcc *instance_methods;
/** /**
* List of class methods required by this protocol. * List of class methods required by this protocol.
*/ */
struct objc_method_description_list *class_methods; struct objc_protocol_method_description_list_gcc *class_methods;
}; };
struct objc_protocol_gsv1 struct objc_protocol_gsv1
@ -78,16 +109,16 @@ struct objc_protocol_gsv1
id isa; id isa;
char *name; char *name;
struct objc_protocol_list *protocol_list; struct objc_protocol_list *protocol_list;
struct objc_method_description_list *instance_methods; struct objc_protocol_method_description_list_gcc *instance_methods;
struct objc_method_description_list *class_methods; struct objc_protocol_method_description_list_gcc *class_methods;
/** /**
* Instance methods that are declared as optional for this protocol. * Instance methods that are declared as optional for this protocol.
*/ */
struct objc_method_description_list *optional_instance_methods; struct objc_protocol_method_description_list_gcc *optional_instance_methods;
/** /**
* Class methods that are declared as optional for this protocol. * Class methods that are declared as optional for this protocol.
*/ */
struct objc_method_description_list *optional_class_methods; struct objc_protocol_method_description_list_gcc *optional_class_methods;
/** /**
* Properties that are required by this protocol. * Properties that are required by this protocol.
*/ */

Loading…
Cancel
Save