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
ForeignException.m
Forward.m
ManyManySelectors.m
NestedExceptions.m
PropertyAttributeTest.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;
/** 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<<dtable_depth > 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);
}
}
}

@ -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"
/**

@ -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<<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)
{
sarray->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 (j<max)
{
// If the shift is not 0, then we need to recursively look at child
// nodes.
uint32_t zeromask = ~(sarray->mask >> base_shift);
while (j<max)
{
//Look in child nodes
if (sarray->data[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<<sarray->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;

@ -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.
*/

Loading…
Cancel
Save