From bb8cafaa15c2b7c4bebbc48ea3bf020bd1109f93 Mon Sep 17 00:00:00 2001 From: theraven Date: Wed, 2 Jun 2010 22:25:12 +0000 Subject: [PATCH] Finished rewriting the loader. It's now cleanly separated into logically-separate components, so there's a chance it might actually be maintainable... --- GNUmakefile | 6 +- buffer.h | 62 ++++++++ category.h | 35 +++++ category_loader.c | 81 ++++++++++ class.h | 4 +- class_table.c | 27 +++- dtable.c | 13 +- init.c | 388 ---------------------------------------------- ivar.c | 4 +- loader.c | 92 +++++++++++ loader.h | 67 ++++++++ module.h | 20 ++- objc/objc-api.h | 34 ---- properties.h | 99 ++++++++++++ protocol.c | 126 ++++++++++++--- protocol.h | 115 ++++++++++++++ selector_table.c | 10 +- statics_loader.c | 71 +++++++++ thr.c | 1 - 19 files changed, 787 insertions(+), 468 deletions(-) create mode 100644 buffer.h create mode 100644 category.h create mode 100644 category_loader.c delete mode 100644 init.c create mode 100644 loader.c create mode 100644 loader.h create mode 100644 properties.h create mode 100644 protocol.h create mode 100644 statics_loader.c diff --git a/GNUmakefile b/GNUmakefile index bd0c49d..ee1864a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -20,20 +20,22 @@ libobjc_OBJC_FILES = \ libobjc_C_FILES = \ abi_version.c\ + category_loader.c\ class_table.c\ encoding.c\ hash_table.c\ exception.c\ hooks.c\ ivar.c\ - init.c\ + loader.c\ misc.c\ nil_method.c\ protocol.c\ runtime.c\ sarray2.c\ selector_table.c\ - sendmsg.c + sendmsg.c\ + statics_loader.c ifneq ($(enable_legacy), no) libobjc_C_FILES += \ diff --git a/buffer.h b/buffer.h new file mode 100644 index 0000000..4f49aa7 --- /dev/null +++ b/buffer.h @@ -0,0 +1,62 @@ +/** + * buffer.h defines a simple dynamic array that is used to store temporary + * values for later processing. Define BUFFER_TYPE before including this file. + */ + +#include + +#define BUFFER_SIZE 128 +static BUFFER_TYPE *buffered_object_buffer[BUFFER_SIZE]; +static BUFFER_TYPE **buffered_object_overflow; +static int buffered_objects; +static int buffered_object_overflow_space; + +static void set_buffered_object_at_index(BUFFER_TYPE *cat, unsigned int i) +{ + if (i < BUFFER_SIZE) + { + buffered_object_buffer[i] = cat; + } + else + { + i -= BUFFER_SIZE; + if (NULL == buffered_object_overflow) + { + buffered_object_overflow = + calloc(BUFFER_SIZE, sizeof(BUFFER_TYPE*)); + buffered_object_overflow_space = BUFFER_SIZE; + } + while (i > buffered_object_overflow_space) + { + buffered_object_overflow_space <<= 1; + buffered_object_overflow = realloc(buffered_object_overflow, + buffered_object_overflow_space * sizeof(BUFFER_TYPE*)); + } + buffered_object_overflow[i] = cat; + } +} + +static BUFFER_TYPE *buffered_object_at_index(unsigned int i) +{ + if (i +#include "objc/runtime.h" +#include "loader.h" + +#define BUFFER_TYPE struct objc_category +#include "buffer.h" + +static void register_methods(struct objc_class *cls, struct objc_method_list *l) +{ + if (NULL == l) { return; } + + // Replace the method names with selectors. + objc_register_selectors_from_list(l); + // Add the method list at the head of the list of lists. + l->next = cls->methods; + cls->methods = l; + // Update the dtable to catch the new methods. + // FIXME: We can make this more efficient by simply passing the new method + // list to the dtable and telling it only to update those methods. + objc_update_dtable_for_class(cls); +} + +static void load_category(struct objc_category *cat, struct objc_class *class) +{ + register_methods(class, cat->instance_methods); + register_methods(class->isa, cat->class_methods); + + if (cat->protocols) + { + objc_init_protocols(cat->protocols); + cat->protocols->next = class->protocols; + class->protocols = cat->protocols; + } +} + +static BOOL try_load_category(struct objc_category *cat) +{ + Class class = (Class)objc_getClass(cat->class_name); + if (Nil != class) + { + load_category(cat, class); + return YES; + } + return NO; +} + +/** + * Attaches a category to its class, if the class is already loaded. Buffers + * it for future resolution if not. + */ +void objc_try_load_category(struct objc_category *cat) +{ + if (!try_load_category(cat)) + { + set_buffered_object_at_index(cat, buffered_objects++); + } +} + +void objc_load_buffered_categories(void) +{ + BOOL shouldReshuffle = NO; + + for (unsigned i=0 ; iinfo & (long)flag; } +#endif //__OBJC_CLASS_H_INCLUDED diff --git a/class_table.c b/class_table.c index 47d9ba6..dad69b9 100644 --- a/class_table.c +++ b/class_table.c @@ -8,10 +8,10 @@ #include #include -void __objc_register_selectors_from_class(Class class); +void objc_register_selectors_from_class(Class class); void *__objc_uninstalled_dtable; -void __objc_init_protocols(struct objc_protocol_list *protos); -void __objc_compute_ivar_offsets(Class class); +void objc_init_protocols(struct objc_protocol_list *protos); +void objc_compute_ivar_offsets(Class class); //////////////////////////////////////////////////////////////////////////////// // +load method hash table @@ -216,7 +216,7 @@ BOOL objc_resolve_class(Class cls) objc_set_class_flag(cls, objc_class_flag_resolved); objc_set_class_flag(cls->isa, objc_class_flag_resolved); // Fix up the ivar offsets - __objc_compute_ivar_offsets(cls); + objc_compute_ivar_offsets(cls); // Send the +load message, if required objc_send_load_message(cls->isa); if (_objc_load_callback) @@ -226,7 +226,7 @@ BOOL objc_resolve_class(Class cls) return YES; } -void __objc_resolve_class_links(void) +void objc_resolve_class_links(void) { LOCK_UNTIL_RETURN(__objc_runtime_mutex); Class class = unresolved_class_list; @@ -247,6 +247,17 @@ void __objc_resolve_class_links(void) } } while (resolvedClass); } +void __objc_resolve_class_links(void) +{ + static BOOL warned = NO; + if (!warned) + { + fprintf(stderr, + "Warning: Calling deprecated private ObjC runtime function %s\n", __func__); + warned = YES; + } + objc_resolve_class_links(); +} // FIXME: Remove this once all uses of it in the runtime have been removed void __objc_add_class_to_hash(Class class) @@ -281,8 +292,8 @@ void objc_load_class(struct objc_class *class) class_table_insert (class); // Register all of the selectors used by this class and its metaclass - __objc_register_selectors_from_class(class); - __objc_register_selectors_from_class(class->isa); + objc_register_selectors_from_class(class); + objc_register_selectors_from_class(class->isa); // Set the uninstalled dtable. The compiler could do this as well. class->dtable = __objc_uninstalled_dtable; @@ -297,7 +308,7 @@ void objc_load_class(struct objc_class *class) if (class->protocols) { - __objc_init_protocols (class->protocols); + objc_init_protocols(class->protocols); } } diff --git a/dtable.c b/dtable.c index 32d80ef..47dd87e 100644 --- a/dtable.c +++ b/dtable.c @@ -195,7 +195,7 @@ static void mergeMethodsFromSuperclass(Class super, Class cls, SparseArray *meth Class class_getSuperclass(Class); -void __objc_update_dispatch_table_for_class(Class cls) +void objc_update_dtable_for_class(Class cls) { // Only update real dtables if (!classHasDtable(cls)) { return; } @@ -210,6 +210,17 @@ void __objc_update_dispatch_table_for_class(Class cls) mergeMethodsFromSuperclass(cls, cls, methods); SparseArrayDestroy(methods); } +void __objc_update_dispatch_table_for_class(Class cls) +{ + static BOOL warned = NO; + if (!warned) + { + fprintf(stderr, + "Warning: Calling deprecated private ObjC runtime function %s\n", __func__); + warned = YES; + } + objc_update_dtable_for_class(cls); +} static SparseArray *create_dtable_for_class(Class class) { diff --git a/init.c b/init.c deleted file mode 100644 index 42d0bb4..0000000 --- a/init.c +++ /dev/null @@ -1,388 +0,0 @@ -/* GNU Objective C Runtime initialization - Copyright (C) 1993, 1995, 1996, 1997, 2002, 2009 - Free Software Foundation, Inc. - Contributed by Kresten Krab Thorup - +load support contributed by Ovidiu Predescu - -This file is part of GCC. - -GCC is free software; you can redistribute it and/or modify it under the -terms of the GNU General Public License as published by the Free Software -Foundation; either version 3, or (at your option) any later version. - -GCC is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU General Public License for more -details. - -Under Section 7 of GPL version 3, you are granted additional -permissions described in the GCC Runtime Library Exception, version -3.1, as published by the Free Software Foundation. - -You should have received a copy of the GNU General Public License and -a copy of the GCC Runtime Library Exception along with this program; -see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -. */ - -#include -#include "objc/runtime-legacy.h" -#include "objc/encoding.h" -#include "lock.h" -#include "magic_objects.h" - -/* The version number of this runtime. This must match the number - defined in gcc (objc-act.c). */ -#define PROTOCOL_VERSION 2 -#define OBJC2_PROTOCOL_VERSION 3 - -void __objc_sync_init(void); -void __objc_resolve_class_links(void); -void objc_load_class(struct objc_class *class); - -/* This list contains all proto_list's not yet assigned class links. */ -static struct objc_list *unclaimed_proto_list = 0; /* !T:MUTEX */ - -/* List of unresolved static instances. */ -static struct objc_list *uninitialized_statics = 0; /* !T:MUTEX */ - -/* Global runtime "write" mutex. */ -static mutex_t objc_runtime_mutex; -objc_mutex_t __objc_runtime_mutex = &objc_runtime_mutex; - -/* Number of threads that are alive. */ -int __objc_runtime_threads_alive = 1; /* !T:MUTEX */ - -void __objc_register_selector_array(SEL selectors, unsigned long count); - -/* Check compiler vs runtime version. */ -static void init_check_module_version (Module_t); - -/* Assign isa links to protos. */ -void __objc_init_protocols (struct objc_protocol_list *protos); - -/* Add protocol to class. */ -static void __objc_class_add_protocols (Class, struct objc_protocol_list *); - -/* Is all categories/classes resolved? */ -BOOL __objc_dangling_categories = NO; /* !T:UNUSED */ - -/* Extern function used to reference the Object and NXConstantString - classes. */ - -extern void __objc_force_linking (void); - -void -__objc_force_linking (void) -{ - extern void __objc_linking (void); - __objc_linking (); -} - -/* Run through the statics list, removing modules as soon as all its - statics have been initialized. */ - -static void -objc_init_statics (void) -{ - struct objc_list **cell = &uninitialized_statics; - struct objc_static_instances **statics_in_module; - - LOCK(__objc_runtime_mutex); - - while (*cell) - { - int module_initialized = 1; - - for (statics_in_module = (*cell)->head; - *statics_in_module; statics_in_module++) - { - struct objc_static_instances *statics = *statics_in_module; - Class class = objc_lookup_class (statics->class_name); - if (strcmp(statics->class_name, "NXConstantString") == 0) - { - Class constStr = objc_lookup_class(CONSTANT_STRING_CLASS); - if (constStr) - { - class = constStr; - } - } - - if (! class) - module_initialized = 0; - /* Actually, the static's class_pointer will be NULL when we - haven't been here before. However, the comparison is to be - reminded of taking into account class posing and to think about - possible semantics... */ - else if (class != statics->instances[0]->class_pointer) - { - id *inst; - - for (inst = &statics->instances[0]; *inst; inst++) - { - (*inst)->class_pointer = class; - - /* ??? Make sure the object will not be freed. With - refcounting, invoke `-retain'. Without refcounting, do - nothing and hope that `-free' will never be invoked. */ - - /* ??? Send the object an `-initStatic' or something to - that effect now or later on? What are the semantics of - statically allocated instances, besides the trivial - NXConstantString, anyway? */ - } - } - } - if (module_initialized) - { - /* Remove this module from the uninitialized list. */ - struct objc_list *this = *cell; - *cell = this->tail; - objc_free (this); - } - else - cell = &(*cell)->tail; - } - - UNLOCK(__objc_runtime_mutex); -} /* objc_init_statics */ - -void __objc_init_protocol_table(void); -/* This function is called by constructor functions generated for each - module compiled. (_GLOBAL_$I$...) The purpose of this function is - to gather the module pointers so that they may be processed by the - initialization routines as soon as possible. */ - -BOOL objc_check_abi_version(unsigned long version, unsigned long module_size); - -void -__objc_exec_class (Module_t module) -{ - /* Have we processed any constructors previously? This flag is used to - indicate that some global data structures need to be built. */ - static BOOL previous_constructors = 0; - - static struct objc_list *unclaimed_categories = 0; - - /* The symbol table (defined in objc-api.h) generated by gcc */ - Symtab_t symtab = module->symtab; - - /* The statics in this module */ - struct objc_static_instances **statics - = symtab->defs[symtab->cls_def_cnt + symtab->cat_def_cnt]; - - /* Entry used to traverse hash lists */ - struct objc_list **cell; - - /* dummy counter */ - int i; - - DEBUG_PRINTF ("received module: %s\n", module->name); - - /* check compiler version */ - assert(objc_check_abi_version(module->version, module->size)); - - /* On the first call of this routine, initialize some data structures. */ - // FIXME: This should really be using a pthread_once or equivalent. - if (! previous_constructors) - { - /* Initialize thread-safe system */ - INIT_LOCK(objc_runtime_mutex); - __objc_init_thread_system (); - __objc_sync_init(); - __objc_runtime_threads_alive = 1; - - __objc_init_selector_tables (); - __objc_init_protocol_table (); - __objc_init_class_tables (); - __objc_init_dispatch_tables (); - previous_constructors = 1; - } - - /* Save the module pointer for later processing. (not currently used) */ - LOCK(__objc_runtime_mutex); - - /* Replace referenced selectors from names to SEL's. */ - if (symtab->refs) - { - __objc_register_selector_array(symtab->refs, symtab->sel_ref_cnt); - } - - /* Parse the classes in the load module and gather selector information. */ - DEBUG_PRINTF ("gathering selectors from module: %s\n", module->name); - for (i = 0; i < symtab->cls_def_cnt; ++i) - { - objc_load_class((Class) symtab->defs[i]); - } - - /* Process category information from the module. */ - for (i = 0; i < symtab->cat_def_cnt; ++i) - { - Category_t category = symtab->defs[i + symtab->cls_def_cnt]; - Class class = objc_lookup_class (category->class_name); - - /* If the class for the category exists then append its methods. */ - if (class) - { - - DEBUG_PRINTF ("processing categories from (module,object): %s, %s\n", - module->name, - class->name); - - /* Do instance methods. */ - if (category->instance_methods) - class_add_method_list (class, category->instance_methods); - - /* Do class methods. */ - if (category->class_methods) - class_add_method_list ((Class) class->class_pointer, - category->class_methods); - - if (category->protocols) - { - __objc_init_protocols (category->protocols); - __objc_class_add_protocols (class, category->protocols); - } - } - else - { - /* The object to which the category methods belong can't be found. - Save the information. */ - unclaimed_categories = list_cons (category, unclaimed_categories); - } - } - - if (statics) - uninitialized_statics = list_cons (statics, uninitialized_statics); - if (uninitialized_statics) - objc_init_statics (); - - /* Scan the unclaimed category hash. Attempt to attach any unclaimed - categories to objects. */ - for (cell = &unclaimed_categories; *cell; ) - { - Category_t category = (*cell)->head; - Class class = objc_lookup_class (category->class_name); - - if (class) - { - DEBUG_PRINTF ("attaching stored categories to object: %s\n", - class->name); - - list_remove_head (cell); - - if (category->instance_methods) - class_add_method_list (class, category->instance_methods); - - if (category->class_methods) - class_add_method_list ((Class) class->class_pointer, - category->class_methods); - - if (category->protocols) - { - __objc_init_protocols (category->protocols); - __objc_class_add_protocols (class, category->protocols); - } - } - else - cell = &(*cell)->tail; - } - - if (unclaimed_proto_list && objc_lookup_class ("Protocol")) - { - list_mapcar (unclaimed_proto_list, - (void (*) (void *))__objc_init_protocols); - list_free (unclaimed_proto_list); - unclaimed_proto_list = 0; - } - - __objc_resolve_class_links(); - - UNLOCK(__objc_runtime_mutex); -} - -void __objc_compute_ivar_offsets(Class class); - - -struct objc_protocol *__objc_unique_protocol(struct objc_protocol*); -void __objc_init_protocols (struct objc_protocol_list *protos) -{ - size_t i; - static Class proto_class = 0; - static Class proto_class2 = 0; - - if (! protos) - return; - - LOCK(__objc_runtime_mutex); - - if (! proto_class) - proto_class = objc_lookup_class ("Protocol"); - if (! proto_class2) - proto_class2 = objc_lookup_class ("Protocol2"); - - /* Protocol2 will always exist if Protocol exists */ - if (! proto_class2) - { - unclaimed_proto_list = list_cons (protos, unclaimed_proto_list); - UNLOCK(__objc_runtime_mutex); - return; - } - -#if 0 - assert (protos->next == 0); /* only single ones allowed */ -#endif - - for (i = 0; i < protos->count; i++) - { - struct objc_protocol *aProto = protos->list[i]; - switch (((size_t)aProto->class_pointer)) - { - case PROTOCOL_VERSION: - { - /* assign class pointer */ - aProto->class_pointer = proto_class; - - /* init super protocols */ - __objc_init_protocols (aProto->protocol_list); - protos->list[i] = __objc_unique_protocol (aProto); - break; - } - // FIXME: Initialize empty protocol by updating fields to reflect - // those of a real protocol with the same name - case OBJC2_PROTOCOL_VERSION: - { - /* assign class pointer */ - aProto->class_pointer = proto_class2; - /* init super protocols */ - __objc_init_protocols (aProto->protocol_list); - protos->list[i] = __objc_unique_protocol (aProto); - } - default: - { - if (protos->list[i]->class_pointer != proto_class - && protos->list[i]->class_pointer != proto_class2) - { - objc_error (nil, OBJC_ERR_PROTOCOL_VERSION, - "Version %d doesn't match runtime protocol version %d\n", - (int) ((char *) protos->list[i]->class_pointer - - (char *) 0), - PROTOCOL_VERSION); - } - } - } - } - - UNLOCK(__objc_runtime_mutex); -} - -static void -__objc_class_add_protocols (Class class, struct objc_protocol_list *protos) -{ - /* Well... */ - if (! protos) - return; - - /* Add it... */ - protos->next = class->protocols; - class->protocols = protos; -} diff --git a/ivar.c b/ivar.c index f8ea602..b834d85 100644 --- a/ivar.c +++ b/ivar.c @@ -7,7 +7,7 @@ ptrdiff_t objc_alignof_type(const char *); ptrdiff_t objc_sizeof_type(const char *); -void __objc_compute_ivar_offsets(Class class) +void objc_compute_ivar_offsets(Class class) { int i = 0; /* If this class was compiled with support for late-bound ivars, the @@ -23,7 +23,7 @@ void __objc_compute_ivar_offsets(Class class) { if (super->instance_size <= 0) { - __objc_compute_ivar_offsets(super); + objc_compute_ivar_offsets(super); } ivar_start = super->instance_size; } diff --git a/loader.c b/loader.c new file mode 100644 index 0000000..e4274c6 --- /dev/null +++ b/loader.c @@ -0,0 +1,92 @@ +#include +#include +#include "objc/runtime.h" +#include "lock.h" +#include "loader.h" +#include "magic_objects.h" + +/** + * Runtime lock. This is exposed in + */ +static mutex_t objc_runtime_mutex; +void *__objc_runtime_mutex = &objc_runtime_mutex; + +void __objc_sync_init(void); +void __objc_init_selector_tables(void); +void __objc_init_protocol_table(void); +void __objc_init_class_tables(void); +void __objc_init_dispatch_tables(void); + +/* Number of threads that are alive. */ +int __objc_runtime_threads_alive = 1; /* !T:MUTEX */ + +void __objc_exec_class(struct objc_module_abi_8 *module) +{ + static BOOL first_run = YES; + + // Check that this module uses an ABI version that we recognise. + // In future, we should pass the ABI version to the class / category load + // functions so that we can change various structures more easily. + assert(objc_check_abi_version(module->version, module->size)); + + if (first_run) + { + // Create the main runtime lock. This is not safe in theory, but in + // practice the first time that this function is called will be in the + // loader, from the main thread. Future loaders may run concurrently, + // but that is likely to break the semantics of a lot of languages, so + // we don't have to worry about it for a long time. + // + // The only case when this can potentially go badly wrong is when a + // pure-C main() function spawns two threads which then, concurrently, + // call dlopen() or equivalent, and the platform's implementation of + // this does not perform any synchronization. + INIT_LOCK(objc_runtime_mutex); + // Create the lock used to protect the creation of hidden classes by + // @synchronize() + __objc_sync_init(); + + // Create the various tables that the runtime needs. + __objc_init_selector_tables(); + __objc_init_protocol_table(); + __objc_init_class_tables(); + __objc_init_dispatch_tables(); + first_run = NO; + } + + // The runtime mutex is held for the entire duration of a load. It does + // not need to be acquired or released in any of the called load functions. + LOCK_UNTIL_RETURN(__objc_runtime_mutex); + + struct objc_symbol_table_abi_8 *symbols = module->symbol_table; + // Register all of the selectors used in this module. + if (symbols->selectors) + { + objc_register_selector_array(symbols->selectors, + symbols->selector_count); + } + + unsigned short defs = 0; + // Load the classes from this module + for (unsigned short i=0 ; iclass_count ; i++) + { + objc_load_class(symbols->definitions[defs++]); + } + // Load the categories from this module + for (unsigned short i=0 ; icategory_count; i++) + { + objc_try_load_category(symbols->definitions[defs++]); + } + // Load the static instances + struct objc_static_instance_list **statics = (void*)symbols->definitions[defs]; + while (NULL != statics && NULL != *statics) + { + objc_init_statics(*(statics++)); + } + + // Fix up the class links for loaded classes. + objc_resolve_class_links(); + // Load categories and statics that were deferred. + objc_load_buffered_categories(); + objc_init_buffered_statics(); +} diff --git a/loader.h b/loader.h new file mode 100644 index 0000000..7cf63fb --- /dev/null +++ b/loader.h @@ -0,0 +1,67 @@ +#ifndef __OBJC_LOADER_H_INCLUDED +#define __OBJC_LOADER_H_INCLUDED +#include "category.h" +#include "method_list.h" +#include "module.h" +#include "class.h" +#include "protocol.h" + +/** + * Checks whether it is safe to load a module with the specified version and + * module size. This depends on whether another module with an incompatible + * ABI has already been loaded. + */ +BOOL objc_check_abi_version(unsigned long version, unsigned long module_size); +/** + * Initializes a protocol list, uniquing the protocols in the list. + */ +void objc_init_protocols(struct objc_protocol_list *protocols); +/** + * Registers a set of selectors from a method list. + */ +void objc_register_selectors_from_list(struct objc_method_list *l); +/** + * Register all of the (unregistered) selectors that are used in a class. + */ +void objc_register_selectors_from_class(Class class); +/** + * Registers all of the selectors in an array. + */ +void objc_register_selector_array(SEL selectors, unsigned long count); +/** + * Loads a class into the runtime system. If possible, the class is resolved + * (inserted into the class tree) immediately. If its superclass is not yet + * resolved, it is enqueued for later resolution. + */ +void objc_load_class(struct objc_class *class); +/** + * Resolves classes that have not yet been resolved, if their superclasses have + * subsequently been loaded. + */ +void objc_resolve_class_links(void); +/** + * Attaches a category to its class, if the class is already loaded. Buffers + * it for future resolution if not. + */ +void objc_try_load_category(struct objc_category *cat); +/** + * Tries to load all of the categories that could not previously be loaded + * because their classes were not yet loaded. + */ +void objc_load_buffered_categories(void); +/** + * Updates the dispatch table for a class. + */ +void objc_update_dtable_for_class(Class cls); +/** + * Initialises a list of static object instances belonging to the same class if + * possible, or defers initialisation until the class has been loaded it not. + */ +void objc_init_statics(struct objc_static_instance_list *statics); +/** + * Tries again to initialise static instances which could not be initialised + * earlier. + */ +void objc_init_buffered_statics(void); + +#endif //__OBJC_LOADER_H_INCLUDED diff --git a/module.h b/module.h index 675fbef..777a46a 100644 --- a/module.h +++ b/module.h @@ -14,12 +14,12 @@ struct objc_symbol_table_abi_8 /** * The number of selectors referenced in this module. */ - unsigned long selector_refs_count; + unsigned long selector_count; /** * An array of selectors used in this compilation unit. SEL is a pointer * type and this points to the first element in an array of selectors. */ - SEL refs; + SEL selectors; /** * The number of classes defined in this module. */ @@ -71,3 +71,19 @@ struct objc_module_abi_8 */ struct objc_symbol_table_abi_8 *symbol_table; }; + +/** + * List of static instances of a named class provided in this module. + */ +struct objc_static_instance_list +{ + /** + * The name of the class. The isa pointer of all of the instances will be + * set to the class with this name. + */ + char *class_name; + /** + * NULL-terminated array of statically-allocated instances. + */ + id instances[1]; +}; diff --git a/objc/objc-api.h b/objc/objc-api.h index 5da20c3..3c9df43 100644 --- a/objc/objc-api.h +++ b/objc/objc-api.h @@ -229,40 +229,6 @@ typedef struct objc_ivar_list { structure. */ } IvarList, *IvarList_t; -enum PropertyAttributeKind -{ - OBJC_PR_noattr = 0x00, - OBJC_PR_readonly = 0x01, - OBJC_PR_getter = 0x02, - OBJC_PR_assign = 0x04, - OBJC_PR_readwrite = 0x08, - OBJC_PR_retain = 0x10, - OBJC_PR_copy = 0x20, - OBJC_PR_nonatomic = 0x40, - OBJC_PR_setter = 0x80 -}; - -/** - * Structure used for property enumeration. Note that property enumeration is currently quite broken on OS X, so achieving full compatibility there is impossible. Instead, we strive to achieve com - */ -struct objc_property { - const char *name; - const char attributes; - const char isSynthesized; - const char *setter_name; - const char *setter_types; - const char *getter_name; - const char *getter_types; -}; - -struct objc_property_list { - int property_count; - struct objc_property_list *next; /* UNUSED. In future, categories will be - allowed to add properties and then this will - be used to link the declarations together. */ - struct objc_property properties[1]; -}; - /* ** The compiler generates one (or more) of these structures for a class that ** has methods defined in its specification. diff --git a/properties.h b/properties.h new file mode 100644 index 0000000..e904f6b --- /dev/null +++ b/properties.h @@ -0,0 +1,99 @@ + +enum PropertyAttributeKind +{ + /** + * Property has no attributes. + */ + OBJC_PR_noattr = 0x00, + /** + * The property is declared read-only. + */ + OBJC_PR_readonly = (1<<0), + /** + * The property has a getter. + */ + OBJC_PR_getter = (1<<1), + /** + * The property has assign semantics. + */ + OBJC_PR_assign = (1<<2), + /** + * The property is declared read-write. + */ + OBJC_PR_readwrite = (1<<3), + /** + * Property has retain semantics. + */ + OBJC_PR_retain = (1<<4), + /** + * Property has copy semantics. + */ + OBJC_PR_copy = (1<<5), + /** + * Property is marked as non-atomic. + */ + OBJC_PR_nonatomic = (1<<6), + /** + * Property has setter. + */ + OBJC_PR_setter = (1<<7) +}; + +/** + * Structure used for property enumeration. Note that property enumeration is + * currently quite broken on OS X, so achieving full compatibility there is + * impossible. Instead, we strive to achieve com + */ +struct objc_property +{ + /** + * Name of this property. + */ + const char *name; + /** + * Attributes for this property. Made by ORing together + * PropertyAttributeKinds. + */ + const char attributes; + /** + * Flag set if the property is synthesized. + */ + const char isSynthesized; + /** + * Name of the set method for this property. + */ + const char *setter_name; + /** + * Type encoding of the setter for this property. + */ + const char *setter_types; + /** + * Name of the getter for this property. + */ + const char *getter_name; + /** + * Type encoding for the get method for this property. + */ + const char *getter_types; +}; + +/** + * List of property inrospection data. + */ +struct objc_property_list +{ + /** + * Number of properties in this array. + */ + int count; + /* + * UNUSED. In future, categories will be allowed to add properties and + * then this will be used to link the declarations together. + */ + struct objc_property_list *next; + /** + * List of properties. + */ + struct objc_property properties[1]; +}; + diff --git a/protocol.c b/protocol.c index 671dcfa..969f2be 100644 --- a/protocol.c +++ b/protocol.c @@ -1,19 +1,23 @@ -#include "objc/objc.h" -#include "objc/objc-api.h" +#include "objc/runtime.h" +#include "protocol.h" +#include "properties.h" #include "lock.h" #include +#define BUFFER_TYPE struct objc_protocol_list +#include "buffer.h" + // Get the functions for string hashing #include "string_hash.h" static int protocol_compare(const char *name, const struct objc_protocol2 *protocol) { - return string_compare(name, protocol->protocol_name); + return string_compare(name, protocol->name); } static int protocol_hash(const struct objc_protocol2 *protocol) { - return string_hash(protocol->protocol_name); + return string_hash(protocol->name); } #define MAP_TABLE_NAME protocol #define MAP_TABLE_COMPARE_FUNCTION protocol_compare @@ -33,18 +37,12 @@ static void protocol_table_insert(const struct objc_protocol2 *protocol) protocol_insert(known_protocol_table, (void*)protocol); } -struct objc_protocol2 *protocol_for_name(const char *protocol_name) +struct objc_protocol2 *protocol_for_name(const char *name) { - return protocol_table_get(known_protocol_table, protocol_name); + return protocol_table_get(known_protocol_table, name); } -struct objc_method_description_list -{ - int count; - struct objc_method_description list[1]; -}; - -static Class ObjC2ProtocolClass = 0; +static id ObjC2ProtocolClass = 0; static int isEmptyProtocol(struct objc_protocol2 *aProto) { @@ -55,13 +53,13 @@ static int isEmptyProtocol(struct objc_protocol2 *aProto) (aProto->class_methods->count == 0)) && ((aProto->protocol_list == NULL) || (aProto->protocol_list->count == 0)); - if (aProto->class_pointer == ObjC2ProtocolClass) + if (aProto->isa == ObjC2ProtocolClass) { struct objc_protocol2 *p2 = (struct objc_protocol2*)aProto; isEmpty &= (p2->optional_instance_methods->count == 0); isEmpty &= (p2->optional_class_methods->count == 0); - isEmpty &= (p2->properties->property_count == 0); - isEmpty &= (p2->optional_properties->property_count == 0); + isEmpty &= (p2->properties->count == 0); + isEmpty &= (p2->optional_properties->count == 0); } return isEmpty; } @@ -74,8 +72,8 @@ static void makeProtocolEqualToProtocol(struct objc_protocol2 *p1, COPY(instance_methods); COPY(class_methods); COPY(protocol_list); - if (p1->class_pointer == ObjC2ProtocolClass && - p2->class_pointer == ObjC2ProtocolClass) + if (p1->isa == ObjC2ProtocolClass && + p2->isa == ObjC2ProtocolClass) { COPY(optional_instance_methods); COPY(optional_class_methods); @@ -85,14 +83,14 @@ static void makeProtocolEqualToProtocol(struct objc_protocol2 *p1, #undef COPY } -struct objc_protocol2 *__objc_unique_protocol(struct objc_protocol2 *aProto) +static struct objc_protocol2 *unique_protocol(struct objc_protocol2 *aProto) { if (ObjC2ProtocolClass == 0) { - ObjC2ProtocolClass = objc_get_class("Protocol2"); + ObjC2ProtocolClass = objc_getClass("Protocol2"); } struct objc_protocol2 *oldProtocol = - protocol_for_name(aProto->protocol_name); + protocol_for_name(aProto->name); if (NULL == oldProtocol) { // This is the first time we've seen this protocol, so add it to the @@ -130,6 +128,86 @@ struct objc_protocol2 *__objc_unique_protocol(struct objc_protocol2 *aProto) } } +static id protocol_class; +static id protocol_class2; +enum protocol_version +{ + /** + * Legacy (GCC-compatible) protocol version. + */ + protocol_version_legacy = 2, + /** + * New (Objective-C 2-compatible) protocol version. + */ + protocol_version_objc2 = 3 +}; + +static BOOL init_protocols(struct objc_protocol_list *protocols) +{ + // Protocol2 is a subclass of Protocol, so if we have loaded Protocol2 we + // must have also loaded Protocol. + if (nil == protocol_class2) + { + protocol_class = objc_getClass("Protocol"); + protocol_class2 = objc_getClass("Protocol2"); + } + if (nil == protocol_class2) + { + return NO; + } + + for (unsigned i=0 ; icount ; i++) + { + struct objc_protocol2 *aProto = protocols->list[i]; + // Don't initialise a protocol twice + if (aProto->isa == protocol_class || + aProto->isa == protocol_class2) { continue ;} + + // Protocols in the protocol list have their class pointers set to the + // version of the protocol class that they expect. + enum protocol_version version = + (enum protocol_version)(uintptr_t)aProto->isa; + switch (version) + { + default: + fprintf(stderr, "Unknown protocol version"); + abort(); + case protocol_version_legacy: + aProto->isa = protocol_class; + break; + case protocol_version_objc2: + aProto->isa = protocol_class2; + break; + } + // Initialize all of the protocols that this protocol refers to + init_protocols(aProto->protocol_list); + // Replace this protocol with a unique version of it. + protocols->list[i] = unique_protocol(aProto); + } + return YES; +} + +void objc_init_protocols(struct objc_protocol_list *protocols) +{ + if (!init_protocols(protocols)) + { + set_buffered_object_at_index(protocols, buffered_objects++); + return; + } + if (buffered_objects > 0) { return; } + + // If we can load one protocol, then we can load all of them. + for (unsigned i=0 ; iprotocol_name; + return p->name; } return NULL; } @@ -172,8 +250,8 @@ BOOL protocol_isEqual(Protocol *p, Protocol *other) return NO; } if (p == other || - p->protocol_name == other->protocol_name || - 0 == strcmp(p->protocol_name, other->protocol_name)) + p->name == other->name || + 0 == strcmp(p->name, other->name)) { return YES; } diff --git a/protocol.h b/protocol.h new file mode 100644 index 0000000..d5ffd46 --- /dev/null +++ b/protocol.h @@ -0,0 +1,115 @@ +#include "selector.h" + +struct objc_method_description_list +{ + /** + * Number of method descriptions in this list. + */ + int count; + /** + * Methods in this list. Note: these selectors are NOT resolved. The name + * field points to the name, not to the index of the uniqued version of the + * name. You must not use them for dispatch. + */ + struct objc_selector methods[1]; +}; + +/** + * Definition of the Protocol type. Protocols are objects, but are rarely used + * as such. + */ +#ifdef __OBJC__ +@interface Protocol +#else +struct objc_protocol +#endif +{ + /** Class pointer. */ + id isa; + /** + * The name of this protocol. Two protocols are regarded as identical if + * they have the same name. + */ + char *name; + /** + * The list of protocols that this protocol conforms to. + */ + struct objc_protocol_list *protocol_list; + /** + * List of instance methods required by this protocol. + */ + struct objc_method_description_list *instance_methods; + /** + * List of class methods required by this protocol. + */ + struct objc_method_description_list *class_methods; +} +#ifdef __OBJC__ +@end +#else +; +#endif + +#ifdef __OBJC__ +@interface Protocol2 +{ +#else +typedef struct objc_protocol2 +{ + /** + * Redefinition of the superclass ivars in the C version. + */ + id isa; + char *name; + struct objc_protocol_list *protocol_list; + struct objc_method_description_list *instance_methods; + struct objc_method_description_list *class_methods; +#endif + /** + * Instance methods that are declared as optional for this protocol. + */ + struct objc_method_description_list *optional_instance_methods; + /** + * Class methods that are declared as optional for this protocol. + */ + struct objc_method_description_list *optional_class_methods; + /** + * Properties that are required by this protocol. + */ + struct objc_property_list *properties; + /** + * Optional properties. + */ + struct objc_property_list *optional_properties; +} +#ifdef __OBJC__ +@end +#else +Protocol2; +#endif + +/** + * List of protocols. Attached to a class or a category by the compiler and to + * a class by the runtime. + */ +struct objc_protocol_list +{ + /** + * Additional protocol lists. Loading a category that declares protocols + * will cause a new list to be prepended using this pointer to the protocol + * list for the class. Unlike methods, protocols can not be overridden, + * although it is possible for a protocol to appear twice. + */ + struct objc_protocol_list *next; + /** + * The number of protocols in this list. + */ + size_t count; + /** + * An array of protocols. Actually contains count elements, not 1. + * + * The instances in this array may be any version of protocols. + */ + Protocol2 *list[1]; +}; + diff --git a/selector_table.c b/selector_table.c index 97b0c58..5b7d024 100644 --- a/selector_table.c +++ b/selector_table.c @@ -124,7 +124,7 @@ void __objc_init_selector_tables() { selector_list = SparseArrayNew(); INIT_LOCK(selector_table_lock); - sel_table = selector_create(40960); + sel_table = selector_create(4096); } static SEL selector_lookup(const char *name, const char *types) @@ -306,7 +306,7 @@ unsigned sel_copyTypes(const char *selName, const char **types, unsigned count) return found; } -void __objc_register_selectors_from_list(struct objc_method_list *l) +void objc_register_selectors_from_list(struct objc_method_list *l) { for (int i=0 ; icount ; i++) { @@ -318,14 +318,14 @@ void __objc_register_selectors_from_list(struct objc_method_list *l) /** * Register all of the (unregistered) selectors that are used in a class. */ -void __objc_register_selectors_from_class(Class class) +void objc_register_selectors_from_class(Class class) { for (struct objc_method_list *l=class->methods ; NULL!=l ; l=l->next) { - __objc_register_selectors_from_list(l); + objc_register_selectors_from_list(l); } } -void __objc_register_selector_array(SEL selectors, unsigned long count) +void objc_register_selector_array(SEL selectors, unsigned long count) { // GCC is broken and always sets the count to 0, so we ignore count until // we can throw stupid and buggy compilers in the bin. diff --git a/statics_loader.c b/statics_loader.c new file mode 100644 index 0000000..068f7ad --- /dev/null +++ b/statics_loader.c @@ -0,0 +1,71 @@ +#include +#include +#include "objc/runtime.h" +#include "module.h" +#include "magic_objects.h" + +#define BUFFER_TYPE struct objc_static_instance_list +#include "buffer.h" + +static BOOL try_init_statics(struct objc_static_instance_list *statics) +{ + const char *class_name = statics->class_name; + + // This is a horrible hack. + // + // Very bad things happen when you have more than one constant string class + // used in a program. Unfortunately, GCC defaults to using + // NXConstantString, and if you forget to specify + // -fconstant-string-class=NSConstantString for some compilation units then + // you will end up with some NSConstantString instances and some + // NXConstantString instances. This is a mess. We hack around this by + // silently assuming that the user meant NSConstantString when they said + // NXConstantString if NSConstantString is set as the constant string class + // in magic_objects.h + if (strcmp(class_name, "NXConstantString") == 0) + { + class_name = CONSTANT_STRING_CLASS; + } + + Class class = (Class)objc_getClass(class_name); + + if (Nil == class) + { + return NO; + } + for (id *instance=statics->instances ; nil!=*instance ; instance++) + { + (*instance)->isa = class; + } + return YES; +} +void objc_init_statics(struct objc_static_instance_list *statics) +{ + if (!try_init_statics(statics)) + { + set_buffered_object_at_index(statics, buffered_objects++); + } +} + +void objc_init_buffered_statics(void) +{ + BOOL shouldReshuffle = NO; + + for (unsigned i=0 ; i