bool install_slot(struct objc_object *obj, int offset, IMP method, char * types, uint32_t sel) { //Type must match selector type char * seltypes = types_for_selector(sel); if(safestrcmp(seltypes, types) != 0) { return false; } objc_lock_object(obj); if(SLOTS(obj) == NULL) { SLOTS(obj) = SparseArrayNew(); } struct objc_slot * oldSlot = SparseArrayLookup(SLOTS(obj), sel); if(oldSlot) { if(safestrcmp(types, oldSlot->types) != 0) { objc_unlock_object(obj); return false; } oldSlot->offset = offset; oldSlot->method = method; } else { //We are adding a new slot, not modifying an old one, so we need to //invalidate a load of caches. id object = obj; oldSlot = slot_lookup(&object, sel, nil); struct objc_slot * newslot = calloc(1, sizeof(struct objc_slot)); //TODO: Factor this out into a slot constructor newslot->offset = offset; newslot->method = method; if(types == NULL) { newslot->types = NULL; } else { newslot->types = strdup(types); } if(oldSlot != NULL) { oldSlot->version++; } SparseArrayInsert(SLOTS(obj), sel, newslot); } objc_unlock_object(obj); return true; }
static inline struct objc_slot * real_slot_lookup(struct objc_object ** obj, uint32_t sel, id sender) { id object = *obj; struct objc_slot * ret = NULL; do { if(LOOKUP(object)) { return (*LOOKUP(object))(obj, object, sel, sender); } if(SLOTS(object)) { ret = SparseArrayLookup(SLOTS(object), sel); } object = object->isa; } while(object != NULL && ret == NULL); return ret; }
static void _objc_unload_IMPs_in_class(Class cl, void *kernel_module){ if (cl->methods == NULL){ return; } objc_debug_log("Unloading IMPs in class %s\n", class_getName(cl)); for (objc_method_list *list = cl->methods; list != NULL; list = list->next){ objc_debug_log("\t [%p]\n", list); for (int i = 0; i < list->size; ++i){ Method m = &list->list[i]; if (module_contains_IMP(kernel_module, m->implementation)){ objc_debug_log("\t\t [%02i] - %s\n", i, sel_getName(m->selector)); IMP old_imp = m->implementation; m->implementation = objc_unloaded_module_method; /* Since the selector name and types can actually be also * allocated in the module, we better update the strings * as well. */ m->selector_name = "__objc_unloaded_method"; m->selector_types = sizeof(void*) == 4 ? "v8@0:4" : "v16@0:8"; /* Update the dtable! */ SparseArray *arr = (SparseArray*)cl->dtable; if (arr != NULL && arr != uninstalled_dtable){ struct objc_slot *slot = SparseArrayLookup(arr, m->selector); if (slot != NULL && slot->implementation == old_imp){ slot->implementation = m->implementation; slot->types = m->selector_types; ++slot->version; } } } } } }
SparseArrayDestroy(&uninstalled_dtable); slot_pool_free(); } static BOOL installMethodInDtable(Class class, Class owner, SparseArray *dtable, struct objc_method *method, BOOL replaceExisting) { objc_debug_log("Installing method %s into dtable of class %s\n", sel_getName(method->selector), class_getName(class)); objc_assert(uninstalled_dtable != dtable, ""); SEL sel_id = method->selector; struct objc_slot *slot = SparseArrayLookup(dtable, sel_id); if (NULL != slot) { // If this method is the one already installed, pretend to install it again. if (slot->implementation == method->implementation) { 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->implementation = method->implementation; return YES;