mirror of
https://github.com/darlinghq/darling-objc4.git
synced 2024-11-26 21:50:26 +00:00
368 lines
12 KiB
XML
368 lines
12 KiB
XML
/*
|
|
* 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;
|
|
}
|
|
|
|
|