Merge branch 'master' into newabi

Simplify the setSubclass dtable updating mechanism.
main
David Chisnall 8 years ago
commit b81df02b91

@ -43,6 +43,13 @@ Highlights of this release include:
requirements, which should fix issues relating to using vector types in
Objective-C objects.
- The option to build a separate libobjcxx has been removed. The runtime will
now depend on the C++ standard library implementation if no useable C++
runtime is available. Note that C++ exception interworking does not work
because LLVM's libc++abi (shipped by Apple) does not provide GNU-compatible
hooks and so Objective-C++ exception support will be automatically disabled
on this platform. Any other platforms shipping libc++abi should consider
either GNU libsupc++ or libcxxrt as an alternative.
You may obtain the code for this release from git and use the 1.x branch:

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.1)
add_executable(test_cxx_runtime typeinfo_test.cc)
set(CMAKE_CXX_IMPLICIT_LINK_LIBRARIES "")

@ -1,8 +1,10 @@
cmake_minimum_required(VERSION 2.8)
cmake_minimum_required(VERSION 3.1)
project(libobjc)
enable_language(ASM)
INCLUDE (CheckCXXSourceCompiles)
macro(install_symlink filepath sympath)
install(CODE "execute_process(COMMAND ${CMAKE_COMMAND} -E create_symlink ${filepath} ${sympath})")
install(CODE "message(\"-- Symlinking: ${sympath} -> ${filepath}\")")
@ -364,3 +366,17 @@ if (TESTS)
add_subdirectory(Test)
endif (TESTS)
CHECK_CXX_SOURCE_COMPILES("
#include <stdlib.h>
extern \"C\" {
__attribute__((weak))
void *__cxa_allocate_exception(size_t thrown_size) noexcept;
}
#include <exception>
int main() { return 0; }" CXA_ALLOCATE_EXCEPTION_NOEXCEPT_COMPILES)
if (CXA_ALLOCATE_EXCEPTION_NOEXCEPT_COMPILES)
add_definitions(-DCXA_ALLOCATE_EXCEPTION_SPECIFIER=noexcept)
else ()
add_definitions(-DCXA_ALLOCATE_EXCEPTION_SPECIFIER=)
endif ()

@ -1,4 +1,6 @@
#include "Test.h"
#include <stdio.h>
#include <inttypes.h>
static BOOL deallocCalled = NO;
static const char* objc_setAssociatedObjectKey = "objc_setAssociatedObjectKey";
@ -19,8 +21,41 @@ int main(void)
@autoreleasepool {
Associated *object = [Associated new];
Test *holder = [[Test new] autorelease];
objc_setAssociatedObject(object, &objc_setAssociatedObjectKey, holder, OBJC_ASSOCIATION_RETAIN);
objc_setAssociatedObject(holder, &objc_setAssociatedObjectKey, object, OBJC_ASSOCIATION_RETAIN);
[object release];
assert(!deallocCalled);
}
// dealloc should be called when holder is released during pool drain
assert(deallocCalled);
deallocCalled = NO;
Associated *object = [Associated new];
Test *holder = [Test new];
objc_setAssociatedObject(holder, &objc_setAssociatedObjectKey, object, OBJC_ASSOCIATION_RETAIN);
[object release]; // commuted into associated object storage
objc_setAssociatedObject(holder, &objc_setAssociatedObjectKey, nil, OBJC_ASSOCIATION_ASSIGN);
[holder release];
assert(deallocCalled);
object = [Associated new];
holder = [Test new];
for (uintptr_t i = 1; i <= 20; ++i)
{
objc_setAssociatedObject(holder, (void*)i, object, OBJC_ASSOCIATION_RETAIN);
}
int lost = 0;
for (uintptr_t i = 1; i <= 20; ++i)
{
if (object != objc_getAssociatedObject(holder, (void*)i))
{
fprintf(stderr, "lost object %" PRIuPTR "\n", i);
++lost;
}
}
[holder release];
[object release];
assert(0 == lost);
return 0;
}

@ -38,6 +38,8 @@ set(TESTS
MethodArguments.m
zeroSizedIVar.m
exchange.m
hash_table_delete.c
setSuperclass.m
)
# List of single-file tests that won't work with the legacy ABI and so

