Refactor to use `objc_method` as the slot.

This change set incorporates a number of changes that all needed to
happen together:

 * The imp is now the first field of the `objc_method` structure.  This
   makes it possible to extend the structure without breaking anything
   that relies on being able to access the IMP.
 * There is no owner in the slot, so we must use other mechanisms for
   determining the owner of a method (e.g. whether the same method appears
   in the superclass)
 * Again, because there is no owner in the slot, we can't use this as a
   fast path for finding the C++ construct / destruct methods.  These are
   now cached in the class structure when they are found.
 * The version field is gone from the slot and now we provide a global
   version.  This is based on the observation that method replacements
   are relatively infrequent and the overhead of invalidating all method
   caches is cheaper than adding extra state for every (class, method)
   pair.
 * A number of the runtime functions are simplified because replacing
   the IMP in a `Method` now implicitly updates the dtable.
main
David Chisnall 8 years ago
parent 33dc69387e
commit a9a2ed6b10

@ -0,0 +1,26 @@
#include "Test.h"
#include "../objc/runtime.h"
#pragma clang diagnostic ignored "-Wobjc-protocol-method-implementation"
@interface Foo : Test
+ (int)replaced;
@end
@implementation Foo
+ (int)replaced
{
return 1;
}
@end
@implementation Foo (bar)
+ (int)replaced
{
return 2;
}
@end
int main (void)
{
assert([Foo replaced] == 2);
return 0;
}

