Skip to content
This repository has been archived by the owner on Dec 5, 2019. It is now read-only.

Commit

Permalink
Changes per @jspahrsummers review
Browse files Browse the repository at this point in the history
Squash before merging into master
  • Loading branch information
claybridges committed Aug 14, 2013
1 parent 5a47ea3 commit d26af12
Showing 1 changed file with 29 additions and 25 deletions.
54 changes: 29 additions & 25 deletions extobjc/EXTRuntimeExtensions.m
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,12 @@
typedef NSMethodSignature *(*methodSignatureForSelectorIMP)(id, SEL, SEL);
typedef void (^ext_specialProtocolInjectionBlock)(Class);

// a `const char *` equivalent to system struct objc_method_description
typedef struct {
SEL name;
const char *types;
} ext_methodDescription;

// contains the information needed to reference a full special protocol
typedef struct {
// the actual protocol declaration (@protocol block)
Expand Down Expand Up @@ -726,34 +732,31 @@ BOOL ext_getPropertyAccessorsForClass (objc_property_t property, Class aClass, M
}

NSMethodSignature *ext_globalMethodSignatureForSelector (SEL aSelector) {
if (aSelector == NULL)
return nil;

typedef struct objc_method_description MethodDescription;
NSCParameterAssert(aSelector != NULL);

// set up a small & simple cache/hash to avoid repeatedly scouring every
// class & protocol in the runtime.
static const size_t selectorCacheLength = 1 << 8;
static const uintptr_t selectorCacheMask = (selectorCacheLength - 1);
static MethodDescription volatile methodDescriptionCache[selectorCacheLength];
static ext_methodDescription volatile methodDescriptionCache[selectorCacheLength];

// reads and writes need to be atomic, but will be ridiculously fast,
// so we can stay in userland for locks, and keep the speed.
static OSSpinLock lock = OS_SPINLOCK_INIT;

uintptr_t hash = (uintptr_t)((void *)aSelector) & selectorCacheMask;
MethodDescription methodDesc;
ext_methodDescription methodDesc;

OSSpinLockLock(&lock);
methodDesc = methodDescriptionCache[hash];
OSSpinLockUnlock(&lock);

// cache hit? check the selector to insure we aren't colliding
if (methodDesc.name && methodDesc.name == aSelector) {
if (methodDesc.name == aSelector) {
return [NSMethodSignature signatureWithObjCTypes:methodDesc.types];
}

methodDesc = (MethodDescription){.name = NULL, .types = NULL};
methodDesc = (ext_methodDescription){.name = NULL, .types = NULL};

uint classCount = 0;
Class *classes = ext_copyClassList(&classCount);
Expand All @@ -770,7 +773,7 @@ BOOL ext_getPropertyAccessorsForClass (objc_property_t property, Class aClass, M
method = class_getClassMethod(cls, aSelector);

if (method) {
methodDesc = (MethodDescription){.name = aSelector, .types = (char *)method_getTypeEncoding(method)};
methodDesc = (ext_methodDescription){.name = aSelector, .types = method_getTypeEncoding(method)};
break;
}
}
Expand All @@ -783,29 +786,30 @@ BOOL ext_getPropertyAccessorsForClass (objc_property_t property, Class aClass, M
uint protocolCount = 0;
Protocol * __unsafe_unretained *protocols = objc_copyProtocolList(&protocolCount);
if (protocols) {
@autoreleasepool {
for (uint i = 0;i < protocolCount;++i) {
methodDesc = protocol_getMethodDescription(protocols[i], aSelector, NO, YES);
if (!methodDesc.name)
methodDesc = protocol_getMethodDescription(protocols[i], aSelector, NO, NO);

if (methodDesc.name)
break;
struct objc_method_description objcMethodDesc;
for (uint i = 0;i < protocolCount;++i) {
objcMethodDesc = protocol_getMethodDescription(protocols[i], aSelector, NO, YES);
if (!objcMethodDesc.name)
objcMethodDesc = protocol_getMethodDescription(protocols[i], aSelector, NO, NO);

if (objcMethodDesc.name) {
methodDesc = (ext_methodDescription){.name = objcMethodDesc.name, .types = objcMethodDesc.types};
break;
}
}
free(protocols);
}
}

if (methodDesc.name) {
// write to cache
uintptr_t hash = (uintptr_t)(void *)aSelector & selectorCacheMask;
OSSpinLockLock(&lock);
methodDescriptionCache[hash] = methodDesc;
OSSpinLockUnlock(&lock);

// NB: on OS X, there's a system problem with the type encoding for -[NSDecimalNumber* -initWithDecimal:]
// which causes this to fail. Doubt it's worth trying to catch here.
// if not locked, cache this value, but don't wait around
if (OSSpinLockTry(&lock)) {
methodDescriptionCache[hash] = methodDesc;
OSSpinLockUnlock(&lock);
}

// NB: there are some esoteric system type encodings that cause -signatureWithObjCTypes: to fail,
// e.g on OS X 10.8, -[NSDecimalNumber* -initWithDecimal:]. Doubt it's worth trying to catch here.
return [NSMethodSignature signatureWithObjCTypes:methodDesc.types];
} else {
return nil;
Expand Down

0 comments on commit d26af12

Please sign in to comment.