]> git.saurik.com Git - apple/objc4.git/commitdiff
objc4-781.tar.gz macos-10151 v781
authorApple <opensource@apple.com>
Tue, 24 Mar 2020 21:39:08 +0000 (21:39 +0000)
committerApple <opensource@apple.com>
Tue, 24 Mar 2020 21:39:08 +0000 (21:39 +0000)
12 files changed:
objc.xcodeproj/project.pbxproj
runtime/NSObject.mm
runtime/PointerUnion.h [new file with mode: 0644]
runtime/objc-cache.mm
runtime/objc-gdb.h
runtime/objc-os.mm
runtime/objc-private.h
runtime/objc-runtime-new.h
runtime/objc-runtime-new.mm
runtime/objc-zalloc.h [new file with mode: 0644]
runtime/objc-zalloc.mm [new file with mode: 0644]
test/runtime.m

index 3c3853fee3000e1ad33f684ae89783e3fd8ed38f..80c47fb6bf2721fa378151abccf3bbc33f317dea 100644 (file)
@@ -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 */; };
                6E1475E721DFDB1B001357EA /* llvm-DenseMapInfo.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; name = "llvm-DenseMapInfo.h"; path = "runtime/llvm-DenseMapInfo.h"; sourceTree = "<group>"; 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 = "<group>"; 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 = "<group>"; tabWidth = 2; };
+               6E7B0861232DE7CA00689009 /* PointerUnion.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = PointerUnion.h; path = runtime/PointerUnion.h; sourceTree = "<group>"; };
+               6EACB841232C97A400CE9176 /* objc-zalloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "objc-zalloc.h"; path = "runtime/objc-zalloc.h"; sourceTree = "<group>"; };
+               6EACB843232C97B900CE9176 /* objc-zalloc.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = "objc-zalloc.mm"; path = "runtime/objc-zalloc.mm"; sourceTree = "<group>"; };
                6ECD0B1E2244999E00910D88 /* llvm-DenseSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "llvm-DenseSet.h"; path = "runtime/llvm-DenseSet.h"; sourceTree = "<group>"; };
                7213C36221FA7C730090A271 /* NSObject-internal.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = "NSObject-internal.h"; path = "runtime/NSObject-internal.h"; sourceTree = "<group>"; };
                7593EC57202248DF0046AB96 /* objc-object.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = "objc-object.h"; path = "runtime/objc-object.h"; sourceTree = "<group>"; };
                                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 */,
                                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 */,
                                83BE02E70FCCB24D00661494 /* objc-runtime-old.h */,
                                838485E50D6D68A200CEA253 /* objc-sel-set.h */,
                                39ABD71F12F0B61800D1054C /* objc-weak.h */,
+                               6EACB841232C97A400CE9176 /* objc-zalloc.h */,
                        );
                        name = "Project Headers";
                        sourceTree = "<group>";
                                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 */,
                                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 */,
                                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 */,
                                        "-D_LIBCPP_VISIBLE=\"\"",
                                );
                                SDKROOT = macosx.internal;
-                               SUPPORTED_PLATFORMS = "macosx iphoneos";
+                               SUPPORTED_PLATFORMS = "macosx iphoneos appletvos watchos";
                                WARNING_CFLAGS = (
                                        "-Wall",
                                        "-Wextra",
                                        "-D_LIBCPP_VISIBLE=\"\"",
                                );
                                SDKROOT = macosx.internal;
