|
|
|
@ -24,7 +24,7 @@ static struct objc_method nil_slot_D = { (IMP)nil_method_D, NULL, NULL };
|
|
|
|
static struct objc_method nil_slot_d = { (IMP)nil_method_d, NULL, NULL };
|
|
|
|
static struct objc_method nil_slot_d = { (IMP)nil_method_d, NULL, NULL };
|
|
|
|
static struct objc_method nil_slot_f = { (IMP)nil_method_f, NULL, NULL };
|
|
|
|
static struct objc_method nil_slot_f = { (IMP)nil_method_f, NULL, NULL };
|
|
|
|
|
|
|
|
|
|
|
|
struct objc_slot* objc_slot_lookup(id *receiver, SEL selector);
|
|
|
|
static struct objc_slot* objc_slot_lookup(id *receiver, SEL selector);
|
|
|
|
|
|
|
|
|
|
|
|
// Default implementations of the two new hooks. Return NULL.
|
|
|
|
// Default implementations of the two new hooks. Return NULL.
|
|
|
|
static id objc_proxy_lookup_null(id receiver, SEL op) { return nil; }
|
|
|
|
static id objc_proxy_lookup_null(id receiver, SEL op) { return nil; }
|
|
|
|
@ -43,7 +43,7 @@ __thread struct objc_method uncacheable_slot = { (IMP)nil_method, NULL, NULL };
|
|
|
|
__thread struct objc_slot_v1 uncacheable_slot_v1 = { Nil, Nil, 0, 0, (IMP)nil_method };
|
|
|
|
__thread struct objc_slot_v1 uncacheable_slot_v1 = { Nil, Nil, 0, 0, (IMP)nil_method };
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef NO_SELECTOR_MISMATCH_WARNINGS
|
|
|
|
#ifndef NO_SELECTOR_MISMATCH_WARNINGS
|
|
|
|
static struct objc_slot* objc_selector_type_mismatch(Class cls, SEL
|
|
|
|
static IMP objc_selector_type_mismatch(Class cls, SEL
|
|
|
|
selector, struct objc_slot *result)
|
|
|
|
selector, struct objc_slot *result)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
fprintf(stderr, "Calling [%s %c%s] with incorrect signature. "
|
|
|
|
fprintf(stderr, "Calling [%s %c%s] with incorrect signature. "
|
|
|
|
@ -54,22 +54,22 @@ static struct objc_slot* objc_selector_type_mismatch(Class cls, SEL
|
|
|
|
sel_getType_np(((struct objc_method*)result)->selector),
|
|
|
|
sel_getType_np(((struct objc_method*)result)->selector),
|
|
|
|
((struct objc_method*)result)->types,
|
|
|
|
((struct objc_method*)result)->types,
|
|
|
|
sel_getType_np(selector));
|
|
|
|
sel_getType_np(selector));
|
|
|
|
return result;
|
|
|
|
return result->method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#else
|
|
|
|
static struct objc_slot* objc_selector_type_mismatch(Class cls, SEL
|
|
|
|
static IMP objc_selector_type_mismatch(Class cls, SEL
|
|
|
|
selector, struct objc_slot *result)
|
|
|
|
selector, struct objc_slot *result)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return result;
|
|
|
|
return result->method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
struct objc_slot *(*_objc_selector_type_mismatch2)(Class cls, SEL
|
|
|
|
IMP (*_objc_selector_type_mismatch2)(Class cls, SEL
|
|
|
|
selector, struct objc_slot *result) = objc_selector_type_mismatch;
|
|
|
|
selector, struct objc_slot *result) = objc_selector_type_mismatch;
|
|
|
|
struct objc_slot_v1 *(*_objc_selector_type_mismatch)(Class cls, SEL
|
|
|
|
struct objc_slot_v1 *(*_objc_selector_type_mismatch)(Class cls, SEL
|
|
|
|
selector, struct objc_slot_v1 *result);
|
|
|
|
selector, struct objc_slot_v1 *result);
|
|
|
|
|
|
|
|
|
|
|
|
static struct objc_slot *call_mismatch_hook(Class cls, SEL sel, struct objc_slot *slot)
|
|
|
|
static IMP call_mismatch_hook(Class cls, SEL sel, struct objc_slot *slot)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (_objc_selector_type_mismatch &&
|
|
|
|
if (_objc_selector_type_mismatch &&
|
|
|
|
(!_objc_selector_type_mismatch2 ||
|
|
|
|
(!_objc_selector_type_mismatch2 ||
|
|
|
|
@ -80,12 +80,7 @@ static struct objc_slot *call_mismatch_hook(Class cls, SEL sel, struct objc_slot
|
|
|
|
fwdslot.selector = sel;
|
|
|
|
fwdslot.selector = sel;
|
|
|
|
fwdslot.method = slot->method;
|
|
|
|
fwdslot.method = slot->method;
|
|
|
|
struct objc_slot_v1 *slot_v1 = _objc_selector_type_mismatch(cls, sel, &uncacheable_slot_v1);
|
|
|
|
struct objc_slot_v1 *slot_v1 = _objc_selector_type_mismatch(cls, sel, &uncacheable_slot_v1);
|
|
|
|
if (slot_v1 == &fwdslot)
|
|
|
|
return slot_v1->method;
|
|
|
|
{
|
|
|
|
|
|
|
|
return slot;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
uncacheable_slot.imp = slot_v1->method;
|
|
|
|
|
|
|
|
return (struct objc_slot*)&uncacheable_slot;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return _objc_selector_type_mismatch2(cls, sel, slot);
|
|
|
|
return _objc_selector_type_mismatch2(cls, sel, slot);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -94,10 +89,14 @@ static
|
|
|
|
// Uncomment for debugging
|
|
|
|
// Uncomment for debugging
|
|
|
|
//__attribute__((noinline))
|
|
|
|
//__attribute__((noinline))
|
|
|
|
__attribute__((always_inline))
|
|
|
|
__attribute__((always_inline))
|
|
|
|
struct objc_slot *objc_msg_lookup_internal(id *receiver, SEL selector)
|
|
|
|
struct objc_slot *objc_msg_lookup_internal(id *receiver, SEL selector, uint64_t *version)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
retry:;
|
|
|
|
if (version)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
*version = objc_method_cache_version;
|
|
|
|
|
|
|
|
}
|
|
|
|
Class class = classForObject((*receiver));
|
|
|
|
Class class = classForObject((*receiver));
|
|
|
|
|
|
|
|
retry:;
|
|
|
|
struct objc_slot * result = objc_dtable_lookup(class->dtable, selector->index);
|
|
|
|
struct objc_slot * result = objc_dtable_lookup(class->dtable, selector->index);
|
|
|
|
if (UNLIKELY(0 == result))
|
|
|
|
if (UNLIKELY(0 == result))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -126,7 +125,12 @@ retry:;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((result = objc_dtable_lookup(dtable, get_untyped_idx(selector))))
|
|
|
|
if ((result = objc_dtable_lookup(dtable, get_untyped_idx(selector))))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return call_mismatch_hook(class, selector, result);
|
|
|
|
if (version)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
*version = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
uncacheable_slot.imp = call_mismatch_hook(class, selector, result);
|
|
|
|
|
|
|
|
result = (struct objc_slot*)&uncacheable_slot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
id newReceiver = objc_proxy_lookup(*receiver, selector);
|
|
|
|
id newReceiver = objc_proxy_lookup(*receiver, selector);
|
|
|
|
// If some other library wants us to play forwarding games, try
|
|
|
|
// If some other library wants us to play forwarding games, try
|
|
|
|
@ -138,6 +142,10 @@ retry:;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (0 == result)
|
|
|
|
if (0 == result)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
if (version)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
*version = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
uncacheable_slot.imp = __objc_msg_forward2(*receiver, selector);
|
|
|
|
uncacheable_slot.imp = __objc_msg_forward2(*receiver, selector);
|
|
|
|
result = (struct objc_slot*)&uncacheable_slot;
|
|
|
|
result = (struct objc_slot*)&uncacheable_slot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -150,7 +158,7 @@ PRIVATE IMP slowMsgLookup(id *receiver, SEL cmd)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// By the time we've got here, the assembly version of this function has
|
|
|
|
// By the time we've got here, the assembly version of this function has
|
|
|
|
// already done the nil checks.
|
|
|
|
// already done the nil checks.
|
|
|
|
return objc_msg_lookup_internal(receiver, cmd)->method;
|
|
|
|
return objc_msg_lookup_internal(receiver, cmd, NULL)->method;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PRIVATE void logInt(void *a)
|
|
|
|
PRIVATE void logInt(void *a)
|
|
|
|
@ -189,7 +197,7 @@ struct objc_slot_v1 *objc_msg_lookup_sender(id *receiver, SEL selector, id sende
|
|
|
|
return &nil_slot_v1;
|
|
|
|
return &nil_slot_v1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct objc_slot *slot = objc_msg_lookup_internal(receiver, selector);
|
|
|
|
struct objc_slot *slot = objc_msg_lookup_internal(receiver, selector, NULL);
|
|
|
|
uncacheable_slot_v1.owner = Nil;
|
|
|
|
uncacheable_slot_v1.owner = Nil;
|
|
|
|
uncacheable_slot_v1.types = sel_getType_np(((struct objc_method*)slot)->selector);
|
|
|
|
uncacheable_slot_v1.types = sel_getType_np(((struct objc_method*)slot)->selector);
|
|
|
|
uncacheable_slot_v1.selector = selector;
|
|
|
|
uncacheable_slot_v1.selector = selector;
|
|
|
|
@ -197,7 +205,8 @@ struct objc_slot_v1 *objc_msg_lookup_sender(id *receiver, SEL selector, id sende
|
|
|
|
return &uncacheable_slot_v1;
|
|
|
|
return &uncacheable_slot_v1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct objc_slot* objc_slot_lookup(id *receiver, SEL selector)
|
|
|
|
|
|
|
|
|
|
|
|
static struct objc_slot* objc_slot_lookup(id *receiver, SEL selector)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Returning a nil slot allows the caller to cache the lookup for nil too,
|
|
|
|
// Returning a nil slot allows the caller to cache the lookup for nil too,
|
|
|
|
// although this is not particularly useful because the nil method can be
|
|
|
|
// although this is not particularly useful because the nil method can be
|
|
|
|
@ -224,9 +233,49 @@ struct objc_slot* objc_slot_lookup(id *receiver, SEL selector)
|
|
|
|
return (struct objc_slot*)&nil_slot;
|
|
|
|
return (struct objc_slot*)&nil_slot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return objc_msg_lookup_internal(receiver, selector);
|
|
|
|
return objc_msg_lookup_internal(receiver, selector, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct objc_slot *objc_slot_lookup_version(id *receiver, SEL selector, uint64_t *version)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
// Returning a nil slot allows the caller to cache the lookup for nil too,
|
|
|
|
|
|
|
|
// although this is not particularly useful because the nil method can be
|
|
|
|
|
|
|
|
// inlined trivially.
|
|
|
|
|
|
|
|
if (UNLIKELY(*receiver == nil))
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
if (version)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
*version = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
// Return the correct kind of zero, depending on the type encoding.
|
|
|
|
|
|
|
|
if (selector->types)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
const char *t = selector->types;
|
|
|
|
|
|
|
|
// Skip type qualifiers
|
|
|
|
|
|
|
|
while ('r' == *t || 'n' == *t || 'N' == *t || 'o' == *t ||
|
|
|
|
|
|
|
|
'O' == *t || 'R' == *t || 'V' == *t)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
t++;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (selector->types[0])
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
case 'D': return (struct objc_slot*)&nil_slot_D;
|
|
|
|
|
|
|
|
case 'd': return (struct objc_slot*)&nil_slot_d;
|
|
|
|
|
|
|
|
case 'f': return (struct objc_slot*)&nil_slot_f;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return (struct objc_slot*)&nil_slot;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return objc_msg_lookup_internal(receiver, selector, version);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
IMP objc_msg_lookup2(id *receiver, SEL selector)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
return objc_slot_lookup(receiver, selector)->method;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct objc_slot *objc_slot_lookup_super2(struct objc_super *super, SEL selector)
|
|
|
|
struct objc_slot *objc_slot_lookup_super2(struct objc_super *super, SEL selector)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
id receiver = super->receiver;
|
|
|
|
id receiver = super->receiver;
|
|
|
|
@ -378,10 +427,14 @@ void objc_msg_profile(id receiver, IMP method,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
/**
|
|
|
|
* Looks up a slot without invoking any forwarding mechanisms
|
|
|
|
* looks up a slot without invoking any forwarding mechanisms
|
|
|
|
*/
|
|
|
|
*/
|
|
|
|
struct objc_slot *objc_get_slot2(Class cls, SEL selector)
|
|
|
|
struct objc_slot *objc_get_slot2(Class cls, SEL selector, uint64_t *version)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
|
|
|
|
if (version)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
*version = objc_method_cache_version;
|
|
|
|
|
|
|
|
}
|
|
|
|
struct objc_slot * result = objc_dtable_lookup(cls->dtable, selector->index);
|
|
|
|
struct objc_slot * result = objc_dtable_lookup(cls->dtable, selector->index);
|
|
|
|
if (0 == result)
|
|
|
|
if (0 == result)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
@ -403,11 +456,16 @@ struct objc_slot *objc_get_slot2(Class cls, SEL selector)
|
|
|
|
if (!isSelRegistered(selector))
|
|
|
|
if (!isSelRegistered(selector))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
objc_register_selector(selector);
|
|
|
|
objc_register_selector(selector);
|
|
|
|
return objc_get_slot2(cls, selector);
|
|
|
|
return objc_get_slot2(cls, selector, version);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ((result = objc_dtable_lookup(dtable, get_untyped_idx(selector))))
|
|
|
|
if ((result = objc_dtable_lookup(dtable, get_untyped_idx(selector))))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return call_mismatch_hook(cls, selector, result);
|
|
|
|
if (version)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
*version = 0;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
uncacheable_slot.imp = call_mismatch_hook(cls, selector, result);
|
|
|
|
|
|
|
|
result = (struct objc_slot*)&uncacheable_slot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
@ -416,7 +474,7 @@ struct objc_slot *objc_get_slot2(Class cls, SEL selector)
|
|
|
|
|
|
|
|
|
|
|
|
struct objc_slot_v1 *objc_get_slot(Class cls, SEL selector)
|
|
|
|
struct objc_slot_v1 *objc_get_slot(Class cls, SEL selector)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
struct objc_slot *result = objc_get_slot2(cls, selector);
|
|
|
|
struct objc_slot *result = objc_get_slot2(cls, selector, NULL);
|
|
|
|
if (result == NULL)
|
|
|
|
if (result == NULL)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
return NULL;
|
|
|
|
return NULL;
|
|
|
|
@ -437,13 +495,13 @@ BOOL class_respondsToSelector(Class cls, SEL selector)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (0 == selector || 0 == cls) { return NO; }
|
|
|
|
if (0 == selector || 0 == cls) { return NO; }
|
|
|
|
|
|
|
|
|
|
|
|
return NULL != objc_get_slot2(cls, selector);
|
|
|
|
return NULL != objc_get_slot2(cls, selector, NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
IMP class_getMethodImplementation(Class cls, SEL name)
|
|
|
|
IMP class_getMethodImplementation(Class cls, SEL name)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if ((Nil == cls) || (NULL == name)) { return (IMP)0; }
|
|
|
|
if ((Nil == cls) || (NULL == name)) { return (IMP)0; }
|
|
|
|
struct objc_slot * slot = objc_get_slot2(cls, name);
|
|
|
|
struct objc_slot * slot = objc_get_slot2(cls, name, NULL);
|
|
|
|
return NULL != slot ? slot->method : __objc_msg_forward2(nil, name);
|
|
|
|
return NULL != slot ? slot->method : __objc_msg_forward2(nil, name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
@ -490,7 +548,7 @@ IMP objc_msg_lookup(id receiver, SEL selector)
|
|
|
|
if (nil == receiver) { return (IMP)nil_method; }
|
|
|
|
if (nil == receiver) { return (IMP)nil_method; }
|
|
|
|
|
|
|
|
|
|
|
|
id self = receiver;
|
|
|
|
id self = receiver;
|
|
|
|
struct objc_slot * slot = objc_msg_lookup_internal(&self, selector);
|
|
|
|
struct objc_slot * slot = objc_msg_lookup_internal(&self, selector, NULL);
|
|
|
|
// If the receiver is changed by the lookup mechanism then we have to fall
|
|
|
|
// If the receiver is changed by the lookup mechanism then we have to fall
|
|
|
|
// back to old-style forwarding.
|
|
|
|
// back to old-style forwarding.
|
|
|
|
if (self != receiver)
|
|
|
|
if (self != receiver)
|
|
|
|
|