Fix the case where the number of selectors grows to more than 2^16 and we need

to fall back to 3-level dtables.  

Added a test case that registers far more selectors than a sane program
contains and tries to use them.
main
theraven 13 years ago
parent 1a386c9efd
commit eb234ea26a

@ -11,6 +11,7 @@ set(TESTS
ExceptionTest.m ExceptionTest.m
ForeignException.m ForeignException.m
Forward.m Forward.m
ManyManySelectors.m
NestedExceptions.m NestedExceptions.m
PropertyAttributeTest.m PropertyAttributeTest.m
PropertyIntrospectionTest.m PropertyIntrospectionTest.m

@ -0,0 +1,35 @@
#include "Test.h"
#include <inttypes.h>
#include <stdio.h>
#include <string.h>
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;
}

@ -18,7 +18,8 @@ PRIVATE dtable_t uninstalled_dtable;
PRIVATE InitializingDtable *temporary_dtables; PRIVATE InitializingDtable *temporary_dtables;
/** Lock used to protect the temporary dtables list. */ /** Lock used to protect the temporary dtables list. */
PRIVATE mutex_t initialize_lock; 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; static uint32_t dtable_depth = 8;
struct objc_slot* objc_get_slot(Class cls, SEL selector); 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(); LOCK_RUNTIME_FOR_SCOPE();
dtable_depth <<= 1; if (1<<dtable_depth > newSize) { return; }
dtable_depth += 8;
uint32_t oldMask = uninstalled_dtable->mask; uint32_t oldMask = uninstalled_dtable->mask;
SparseArrayExpandingArray(uninstalled_dtable); SparseArrayExpandingArray(uninstalled_dtable, dtable_depth);
// Resize all existing dtables // Resize all existing dtables
void *e = NULL; void *e = NULL;
struct objc_class *next; struct objc_class *next;
@ -569,7 +572,8 @@ PRIVATE void objc_resize_dtables(uint32_t newSize)
NULL != next->dtable && NULL != next->dtable &&
((SparseArray*)next->dtable)->mask == oldMask) ((SparseArray*)next->dtable)->mask == oldMask)
{ {
SparseArrayExpandingArray((void*)next->dtable); SparseArrayExpandingArray((void*)next->dtable, dtable_depth);
SparseArrayExpandingArray((void*)next->isa->dtable, dtable_depth);
} }
} }
} }

