From 3f38f691afaf1c552722f30c161111e8ac4c3310 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Tue, 30 Jul 2019 13:41:16 +0100 Subject: [PATCH 1/8] Rework ARC code as C++. Move the weak references hash table to use a well-tested third-party implementation instead of the hand-rolled version. --- .gitmodules | 3 + CMakeLists.txt | 12 ++- arc.m => arc.mm | 185 +++++++++++++++++++++++------------------- class.h | 36 ++++---- third_party/robin-map | 1 + 5 files changed, 134 insertions(+), 103 deletions(-) create mode 100644 .gitmodules rename arc.m => arc.mm (85%) create mode 160000 third_party/robin-map diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5f12667 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "third_party/robin-map"] + path = third_party/robin-map + url = https://github.com/Tessil/robin-map diff --git a/CMakeLists.txt b/CMakeLists.txt index beacc22..dddce40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,10 +25,12 @@ add_definitions( -DGNUSTEP -D__OBJC_RUNTIME_INTERNAL__=1) set(libobjc_ASM_SRCS block_trampolines.S objc_msgSend.S) +set(libobjc_OBJCXX_SRCS + arc.mm + ) set(libobjc_OBJC_SRCS NSBlocks.m Protocol2.m - arc.m associate.m blocks_runtime.m properties.m) @@ -193,6 +195,12 @@ set_source_files_properties( COMPILE_FLAGS "${CMAKE_OBJC_FLAGS} -Xclang -x -Xclang objective-c" ) +set_source_files_properties( + ${libobjc_OBJCXX_SRCS} + COMPILE_FLAGS "${CMAKE_OBJC_FLAGS} -Xclang -x -Xclang objective-c++" +) + + # # C++ Runtime interaction # @@ -252,7 +260,7 @@ if (MSVC) endif() -add_library(objc SHARED ${libobjc_C_SRCS} ${libobjc_ASM_SRCS} ${libobjc_OBJC_SRCS} ${libobjc_ASM_OBJS}) +add_library(objc SHARED ${libobjc_C_SRCS} ${libobjc_ASM_SRCS} ${libobjc_OBJC_SRCS} ${libobjc_OBJCXX_SRCS} ${libobjc_ASM_OBJS}) if (ENABLE_OBJCXX) if (WIN32) diff --git a/arc.m b/arc.mm similarity index 85% rename from arc.m rename to arc.mm index 8c47e2d..9e34caa 100644 --- a/arc.m +++ b/arc.mm @@ -1,7 +1,9 @@ +#define TSL_NO_EXCEPTIONS 1 +#include #include -#include #include -#import "stdio.h" +#include "third_party/robin-map/include/tsl/robin_map.h" +#import "lock.h" #import "objc/runtime.h" #import "objc/blocks_runtime.h" #import "nsobject.h" @@ -12,7 +14,7 @@ #import "objc/objc-arc.h" #import "objc/blocks_runtime.h" -id (*_objc_weak_load)(id object); +extern "C" id (*_objc_weak_load)(id object); #if defined(_WIN32) // We're using the Fiber-Local Storage APIs on Windows @@ -54,9 +56,9 @@ static inline arc_tls_key_t arc_tls_key_create(arc_cleanup_function_t cleanupFun arc_tls_key_t ARCThreadKey; #endif -extern void _NSConcreteMallocBlock; -extern void _NSConcreteStackBlock; -extern void _NSConcreteGlobalBlock; +extern struct objc_class _NSConcreteMallocBlock; +extern struct objc_class _NSConcreteStackBlock; +extern struct objc_class _NSConcreteGlobalBlock; @interface NSAutoreleasePool + (Class)class; @@ -95,15 +97,25 @@ struct arc_tls id returnRetained; }; +/** + * Type-safe wrapper around calloc. + */ +template +static inline T* new_zeroed() +{ + return static_cast(calloc(sizeof(T), 1)); +} + + static inline struct arc_tls* getARCThreadData(void) { #ifndef arc_tls_store return NULL; #else // !defined arc_tls_store - struct arc_tls *tls = arc_tls_load(ARCThreadKey); + auto tls = static_cast(arc_tls_load(ARCThreadKey)); if (NULL == tls) { - tls = calloc(sizeof(struct arc_tls), 1); + tls = new_zeroed(); arc_tls_store(ARCThreadKey, tls); } return tls; @@ -115,7 +127,7 @@ static inline void release(id obj); * Empties objects from the autorelease pool, stating at the head of the list * specified by pool and continuing until it reaches the stop point. If the stop point is NULL then */ -static void emptyPool(struct arc_tls *tls, id *stop) +static void emptyPool(struct arc_tls *tls, void *stop) { struct arc_autorelease_pool *stopPool = NULL; if (NULL != stop) @@ -211,14 +223,14 @@ static const size_t weak_mask = ((size_t)1)<<((sizeof(size_t)*8)-refcount_shift) */ static const size_t refcount_mask = ~weak_mask; -OBJC_PUBLIC size_t object_getRetainCount_np(id obj) +extern "C" OBJC_PUBLIC size_t object_getRetainCount_np(id obj) { uintptr_t *refCount = ((uintptr_t*)obj) - 1; uintptr_t refCountVal = __sync_fetch_and_add(refCount, 0); return (((size_t)refCountVal) & refcount_mask) + 1; } -OBJC_PUBLIC id objc_retain_fast_np(id obj) +extern "C" OBJC_PUBLIC id objc_retain_fast_np(id obj) { uintptr_t *refCount = ((uintptr_t*)obj) - 1; uintptr_t refCountVal = __sync_fetch_and_add(refCount, 0); @@ -291,7 +303,7 @@ static inline id retain(id obj) return [obj retain]; } -OBJC_PUBLIC BOOL objc_release_fast_no_destroy_np(id obj) +extern "C" OBJC_PUBLIC BOOL objc_release_fast_no_destroy_np(id obj) { uintptr_t *refCount = ((uintptr_t*)obj) - 1; uintptr_t refCountVal = __sync_fetch_and_add(refCount, 0); @@ -329,7 +341,7 @@ OBJC_PUBLIC BOOL objc_release_fast_no_destroy_np(id obj) return NO; } -OBJC_PUBLIC void objc_release_fast_np(id obj) +extern "C" OBJC_PUBLIC void objc_release_fast_np(id obj) { if (objc_release_fast_no_destroy_np(obj)) { @@ -341,12 +353,12 @@ static inline void release(id obj) { if (isPersistentObject(obj)) { return; } Class cls = obj->isa; - if (cls == &_NSConcreteMallocBlock) + if (cls == static_cast(&_NSConcreteMallocBlock)) { _Block_release(obj); return; } - if (cls == &_NSConcreteStackBlock) + if (cls == static_cast(&_NSConcreteStackBlock)) { return; } @@ -396,7 +408,7 @@ static inline id autorelease(id obj) struct arc_autorelease_pool *pool = tls->pool; if (NULL == pool || (pool->insert >= &pool->pool[POOL_SIZE])) { - pool = calloc(sizeof(struct arc_autorelease_pool), 1); + pool = new_zeroed(); pool->previous = tls->pool; pool->insert = pool->pool; tls->pool = pool; @@ -418,7 +430,7 @@ static inline id autorelease(id obj) return [obj autorelease]; } -OBJC_PUBLIC unsigned long objc_arc_autorelease_count_np(void) +extern "C" OBJC_PUBLIC unsigned long objc_arc_autorelease_count_np(void) { struct arc_tls* tls = getARCThreadData(); unsigned long count = 0; @@ -432,7 +444,7 @@ OBJC_PUBLIC unsigned long objc_arc_autorelease_count_np(void) } return count; } -OBJC_PUBLIC unsigned long objc_arc_autorelease_count_for_object_np(id obj) +extern "C" OBJC_PUBLIC unsigned long objc_arc_autorelease_count_for_object_np(id obj) { struct arc_tls* tls = getARCThreadData(); unsigned long count = 0; @@ -453,7 +465,7 @@ OBJC_PUBLIC unsigned long objc_arc_autorelease_count_for_object_np(id obj) return count; } -void *objc_autoreleasePoolPush(void) +extern "C" OBJC_PUBLIC void *objc_autoreleasePoolPush(void) { initAutorelease(); struct arc_tls* tls = getARCThreadData(); @@ -472,7 +484,7 @@ void *objc_autoreleasePoolPush(void) struct arc_autorelease_pool *pool = tls->pool; if (NULL == pool || (pool->insert >= &pool->pool[POOL_SIZE])) { - pool = calloc(sizeof(struct arc_autorelease_pool), 1); + pool = new_zeroed(); pool->previous = tls->pool; pool->insert = pool->pool; tls->pool = pool; @@ -486,7 +498,7 @@ void *objc_autoreleasePoolPush(void) if (0 == NewAutoreleasePool) { return NULL; } return NewAutoreleasePool(AutoreleasePool, SELECTOR(new)); } -OBJC_PUBLIC void objc_autoreleasePoolPop(void *pool) +extern "C" OBJC_PUBLIC void objc_autoreleasePoolPop(void *pool) { if (useARCAutoreleasePool) { @@ -500,7 +512,7 @@ OBJC_PUBLIC void objc_autoreleasePoolPop(void *pool) return; } } - DeleteAutoreleasePool(pool, SELECTOR(release)); + DeleteAutoreleasePool(static_cast(pool), SELECTOR(release)); struct arc_tls* tls = getARCThreadData(); if (tls && tls->returnRetained) { @@ -509,7 +521,7 @@ OBJC_PUBLIC void objc_autoreleasePoolPop(void *pool) } } -OBJC_PUBLIC id objc_autorelease(id obj) +extern "C" OBJC_PUBLIC id objc_autorelease(id obj) { if (nil != obj) { @@ -518,7 +530,7 @@ OBJC_PUBLIC id objc_autorelease(id obj) return obj; } -OBJC_PUBLIC id objc_autoreleaseReturnValue(id obj) +extern "C" OBJC_PUBLIC id objc_autoreleaseReturnValue(id obj) { if (!useARCAutoreleasePool) { @@ -533,7 +545,7 @@ OBJC_PUBLIC id objc_autoreleaseReturnValue(id obj) return objc_autorelease(obj); } -OBJC_PUBLIC id objc_retainAutoreleasedReturnValue(id obj) +extern "C" OBJC_PUBLIC id objc_retainAutoreleasedReturnValue(id obj) { // If the previous object was released with objc_autoreleaseReturnValue() // just before return, then it will not have actually been autoreleased. @@ -565,36 +577,36 @@ OBJC_PUBLIC id objc_retainAutoreleasedReturnValue(id obj) return objc_retain(obj); } -OBJC_PUBLIC id objc_retain(id obj) +extern "C" OBJC_PUBLIC id objc_retain(id obj) { if (nil == obj) { return nil; } return retain(obj); } -OBJC_PUBLIC id objc_retainAutorelease(id obj) +extern "C" OBJC_PUBLIC id objc_retainAutorelease(id obj) { return objc_autorelease(objc_retain(obj)); } -OBJC_PUBLIC id objc_retainAutoreleaseReturnValue(id obj) +extern "C" OBJC_PUBLIC id objc_retainAutoreleaseReturnValue(id obj) { if (nil == obj) { return obj; } return objc_autoreleaseReturnValue(retain(obj)); } -OBJC_PUBLIC id objc_retainBlock(id b) +extern "C" OBJC_PUBLIC id objc_retainBlock(id b) { - return _Block_copy(b); + return static_cast(_Block_copy(b)); } -OBJC_PUBLIC void objc_release(id obj) +extern "C" OBJC_PUBLIC void objc_release(id obj) { if (nil == obj) { return; } release(obj); } -OBJC_PUBLIC id objc_storeStrong(id *addr, id value) +extern "C" OBJC_PUBLIC id objc_storeStrong(id *addr, id value) { value = objc_retain(value); id oldValue = *addr; @@ -609,44 +621,53 @@ OBJC_PUBLIC id objc_storeStrong(id *addr, id value) static int weakref_class; -typedef struct objc_weak_ref -{ - void *isa; - id obj; - size_t weak_count; -} WeakRef; - +namespace { -static int weak_ref_compare(const id obj, const WeakRef *weak_ref) +struct WeakRef { - return obj == weak_ref->obj; -} + void *isa = &weakref_class; + id obj = nullptr; + size_t weak_count = 1; + WeakRef(id o) : obj(o) {} +}; -static uint32_t ptr_hash(const void *ptr) +template +struct malloc_allocator { - // Bit-rotate right 4, since the lowest few bits in an object pointer will - // always be 0, which is not so useful for a hash value - return ((uintptr_t)ptr >> 4) | ((uintptr_t)ptr << ((sizeof(id) * 8) - 4)); -} -static int weak_ref_hash(const WeakRef *weak_ref) + typedef T value_type; + T* allocate(std::size_t n) + { + return static_cast(malloc(sizeof(T) * n)); + } + void deallocate(T* p, std::size_t) + { + free(p); + } + template + operator malloc_allocator() const + { + return malloc_allocator(); + } +}; + +using weak_ref_table = tsl::robin_pg_map, + std::equal_to, + malloc_allocator>>; + +weak_ref_table &weakRefs() { - return ptr_hash(weak_ref->obj); + static weak_ref_table w{128}; + return w; } -#define MAP_TABLE_NAME weak_ref -#define MAP_TABLE_COMPARE_FUNCTION weak_ref_compare -#define MAP_TABLE_HASH_KEY ptr_hash -#define MAP_TABLE_HASH_VALUE weak_ref_hash -#define MAP_TABLE_SINGLE_THREAD 1 -#define MAP_TABLE_NO_LOCK 1 -#include "hash_table.h" - -static weak_ref_table *weakRefs; mutex_t weakRefLock; -PRIVATE void init_arc(void) +} + +PRIVATE extern "C" void init_arc(void) { - weak_ref_initialize(&weakRefs, 128); INIT_LOCK(weakRefLock); #ifdef arc_tls_store ARCThreadKey = arc_tls_key_create((arc_cleanup_function_t)cleanupPools); @@ -686,14 +707,14 @@ static inline BOOL weakRefRelease(WeakRef *ref) ref->weak_count--; if (ref->weak_count == 0) { - weak_ref_remove(weakRefs, ref->obj); - free(ref); + weakRefs().erase(ref->obj); + delete ref; return YES; } return NO; } -void* block_load_weak(void *block); +extern "C" void* block_load_weak(void *block); static BOOL setObjectHasWeakRefs(id obj) { @@ -742,14 +763,10 @@ static BOOL setObjectHasWeakRefs(id obj) WeakRef *incrementWeakRefCount(id obj) { - WeakRef *ref = weak_ref_table_get(weakRefs, obj); - if (ref == NULL) + WeakRef *&ref = weakRefs()[obj]; + if (ref == nullptr) { - ref = calloc(1, sizeof(WeakRef)); - ref->isa = (Class)&weakref_class; - ref->obj = obj; - ref->weak_count = 1; - weak_ref_insert(weakRefs, ref); + ref = new WeakRef(obj); } else { @@ -759,7 +776,7 @@ WeakRef *incrementWeakRefCount(id obj) return ref; } -OBJC_PUBLIC id objc_storeWeak(id *addr, id obj) +extern "C" OBJC_PUBLIC id objc_storeWeak(id *addr, id obj) { LOCK_FOR_SCOPE(&weakRefLock); WeakRef *oldRef; @@ -797,7 +814,7 @@ OBJC_PUBLIC id objc_storeWeak(id *addr, id obj) return obj; } -OBJC_PUBLIC BOOL objc_delete_weak_refs(id obj) +extern "C" OBJC_PUBLIC BOOL objc_delete_weak_refs(id obj) { LOCK_FOR_SCOPE(&weakRefLock); if (objc_test_class_flag(classForObject(obj), objc_class_flag_fast_arc)) @@ -816,13 +833,15 @@ OBJC_PUBLIC BOOL objc_delete_weak_refs(id obj) return NO; } } - WeakRef *oldRef = weak_ref_table_get(weakRefs, obj); - if (0 != oldRef) + auto table = weakRefs(); + auto old = table.find(obj); + if (old != table.end()) { + WeakRef *oldRef = old->second; // The address of obj is likely to be reused, so remove it from // the table so that we don't accidentally alias weak // references - weak_ref_remove(weakRefs, obj); + table.erase(old); // Zero the object pointer. This prevents any other weak // accesses from loading from this. This must be done after // removing the ref from the table, because the compare operation @@ -835,7 +854,7 @@ OBJC_PUBLIC BOOL objc_delete_weak_refs(id obj) return YES; } -OBJC_PUBLIC id objc_loadWeakRetained(id* addr) +extern "C" OBJC_PUBLIC id objc_loadWeakRetained(id* addr) { LOCK_FOR_SCOPE(&weakRefLock); id obj; @@ -858,9 +877,9 @@ OBJC_PUBLIC id objc_loadWeakRetained(id* addr) return nil; } Class cls = classForObject(obj); - if (&_NSConcreteMallocBlock == cls) + if (static_cast(&_NSConcreteMallocBlock) == cls) { - obj = block_load_weak(obj); + obj = static_cast(block_load_weak(obj)); } else if (objc_test_class_flag(cls, objc_class_flag_permanent_instances)) { @@ -873,12 +892,12 @@ OBJC_PUBLIC id objc_loadWeakRetained(id* addr) return objc_retain(obj); } -OBJC_PUBLIC id objc_loadWeak(id* object) +extern "C" OBJC_PUBLIC id objc_loadWeak(id* object) { return objc_autorelease(objc_loadWeakRetained(object)); } -OBJC_PUBLIC void objc_copyWeak(id *dest, id *src) +extern "C" OBJC_PUBLIC void objc_copyWeak(id *dest, id *src) { // Don't retain or release. // `src` is a valid pointer to a __weak pointer or nil. @@ -895,7 +914,7 @@ OBJC_PUBLIC void objc_copyWeak(id *dest, id *src) } } -OBJC_PUBLIC void objc_moveWeak(id *dest, id *src) +extern "C" OBJC_PUBLIC void objc_moveWeak(id *dest, id *src) { // Don't retain or release. // `dest` is a valid pointer to uninitialized memory. @@ -912,7 +931,7 @@ OBJC_PUBLIC void objc_moveWeak(id *dest, id *src) *src = nil; } -OBJC_PUBLIC void objc_destroyWeak(id* obj) +extern "C" OBJC_PUBLIC void objc_destroyWeak(id* obj) { LOCK_FOR_SCOPE(&weakRefLock); WeakRef *oldRef; @@ -926,7 +945,7 @@ OBJC_PUBLIC void objc_destroyWeak(id* obj) } } -OBJC_PUBLIC id objc_initWeak(id *addr, id obj) +extern "C" OBJC_PUBLIC id objc_initWeak(id *addr, id obj) { if (obj == nil) { diff --git a/class.h b/class.h index 44ed65f..8aa17ba 100644 --- a/class.h +++ b/class.h @@ -1,6 +1,7 @@ #ifndef __OBJC_CLASS_H_INCLUDED #define __OBJC_CLASS_H_INCLUDED #include "visibility.h" +#include "objc/runtime.h" #include /** @@ -36,7 +37,6 @@ static inline BOOL objc_bitfield_test(uintptr_t bitfield, uint64_t field) return (bf->values[byte] & bit) == bit; } - // begin: objc_class struct objc_class { @@ -45,13 +45,13 @@ struct objc_class * methods use when a message is sent to the class, rather than an * instance. */ - struct objc_class *isa; + Class isa; /** * Pointer to the superclass. The compiler will set this to the name of * the superclass, the runtime will initialize it to point to the real * class. */ - struct objc_class *super_class; + Class super_class; /** * The name of this class. Set to the same value for both the class and * its associated metaclass. @@ -96,7 +96,7 @@ struct objc_class * A pointer to the first subclass for this class. Filled in by the * runtime. */ - struct objc_class *subclass_list; + Class subclass_list; /** * Pointer to the .cxx_construct method if one exists. This method needs * to be called outside of the normal dispatch mechanism. @@ -113,7 +113,7 @@ struct objc_class * then subsequently following the sibling_class pointers in the * subclasses. */ - struct objc_class *sibling_class; + Class sibling_class; /** * Metadata describing the protocols adopted by this class. Not used by @@ -143,13 +143,13 @@ struct objc_class_gsv1 * methods use when a message is sent to the class, rather than an * instance. */ - struct objc_class *isa; + Class isa; /** * Pointer to the superclass. The compiler will set this to the name of * the superclass, the runtime will initialize it to point to the real * class. */ - struct objc_class *super_class; + Class super_class; /** * The name of this class. Set to the same value for both the class and * its associated metaclass. @@ -194,14 +194,14 @@ struct objc_class_gsv1 * A pointer to the first subclass for this class. Filled in by the * runtime. */ - struct objc_class *subclass_list; + Class subclass_list; /** * A pointer to the next sibling class to this. You may find all * subclasses of a given class by following the subclass_list pointer and * then subsequently following the sibling_class pointers in the * subclasses. */ - struct objc_class *sibling_class; + Class sibling_class; /** * Metadata describing the protocols adopted by this class. Not used by @@ -277,17 +277,17 @@ struct objc_class_gsv1 */ struct objc_class_gcc { - struct objc_class *isa; - struct objc_class *super_class; + Class isa; + Class super_class; const char *name; long version; unsigned long info; long instance_size; - struct objc_ivar_list_gcc *ivars; + struct objc_ivar_list_gcc *ivars; struct objc_method_list *methods; void *dtable; - struct objc_class *subclass_list; - struct objc_class *sibling_class; + Class subclass_list; + Class sibling_class; struct objc_protocol_list *protocols; void *gc_object_type; }; @@ -357,7 +357,7 @@ enum objc_class_flags /** * Sets the specific class flag. Note: This is not atomic. */ -static inline void objc_set_class_flag(struct objc_class *aClass, +static inline void objc_set_class_flag(Class aClass, enum objc_class_flags flag) { aClass->info |= (unsigned long)flag; @@ -365,7 +365,7 @@ static inline void objc_set_class_flag(struct objc_class *aClass, /** * Unsets the specific class flag. Note: This is not atomic. */ -static inline void objc_clear_class_flag(struct objc_class *aClass, +static inline void objc_clear_class_flag(Class aClass, enum objc_class_flags flag) { aClass->info &= ~(unsigned long)flag; @@ -373,7 +373,7 @@ static inline void objc_clear_class_flag(struct objc_class *aClass, /** * Checks whether a specific class flag is set. */ -static inline BOOL objc_test_class_flag(struct objc_class *aClass, +static inline BOOL objc_test_class_flag(Class aClass, enum objc_class_flags flag) { return (aClass->info & (unsigned long)flag) == (unsigned long)flag; @@ -383,7 +383,7 @@ static inline BOOL objc_test_class_flag(struct objc_class *aClass, /** * Adds a class to the class table. */ -void class_table_insert(Class class); +void class_table_insert(Class cls); /** * Removes a class from the class table. Must be called with the runtime lock diff --git a/third_party/robin-map b/third_party/robin-map new file mode 160000 index 0000000..757de82 --- /dev/null +++ b/third_party/robin-map @@ -0,0 +1 @@ +Subproject commit 757de829927489bee55ab02147484850c687b620 From de9f74015f16aecaa4b29ed3a102b5a6321e1c67 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Tue, 30 Jul 2019 13:47:28 +0100 Subject: [PATCH 2/8] Fix warning. --- blocks_runtime.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/blocks_runtime.m b/blocks_runtime.m index 7be65c9..9129317 100644 --- a/blocks_runtime.m +++ b/blocks_runtime.m @@ -62,7 +62,7 @@ OBJC_PUBLIC const char * _Block_signature(void *b) } OBJC_PUBLIC const char *block_getType_np(const void *b) { - return _Block_signature(b); + return _Block_signature((void*)b); } static int increment24(int *ref) From 824135cc54b5bba29de610330687d39927f549c7 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Tue, 30 Jul 2019 13:52:14 +0100 Subject: [PATCH 3/8] Tell Azure CI to get submodules. --- azure-pipelines.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8ebec39..13940c2 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -10,6 +10,8 @@ jobs: Release: BuildType: Release steps: + - checkout: self + submodules: true - script: | sudo add-apt-repository "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-8 main" sudo apt-get update @@ -55,6 +57,8 @@ jobs: Arch: x64 Flags: -m64 steps: + - checkout: self + submodules: true - script: | choco.exe install ninja choco.exe install llvm From a04d0e85472f934ee506ffa1a050aa247e345dbc Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Tue, 30 Jul 2019 14:04:52 +0100 Subject: [PATCH 4/8] Attempt to make submodules work with cirrus. --- .cirrus.yml | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/.cirrus.yml b/.cirrus.yml index 9bb55f9..fd9ee1e 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -2,7 +2,18 @@ freebsd_instance: image: freebsd-12-0-release-amd64 task: - install_script: pkg install -y cmake ninja llvm80 + install_script: pkg install -y cmake ninja llvm80 git + + clone_script: | + if [ -z "$CIRRUS_PR" ]; then + git clone --recursive --branch=$CIRRUS_BRANCH https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR + git reset --hard $CIRRUS_CHANGE_IN_REPO + else + git clone --recursive https://x-access-token:${CIRRUS_REPO_CLONE_TOKEN}@github.com/${CIRRUS_REPO_FULL_NAME}.git $CIRRUS_WORKING_DIR + git fetch origin pull/$CIRRUS_PR/head:pull/$CIRRUS_PR + git reset --hard $CIRRUS_CHANGE_IN_REPO + fi + script: | mkdir Build cd Build From af55c290079f207144ee3e836244a0fda6d25f09 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Tue, 30 Jul 2019 14:28:59 +0100 Subject: [PATCH 5/8] Fix the build on Windows. --- arc.mm | 22 +++++++++++++++++++--- class.h | 8 ++++++++ 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/arc.mm b/arc.mm index 9e34caa..4070017 100644 --- a/arc.mm +++ b/arc.mm @@ -56,9 +56,12 @@ static inline arc_tls_key_t arc_tls_key_create(arc_cleanup_function_t cleanupFun arc_tls_key_t ARCThreadKey; #endif -extern struct objc_class _NSConcreteMallocBlock; -extern struct objc_class _NSConcreteStackBlock; -extern struct objc_class _NSConcreteGlobalBlock; +extern "C" +{ + extern struct objc_class _NSConcreteMallocBlock; + extern struct objc_class _NSConcreteStackBlock; + extern struct objc_class _NSConcreteGlobalBlock; +} @interface NSAutoreleasePool + (Class)class; @@ -639,10 +642,23 @@ struct malloc_allocator { return static_cast(malloc(sizeof(T) * n)); } + void deallocate(T* p, std::size_t) { free(p); } + + template + malloc_allocator &operator=(const malloc_allocator&) const + { + return *this; + } + + bool operator==(const malloc_allocator &) const + { + return true; + } + template operator malloc_allocator() const { diff --git a/class.h b/class.h index 8aa17ba..20e8cf9 100644 --- a/class.h +++ b/class.h @@ -4,6 +4,11 @@ #include "objc/runtime.h" #include +#ifdef __cplusplus +extern "C" +{ +#endif + /** * Overflow bitfield. Used for bitfields that are more than 63 bits. */ @@ -443,4 +448,7 @@ void freeIvarLists(Class aClass); */ void freeMethodLists(Class aClass); +#ifdef __cplusplus +} // extern "C" +#endif #endif //__OBJC_CLASS_H_INCLUDED From 066a4e65a9810c5f1f74228a474080efec1f91e4 Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Tue, 30 Jul 2019 14:37:17 +0100 Subject: [PATCH 6/8] Another go at fixing Cirrus... --- .cirrus.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.cirrus.yml b/.cirrus.yml index fd9ee1e..a868eeb 100644 --- a/.cirrus.yml +++ b/.cirrus.yml @@ -13,6 +13,8 @@ task: git fetch origin pull/$CIRRUS_PR/head:pull/$CIRRUS_PR git reset --hard $CIRRUS_CHANGE_IN_REPO fi + git submodule sync + git submodule update script: | mkdir Build From 336d8a828c36a5b8ae6f31a54ae660fc5774e8cd Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Wed, 31 Jul 2019 10:38:43 +0100 Subject: [PATCH 7/8] Fix test failure. This bug is also present in the original version: When removing a WeakRef from the map, we use its obj field to find the key, but the obj field has already been zeroed by this point and so we end up leaving dangling pointers in the map. --- arc.mm | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/arc.mm b/arc.mm index 4070017..4873fa8 100644 --- a/arc.mm +++ b/arc.mm @@ -629,9 +629,21 @@ namespace { struct WeakRef { void *isa = &weakref_class; - id obj = nullptr; - size_t weak_count = 1; - WeakRef(id o) : obj(o) {} + id object; + struct + { + size_t weak_count:((sizeof(size_t) * 8) - 1); + bool isDeleted:1; + }; + id obj() + { + if (isDeleted) + { + return nil; + } + return object; + } + WeakRef(id o) : object(o), weak_count(1), isDeleted(false) {} }; template @@ -709,7 +721,7 @@ static BOOL loadWeakPointer(id *addr, id *obj, WeakRef **ref) if (classForObject(oldObj) == (Class)&weakref_class) { *ref = (WeakRef*)oldObj; - *obj = (*ref)->obj; + *obj = (*ref)->obj(); return YES; } *ref = NULL; @@ -723,7 +735,7 @@ static inline BOOL weakRefRelease(WeakRef *ref) ref->weak_count--; if (ref->weak_count == 0) { - weakRefs().erase(ref->obj); + weakRefs().erase(ref->object); delete ref; return YES; } @@ -786,7 +798,7 @@ WeakRef *incrementWeakRefCount(id obj) } else { - assert(ref->obj == obj); + assert(ref->obj() == obj); ref->weak_count++; } return ref; @@ -862,7 +874,7 @@ extern "C" OBJC_PUBLIC BOOL objc_delete_weak_refs(id obj) // accesses from loading from this. This must be done after // removing the ref from the table, because the compare operation // tests the obj field. - oldRef->obj = nil; + oldRef->isDeleted = true; // If the weak reference count is zero, then we should have // already removed this. assert(oldRef->weak_count > 0); From 75ad9243a1237e74a1a873a2a2cf00877daba34e Mon Sep 17 00:00:00 2001 From: David Chisnall Date: Sat, 23 Nov 2019 13:17:46 +0000 Subject: [PATCH 8/8] Prevent libc++ from using exceptions. --- arc.mm | 1 + 1 file changed, 1 insertion(+) diff --git a/arc.mm b/arc.mm index 8a504dc..d653e55 100644 --- a/arc.mm +++ b/arc.mm @@ -1,3 +1,4 @@ +#define _LIBCPP_NO_EXCEPTIONS 1 #define TSL_NO_EXCEPTIONS 1 #include #include