* @APPLE_LICENSE_HEADER_END@
*/
/*
- * objc-private.h
- * Copyright 1988-1996, NeXT Software, Inc.
+ * objc-private.h
+ * Copyright 1988-1996, NeXT Software, Inc.
*/
#ifndef _OBJC_PRIVATE_H_
#define _OBJC_PRIVATE_H_
-#include "objc-os.h"
+#include "objc-config.h"
+
+/* Isolate ourselves from the definitions of id and Class in the compiler
+ * and public headers.
+ */
+
+#ifdef _OBJC_OBJC_H_
+#error include objc-private.h before other headers
+#endif
+
+#define OBJC_TYPES_DEFINED 1
+#undef OBJC_OLD_DISPATCH_PROTOTYPES
+#define OBJC_OLD_DISPATCH_PROTOTYPES 0
+
+#include <cstddef> // for nullptr_t
+#include <stdint.h>
+#include <assert.h>
+
+// An assert that's disabled for release builds but still ensures the expression compiles.
+#ifdef NDEBUG
+#define ASSERT(x) (void)sizeof(!(x))
+#else
+#define ASSERT(x) assert(x)
+#endif
+
+// `this` is never NULL in C++ unless we encounter UB, but checking for what's impossible
+// is the point of these asserts, so disable the corresponding warning, and let's hope
+// we will reach the assert despite the UB
+#define ASSERT_THIS_NOT_NULL \
+_Pragma("clang diagnostic push") \
+_Pragma("clang diagnostic ignored \"-Wundefined-bool-conversion\"") \
+ASSERT(this) \
+_Pragma("clang diagnostic pop")
+
+
+struct objc_class;
+struct objc_object;
+struct category_t;
+
+typedef struct objc_class *Class;
+typedef struct objc_object *id;
+typedef struct classref *classref_t;
+
+namespace {
+ struct SideTable;
+};
+
+#include "isa.h"
+
+union isa_t {
+ isa_t() { }
+ isa_t(uintptr_t value) : bits(value) { }
+
+ uintptr_t bits;
+
+private:
+ // Accessing the class requires custom ptrauth operations, so
+ // force clients to go through setClass/getClass by making this
+ // private.
+ Class cls;
+
+public:
+#if defined(ISA_BITFIELD)
+ struct {
+ ISA_BITFIELD; // defined in isa.h
+ };
+
+ bool isDeallocating() {
+ return extra_rc == 0 && has_sidetable_rc == 0;
+ }
+ void setDeallocating() {
+ extra_rc = 0;
+ has_sidetable_rc = 0;
+ }
+#endif
+
+ void setClass(Class cls, objc_object *obj);
+ Class getClass(bool authenticated);
+ Class getDecodedClass(bool authenticated);
+};
+
+
+struct objc_object {
+private:
+ isa_t isa;
+
+public:
+
+ // ISA() assumes this is NOT a tagged pointer object
+ Class ISA(bool authenticated = false);
+
+ // rawISA() assumes this is NOT a tagged pointer object or a non pointer ISA
+ Class rawISA();
+
+ // getIsa() allows this to be a tagged pointer object
+ Class getIsa();
+
+ uintptr_t isaBits() const;
+
+ // initIsa() should be used to init the isa of new objects only.
+ // If this object already has an isa, use changeIsa() for correctness.
+ // initInstanceIsa(): objects with no custom RR/AWZ
+ // initClassIsa(): class objects
+ // initProtocolIsa(): protocol objects
+ // initIsa(): other objects
+ void initIsa(Class cls /*nonpointer=false*/);
+ void initClassIsa(Class cls /*nonpointer=maybe*/);
+ void initProtocolIsa(Class cls /*nonpointer=maybe*/);
+ void initInstanceIsa(Class cls, bool hasCxxDtor);
+
+ // changeIsa() should be used to change the isa of existing objects.
+ // If this is a new object, use initIsa() for performance.
+ Class changeIsa(Class newCls);
+
+ bool hasNonpointerIsa();
+ bool isTaggedPointer();
+ bool isTaggedPointerOrNil();
+ bool isBasicTaggedPointer();
+ bool isExtTaggedPointer();
+ bool isClass();
+
+ // object may have associated objects?
+ bool hasAssociatedObjects();
+ void setHasAssociatedObjects();
+
+ // object may be weakly referenced?
+ bool isWeaklyReferenced();
+ void setWeaklyReferenced_nolock();
+
+ // object may have -.cxx_destruct implementation?
+ bool hasCxxDtor();
+
+ // Optimized calls to retain/release methods
+ id retain();
+ void release();
+ id autorelease();
+
+ // Implementations of retain/release methods
+ id rootRetain();
+ bool rootRelease();
+ id rootAutorelease();
+ bool rootTryRetain();
+ bool rootReleaseShouldDealloc();
+ uintptr_t rootRetainCount();
+
+ // Implementation of dealloc methods
+ bool rootIsDeallocating();
+ void clearDeallocating();
+ void rootDealloc();
+
+private:
+ void initIsa(Class newCls, bool nonpointer, bool hasCxxDtor);
+
+ // Slow paths for inline control
+ id rootAutorelease2();
+ uintptr_t overrelease_error();
+
+#if SUPPORT_NONPOINTER_ISA
+ // Controls what parts of root{Retain,Release} to emit/inline
+ // - Full means the full (slow) implementation
+ // - Fast means the fastpaths only
+ // - FastOrMsgSend means the fastpaths but checking whether we should call
+ // -retain/-release or Swift, for the usage of objc_{retain,release}
+ enum class RRVariant {
+ Full,
+ Fast,
+ FastOrMsgSend,
+ };
+
+ // Unified retain count manipulation for nonpointer isa
+ inline id rootRetain(bool tryRetain, RRVariant variant);
+ inline bool rootRelease(bool performDealloc, RRVariant variant);
+ id rootRetain_overflow(bool tryRetain);
+ uintptr_t rootRelease_underflow(bool performDealloc);
+
+ void clearDeallocating_slow();
+
+ // Side table retain count overflow for nonpointer isa
+ struct SidetableBorrow { size_t borrowed, remaining; };
+
+ void sidetable_lock();
+ void sidetable_unlock();
+
+ void sidetable_moveExtraRC_nolock(size_t extra_rc, bool isDeallocating, bool weaklyReferenced);
+ bool sidetable_addExtraRC_nolock(size_t delta_rc);
+ SidetableBorrow sidetable_subExtraRC_nolock(size_t delta_rc);
+ size_t sidetable_getExtraRC_nolock();
+ void sidetable_clearExtraRC_nolock();
+#endif
+
+ // Side-table-only retain count
+ bool sidetable_isDeallocating();
+ void sidetable_clearDeallocating();
+
+ bool sidetable_isWeaklyReferenced();
+ void sidetable_setWeaklyReferenced_nolock();
+
+ id sidetable_retain(bool locked = false);
+ id sidetable_retain_slow(SideTable& table);
+
+ uintptr_t sidetable_release(bool locked = false, bool performDealloc = true);
+ uintptr_t sidetable_release_slow(SideTable& table, bool performDealloc = true);
+
+ bool sidetable_tryRetain();
+
+ uintptr_t sidetable_retainCount();
+#if DEBUG
+ bool sidetable_present();
+#endif
+};
+
+
+#if __OBJC2__
+typedef struct method_t *Method;
+typedef struct ivar_t *Ivar;
+typedef struct category_t *Category;
+typedef struct property_t *objc_property_t;
+#else
+typedef struct old_method *Method;
+typedef struct old_ivar *Ivar;
+typedef struct old_category *Category;
+typedef struct old_property *objc_property_t;
+#endif
+
+// Public headers
#include "objc.h"
#include "runtime.h"
-#include "maptable.h"
-#include "hashtable2.h"
+#include "objc-os.h"
+#include "objc-abi.h"
#include "objc-api.h"
#include "objc-config.h"
-#include "objc-references.h"
-#include "objc-initialize.h"
-#include "objc-loadmethod.h"
#include "objc-internal.h"
-#include "objc-abi.h"
+#include "maptable.h"
+#include "hashtable2.h"
-#include "objc-auto.h"
+/* Do not include message.h here. */
+/* #include "message.h" */
#define __APPLE_API_PRIVATE
#include "objc-gdb.h"
#undef __APPLE_API_PRIVATE
-/* Do not include message.h here. */
-/* #include "message.h" */
+// Private headers
-__BEGIN_DECLS
+#include "objc-ptrauth.h"
-#ifdef __LP64__
-# define WORD_SHIFT 3UL
-# define WORD_MASK 7UL
+#if __OBJC2__
+#include "objc-runtime-new.h"
#else
-# define WORD_SHIFT 2UL
-# define WORD_MASK 3UL
+#include "objc-runtime-old.h"
#endif
-#if (defined(OBJC_NO_GC) && SUPPORT_GC) || \
- (!defined(OBJC_NO_GC) && !SUPPORT_GC)
-# error OBJC_NO_GC and SUPPORT_GC inconsistent
-#endif
+#include "objc-references.h"
+#include "objc-initialize.h"
+#include "objc-loadmethod.h"
+
+
+#define STRINGIFY(x) #x
+#define STRINGIFY2(x) STRINGIFY(x)
-#if SUPPORT_GC
-# include <auto_zone.h>
- // PRIVATE_EXTERN is needed to help the compiler know "how" extern these are
- PRIVATE_EXTERN extern BOOL UseGC; // equivalent to calling objc_collecting_enabled()
- PRIVATE_EXTERN extern BOOL UseCompaction; // if binary has opted-in for compaction.
- PRIVATE_EXTERN extern auto_zone_t *gc_zone; // the GC zone, or NULL if no GC
- extern void objc_addRegisteredClass(Class c);
- extern void objc_removeRegisteredClass(Class c);
- extern void objc_disableCompaction();
+__BEGIN_DECLS
+
+namespace objc {
+
+struct SafeRanges {
+private:
+ struct Range {
+ uintptr_t start;
+ uintptr_t end;
+
+ inline bool contains(uintptr_t ptr) const {
+ uintptr_t m_start, m_end;
+#if __arm64__
+ // <rdar://problem/48304934> Force the compiler to use ldp
+ // we really don't want 2 loads and 2 jumps.
+ __asm__(
+# if __LP64__
+ "ldp %x[one], %x[two], [%x[src]]"
+# else
+ "ldp %w[one], %w[two], [%x[src]]"
+# endif
+ : [one] "=r" (m_start), [two] "=r" (m_end)
+ : [src] "r" (this)
+ );
#else
-# define UseGC NO
-# define UseCompaction NO
-# define gc_zone NULL
-# define objc_addRegisteredClass(c) do {} while(0)
-# define objc_removeRegisteredClass(c) do {} while(0)
- /* Uses of the following must be protected with UseGC. */
- extern id gc_unsupported_dont_call();
-# define auto_zone_allocate_object gc_unsupported_dont_call
-# define auto_zone_retain gc_unsupported_dont_call
-# define auto_zone_release gc_unsupported_dont_call
-# define auto_zone_is_valid_pointer gc_unsupported_dont_call
-# define auto_zone_write_barrier_memmove gc_unsupported_dont_call
-# define AUTO_OBJECT_SCANNED 0
+ m_start = start;
+ m_end = end;
#endif
+ return m_start <= ptr && ptr < m_end;
+ }
+ };
+
+ struct Range shared_cache;
+ struct Range *ranges;
+ uint32_t count;
+ uint32_t size : 31;
+ uint32_t sorted : 1;
+
+public:
+ inline bool inSharedCache(uintptr_t ptr) const {
+ return shared_cache.contains(ptr);
+ }
+ inline bool contains(uint16_t witness, uintptr_t ptr) const {
+ return witness < count && ranges[witness].contains(ptr);
+ }
-#if __OBJC2__
-typedef struct objc_cache *Cache;
-#else
-// definition in runtime.h
+ inline void setSharedCacheRange(uintptr_t start, uintptr_t end) {
+ shared_cache = Range{start, end};
+ add(start, end);
+ }
+ bool find(uintptr_t ptr, uint32_t &pos);
+ void add(uintptr_t start, uintptr_t end);
+ void remove(uintptr_t start, uintptr_t end);
+};
+
+extern struct SafeRanges dataSegmentsRanges;
+
+static inline bool inSharedCache(uintptr_t ptr) {
+ return dataSegmentsRanges.inSharedCache(ptr);
+}
+
+} // objc
+
+struct header_info;
+
+// Split out the rw data from header info. For now put it in a huge array
+// that more than exceeds the space needed. In future we'll just allocate
+// this in the shared cache builder.
+typedef struct header_info_rw {
+
+ bool getLoaded() const {
+ return isLoaded;
+ }
+
+ void setLoaded(bool v) {
+ isLoaded = v ? 1: 0;
+ }
+
+ bool getAllClassesRealized() const {
+ return allClassesRealized;
+ }
+
+ void setAllClassesRealized(bool v) {
+ allClassesRealized = v ? 1: 0;
+ }
+
+ header_info *getNext() const {
+ return (header_info *)(next << 2);
+ }
+
+ void setNext(header_info *v) {
+ next = ((uintptr_t)v) >> 2;
+ }
+
+private:
+#ifdef __LP64__
+ uintptr_t isLoaded : 1;
+ uintptr_t allClassesRealized : 1;
+ uintptr_t next : 62;
+#else
+ uintptr_t isLoaded : 1;
+ uintptr_t allClassesRealized : 1;
+ uintptr_t next : 30;
#endif
+} header_info_rw;
+struct header_info_rw* getPreoptimizedHeaderRW(const struct header_info *const hdr);
-typedef struct {
- uint32_t version; // currently 0
- uint32_t flags;
-} objc_image_info;
-
-// masks for objc_image_info.flags
-#define OBJC_IMAGE_IS_REPLACEMENT (1<<0)
-#define OBJC_IMAGE_SUPPORTS_GC (1<<1)
-#define OBJC_IMAGE_REQUIRES_GC (1<<2)
-#define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3)
-#define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4)
-
-
-#define _objcHeaderIsReplacement(h) ((h)->info && ((h)->info->flags & OBJC_IMAGE_IS_REPLACEMENT))
-
-/* OBJC_IMAGE_IS_REPLACEMENT:
- Don't load any classes
- Don't load any categories
- Do fix up selector refs (@selector points to them)
- Do fix up class refs (@class and objc_msgSend points to them)
- Do fix up protocols (@protocol points to them)
- Do fix up super_class pointers in classes ([super ...] points to them)
- Future: do load new classes?
- Future: do load new categories?
- Future: do insert new methods on existing classes?
- Future: do insert new methods on existing categories?
-*/
+typedef struct header_info {
+private:
+ // Note, this is no longer a pointer, but instead an offset to a pointer
+ // from this location.
+ intptr_t mhdr_offset;
-#define _objcInfoSupportsGC(info) (((info)->flags & OBJC_IMAGE_SUPPORTS_GC) ? 1 : 0)
-#define _objcInfoRequiresGC(info) (((info)->flags & OBJC_IMAGE_REQUIRES_GC) ? 1 : 0)
-#define _objcInfoSupportsCompaction(info) (((info)->flags & OBJC_IMAGE_SUPPORTS_COMPACTION) ? 1 : 0)
-#define _objcHeaderSupportsGC(h) ((h)->info && _objcInfoSupportsGC((h)->info))
-#define _objcHeaderRequiresGC(h) ((h)->info && _objcInfoRequiresGC((h)->info))
-#define _objcHeaderSupportsCompaction(h) ((h)->info && _objcInfoSupportsCompaction((h)->info))
+ // Note, this is no longer a pointer, but instead an offset to a pointer
+ // from this location.
+ intptr_t info_offset;
-/* OBJC_IMAGE_SUPPORTS_GC:
- was compiled with -fobjc-gc flag, regardless of whether write-barriers were issued
- if executable image compiled this way, then all subsequent libraries etc. must also be this way
-*/
+ // Offset from this location to the non-lazy class list
+ intptr_t nlclslist_offset;
+ uintptr_t nlclslist_count;
-#define _objcHeaderOptimizedByDyld(h) ((h)->info && ((h)->info->flags & OBJC_IMAGE_OPTIMIZED_BY_DYLD))
+ // Offset from this location to the non-lazy category list
+ intptr_t nlcatlist_offset;
+ uintptr_t nlcatlist_count;
-/* OBJC_IMAGE_OPTIMIZED_BY_DYLD:
- Assorted metadata precooked in the dyld shared cache.
- Never set for images outside the shared cache file itself.
-*/
-
+ // Offset from this location to the category list
+ intptr_t catlist_offset;
+ uintptr_t catlist_count;
-typedef struct _header_info {
- struct _header_info *next;
- const headerType *mhdr;
- const objc_image_info *info;
- const char *fname; // same as Dl_info.dli_fname
- bool loaded;
- bool inSharedCache;
- bool allClassesRealized;
+ // Offset from this location to the category list 2
+ intptr_t catlist2_offset;
+ uintptr_t catlist2_count;
// Do not add fields without editing ObjCModernAbstraction.hpp
+public:
+
+ header_info_rw *getHeaderInfoRW() {
+ header_info_rw *preopt =
+ isPreoptimized() ? getPreoptimizedHeaderRW(this) : nil;
+ if (preopt) return preopt;
+ else return &rw_data[0];
+ }
+
+ const headerType *mhdr() const {
+ return (const headerType *)(((intptr_t)&mhdr_offset) + mhdr_offset);
+ }
+
+ void setmhdr(const headerType *mhdr) {
+ mhdr_offset = (intptr_t)mhdr - (intptr_t)&mhdr_offset;
+ }
+
+ const objc_image_info *info() const {
+ return (const objc_image_info *)(((intptr_t)&info_offset) + info_offset);
+ }
+
+ void setinfo(const objc_image_info *info) {
+ info_offset = (intptr_t)info - (intptr_t)&info_offset;
+ }
+
+ const classref_t *nlclslist(size_t *outCount) const;
+
+ void set_nlclslist(const void *list) {
+ nlclslist_offset = (intptr_t)list - (intptr_t)&nlclslist_offset;
+ }
+
+ category_t * const *nlcatlist(size_t *outCount) const;
+
+ void set_nlcatlist(const void *list) {
+ nlcatlist_offset = (intptr_t)list - (intptr_t)&nlcatlist_offset;
+ }
+
+ category_t * const *catlist(size_t *outCount) const;
+
+ void set_catlist(const void *list) {
+ catlist_offset = (intptr_t)list - (intptr_t)&catlist_offset;
+ }
+
+ category_t * const *catlist2(size_t *outCount) const;
+
+ void set_catlist2(const void *list) {
+ catlist2_offset = (intptr_t)list - (intptr_t)&catlist2_offset;
+ }
+
+ bool isLoaded() {
+ return getHeaderInfoRW()->getLoaded();
+ }
+
+ void setLoaded(bool v) {
+ getHeaderInfoRW()->setLoaded(v);
+ }
+
+ bool areAllClassesRealized() {
+ return getHeaderInfoRW()->getAllClassesRealized();
+ }
+
+ void setAllClassesRealized(bool v) {
+ getHeaderInfoRW()->setAllClassesRealized(v);
+ }
+
+ header_info *getNext() {
+ return getHeaderInfoRW()->getNext();
+ }
+
+ void setNext(header_info *v) {
+ getHeaderInfoRW()->setNext(v);
+ }
+
+ bool isBundle() {
+ return mhdr()->filetype == MH_BUNDLE;
+ }
+
+ const char *fname() const {
+ return dyld_image_path_containing_address(mhdr());
+ }
+
+ bool isPreoptimized() const;
+
+ bool hasPreoptimizedSelectors() const;
+
+ bool hasPreoptimizedClasses() const;
+
+ bool hasPreoptimizedProtocols() const;
+
+ bool hasPreoptimizedSectionLookups() const;
#if !__OBJC2__
struct old_protocol **proto_refs;
TCHAR *moduleName;
# endif
#endif
+
+private:
+ // Images in the shared cache will have an empty array here while those
+ // allocated at run time will allocate a single entry.
+ header_info_rw rw_data[];
} header_info;
extern header_info *FirstHeader;
extern header_info *LastHeader;
-extern int HeaderCount;
extern void appendHeader(header_info *hi);
extern void removeHeader(header_info *hi);
extern objc_image_info *_getObjcImageInfo(const headerType *head, size_t *size);
-extern BOOL _hasObjcContents(const header_info *hi);
+extern bool _hasObjcContents(const header_info *hi);
+
+
+// Mach-O segment and section names are 16 bytes and may be un-terminated.
+
+static inline bool segnameEquals(const char *lhs, const char *rhs) {
+ return 0 == strncmp(lhs, rhs, 16);
+}
+
+static inline bool segnameStartsWith(const char *segname, const char *prefix) {
+ return 0 == strncmp(segname, prefix, strlen(prefix));
+}
+
+static inline bool sectnameEquals(const char *lhs, const char *rhs) {
+ return segnameEquals(lhs, rhs);
+}
+
+static inline bool sectnameStartsWith(const char *sectname, const char *prefix){
+ return segnameStartsWith(sectname, prefix);
+}
+
+
+#if __OBJC2__
+extern bool didCallDyldNotifyRegister;
+#endif
/* selectors */
-extern void sel_init(BOOL gc, size_t selrefCount);
-extern SEL sel_registerNameNoLock(const char *str, BOOL copy);
-extern void sel_lock(void);
-extern void sel_unlock(void);
-extern BOOL sel_preoptimizationValid(const header_info *hi);
-
-extern SEL SEL_load;
-extern SEL SEL_initialize;
-extern SEL SEL_resolveClassMethod;
-extern SEL SEL_resolveInstanceMethod;
+extern void sel_init(size_t selrefCount);
+extern SEL sel_registerNameNoLock(const char *str, bool copy);
+
extern SEL SEL_cxx_construct;
extern SEL SEL_cxx_destruct;
-extern SEL SEL_retain;
-extern SEL SEL_release;
-extern SEL SEL_autorelease;
-extern SEL SEL_retainCount;
-extern SEL SEL_alloc;
-extern SEL SEL_allocWithZone;
-extern SEL SEL_copy;
-extern SEL SEL_new;
-extern SEL SEL_finalize;
-extern SEL SEL_forwardInvocation;
/* preoptimization */
extern void preopt_init(void);
extern void disableSharedCacheOptimizations(void);
extern bool isPreoptimized(void);
+extern bool noMissingWeakSuperclasses(void);
extern header_info *preoptimizedHinfoForHeader(const headerType *mhdr);
-#if __cplusplus
-namespace objc_opt { struct objc_selopt_t; };
-extern const struct objc_opt::objc_selopt_t *preoptimizedSelectors(void);
-extern struct class_t * getPreoptimizedClass(const char *name);
-#endif
+extern Protocol *getPreoptimizedProtocol(const char *name);
+extern Protocol *getSharedCachePreoptimizedProtocol(const char *name);
+
+extern unsigned getPreoptimizedClassUnreasonableCount();
+extern Class getPreoptimizedClass(const char *name);
+extern Class* copyPreoptimizedClasses(const char *name, int *outCount);
+extern Class _calloc_class(size_t size);
+/* method lookup */
+enum {
+ LOOKUP_INITIALIZE = 1,
+ LOOKUP_RESOLVER = 2,
+ LOOKUP_NIL = 4,
+ LOOKUP_NOCACHE = 8,
+};
+extern IMP lookUpImpOrForward(id obj, SEL, Class cls, int behavior);
+extern IMP lookUpImpOrForwardTryCache(id obj, SEL, Class cls, int behavior = 0);
+extern IMP lookUpImpOrNilTryCache(id obj, SEL, Class cls, int behavior = 0);
-/* optional malloc zone for runtime data */
-extern malloc_zone_t *_objc_internal_zone(void);
-extern void *_malloc_internal(size_t size);
-extern void *_calloc_internal(size_t count, size_t size);
-extern void *_realloc_internal(void *ptr, size_t size);
-extern char *_strdup_internal(const char *str);
-extern char *_strdupcat_internal(const char *s1, const char *s2);
-extern uint8_t *_ustrdup_internal(const uint8_t *str);
-extern void *_memdup_internal(const void *mem, size_t size);
-extern void _free_internal(void *ptr);
-extern size_t _malloc_size_internal(void *ptr);
+extern IMP lookupMethodInClassAndLoadCache(Class cls, SEL sel);
-extern Class _calloc_class(size_t size);
+struct IMPAndSEL {
+ IMP imp;
+ SEL sel;
+};
+
+extern IMPAndSEL _method_getImplementationAndName(Method m);
-extern IMP lookUpMethod(Class, SEL, BOOL initialize, BOOL cache, id obj);
-extern void lockForMethodLookup(void);
-extern void unlockForMethodLookup(void);
-extern IMP prepareForMethodLookup(Class cls, SEL sel, BOOL initialize, id obj);
+extern BOOL class_respondsToSelector_inst(id inst, SEL sel, Class cls);
+extern Class class_initialize(Class cls, id inst);
-extern IMP _cache_getImp(Class cls, SEL sel);
-extern Method _cache_getMethod(Class cls, SEL sel, IMP objc_msgForward_internal_imp);
+extern bool objcMsgLogEnabled;
+extern bool logMessageSend(bool isClassMethod,
+ const char *objectsClass,
+ const char *implementingClass,
+ SEL selector);
/* message dispatcher */
-extern IMP _class_lookupMethodAndLoadCache3(id, SEL, Class);
#if !OBJC_OLD_DISPATCH_PROTOTYPES
-extern void _objc_msgForward_internal(void);
-extern void _objc_ignored_method(void);
+extern void _objc_msgForward_impcache(void);
#else
-extern id _objc_msgForward_internal(id, SEL, ...);
-extern id _objc_ignored_method(id, SEL, ...);
+extern id _objc_msgForward_impcache(id, SEL, ...);
#endif
/* errors */
-extern void __objc_error(id, const char *, ...) __attribute__((format (printf, 2, 3), noreturn));
-extern void _objc_inform(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern void _objc_inform_on_crash(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern void _objc_inform_now_and_on_crash(const char *fmt, ...) __attribute__((format (printf, 1, 2)));
-extern void _objc_inform_deprecated(const char *oldname, const char *newname) __attribute__((noinline));
+extern id(*badAllocHandler)(Class);
+extern id _objc_callBadAllocHandler(Class cls) __attribute__((cold, noinline));
+extern void __objc_error(id, const char *, ...) __attribute__((cold, format (printf, 2, 3), noreturn));
+extern void _objc_inform(const char *fmt, ...) __attribute__((cold, format(printf, 1, 2)));
+extern void _objc_inform_on_crash(const char *fmt, ...) __attribute__((cold, format (printf, 1, 2)));
+extern void _objc_inform_now_and_on_crash(const char *fmt, ...) __attribute__((cold, format (printf, 1, 2)));
+extern void _objc_inform_deprecated(const char *oldname, const char *newname) __attribute__((cold, noinline));
extern void inform_duplicate(const char *name, Class oldCls, Class cls);
-extern bool crashlog_header_name(header_info *hi);
-extern bool crashlog_header_name_string(const char *name);
/* magic */
extern Class _objc_getFreedObjectClass (void);
extern objc_property_attribute_t *copyPropertyAttributeList(const char *attrs, unsigned int *outCount);
extern char *copyPropertyAttributeValue(const char *attrs, const char *name);
-
/* locking */
-/* Every lock used anywhere must be declared here.
- * Locks not declared here may cause gdb deadlocks. */
-extern void lock_init(void);
-extern rwlock_t selLock;
-extern mutex_t cacheUpdateLock;
-extern recursive_mutex_t loadMethodLock;
-#if __OBJC2__
-extern rwlock_t runtimeLock;
-#else
-extern mutex_t classLock;
-extern mutex_t methodListLock;
-#endif
-
-/* Debugger mode for gdb */
-#define DEBUGGER_OFF 0
-#define DEBUGGER_PARTIAL 1
-#define DEBUGGER_FULL 2
-extern int startDebuggerMode(void);
-extern void endDebuggerMode(void);
-
-#if defined(NDEBUG) || TARGET_OS_WIN32
-
-#define mutex_lock(m) _mutex_lock_nodebug(m)
-#define mutex_try_lock(m) _mutex_try_lock_nodebug(m)
-#define mutex_unlock(m) _mutex_unlock_nodebug(m)
-#define mutex_assert_locked(m) do { } while (0)
-#define mutex_assert_unlocked(m) do { } while (0)
-
-#define recursive_mutex_lock(m) _recursive_mutex_lock_nodebug(m)
-#define recursive_mutex_try_lock(m) _recursive_mutex_try_lock_nodebug(m)
-#define recursive_mutex_unlock(m) _recursive_mutex_unlock_nodebug(m)
-#define recursive_mutex_assert_locked(m) do { } while (0)
-#define recursive_mutex_assert_unlocked(m) do { } while (0)
-
-#define monitor_enter(m) _monitor_enter_nodebug(m)
-#define monitor_exit(m) _monitor_exit_nodebug(m)
-#define monitor_wait(m) _monitor_wait_nodebug(m)
-#define monitor_assert_locked(m) do { } while (0)
-#define monitor_assert_unlocked(m) do { } while (0)
-
-#define rwlock_read(m) _rwlock_read_nodebug(m)
-#define rwlock_write(m) _rwlock_write_nodebug(m)
-#define rwlock_try_read(m) _rwlock_try_read_nodebug(m)
-#define rwlock_try_write(m) _rwlock_try_write_nodebug(m)
-#define rwlock_unlock_read(m) _rwlock_unlock_read_nodebug(m)
-#define rwlock_unlock_write(m) _rwlock_unlock_write_nodebug(m)
-#define rwlock_assert_reading(m) do { } while (0)
-#define rwlock_assert_writing(m) do { } while (0)
-#define rwlock_assert_locked(m) do { } while (0)
-#define rwlock_assert_unlocked(m) do { } while (0)
-
-#else
-extern int _mutex_lock_debug(mutex_t *lock, const char *name);
-extern int _mutex_try_lock_debug(mutex_t *lock, const char *name);
-extern int _mutex_unlock_debug(mutex_t *lock, const char *name);
-extern void _mutex_assert_locked_debug(mutex_t *lock, const char *name);
-extern void _mutex_assert_unlocked_debug(mutex_t *lock, const char *name);
-
-extern int _recursive_mutex_lock_debug(recursive_mutex_t *lock, const char *name);
-extern int _recursive_mutex_try_lock_debug(recursive_mutex_t *lock, const char *name);
-extern int _recursive_mutex_unlock_debug(recursive_mutex_t *lock, const char *name);
-extern void _recursive_mutex_assert_locked_debug(recursive_mutex_t *lock, const char *name);
-extern void _recursive_mutex_assert_unlocked_debug(recursive_mutex_t *lock, const char *name);
-
-extern int _monitor_enter_debug(monitor_t *lock, const char *name);
-extern int _monitor_exit_debug(monitor_t *lock, const char *name);
-extern int _monitor_wait_debug(monitor_t *lock, const char *name);
-extern void _monitor_assert_locked_debug(monitor_t *lock, const char *name);
-extern void _monitor_assert_unlocked_debug(monitor_t *lock, const char *name);
-
-extern void _rwlock_read_debug(rwlock_t *l, const char *name);
-extern void _rwlock_write_debug(rwlock_t *l, const char *name);
-extern int _rwlock_try_read_debug(rwlock_t *l, const char *name);
-extern int _rwlock_try_write_debug(rwlock_t *l, const char *name);
-extern void _rwlock_unlock_read_debug(rwlock_t *l, const char *name);
-extern void _rwlock_unlock_write_debug(rwlock_t *l, const char *name);
-extern void _rwlock_assert_reading_debug(rwlock_t *l, const char *name);
-extern void _rwlock_assert_writing_debug(rwlock_t *l, const char *name);
-extern void _rwlock_assert_locked_debug(rwlock_t *l, const char *name);
-extern void _rwlock_assert_unlocked_debug(rwlock_t *l, const char *name);
-
-#define mutex_lock(m) _mutex_lock_debug (m, #m)
-#define mutex_try_lock(m) _mutex_try_lock_debug (m, #m)
-#define mutex_unlock(m) _mutex_unlock_debug (m, #m)
-#define mutex_assert_locked(m) _mutex_assert_locked_debug (m, #m)
-#define mutex_assert_unlocked(m) _mutex_assert_unlocked_debug (m, #m)
-
-#define recursive_mutex_lock(m) _recursive_mutex_lock_debug (m, #m)
-#define recursive_mutex_try_lock(m) _recursive_mutex_try_lock_debug (m, #m)
-#define recursive_mutex_unlock(m) _recursive_mutex_unlock_debug (m, #m)
-#define recursive_mutex_assert_locked(m) _recursive_mutex_assert_locked_debug (m, #m)
-#define recursive_mutex_assert_unlocked(m) _recursive_mutex_assert_unlocked_debug (m, #m)
-
-#define monitor_enter(m) _monitor_enter_debug(m, #m)
-#define monitor_exit(m) _monitor_exit_debug(m, #m)
-#define monitor_wait(m) _monitor_wait_debug(m, #m)
-#define monitor_assert_locked(m) _monitor_assert_locked_debug(m, #m)
-#define monitor_assert_unlocked(m) _monitor_assert_unlocked_debug(m, #m)
-
-#define rwlock_read(m) _rwlock_read_debug(m, #m)
-#define rwlock_write(m) _rwlock_write_debug(m, #m)
-#define rwlock_try_read(m) _rwlock_try_read_debug(m, #m)
-#define rwlock_try_write(m) _rwlock_try_write_debug(m, #m)
-#define rwlock_unlock_read(m) _rwlock_unlock_read_debug(m, #m)
-#define rwlock_unlock_write(m) _rwlock_unlock_write_debug(m, #m)
-#define rwlock_assert_reading(m) _rwlock_assert_reading_debug(m, #m)
-#define rwlock_assert_writing(m) _rwlock_assert_writing_debug(m, #m)
-#define rwlock_assert_locked(m) _rwlock_assert_locked_debug(m, #m)
-#define rwlock_assert_unlocked(m) _rwlock_assert_unlocked_debug(m, #m)
+class monitor_locker_t : nocopy_t {
+ monitor_t& lock;
+ public:
+ monitor_locker_t(monitor_t& newLock) : lock(newLock) { lock.enter(); }
+ ~monitor_locker_t() { lock.leave(); }
+};
-#endif
-
-extern bool noSideTableLocksHeld(void);
-
-#define rwlock_unlock(m, s) \
- do { \
- if ((s) == RDONLY) rwlock_unlock_read(m); \
- else if ((s) == RDWR) rwlock_unlock_write(m); \
- } while (0)
-
-
-extern NXHashTable *class_hash;
-
-#if !TARGET_OS_WIN32
-/* nil handler object */
-extern id _objc_nilReceiver;
-extern id _objc_setNilReceiver(id newNilReceiver);
-extern id _objc_getNilReceiver(void);
-#endif
-
-/* forward handler functions */
-extern void *_objc_forward_handler;
-extern void *_objc_forward_stret_handler;
-
-/* tagged pointer support */
-#if SUPPORT_TAGGED_POINTERS
-
-#define OBJC_IS_TAGGED_PTR(PTR) ((uintptr_t)(PTR) & 0x1)
-extern Class _objc_tagged_isa_table[16];
-
-#else
-
-#define OBJC_IS_TAGGED_PTR(PTR) 0
-
-#endif
-
-
-/* ignored selector support */
-
-/* Non-GC: no ignored selectors
- GC without fixup dispatch: some selectors ignored, remapped to kIgnore
- GC with fixup dispatch: some selectors ignored, but not remapped
-*/
-
-static inline int ignoreSelector(SEL sel)
-{
-#if !SUPPORT_GC
- return NO;
-#elif SUPPORT_IGNORED_SELECTOR_CONSTANT
- return UseGC && sel == (SEL)kIgnore;
-#else
- return UseGC &&
- (sel == @selector(retain) ||
- sel == @selector(release) ||
- sel == @selector(autorelease) ||
- sel == @selector(retainCount) ||
- sel == @selector(dealloc));
-#endif
-}
+class recursive_mutex_locker_t : nocopy_t {
+ recursive_mutex_t& lock;
+ public:
+ recursive_mutex_locker_t(recursive_mutex_t& newLock)
+ : lock(newLock) { lock.lock(); }
+ ~recursive_mutex_locker_t() { lock.unlock(); }
+};
-static inline int ignoreSelectorNamed(const char *sel)
-{
-#if !SUPPORT_GC
- return NO;
-#else
- // release retain retainCount dealloc autorelease
- return (UseGC &&
- ( (sel[0] == 'r' && sel[1] == 'e' &&
- (strcmp(&sel[2], "lease") == 0 ||
- strcmp(&sel[2], "tain") == 0 ||
- strcmp(&sel[2], "tainCount") == 0 ))
- ||
- (strcmp(sel, "dealloc") == 0)
- ||
- (sel[0] == 'a' && sel[1] == 'u' &&
- strcmp(&sel[2], "torelease") == 0)));
-#endif
-}
-
-/* Protocol implementation */
-#if !__OBJC2__
-struct old_protocol;
-struct objc_method_description * lookup_protocol_method(struct old_protocol *proto, SEL aSel, BOOL isRequiredMethod, BOOL isInstanceMethod, BOOL recursive);
-#else
-Method _protocol_getMethod(Protocol *p, SEL sel, BOOL isRequiredMethod, BOOL isInstanceMethod, BOOL recursive);
-#endif
-
-/* GC and RTP startup */
-extern void gc_init(BOOL wantsGC, BOOL wantsCompaction);
-extern void gc_init2(void);
-extern void rtp_init(void);
/* Exceptions */
struct alt_handler_list;
extern void gdb_objc_class_changed(Class cls, unsigned long changes, const char *classname)
__attribute__((noinline));
-/* Write barrier implementations */
-extern id objc_assign_strongCast_non_gc(id value, id *dest);
-extern id objc_assign_global_non_gc(id value, id *dest);
-extern id objc_assign_threadlocal_non_gc(id value, id *dest);
-extern id objc_assign_ivar_non_gc(id value, id dest, ptrdiff_t offset);
-extern id objc_assign_strongCast_gc(id val, id *dest);
-extern id objc_assign_global_gc(id val, id *dest);
-extern id objc_assign_threadlocal_gc(id val, id *dest);
-extern id objc_assign_ivar_gc(id value, id dest, ptrdiff_t offset);
-
-extern id objc_getAssociatedObject_non_gc(id object, const void *key);
-extern void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy);
-extern id objc_getAssociatedObject_gc(id object, const void *key);
-extern void objc_setAssociatedObject_gc(id object, const void *key, id value, objc_AssociationPolicy policy);
-
-#if SUPPORT_GC
-
-/* GC weak reference fixup. */
-extern void gc_fixup_weakreferences(id newObject, id oldObject);
-
-/* GC datasegment registration. */
-extern void gc_register_datasegment(uintptr_t base, size_t size);
-extern void gc_unregister_datasegment(uintptr_t base, size_t size);
-extern void gc_fixup_barrier_stubs(const struct dyld_image_info *info);
-
-/* objc_dumpHeap implementation */
-extern BOOL _objc_dumpHeap(auto_zone_t *zone, const char *filename);
-
-/*
- objc_assign_ivar, objc_assign_global, objc_assign_threadlocal, and objc_assign_strongCast MUST NOT be called directly
- from inside libobjc. They live in the data segment, and must be called through the
- following pointer(s) for libobjc to exist in the shared cache.
-
- Note: If we build with GC enabled, gcc will emit calls to the original functions, which will break this.
-*/
-
-extern id (*objc_assign_ivar_internal)(id, id, ptrdiff_t);
-
-#endif
-
-/* Code modification */
-extern size_t objc_branch_size(void *entry, void *target);
-extern size_t objc_write_branch(void *entry, void *target);
-extern size_t objc_cond_branch_size(void *entry, void *target, unsigned cond);
-extern size_t objc_write_cond_branch(void *entry, void *target, unsigned cond);
-#if defined(__i386__) || defined(__x86_64__)
-#define COND_ALWAYS 0xE9 /* JMP rel32 */
-#define COND_NE 0x85 /* JNE rel32 (0F 85) */
-#endif
-
// Settings from environment variables
-#if !SUPPORT_ENVIRON
-# define ENV(x) enum { x = 0 }
-#else
-# define ENV(x) extern int x
-#endif
-ENV(PrintImages); // env OBJC_PRINT_IMAGES
-ENV(PrintLoading); // env OBJC_PRINT_LOAD_METHODS
-ENV(PrintInitializing); // env OBJC_PRINT_INITIALIZE_METHODS
-ENV(PrintResolving); // env OBJC_PRINT_RESOLVED_METHODS
-ENV(PrintConnecting); // env OBJC_PRINT_CLASS_SETUP
-ENV(PrintProtocols); // env OBJC_PRINT_PROTOCOL_SETUP
-ENV(PrintIvars); // env OBJC_PRINT_IVAR_SETUP
-ENV(PrintVtables); // env OBJC_PRINT_VTABLE_SETUP
-ENV(PrintVtableImages); // env OBJC_PRINT_VTABLE_IMAGES
-ENV(PrintFuture); // env OBJC_PRINT_FUTURE_CLASSES
-ENV(PrintRTP); // env OBJC_PRINT_RTP
-ENV(PrintGC); // env OBJC_PRINT_GC
-ENV(PrintPreopt); // env OBJC_PRINT_PREOPTIMIZATION
-ENV(PrintCxxCtors); // env OBJC_PRINT_CXX_CTORS
-ENV(PrintExceptions); // env OBJC_PRINT_EXCEPTIONS
-ENV(PrintExceptionThrow); // env OBJC_PRINT_EXCEPTION_THROW
-ENV(PrintAltHandlers); // env OBJC_PRINT_ALT_HANDLERS
-ENV(PrintDeprecation); // env OBJC_PRINT_DEPRECATION_WARNINGS
-ENV(PrintReplacedMethods); // env OBJC_PRINT_REPLACED_METHODS
-ENV(PrintCaches); // env OBJC_PRINT_CACHE_SETUP
-ENV(PrintPoolHiwat); // env OBJC_PRINT_POOL_HIGHWATER
-ENV(PrintCustomRR); // env OBJC_PRINT_CUSTOM_RR
-ENV(PrintCustomAWZ); // env OBJC_PRINT_CUSTOM_AWZ
-ENV(UseInternalZone); // env OBJC_USE_INTERNAL_ZONE
-
-ENV(DebugUnload); // env OBJC_DEBUG_UNLOAD
-ENV(DebugFragileSuperclasses); // env OBJC_DEBUG_FRAGILE_SUPERCLASSES
-ENV(DebugFinalizers); // env OBJC_DEBUG_FINALIZERS
-ENV(DebugNilSync); // env OBJC_DEBUG_NIL_SYNC
-ENV(DebugNonFragileIvars); // env OBJC_DEBUG_NONFRAGILE_IVARS
-ENV(DebugAltHandlers); // env OBJC_DEBUG_ALT_HANDLERS
-
-ENV(DisableGC); // env OBJC_DISABLE_GC
-ENV(DisableVtables); // env OBJC_DISABLE_VTABLES
-ENV(DisablePreopt); // env OBJC_DISABLE_PREOPTIMIZATION
-
-#undef ENV
+#define OPTION(var, env, help) extern bool var;
+#include "objc-env.h"
+#undef OPTION
extern void environ_init(void);
+extern void runtime_init(void);
-extern void logReplacedMethod(const char *className, SEL s, BOOL isMeta, const char *catName, IMP oldImp, IMP newImp);
-
-static __inline uint32_t _objc_strhash(const char *s) {
- uint32_t hash = 0;
- for (;;) {
- int a = *s++;
- if (0 == a) break;
- hash += (hash << 8) + a;
- }
- return hash;
-}
+extern void logReplacedMethod(const char *className, SEL s, bool isMeta, const char *catName, IMP oldImp, IMP newImp);
// objc per-thread storage
struct _objc_initializing_classes *initializingClasses; // for +initialize
struct SyncCache *syncCache; // for @synchronize
struct alt_handler_list *handlerList; // for exception alt handlers
+ char *printableNames[4]; // temporary demangled names for logging
+ const char **classNameLookups; // for objc_getClass() hooks
+ unsigned classNameLookupsAllocated;
+ unsigned classNameLookupsUsed;
// If you add new fields here, don't forget to update
// _objc_pthread_destroyspecific()
} _objc_pthread_data;
-extern _objc_pthread_data *_objc_fetch_pthread_data(BOOL create);
+extern _objc_pthread_data *_objc_fetch_pthread_data(bool create);
extern void tls_init(void);
-
-// cache.h
-#if TARGET_OS_WIN32
-
-#else
-static inline int isPowerOf2(unsigned long l) { return 1 == __builtin_popcountl(l); }
-#endif
-extern void flush_caches(Class cls, BOOL flush_meta);
-extern void flush_cache(Class cls);
-extern BOOL _cache_fill(Class cls, Method smt, SEL sel);
-extern void _cache_addForwardEntry(Class cls, SEL sel);
-extern IMP _cache_addIgnoredEntry(Class cls, SEL sel);
-extern void _cache_free(Cache cache);
-extern void _cache_collect(bool collectALot);
-
-extern mutex_t cacheUpdateLock;
-
// encoding.h
extern unsigned int encoding_getNumberOfArguments(const char *typedesc);
extern unsigned int encoding_getSizeOfArguments(const char *typedesc);
extern void _destroySyncCache(struct SyncCache *cache);
// arr
-extern void (^objc_arr_log)(const char *, id param);
extern void arr_init(void);
extern id objc_autoreleaseReturnValue(id obj);
+// block trampolines
+extern void _imp_implementationWithBlock_init(void);
+extern IMP _imp_implementationWithBlockNoCopy(id block);
// layout.h
typedef struct {
uint8_t *bits;
size_t bitCount;
size_t bitsAllocated;
- BOOL weak;
+ bool weak;
} layout_bitmap;
-extern layout_bitmap layout_bitmap_create(const unsigned char *layout_string, size_t layoutStringInstanceSize, size_t instanceSize, BOOL weak);
-extern layout_bitmap layout_bitmap_create_empty(size_t instanceSize, BOOL weak);
+extern layout_bitmap layout_bitmap_create(const unsigned char *layout_string, size_t layoutStringInstanceSize, size_t instanceSize, bool weak);
+extern layout_bitmap layout_bitmap_create_empty(size_t instanceSize, bool weak);
extern void layout_bitmap_free(layout_bitmap bits);
extern const unsigned char *layout_string_create(layout_bitmap bits);
extern void layout_bitmap_set_ivar(layout_bitmap bits, const char *type, size_t offset);
extern void layout_bitmap_grow(layout_bitmap *bits, size_t newCount);
extern void layout_bitmap_slide(layout_bitmap *bits, size_t oldPos, size_t newPos);
extern void layout_bitmap_slide_anywhere(layout_bitmap *bits, size_t oldPos, size_t newPos);
-extern BOOL layout_bitmap_splat(layout_bitmap dst, layout_bitmap src,
+extern bool layout_bitmap_splat(layout_bitmap dst, layout_bitmap src,
size_t oldSrcInstanceSize);
-extern BOOL layout_bitmap_or(layout_bitmap dst, layout_bitmap src, const char *msg);
-extern BOOL layout_bitmap_clear(layout_bitmap dst, layout_bitmap src, const char *msg);
+extern bool layout_bitmap_or(layout_bitmap dst, layout_bitmap src, const char *msg);
+extern bool layout_bitmap_clear(layout_bitmap dst, layout_bitmap src, const char *msg);
extern void layout_bitmap_print(layout_bitmap bits);
// fixme runtime
-extern id look_up_class(const char *aClassName, BOOL includeUnconnected, BOOL includeClassHandler);
-extern const char *map_images(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]);
-extern const char *map_images_nolock(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]);
-extern const char * load_images(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]);
-extern BOOL load_images_nolock(enum dyld_image_states state, uint32_t infoCount, const struct dyld_image_info infoList[]);
-extern void unmap_image(const struct mach_header *mh, intptr_t vmaddr_slide);
+extern bool MultithreadedForkChild;
+extern id objc_noop_imp(id self, SEL _cmd);
+extern Class look_up_class(const char *aClassName, bool includeUnconnected, bool includeClassHandler);
+extern "C" void map_images(unsigned count, const char * const paths[],
+ const struct mach_header * const mhdrs[]);
+extern void map_images_nolock(unsigned count, const char * const paths[],
+ const struct mach_header * const mhdrs[]);
+extern void load_images(const char *path, const struct mach_header *mh);
+extern void unmap_image(const char *path, const struct mach_header *mh);
extern void unmap_image_nolock(const struct mach_header *mh);
-extern void _read_images(header_info **hList, uint32_t hCount);
-extern void prepare_load_methods(header_info *hi);
+extern void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClass);
extern void _unload_image(header_info *hi);
-extern const char ** _objc_copyClassNamesForImage(header_info *hi, unsigned int *outCount);
-
-extern Class _objc_allocateFutureClass(const char *name);
-
extern const header_info *_headerForClass(Class cls);
-extern Class _class_getSuperclass(Class cls);
extern Class _class_remap(Class cls);
-extern BOOL _class_getInfo(Class cls, int info);
-extern const char *_class_getName(Class cls);
-extern size_t _class_getInstanceSize(Class cls);
-extern Class _class_getMeta(Class cls);
-extern BOOL _class_isMetaClass(Class cls);
-extern Cache _class_getCache(Class cls);
-extern void _class_setCache(Class cls, Cache cache);
-extern BOOL _class_isInitializing(Class cls);
-extern BOOL _class_isInitialized(Class cls);
-extern void _class_setInitializing(Class cls);
-extern void _class_setInitialized(Class cls);
-extern Class _class_getNonMetaClass(Class cls, id obj);
-extern Method _class_getMethod(Class cls, SEL sel);
-extern Method _class_getMethodNoSuper(Class cls, SEL sel);
-extern Method _class_getMethodNoSuper_nolock(Class cls, SEL sel);
-extern BOOL _class_isLoadable(Class cls);
-extern IMP _class_getLoadMethod(Class cls);
-extern BOOL _class_hasLoadMethod(Class cls);
-extern BOOL _class_hasCxxStructors(Class cls);
-extern BOOL _class_shouldFinalizeOnMainThread(Class cls);
-extern void _class_setFinalizeOnMainThread(Class cls);
-extern BOOL _class_instancesHaveAssociatedObjects(Class cls);
-extern void _class_setInstancesHaveAssociatedObjects(Class cls);
-extern BOOL _class_shouldGrowCache(Class cls);
-extern void _class_setGrowCache(Class cls, BOOL grow);
-extern Ivar _class_getVariable(Class cls, const char *name, Class *memberOf);
-extern BOOL _class_usesAutomaticRetainRelease(Class cls);
-extern uint32_t _class_getInstanceStart(Class cls);
+extern Ivar _class_getVariable(Class cls, const char *name);
extern unsigned _class_createInstancesFromZone(Class cls, size_t extraBytes, void *zone, id *results, unsigned num_requested);
-extern id _objc_constructOrFree(Class cls, void *bytes);
extern const char *_category_getName(Category cat);
extern const char *_category_getClassName(Category cat);
extern Class _category_getClass(Category cat);
extern IMP _category_getLoadMethod(Category cat);
-extern BOOL object_cxxConstruct(id obj);
+enum {
+ OBJECT_CONSTRUCT_NONE = 0,
+ OBJECT_CONSTRUCT_FREE_ONFAILURE = 1,
+ OBJECT_CONSTRUCT_CALL_BADALLOC = 2,
+};
+extern id object_cxxConstructFromClass(id obj, Class cls, int flags);
extern void object_cxxDestruct(id obj);
-extern Method _class_resolveMethod(Class cls, SEL sel);
-extern void log_and_fill_cache(Class cls, Class implementer, Method meth, SEL sel);
+extern void fixupCopiedIvars(id newObject, id oldObject);
+extern Class _class_getClassForIvar(Class cls, Ivar ivar);
+
#define OBJC_WARN_DEPRECATED \
do { \
} _static_assert_ ## line __attribute__((unavailable))
#endif
+#define countof(arr) (sizeof(arr) / sizeof((arr)[0]))
-/***********************************************************************
-* object_getClass.
-* Locking: None. If you add locking, tell gdb (rdar://7516456).
-**********************************************************************/
-static inline Class _object_getClass(id obj)
-{
-#if SUPPORT_TAGGED_POINTERS
- if (OBJC_IS_TAGGED_PTR(obj)) {
- uint8_t slotNumber = ((uint8_t) (uint64_t) obj) & 0x0F;
- Class isa = _objc_tagged_isa_table[slotNumber];
- return isa;
+
+static __inline uint32_t _objc_strhash(const char *s) {
+ uint32_t hash = 0;
+ for (;;) {
+ int a = *s++;
+ if (0 == a) break;
+ hash += (hash << 8) + a;
}
-#endif
- if (obj) return obj->isa;
- else return Nil;
+ return hash;
}
+#if __cplusplus
+
+template <typename T>
+static inline T log2u(T x) {
+ return (x<2) ? 0 : log2u(x>>1)+1;
+}
+
+template <typename T>
+static inline T exp2u(T x) {
+ return (1 << x);
+}
+
+template <typename T>
+static T exp2m1u(T x) {
+ return (1 << x) - 1;
+}
+
+#endif
+
+// Misalignment-safe integer types
+__attribute__((aligned(1))) typedef uintptr_t unaligned_uintptr_t;
+__attribute__((aligned(1))) typedef intptr_t unaligned_intptr_t;
+__attribute__((aligned(1))) typedef uint64_t unaligned_uint64_t;
+__attribute__((aligned(1))) typedef int64_t unaligned_int64_t;
+__attribute__((aligned(1))) typedef uint32_t unaligned_uint32_t;
+__attribute__((aligned(1))) typedef int32_t unaligned_int32_t;
+__attribute__((aligned(1))) typedef uint16_t unaligned_uint16_t;
+__attribute__((aligned(1))) typedef int16_t unaligned_int16_t;
+
// Global operator new and delete. We must not use any app overrides.
// This ALSO REQUIRES each of these be in libobjc's unexported symbol list.
-#if __cplusplus
+#if __cplusplus && !defined(TEST_OVERRIDES_NEW)
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Winline-new-delete"
#include <new>
-inline void* operator new(std::size_t size) throw (std::bad_alloc) { return _malloc_internal(size); }
-inline void* operator new[](std::size_t size) throw (std::bad_alloc) { return _malloc_internal(size); }
-inline void* operator new(std::size_t size, const std::nothrow_t&) throw() { return _malloc_internal(size); }
-inline void* operator new[](std::size_t size, const std::nothrow_t&) throw() { return _malloc_internal(size); }
-inline void operator delete(void* p) throw() { _free_internal(p); }
-inline void operator delete[](void* p) throw() { _free_internal(p); }
-inline void operator delete(void* p, const std::nothrow_t&) throw() { _free_internal(p); }
-inline void operator delete[](void* p, const std::nothrow_t&) throw() { _free_internal(p); }
+inline void* operator new(std::size_t size) { return malloc(size); }
+inline void* operator new[](std::size_t size) { return malloc(size); }
+inline void* operator new(std::size_t size, const std::nothrow_t&) noexcept(true) { return malloc(size); }
+inline void* operator new[](std::size_t size, const std::nothrow_t&) noexcept(true) { return malloc(size); }
+inline void operator delete(void* p) noexcept(true) { free(p); }
+inline void operator delete[](void* p) noexcept(true) { free(p); }
+inline void operator delete(void* p, const std::nothrow_t&) noexcept(true) { free(p); }
+inline void operator delete[](void* p, const std::nothrow_t&) noexcept(true) { free(p); }
+#pragma clang diagnostic pop
+#endif
+
+
+class TimeLogger {
+ uint64_t mStart;
+ bool mRecord;
+ public:
+ TimeLogger(bool record = true)
+ : mStart(nanoseconds())
+ , mRecord(record)
+ { }
+
+ void log(const char *msg) {
+ if (mRecord) {
+ uint64_t end = nanoseconds();
+ _objc_inform("%.2f ms: %s", (end - mStart) / 1000000.0, msg);
+ mStart = nanoseconds();
+ }
+ }
+};
+
+enum { CacheLineSize = 64 };
+
+// StripedMap<T> is a map of void* -> T, sized appropriately
+// for cache-friendly lock striping.
+// For example, this may be used as StripedMap<spinlock_t>
+// or as StripedMap<SomeStruct> where SomeStruct stores a spin lock.
+template<typename T>
+class StripedMap {
+#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
+ enum { StripeCount = 8 };
+#else
+ enum { StripeCount = 64 };
+#endif
+
+ struct PaddedT {
+ T value alignas(CacheLineSize);
+ };
+
+ PaddedT array[StripeCount];
+
+ static unsigned int indexForPointer(const void *p) {
+ uintptr_t addr = reinterpret_cast<uintptr_t>(p);
+ return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
+ }
+
+ public:
+ T& operator[] (const void *p) {
+ return array[indexForPointer(p)].value;
+ }
+ const T& operator[] (const void *p) const {
+ return const_cast<StripedMap<T>>(this)[p];
+ }
+
+ // Shortcuts for StripedMaps of locks.
+ void lockAll() {
+ for (unsigned int i = 0; i < StripeCount; i++) {
+ array[i].value.lock();
+ }
+ }
+
+ void unlockAll() {
+ for (unsigned int i = 0; i < StripeCount; i++) {
+ array[i].value.unlock();
+ }
+ }
+
+ void forceResetAll() {
+ for (unsigned int i = 0; i < StripeCount; i++) {
+ array[i].value.forceReset();
+ }
+ }
+
+ void defineLockOrder() {
+ for (unsigned int i = 1; i < StripeCount; i++) {
+ lockdebug_lock_precedes_lock(&array[i-1].value, &array[i].value);
+ }
+ }
+
+ void precedeLock(const void *newlock) {
+ // assumes defineLockOrder is also called
+ lockdebug_lock_precedes_lock(&array[StripeCount-1].value, newlock);
+ }
+
+ void succeedLock(const void *oldlock) {
+ // assumes defineLockOrder is also called
+ lockdebug_lock_precedes_lock(oldlock, &array[0].value);
+ }
+
+ const void *getLock(int i) {
+ if (i < StripeCount) return &array[i].value;
+ else return nil;
+ }
+
+#if DEBUG
+ StripedMap() {
+ // Verify alignment expectations.
+ uintptr_t base = (uintptr_t)&array[0].value;
+ uintptr_t delta = (uintptr_t)&array[1].value - base;
+ ASSERT(delta % CacheLineSize == 0);
+ ASSERT(base % CacheLineSize == 0);
+ }
+#else
+ constexpr StripedMap() {}
#endif
+};
+
+
+// DisguisedPtr<T> acts like pointer type T*, except the
+// stored value is disguised to hide it from tools like `leaks`.
+// nil is disguised as itself so zero-filled memory works as expected,
+// which means 0x80..00 is also disguised as itself but we don't care.
+// Note that weak_entry_t knows about this encoding.
+template <typename T>
+class DisguisedPtr {
+ uintptr_t value;
+
+ static uintptr_t disguise(T* ptr) {
+ return -(uintptr_t)ptr;
+ }
+
+ static T* undisguise(uintptr_t val) {
+ return (T*)-val;
+ }
+
+ public:
+ DisguisedPtr() { }
+ DisguisedPtr(T* ptr)
+ : value(disguise(ptr)) { }
+ DisguisedPtr(const DisguisedPtr<T>& ptr)
+ : value(ptr.value) { }
+
+ DisguisedPtr<T>& operator = (T* rhs) {
+ value = disguise(rhs);
+ return *this;
+ }
+ DisguisedPtr<T>& operator = (const DisguisedPtr<T>& rhs) {
+ value = rhs.value;
+ return *this;
+ }
+
+ operator T* () const {
+ return undisguise(value);
+ }
+ T* operator -> () const {
+ return undisguise(value);
+ }
+ T& operator * () const {
+ return *undisguise(value);
+ }
+ T& operator [] (size_t i) const {
+ return undisguise(value)[i];
+ }
+
+ // pointer arithmetic operators omitted
+ // because we don't currently use them anywhere
+};
+
+// fixme type id is weird and not identical to objc_object*
+static inline bool operator == (DisguisedPtr<objc_object> lhs, id rhs) {
+ return lhs == (objc_object *)rhs;
+}
+static inline bool operator != (DisguisedPtr<objc_object> lhs, id rhs) {
+ return lhs != (objc_object *)rhs;
+}
+
+
+// Storage for a thread-safe chained hook function.
+// get() returns the value for calling.
+// set() installs a new function and returns the old one for chaining.
+// More precisely, set() writes the old value to a variable supplied by
+// the caller. get() and set() use appropriate barriers so that the
+// old value is safely written to the variable before the new value is
+// called to use it.
+//
+// T1: store to old variable; store-release to hook variable
+// T2: load-acquire from hook variable; call it; called hook loads old variable
+
+template <typename Fn>
+class ChainedHookFunction {
+ std::atomic<Fn> hook{nil};
+
+public:
+ constexpr ChainedHookFunction(Fn f) : hook{f} { };
+
+ Fn get() {
+ return hook.load(std::memory_order_acquire);
+ }
+
+ void set(Fn newValue, Fn *oldVariable)
+ {
+ Fn oldValue = hook.load(std::memory_order_relaxed);
+ do {
+ *oldVariable = oldValue;
+ } while (!hook.compare_exchange_weak(oldValue, newValue,
+ std::memory_order_release,
+ std::memory_order_relaxed));
+ }
+};
+
+
+// A small vector for use as a global variable. Only supports appending and
+// iteration. Stores up to N elements inline, and multiple elements in a heap
+// allocation. There is no attempt to amortize reallocation cost; this is
+// intended to be used in situation where a small number of elements is
+// common, more might happen, and significantly more is very rare.
+//
+// This does not clean up its allocation, and thus cannot be used as a local
+// variable or member of something with limited lifetime.
+
+template <typename T, unsigned InlineCount>
+class GlobalSmallVector {
+ static_assert(std::is_pod<T>::value, "SmallVector requires POD types");
+
+protected:
+ unsigned count{0};
+ union {
+ T inlineElements[InlineCount];
+ T *elements{nullptr};
+ };
+
+public:
+ void append(const T &val) {
+ if (count < InlineCount) {
+ // We have space. Store the new value inline.
+ inlineElements[count] = val;
+ } else if (count == InlineCount) {
+ // Inline storage is full. Switch to a heap allocation.
+ T *newElements = (T *)malloc((count + 1) * sizeof(T));
+ memcpy(newElements, inlineElements, count * sizeof(T));
+ newElements[count] = val;
+ elements = newElements;
+ } else {
+ // Resize the heap allocation and append.
+ elements = (T *)realloc(elements, (count + 1) * sizeof(T));
+ elements[count] = val;
+ }
+ count++;
+ }
+
+ const T *begin() const {
+ return count <= InlineCount ? inlineElements : elements;
+ }
+
+ const T *end() const {
+ return begin() + count;
+ }
+};
+
+// A small vector that cleans up its internal memory allocation when destroyed.
+template <typename T, unsigned InlineCount>
+class SmallVector: public GlobalSmallVector<T, InlineCount> {
+public:
+ ~SmallVector() {
+ if (this->count > InlineCount)
+ free(this->elements);
+ }
+
+ template <unsigned OtherCount>
+ void initFrom(const GlobalSmallVector<T, OtherCount> &other) {
+ ASSERT(this->count == 0);
+ this->count = (unsigned)(other.end() - other.begin());
+ if (this->count > InlineCount) {
+ this->elements = (T *)memdup(other.begin(), this->count * sizeof(T));
+ } else {
+ memcpy(this->inlineElements, other.begin(), this->count * sizeof(T));
+ }
+ }
+};
+
+// Pointer hash function.
+// This is not a terrific hash, but it is fast
+// and not outrageously flawed for our purposes.
+
+// Based on principles from http://locklessinc.com/articles/fast_hash/
+// and evaluation ideas from http://floodyberry.com/noncryptohashzoo/
+#if __LP64__
+static inline uint32_t ptr_hash(uint64_t key)
+{
+ key ^= key >> 4;
+ key *= 0x8a970be7488fda55;
+ key ^= __builtin_bswap64(key);
+ return (uint32_t)key;
+}
+#else
+static inline uint32_t ptr_hash(uint32_t key)
+{
+ key ^= key >> 4;
+ key *= 0x5052acdb;
+ key ^= __builtin_bswap32(key);
+ return key;
+}
+#endif
+
+/*
+ Higher-quality hash function. This is measurably slower in some workloads.
+#if __LP64__
+ uint32_t ptr_hash(uint64_t key)
+{
+ key -= __builtin_bswap64(key);
+ key *= 0x8a970be7488fda55;
+ key ^= __builtin_bswap64(key);
+ key *= 0x8a970be7488fda55;
+ key ^= __builtin_bswap64(key);
+ return (uint32_t)key;
+}
+#else
+static uint32_t ptr_hash(uint32_t key)
+{
+ key -= __builtin_bswap32(key);
+ key *= 0x5052acdb;
+ key ^= __builtin_bswap32(key);
+ key *= 0x5052acdb;
+ key ^= __builtin_bswap32(key);
+ return key;
+}
+#endif
+*/
+
+
+
+// Lock declarations
+#include "objc-locks.h"
+// Inlined parts of objc_object's implementation
+#include "objc-object.h"
#endif /* _OBJC_PRIVATE_H_ */