@ -97,6 +97,16 @@ struct objc_class
* runtime. * runtime.
*/ */
struct objc_class *subclass_list; struct objc_class *subclass_list;
/**
* Pointer to the .cxx_construct method if one exists. This method needs
* to be called outside of the normal dispatch mechanism.
*/
IMP cxx_construct;
/**
* Pointer to the .cxx_destruct method if one exists. This method needs to
* be called outside of the normal dispatch mechanism.
*/
IMP cxx_destruct;
/** /**
* A pointer to the next sibling class to this. You may find all * A pointer to the next sibling class to this. You may find all
* subclasses of a given class by following the subclass_list pointer and * subclasses of a given class by following the subclass_list pointer and

@ -10,7 +10,6 @@
#include "class.h" #include "class.h"
#include "lock.h" #include "lock.h"
#include "method_list.h" #include "method_list.h"
#include "slot_pool.h"
#include "dtable.h" #include "dtable.h"
#include "visibility.h" #include "visibility.h"
#include "asmconstants.h" #include "asmconstants.h"
@ -21,8 +20,12 @@ _Static_assert(__builtin_offsetof(SparseArray, shift) == SHIFT_OFFSET,
"Incorrect shift offset for assembly"); "Incorrect shift offset for assembly");
_Static_assert(__builtin_offsetof(SparseArray, data) == DATA_OFFSET, _Static_assert(__builtin_offsetof(SparseArray, data) == DATA_OFFSET,
"Incorrect data offset for assembly"); "Incorrect data offset for assembly");
// Slots are now a public interface to part of the method structure, so make
// sure that it's safe to use method and slot structures interchangeably.
_Static_assert(__builtin_offsetof(struct objc_slot, method) == SLOT_OFFSET, _Static_assert(__builtin_offsetof(struct objc_slot, method) == SLOT_OFFSET,
"Incorrect slot offset for assembly"); "Incorrect slot offset for assembly");
_Static_assert(__builtin_offsetof(struct objc_method, imp) == SLOT_OFFSET,
"Incorrect slot offset for assembly");
PRIVATE dtable_t uninstalled_dtable; PRIVATE dtable_t uninstalled_dtable;
#if defined(WITH_TRACING) && defined (__x86_64) #if defined(WITH_TRACING) && defined (__x86_64)
@ -41,17 +44,34 @@ PRIVATE mutex_t initialize_lock;
static uint32_t dtable_depth = 8; static uint32_t dtable_depth = 8;
/** /**
* Returns YES if the class implements a method for the specified selector, NO * Starting at `cls`, finds the class that provides the implementation of the
* otherwise. * method identified by `sel`.
*/ */
static BOOL ownsMethod(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);
if ((NULL != slot) && (slot->owner == cls)) if (slot == NULL)
{
return Nil;
}
if (cls->super_class == NULL)
{
return cls;
}
if (objc_get_slot2(cls->super_class, sel) == slot)
{ {
return YES; return ownerForMethod(cls->super_class, sel);
} }
return NO; return cls;
}
/**
* Returns YES if the class implements a method for the specified selector, NO
* otherwise.
*/
static BOOL ownsMethod(Class cls, SEL sel)
{
return ownerForMethod(cls, sel) == cls;
} }
@ -75,22 +95,22 @@ static void checkARCAccessors(Class cls)
autorelease = sel_registerName("autorelease"); autorelease = sel_registerName("autorelease");
isARC = sel_registerName("_ARCCompliantRetainRelease"); isARC = sel_registerName("_ARCCompliantRetainRelease");
} }
struct objc_slot *slot = objc_get_slot2(cls, retain); Class owner = ownerForMethod(cls, retain);
if ((NULL != slot) && !ownsMethod(slot->owner, isARC)) if ((NULL != owner) && !ownsMethod(owner, isARC))
{ {
ARC_DEBUG_LOG("%s does not support ARC correctly (implements retain)\n", cls->name); ARC_DEBUG_LOG("%s does not support ARC correctly (implements retain)\n", cls->name);
objc_clear_class_flag(cls, objc_class_flag_fast_arc); objc_clear_class_flag(cls, objc_class_flag_fast_arc);
return; return;
} }
slot = objc_get_slot2(cls, release); owner = ownerForMethod(cls, retain);
if ((NULL != slot) && !ownsMethod(slot->owner, isARC)) if ((NULL != owner) && !ownsMethod(owner, isARC))
{ {
ARC_DEBUG_LOG("%s does not support ARC correctly (implements release)\n", cls->name); ARC_DEBUG_LOG("%s does not support ARC correctly (implements release)\n", cls->name);
objc_clear_class_flag(cls, objc_class_flag_fast_arc); objc_clear_class_flag(cls, objc_class_flag_fast_arc);
return; return;
} }
slot = objc_get_slot2(cls, autorelease); owner = ownerForMethod(cls, retain);
if ((NULL != slot) && !ownsMethod(slot->owner, isARC)) if ((NULL != owner) && !ownsMethod(owner, isARC))
{ {
ARC_DEBUG_LOG("%s does not support ARC correctly (implements autorelease)\n", cls->name); ARC_DEBUG_LOG("%s does not support ARC correctly (implements autorelease)\n", cls->name);
objc_clear_class_flag(cls, objc_class_flag_fast_arc); objc_clear_class_flag(cls, objc_class_flag_fast_arc);
@ -99,6 +119,15 @@ static void checkARCAccessors(Class cls)
objc_set_class_flag(cls, objc_class_flag_fast_arc); objc_set_class_flag(cls, objc_class_flag_fast_arc);
} }
static BOOL selEqualUnTyped(SEL expected, SEL untyped)
{
return (expected->index == untyped->index)
#ifdef TYPE_DEPENDENT_DISPATCH
|| (get_untyped_idx(expected) == untyped->index)
#endif
;
}
PRIVATE void checkARCAccessorsSlow(Class cls) PRIVATE void checkARCAccessorsSlow(Class cls)
{ {
if (cls->dtable != uninstalled_dtable) if (cls->dtable != uninstalled_dtable)
@ -113,31 +142,32 @@ PRIVATE void checkARCAccessorsSlow(Class cls)
autorelease = sel_registerName("autorelease"); autorelease = sel_registerName("autorelease");
isARC = sel_registerName("_ARCCompliantRetainRelease"); isARC = sel_registerName("_ARCCompliantRetainRelease");
} }
BOOL superIsFast = YES;
if (cls->super_class != Nil) if (cls->super_class != Nil)
{ {
checkARCAccessorsSlow(cls->super_class); checkARCAccessorsSlow(cls->super_class);
superIsFast = objc_test_class_flag(cls->super_class, objc_class_flag_fast_arc);
} }
BOOL superIsFast = objc_test_class_flag(cls, objc_class_flag_fast_arc);
BOOL selfImplementsRetainRelease = NO; BOOL selfImplementsRetainRelease = NO;
for (struct objc_method_list *l=cls->methods ; l != NULL ; l= l->next) for (struct objc_method_list *l=cls->methods ; l != NULL ; l= l->next)
{ {
for (int i=0 ; i<l->count ; i++) for (int i=0 ; i<l->count ; i++)
{ {
SEL s = l->methods[i].selector; SEL s = l->methods[i].selector;
if (sel_isEqual(s, retain) || if (selEqualUnTyped(s, retain) ||
sel_isEqual(s, release) || selEqualUnTyped(s, release) ||
sel_isEqual(s, autorelease)) selEqualUnTyped(s, autorelease))
{ {
selfImplementsRetainRelease = YES; selfImplementsRetainRelease = YES;
} }
else if (sel_isEqual(s, isARC)) else if (selEqualUnTyped(s, isARC))
{ {
objc_set_class_flag(cls, objc_class_flag_fast_arc); objc_set_class_flag(cls, objc_class_flag_fast_arc);
return; return;
} }
} }
} }
if (superIsFast && ! selfImplementsRetainRelease) if (superIsFast && !selfImplementsRetainRelease)
{ {
objc_set_class_flag(cls, objc_class_flag_fast_arc); objc_set_class_flag(cls, objc_class_flag_fast_arc);
} }
@ -242,63 +272,87 @@ int objc_registerTracingHook(SEL aSel, objc_tracing_hook aHook)
#endif #endif
} }
/**
* Installs a new method in the dtable for `class`. If `replaceMethod` is
* `YES` then this will replace any dtable entry where the original is
* `method_to_replace`. This is used when a superclass method is replaced, to
* replace all subclass dtable entries that are inherited, but not ones that
* are overridden.
*/
static BOOL installMethodInDtable(Class class, static BOOL installMethodInDtable(Class class,
Class owner,
SparseArray *dtable, SparseArray *dtable,
struct objc_method *method, struct objc_method *method,
struct objc_method *method_to_replace,
BOOL replaceExisting) BOOL replaceExisting)
{ {
ASSERT(uninstalled_dtable != dtable); ASSERT(uninstalled_dtable != dtable);
uint32_t sel_id = method->selector->index; uint32_t sel_id = method->selector->index;
struct objc_slot *slot = SparseArrayLookup(dtable, sel_id); struct objc_method *oldMethod = SparseArrayLookup(dtable, sel_id);
if (NULL != slot) // If we're being asked to replace an existing method, don't if it's the
// wrong one.
if ((replaceExisting) && (method_to_replace != oldMethod))
{ {
// If this method is the one already installed, pretend to install it again. return NO;
if (slot->method == method->imp) { return NO; }
// If the existing slot is for this class, we can just replace the
// implementation. We don't need to bump the version; this operation
// updates cached slots, it doesn't invalidate them.
if (slot->owner == owner)
{
// Don't replace methods if we're not meant to (if they're from
// later in a method list, for example)
if (!replaceExisting) { return NO; }
slot->method = method->imp;
return YES;
}
// Check whether the owner of this method is a subclass of the one that
// owns this method. If it is, then we don't want to install this
// method irrespective of other cases, because it has been overridden.
for (Class installedFor = slot->owner ;
Nil != installedFor ;
installedFor = installedFor->super_class)
{
if (installedFor == owner)
{
return NO;
}
}
} }
struct objc_slot *oldSlot = slot; // If we're not being asked to replace existing methods and there is an
slot = new_slot_for_method_in_class((void*)method, owner); // existing one, don't replace it.
SparseArrayInsert(dtable, sel_id, slot); if (!replaceExisting && (oldMethod != NULL))
{
return NO;
}
// If this method is the one already installed, pretend to install it again.
if (NULL != oldMethod && (oldMethod->imp == method->imp))
{
return NO;
}
SparseArrayInsert(dtable, sel_id, method);
// In TDD mode, we also register the first typed method that we // In TDD mode, we also register the first typed method that we
// encounter as the untyped version. // encounter as the untyped version.
#ifdef TYPE_DEPENDENT_DISPATCH #ifdef TYPE_DEPENDENT_DISPATCH
SparseArrayInsert(dtable, get_untyped_idx(method->selector), slot); uint32_t untyped_idx = get_untyped_idx(method->selector);
SparseArrayInsert(dtable, untyped_idx, method);
#endif #endif
static SEL cxx_construct, cxx_destruct;
if (NULL == cxx_construct)
{
cxx_construct = sel_registerName(".cxx_construct");
cxx_destruct = sel_registerName(".cxx_destruct");
}
if (selEqualUnTyped(method->selector, cxx_construct))
{
class->cxx_construct = method->imp;
}
else if (selEqualUnTyped(method->selector, cxx_destruct))
{
class->cxx_destruct = method->imp;
}
for (struct objc_class *subclass=class->subclass_list ;
Nil != subclass ; subclass = subclass->sibling_class)
{
// Don't bother updating dtables for subclasses that haven't been
// initialized yet
if (!classHasDtable(subclass)) { continue; }
// Recursively install this method in all subclasses
installMethodInDtable(subclass,
dtable_for_class(subclass),
method,
oldMethod,
YES);
}
// Invalidate the old slot, if there is one. // Invalidate the old slot, if there is one.
if (NULL != oldSlot) if (NULL != oldMethod)
{ {
oldSlot->version++; objc_method_cache_version++;
} }
return YES; return YES;
} }
static void installMethodsInClass(Class cls, static void installMethodsInClass(Class cls,
Class owner, SparseArray *methods_to_replace,
SparseArray *methods, SparseArray *methods,
BOOL replaceExisting) BOOL replaceExisting)
{ {
@ -309,7 +363,10 @@ static void installMethodsInClass(Class cls,
struct objc_method *m; struct objc_method *m;
while ((m = SparseArrayNext(methods, &idx))) while ((m = SparseArrayNext(methods, &idx)))
{ {
if (!installMethodInDtable(cls, owner, dtable, m, replaceExisting)) struct objc_method *method_to_replace = methods_to_replace
? SparseArrayLookup(methods_to_replace, m->selector->index)
: NULL;
if (!installMethodInDtable(cls, dtable, m, method_to_replace, replaceExisting))
{ {
// Remove this method from the list, if it wasn't actually installed // Remove this method from the list, if it wasn't actually installed
SparseArrayInsert(methods, idx, 0); SparseArrayInsert(methods, idx, 0);
@ -317,27 +374,6 @@ static void installMethodsInClass(Class cls,
} }
} }
static void mergeMethodsFromSuperclass(Class super, Class cls, SparseArray *methods)
{
for (struct objc_class *subclass=cls->subclass_list ;
Nil != subclass ; subclass = subclass->sibling_class)
{
// Don't bother updating dtables for subclasses that haven't been
// initialized yet
if (!classHasDtable(subclass)) { continue; }
// Create a new (copy-on-write) array to pass down to children
SparseArray *newMethods = SparseArrayCopy(methods);
// Install all of these methods except ones that are overridden in the
// subclass. All of the methods that we are updating were added in a
// superclass, so we don't replace versions registered to the subclass.
installMethodsInClass(subclass, super, newMethods, YES);
// Recursively add the methods to the subclass's subclasses.
mergeMethodsFromSuperclass(super, subclass, newMethods);
SparseArrayDestroy(newMethods);
}
}
Class class_getSuperclass(Class); Class class_getSuperclass(Class);
PRIVATE void objc_update_dtable_for_class(Class cls) PRIVATE void objc_update_dtable_for_class(Class cls)
@ -349,9 +385,9 @@ PRIVATE void objc_update_dtable_for_class(Class cls)
SparseArray *methods = SparseArrayNewWithDepth(dtable_depth); SparseArray *methods = SparseArrayNewWithDepth(dtable_depth);
collectMethodsForMethodListToSparseArray((void*)cls->methods, methods, YES); collectMethodsForMethodListToSparseArray((void*)cls->methods, methods, YES);
installMethodsInClass(cls, cls, methods, YES); SparseArray *super_dtable = cls->super_class ? dtable_for_class(cls->super_class)
// Methods now contains only the new methods for this class. : NULL;
mergeMethodsFromSuperclass(cls, cls, methods); installMethodsInClass(cls, super_dtable, methods, YES);
SparseArrayDestroy(methods); SparseArrayDestroy(methods);
checkARCAccessors(cls); checkARCAccessors(cls);
} }
@ -365,10 +401,11 @@ PRIVATE void add_method_list_to_class(Class cls,
LOCK_RUNTIME_FOR_SCOPE(); LOCK_RUNTIME_FOR_SCOPE();
SparseArray *methods = SparseArrayNewWithDepth(dtable_depth); SparseArray *methods = SparseArrayNewWithDepth(dtable_depth);
SparseArray *super_dtable = cls->super_class ? dtable_for_class(cls->super_class)
: NULL;
collectMethodsForMethodListToSparseArray(list, methods, NO); collectMethodsForMethodListToSparseArray(list, methods, NO);
installMethodsInClass(cls, cls, methods, YES); installMethodsInClass(cls, super_dtable, methods, YES);
// Methods now contains only the new methods for this class. // Methods now contains only the new methods for this class.
mergeMethodsFromSuperclass(cls, cls, methods);
SparseArrayDestroy(methods); SparseArrayDestroy(methods);
checkARCAccessors(cls); checkARCAccessors(cls);
} }
@ -386,7 +423,7 @@ static dtable_t create_dtable_for_class(Class class, dtable_t root_dtable)
Class super = class_getSuperclass(class); Class super = class_getSuperclass(class);
dtable_t dtable; dtable_t dtable;
dtable_t super_dtable = NULL;
if (Nil == super) if (Nil == super)
{ {
@ -394,7 +431,7 @@ static dtable_t create_dtable_for_class(Class class, dtable_t root_dtable)
} }
else else
{ {
dtable_t super_dtable = dtable_for_class(super); super_dtable = dtable_for_class(super);
if (super_dtable == uninstalled_dtable) if (super_dtable == uninstalled_dtable)
{ {
if (super->isa == class) if (super->isa == class)
@ -412,15 +449,18 @@ static dtable_t create_dtable_for_class(Class class, dtable_t root_dtable)
// When constructing the initial dtable for a class, we iterate along the // When constructing the initial dtable for a class, we iterate along the
// method list in forward-traversal order. The first method that we // method list in forward-traversal order. The first method that we
// encounter is always the one that we want to keep, so we instruct // encounter is always the one that we want to keep, so we instruct
// installMethodInDtable() not to replace methods that are already // installMethodInDtable() to replace only methods that are inherited from
// associated with this class. // the superclass.
struct objc_method_list *list = (void*)class->methods; struct objc_method_list *list = (void*)class->methods;
while (NULL != list) while (NULL != list)
{ {
for (unsigned i=0 ; i<list->count ; i++) for (unsigned i=0 ; i<list->count ; i++)
{ {
installMethodInDtable(class, class, dtable, &list->methods[i], NO); struct objc_method *super_method = super_dtable
? SparseArrayLookup(super_dtable, list->methods[i].selector->index)
: NULL;
installMethodInDtable(class, dtable, &list->methods[i], super_method, YES);
} }
list = list->next; list = list->next;
} }
@ -623,7 +663,7 @@ PRIVATE void objc_send_initialize(id object)
initializeSel = sel_registerName("initialize"); initializeSel = sel_registerName("initialize");
} }
struct objc_slot *initializeSlot = skipMeta ? 0 : struct objc_method *initializeSlot = skipMeta ? 0 :
objc_dtable_lookup(dtable, initializeSel->index); objc_dtable_lookup(dtable, initializeSel->index);
// If there's no initialize method, then don't bother installing and // If there's no initialize method, then don't bother installing and
@ -662,6 +702,6 @@ PRIVATE void objc_send_initialize(id object)
// Store the buffer in the temporary dtables list. Note that it is safe to // Store the buffer in the temporary dtables list. Note that it is safe to
// insert it into a global list, even though it's a temporary variable, // insert it into a global list, even though it's a temporary variable,
// because we will clean it up after this function. // because we will clean it up after this function.
initializeSlot->method((id)class, initializeSel); initializeSlot->imp((id)class, initializeSel);
} }

@ -79,7 +79,12 @@ static struct objc_method_list *upgradeMethodList(struct objc_method_list_legacy
l->next = upgradeMethodList(old->next); l->next = upgradeMethodList(old->next);
} }
l->size = sizeof(struct objc_method); l->size = sizeof(struct objc_method);
memcpy(&l->methods, &old->methods, old->count * sizeof(struct objc_method)); for (int i=0 ; i<old->count ; i++)
{
l->methods[i].imp = old->methods[i].imp;
l->methods[i].selector = old->methods[i].selector;
l->methods[i].types = old->methods[i].types;
}
return l; return l;
} }