@ -0,0 +1,64 @@
#include <stdbool.h>
#include <stdint.h>
struct test_struct {
uintptr_t key;
};
struct test_struct null_placeholder = {0};
static int test_compare(const void *key, const struct test_struct test) {
return (uintptr_t)key == test.key;
}
// force hash collisions
static uint32_t test_key_hash(const void *ptr) {
return ((uint32_t)(uintptr_t)ptr)>>2;
}
static uint32_t test_value_hash(const struct test_struct test) {
return test.key>>2;
}
static int test_is_null(const struct test_struct test) {
return test.key == 0;
}
#define MAP_TABLE_NAME test
#define MAP_TABLE_COMPARE_FUNCTION test_compare
#define MAP_TABLE_VALUE_TYPE struct test_struct
#define MAP_TABLE_VALUE_NULL test_is_null
#define MAP_TABLE_HASH_KEY test_key_hash
#define MAP_TABLE_HASH_VALUE test_value_hash
#define MAP_TABLE_VALUE_PLACEHOLDER null_placeholder
#define MAP_TABLE_ACCESS_BY_REFERENCE 1
#define MAP_TABLE_SINGLE_THREAD 1
#define MAP_TABLE_NO_LOCK 1
#include "../hash_table.h"
int main(int argc, char *argv[])
{
test_table *testTable;
test_initialize(&testTable, 128);
struct test_struct one, two, three;
one.key = 1;
two.key = 2;
three.key = 3;
test_insert(testTable, one);
test_insert(testTable, two);
test_insert(testTable, three);
test_remove(testTable, (void*)2);
test_remove(testTable, (void*)1);
struct test_struct *pthree = test_table_get(testTable, (void*)3);
if (!pthree) {
fprintf(stderr, "failed to find value (key=3) inserted into hash table\n");
return 1;
}
return 0;
}

