objc-loadmethod.mm 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367
  1. /*
  2. * Copyright (c) 2004-2006 Apple Inc. All Rights Reserved.
  3. *
  4. * @APPLE_LICENSE_HEADER_START@
  5. *
  6. * This file contains Original Code and/or Modifications of Original Code
  7. * as defined in and that are subject to the Apple Public Source License
  8. * Version 2.0 (the 'License'). You may not use this file except in
  9. * compliance with the License. Please obtain a copy of the License at
  10. * http://www.opensource.apple.com/apsl/ and read it before using this
  11. * file.
  12. *
  13. * The Original Code and all software distributed under the License are
  14. * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  15. * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  16. * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
  17. * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
  18. * Please see the License for the specific language governing rights and
  19. * limitations under the License.
  20. *
  21. * @APPLE_LICENSE_HEADER_END@
  22. */
  23. /***********************************************************************
  24. * objc-loadmethod.m
  25. * Support for +load methods.
  26. **********************************************************************/
  27. #include "objc-loadmethod.h"
  28. #include "objc-private.h"
  29. typedef void(*load_method_t)(id, SEL);
  30. struct loadable_class {
  31. Class cls; // may be nil
  32. IMP method;
  33. };
  34. struct loadable_category {
  35. Category cat; // may be nil
  36. IMP method;
  37. };
  38. // List of classes that need +load called (pending superclass +load)
  39. // This list always has superclasses first because of the way it is constructed
  40. static struct loadable_class *loadable_classes = nil;
  41. static int loadable_classes_used = 0;
  42. static int loadable_classes_allocated = 0;
  43. // List of categories that need +load called (pending parent class +load)
  44. static struct loadable_category *loadable_categories = nil;
  45. static int loadable_categories_used = 0;
  46. static int loadable_categories_allocated = 0;
  47. /***********************************************************************
  48. * add_class_to_loadable_list
  49. * Class cls has just become connected. Schedule it for +load if
  50. * it implements a +load method.
  51. **********************************************************************/
  52. void add_class_to_loadable_list(Class cls)
  53. {
  54. IMP method;
  55. loadMethodLock.assertLocked();
  56. method = cls->getLoadMethod();
  57. if (!method) return; // Don't bother if cls has no +load method
  58. if (PrintLoading) {
  59. _objc_inform("LOAD: class '%s' scheduled for +load",
  60. cls->nameForLogging());
  61. }
  62. if (loadable_classes_used == loadable_classes_allocated) {
  63. loadable_classes_allocated = loadable_classes_allocated*2 + 16;
  64. loadable_classes = (struct loadable_class *)
  65. realloc(loadable_classes,
  66. loadable_classes_allocated *
  67. sizeof(struct loadable_class));
  68. }
  69. loadable_classes[loadable_classes_used].cls = cls;
  70. loadable_classes[loadable_classes_used].method = method;
  71. loadable_classes_used++;
  72. }
  73. /***********************************************************************
  74. * add_category_to_loadable_list
  75. * Category cat's parent class exists and the category has been attached
  76. * to its class. Schedule this category for +load after its parent class
  77. * becomes connected and has its own +load method called.
  78. **********************************************************************/
  79. void add_category_to_loadable_list(Category cat)
  80. {
  81. IMP method;
  82. loadMethodLock.assertLocked();
  83. method = _category_getLoadMethod(cat);
  84. // Don't bother if cat has no +load method
  85. if (!method) return;
  86. if (PrintLoading) {
  87. _objc_inform("LOAD: category '%s(%s)' scheduled for +load",
  88. _category_getClassName(cat), _category_getName(cat));
  89. }
  90. if (loadable_categories_used == loadable_categories_allocated) {
  91. loadable_categories_allocated = loadable_categories_allocated*2 + 16;
  92. loadable_categories = (struct loadable_category *)
  93. realloc(loadable_categories,
  94. loadable_categories_allocated *
  95. sizeof(struct loadable_category));
  96. }
  97. loadable_categories[loadable_categories_used].cat = cat;
  98. loadable_categories[loadable_categories_used].method = method;
  99. loadable_categories_used++;
  100. }
  101. /***********************************************************************
  102. * remove_class_from_loadable_list
  103. * Class cls may have been loadable before, but it is now no longer
  104. * loadable (because its image is being unmapped).
  105. **********************************************************************/
  106. void remove_class_from_loadable_list(Class cls)
  107. {
  108. loadMethodLock.assertLocked();
  109. if (loadable_classes) {
  110. int i;
  111. for (i = 0; i < loadable_classes_used; i++) {
  112. if (loadable_classes[i].cls == cls) {
  113. loadable_classes[i].cls = nil;
  114. if (PrintLoading) {
  115. _objc_inform("LOAD: class '%s' unscheduled for +load",
  116. cls->nameForLogging());
  117. }
  118. return;
  119. }
  120. }
  121. }
  122. }
  123. /***********************************************************************
  124. * remove_category_from_loadable_list
  125. * Category cat may have been loadable before, but it is now no longer
  126. * loadable (because its image is being unmapped).
  127. **********************************************************************/
  128. void remove_category_from_loadable_list(Category cat)
  129. {
  130. loadMethodLock.assertLocked();
  131. if (loadable_categories) {
  132. int i;
  133. for (i = 0; i < loadable_categories_used; i++) {
  134. if (loadable_categories[i].cat == cat) {
  135. loadable_categories[i].cat = nil;
  136. if (PrintLoading) {
  137. _objc_inform("LOAD: category '%s(%s)' unscheduled for +load",
  138. _category_getClassName(cat),
  139. _category_getName(cat));
  140. }
  141. return;
  142. }
  143. }
  144. }
  145. }
  146. /***********************************************************************
  147. * call_class_loads
  148. * Call all pending class +load methods.
  149. * If new classes become loadable, +load is NOT called for them.
  150. *
  151. * Called only by call_load_methods().
  152. **********************************************************************/
  153. static void call_class_loads(void)
  154. {
  155. int i;
  156. // Detach current loadable list.
  157. struct loadable_class *classes = loadable_classes;
  158. int used = loadable_classes_used;
  159. loadable_classes = nil;
  160. loadable_classes_allocated = 0;
  161. loadable_classes_used = 0;
  162. // Call all +loads for the detached list.
  163. for (i = 0; i < used; i++) {
  164. Class cls = classes[i].cls;
  165. load_method_t load_method = (load_method_t)classes[i].method;
  166. if (!cls) continue;
  167. if (PrintLoading) {
  168. _objc_inform("LOAD: +[%s load]\n", cls->nameForLogging());
  169. }
  170. (*load_method)(cls, @selector(load));
  171. }
  172. // Destroy the detached list.
  173. if (classes) free(classes);
  174. }
  175. /***********************************************************************
  176. * call_category_loads
  177. * Call some pending category +load methods.
  178. * The parent class of the +load-implementing categories has all of
  179. * its categories attached, in case some are lazily waiting for +initalize.
  180. * Don't call +load unless the parent class is connected.
  181. * If new categories become loadable, +load is NOT called, and they
  182. * are added to the end of the loadable list, and we return TRUE.
  183. * Return FALSE if no new categories became loadable.
  184. *
  185. * Called only by call_load_methods().
  186. **********************************************************************/
  187. static bool call_category_loads(void)
  188. {
  189. int i, shift;
  190. bool new_categories_added = NO;
  191. // Detach current loadable list.
  192. struct loadable_category *cats = loadable_categories;
  193. int used = loadable_categories_used;
  194. int allocated = loadable_categories_allocated;
  195. loadable_categories = nil;
  196. loadable_categories_allocated = 0;
  197. loadable_categories_used = 0;
  198. // Call all +loads for the detached list.
  199. for (i = 0; i < used; i++) {
  200. Category cat = cats[i].cat;
  201. load_method_t load_method = (load_method_t)cats[i].method;
  202. Class cls;
  203. if (!cat) continue;
  204. cls = _category_getClass(cat);
  205. if (cls && cls->isLoadable()) {
  206. if (PrintLoading) {
  207. _objc_inform("LOAD: +[%s(%s) load]\n",
  208. cls->nameForLogging(),
  209. _category_getName(cat));
  210. }
  211. (*load_method)(cls, @selector(load));
  212. cats[i].cat = nil;
  213. }
  214. }
  215. // Compact detached list (order-preserving)
  216. shift = 0;
  217. for (i = 0; i < used; i++) {
  218. if (cats[i].cat) {
  219. cats[i-shift] = cats[i];
  220. } else {
  221. shift++;
  222. }
  223. }
  224. used -= shift;
  225. // Copy any new +load candidates from the new list to the detached list.
  226. new_categories_added = (loadable_categories_used > 0);
  227. for (i = 0; i < loadable_categories_used; i++) {
  228. if (used == allocated) {
  229. allocated = allocated*2 + 16;
  230. cats = (struct loadable_category *)
  231. realloc(cats, allocated *
  232. sizeof(struct loadable_category));
  233. }
  234. cats[used++] = loadable_categories[i];
  235. }
  236. // Destroy the new list.
  237. if (loadable_categories) free(loadable_categories);
  238. // Reattach the (now augmented) detached list.
  239. // But if there's nothing left to load, destroy the list.
  240. if (used) {
  241. loadable_categories = cats;
  242. loadable_categories_used = used;
  243. loadable_categories_allocated = allocated;
  244. } else {
  245. if (cats) free(cats);
  246. loadable_categories = nil;
  247. loadable_categories_used = 0;
  248. loadable_categories_allocated = 0;
  249. }
  250. if (PrintLoading) {
  251. if (loadable_categories_used != 0) {
  252. _objc_inform("LOAD: %d categories still waiting for +load\n",
  253. loadable_categories_used);
  254. }
  255. }
  256. return new_categories_added;
  257. }
  258. /***********************************************************************
  259. * call_load_methods
  260. * Call all pending class and category +load methods.
  261. * Class +load methods are called superclass-first.
  262. * Category +load methods are not called until after the parent class's +load.
  263. *
  264. * This method must be RE-ENTRANT, because a +load could trigger
  265. * more image mapping. In addition, the superclass-first ordering
  266. * must be preserved in the face of re-entrant calls. Therefore,
  267. * only the OUTERMOST call of this function will do anything, and
  268. * that call will handle all loadable classes, even those generated
  269. * while it was running.
  270. *
  271. * The sequence below preserves +load ordering in the face of
  272. * image loading during a +load, and make sure that no
  273. * +load method is forgotten because it was added during
  274. * a +load call.
  275. * Sequence:
  276. * 1. Repeatedly call class +loads until there aren't any more
  277. * 2. Call category +loads ONCE.
  278. * 3. Run more +loads if:
  279. * (a) there are more classes to load, OR
  280. * (b) there are some potential category +loads that have
  281. * still never been attempted.
  282. * Category +loads are only run once to ensure "parent class first"
  283. * ordering, even if a category +load triggers a new loadable class
  284. * and a new loadable category attached to that class.
  285. *
  286. * Locking: loadMethodLock must be held by the caller
  287. * All other locks must not be held.
  288. **********************************************************************/
  289. void call_load_methods(void)
  290. {
  291. static bool loading = NO;
  292. bool more_categories;
  293. loadMethodLock.assertLocked();
  294. // Re-entrant calls do nothing; the outermost call will finish the job.
  295. if (loading) return;
  296. loading = YES;
  297. void *pool = objc_autoreleasePoolPush();
  298. do {
  299. // 1. Repeatedly call class +loads until there aren't any more
  300. while (loadable_classes_used > 0) {
  301. call_class_loads();
  302. }
  303. // 2. Call category +loads ONCE
  304. more_categories = call_category_loads();
  305. // 3. Run more +loads if there are classes OR more untried categories
  306. } while (loadable_classes_used > 0 || more_categories);
  307. objc_autoreleasePoolPop(pool);
  308. loading = NO;
  309. }