diff --git a/CMakeLists.txt b/CMakeLists.txt index 9041e03..245e9ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,7 +136,7 @@ set(INCLUDE_DIRECTORY "objc" CACHE STRING if (${CMAKE_C_COMPILER_ID} MATCHES Clang*) set(CMAKE_OBJC_FLAGS "${CMAKE_OBJC_FLAGS} -Wno-deprecated-objc-isa-usage -Wno-objc-root-class") if (${CMAKE_C_COMPILER_VERSION} VERSION_GREATER 3.1) - set(CMAKE_OBJC_FLAGS "${CMAKE_OBJC_FLAGS} -fobjc-runtime=gnustep-1.7") + set(CMAKE_OBJC_FLAGS "${CMAKE_OBJC_FLAGS} -fobjc-runtime=gnustep-2.0") endif () else (${CMAKE_C_COMPILER_ID} MATCHES Clang*) MESSAGE("WARNING: It is strongly recommended that you compile with clang") diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index 6425136..8277063 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -47,7 +47,7 @@ function(addtest_flags TEST_NAME FLAGS TEST_SOURCE) set(ARC "") set_target_properties(${TEST_NAME} PROPERTIES INCLUDE_DIRECTORIES "${CMAKE_SOURCE_DIR}" - COMPILE_FLAGS "-fobjc-runtime=gnustep-1.7 -fblocks ${FLAGS}" + COMPILE_FLAGS "-fblocks ${FLAGS}" LINKER_LANGUAGE C ) set_property(TEST ${TEST_NAME} PROPERTY @@ -58,8 +58,10 @@ endfunction(addtest_flags) foreach(TEST_SOURCE ${TESTS}) get_filename_component(TEST ${TEST_SOURCE} NAME_WE) - addtest_flags(${TEST} "-O0" ${TEST_SOURCE}) - addtest_flags("${TEST}_optimised" "-O3" ${TEST_SOURCE}) + addtest_flags(${TEST} "-O0 -fobjc-runtime=gnustep-2.0" ${TEST_SOURCE}) + addtest_flags("${TEST}_optimised" "-O3 -fobjc-runtime=gnustep-2.0" ${TEST_SOURCE}) + addtest_flags("${TEST}_legacy" "-O0 -fobjc-runtime=gnustep-1.7" ${TEST_SOURCE}) + addtest_flags("${TEST}_legacy_optimised" "-O3 -fobjc-runtime=gnustep-1.7" ${TEST_SOURCE}) endforeach() # Tests that are more than a single file. diff --git a/loader.c b/loader.c index 70ab229..d0a2f54 100644 --- a/loader.c +++ b/loader.c @@ -43,15 +43,9 @@ __attribute__((weak)) void (*dispatch_end_thread_4GC)(void); __attribute__((weak)) void *(*_dispatch_begin_NSAutoReleasePool)(void); __attribute__((weak)) void (*_dispatch_end_NSAutoReleasePool)(void *); -void __objc_exec_class(struct objc_module_abi_8 *module) +static void init_runtime(void) { 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)); - if (first_run) { #if ENABLE_GC @@ -94,6 +88,117 @@ void __objc_exec_class(struct objc_module_abi_8 *module) _dispatch_end_NSAutoReleasePool = objc_autoreleasePoolPop; } } +} + +struct objc_init +{ + uint64_t version; + SEL sel_begin; + SEL sel_end; + Class *cls_begin; + Class *cls_end; + char **cls_ref_begin; + char **cls_ref_end; + struct objc_category *cat_begin; + struct objc_category *cat_end; + struct objc_protocol2 *proto_begin; + struct objc_protocol2 *proto_end; +}; +#include + +void registerProtocol(Protocol *proto); + +void __objc_load(struct objc_init *init) +{ + init_runtime(); +#ifdef DEBUG_LOADING + Dl_info info; + if (dladdr(init, &info)) + { + fprintf(stderr, "Loading %p from object: %s (%p)\n", init, info.dli_fname, __builtin_return_address(0)); + } + else + { + fprintf(stderr, "Loading %p from unknown object\n", init); + } +#endif + LOCK_RUNTIME_FOR_SCOPE(); + assert(init->version == 0); + assert((((uintptr_t)init->sel_end-(uintptr_t)init->sel_begin) % sizeof(*init->sel_begin)) == 0); + assert((((uintptr_t)init->cls_end-(uintptr_t)init->cls_begin) % sizeof(*init->cls_begin)) == 0); + assert((((uintptr_t)init->cat_end-(uintptr_t)init->cat_begin) % sizeof(*init->cat_begin)) == 0); + for (SEL sel = init->sel_begin ; sel < init->sel_end ; sel++) + { + if (sel->name == 0) + { + continue; + } + objc_register_selector(sel); + } + int i = 0; + for (struct objc_protocol2 *proto = init->proto_begin ; proto < init->proto_end ; + proto++) + { + if (proto->name == NULL) + { + continue; + } + registerProtocol((struct objc_protocol*)proto); + } + for (Class *cls = init->cls_begin ; cls < init->cls_end ; cls++) + { + if (*cls == NULL) + { + continue; + } + objc_load_class(*cls); + } + for (char **cls = init->cls_ref_begin ; cls < init->cls_ref_end ; cls++) + { + id *out = (id*)cls; + if (*out == nil) + { + continue; + } + *out = objc_getClass(*cls); + assert(*out); + } + for (struct objc_category *cat = init->cat_begin ; cat < init->cat_end ; + cat++) + { + if (cat == NULL) + { + continue; + } + objc_try_load_category(cat); + } + // Load categories and statics that were deferred. + objc_load_buffered_categories(); + // Fix up the class links for loaded classes. + objc_resolve_class_links(); + for (struct objc_category *cat = init->cat_begin ; cat < init->cat_end ; + cat++) + { + Class class = (Class)objc_getClass(cat->class_name); + if ((Nil != class) && + objc_test_class_flag(class, objc_class_flag_resolved)) + { + objc_send_load_message(class); + } + } + init->version = 0xffffffffffffffffULL; +} + +void __objc_exec_class(struct objc_module_abi_8 *module) +{ + init_runtime(); + + // 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)); + fprintf(stderr, "Loading %s\n", module->name); + // 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. diff --git a/protocol.c b/protocol.c index 4a58e96..9c11e46 100644 --- a/protocol.c +++ b/protocol.c @@ -521,6 +521,11 @@ void objc_registerProtocol(Protocol *proto) proto->isa = ObjC2ProtocolClass; protocol_table_insert((struct objc_protocol2*)proto); } +PRIVATE void registerProtocol(Protocol *proto) +{ + LOCK_RUNTIME_FOR_SCOPE(); + protocol_table_insert((struct objc_protocol2*)proto); +} void protocol_addMethodDescription(Protocol *aProtocol, SEL name, const char *types,