-                               SUPPORTED_PLATFORMS = "macosx iphoneos";
+                               SUPPORTED_PLATFORMS = "macosx iphoneos appletvos watchos";
                                WARNING_CFLAGS = (
                                        "-Wall",
                                        "-Wextra",
index 6a56ec68ffb3bc27518d1352b91f8b2d131b1a0f..f672f4c1eacde36170c01eafe252b83c0253609c 100644 (file)
@@ -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 (file)
index 0000000..85b7846
--- /dev/null
@@ -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 <cstdint>
+#include <atomic>
+
+namespace objc {
+
+template <typename T> struct PointerUnionTypeSelectorReturn {
+  using Return = T;
+};
+
+/// Get a type based on whether two types are the same or not.
+///
+/// For:
+///
+/// \code
+///   using Ret = typename PointerUnionTypeSelector<T1, T2, EQ, NE>::Return;
+/// \endcode
+///
+/// Ret will be EQ type if T1 is same as T2 or NE type otherwise.
+template <typename T1, typename T2, typename RET_EQ, typename RET_NE>
+struct PointerUnionTypeSelector {
+  using Return = typename PointerUnionTypeSelectorReturn<RET_NE>::Return;
+};
+
+template <typename T, typename RET_EQ, typename RET_NE>
+struct PointerUnionTypeSelector<T, T, RET_EQ, RET_NE> {
+  using Return = typename PointerUnionTypeSelectorReturn<RET_EQ>::Return;
+};
+
+template <typename T1, typename T2, typename RET_EQ, typename RET_NE>
+struct PointerUnionTypeSelectorReturn<
+    PointerUnionTypeSelector<T1, T2, RET_EQ, RET_NE>> {
+  using Return =
+      typename PointerUnionTypeSelector<T1, T2, RET_EQ, RET_NE>::Return;
+};
+
+template <class PT1, class PT2>
+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 <typename T> 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<uintptr_t> &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<uintptr_t> &raw, std::memory_order order) const {
+        raw.store(_value, order);
+    }
+
+    template <typename T>
+    bool is() const {
+        using Ty = typename PointerUnionTypeSelector<PT1, T, IsPT1,
+            PointerUnionTypeSelector<PT2, T, IsPT2,
+            UNION_DOESNT_CONTAIN_TYPE<T>>>::Return;
+        return getTag() == Ty::Num;
+    }
+
+    template <typename T> T get() const {
+      ASSERT(is<T>() && "Invalid accessor called");
+      return reinterpret_cast<T>(getPointer());
+    }
+
+    template <typename T> T dyn_cast() const {
+      if (is<T>())
+        return get<T>();
+      return T();
+    }
+};
+
+template <class PT1, class PT2, class PT3, class PT4 = void>
+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 <typename T> 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<uintptr_t> &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<uintptr_t> &raw, std::memory_order order) const {
+        raw.store(_value, order);
+    }
+
+    template <typename T>
+    bool is() const {
+        using Ty = typename PointerUnionTypeSelector<PT1, T, IsPT1,
+            PointerUnionTypeSelector<PT2, T, IsPT2,
+            PointerUnionTypeSelector<PT3, T, IsPT3,
+            PointerUnionTypeSelector<PT4, T, IsPT4,
+               UNION_DOESNT_CONTAIN_TYPE<T>>>>>::Return;
+        return getTag() == Ty::Num;
+    }
+
+    template <typename T> T get() const {
+      ASSERT(is<T>() && "Invalid accessor called");
+      return reinterpret_cast<T>(getPointer());
+    }
+
+    template <typename T> T dyn_cast() const {
+      if (is<T>())
+        return get<T>();
+      return T();
+    }
+};
+
+} // namespace objc
+
+#endif /* DENSEMAPEXTRAS_H */
index 4735a2de74dbce92ee60440496c5129449f97b1f..4e6ca11309fdb2e82657977e04980519f6d57db0 100644 (file)
@@ -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 {
index f1e4a3c2eef2d63701d76632b9878b4c00a87ea7..9cab4a3356edc2bc195a85cf691aae036d37124a 100644 (file)
@@ -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
 
 
index e2f65f28e7bc5bbbfca792a140932c113af334e4..7d600ef4a0ca962be94d42c167029b41574c96e7 100644 (file)
@@ -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
 }
 
 
index fbeaeb5ae67a055fcf8e07dc3a3d8fdee8063197..4d7aab23f5bc5efef2fd866d8f523d7d88ea5d07 100644 (file)
@@ -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);
index 532a353d78b8c5750197f2c4829ba11bd2f15915..d3541cf61e01f2242a543e1b220bae6c18126e93 100644 (file)
@@ -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.
 
 // 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<method_t, method_list_t> 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<method_array_t>();
@@ -969,6 +980,9 @@ class property_array_t :
     typedef list_array_tt<property_t, property_list_t> Super;
 
  public:
+    property_array_t() : Super() { }
+    property_array_t(property_list_t *l) : Super(l) { }
+
     property_array_t duplicate() {
         return Super::duplicate<property_array_t>();
     }
@@ -981,34 +995,58 @@ class protocol_array_t :
     typedef list_array_tt<protocol_ref_t, protocol_list_t> Super;
 
  public:
+    protocol_array_t() : Super() { }
+    protocol_array_t(protocol_list_t *l) : Super(l) { }
+
     protocol_array_t duplicate() {
         return Super::duplicate<protocol_array_t>();
     }
 };
 
+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<uintptr_t> ro_or_rw_ext;
 
     Class firstSubclass;
     Class nextSiblingClass;
 
-    char *demangledName;
+private:
+    using ro_or_rw_ext_t = objc::PointerUnion<const class_ro_t *, class_rw_ext_t *>;
 
