Clean up method / slot lookup interfaces.

main
David Chisnall 8 years ago
parent a9a2ed6b10
commit a8752cd842

@ -49,7 +49,7 @@ static uint32_t dtable_depth = 8;
*/ */
static Class ownerForMethod(Class cls, SEL sel) static Class ownerForMethod(Class cls, SEL sel)
{ {
struct objc_slot *slot = objc_get_slot2(cls, sel); struct objc_slot *slot = objc_get_slot2(cls, sel, NULL);
if (slot == NULL) if (slot == NULL)
{ {
return Nil; return Nil;
@ -58,7 +58,7 @@ static Class ownerForMethod(Class cls, SEL sel)
{ {
return cls; return cls;
} }
if (objc_get_slot2(cls->super_class, sel) == slot) if (objc_get_slot2(cls->super_class, sel, NULL) == slot)
{ {
return ownerForMethod(cls->super_class, sel); return ownerForMethod(cls->super_class, sel);
} }

@ -62,7 +62,7 @@ OBJC_HOOK Class (*_objc_class_for_boxing_foreign_exception)(int64_t exceptionCla
* receiver. This should return the slot to use instead, although it may throw * receiver. This should return the slot to use instead, although it may throw
* an exception or perform some other action. * an exception or perform some other action.
*/ */
extern struct objc_slot* (*_objc_selector_type_mismatch2)(Class cls, extern IMP (*_objc_selector_type_mismatch2)(Class cls,
SEL selector, struct objc_slot *result); SEL selector, struct objc_slot *result);
/** /**
* Legacy hook for when selector types do not match. This is only called * Legacy hook for when selector types do not match. This is only called

@ -848,11 +848,28 @@ extern struct objc_slot_v1 *objc_get_slot(Class, SEL)
OBJC_NONPORTABLE OBJC_DEPRECATED; OBJC_NONPORTABLE OBJC_DEPRECATED;
/** /**
* Look up a slot, without invoking any forwarding mechanisms. * Look up a slot, without invoking any forwarding mechanisms. The third
* parameter is used to return a pointer to the current value of the version
* counter. If this value is equal to `objc_method_cache_version` then the
* slot is safe to reuse without performing another lookup.
*/ */
extern struct objc_slot *objc_get_slot2(Class, SEL) extern struct objc_slot *objc_get_slot2(Class, SEL, uint64_t*)
OBJC_NONPORTABLE; OBJC_NONPORTABLE;
/**
* Look up a slot, invoking any required forwarding mechanisms. The third
* parameter is used to return a pointer to the current value of the version
* counter. If this value is equal to `objc_method_cache_version` then the
* slot is safe to reuse without performing another lookup.
*/
extern struct objc_slot *objc_slot_lookup_version(id *receiver, SEL selector, uint64_t*)
OBJC_NONPORTABLE;
/**
* Look up a slot, invoking any required forwarding mechanisms.
*/
extern IMP objc_msg_lookup2(id *receiver, SEL selector) OBJC_NONPORTABLE;
/** /**
* Registers a class for small objects. Small objects are stored inside a * Registers a class for small objects. Small objects are stored inside a
* pointer. If the class can be registered, then this returns YES. The second * pointer. If the class can be registered, then this returns YES. The second

@ -362,10 +362,10 @@ Method class_getInstanceMethod(Class aClass, SEL aSelector)
if (classHasInstalledDtable(aClass)) if (classHasInstalledDtable(aClass))
{ {
// Do a dtable lookup to find out which class the method comes from. // Do a dtable lookup to find out which class the method comes from.
struct objc_slot *slot = objc_get_slot2(aClass, aSelector); struct objc_slot *slot = objc_get_slot2(aClass, aSelector, NULL);
if (NULL == slot) if (NULL == slot)
{ {
slot = objc_get_slot2(aClass, sel_registerName(sel_getName(aSelector))); slot = objc_get_slot2(aClass, sel_registerName(sel_getName(aSelector)), NULL);
if (NULL == slot) if (NULL == slot)
{ {
return NULL; return NULL;

@ -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)

Loading…
Cancel
Save