From: Apple Date: Tue, 24 Mar 2020 21:39:08 +0000 (+0000) Subject: objc4-781.tar.gz X-Git-Tag: macos-10151^0 X-Git-Url: https://git.saurik.com/apple/objc4.git/commitdiff_plain/f192a3e2001d25ba840f5549faa27485f61dbe9e objc4-781.tar.gz --- diff --git a/objc.xcodeproj/project.pbxproj b/objc.xcodeproj/project.pbxproj index 3c3853f..80c47fb 100644 --- a/objc.xcodeproj/project.pbxproj +++ b/objc.xcodeproj/project.pbxproj @@ -41,6 +41,9 @@ 6E1475EC21DFDB1B001357EA /* llvm-DenseMapInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E1475E721DFDB1B001357EA /* llvm-DenseMapInfo.h */; }; 6E1475ED21DFDB1B001357EA /* llvm-type_traits.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E1475E821DFDB1B001357EA /* llvm-type_traits.h */; }; 6E1475EE21DFDB1B001357EA /* llvm-MathExtras.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E1475E921DFDB1B001357EA /* llvm-MathExtras.h */; }; + 6E7B0862232DE7CA00689009 /* PointerUnion.h in Headers */ = {isa = PBXBuildFile; fileRef = 6E7B0861232DE7CA00689009 /* PointerUnion.h */; }; + 6EACB842232C97A400CE9176 /* objc-zalloc.h in Headers */ = {isa = PBXBuildFile; fileRef = 6EACB841232C97A400CE9176 /* objc-zalloc.h */; }; + 6EACB844232C97B900CE9176 /* objc-zalloc.mm in Sources */ = {isa = PBXBuildFile; fileRef = 6EACB843232C97B900CE9176 /* objc-zalloc.mm */; }; 6ECD0B1F2244999E00910D88 /* llvm-DenseSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 6ECD0B1E2244999E00910D88 /* llvm-DenseSet.h */; }; 7213C36321FA7C730090A271 /* NSObject-internal.h in Headers */ = {isa = PBXBuildFile; fileRef = 7213C36221FA7C730090A271 /* NSObject-internal.h */; settings = {ATTRIBUTES = (Private, ); }; }; 7593EC58202248E50046AB96 /* objc-object.h in Headers */ = {isa = PBXBuildFile; fileRef = 7593EC57202248DF0046AB96 /* objc-object.h */; }; @@ -157,6 +160,9 @@ 6E1475E721DFDB1B001357EA /* llvm-DenseMapInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = "llvm-DenseMapInfo.h"; path = "runtime/llvm-DenseMapInfo.h"; sourceTree = ""; tabWidth = 2; }; 6E1475E821DFDB1B001357EA /* llvm-type_traits.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = "llvm-type_traits.h"; path = "runtime/llvm-type_traits.h"; sourceTree = ""; tabWidth = 2; }; 6E1475E921DFDB1B001357EA /* llvm-MathExtras.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = "llvm-MathExtras.h"; path = "runtime/llvm-MathExtras.h"; sourceTree = ""; tabWidth = 2; }; + 6E7B0861232DE7CA00689009 /* PointerUnion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PointerUnion.h; path = runtime/PointerUnion.h; sourceTree = ""; }; + 6EACB841232C97A400CE9176 /* objc-zalloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-zalloc.h"; path = "runtime/objc-zalloc.h"; sourceTree = ""; }; + 6EACB843232C97B900CE9176 /* objc-zalloc.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-zalloc.mm"; path = "runtime/objc-zalloc.mm"; sourceTree = ""; }; 6ECD0B1E2244999E00910D88 /* llvm-DenseSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "llvm-DenseSet.h"; path = "runtime/llvm-DenseSet.h"; sourceTree = ""; }; 7213C36221FA7C730090A271 /* NSObject-internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject-internal.h"; path = "runtime/NSObject-internal.h"; sourceTree = ""; }; 7593EC57202248DF0046AB96 /* objc-object.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "objc-object.h"; path = "runtime/objc-object.h"; sourceTree = ""; }; @@ -299,6 +305,7 @@ 830F2A930D73876100392440 /* objc-accessors.mm */, 838485CA0D6D68A200CEA253 /* objc-auto.mm */, 39ABD72012F0B61800D1054C /* objc-weak.mm */, + 6EACB843232C97B900CE9176 /* objc-zalloc.mm */, E8923DA0116AB2820071B552 /* objc-block-trampolines.mm */, 838485CB0D6D68A200CEA253 /* objc-cache.mm */, 83F550DF155E030800E95D3B /* objc-cache-old.mm */, @@ -426,6 +433,7 @@ 6E1475E921DFDB1B001357EA /* llvm-MathExtras.h */, 6E1475E821DFDB1B001357EA /* llvm-type_traits.h */, C2E6D3FB2225DCF00059DFAA /* DenseMapExtras.h */, + 6E7B0861232DE7CA00689009 /* PointerUnion.h */, 83D9269721225A7400299F69 /* arm64-asm.h */, 83D92695212254CF00299F69 /* isa.h */, 838485CF0D6D68A200CEA253 /* objc-config.h */, @@ -444,6 +452,7 @@ 83BE02E70FCCB24D00661494 /* objc-runtime-old.h */, 838485E50D6D68A200CEA253 /* objc-sel-set.h */, 39ABD71F12F0B61800D1054C /* objc-weak.h */, + 6EACB841232C97A400CE9176 /* objc-zalloc.h */, ); name = "Project Headers"; sourceTree = ""; @@ -475,6 +484,7 @@ 6E1475ED21DFDB1B001357EA /* llvm-type_traits.h in Headers */, 83A4AEDC1EA0840800ACADDE /* module.modulemap in Headers */, 830F2A980D738DC200392440 /* hashtable.h in Headers */, + 6EACB842232C97A400CE9176 /* objc-zalloc.h in Headers */, 6E1475EA21DFDB1B001357EA /* llvm-AlignOf.h in Headers */, 838485BF0D6D687300CEA253 /* hashtable2.h in Headers */, 6E1475EC21DFDB1B001357EA /* llvm-DenseMapInfo.h in Headers */, @@ -507,6 +517,7 @@ 83BE02EA0FCCB24D00661494 /* objc-runtime-old.h in Headers */, 8384860A0D6D68A200CEA253 /* objc-runtime.h in Headers */, 8384860C0D6D68A200CEA253 /* objc-sel-set.h in Headers */, + 6E7B0862232DE7CA00689009 /* PointerUnion.h in Headers */, 7213C36321FA7C730090A271 /* NSObject-internal.h in Headers */, 838486100D6D68A200CEA253 /* objc-sync.h in Headers */, 83D92696212254CF00299F69 /* isa.h in Headers */, @@ -697,6 +708,7 @@ 83D49E4F13C7C84F0057F1DD /* objc-msg-arm64.s in Sources */, 9672F7EE14D5F488007CEC96 /* NSObject.mm in Sources */, 83725F4A14CA5BFA0014370E /* objc-opt.mm in Sources */, + 6EACB844232C97B900CE9176 /* objc-zalloc.mm in Sources */, 83F550E0155E030800E95D3B /* objc-cache-old.mm in Sources */, 834DF8B715993EE1002F2BC9 /* objc-sel-old.mm in Sources */, 83C9C3391668B50E00F4E544 /* objc-msg-simulator-x86_64.s in Sources */, @@ -922,7 +934,7 @@ "-D_LIBCPP_VISIBLE=\"\"", ); SDKROOT = macosx.internal; - SUPPORTED_PLATFORMS = "macosx iphoneos"; + SUPPORTED_PLATFORMS = "macosx iphoneos appletvos watchos"; WARNING_CFLAGS = ( "-Wall", "-Wextra", @@ -970,7 +982,7 @@ "-D_LIBCPP_VISIBLE=\"\"", ); SDKROOT = macosx.internal; - SUPPORTED_PLATFORMS = "macosx iphoneos"; + SUPPORTED_PLATFORMS = "macosx iphoneos appletvos watchos"; WARNING_CFLAGS = ( "-Wall", "-Wextra", diff --git a/runtime/NSObject.mm b/runtime/NSObject.mm index 6a56ec6..f672f4c 100644 --- a/runtime/NSObject.mm +++ b/runtime/NSObject.mm @@ -51,6 +51,9 @@ OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_parent_offset = __buil OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_child_offset = __builtin_offsetof(AutoreleasePoolPageData, child); OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_depth_offset = __builtin_offsetof(AutoreleasePoolPageData, depth); OBJC_EXTERN const uint32_t objc_debug_autoreleasepoolpage_hiwat_offset = __builtin_offsetof(AutoreleasePoolPageData, hiwat); +#if __OBJC2__ +OBJC_EXTERN const uint32_t objc_class_abi_version = OBJC_CLASS_ABI_VERSION_MAX; +#endif /*********************************************************************** * Weak ivar support diff --git a/runtime/PointerUnion.h b/runtime/PointerUnion.h new file mode 100644 index 0000000..85b7846 --- /dev/null +++ b/runtime/PointerUnion.h @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2019 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#ifndef POINTERUNION_H +#define POINTERUNION_H + +#include +#include + +namespace objc { + +template struct PointerUnionTypeSelectorReturn { + using Return = T; +}; + +/// Get a type based on whether two types are the same or not. +/// +/// For: +/// +/// \code +/// using Ret = typename PointerUnionTypeSelector::Return; +/// \endcode +/// +/// Ret will be EQ type if T1 is same as T2 or NE type otherwise. +template +struct PointerUnionTypeSelector { + using Return = typename PointerUnionTypeSelectorReturn::Return; +}; + +template +struct PointerUnionTypeSelector { + using Return = typename PointerUnionTypeSelectorReturn::Return; +}; + +template +struct PointerUnionTypeSelectorReturn< + PointerUnionTypeSelector> { + using Return = + typename PointerUnionTypeSelector::Return; +}; + +template +class PointerUnion { + uintptr_t _value; + + static_assert(alignof(PT1) >= 2, "alignment requirement"); + static_assert(alignof(PT2) >= 2, "alignment requirement"); + + struct IsPT1 { + static const uintptr_t Num = 0; + }; + struct IsPT2 { + static const uintptr_t Num = 1; + }; + template struct UNION_DOESNT_CONTAIN_TYPE {}; + + uintptr_t getPointer() const { + return _value & ~1; + } + uintptr_t getTag() const { + return _value & 1; + } + +public: + explicit PointerUnion(const std::atomic &raw) + : _value(raw.load(std::memory_order_relaxed)) + { } + PointerUnion(PT1 t) : _value((uintptr_t)t) { } + PointerUnion(PT2 t) : _value((uintptr_t)t | 1) { } + + void storeAt(std::atomic &raw, std::memory_order order) const { + raw.store(_value, order); + } + + template + bool is() const { + using Ty = typename PointerUnionTypeSelector>>::Return; + return getTag() == Ty::Num; + } + + template T get() const { + ASSERT(is() && "Invalid accessor called"); + return reinterpret_cast(getPointer()); + } + + template T dyn_cast() const { + if (is()) + return get(); + return T(); + } +}; + +template +class PointerUnion4 { + uintptr_t _value; + + static_assert(alignof(PT1) >= 4, "alignment requirement"); + static_assert(alignof(PT2) >= 4, "alignment requirement"); + static_assert(alignof(PT3) >= 4, "alignment requirement"); + static_assert(alignof(PT4) >= 4, "alignment requirement"); + + struct IsPT1 { + static const uintptr_t Num = 0; + }; + struct IsPT2 { + static const uintptr_t Num = 1; + }; + struct IsPT3 { + static const uintptr_t Num = 2; + }; + struct IsPT4 { + static const uintptr_t Num = 3; + }; + template struct UNION_DOESNT_CONTAIN_TYPE {}; + + uintptr_t getPointer() const { + return _value & ~3; + } + uintptr_t getTag() const { + return _value & 3; + } + +public: + explicit PointerUnion4(const std::atomic &raw) + : _value(raw.load(std::memory_order_relaxed)) + { } + PointerUnion4(PT1 t) : _value((uintptr_t)t) { } + PointerUnion4(PT2 t) : _value((uintptr_t)t | 1) { } + PointerUnion4(PT3 t) : _value((uintptr_t)t | 2) { } + PointerUnion4(PT4 t) : _value((uintptr_t)t | 3) { } + + void storeAt(std::atomic &raw, std::memory_order order) const { + raw.store(_value, order); + } + + template + bool is() const { + using Ty = typename PointerUnionTypeSelector>>>>::Return; + return getTag() == Ty::Num; + } + + template T get() const { + ASSERT(is() && "Invalid accessor called"); + return reinterpret_cast(getPointer()); + } + + template T dyn_cast() const { + if (is()) + return get(); + return T(); + } +}; + +} // namespace objc + +#endif /* DENSEMAPEXTRAS_H */ diff --git a/runtime/objc-cache.mm b/runtime/objc-cache.mm index 4735a2d..4e6ca11 100644 --- a/runtime/objc-cache.mm +++ b/runtime/objc-cache.mm @@ -465,17 +465,15 @@ unsigned cache_t::capacity() } -#if CACHE_END_MARKER - -size_t cache_t::bytesForCapacity(uint32_t cap) +size_t cache_t::bytesForCapacity(uint32_t cap) { - // fixme put end marker inline when capacity+1 malloc is inefficient - return sizeof(bucket_t) * (cap + 1); + return sizeof(bucket_t) * cap; } -bucket_t *cache_t::endMarker(struct bucket_t *b, uint32_t cap) +#if CACHE_END_MARKER + +bucket_t *cache_t::endMarker(struct bucket_t *b, uint32_t cap) { - // bytesForCapacity() chooses whether the end marker is inline or not return (bucket_t *)((uintptr_t)b + bytesForCapacity(cap)) - 1; } @@ -483,7 +481,6 @@ bucket_t *allocateBuckets(mask_t newCapacity) { // Allocate one extra bucket to mark the end of the list. // This can't overflow mask_t because newCapacity is a power of 2. - // fixme instead put the end mark inline when +1 is malloc-inefficient bucket_t *newBuckets = (bucket_t *) calloc(cache_t::bytesForCapacity(newCapacity), 1); @@ -505,11 +502,6 @@ bucket_t *allocateBuckets(mask_t newCapacity) #else -size_t cache_t::bytesForCapacity(uint32_t cap) -{ - return sizeof(bucket_t) * cap; -} - bucket_t *allocateBuckets(mask_t newCapacity) { if (PrintCaches) recordNewCache(newCapacity); @@ -662,7 +654,7 @@ void cache_t::insert(Class cls, SEL sel, IMP imp, id receiver) if (!capacity) capacity = INIT_CACHE_SIZE; reallocate(oldCapacity, capacity, /* freeOld */false); } - else if (fastpath(newOccupied <= capacity / 4 * 3)) { + else if (fastpath(newOccupied + CACHE_END_MARKER <= capacity / 4 * 3)) { // Cache is less than 3/4 full. Use it as-is. } else { diff --git a/runtime/objc-gdb.h b/runtime/objc-gdb.h index f1e4a3c..9cab4a3 100644 --- a/runtime/objc-gdb.h +++ b/runtime/objc-gdb.h @@ -157,6 +157,49 @@ OBJC_EXPORT uintptr_t objc_indexed_classes_count; OBJC_EXPORT const uintptr_t objc_debug_class_rw_data_mask OBJC_AVAILABLE(10.13, 11.0, 11.0, 4.0, 2.0); +// The ABI version for the internal runtime representations +// lldb, CoreSymbolication and debugging tools need to know +OBJC_EXTERN const uint32_t objc_class_abi_version + OBJC_AVAILABLE(10.15, 13.0, 13.0, 6.0, 5.0); + +// the maximum ABI version in existence for now +#define OBJC_CLASS_ABI_VERSION_MAX 1 + +// Used when objc_class_abi_version is absent or 0 +struct class_rw_v0_t { + uint32_t flags; + uint32_t version; + uintptr_t ro; // class_ro_t + uintptr_t methods; // method_array_t + uintptr_t properties; // property_array_t + uintptr_t protocols; // protocol_array_t + Class _Nullable firstSubclass; + Class _Nullable nextSiblingClass; + char *_Nullable demangledName; + + // uint32_t index; // only when indexed-isa is used +}; + +// Used when objc_class_abi_version is 1 +struct class_rw_v1_t { + uint32_t flags; + uint16_t witness; + uint16_t index; // only when indexed-isa is used + uintptr_t ro_or_rw_ext; // tagged union based on the low bit: + // 0: class_ro_t 1: class_rw_ext_t + Class _Nullable firstSubclass; + Class _Nullable nextSiblingClass; +}; + +struct class_rw_ext_v1_t { + uintptr_t ro; // class_ro_t + uintptr_t methods; // method_array_t + uintptr_t properties; // property_array_t + uintptr_t protocols; // protocol_array_t + char *_Nullable demangledName; + uint32_t version; +}; + #endif diff --git a/runtime/objc-os.mm b/runtime/objc-os.mm index e2f65f2..7d600ef 100644 --- a/runtime/objc-os.mm +++ b/runtime/objc-os.mm @@ -926,6 +926,10 @@ void _objc_init(void) _imp_implementationWithBlock_init(); _dyld_objc_notify_register(&map_images, load_images, unmap_image); + +#if __OBJC2__ + didCallDyldNotifyRegister = true; +#endif } diff --git a/runtime/objc-private.h b/runtime/objc-private.h index fbeaeb5..4d7aab2 100644 --- a/runtime/objc-private.h +++ b/runtime/objc-private.h @@ -478,6 +478,11 @@ static inline bool sectnameStartsWith(const char *sectname, const char *prefix){ } +#if __OBJC2__ +extern bool didCallDyldNotifyRegister; +#endif + + /* selectors */ extern void sel_init(size_t selrefCount); extern SEL sel_registerNameNoLock(const char *str, bool copy); diff --git a/runtime/objc-runtime-new.h b/runtime/objc-runtime-new.h index 532a353..d3541cf 100644 --- a/runtime/objc-runtime-new.h +++ b/runtime/objc-runtime-new.h @@ -24,6 +24,8 @@ #ifndef _OBJC_RUNTIME_NEW_H #define _OBJC_RUNTIME_NEW_H +#include "PointerUnion.h" + // class_data_bits_t is the class_t->data field (class_rw_t pointer plus flags) // The extra bits are optimized for the retain/release and alloc/dealloc paths. @@ -92,6 +94,10 @@ // class has started realizing but not yet completed it #define RW_REALIZING (1<<19) +// class is a metaclass (copied from ro) +#define RW_META RO_META // (1<<0) + + // NOTE: MORE RW_ FLAGS DEFINED BELOW @@ -769,12 +775,12 @@ class list_array_tt { protected: class iterator { - List **lists; - List **listsEnd; + List * const *lists; + List * const *listsEnd; typename List::iterator m, mEnd; public: - iterator(List **begin, List **end) + iterator(List *const *begin, List *const *end) : lists(begin), listsEnd(end) { if (begin != end) { @@ -822,7 +828,7 @@ class list_array_tt { return arrayAndFlag & 1; } - array_t *array() { + array_t *array() const { return (array_t *)(arrayAndFlag & ~1); } @@ -831,8 +837,10 @@ class list_array_tt { } public: + list_array_tt() : list(nullptr) { } + list_array_tt(List *l) : list(l) { } - uint32_t count() { + uint32_t count() const { uint32_t result = 0; for (auto lists = beginLists(), end = endLists(); lists != end; @@ -843,12 +851,12 @@ class list_array_tt { return result; } - iterator begin() { + iterator begin() const { return iterator(beginLists(), endLists()); } - iterator end() { - List **e = endLists(); + iterator end() const { + List * const *e = endLists(); return iterator(e, e); } @@ -863,7 +871,7 @@ class list_array_tt { } } - List** beginLists() { + List* const * beginLists() const { if (hasArray()) { return array()->lists; } else { @@ -871,7 +879,7 @@ class list_array_tt { } } - List** endLists() { + List* const * endLists() const { if (hasArray()) { return array()->lists + array()->count; } else if (list) { @@ -951,11 +959,14 @@ class method_array_t : typedef list_array_tt Super; public: - method_list_t **beginCategoryMethodLists() { + method_array_t() : Super() { } + method_array_t(method_list_t *l) : Super(l) { } + + method_list_t * const *beginCategoryMethodLists() const { return beginLists(); } - method_list_t **endCategoryMethodLists(Class cls); + method_list_t * const *endCategoryMethodLists(Class cls) const; method_array_t duplicate() { return Super::duplicate(); @@ -969,6 +980,9 @@ class property_array_t : typedef list_array_tt Super; public: + property_array_t() : Super() { } + property_array_t(property_list_t *l) : Super(l) { } + property_array_t duplicate() { return Super::duplicate(); } @@ -981,34 +995,58 @@ class protocol_array_t : typedef list_array_tt Super; public: + protocol_array_t() : Super() { } + protocol_array_t(protocol_list_t *l) : Super(l) { } + protocol_array_t duplicate() { return Super::duplicate(); } }; +struct class_rw_ext_t { + const class_ro_t *ro; + method_array_t methods; + property_array_t properties; + protocol_array_t protocols; + char *demangledName; + uint32_t version; +}; struct class_rw_t { // Be warned that Symbolication knows the layout of this structure. uint32_t flags; - uint16_t version; uint16_t witness; +#if SUPPORT_INDEXED_ISA + uint16_t index; +#endif - const class_ro_t *ro; - - method_array_t methods; - property_array_t properties; - protocol_array_t protocols; + explicit_atomic ro_or_rw_ext; Class firstSubclass; Class nextSiblingClass; - char *demangledName; +private: + using ro_or_rw_ext_t = objc::PointerUnion; -#if SUPPORT_INDEXED_ISA - uint32_t index; -#endif + const ro_or_rw_ext_t get_ro_or_rwe() const { + return ro_or_rw_ext_t{ro_or_rw_ext}; + } + + void set_ro_or_rwe(const class_ro_t *ro) { + ro_or_rw_ext_t{ro}.storeAt(ro_or_rw_ext, memory_order_relaxed); + } + + void set_ro_or_rwe(class_rw_ext_t *rwe, const class_ro_t *ro) { + // the release barrier is so that the class_rw_ext_t::ro initialization + // is visible to lockless readers + rwe->ro = ro; + ro_or_rw_ext_t{rwe}.storeAt(ro_or_rw_ext, memory_order_release); + } - void setFlags(uint32_t set) + class_rw_ext_t *extAlloc(const class_ro_t *ro, bool deep = false); + +public: + void setFlags(uint32_t set) { __c11_atomic_fetch_or((_Atomic(uint32_t) *)&flags, set, __ATOMIC_RELAXED); } @@ -1029,6 +1067,67 @@ struct class_rw_t { newf = (oldf | set) & ~clear; } while (!OSAtomicCompareAndSwap32Barrier(oldf, newf, (volatile int32_t *)&flags)); } + + class_rw_ext_t *ext() const { + return get_ro_or_rwe().dyn_cast(); + } + + class_rw_ext_t *extAllocIfNeeded() { + auto v = get_ro_or_rwe(); + if (fastpath(v.is())) { + return v.get(); + } else { + return extAlloc(v.get()); + } + } + + class_rw_ext_t *deepCopy(const class_ro_t *ro) { + return extAlloc(ro, true); + } + + const class_ro_t *ro() const { + auto v = get_ro_or_rwe(); + if (slowpath(v.is())) { + return v.get()->ro; + } + return v.get(); + } + + void set_ro(const class_ro_t *ro) { + auto v = get_ro_or_rwe(); + if (v.is()) { + v.get()->ro = ro; + } else { + set_ro_or_rwe(ro); + } + } + + const method_array_t methods() const { + auto v = get_ro_or_rwe(); + if (v.is()) { + return v.get()->methods; + } else { + return method_array_t{v.get()->baseMethods()}; + } + } + + const property_array_t properties() const { + auto v = get_ro_or_rwe(); + if (v.is()) { + return v.get()->properties; + } else { + return property_array_t{v.get()->baseProperties}; + } + } + + const protocol_array_t protocols() const { + auto v = get_ro_or_rwe(); + if (v.is()) { + return v.get()->protocols; + } else { + return protocol_array_t{v.get()->baseProtocols}; + } + } }; @@ -1087,7 +1186,7 @@ public: class_rw_t *maybe_rw = data(); if (maybe_rw->flags & RW_REALIZED) { // maybe_rw is rw - return maybe_rw->ro; + return maybe_rw->ro(); } else { // maybe_rw is actually ro return (class_ro_t *)maybe_rw; @@ -1359,12 +1458,12 @@ struct objc_class : objc_object { // Return YES if the class's ivars are managed by ARC, // or the class is MRC but has ARC-style weak ivars. bool hasAutomaticIvars() { - return data()->ro->flags & (RO_IS_ARC | RO_HAS_WEAK_WITHOUT_ARC); + return data()->ro()->flags & (RO_IS_ARC | RO_HAS_WEAK_WITHOUT_ARC); } // Return YES if the class's ivars are managed by ARC. bool isARC() { - return data()->ro->flags & RO_IS_ARC; + return data()->ro()->flags & RO_IS_ARC; } @@ -1435,13 +1534,15 @@ struct objc_class : objc_object { #if FAST_CACHE_META return cache.getBit(FAST_CACHE_META); #else - return data()->ro->flags & RO_META; + return data()->flags & RW_META; #endif } // Like isMetaClass, but also valid on un-realized classes bool isMetaClassMaybeUnrealized() { - return bits.safe_ro()->flags & RO_META; + static_assert(offsetof(class_rw_t, flags) == offsetof(class_ro_t, flags), "flags alias"); + static_assert(RO_META == RW_META, "flags alias"); + return data()->flags & RW_META; } // NOT identical to this->ISA when this is a metaclass @@ -1462,19 +1563,19 @@ struct objc_class : objc_object { ASSERT(this); if (isRealized() || isFuture()) { - return data()->ro->name; + return data()->ro()->name; } else { return ((const class_ro_t *)data())->name; } } - const char *demangledName(); + const char *demangledName(bool needsLock); const char *nameForLogging(); // May be unaligned depending on class's ivars. uint32_t unalignedInstanceStart() const { ASSERT(isRealized()); - return data()->ro->instanceStart; + return data()->ro()->instanceStart; } // Class's instance start rounded up to a pointer-size boundary. @@ -1486,7 +1587,7 @@ struct objc_class : objc_object { // May be unaligned depending on class's ivars. uint32_t unalignedInstanceSize() const { ASSERT(isRealized()); - return data()->ro->instanceSize; + return data()->ro()->instanceSize; } // Class's ivar size rounded up to a pointer-size boundary. @@ -1508,9 +1609,10 @@ struct objc_class : objc_object { void setInstanceSize(uint32_t newSize) { ASSERT(isRealized()); ASSERT(data()->flags & RW_REALIZING); - if (newSize != data()->ro->instanceSize) { + auto ro = data()->ro(); + if (newSize != ro->instanceSize) { ASSERT(data()->flags & RW_COPIED_RO); - *const_cast(&data()->ro->instanceSize) = newSize; + *const_cast(&ro->instanceSize) = newSize; } cache.setFastInstanceSize(newSize); } diff --git a/runtime/objc-runtime-new.mm b/runtime/objc-runtime-new.mm index be70319..ee12127 100644 --- a/runtime/objc-runtime-new.mm +++ b/runtime/objc-runtime-new.mm @@ -33,6 +33,7 @@ #include "objc-runtime-new.h" #include "objc-file.h" #include "objc-cache.h" +#include "objc-zalloc.h" #include #include #include @@ -45,7 +46,7 @@ static void free_class(Class cls); static IMP addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace); static void adjustCustomFlagsForMethodChange(Class cls, method_t *meth); static method_t *search_method_list(const method_list_t *mlist, SEL sel); -static bool method_lists_contains_any(method_list_t **mlists, method_list_t **end, +static bool method_lists_contains_any(method_list_t * const *mlists, method_list_t * const *end, SEL sels[], size_t selcount); static void flushCaches(Class cls); static void initializeTaggedPointerObfuscator(void); @@ -198,6 +199,19 @@ static ExplicitInitDenseSet allocatedClasses; **********************************************************************/ static Class _firstRealizedClass = nil; +/*********************************************************************** +* didInitialAttachCategories +* Whether the initial attachment of categories present at startup has +* been done. +**********************************************************************/ +static bool didInitialAttachCategories = false; + +/*********************************************************************** +* didCallDyldNotifyRegister +* Whether the call to _dyld_objc_notify_register has completed. +**********************************************************************/ +bool didCallDyldNotifyRegister = false; + /* Low two bits of mlist->entsize is used as the fixed-up marker. PREOPTIMIZED VERSION: @@ -274,12 +288,12 @@ void protocol_t::clearIsCanonical() { } -method_list_t **method_array_t::endCategoryMethodLists(Class cls) +method_list_t * const *method_array_t::endCategoryMethodLists(Class cls) const { - method_list_t **mlists = beginLists(); - method_list_t **mlistsEnd = endLists(); + auto mlists = beginLists(); + auto mlistsEnd = endLists(); - if (mlists == mlistsEnd || !cls->data()->ro->baseMethods()) + if (mlists == mlistsEnd || !cls->data()->ro()->baseMethods()) { // No methods, or no base methods. // Everything here is a category method. @@ -394,10 +408,10 @@ static class_ro_t *make_ro_writeable(class_rw_t *rw) if (rw->flags & RW_COPIED_RO) { // already writeable, do nothing } else { - rw->ro = rw->ro->duplicate(); + rw->set_ro(rw->ro()->duplicate()); rw->flags |= RW_COPIED_RO; } - return (class_ro_t *)rw->ro; + return const_cast(rw->ro()); } @@ -546,7 +560,7 @@ printReplacements(Class cls, const locstamped_category_t *cats_list, uint32_t ca } // Look for method in cls - for (const auto& meth2 : cls->data()->methods) { + for (const auto& meth2 : cls->data()->methods()) { SEL s2 = sel_registerName(sel_cname(meth2.name)); if (s == s2) { logReplacedMethod(cls->nameForLogging(), s, @@ -762,7 +776,7 @@ class Mixin { cls = classNSObject(); if (Domain != Scope::Classes && !isNSObjectSwizzled(NO)) { - for (const auto &meth2: as_objc_class(cls)->data()->methods) { + for (const auto &meth2: as_objc_class(cls)->data()->methods()) { if (meth == &meth2) { setNSObjectSwizzled(cls, NO); break; @@ -772,7 +786,7 @@ class Mixin { cls = metaclassNSObject(); if (Domain != Scope::Instances && !isNSObjectSwizzled(YES)) { - for (const auto &meth2: as_objc_class(cls)->data()->methods) { + for (const auto &meth2: as_objc_class(cls)->data()->methods()) { if (meth == &meth2) { setNSObjectSwizzled(cls, YES); break; @@ -791,7 +805,7 @@ class Mixin { setCustom = YES; } else if (cls == NSOClass) { // NSObject is default but we need to check categories - auto &methods = as_objc_class(cls)->data()->methods; + auto &methods = as_objc_class(cls)->data()->methods(); setCustom = Traits::scanMethodLists(methods.beginCategoryMethodLists(), methods.endCategoryMethodLists(cls)); } else if (!isMeta && !as_objc_class(cls)->superclass) { @@ -803,7 +817,7 @@ class Mixin { inherited = YES; } else { // Not NSObject. - auto &methods = as_objc_class(cls)->data()->methods; + auto &methods = as_objc_class(cls)->data()->methods(); setCustom = Traits::scanMethodLists(methods.beginLists(), methods.endLists()); } @@ -924,7 +938,7 @@ struct AWZScanner : scanner::Mixin { sel == @selector(isKindOfClass:) || sel == @selector(respondsToSelector:); } - static bool scanMethodLists(method_list_t **mlists, method_list_t **end) { + static bool scanMethodLists(method_list_t * const *mlists, method_list_t * const *end) { SEL sels[5] = { @selector(new), @selector(self), @@ -1162,7 +1176,7 @@ static UnattachedCategories unattachedCategories; static bool isBundleClass(Class cls) { - return cls->data()->ro->flags & RO_FROM_BUNDLE; + return cls->data()->ro()->flags & RO_FROM_BUNDLE; } @@ -1235,6 +1249,39 @@ prepareMethodLists(Class cls, method_list_t **addedLists, int addedCount, } } +class_rw_ext_t * +class_rw_t::extAlloc(const class_ro_t *ro, bool deepCopy) +{ + runtimeLock.assertLocked(); + + auto rwe = objc::zalloc(); + + rwe->version = (ro->flags & RO_META) ? 7 : 0; + + method_list_t *list = ro->baseMethods(); + if (list) { + if (deepCopy) list = list->duplicate(); + rwe->methods.attachLists(&list, 1); + } + + // See comments in objc_duplicateClass + // property lists and protocol lists historically + // have not been deep-copied + // + // This is probably wrong and ought to be fixed some day + property_list_t *proplist = ro->baseProperties; + if (proplist) { + rwe->properties.attachLists(&proplist, 1); + } + + protocol_list_t *protolist = ro->baseProtocols; + if (protolist) { + rwe->protocols.attachLists(&protolist, 1); + } + + set_ro_or_rwe(rwe, ro); + return rwe; +} // Attach method lists and properties and protocols from categories to a class. // Assumes the categories in cats are all loaded and sorted by load order, @@ -1272,7 +1319,7 @@ attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cat uint32_t protocount = 0; bool fromBundle = NO; bool isMeta = (flags & ATTACH_METACLASS); - auto rw = cls->data(); + auto rwe = cls->data()->extAllocIfNeeded(); for (uint32_t i = 0; i < cats_count; i++) { auto& entry = cats_list[i]; @@ -1281,7 +1328,7 @@ attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cat if (mlist) { if (mcount == ATTACH_BUFSIZ) { prepareMethodLists(cls, mlists, mcount, NO, fromBundle); - rw->methods.attachLists(mlists, mcount); + rwe->methods.attachLists(mlists, mcount); mcount = 0; } mlists[ATTACH_BUFSIZ - ++mcount] = mlist; @@ -1292,7 +1339,7 @@ attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cat entry.cat->propertiesForMeta(isMeta, entry.hi); if (proplist) { if (propcount == ATTACH_BUFSIZ) { - rw->properties.attachLists(proplists, propcount); + rwe->properties.attachLists(proplists, propcount); propcount = 0; } proplists[ATTACH_BUFSIZ - ++propcount] = proplist; @@ -1301,7 +1348,7 @@ attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cat protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta); if (protolist) { if (protocount == ATTACH_BUFSIZ) { - rw->protocols.attachLists(protolists, protocount); + rwe->protocols.attachLists(protolists, protocount); protocount = 0; } protolists[ATTACH_BUFSIZ - ++protocount] = protolist; @@ -1310,13 +1357,13 @@ attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cat if (mcount > 0) { prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle); - rw->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount); + rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount); if (flags & ATTACH_EXISTING) flushCaches(cls); } - rw->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount); + rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount); - rw->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount); + rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount); } @@ -1332,7 +1379,8 @@ static void methodizeClass(Class cls, Class previously) bool isMeta = cls->isMetaClass(); auto rw = cls->data(); - auto ro = rw->ro; + auto ro = rw->ro(); + auto rwe = rw->ext(); // Methodizing for the first time if (PrintConnecting) { @@ -1344,17 +1392,17 @@ static void methodizeClass(Class cls, Class previously) method_list_t *list = ro->baseMethods(); if (list) { prepareMethodLists(cls, &list, 1, YES, isBundleClass(cls)); - rw->methods.attachLists(&list, 1); + if (rwe) rwe->methods.attachLists(&list, 1); } property_list_t *proplist = ro->baseProperties; - if (proplist) { - rw->properties.attachLists(&proplist, 1); + if (rwe && proplist) { + rwe->properties.attachLists(&proplist, 1); } protocol_list_t *protolist = ro->baseProtocols; - if (protolist) { - rw->protocols.attachLists(&protolist, 1); + if (rwe && protolist) { + rwe->protocols.attachLists(&protolist, 1); } // Root classes get bonus method implementations if they don't have @@ -1382,7 +1430,7 @@ static void methodizeClass(Class cls, Class previously) #if DEBUG // Debug: sanity-check all SELs; log method list contents - for (const auto& meth : rw->methods) { + for (const auto& meth : rw->methods()) { if (PrintConnecting) { _objc_inform("METHOD %c[%s %s]", isMeta ? '+' : '-', cls->nameForLogging(), sel_getName(meth.name)); @@ -1704,10 +1752,10 @@ static void addFutureNamedClass(const char *name, Class cls) _objc_inform("FUTURE: reserving %p for %s", (void*)cls, name); } - class_rw_t *rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1); + class_rw_t *rw = objc::zalloc(); class_ro_t *ro = (class_ro_t *)calloc(sizeof(class_ro_t), 1); ro->name = strdupIfMutable(name); - rw->ro = ro; + rw->set_ro(ro); cls->setData(rw); cls->data()->flags = RO_FUTURE; @@ -2345,7 +2393,7 @@ static void reconcileInstanceVariables(Class cls, Class supercls, const class_ro */ // Non-fragile ivars - reconcile this class with its superclass - const class_ro_t *super_ro = supercls->data()->ro; + const class_ro_t *super_ro = supercls->data()->ro(); if (DebugNonFragileIvars) { // Debugging: Force non-fragile ivars to slide. @@ -2367,7 +2415,7 @@ static void reconcileInstanceVariables(Class cls, Class supercls, const class_ro { uint32_t oldStart = ro->instanceStart; class_ro_t *ro_w = make_ro_writeable(rw); - ro = rw->ro; + ro = rw->ro(); // Find max ivar alignment in class. // default to word size to simplify ivar update @@ -2418,7 +2466,7 @@ static void reconcileInstanceVariables(Class cls, Class supercls, const class_ro super_ro->instanceSize); } class_ro_t *ro_w = make_ro_writeable(rw); - ro = rw->ro; + ro = rw->ro(); moveIvars(ro_w, super_ro->instanceSize); gdb_objc_class_changed(cls, OBJC_CLASS_IVARS_CHANGED, ro->name); } @@ -2437,11 +2485,9 @@ static Class realizeClassWithoutSwift(Class cls, Class previously) { runtimeLock.assertLocked(); - const class_ro_t *ro; class_rw_t *rw; Class supercls; Class metacls; - bool isMeta; if (!cls) return nil; if (cls->isRealized()) return cls; @@ -2449,26 +2495,25 @@ static Class realizeClassWithoutSwift(Class cls, Class previously) // fixme verify class is not in an un-dlopened part of the shared cache? - ro = (const class_ro_t *)cls->data(); + auto ro = (const class_ro_t *)cls->data(); + auto isMeta = ro->flags & RO_META; if (ro->flags & RO_FUTURE) { // This was a future class. rw data is already allocated. rw = cls->data(); - ro = cls->data()->ro; + ro = cls->data()->ro(); + ASSERT(!isMeta); cls->changeInfo(RW_REALIZED|RW_REALIZING, RW_FUTURE); } else { // Normal class. Allocate writeable class data. - rw = (class_rw_t *)calloc(sizeof(class_rw_t), 1); - rw->ro = ro; - rw->flags = RW_REALIZED|RW_REALIZING; + rw = objc::zalloc(); + rw->set_ro(ro); + rw->flags = RW_REALIZED|RW_REALIZING|isMeta; cls->setData(rw); } - isMeta = ro->flags & RO_META; #if FAST_CACHE_META if (isMeta) cls->cache.setBit(FAST_CACHE_META); #endif - rw->version = isMeta ? 7 : 0; // old runtime went up to 6 - // Choose an index for this class. // Sets cls->instancesRequireRawIsa if indexes no more indexes are available @@ -2943,6 +2988,83 @@ map_images(unsigned count, const char * const paths[], } +static void load_categories_nolock(header_info *hi) { + bool hasClassProperties = hi->info()->hasCategoryClassProperties(); + + size_t count; + auto processCatlist = [&](category_t * const *catlist) { + for (unsigned i = 0; i < count; i++) { + category_t *cat = catlist[i]; + Class cls = remapClass(cat->cls); + locstamped_category_t lc{cat, hi}; + + if (!cls) { + // Category's target class is missing (probably weak-linked). + // Ignore the category. + if (PrintConnecting) { + _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with " + "missing weak-linked target class", + cat->name, cat); + } + continue; + } + + // Process this category. + if (cls->isStubClass()) { + // Stub classes are never realized. Stub classes + // don't know their metaclass until they're + // initialized, so we have to add categories with + // class methods or properties to the stub itself. + // methodizeClass() will find them and add them to + // the metaclass as appropriate. + if (cat->instanceMethods || + cat->protocols || + cat->instanceProperties || + cat->classMethods || + cat->protocols || + (hasClassProperties && cat->_classProperties)) + { + objc::unattachedCategories.addForClass(lc, cls); + } + } else { + // First, register the category with its target class. + // Then, rebuild the class's method lists (etc) if + // the class is realized. + if (cat->instanceMethods || cat->protocols + || cat->instanceProperties) + { + if (cls->isRealized()) { + attachCategories(cls, &lc, 1, ATTACH_EXISTING); + } else { + objc::unattachedCategories.addForClass(lc, cls); + } + } + + if (cat->classMethods || cat->protocols + || (hasClassProperties && cat->_classProperties)) + { + if (cls->ISA()->isRealized()) { + attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS); + } else { + objc::unattachedCategories.addForClass(lc, cls->ISA()); + } + } + } + } + }; + + processCatlist(_getObjc2CategoryList(hi, &count)); + processCatlist(_getObjc2CategoryList2(hi, &count)); +} + +static void loadAllCategories() { + mutex_locker_t lock(runtimeLock); + + for (auto *hi = FirstHeader; hi != NULL; hi = hi->getNext()) { + load_categories_nolock(hi); + } +} + /*********************************************************************** * load_images * Process +load in the given images which are being mapped in by dyld. @@ -2955,6 +3077,11 @@ extern void prepare_load_methods(const headerType *mhdr); void load_images(const char *path __unused, const struct mach_header *mh) { + if (!didInitialAttachCategories && didCallDyldNotifyRegister) { + didInitialAttachCategories = true; + loadAllCategories(); + } + // Return without taking locks if there are no +load methods here. if (!hasLoadMethods((const headerType *)mh)) return; @@ -3091,9 +3218,9 @@ Class readClass(Class cls, bool headerIsBundle, bool headerIsPreoptimized) } class_rw_t *rw = newCls->data(); - const class_ro_t *old_ro = rw->ro; + const class_ro_t *old_ro = rw->ro(); memcpy(newCls, cls, sizeof(objc_class)); - rw->ro = (class_ro_t *)newCls->data(); + rw->set_ro((class_ro_t *)newCls->data()); newCls->setData(rw); freeIfMutable((char *)old_ro->name); free((void *)old_ro); @@ -3480,72 +3607,14 @@ void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int un ts.log("IMAGE TIMES: fix up @protocol references"); - // Discover categories. - for (EACH_HEADER) { - bool hasClassProperties = hi->info()->hasCategoryClassProperties(); - - auto processCatlist = [&](category_t * const *catlist) { - for (i = 0; i < count; i++) { - category_t *cat = catlist[i]; - Class cls = remapClass(cat->cls); - locstamped_category_t lc{cat, hi}; - - if (!cls) { - // Category's target class is missing (probably weak-linked). - // Ignore the category. - if (PrintConnecting) { - _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with " - "missing weak-linked target class", - cat->name, cat); - } - continue; - } - - // Process this category. - if (cls->isStubClass()) { - // Stub classes are never realized. Stub classes - // don't know their metaclass until they're - // initialized, so we have to add categories with - // class methods or properties to the stub itself. - // methodizeClass() will find them and add them to - // the metaclass as appropriate. - if (cat->instanceMethods || - cat->protocols || - cat->instanceProperties || - cat->classMethods || - cat->protocols || - (hasClassProperties && cat->_classProperties)) - { - objc::unattachedCategories.addForClass(lc, cls); - } - } else { - // First, register the category with its target class. - // Then, rebuild the class's method lists (etc) if - // the class is realized. - if (cat->instanceMethods || cat->protocols - || cat->instanceProperties) - { - if (cls->isRealized()) { - attachCategories(cls, &lc, 1, ATTACH_EXISTING); - } else { - objc::unattachedCategories.addForClass(lc, cls); - } - } - - if (cat->classMethods || cat->protocols - || (hasClassProperties && cat->_classProperties)) - { - if (cls->ISA()->isRealized()) { - attachCategories(cls->ISA(), &lc, 1, ATTACH_EXISTING | ATTACH_METACLASS); - } else { - objc::unattachedCategories.addForClass(lc, cls->ISA()); - } - } - } - } - }; - processCatlist(_getObjc2CategoryList(hi, &count)); - processCatlist(_getObjc2CategoryList2(hi, &count)); + // Discover categories. Only do this after the initial category + // attachment has been done. For categories present at startup, + // discovery is deferred until the first load_images call after + // the call to _dyld_objc_notify_register completes. rdar://problem/53119145 + if (didInitialAttachCategories) { + for (EACH_HEADER) { + load_categories_nolock(hi); + } } ts.log("IMAGE TIMES: discover categories"); @@ -5055,16 +5124,17 @@ class_copyMethodList(Class cls, unsigned int *outCount) } mutex_locker_t lock(runtimeLock); + const auto methods = cls->data()->methods(); ASSERT(cls->isRealized()); - count = cls->data()->methods.count(); + count = methods.count(); if (count > 0) { result = (Method *)malloc((count + 1) * sizeof(Method)); count = 0; - for (auto& meth : cls->data()->methods) { + for (auto& meth : methods) { result[count++] = &meth; } result[count] = nil; @@ -5096,7 +5166,7 @@ class_copyIvarList(Class cls, unsigned int *outCount) ASSERT(cls->isRealized()); - if ((ivars = cls->data()->ro->ivars) && ivars->count) { + if ((ivars = cls->data()->ro()->ivars) && ivars->count) { result = (Ivar *)malloc((ivars->count+1) * sizeof(Ivar)); for (auto& ivar : *ivars) { @@ -5134,12 +5204,13 @@ class_copyPropertyList(Class cls, unsigned int *outCount) auto rw = cls->data(); property_t **result = nil; - unsigned int count = rw->properties.count(); + auto const properties = rw->properties(); + unsigned int count = properties.count(); if (count > 0) { result = (property_t **)malloc((count + 1) * sizeof(property_t *)); count = 0; - for (auto& prop : rw->properties) { + for (auto& prop : properties) { result[count++] = ∝ } result[count] = nil; @@ -5168,7 +5239,7 @@ objc_class::getLoadMethod() ASSERT(!isMetaClass()); ASSERT(ISA()->isMetaClass()); - mlist = ISA()->data()->ro->baseMethods(); + mlist = ISA()->data()->ro()->baseMethods(); if (mlist) { for (const auto& meth : *mlist) { const char *name = sel_cname(meth.name); @@ -5283,18 +5354,19 @@ class_copyProtocolList(Class cls, unsigned int *outCount) } mutex_locker_t lock(runtimeLock); + const auto protocols = cls->data()->protocols(); checkIsKnownClass(cls); ASSERT(cls->isRealized()); - count = cls->data()->protocols.count(); + count = protocols.count(); if (count > 0) { result = (Protocol **)malloc((count+1) * sizeof(Protocol *)); count = 0; - for (const auto& proto : cls->data()->protocols) { + for (const auto& proto : protocols) { result[count++] = (Protocol *)remapProtocol(proto); } result[count] = nil; @@ -5377,7 +5449,7 @@ copyClassNamesForImage_nolock(header_info *hi, unsigned int *outCount) for (size_t i = 0; i < count; i++) { Class cls = remapClass(classlist[i]); if (cls) { - names[i-shift] = cls->demangledName(); + names[i-shift] = cls->demangledName(/* needs lock */false); } else { shift++; // ignored weak-linked class } @@ -5495,7 +5567,13 @@ objc_class::nameForLogging() { // Handle the easy case directly. if (isRealized() || isFuture()) { - if (data()->demangledName) return data()->demangledName; + if (!isAnySwift()) { + return data()->ro()->name; + } + auto rwe = data()->ext(); + if (rwe && rwe->demangledName) { + return rwe->demangledName; + } } char *result; @@ -5518,26 +5596,44 @@ objc_class::nameForLogging() mutex_t DemangleCacheLock; static objc::DenseSet *DemangleCache; const char * -objc_class::demangledName() +objc_class::demangledName(bool needsLock) { + if (!needsLock) { + runtimeLock.assertLocked(); + } + // Return previously demangled name if available. if (isRealized() || isFuture()) { - if (data()->demangledName) return data()->demangledName; + if (!isAnySwift()) { + return data()->ro()->name; + } + auto rwe = data()->ext(); + if (rwe && rwe->demangledName) { + return rwe->demangledName; + } } // Try demangling the mangled name. const char *mangled = mangledName(); char *de = copySwiftV1DemangledName(mangled); + class_rw_ext_t *rwe; + if (isRealized() || isFuture()) { - // Class is already realized or future. + if (needsLock) { + mutex_locker_t lock(runtimeLock); + rwe = data()->extAllocIfNeeded(); + } else { + rwe = data()->extAllocIfNeeded(); + } + // Class is already realized or future. // Save demangling result in rw data. // We may not own runtimeLock so use an atomic operation instead. if (! OSAtomicCompareAndSwapPtrBarrier(nil, (void*)(de ?: mangled), - (void**)&data()->demangledName)) + (void**)&rwe->demangledName)) { if (de) free(de); } - return data()->demangledName; + return rwe->demangledName; } // Class is not yet realized. @@ -5557,7 +5653,6 @@ objc_class::demangledName() // Only objc_copyClassNamesForImage() should get here. // fixme lldb's calls to class_getName() can also get here when // interrogating the dyld shared cache. (rdar://27258517) - // fixme runtimeLock.assertLocked(); // fixme ASSERT(realize); const char *cached; @@ -5583,7 +5678,7 @@ const char *class_getName(Class cls) if (!cls) return "nil"; // fixme lldb calls class_getName() on unrealized classes (rdar://27258517) // ASSERT(cls->isRealized() || cls->isFuture()); - return cls->demangledName(); + return cls->demangledName(/* needs lock */true); } /*********************************************************************** @@ -5608,7 +5703,11 @@ class_getVersion(Class cls) { if (!cls) return 0; ASSERT(cls->isRealized()); - return cls->data()->version; + auto rwe = cls->data()->ext(); + if (rwe) { + return rwe->version; + } + return cls->isMetaClass() ? 7 : 0; } @@ -5622,7 +5721,13 @@ class_setVersion(Class cls, int version) { if (!cls) return; ASSERT(cls->isRealized()); - cls->data()->version = version; + auto rwe = cls->data()->ext(); + if (!rwe) { + mutex_locker_t lock(runtimeLock); + rwe = cls->data()->extAllocIfNeeded(); + } + + rwe->version = version; } /*********************************************************************** @@ -5702,7 +5807,7 @@ search_method_list(const method_list_t *mlist, SEL sel) * method_lists_contains_any **********************************************************************/ static NEVER_INLINE bool -method_lists_contains_any(method_list_t **mlists, method_list_t **end, +method_lists_contains_any(method_list_t * const *mlists, method_list_t * const *end, SEL sels[], size_t selcount) { while (mlists < end) { @@ -5743,8 +5848,9 @@ getMethodNoSuper_nolock(Class cls, SEL sel) // fixme nil cls? // fixme nil sel? - for (auto mlists = cls->data()->methods.beginLists(), - end = cls->data()->methods.endLists(); + auto const methods = cls->data()->methods(); + for (auto mlists = methods.beginLists(), + end = methods.endLists(); mlists != end; ++mlists) { @@ -6148,7 +6254,7 @@ objc_property_t class_getProperty(Class cls, const char *name) ASSERT(cls->isRealized()); for ( ; cls; cls = cls->superclass) { - for (auto& prop : cls->data()->properties) { + for (auto& prop : cls->data()->properties()) { if (0 == strcmp(name, prop.name)) { return (objc_property_t)∝ } @@ -6290,7 +6396,7 @@ adjustCustomFlagsForMethodChange(Class cls, method_t *meth) const uint8_t * class_getIvarLayout(Class cls) { - if (cls) return cls->data()->ro->ivarLayout; + if (cls) return cls->data()->ro()->ivarLayout; else return nil; } @@ -6304,7 +6410,7 @@ class_getIvarLayout(Class cls) const uint8_t * class_getWeakIvarLayout(Class cls) { - if (cls) return cls->data()->ro->weakIvarLayout; + if (cls) return cls->data()->ro()->weakIvarLayout; else return nil; } @@ -6388,7 +6494,7 @@ static ivar_t *getIvar(Class cls, const char *name) const ivar_list_t *ivars; ASSERT(cls->isRealized()); - if ((ivars = cls->data()->ro->ivars)) { + if ((ivars = cls->data()->ro()->ivars)) { for (auto& ivar : *ivars) { if (!ivar.offset) continue; // anonymous bitfield @@ -6413,7 +6519,7 @@ Class _class_getClassForIvar(Class cls, Ivar ivar) mutex_locker_t lock(runtimeLock); for ( ; cls; cls = cls->superclass) { - if (auto ivars = cls->data()->ro->ivars) { + if (auto ivars = cls->data()->ro()->ivars) { if (ivars->containsIvar(ivar)) { return cls; } @@ -6463,7 +6569,7 @@ BOOL class_conformsToProtocol(Class cls, Protocol *proto_gen) ASSERT(cls->isRealized()); - for (const auto& proto_ref : cls->data()->protocols) { + for (const auto& proto_ref : cls->data()->protocols()) { protocol_t *p = remapProtocol(proto_ref); if (p == proto || protocol_conformsToProtocol_nolock(p, proto)) { return YES; @@ -6500,6 +6606,8 @@ addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace) result = _method_setImplementation(cls, m, imp); } } else { + auto rwe = cls->data()->extAllocIfNeeded(); + // fixme optimize method_list_t *newlist; newlist = (method_list_t *)calloc(sizeof(*newlist), 1); @@ -6511,7 +6619,7 @@ addMethod(Class cls, SEL name, IMP imp, const char *types, bool replace) newlist->first.imp = imp; prepareMethodLists(cls, &newlist, 1, NO, NO); - cls->data()->methods.attachLists(&newlist, 1); + rwe->methods.attachLists(&newlist, 1); flushCaches(cls); result = nil; @@ -6578,6 +6686,8 @@ addMethods(Class cls, const SEL *names, const IMP *imps, const char **types, } if (newlist->count > 0) { + auto rwe = cls->data()->extAllocIfNeeded(); + // fixme resize newlist because it may have been over-allocated above. // Note that realloc() alone doesn't work due to ptrauth. @@ -6585,7 +6695,7 @@ addMethods(Class cls, const SEL *names, const IMP *imps, const char **types, std::stable_sort(newlist->begin(), newlist->end(), sorter); prepareMethodLists(cls, &newlist, 1, NO, NO); - cls->data()->methods.attachLists(&newlist, 1); + rwe->methods.attachLists(&newlist, 1); flushCaches(cls); } else { // Attaching the method list to the class consumes it. If we don't @@ -6685,7 +6795,7 @@ class_addIvar(Class cls, const char *name, size_t size, // fixme allocate less memory here ivar_list_t *oldlist, *newlist; - if ((oldlist = (ivar_list_t *)cls->data()->ro->ivars)) { + if ((oldlist = (ivar_list_t *)cls->data()->ro()->ivars)) { size_t oldsize = oldlist->byteSize(); newlist = (ivar_list_t *)calloc(oldsize + oldlist->entsize(), 1); memcpy(newlist, oldlist, oldsize); @@ -6735,6 +6845,7 @@ BOOL class_addProtocol(Class cls, Protocol *protocol_gen) if (class_conformsToProtocol(cls, protocol_gen)) return NO; mutex_locker_t lock(runtimeLock); + auto rwe = cls->data()->extAllocIfNeeded(); ASSERT(cls->isRealized()); @@ -6744,7 +6855,7 @@ BOOL class_addProtocol(Class cls, Protocol *protocol_gen) protolist->count = 1; protolist->list[0] = (protocol_ref_t)protocol; - cls->data()->protocols.attachLists(&protolist, 1); + rwe->protocols.attachLists(&protolist, 1); // fixme metaclass? @@ -6779,6 +6890,7 @@ _class_addProperty(Class cls, const char *name, } else { mutex_locker_t lock(runtimeLock); + auto rwe = cls->data()->extAllocIfNeeded(); ASSERT(cls->isRealized()); @@ -6789,7 +6901,7 @@ _class_addProperty(Class cls, const char *name, proplist->first.name = strdupIfMutable(name); proplist->first.attributes = copyPropertyAttributeString(attrs, count); - cls->data()->properties.attachLists(&proplist, 1); + rwe->properties.attachLists(&proplist, 1); return YES; } @@ -6910,6 +7022,10 @@ objc_duplicateClass(Class original, const char *name, checkIsKnownClass(original); + auto orig_rw = original->data(); + auto orig_rwe = orig_rw->ext(); + auto orig_ro = orig_rw->ro(); + ASSERT(original->isRealized()); ASSERT(!original->isMetaClass()); @@ -6920,23 +7036,31 @@ objc_duplicateClass(Class original, const char *name, duplicate->cache.initializeToEmpty(); - class_rw_t *rw = (class_rw_t *)calloc(sizeof(*original->data()), 1); - rw->flags = (original->data()->flags | RW_COPIED_RO | RW_REALIZING); - rw->version = original->data()->version; + class_rw_t *rw = objc::zalloc(); + rw->flags = (orig_rw->flags | RW_COPIED_RO | RW_REALIZING); rw->firstSubclass = nil; rw->nextSiblingClass = nil; duplicate->bits = original->bits; duplicate->setData(rw); - rw->ro = original->data()->ro->duplicate(); - *(char **)&rw->ro->name = strdupIfMutable(name); + auto ro = orig_ro->duplicate(); + *(char **)&ro->name = strdupIfMutable(name); + rw->set_ro(ro); - rw->methods = original->data()->methods.duplicate(); + if (orig_rwe) { + auto rwe = rw->extAllocIfNeeded(); + rwe->version = orig_rwe->version; + rwe->methods = orig_rwe->methods.duplicate(); - // fixme dies when categories are added to the base - rw->properties = original->data()->properties; - rw->protocols = original->data()->protocols; + // fixme dies when categories are added to the base + rwe->properties = orig_rwe->properties; + rwe->protocols = orig_rwe->protocols; + } else if (ro->baseMethods()) { + // if we have base methods, we need to make a deep copy + // which requires a class_rw_ext_t to be allocated + rw->deepCopy(ro); + } duplicate->chooseClassArrayIndex(); @@ -6949,13 +7073,12 @@ objc_duplicateClass(Class original, const char *name, // Don't methodize class - construction above is correct - addNamedClass(duplicate, duplicate->data()->ro->name); + addNamedClass(duplicate, ro->name); addClassTableEntry(duplicate, /*addMeta=*/false); if (PrintConnecting) { _objc_inform("CLASS: realizing class '%s' (duplicate of %s) %p %p", - name, original->nameForLogging(), - (void*)duplicate, duplicate->data()->ro); + name, original->nameForLogging(), (void*)duplicate, ro); } duplicate->clearInfo(RW_REALIZING); @@ -6976,35 +7099,35 @@ static void objc_initializeClassPair_internal(Class superclass, const char *name runtimeLock.assertLocked(); class_ro_t *cls_ro_w, *meta_ro_w; + class_rw_t *cls_rw_w, *meta_rw_w; - cls->setData((class_rw_t *)calloc(sizeof(class_rw_t), 1)); - meta->setData((class_rw_t *)calloc(sizeof(class_rw_t), 1)); + cls_rw_w = objc::zalloc(); + meta_rw_w = objc::zalloc(); cls_ro_w = (class_ro_t *)calloc(sizeof(class_ro_t), 1); meta_ro_w = (class_ro_t *)calloc(sizeof(class_ro_t), 1); - cls->data()->ro = cls_ro_w; - meta->data()->ro = meta_ro_w; + + cls->setData(cls_rw_w); + cls_rw_w->set_ro(cls_ro_w); + meta->setData(meta_rw_w); + meta_rw_w->set_ro(meta_ro_w); // Set basic info - cls->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING; - meta->data()->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING; - cls->data()->version = 0; - meta->data()->version = 7; + cls_rw_w->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING; + meta_rw_w->flags = RW_CONSTRUCTING | RW_COPIED_RO | RW_REALIZED | RW_REALIZING | RW_META; cls_ro_w->flags = 0; meta_ro_w->flags = RO_META; - if (!superclass) { - cls_ro_w->flags |= RO_ROOT; - meta_ro_w->flags |= RO_ROOT; - } if (superclass) { uint32_t flagsToCopy = RW_FORBIDS_ASSOCIATED_OBJECTS; - cls->data()->flags |= superclass->data()->flags & flagsToCopy; + cls_rw_w->flags |= superclass->data()->flags & flagsToCopy; cls_ro_w->instanceStart = superclass->unalignedInstanceSize(); meta_ro_w->instanceStart = superclass->ISA()->unalignedInstanceSize(); cls->setInstanceSize(cls_ro_w->instanceStart); meta->setInstanceSize(meta_ro_w->instanceStart); } else { + cls_ro_w->flags |= RO_ROOT; + meta_ro_w->flags |= RO_ROOT; cls_ro_w->instanceStart = 0; meta_ro_w->instanceStart = (uint32_t)sizeof(objc_class); cls->setInstanceSize((uint32_t)sizeof(id)); // just an isa @@ -7149,7 +7272,7 @@ void objc_registerClassPair(Class cls) (cls->ISA()->data()->flags & RW_CONSTRUCTED)) { _objc_inform("objc_registerClassPair: class '%s' was already " - "registered!", cls->data()->ro->name); + "registered!", cls->data()->ro()->name); return; } @@ -7158,7 +7281,7 @@ void objc_registerClassPair(Class cls) { _objc_inform("objc_registerClassPair: class '%s' was not " "allocated with objc_allocateClassPair!", - cls->data()->ro->name); + cls->data()->ro()->name); return; } @@ -7167,7 +7290,7 @@ void objc_registerClassPair(Class cls) cls->changeInfo(RW_CONSTRUCTED, RW_CONSTRUCTING | RW_REALIZING); // Add to named class table. - addNamedClass(cls, cls->data()->ro->name); + addNamedClass(cls, cls->data()->ro()->name); } @@ -7258,14 +7381,17 @@ static void free_class(Class cls) if (! cls->isRealized()) return; auto rw = cls->data(); - auto ro = rw->ro; + auto rwe = rw->ext(); + auto ro = rw->ro(); cache_delete(cls); - - for (auto& meth : rw->methods) { - try_free(meth.types); + + if (rwe) { + for (auto& meth : rwe->methods) { + try_free(meth.types); + } + rwe->methods.tryFree(); } - rw->methods.tryFree(); const ivar_list_t *ivars = ro->ivars; if (ivars) { @@ -7277,19 +7403,22 @@ static void free_class(Class cls) try_free(ivars); } - for (auto& prop : rw->properties) { - try_free(prop.name); - try_free(prop.attributes); - } - rw->properties.tryFree(); + if (rwe) { + for (auto& prop : rwe->properties) { + try_free(prop.name); + try_free(prop.attributes); + } + rwe->properties.tryFree(); - rw->protocols.tryFree(); + rwe->protocols.tryFree(); + } try_free(ro->ivarLayout); try_free(ro->weakIvarLayout); try_free(ro->name); try_free(ro); - try_free(rw); + objc::zfree(rwe); + objc::zfree(rw); try_free(cls); } @@ -7307,25 +7436,25 @@ void objc_disposeClassPair(Class cls) // disposing still-unregistered class is OK! _objc_inform("objc_disposeClassPair: class '%s' was not " "allocated with objc_allocateClassPair!", - cls->data()->ro->name); + cls->data()->ro()->name); return; } if (cls->isMetaClass()) { _objc_inform("objc_disposeClassPair: class '%s' is a metaclass, " - "not a class!", cls->data()->ro->name); + "not a class!", cls->data()->ro()->name); return; } // Shouldn't have any live subclasses. if (cls->data()->firstSubclass) { _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, " - "including '%s'!", cls->data()->ro->name, + "including '%s'!", cls->data()->ro()->name, cls->data()->firstSubclass->nameForLogging()); } if (cls->ISA()->data()->firstSubclass) { _objc_inform("objc_disposeClassPair: class '%s' still has subclasses, " - "including '%s'!", cls->data()->ro->name, + "including '%s'!", cls->data()->ro()->name, cls->ISA()->data()->firstSubclass->nameForLogging()); } diff --git a/runtime/objc-zalloc.h b/runtime/objc-zalloc.h new file mode 100644 index 0000000..ef8203c --- /dev/null +++ b/runtime/objc-zalloc.h @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2019 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/** + * @file objc-zalloc.h + * + * "zone allocator" for objc. + * + * Provides packed allocation for data structures the runtime + * almost never frees. + */ + +#ifndef _OBJC_ZALLOC_H +#define _OBJC_ZALLOC_H + +#include +#include +#include + +namespace objc { + +// Darwin malloc always aligns to 16 bytes +#define MALLOC_ALIGNMENT 16 + +class AtomicQueue { +#if __LP64__ + using pair_t = __int128_t; +#else + using pair_t = uint64_t; +#endif + static constexpr auto relaxed = std::memory_order_relaxed; + static constexpr auto release = std::memory_order_release; + + struct Entry { + struct Entry *next; + }; + + union { + struct { + Entry *head; + unsigned long gen; + }; + std::atomic atomic_pair; + pair_t pair; + }; + +public: + void *pop(); + void push_list(void *_head, void *_tail); + inline void push(void *head) + { + push_list(head, head); + } +}; + +template +class Zone { +}; + +template +class Zone { + struct Element { + Element *next; + char buf[sizeof(T) - sizeof(void *)]; + } __attribute__((packed)); + + static AtomicQueue _freelist; + static T *alloc_slow(); + +public: + static T *alloc(); + static void free(T *); +}; + +template +class Zone { +public: + static inline T *alloc() { + return reinterpret_cast(::calloc(sizeof(T), 1)); + } + static inline void free(T *ptr) { + ::free(ptr); + } +}; + +/* + * This allocator returns always zeroed memory, + * and the template needs to be instantiated in objc-zalloc.mm + */ + +template +T *zalloc() +{ + return Zone::alloc(); +} + +template +void zfree(T *e) +{ + Zone::free(e); +} + +}; + +#endif diff --git a/runtime/objc-zalloc.mm b/runtime/objc-zalloc.mm new file mode 100644 index 0000000..cdfd043 --- /dev/null +++ b/runtime/objc-zalloc.mm @@ -0,0 +1,125 @@ +/* + * Copyright (c) 2007 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/** + * @file objc-zalloc.h + * + * "zone allocator" for objc. + * + * Provides packed allocation for data structures the runtime + * almost never frees. + */ + +#include "objc-private.h" +#include "objc-zalloc.h" + +namespace objc { + +void *AtomicQueue::pop() +{ + AtomicQueue l1, l2; + + l1.pair = pair; // non atomic on purpose + + do { + if (l1.head == nullptr) { + return nullptr; + } + l2.head = l1.head->next; + l2.gen = l1.gen + 1; + } while (!atomic_pair.compare_exchange_weak(l1.pair, l2.pair, relaxed, relaxed)); + + return reinterpret_cast(l1.head); +} + +void AtomicQueue::push_list(void *_head, void *_tail) +{ + Entry *head = reinterpret_cast(_head); + Entry *tail = reinterpret_cast(_tail); + AtomicQueue l1, l2; + + l1.pair = pair; // non atomic load on purpose + do { + tail->next = l1.head; + l2.head = head; + l2.gen = l1.gen + 1; + } while (!atomic_pair.compare_exchange_weak(l1.pair, l2.pair, release, relaxed)); +} + +template +constexpr inline +T gcd(T a, T b) +{ + return b == 0 ? a : gcd(b, a % b); +} + +template +AtomicQueue Zone::_freelist; + +template +T *Zone::alloc_slow() +{ + // our malloc aligns to 16 bytes and this code should be used for sizes + // small enough that this should always be an actual malloc bucket. + // + // The point of this code is *NOT* speed but optimal density + constexpr size_t n_elem = MALLOC_ALIGNMENT / gcd(sizeof(T), size_t{MALLOC_ALIGNMENT}); + Element *slab = reinterpret_cast(::calloc(n_elem, sizeof(T))); + for (size_t i = 1; i < n_elem - 1; i++) { + slab[i].next = &slab[i + 1]; + } + _freelist.push_list(reinterpret_cast(&slab[1]), + reinterpret_cast(&slab[n_elem - 1])); + return reinterpret_cast(&slab[0]); +} + +template +T *Zone::alloc() +{ + void *e = _freelist.pop(); + if (e) { + __builtin_bzero(e, sizeof(void *)); + return reinterpret_cast(e); + } + return alloc_slow(); +} + +template +void Zone::free(T *ptr) +{ + if (ptr) { + Element *e = reinterpret_cast(ptr); + __builtin_bzero(e->buf, sizeof(e->buf)); + _freelist.push(e); + } +} + +#if __OBJC2__ +#define ZoneInstantiate(type) \ + template class Zone + +ZoneInstantiate(class_rw_t); +ZoneInstantiate(class_rw_ext_t); +#endif + +} diff --git a/test/runtime.m b/test/runtime.m index c70620b..ec8fda8 100644 --- a/test/runtime.m +++ b/test/runtime.m @@ -126,7 +126,14 @@ int main() count = objc_getClassList(list, count0-1); testassert(list[count0-1] == NULL); testassert(count == count0); - + + // So that demangling works, fake what would have happened with Swift + // and force the "Swift" bit on the class + ((uintptr_t *)objc_getClass(SwiftV1MangledName))[4] |= 2; + ((uintptr_t *)objc_getClass(SwiftV1MangledName2))[4] |= 2; + ((uintptr_t *)objc_getClass(SwiftV1MangledName3))[4] |= 2; + ((uintptr_t *)objc_getClass(SwiftV1MangledName4))[4] |= 2; + count = objc_getClassList(list, count0); testassert(count == count0);