@ -2,6 +2,25 @@
* Metadata structure describing a method. * Metadata structure describing a method.
*/ */
struct objc_method struct objc_method
{
/**
* A pointer to the function implementing this method.
*/
IMP imp;
/**
* Selector used to send messages to this method. The type encoding of
* this method should match the types field.
*/
SEL selector;
/**
* The type encoding for this selector. Used only for introspection, and
* 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;
};
struct objc_method_legacy
{ {
/** /**
* Selector used to send messages to this method. The type encoding of * Selector used to send messages to this method. The type encoding of
@ -63,5 +82,5 @@ struct objc_method_list_legacy
/** /**
* An array of methods. Note that the actual size of this is count. * An array of methods. Note that the actual size of this is count.
*/ */
struct objc_method methods[]; struct objc_method_legacy methods[];
}; };

@ -27,19 +27,11 @@
*/ */
struct objc_slot struct objc_slot
{ {
/** The method pointer for this method. */
IMP method; IMP method;
/** The current version. This changes if the method changes or if a
* subclass overrides this method, potentially invalidating this cache. */
int version;
/**
* The method for this slot.
*/
Method method_metadata;
/** The class to which this slot is attached (used internally). */
Class owner;
} OBJC_NONPORTABLE; } OBJC_NONPORTABLE;
_Atomic(uint64_t) objc_method_cache_version;
struct objc_slot_v1 struct objc_slot_v1
{ {
/** The class to which this slot is attached (used internally). */ /** The class to which this slot is attached (used internally). */

@ -36,13 +36,11 @@ PRIVATE void call_cxx_destruct(id obj)
while (cls) while (cls)
{ {
struct objc_slot *slot = objc_get_slot2(cls, cxx_destruct); if (cls->cxx_destruct)
cls = Nil;
if (NULL != slot)
{ {
cls = slot->owner->super_class; cls->cxx_destruct(obj, cxx_destruct);
slot->method(obj, cxx_destruct);
} }
cls = cls->super_class;
} }
} }
@ -53,15 +51,14 @@ static void call_cxx_construct_for_class(Class cls, id obj)
{ {
cxx_construct = sel_registerName(".cxx_construct"); cxx_construct = sel_registerName(".cxx_construct");
} }
struct objc_slot *slot = objc_get_slot2(cls, cxx_construct);
if (NULL != slot) if (cls->super_class)
{ {
cls = slot->owner->super_class; call_cxx_construct_for_class(cls->super_class, obj);
if (Nil != cls) }
{ if (cls->cxx_construct)
call_cxx_construct_for_class(cls, obj); {
} cls->cxx_construct(obj, cxx_construct);
slot->method(obj, cxx_construct);
} }
} }
@ -91,27 +88,6 @@ static Method class_getInstanceMethodNonrecursive(Class aClass, SEL aSelector)
return NULL; return NULL;
} }
static void objc_updateDtableForClassContainingMethod(Method m)
{
Class nextClass = Nil;
void *state = NULL;
SEL sel = method_getName(m);
while (Nil != (nextClass = objc_next_class(&state)))
{
if (class_getInstanceMethodNonrecursive(nextClass, sel) == m)
{
objc_update_dtable_for_class(nextClass);
return;
}
Class meta = object_getClass((id)nextClass);
if (class_getInstanceMethodNonrecursive(meta, sel) == m)
{
objc_update_dtable_for_class(meta);
return;
}
}
}
BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment, BOOL class_addIvar(Class cls, const char *name, size_t size, uint8_t alignment,
const char *types) const char *types)
{ {
@ -395,9 +371,8 @@ Method class_getInstanceMethod(Class aClass, SEL aSelector)
return NULL; return NULL;
} }
} }
// Slots are the same as methods.
// Then do the slow lookup to find the method. return (struct objc_method*)slot;
return slot->method_metadata;
} }
Method m = class_getInstanceMethodNonrecursive(aClass, aSelector); Method m = class_getInstanceMethodNonrecursive(aClass, aSelector);
if (NULL != m) if (NULL != m)
@ -494,12 +469,6 @@ IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)
} }
IMP old = (IMP)method->imp; IMP old = (IMP)method->imp;
method->imp = imp; method->imp = imp;
if (objc_test_class_flag(cls, objc_class_flag_resolved))
{
objc_update_dtable_for_class(cls);
}
return old; return old;
} }
@ -561,8 +530,6 @@ void method_exchangeImplementations(Method m1, Method m2)
IMP tmp = (IMP)m1->imp; IMP tmp = (IMP)m1->imp;
m1->imp = m2->imp; m1->imp = m2->imp;
m2->imp = tmp; m2->imp = tmp;
objc_updateDtableForClassContainingMethod(m1);
objc_updateDtableForClassContainingMethod(m2);
} }
IMP method_getImplementation(Method method) IMP method_getImplementation(Method method)
@ -583,7 +550,6 @@ IMP method_setImplementation(Method method, IMP imp)
if (NULL == method) { return (IMP)NULL; } if (NULL == method) { return (IMP)NULL; }
IMP old = (IMP)method->imp; IMP old = (IMP)method->imp;
method->imp = imp; method->imp = imp;
objc_updateDtableForClassContainingMethod(method);
return old; return old;
} }