-#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 *>();
+    }
+
+    class_rw_ext_t *extAllocIfNeeded() {
+        auto v = get_ro_or_rwe();
+        if (fastpath(v.is<class_rw_ext_t *>())) {
+            return v.get<class_rw_ext_t *>();
+        } else {
+            return extAlloc(v.get<const class_ro_t *>());
+        }
+    }
+
+    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<class_rw_ext_t *>())) {
+            return v.get<class_rw_ext_t *>()->ro;
+        }
+        return v.get<const class_ro_t *>();
+    }
+
+    void set_ro(const class_ro_t *ro) {
+        auto v = get_ro_or_rwe();
+        if (v.is<class_rw_ext_t *>()) {
+            v.get<class_rw_ext_t *>()->ro = ro;
+        } else {
+            set_ro_or_rwe(ro);
+        }
+    }
+
+    const method_array_t methods() const {
+        auto v = get_ro_or_rwe();
+        if (v.is<class_rw_ext_t *>()) {
+            return v.get<class_rw_ext_t *>()->methods;
+        } else {
+            return method_array_t{v.get<const class_ro_t *>()->baseMethods()};
+        }
+    }
+
+    const property_array_t properties() const {
+        auto v = get_ro_or_rwe();
+        if (v.is<class_rw_ext_t *>()) {
+            return v.get<class_rw_ext_t *>()->properties;
+        } else {
+            return property_array_t{v.get<const class_ro_t *>()->baseProperties};
+        }
+    }
+
+    const protocol_array_t protocols() const {
+        auto v = get_ro_or_rwe();
+        if (v.is<class_rw_ext_t *>()) {
+            return v.get<class_rw_ext_t *>()->protocols;
+        } else {
+            return protocol_array_t{v.get<const class_ro_t *>()->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<uint32_t *>(&data()->ro->instanceSize) = newSize;
+            *const_cast<uint32_t *>(&ro->instanceSize) = newSize;
         }
         cache.setFastInstanceSize(newSize);
     }
index be70319c261818ddf88243dccf39cdbbb9d10254..ee1212714af93e80be07e90f1aeedd0581f233e4 100644 (file)
@@ -33,6 +33,7 @@
 #include "objc-runtime-new.h"
 #include "objc-file.h"
 #include "objc-cache.h"
+#include "objc-zalloc.h"
 #include <Block.h>
 #include <objc/message.h>
 #include <mach/shared_region.h>
@@ -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<Class> 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<class_ro_t *>(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<AWZScanner, AWZ, PrintCustomAWZ, scanner::Sco
     static bool isInterestingSelector(SEL sel) {
         return sel == @selector(alloc) || sel == @selector(allocWithZone:);
     }
-    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[2] = { @selector(alloc), @selector(allocWithZone:), };
         return method_lists_contains_any(mlists, end, sels, 2);
     }
@@ -958,7 +972,7 @@ struct RRScanner : scanner::Mixin<RRScanner, RR, PrintCustomRR
                sel == @selector(allowsWeakReference) ||
                sel == @selector(retainWeakReference);
     }
-    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[8] = {
             @selector(retain),
             @selector(release),
@@ -993,7 +1007,7 @@ struct CoreScanner : scanner::Mixin<CoreScanner, Core, PrintCustomCore> {
                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<class_rw_ext_t>();
+
+    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_rw_t>();
     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<class_rw_t>();
+        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++] = &prop;
         }
         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<const char *> *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)&prop;
             }
@@ -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<class_rw_t>();
+    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<class_rw_t>();
+    meta_rw_w  = objc::zalloc<class_rw_t>();
     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 (file)
index 0000000..ef8203c
--- /dev/null
@@ -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 <cstdint>
+#include <atomic>
+#include <cstdlib>
+
+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<pair_t> 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 T, bool useMalloc>
+class Zone {
+};
+
+template<class T>
+class Zone<T, false> {
+    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 T>
+class Zone<T, true> {
+public:
+    static inline T *alloc() {
+        return reinterpret_cast<T *>(::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<class T>
+T *zalloc()
+{
+    return Zone<T, sizeof(T) % MALLOC_ALIGNMENT == 0>::alloc();
+}
+
+template<class T>
+void zfree(T *e)
+{
+    Zone<T, sizeof(T) % MALLOC_ALIGNMENT == 0>::free(e);
+}
+
+};
+
+#endif
diff --git a/runtime/objc-zalloc.mm b/runtime/objc-zalloc.mm
new file mode 100644 (file)
index 0000000..cdfd043
--- /dev/null
@@ -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<void *>(l1.head);
+}
+
+void AtomicQueue::push_list(void *_head, void *_tail)
+{
+    Entry *head = reinterpret_cast<Entry *>(_head);
+    Entry *tail = reinterpret_cast<Entry *>(_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<class T>
+constexpr inline
+T gcd(T a, T b)
+{
+    return b == 0 ? a : gcd(b, a % b);
+}
+
+template<class T>
+AtomicQueue Zone<T, false>::_freelist;
+
+template<class T>
+T *Zone<T, false>::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<Element *>(::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<void *>(&slab[1]),
+                        reinterpret_cast<void *>(&slab[n_elem - 1]));
+    return reinterpret_cast<T *>(&slab[0]);
+}
+
+template<class T>
+T *Zone<T, false>::alloc()
+{
+    void *e = _freelist.pop();
+    if (e) {
+        __builtin_bzero(e, sizeof(void *));
+        return reinterpret_cast<T *>(e);
+    }
+    return alloc_slow();
+}
+
+template<class T>
+void Zone<T, false>::free(T *ptr)
+{
+    if (ptr) {
+        Element *e = reinterpret_cast<Element *>(ptr);
+        __builtin_bzero(e->buf, sizeof(e->buf));
+        _freelist.push(e);
+    }
+}
+
+#if __OBJC2__
+#define ZoneInstantiate(type) \
+       template class Zone<type, sizeof(type) % MALLOC_ALIGNMENT == 0>
+
+ZoneInstantiate(class_rw_t);
+ZoneInstantiate(class_rw_ext_t);
+#endif
+
+}
index c70620bf361e3bf6642049967fbe72ad9249118f..ec8fda8b7776ee294bc45eeedd7668269971b978 100644 (file)
@@ -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);