@ -0,0 +1,297 @@
#include "../objc/runtime.h"
#include <assert.h>
#include <stdio.h>
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
__attribute__((objc_root_class))
@interface Root
{
id isa;
}
@end
@interface DefaultSuperclass: Root @end
// test: new superclass when not initialized at the time of class_setSuperclass
@interface NotInitializedSuperclass1: Root @end
@interface Subclass1: DefaultSuperclass @end
// test: new superclass when already initialized at the time of class_setSuperclass
@interface NotInitializedSuperclass2: Root @end
@interface Subclass2: DefaultSuperclass @end
@interface Subclass2Subclass: Subclass2 @end
@interface ChangesDuringInitialize: DefaultSuperclass @end
// test: class gets reparented under its parent's parent.
@interface RemovedFromHierarchy: DefaultSuperclass @end
@interface MovesUpwardsInHierarchy: RemovedFromHierarchy @end
// test: one class initializes anotherwhile initializing during class_setSuperclass
@interface OtherInitializedClass: Root @end
@interface InitializesOneClassWhileBeingInitialized: NotInitializedSuperclass2 @end
@interface Subclass3: DefaultSuperclass @end
@implementation Root
+ (Class)class { return self; }
+ (BOOL)respondsToSelector:(SEL)selector {
return class_respondsToSelector(object_getClass(self), selector);
}
+ (BOOL)instancesRespondToSelector:(SEL)selector {
return class_respondsToSelector(self, selector);
}
@end
@implementation NotInitializedSuperclass1
static BOOL _notInitializedSuperclass1Initialized = NO;
+ (void)initialize {
_notInitializedSuperclass1Initialized = YES;
}
+ (void)existsOnNotInitializedSuperclassMeta { };
+ (int)sameNameMeta { return 12; }
+ (int)overriddenMeta { return 12; }
- (BOOL)existsOnNotInitializedSuperclass { return YES; }
- (int)sameName { return 2; }
- (int)overridden { return 2; }
@end
@implementation NotInitializedSuperclass2
static BOOL _notInitializedSuperclass2Initialized = NO;
+ (void)initialize {
_notInitializedSuperclass2Initialized = YES;
}
+ (void)existsOnNotInitializedSuperclassMeta { };
+ (int)sameNameMeta { return 13; }
+ (int)overriddenMeta { return 13; }
- (BOOL)existsOnNotInitializedSuperclass { return YES; }
- (int)sameName { return 3; }
- (int)overridden { return 3; }
@end
@implementation DefaultSuperclass
static BOOL _alreadyInitializedSuperclassInitialized = NO;
+ (void)initialize {
_alreadyInitializedSuperclassInitialized = YES;
}
+ (void)existsOnDefaultSuperclassMeta { };
+ (int)sameNameMeta { return 14; }
+ (int)overriddenMeta { return 14; }
- (BOOL)existsOnDefaultSuperclass { return YES; }
- (int)sameName { return 4; }
- (int)overridden { return 4; }
@end
@implementation Subclass1
static BOOL _subclass1Initialized = NO;
+ (void)initialize {
_subclass1Initialized = YES;
}
+ (int)overriddenMeta { return 15; } // shadows 14
- (BOOL)existsOnSubclass1 { return YES; }
- (int)overridden { return 5; } // shadows 4
@end
@implementation Subclass2
static BOOL _subclass2Initialized = NO;
+ (void)initialize {
_subclass2Initialized = YES;
}
+ (int)overriddenMeta { return 16; } // shadows 14
- (BOOL)existsOnSubclass2 { return YES; }
- (int)overridden { return 6; } // shadows 4
- (int)intermediateOverride { return 100; }
@end
@implementation Subclass2Subclass
- (int)intermediateOverride { return 200; }
@end
@implementation ChangesDuringInitialize
+ (void)initialize {
class_setSuperclass(self, objc_getClass("NotInitializedSuperclass1"));
}
+ (int)overriddenMeta { return 18; }
@end
@implementation RemovedFromHierarchy
+ (int)overriddenMeta { return 19; } // shadows 14 on DefaultSuperClass
+ (int)sameNameMeta { return 19; } // shadows 14 on DefaultSuperClass
+ (void)onlyExistsOnRemovedClassMeta { }
- (void)onlyExistsOnRemovedClass { }
@end
@implementation MovesUpwardsInHierarchy
+ (int)overriddenMeta { return 20; } // shadows 19 on RemovedFromHierarchy or 14 on DefaultSuperClass
@end
@implementation OtherInitializedClass
static BOOL _otherInitializedClassInitialized = NO;
+ (void)initialize {
_otherInitializedClassInitialized = YES;
}
@end
@implementation InitializesOneClassWhileBeingInitialized
+ (void)initialize {
[OtherInitializedClass class];
}
@end
@implementation Subclass3
@end
static int failures = 0;
#define expect(x) do \
{ \
if (!(x)) \
{ \
fprintf(stderr, "expectation FAILED: %s\n", #x); \
++failures; \
} \
} while(0)
int main(int argc, char **argv) {
Class firstSuperclass = objc_getClass("DefaultSuperclass");
Class subclass1 = objc_getClass("Subclass1");
/* Transitioning to a new superclass before +initialize has been called */
{
Class subclass1 = objc_getClass("Subclass1");
Class secondSuperclass = objc_getClass("NotInitializedSuperclass1");
assert(!_notInitializedSuperclass1Initialized);
assert(!_subclass1Initialized);
class_setSuperclass(subclass1, secondSuperclass);
// assert: dtable has not been installed; new superclass is still not initialized
assert(!_notInitializedSuperclass1Initialized);
[Subclass1 class];
// initialization and dtable installation has taken place
assert(_notInitializedSuperclass1Initialized);
Subclass1 *subclass1instance1 = class_createInstance(subclass1, 0);
// CLASS
// can call method on subclass
expect([subclass1instance1 existsOnSubclass1]);
// can call method on _new_ superclass
expect([(id)subclass1instance1 existsOnNotInitializedSuperclass]);
// does not respond to selector from original superclass
expect(![subclass1 instancesRespondToSelector:@selector(existsOnDefaultSuperclass)]);
// *does* respond to selector from new superclass
expect([subclass1 instancesRespondToSelector:@selector(existsOnNotInitializedSuperclass)]);
// method existing on both old and new superclass kept, IMP updated
expect(2 == [subclass1instance1 sameName]);
// method existing on subclass, old and new superclass kept, IMP kept
expect(5 == [subclass1instance1 overridden]);
// METACLASS
// metaclass does not respond to selector from original meta superclass
expect(![subclass1 respondsToSelector:@selector(existsOnDefaultSuperclassMeta)]);
// metaclass *does* respond to selector from new meta superclass
expect([subclass1 respondsToSelector:@selector(existsOnNotInitializedSuperclassMeta)]);
// method existing on both old and new superclass kept, IMP updated
expect(12 == [subclass1 sameNameMeta]);
// method existing on subclass, old and new superclass kept, IMP kept
expect(15 == [subclass1 overriddenMeta]);
}
/* Transitioning to a new superclass when +initialize has already been called */
{
Class subclass2 = objc_getClass("Subclass2");
Class secondSuperclass = objc_getClass("NotInitializedSuperclass2");
assert(!_notInitializedSuperclass2Initialized);
assert(!_subclass2Initialized);
[Subclass2 class];
[Subclass2Subclass class]; // Make sure the subclass is initialized too.
assert(_alreadyInitializedSuperclassInitialized);
assert(_subclass2Initialized);
Subclass2 *subclass2instance1 = class_createInstance(subclass2, 0);
assert([subclass2instance1 existsOnSubclass2]);
class_setSuperclass(subclass2, secondSuperclass);
assert(_notInitializedSuperclass2Initialized);
Subclass2 *subclass2instance2 = class_createInstance(subclass2, 0);
// CLASS
// can call method on subclass
expect([subclass2instance1 existsOnSubclass2]);
// can call method on _new_ superclass
expect([(id)subclass2instance1 existsOnNotInitializedSuperclass]);
// does not respond to selector from original superclass
expect(![subclass2 instancesRespondToSelector:@selector(existsOnDefaultSuperclass)]);
// *does* respond to selector from new superclass
expect([subclass2 instancesRespondToSelector:@selector(existsOnNotInitializedSuperclass)]);
// method existing on both old and new superclass kept, IMP updated
expect(3 == [subclass2instance1 sameName]);
// method existing on subclass, old and new superclass kept, IMP kept
expect(6 == [subclass2instance1 overridden]);
// method existing only on subclass preserved
expect(100 == [subclass2instance1 intermediateOverride]);
// METACLASS
// metaclass does not respond to selector from original meta superclass
expect(![subclass2 respondsToSelector:@selector(existsOnDefaultSuperclassMeta)]);
// metaclass *does* respond to selector from new meta superclass
expect([subclass2 respondsToSelector:@selector(existsOnNotInitializedSuperclassMeta)]);
// method existing on both old and new superclass kept, IMP updated
expect(13 == [subclass2 sameNameMeta]);
// method existing on subclass, old and new superclass kept, IMP kept
expect(16 == [subclass2 overriddenMeta]);
// SUBCLASS
Subclass2 *subclass2subclassInstance = class_createInstance([Subclass2Subclass class], 0);
expect(![Subclass2Subclass instancesRespondToSelector:@selector(existsOnDefaultSuperclass)]);
expect(![Subclass2Subclass respondsToSelector:@selector(existsOnDefaultSuperclassMeta)]);
expect(3 == [subclass2subclassInstance sameName]);
expect(6 == [subclass2subclassInstance overridden]);
expect(200 == [subclass2subclassInstance intermediateOverride]);
expect(13 == [Subclass2Subclass sameNameMeta]);
expect(16 == [Subclass2Subclass overriddenMeta]);
}
/* Transitioning ourselves to a new superclass while +initialize is running */
{
expect(12 == [ChangesDuringInitialize sameNameMeta]);
expect(18 == [ChangesDuringInitialize overriddenMeta]);
}
/* Transitioning to a superclass that's in our inheritance hierarchy already */
{
assert(20 == [MovesUpwardsInHierarchy overriddenMeta]);
assert(19 == [MovesUpwardsInHierarchy sameNameMeta]);
assert([MovesUpwardsInHierarchy respondsToSelector:@selector(onlyExistsOnRemovedClassMeta)]);
assert([MovesUpwardsInHierarchy instancesRespondToSelector:@selector(onlyExistsOnRemovedClass)]);
class_setSuperclass([MovesUpwardsInHierarchy class], [DefaultSuperclass class]);
expect(20 == [MovesUpwardsInHierarchy overriddenMeta]); // still overridden
expect(14 == [MovesUpwardsInHierarchy sameNameMeta]); // falls back to DefaultSuperclass
expect(![MovesUpwardsInHierarchy respondsToSelector:@selector(onlyExistsOnRemovedClassMeta)]);
expect(![MovesUpwardsInHierarchy instancesRespondToSelector:@selector(onlyExistsOnRemovedClass)]);
}
/* Transitioning to a superclass that may cause initialize lock contention */
{
assert(!_otherInitializedClassInitialized);
expect(14 == [Subclass3 sameNameMeta]);
expect(14 == [Subclass3 overriddenMeta]);
class_setSuperclass([Subclass3 class], objc_getClass("InitializesOneClassWhileBeingInitialized"));
expect(_otherInitializedClassInitialized);
expect(13 == [Subclass3 sameNameMeta]);
expect(13 == [Subclass3 overriddenMeta]);
}
return failures;
}

53
arc.m

@ -12,9 +12,44 @@
#import "objc/objc-arc.h"
#import "objc/blocks_runtime.h"
#if defined(_WIN32)
// We're using the Fiber-Local Storage APIs on Windows
// because the TLS APIs won't pass app certification.
// Additionally, the FLS API surface is 1:1 mapped to
// the TLS API surface when fibers are not in use.
# include "safewindows.h"
# define arc_tls_store FlsSetValue
# define arc_tls_load FlsGetValue
# define TLS_CALLBACK(name) void WINAPI name
typedef DWORD arc_tls_key_t;
typedef void WINAPI(*arc_cleanup_function_t)(void*);
static inline arc_tls_key_t arc_tls_key_create(arc_cleanup_function_t cleanupFunction)
{
return FlsAlloc(cleanupFunction);
}
#else // if defined(_WIN32)
# ifndef NO_PTHREADS
# include <pthread.h>
pthread_key_t ARCThreadKey;
# define arc_tls_store pthread_setspecific
# define arc_tls_load pthread_getspecific
# define TLS_CALLBACK(name) void name
typedef pthread_key_t arc_tls_key_t;
typedef void (*arc_cleanup_function_t)(void*);
static inline arc_tls_key_t arc_tls_key_create(arc_cleanup_function_t cleanupFunction)
{
pthread_key_t key;
pthread_key_create(&key, cleanupFunction);
return key;
}
# endif
#endif
#ifdef arc_tls_store
arc_tls_key_t ARCThreadKey;
#endif
extern void _NSConcreteMallocBlock;
@ -60,14 +95,14 @@ struct arc_tls
static inline struct arc_tls* getARCThreadData(void)
{
#ifdef NO_PTHREADS
#ifndef arc_tls_store
return NULL;
#else
struct arc_tls *tls = pthread_getspecific(ARCThreadKey);
#else // !defined arc_tls_store
struct arc_tls *tls = arc_tls_load(ARCThreadKey);
if (NULL == tls)
{
tls = calloc(sizeof(struct arc_tls), 1);
pthread_setspecific(ARCThreadKey, tls);
arc_tls_store(ARCThreadKey, tls);
}
return tls;
#endif
@ -133,7 +168,8 @@ static void emptyPool(struct arc_tls *tls, id *stop)
//fprintf(stderr, "New insert: %p. Stop: %p\n", tls->pool->insert, stop);
}
static void cleanupPools(struct arc_tls* tls)
#ifdef arc_tls_store
static TLS_CALLBACK(cleanupPools)(struct arc_tls* tls)
{
if (tls->returnRetained)
{
@ -151,6 +187,7 @@ static void cleanupPools(struct arc_tls* tls)
}
free(tls);
}
#endif
static Class AutoreleasePool;
@ -596,8 +633,8 @@ PRIVATE void init_arc(void)
{
weak_ref_initialize(&weakRefs, 128);
INIT_LOCK(weakRefLock);
#ifndef NO_PTHREADS
pthread_key_create(&ARCThreadKey, (void(*)(void*))cleanupPools);
#ifdef arc_tls_store
ARCThreadKey = arc_tls_key_create((arc_cleanup_function_t)cleanupPools);
#endif
}

@ -75,8 +75,8 @@ static BOOL isAtomic(uintptr_t policy)
static struct reference* findReference(struct reference_list *list, void *key)
{
if (NULL == list) { return NULL; }
while (list)
{
for (int i=0 ; i<REFERENCE_LIST_SIZE ; i++)
{
if (list->list[i].key == key)
@ -84,6 +84,8 @@ static struct reference* findReference(struct reference_list *list, void *key)
return &list->list[i];
}
}
list = list->next;
}
return NULL;
}
static void cleanupReferenceList(struct reference_list *list)
@ -166,12 +168,17 @@ static void setReference(struct reference_list *list,
lock = lock_for_pointer(r);
lock_spinlock(lock);
}
r->policy = policy;
id old = r->object;
r->object = obj;
@try
{
if (OBJC_ASSOCIATION_ASSIGN != r->policy)
{
objc_release(old);
objc_release(r->object);
}
}
@finally
{
r->policy = policy;
r->object = obj;
}
if (needLock)
{

@ -410,4 +410,15 @@ static inline Class classForObject(id obj)
return obj->isa;
}
static inline BOOL classIsOrInherits(Class cls, Class base)
{
for (Class c = cls ;
Nil != c ;
c = c->super_class)
{
if (c == base) { return YES; }
}
return NO;
}
#endif //__OBJC_CLASS_H_INCLUDED