@ -208,56 +208,7 @@ typedef struct
#endif #endif
#include "slot.h" #include "slot.h"
#include "message.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
/** /**

@ -8,6 +8,12 @@
static void *EmptyArrayData[256]; static void *EmptyArrayData[256];
static SparseArray EmptyArray = { 0xff, 0, 0, (void**)&EmptyArrayData}; 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 MAX_INDEX(sarray) (sarray->mask >> sarray->shift)
#define DATA_SIZE(sarray) ((sarray->mask >> sarray->shift) + 1) #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_shift 8
#define base_mask ((1<<base_shift) - 1) #define base_mask ((1<<base_shift) - 1)
void *EmptyChildForShift(uint32_t shift)
{
switch(shift)
{
default: UNREACHABLE("Broken sparse array");
case 8:
return &EmptyArray;
case 16:
return &EmptyArray8;
case 24:
return &EmptyArray16;
case 32:
return &EmptyArray24;
}
}
static void init_pointers(SparseArray * sarray) static void init_pointers(SparseArray * sarray)
{ {
sarray->data = calloc(DATA_SIZE(sarray), sizeof(void*)); sarray->data = calloc(DATA_SIZE(sarray), sizeof(void*));
if(sarray->shift != 0) if(sarray->shift != 0)
{ {
void *data = EmptyChildForShift(sarray->shift);
for(unsigned i=0 ; i<=MAX_INDEX(sarray) ; i++) for(unsigned i=0 ; i<=MAX_INDEX(sarray) ; i++)
{ {
sarray->data[i] = &EmptyArray; sarray->data[i] = data;
} }
} }
} }
PRIVATE SparseArray * SparseArrayNewWithDepth(uint32_t depth) PRIVATE SparseArray * SparseArrayNewWithDepth(uint32_t depth)
{ {
SparseArray * sarray = calloc(1, sizeof(SparseArray)); SparseArray * sarray = calloc(1, sizeof(SparseArray));
@ -42,8 +66,13 @@ PRIVATE SparseArray *SparseArrayNew()
{ {
return SparseArrayNewWithDepth(32); 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. // Expanding a child sarray has undefined results.
assert(sarray->refCount == 1); assert(sarray->refCount == 1);
SparseArray *new = calloc(1, sizeof(SparseArray)); SparseArray *new = calloc(1, sizeof(SparseArray));
@ -51,12 +80,12 @@ PRIVATE SparseArray *SparseArrayExpandingArray(SparseArray *sarray)
new->shift = sarray->shift; new->shift = sarray->shift;
new->mask = sarray->mask; new->mask = sarray->mask;
void **newData = malloc(DATA_SIZE(sarray) * sizeof(void*)); 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->data = sarray->data;
// new is now an exact copy of sarray.
newData[0] = new; newData[0] = new;
sarray->data = newData; sarray->data = newData;
// Now, any lookup in sarray for any value less than its capacity will have // 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; sarray->shift += base_shift;
// Finally, set the mask to the correct value. Now all lookups should work. // Finally, set the mask to the correct value. Now all lookups should work.
sarray->mask <<= base_shift; sarray->mask <<= base_shift;
return new; return sarray;
} }
static void *SparseArrayFind(SparseArray * sarray, uint32_t * index) static void *SparseArrayFind(SparseArray * sarray, uint32_t * index)
@ -86,27 +115,34 @@ static void *SparseArrayFind(SparseArray * sarray, uint32_t * index)
} }
else while (j<max) else while (j<max)
{ {
// If the shift is not 0, then we need to recursively look at child
// nodes.
uint32_t zeromask = ~(sarray->mask >> base_shift); uint32_t zeromask = ~(sarray->mask >> base_shift);
while (j<max) while (j<max)
{ {
//Look in child nodes //Look in child nodes
if (sarray->data[j] != SARRAY_EMPTY) SparseArray *child = sarray->data[j];
{ // Skip over known-empty children
void * ret = SparseArrayFind(sarray->data[j], index); if ((&EmptyArray == child) ||
if (ret != SARRAY_EMPTY) (&EmptyArray8 == child) ||
{ (&EmptyArray16 == child) ||
return ret; (&EmptyArray24 == child))
}
// The recursive call will set index to the correct value for
// the next index, but won't update j
}
else
{ {
//Add 2^n to index so j is still correct //Add 2^n to index so j is still correct
(*index) += 1<<sarray->shift; (*index) += 1<<sarray->shift;
//Zero off the next component of the index so we don't miss any. //Zero off the next component of the index so we don't miss any.
*index &= zeromask; *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 //Go to the next child
j++; j++;
} }
@ -126,7 +162,10 @@ PRIVATE void SparseArrayInsert(SparseArray * sarray, uint32_t index, void *value
{ {
uint32_t i = MASK_INDEX(index); uint32_t i = MASK_INDEX(index);
SparseArray *child = sarray->data[i]; SparseArray *child = sarray->data[i];
if(&EmptyArray == child) if ((&EmptyArray == child) ||
(&EmptyArray8 == child) ||
(&EmptyArray16 == child) ||
(&EmptyArray24 == child))
{ {
// Insert missing nodes // Insert missing nodes
SparseArray * newsarray = calloc(1, sizeof(SparseArray)); 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 // Don't really delete this sarray if its ref count is > 0
if (sarray == &EmptyArray || if (sarray == &EmptyArray ||
sarray == &EmptyArray8 ||
sarray == &EmptyArray16 ||
(__sync_sub_and_fetch(&sarray->refCount, 1) > 0)) (__sync_sub_and_fetch(&sarray->refCount, 1) > 0))
{ {
return; return;

@ -112,7 +112,7 @@ SparseArray *SparseArrayNewWithDepth(uint32_t depth);
* Returns a new sparse array created by adding this one as the first child * Returns a new sparse array created by adding this one as the first child
* node in an expanded one. * node in an expanded one.
*/ */
SparseArray *SparseArrayExpandingArray(SparseArray *sarray); SparseArray *SparseArrayExpandingArray(SparseArray *sarray, uint32_t new_depth);
/** /**
* Insert a value at the specified index. * Insert a value at the specified index.
*/ */

Loading…
Cancel
Save