123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- /*
- * Copyright (c) 2004-2006 Apple Inc. All Rights Reserved.
- *
- * @APPLE_LICENSE_HEADER_START@
- *
- * This file contains Original Code and/or Modifications of Original Code
- * as defined in and that are subject to the Apple Public Source License
- * Version 2.0 (the 'License'). You may not use this file except in
- * compliance with the License. Please obtain a copy of the License at
- * http://www.opensource.apple.com/apsl/ and read it before using this
- * file.
- *
- * The Original Code and all software distributed under the License are
- * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
- * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
- * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
- * Please see the License for the specific language governing rights and
- * limitations under the License.
- *
- * @APPLE_LICENSE_HEADER_END@
- */
- /***********************************************************************
- * objc-loadmethod.m
- * Support for +load methods.
- **********************************************************************/
- #include "objc-loadmethod.h"
- #include "objc-private.h"
- typedef void(*load_method_t)(id, SEL);
- struct loadable_class {
- Class cls; // may be nil
- IMP method;
- };
- struct loadable_category {
- Category cat; // may be nil
- IMP method;
- };
- // List of classes that need +load called (pending superclass +load)
- // This list always has superclasses first because of the way it is constructed
- static struct loadable_class *loadable_classes = nil;
- static int loadable_classes_used = 0;
- static int loadable_classes_allocated = 0;
- // List of categories that need +load called (pending parent class +load)
- static struct loadable_category *loadable_categories = nil;
- static int loadable_categories_used = 0;
- static int loadable_categories_allocated = 0;
- /***********************************************************************
- * add_class_to_loadable_list
- * Class cls has just become connected. Schedule it for +load if
- * it implements a +load method.
- **********************************************************************/
- void add_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.
- **********************************************************************/
- void add_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;
- if (PrintLoading) {
- _objc_inform("LOAD: category '%s(%s)' scheduled for +load",
- _category_getClassName(cat), _category_getName(cat));
- }
-
- if (loadable_categories_used == loadable_categories_allocated) {
- loadable_categories_allocated = loadable_categories_allocated*2 + 16;
- loadable_categories = (struct loadable_category *)
- realloc(loadable_categories,
- loadable_categories_allocated *
- sizeof(struct loadable_category));
- }
- loadable_categories[loadable_categories_used].cat = cat;
- loadable_categories[loadable_categories_used].method = method;
- loadable_categories_used++;
- }
- /***********************************************************************
- * remove_class_from_loadable_list
- * Class cls may have been loadable before, but it is now no longer
- * loadable (because its image is being unmapped).
- **********************************************************************/
- void remove_class_from_loadable_list(Class cls)
- {
- loadMethodLock.assertLocked();
- if (loadable_classes) {
- int i;
- for (i = 0; i < loadable_classes_used; i++) {
- if (loadable_classes[i].cls == cls) {
- loadable_classes[i].cls = nil;
- if (PrintLoading) {
- _objc_inform("LOAD: class '%s' unscheduled for +load",
- cls->nameForLogging());
- }
- return;
- }
- }
- }
- }
- /***********************************************************************
- * remove_category_from_loadable_list
- * Category cat may have been loadable before, but it is now no longer
- * loadable (because its image is being unmapped).
- **********************************************************************/
- void remove_category_from_loadable_list(Category cat)
- {
- loadMethodLock.assertLocked();
- if (loadable_categories) {
- int i;
- for (i = 0; i < loadable_categories_used; i++) {
- if (loadable_categories[i].cat == cat) {
- loadable_categories[i].cat = nil;
- if (PrintLoading) {
- _objc_inform("LOAD: category '%s(%s)' unscheduled for +load",
- _category_getClassName(cat),
- _category_getName(cat));
- }
- 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().
- **********************************************************************/
- static void call_class_loads(void)
- {
- int i;
-
- // Detach current loadable list.
- struct loadable_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, @selector(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().
- **********************************************************************/
- static bool call_category_loads(void)
- {
- int i, shift;
- bool new_categories_added = NO;
-
- // Detach current loadable list.
- struct loadable_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;
- cls = _category_getClass(cat);
- if (cls && cls->isLoadable()) {
- if (PrintLoading) {
- _objc_inform("LOAD: +[%s(%s) load]\n",
- cls->nameForLogging(),
- _category_getName(cat));
- }
- (*load_method)(cls, @selector(load));
- cats[i].cat = nil;
- }
- }
- // 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);
- }
- }
- return new_categories_added;
- }
- /***********************************************************************
- * call_load_methods
- * Call all pending class and category +load methods.
- * Class +load methods are called superclass-first.
- * Category +load methods are not called until after the parent class's +load.
- *
- * This method must be RE-ENTRANT, because a +load could trigger
- * more image mapping. In addition, the superclass-first ordering
- * must be preserved in the face of re-entrant calls. Therefore,
- * only the OUTERMOST call of this function will do anything, and
- * that call will handle all loadable classes, even those generated
- * while it was running.
- *
- * The sequence below preserves +load ordering in the face of
- * image loading during a +load, and make sure that no
- * +load method is forgotten because it was added during
- * a +load call.
- * Sequence:
- * 1. Repeatedly call class +loads until there aren't any more
- * 2. Call category +loads ONCE.
- * 3. Run more +loads if:
- * (a) there are more classes to load, OR
- * (b) there are some potential category +loads that have
- * still never been attempted.
- * Category +loads are only run once to ensure "parent class first"
- * ordering, even if a category +load triggers a new loadable class
- * and a new loadable category attached to that class.
- *
- * Locking: loadMethodLock must be held by the caller
- * All other locks must not be held.
- **********************************************************************/
- void call_load_methods(void)
- {
- static bool loading = NO;
- bool more_categories;
- loadMethodLock.assertLocked();
- // Re-entrant calls do nothing; the outermost call will finish the job.
- if (loading) return;
- loading = YES;
- void *pool = objc_autoreleasePoolPush();
- do {
- // 1. Repeatedly call class +loads until there aren't any more
- while (loadable_classes_used > 0) {
- call_class_loads();
- }
- // 2. Call category +loads ONCE
- more_categories = call_category_loads();
- // 3. Run more +loads if there are classes OR more untried categories
- } while (loadable_classes_used > 0 || more_categories);
- objc_autoreleasePoolPop(pool);
- loading = NO;
- }
|