@ -1,4 +1,4 @@
#if defined(__WIN32__) || defined(__APPLE__)
#if (defined(_WIN32) && defined(__i386__)) || defined(__APPLE__)
#define CDECL(symbol) _##symbol
#else
#define CDECL(symbol) symbol

@ -392,6 +392,78 @@ PRIVATE void objc_update_dtable_for_class(Class cls)
checkARCAccessors(cls);
}
static void rebaseDtableRecursive(Class cls, Class newSuper)
{
dtable_t parentDtable = dtable_for_class(newSuper);
// Collect all of the methods for this class:
dtable_t temporaryDtable = SparseArrayNewWithDepth(dtable_depth);
for (struct objc_method_list *list = cls->methods ; list != NULL ; list = list->next)
{
for (unsigned i=0 ; i<list->count ; i++)
{
struct objc_method *m = method_at_index(list, i);
uint32_t idx = m->selector->index;
// Don't replace existing methods - we're doing the traversal
// pre-order so we'll see methods from categories first.
if (SparseArrayLookup(temporaryDtable, idx) == NULL)
{
SparseArrayInsert(temporaryDtable, idx, m);
}
}
}
dtable_t dtable = dtable_for_class(cls);
uint32_t idx = 0;
struct objc_method *method;
// Install all methods in the dtable with the correct ones.
while ((method = SparseArrayNext(temporaryDtable, &idx)))
{
SparseArrayInsert(dtable, idx, method);
}
idx = 0;
// Now look at all of the methods in the dtable. If they're not ones from
// the dtable that we've just created, then they must come from the
// superclass, so replace them with whatever the superclass has (which may
// be NULL).
while ((method = SparseArrayNext(dtable, &idx)))
{
if (SparseArrayLookup(temporaryDtable, idx) == NULL)
{
SparseArrayInsert(dtable, idx, SparseArrayLookup(parentDtable, idx));
}
}
SparseArrayDestroy(temporaryDtable);
// merge can make a class ARC-compatible.
checkARCAccessors(cls);
// Now visit all of our subclasses and propagate the changes downwards.
for (struct objc_class *subclass=cls->subclass_list ;
Nil != subclass ; subclass = subclass->sibling_class)
{
// Don't bother updating dtables for subclasses that haven't been
// initialized yet
if (!classHasDtable(subclass)) { continue; }
rebaseDtableRecursive(subclass, cls);
}
}
PRIVATE void objc_update_dtable_for_new_superclass(Class cls, Class newSuper)
{
// Only update real dtables
if (!classHasDtable(cls)) { return; }
LOCK_RUNTIME_FOR_SCOPE();
rebaseDtableRecursive(cls, newSuper);
// Invalidate all caches after this operation.
objc_method_cache_version++;
return;
}
PRIVATE void add_method_list_to_class(Class cls,
struct objc_method_list *list)
{
@ -410,7 +482,7 @@ PRIVATE void add_method_list_to_class(Class cls,
checkARCAccessors(cls);
}
static dtable_t create_dtable_for_class(Class class, dtable_t root_dtable)
PRIVATE dtable_t create_dtable_for_class(Class class, dtable_t root_dtable)
{
// Don't create a dtable for a class that already has one
if (classHasDtable(class)) { return dtable_for_class(class); }

@ -115,6 +115,11 @@ static inline int classHasDtable(struct objc_class *cls)
* modifying a class's method list.
*/
void objc_update_dtable_for_class(Class);
/**
* Updates the dtable for a class and its subclasses. Must be called after
* changing and initializing a class's superclass.
*/
void objc_update_dtable_for_new_superclass(Class, Class);
/**
* Adds a single method list to a class. This is used when loading categories,
* and is faster than completely rebuilding the dtable.

@ -562,7 +562,7 @@ struct thread_data *get_thread_data(void)
}
return td;
#else
return &td;
return &thread_data;
#endif
}
@ -572,7 +572,7 @@ struct thread_data *get_thread_data_fast(void)
struct thread_data *td = pthread_getspecific(key);
return td;
#else
return &td;
return &thread_data;
#endif
}

@ -392,6 +392,22 @@ static void PREFIX(_remove)(PREFIX(_table) *table, void *key)
MAP_LOCK();
PREFIX(_table_cell) cell = PREFIX(_table_get_cell)(table, key);
if (NULL == cell) { return; }
uint32_t hash = MAP_TABLE_HASH_KEY(key);
PREFIX(_table_cell) baseCell = PREFIX(_table_lookup)(table, hash);
if (baseCell && baseCell <= cell && cell - baseCell <= 32)
{
uint32_t jump = 1 << (cell - baseCell - 1);
if ((baseCell->secondMaps & jump))
{
// If we are removing a cell stored adjacent to its base due to hash
// collision, we have to clear the base cell's neighbor bit.
// Otherwise, a later remove can move the new placeholder value to the head
// which will cause further chained lookups to fail.
baseCell->secondMaps &= ~jump;
}
}
// If the cell contains a value, set it to the placeholder and shuffle up
// everything
if (0 == cell->secondMaps)

@ -6,15 +6,13 @@
#ifndef __LIBOBJC_LOCK_H_INCLUDED__
#define __LIBOBJC_LOCK_H_INCLUDED__
#ifdef WIN32
#define BOOL _WINBOOL
# include <windows.h>
#undef BOOL
typedef HANDLE mutex_t;
# define INIT_LOCK(x) x = CreateMutex(NULL, FALSE, NULL)
# define LOCK(x) WaitForSingleObject(*x, INFINITE)
# define UNLOCK(x) ReleaseMutex(*x)
# define DESTROY_LOCK(x) CloseHandle(*x)
#ifdef _WIN32
# include "safewindows.h"
typedef CRITICAL_SECTION mutex_t;
# define INIT_LOCK(x) InitializeCriticalSection(&(x))
# define LOCK(x) EnterCriticalSection(x)
# define UNLOCK(x) LeaveCriticalSection(x)
# define DESTROY_LOCK(x) DeleteCriticalSection(x)
#else
# include <pthread.h>
@ -49,13 +47,26 @@ __attribute__((unused)) static void objc_release_lock(void *x)
mutex_t *lock = *(mutex_t**)x;
UNLOCK(lock);
}
/**
* Concatenate strings during macro expansion.
*/
#define LOCK_HOLDERN_NAME_CAT(x, y) x ## y
/**
* Concatenate string with unique variable during macro expansion.
*/
#define LOCK_HOLDER_NAME_COUNTER(x, y) LOCK_HOLDERN_NAME_CAT(x, y)
/**
* Create a unique name for a lock holder variable
*/
#define LOCK_HOLDER_NAME(x) LOCK_HOLDER_NAME_COUNTER(x, __COUNTER__)
/**
* Acquires the lock and automatically releases it at the end of the current
* scope.
*/
#define LOCK_FOR_SCOPE(lock) \
__attribute__((cleanup(objc_release_lock)))\
__attribute__((unused)) mutex_t *lock_pointer = lock;\
__attribute__((unused)) mutex_t *LOCK_HOLDER_NAME(lock_pointer) = lock;\
LOCK(lock)
/**

@ -5,6 +5,10 @@
#ifndef __LIBOBJC_ENCODING_H_INCLUDED__
#define __LIBOBJC_ENCODING_H_INCLUDED__
#ifdef __cplusplus
extern "C" {
#endif
const char *objc_skip_type_qualifiers (const char *type);
const char *objc_skip_typespec(const char *type);
@ -71,4 +75,8 @@ void objc_layout_structure_get_info (struct objc_struct_layout *layout,
#define _F_ONEWAY 0x10
#define _F_GCINVISIBLE 0x20
#ifdef __cplusplus
}
#endif
#endif // __LIBOBJC_ENCODING_H_INCLUDED__

@ -2,6 +2,10 @@
#pragma clang system_header
#endif
#ifdef __cplusplus
extern "C" {
#endif
/**
* This file includes all of the hooks that can be used to alter the behaviour
* of the runtime.
@ -106,4 +110,6 @@ typedef IMP (*objc_tracing_hook)(id, SEL, IMP, int, void*);
*/
int objc_registerTracingHook(SEL, objc_tracing_hook);
#ifdef __cplusplus
}
#endif

@ -4,6 +4,11 @@
#ifndef __OBJC_ARC_INCLUDED__
#define __OBJC_ARC_INCLUDED__
#ifdef __cplusplus
extern "C" {
#endif
/**
* Autoreleases the argument. Equivalent to [obj autorelease].
*/
@ -138,5 +143,10 @@ unsigned long objc_arc_autorelease_count_np(void);
* this thread.
*/
unsigned long objc_arc_autorelease_count_for_object_np(id);
#ifdef __cplusplus
}
#endif
#endif // __OBJC_ARC_INCLUDED__

@ -198,7 +198,11 @@ typedef struct
#ifdef __GNUC
# define _OBJC_NULL_PTR __null
#elif defined(__cplusplus)
# if __has_feature(cxx_nullptr)
# define _OBJC_NULL_PTR nullptr
# else
# define _OBJC_NULL_PTR 0
# endif
#else
# define _OBJC_NULL_PTR ((void*)0)
#endif

@ -54,7 +54,7 @@
push %ecx # _cmd
push %eax # &self
.cfi_def_cfa_offset 12
call slowMsgLookup@PLT
call CDECL(slowMsgLookup)@PLT
add $8, %esp # restore the stack
@ -65,8 +65,14 @@
7:
popl %ebx;
8:
#if __ELF__
# ELF can support GOT-relative addressing;
# PE/COFF and Mach-O need a text relocation.
addl $_GLOBAL_OFFSET_TABLE_+(8b-7b), %ebx
leal SmallObjectClasses@GOTOFF(%ebx), %eax
#else
leal CDECL(SmallObjectClasses), %eax
#endif
mov (%eax), %eax
popl %ebx
jmp 1b

@ -5,8 +5,18 @@ extern "C" {
* Allocates a C++ exception. This function is part of the Itanium C++ ABI and
* is provided externally.
*/
/*
* Note: Recent versions of libsupc++ already provide a prototype for
* __cxa__allocate_exception(). Since the libsupc++ version is defined with
* _GLIBCXX_NOTHROW, clang gives a type mismatch error.
*/
#ifndef __cplusplus
#undef CXA_ALLOCATE_EXCEPTION_SPECIFIER
#define CXA_ALLOCATE_EXCEPTION_SPECIFIER
#endif
__attribute__((weak))
void *__cxa_allocate_exception(size_t thrown_size);
void *__cxa_allocate_exception(size_t thrown_size) CXA_ALLOCATE_EXCEPTION_SPECIFIER;
/**
* Initialises an exception object returned by __cxa_allocate_exception() for
* storing an Objective-C object. The return value is the location of the

@ -21,6 +21,10 @@
#define CHECK_ARG(arg) if (0 == arg) { return 0; }
static inline void safe_remove_from_subclass_list(Class cls);
PRIVATE void objc_resolve_class(Class);
void objc_send_initialize(id object);
/**
* Calls C++ destructors in the correct order.
*/
@ -331,7 +335,9 @@ id class_createInstance(Class cls, size_t extraBytes)
}
if (Nil == cls) { return nil; }
assert(cls->instance_size >= sizeof(Class));
// Don't try to allocate an object of size 0, because there's no space for
// its isa pointer!
if (cls->instance_size < sizeof(Class)) { return nil; }
id obj = gc->allocate_class(cls, extraBytes);
obj->isa = cls;
checkARCAccessorsSlow(cls);
@ -488,9 +494,58 @@ Class class_setSuperclass(Class cls, Class newSuper)
{
CHECK_ARG(cls);
CHECK_ARG(newSuper);
Class oldSuper;
if (Nil == cls) { return Nil; }
Class oldSuper = cls->super_class;
{
LOCK_RUNTIME_FOR_SCOPE();
oldSuper = cls->super_class;
if (oldSuper == newSuper) { return newSuper; }
safe_remove_from_subclass_list(cls);
objc_resolve_class(newSuper);
cls->super_class = newSuper;
// The super class's subclass list is used in certain method resolution scenarios.
cls->sibling_class = cls->super_class->subclass_list;
cls->super_class->subclass_list = cls;
if (UNLIKELY(class_isMetaClass(cls)))
{
// newSuper is presumably a metaclass. Its isa will therefore be the appropriate root metaclass.
cls->isa = newSuper->isa;
}
else
{
Class meta = cls->isa, newSuperMeta = newSuper->isa;
// Update the metaclass's superclass.
safe_remove_from_subclass_list(meta);
objc_resolve_class(newSuperMeta);
meta->super_class = newSuperMeta;
meta->isa = newSuperMeta->isa;
// The super class's subclass list is used in certain method resolution scenarios.
meta->sibling_class = newSuperMeta->subclass_list;
newSuperMeta->subclass_list = meta;
}
LOCK_FOR_SCOPE(&initialize_lock);
if (!objc_test_class_flag(cls, objc_class_flag_initialized))
{
// Uninitialized classes don't have dtables to update
// and don't need their superclasses initialized.
return oldSuper;
}
}
objc_send_initialize((id)newSuper); // also initializes the metaclass
objc_update_dtable_for_new_superclass(cls->isa, newSuper->isa);
objc_update_dtable_for_new_superclass(cls, newSuper);
return oldSuper;
}
@ -756,8 +811,6 @@ const char *object_getClassName(id obj)
return class_getName(object_getClass(obj));
}
PRIVATE void objc_resolve_class(Class);
void objc_registerClassPair(Class cls)
{
if (cls->ivars != NULL)

@ -0,0 +1,20 @@
#ifndef __LIBOBJC_SAFEWINDOWS_H_INCLUDED__
#define __LIBOBJC_SAFEWINDOWS_H_INCLUDED__
#pragma push_macro("BOOL")
#ifdef BOOL
#undef BOOL
#endif
#define BOOL _WINBOOL
#include <Windows.h>
// Windows.h defines interface -> struct
#ifdef interface
#undef interface
#endif
#pragma pop_macro("BOOL")
#endif // __LIBOBJC_SAFEWINDOWS_H_INCLUDED__

@ -1,5 +1,5 @@
#ifdef __MINGW32__
#include <windows.h>
#ifdef _WIN32
#include "safewindows.h"
static unsigned sleep(unsigned seconds)
{
Sleep(seconds*1000);

@ -17,7 +17,7 @@
# define ASSERT(x) assert(x)
#else
# define UNREACHABLE(x) __builtin_unreachable()
# define ASSERT(x) do { if (x) __builtin_unreachable(); } while(0)
# define ASSERT(x) do { if (!(x)) __builtin_unreachable(); } while(0)
#endif
#define LIKELY(x) __builtin_expect(x, 1)

Loading…
Cancel
Save