diff --git a/class.h b/class.h index 090d7132..454a08c6 100644 --- a/class.h +++ b/class.h @@ -348,6 +348,13 @@ enum objc_class_flags * the underlying blocks runtime. */ objc_class_flag_is_block = (1 << 16), + /** + * Neither this class nor any of its superclasses has a `.cxx_construct` + * method, so constructing an instance of it has nothing to do. Set when + * the class's dispatch table is created, cleared if a `.cxx_construct` is + * installed later. + */ + objc_class_flag_no_cxx_construct = (1<<17), }; /** diff --git a/dtable.c b/dtable.c index 54c0d729..5108c8e3 100644 --- a/dtable.c +++ b/dtable.c @@ -373,17 +373,12 @@ static BOOL installMethodInDtable(Class class, SparseArrayInsert(dtable, untyped_idx, method); #endif - static SEL cxx_construct, cxx_destruct; - if (NULL == cxx_construct) - { - cxx_construct = sel_registerName(".cxx_construct"); - cxx_destruct = sel_registerName(".cxx_destruct"); - } - if (selEqualUnTyped(method->selector, cxx_construct)) + if (selEqualUnTyped(method->selector, cxx_construct_sel)) { class->cxx_construct = method->imp; + objc_clear_class_flag(class, objc_class_flag_no_cxx_construct); } - else if (selEqualUnTyped(method->selector, cxx_destruct)) + else if (selEqualUnTyped(method->selector, cxx_destruct_sel)) { class->cxx_destruct = method->imp; } @@ -604,6 +599,18 @@ PRIVATE dtable_t create_dtable_for_class(Class class, dtable_t root_dtable) list = list->next; } + // The superclass chain is fully initialised by this point, so a class + // whose superclass has nothing to construct and which has installed no + // `.cxx_construct` of its own has nothing to construct either. + if ((NULL == super) || + objc_test_class_flag(super, objc_class_flag_no_cxx_construct)) + { + if (NULL == class->cxx_construct) + { + objc_set_class_flag(class, objc_class_flag_no_cxx_construct); + } + } + return dtable; } diff --git a/loader.c b/loader.c index f4df0471..d44b381b 100644 --- a/loader.c +++ b/loader.c @@ -62,6 +62,10 @@ static void init_runtime(void) #if defined(EMBEDDED_BLOCKS_RUNTIME) init_trampolines(); #endif + // Registering a selector resizes the dispatch tables, which walks the + // class table, so this follows the tables it needs and precedes the + // first class whose methods are installed. + init_cxx_selectors(); init_builtin_classes(); first_run = NO; if (getenv("LIBOBJC_MEMORY_PROFILE")) diff --git a/loader.h b/loader.h index 541c31ee..c0446d8b 100644 --- a/loader.h +++ b/loader.h @@ -100,6 +100,11 @@ void init_protocol_table(void); */ void init_selector_tables(void); +/** + * Register the `.cxx_construct` and `.cxx_destruct` selectors. + */ +void init_cxx_selectors(void); + /** * Initialise the trampolines for using blocks as methods. */ diff --git a/runtime.c b/runtime.c index 45b290e9..14137b10 100644 --- a/runtime.c +++ b/runtime.c @@ -25,16 +25,20 @@ static inline void safe_remove_from_subclass_list(Class cls); PRIVATE BOOL objc_resolve_class(Class); void objc_send_initialize(id object); +PRIVATE SEL cxx_construct_sel; +PRIVATE SEL cxx_destruct_sel; + +PRIVATE void init_cxx_selectors(void) +{ + cxx_construct_sel = sel_registerName(".cxx_construct"); + cxx_destruct_sel = sel_registerName(".cxx_destruct"); +} + /** * Calls C++ destructors in the correct order. */ PRIVATE void call_cxx_destruct(id obj) { - static SEL cxx_destruct; - if (NULL == cxx_destruct) - { - cxx_destruct = sel_registerName(".cxx_destruct"); - } // Don't call object_getClass(), because we want to get hidden classes too Class cls = classForObject(obj); @@ -46,32 +50,33 @@ PRIVATE void call_cxx_destruct(id obj) cls = cls->super_class; if (currentClass->cxx_destruct) { - currentClass->cxx_destruct(obj, cxx_destruct); + currentClass->cxx_destruct(obj, cxx_destruct_sel); } } } static void call_cxx_construct_for_class(Class cls, id obj) { - static SEL cxx_construct; - if (NULL == cxx_construct) - { - cxx_construct = sel_registerName(".cxx_construct"); - } - - if (cls->super_class) + if (cls->super_class && + !objc_test_class_flag(cls->super_class, objc_class_flag_no_cxx_construct)) { call_cxx_construct_for_class(cls->super_class, obj); } if (cls->cxx_construct) { - cls->cxx_construct(obj, cxx_construct); + cls->cxx_construct(obj, cxx_construct_sel); } } PRIVATE void call_cxx_construct(id obj) { - call_cxx_construct_for_class(classForObject(obj), obj); + Class cls = classForObject(obj); + + if (objc_test_class_flag(cls, objc_class_flag_no_cxx_construct)) + { + return; + } + call_cxx_construct_for_class(cls, obj); } /** diff --git a/selector.h b/selector.h index c9409873..6b741b4a 100644 --- a/selector.h +++ b/selector.h @@ -2,6 +2,7 @@ #define OBJC_SELECTOR_H_INCLUDED #include #include "objc/runtime.h" +#include "visibility.h" /** * Structure used to store selectors in the list. @@ -56,6 +57,13 @@ extern "C" */ SEL objc_register_selector(SEL aSel); +/** + * The `.cxx_construct` and `.cxx_destruct` selectors, registered once during + * runtime initialisation by `init_cxx_selectors`. + */ +PRIVATE extern SEL cxx_construct_sel; +PRIVATE extern SEL cxx_destruct_sel; + #ifdef __cplusplus } #endif