Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions class.h
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};

/**
Expand Down
23 changes: 15 additions & 8 deletions dtable.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}

Expand Down
4 changes: 4 additions & 0 deletions loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down
5 changes: 5 additions & 0 deletions loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
*/
Expand Down
35 changes: 20 additions & 15 deletions runtime.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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 &&

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not do the test in cls here and then recurse at the end?

!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);
}

/**
Expand Down
8 changes: 8 additions & 0 deletions selector.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define OBJC_SELECTOR_H_INCLUDED
#include <stdint.h>
#include "objc/runtime.h"
#include "visibility.h"

/**
* Structure used to store selectors in the list.
Expand Down Expand Up @@ -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
Expand Down