diff --git a/Test/CMakeLists.txt b/Test/CMakeLists.txt index 7893406..2e7052d 100644 --- a/Test/CMakeLists.txt +++ b/Test/CMakeLists.txt @@ -11,6 +11,7 @@ set(TESTS ExceptionTest.m ForeignException.m Forward.m + ManyManySelectors.m NestedExceptions.m PropertyAttributeTest.m PropertyIntrospectionTest.m diff --git a/Test/ManyManySelectors.m b/Test/ManyManySelectors.m new file mode 100644 index 0000000..49f8dc2 --- /dev/null +++ b/Test/ManyManySelectors.m @@ -0,0 +1,35 @@ +#include "Test.h" +#include +#include +#include + +static BOOL methodCalled = NO; + +static id x(id self, SEL _cmd) +{ + methodCalled = YES; + assert(strcmp("selectoreffff", sel_getName(_cmd)) == 0); + return self; +} + +int main(void) +{ + char selBuffer[] = "selectorXXXXXXXX"; + SEL nextSel; + Class cls = [Test class]; + assert(cls != Nil); + for (uint32_t i=0 ; i<0xf0000 ; i++) + { + snprintf(selBuffer, 16, "selector%" PRIx32, i); + nextSel = sel_registerName(selBuffer); + } + assert(class_addMethod(object_getClass([Test class]), nextSel, (IMP)x, "@@:")); + assert(cls == [Test class]); + // Test both the C and assembly code paths. + objc_msg_lookup(cls, nextSel)(cls, nextSel); + assert(methodCalled == YES); + methodCalled = NO; + objc_msgSend([Test class], nextSel); + assert(methodCalled == YES); + return 0; +} diff --git a/dtable.c b/dtable.c index cbac716..f534b17 100644 --- a/dtable.c +++ b/dtable.c @@ -18,7 +18,8 @@ PRIVATE dtable_t uninstalled_dtable; PRIVATE InitializingDtable *temporary_dtables; /** Lock used to protect the temporary dtables list. */ PRIVATE mutex_t initialize_lock; -/** The size of the largest dtable, rounded up to the nearest power of two. */ +/** The size of the largest dtable. This is a sparse array shift value, so is + * 2^x in increments of 8. */ static uint32_t dtable_depth = 8; struct objc_slot* objc_get_slot(Class cls, SEL selector); @@ -555,11 +556,13 @@ PRIVATE void objc_resize_dtables(uint32_t newSize) LOCK_RUNTIME_FOR_SCOPE(); - dtable_depth <<= 1; + if (1< newSize) { return; } + + dtable_depth += 8; uint32_t oldMask = uninstalled_dtable->mask; - SparseArrayExpandingArray(uninstalled_dtable); + SparseArrayExpandingArray(uninstalled_dtable, dtable_depth); // Resize all existing dtables void *e = NULL; struct objc_class *next; @@ -569,7 +572,8 @@ PRIVATE void objc_resize_dtables(uint32_t newSize) NULL != next->dtable && ((SparseArray*)next->dtable)->mask == oldMask) { - SparseArrayExpandingArray((void*)next->dtable); + SparseArrayExpandingArray((void*)next->dtable, dtable_depth); + SparseArrayExpandingArray((void*)next->isa->dtable, dtable_depth); } } } diff --git a/objc/runtime.h b/objc/runtime.h index f09bce3..863a89f 100644 --- a/objc/runtime.h +++ b/objc/runtime.h @@ -208,56 +208,7 @@ typedef struct #endif #include "slot.h" - -#if defined(__x86_64) || defined(__i386) || defined(__arm__) || \ - defined(__mips_n64) || defined(__mips_n32) -/** - * Standard message sending function. This function must be cast to the - * correct types for the function before use. The first argument is the - * receiver and the second the selector. - * - * Note that this function is not available on all architectures. For a more - * portable solution to sending arbitrary messages, consider using - * objc_msg_lookup_sender() and then calling the returned IMP directly. - * - * This version of the function is used for all messages that return either an - * integer, a pointer, or a small structure value that is returned in - * registers. Be aware that calling conventions differ between operating - * systems even within the same architecture, so take great care if using this - * function for small (two integer) structures. - */ -id objc_msgSend(id self, SEL _cmd, ...); -/** - * Standard message sending function. This function must be cast to the - * correct types for the function before use. The first argument is the - * receiver and the second the selector. - * - * Note that this function is not available on all architectures. For a more - * portable solution to sending arbitrary messages, consider using - * objc_msg_lookup_sender() and then calling the returned IMP directly. - * - * This version of the function is used for all messages that return a - * structure that is not returned in registers. Be aware that calling - * conventions differ between operating systems even within the same - * architecture, so take great care if using this function for small (two - * integer) structures. - */ -void objc_msgSend_stret(id self, SEL _cmd, ...); -/** - * Standard message sending function. This function must be cast to the - * correct types for the function before use. The first argument is the - * receiver and the second the selector. - * - * Note that this function is not available on all architectures. For a more - * portable solution to sending arbitrary messages, consider using - * objc_msg_lookup_sender() and then calling the returned IMP directly. - * - * This version of the function is used for all messages that return floating - * point values. - */ -long double objc_msgSend_fpret(id self, SEL _cmd, ...); - -#endif +#include "message.h" /** diff --git a/sarray2.c b/sarray2.c index b9ca967..7e98c91 100644 --- a/sarray2.c +++ b/sarray2.c @@ -8,6 +8,12 @@ static void *EmptyArrayData[256]; static SparseArray EmptyArray = { 0xff, 0, 0, (void**)&EmptyArrayData}; +static void *EmptyArrayData8[256] = { [0 ... 255] = &EmptyArray }; +static SparseArray EmptyArray8 = { 0xff00, 8, 0, (void**)&EmptyArrayData8}; +static void *EmptyArrayData16[256] = { [0 ... 255] = &EmptyArray8 }; +static SparseArray EmptyArray16 = { 0xff0000, 16, 0, (void**)&EmptyArrayData16}; +static void *EmptyArrayData24[256] = { [0 ... 255] = &EmptyArray16 }; +static SparseArray EmptyArray24 = { 0xff0000, 24, 0, (void**)&EmptyArrayData24}; #define MAX_INDEX(sarray) (sarray->mask >> sarray->shift) #define DATA_SIZE(sarray) ((sarray->mask >> sarray->shift) + 1) @@ -17,17 +23,35 @@ static SparseArray EmptyArray = { 0xff, 0, 0, (void**)&EmptyArrayData}; #define base_shift 8 #define base_mask ((1<data = calloc(DATA_SIZE(sarray), sizeof(void*)); if(sarray->shift != 0) { + void *data = EmptyChildForShift(sarray->shift); for(unsigned i=0 ; i<=MAX_INDEX(sarray) ; i++) { - sarray->data[i] = &EmptyArray; + sarray->data[i] = data; } } } + PRIVATE SparseArray * SparseArrayNewWithDepth(uint32_t depth) { SparseArray * sarray = calloc(1, sizeof(SparseArray)); @@ -42,8 +66,13 @@ PRIVATE SparseArray *SparseArrayNew() { return SparseArrayNewWithDepth(32); } -PRIVATE SparseArray *SparseArrayExpandingArray(SparseArray *sarray) +PRIVATE SparseArray *SparseArrayExpandingArray(SparseArray *sarray, uint32_t new_depth) { + if (new_depth == sarray->shift) + { + return sarray; + } + assert(new_depth > sarray->shift); // Expanding a child sarray has undefined results. assert(sarray->refCount == 1); SparseArray *new = calloc(1, sizeof(SparseArray)); @@ -51,12 +80,12 @@ PRIVATE SparseArray *SparseArrayExpandingArray(SparseArray *sarray) new->shift = sarray->shift; new->mask = sarray->mask; void **newData = malloc(DATA_SIZE(sarray) * sizeof(void*)); - for(unsigned i=0 ; i<=MAX_INDEX(sarray) ; i++) + void *data = EmptyChildForShift(new->shift + 8); + for(unsigned i=1 ; i<=MAX_INDEX(sarray) ; i++) { - newData[i] = &EmptyArray; + newData[i] = data; } new->data = sarray->data; - // new is now an exact copy of sarray. newData[0] = new; sarray->data = newData; // Now, any lookup in sarray for any value less than its capacity will have @@ -65,7 +94,7 @@ PRIVATE SparseArray *SparseArrayExpandingArray(SparseArray *sarray) sarray->shift += base_shift; // Finally, set the mask to the correct value. Now all lookups should work. sarray->mask <<= base_shift; - return new; + return sarray; } static void *SparseArrayFind(SparseArray * sarray, uint32_t * index) @@ -86,27 +115,34 @@ static void *SparseArrayFind(SparseArray * sarray, uint32_t * index) } else while (jmask >> base_shift); while (jdata[j] != SARRAY_EMPTY) - { - void * ret = SparseArrayFind(sarray->data[j], index); - if (ret != SARRAY_EMPTY) - { - return ret; - } - // The recursive call will set index to the correct value for - // the next index, but won't update j - } - else + SparseArray *child = sarray->data[j]; + // Skip over known-empty children + if ((&EmptyArray == child) || + (&EmptyArray8 == child) || + (&EmptyArray16 == child) || + (&EmptyArray24 == child)) { //Add 2^n to index so j is still correct (*index) += 1<shift; //Zero off the next component of the index so we don't miss any. *index &= zeromask; } + else + { + // The recursive call will set index to the correct value for + // the next index, but won't update j + void * ret = SparseArrayFind(child, index); + if (ret != SARRAY_EMPTY) + { + return ret; + } + } //Go to the next child j++; } @@ -126,7 +162,10 @@ PRIVATE void SparseArrayInsert(SparseArray * sarray, uint32_t index, void *value { uint32_t i = MASK_INDEX(index); SparseArray *child = sarray->data[i]; - if(&EmptyArray == child) + if ((&EmptyArray == child) || + (&EmptyArray8 == child) || + (&EmptyArray16 == child) || + (&EmptyArray24 == child)) { // Insert missing nodes SparseArray * newsarray = calloc(1, sizeof(SparseArray)); @@ -185,6 +224,8 @@ PRIVATE void SparseArrayDestroy(SparseArray * sarray) { // Don't really delete this sarray if its ref count is > 0 if (sarray == &EmptyArray || + sarray == &EmptyArray8 || + sarray == &EmptyArray16 || (__sync_sub_and_fetch(&sarray->refCount, 1) > 0)) { return; diff --git a/sarray2.h b/sarray2.h index ce1d41a..eb98ccf 100644 --- a/sarray2.h +++ b/sarray2.h @@ -112,7 +112,7 @@ SparseArray *SparseArrayNewWithDepth(uint32_t depth); * Returns a new sparse array created by adding this one as the first child * node in an expanded one. */ -SparseArray *SparseArrayExpandingArray(SparseArray *sarray); +SparseArray *SparseArrayExpandingArray(SparseArray *sarray, uint32_t new_depth); /** * Insert a value at the specified index. */