@ -19,10 +19,10 @@ static struct objc_slot_v1 nil_slot_D_v1 = { Nil, Nil, 0, 1, (IMP)nil_method_D }
static struct objc_slot_v1 nil_slot_d_v1 = { Nil, Nil, 0, 1, (IMP)nil_method_d }; static struct objc_slot_v1 nil_slot_d_v1 = { Nil, Nil, 0, 1, (IMP)nil_method_d };
static struct objc_slot_v1 nil_slot_f_v1 = { Nil, Nil, 0, 1, (IMP)nil_method_f }; static struct objc_slot_v1 nil_slot_f_v1 = { Nil, Nil, 0, 1, (IMP)nil_method_f };
static struct objc_slot nil_slot = { (IMP)nil_method, 1, NULL, Nil }; static struct objc_method nil_slot = { (IMP)nil_method, NULL, NULL };
static struct objc_slot nil_slot_D = { (IMP)nil_method_D, 1, NULL, Nil }; static struct objc_method nil_slot_D = { (IMP)nil_method_D, NULL, NULL };
static struct objc_slot nil_slot_d = { (IMP)nil_method_d, 1, NULL, Nil }; static struct objc_method nil_slot_d = { (IMP)nil_method_d, NULL, NULL };
static struct objc_slot nil_slot_f = { (IMP)nil_method_f, 1, NULL, Nil }; static struct objc_method nil_slot_f = { (IMP)nil_method_f, NULL, NULL };
struct objc_slot* objc_slot_lookup(id *receiver, SEL selector); struct objc_slot* objc_slot_lookup(id *receiver, SEL selector);
@ -39,7 +39,7 @@ static IMP forward2(id self, SEL _cmd)
} }
IMP (*__objc_msg_forward2)(id, SEL) = forward2; IMP (*__objc_msg_forward2)(id, SEL) = forward2;
__thread struct objc_slot uncacheable_slot = { (IMP)nil_method, 0, NULL, Nil }; __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
@ -47,11 +47,12 @@ static struct objc_slot* 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. "
"Method has %s, selector has %s\n", "Method has %s (%s), selector has %s\n",
cls->name, cls->name,
class_isMetaClass(cls) ? '+' : '-', class_isMetaClass(cls) ? '+' : '-',
sel_getName(selector), sel_getName(selector),
result->method_metadata->types, sel_getType_np(((struct objc_method*)result)->selector),
((struct objc_method*)result)->types,
sel_getType_np(selector)); sel_getType_np(selector));
return result; return result;
} }
@ -75,8 +76,7 @@ static struct objc_slot *call_mismatch_hook(Class cls, SEL sel, struct objc_slot
(_objc_selector_type_mismatch2 == objc_selector_type_mismatch))) (_objc_selector_type_mismatch2 == objc_selector_type_mismatch)))
{ {
struct objc_slot_v1 fwdslot; struct objc_slot_v1 fwdslot;
fwdslot.owner = slot->owner; fwdslot.types = ((struct objc_method*)slot)->types;
fwdslot.types = slot->method_metadata->types;
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);
@ -84,9 +84,8 @@ static struct objc_slot *call_mismatch_hook(Class cls, SEL sel, struct objc_slot
{ {
return slot; return slot;
} }
uncacheable_slot.owner = slot_v1->owner; uncacheable_slot.imp = slot_v1->method;
uncacheable_slot.method = slot_v1->method; return (struct objc_slot*)&uncacheable_slot;
return &uncacheable_slot;
} }
return _objc_selector_type_mismatch2(cls, sel, slot); return _objc_selector_type_mismatch2(cls, sel, slot);
} }
@ -139,8 +138,8 @@ retry:;
} }
if (0 == result) if (0 == result)
{ {
uncacheable_slot.method = __objc_msg_forward2(*receiver, selector); uncacheable_slot.imp = __objc_msg_forward2(*receiver, selector);
result = &uncacheable_slot; result = (struct objc_slot*)&uncacheable_slot;
} }
} }
} }
@ -191,8 +190,8 @@ struct objc_slot_v1 *objc_msg_lookup_sender(id *receiver, SEL selector, id sende
} }
struct objc_slot *slot = objc_msg_lookup_internal(receiver, selector); struct objc_slot *slot = objc_msg_lookup_internal(receiver, selector);
uncacheable_slot_v1.owner = slot->owner; uncacheable_slot_v1.owner = Nil;
uncacheable_slot_v1.types = slot->method_metadata->types; uncacheable_slot_v1.types = sel_getType_np(((struct objc_method*)slot)->selector);
uncacheable_slot_v1.selector = selector; uncacheable_slot_v1.selector = selector;
uncacheable_slot_v1.method = slot->method; uncacheable_slot_v1.method = slot->method;
return &uncacheable_slot_v1; return &uncacheable_slot_v1;
@ -217,12 +216,12 @@ struct objc_slot* objc_slot_lookup(id *receiver, SEL selector)
} }
switch (selector->types[0]) switch (selector->types[0])
{ {
case 'D': return &nil_slot_D; case 'D': return (struct objc_slot*)&nil_slot_D;
case 'd': return &nil_slot_d; case 'd': return (struct objc_slot*)&nil_slot_d;
case 'f': return &nil_slot_f; case 'f': return (struct objc_slot*)&nil_slot_f;
} }
} }
return &nil_slot; return (struct objc_slot*)&nil_slot;
} }
return objc_msg_lookup_internal(receiver, selector); return objc_msg_lookup_internal(receiver, selector);
@ -254,11 +253,11 @@ struct objc_slot *objc_slot_lookup_super2(struct objc_super *super, SEL selector
objc_send_initialize((id)class); objc_send_initialize((id)class);
return objc_slot_lookup_super2(super, selector); return objc_slot_lookup_super2(super, selector);
} }
return &nil_slot; return (struct objc_slot*)&nil_slot;
} }
return result; return result;
} }
return &nil_slot; return (struct objc_slot*)&nil_slot;
} }
struct objc_slot_v1 *objc_slot_lookup_super(struct objc_super *super, SEL selector) struct objc_slot_v1 *objc_slot_lookup_super(struct objc_super *super, SEL selector)
@ -289,8 +288,8 @@ struct objc_slot_v1 *objc_slot_lookup_super(struct objc_super *super, SEL select
} }
return &nil_slot_v1; return &nil_slot_v1;
} }
uncacheable_slot_v1.owner = result->owner; uncacheable_slot_v1.owner = Nil;
uncacheable_slot_v1.types = result->method_metadata->types; uncacheable_slot_v1.types = sel_getType_np(((struct objc_method*)result)->selector);
uncacheable_slot_v1.selector = selector; uncacheable_slot_v1.selector = selector;
uncacheable_slot_v1.method = result->method; uncacheable_slot_v1.method = result->method;
return &uncacheable_slot_v1; return &uncacheable_slot_v1;
@ -422,9 +421,9 @@ struct objc_slot_v1 *objc_get_slot(Class cls, SEL selector)
{ {
return NULL; return NULL;
} }
uncacheable_slot_v1.owner = result->owner; uncacheable_slot_v1.owner = Nil;
// Don't leak extended type encodings! // Don't leak extended type encodings!
uncacheable_slot_v1.types = sel_getType_np(result->method_metadata->selector); uncacheable_slot_v1.types = sel_getType_np(((struct objc_method*)result)->selector);
uncacheable_slot_v1.selector = selector; uncacheable_slot_v1.selector = selector;
uncacheable_slot_v1.method = result->method; uncacheable_slot_v1.method = result->method;
return &uncacheable_slot_v1; return &uncacheable_slot_v1;

@ -9,9 +9,7 @@ static inline struct objc_slot *new_slot_for_method_in_class(Method method,
Class class) Class class)
{ {
struct objc_slot *slot = slot_pool_alloc(); struct objc_slot *slot = slot_pool_alloc();
slot->owner = class;
slot->method_metadata = method; slot->method_metadata = method;
slot->method = method->imp; slot->method = method->imp;
slot->version = 1;
return slot; return slot;
} }

Loading…
Cancel
Save