/*********************************************************************** * load_images * Process +load in the given images which are being mapped in by dyld. * * Locking: write-locks runtimeLock and loadMethodLock **********************************************************************/ externboolhasLoadMethods(const headerType *mhdr); externvoidprepare_load_methods(const headerType *mhdr);
void load_images(constchar *path __unused, conststruct mach_header *mh) { // Return without taking locks if there are no +load methods here. if (!hasLoadMethods((const headerType *)mh)) return;
/*********************************************************************** * prepare_load_methods * Schedule +load for classes in this image, any un-+load-ed * superclasses in other images, and any categories in this image. **********************************************************************/ // Recursively schedule +load for cls and any un-+load-ed superclasses. // cls must already be connected. staticvoidschedule_class_load(Class cls) { if (!cls) return; assert(cls->isRealized()); // _read_images should realize
/*********************************************************************** * add_class_to_loadable_list * Class cls has just become connected. Schedule it for +load if * it implements a +load method. **********************************************************************/ voidadd_class_to_loadable_list(Class cls) { IMP method;
loadMethodLock.assertLocked();
method = cls->getLoadMethod(); if (!method) return; // Don't bother if cls has no +load method if (PrintLoading) { _objc_inform("LOAD: class '%s' scheduled for +load", cls->nameForLogging()); } if (loadable_classes_used == loadable_classes_allocated) { loadable_classes_allocated = loadable_classes_allocated*2 + 16; loadable_classes = (struct loadable_class *) realloc(loadable_classes, loadable_classes_allocated * sizeof(struct loadable_class)); } loadable_classes[loadable_classes_used].cls = cls; loadable_classes[loadable_classes_used].method = method; loadable_classes_used++; }
/*********************************************************************** * add_category_to_loadable_list * Category cat's parent class exists and the category has been attached * to its class. Schedule this category for +load after its parent class * becomes connected and has its own +load method called. **********************************************************************/ voidadd_category_to_loadable_list(Category cat) { IMP method;
loadMethodLock.assertLocked();
method = _category_getLoadMethod(cat);
// Don't bother if cat has no +load method if (!method) return;
/*********************************************************************** * call_class_loads * Call all pending class +load methods. * If new classes become loadable, +load is NOT called for them. * * Called only by call_load_methods(). **********************************************************************/ staticvoidcall_class_loads(void) { int i; // Detach current loadable list. structloadable_class *classes = loadable_classes; int used = loadable_classes_used; loadable_classes = nil; loadable_classes_allocated = 0; loadable_classes_used = 0; // Call all +loads for the detached list. for (i = 0; i < used; i++) { Class cls = classes[i].cls; load_method_t load_method = (load_method_t)classes[i].method; if (!cls) continue;
if (PrintLoading) { _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging()); } (*load_method)(cls, SEL_load); } // Destroy the detached list. if (classes) free(classes); }
/*********************************************************************** * call_category_loads * Call some pending category +load methods. * The parent class of the +load-implementing categories has all of * its categories attached, in case some are lazily waiting for +initalize. * Don't call +load unless the parent class is connected. * If new categories become loadable, +load is NOT called, and they * are added to the end of the loadable list, and we return TRUE. * Return FALSE if no new categories became loadable. * * Called only by call_load_methods(). **********************************************************************/ staticboolcall_category_loads(void) { int i, shift; bool new_categories_added = NO; // Detach current loadable list. structloadable_category *cats = loadable_categories; int used = loadable_categories_used; int allocated = loadable_categories_allocated; loadable_categories = nil; loadable_categories_allocated = 0; loadable_categories_used = 0;
// Call all +loads for the detached list. for (i = 0; i < used; i++) { Category cat = cats[i].cat; load_method_t load_method = (load_method_t)cats[i].method; Class cls; if (!cat) continue;
// Compact detached list (order-preserving) shift = 0; for (i = 0; i < used; i++) { if (cats[i].cat) { cats[i-shift] = cats[i]; } else { shift++; } } used -= shift;
// Copy any new +load candidates from the new list to the detached list. new_categories_added = (loadable_categories_used > 0); for (i = 0; i < loadable_categories_used; i++) { if (used == allocated) { allocated = allocated*2 + 16; cats = (struct loadable_category *) realloc(cats, allocated * sizeof(struct loadable_category)); } cats[used++] = loadable_categories[i]; }
// Destroy the new list. if (loadable_categories) free(loadable_categories);
// Reattach the (now augmented) detached list. // But if there's nothing left to load, destroy the list. if (used) { loadable_categories = cats; loadable_categories_used = used; loadable_categories_allocated = allocated; } else { if (cats) free(cats); loadable_categories = nil; loadable_categories_used = 0; loadable_categories_allocated = 0; }
if (PrintLoading) { if (loadable_categories_used != 0) { _objc_inform("LOAD: %d categories still waiting for +load\n", loadable_categories_used); } }