/*
* Copyright (c) 1999-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@
*/
/***********************************************************************
* objc-runtime.m
* Copyright 1988-1996, NeXT Software, Inc.
* Author: s. naroff
*
**********************************************************************/
/***********************************************************************
* Imports.
**********************************************************************/
#include "objc-private.h"
#include "objc-loadmethod.h"
#include "message.h"
OBJC_EXPORT Class getOriginalClassForPosingClass(Class);
/***********************************************************************
* Exports.
**********************************************************************/
// Settings from environment variables
#if SUPPORT_ENVIRON
PRIVATE_EXTERN int PrintImages = -1; // env OBJC_PRINT_IMAGES
PRIVATE_EXTERN int PrintLoading = -1; // env OBJC_PRINT_LOAD_METHODS
PRIVATE_EXTERN int PrintInitializing = -1; // env OBJC_PRINT_INITIALIZE_METHODS
PRIVATE_EXTERN int PrintResolving = -1; // env OBJC_PRINT_RESOLVED_METHODS
PRIVATE_EXTERN int PrintConnecting = -1; // env OBJC_PRINT_CLASS_SETUP
PRIVATE_EXTERN int PrintProtocols = -1; // env OBJC_PRINT_PROTOCOL_SETUP
PRIVATE_EXTERN int PrintIvars = -1; // env OBJC_PRINT_IVAR_SETUP
PRIVATE_EXTERN int PrintVtables = -1; // env OBJC_PRINT_VTABLE_SETUP
PRIVATE_EXTERN int PrintVtableImages = -1;//env OBJC_PRINT_VTABLE_IMAGES
PRIVATE_EXTERN int PrintFuture = -1; // env OBJC_PRINT_FUTURE_CLASSES
PRIVATE_EXTERN int PrintRTP = -1; // env OBJC_PRINT_RTP
PRIVATE_EXTERN int PrintGC = -1; // env OBJC_PRINT_GC
PRIVATE_EXTERN int PrintPreopt = -1; // env OBJC_PRINT_PREOPTIMIZATION
PRIVATE_EXTERN int PrintCxxCtors = -1; // env OBJC_PRINT_CXX_CTORS
PRIVATE_EXTERN int PrintExceptions = -1; // env OBJC_PRINT_EXCEPTIONS
PRIVATE_EXTERN int PrintExceptionThrow = -1; // env OBJC_PRINT_EXCEPTION_THROW
PRIVATE_EXTERN int PrintAltHandlers = -1; // env OBJC_PRINT_ALT_HANDLERS
PRIVATE_EXTERN int PrintDeprecation = -1;// env OBJC_PRINT_DEPRECATION_WARNINGS
PRIVATE_EXTERN int PrintReplacedMethods = -1; // env OBJC_PRINT_REPLACED_METHODS
PRIVATE_EXTERN int PrintCaches = -1; // env OBJC_PRINT_CACHE_SETUP
PRIVATE_EXTERN int PrintPoolHiwat = -1; // env OBJC_PRINT_POOL_HIGHWATER
PRIVATE_EXTERN int UseInternalZone = -1; // env OBJC_USE_INTERNAL_ZONE
PRIVATE_EXTERN int DebugUnload = -1; // env OBJC_DEBUG_UNLOAD
PRIVATE_EXTERN int DebugFragileSuperclasses = -1; // env OBJC_DEBUG_FRAGILE_SUPERCLASSES
PRIVATE_EXTERN int DebugNilSync = -1; // env OBJC_DEBUG_NIL_SYNC
PRIVATE_EXTERN int DebugNonFragileIvars = -1; // env OBJC_DEBUG_NONFRAGILE_IVARS
PRIVATE_EXTERN int DebugAltHandlers = -1;// env OBJC_DEBUG_ALT_HANDLERS
PRIVATE_EXTERN int DisableGC = -1; // env OBJC_DISABLE_GC
PRIVATE_EXTERN int DisableVtables = -1; // env OBJC_DISABLE_VTABLES
PRIVATE_EXTERN int DisablePreopt = -1; // env OBJC_DISABLE_PREOPTIMIZATION
PRIVATE_EXTERN int DebugFinalizers = -1; // env OBJC_DEBUG_FINALIZERS
#endif
// objc's key for pthread_getspecific
static tls_key_t _objc_pthread_key;
// Selectors
PRIVATE_EXTERN SEL SEL_load = NULL;
PRIVATE_EXTERN SEL SEL_initialize = NULL;
PRIVATE_EXTERN SEL SEL_resolveInstanceMethod = NULL;
PRIVATE_EXTERN SEL SEL_resolveClassMethod = NULL;
PRIVATE_EXTERN SEL SEL_cxx_construct = NULL;
PRIVATE_EXTERN SEL SEL_cxx_destruct = NULL;
PRIVATE_EXTERN SEL SEL_retain = NULL;
PRIVATE_EXTERN SEL SEL_release = NULL;
PRIVATE_EXTERN SEL SEL_autorelease = NULL;
PRIVATE_EXTERN SEL SEL_retainCount = NULL;
PRIVATE_EXTERN SEL SEL_alloc = NULL;
PRIVATE_EXTERN SEL SEL_copy = NULL;
PRIVATE_EXTERN SEL SEL_new = NULL;
PRIVATE_EXTERN SEL SEL_finalize = NULL;
PRIVATE_EXTERN SEL SEL_forwardInvocation = NULL;
PRIVATE_EXTERN header_info *FirstHeader = 0; // NULL means empty list
PRIVATE_EXTERN header_info *LastHeader = 0; // NULL means invalid; recompute it
PRIVATE_EXTERN int HeaderCount = 0;
/***********************************************************************
* objc_getClass. Return the id of the named class. If the class does
* not exist, call _objc_classLoader and then objc_classHandler, either of
* which may create a new class.
* Warning: doesn't work if aClassName is the name of a posed-for class's isa!
**********************************************************************/
id objc_getClass(const char *aClassName)
{
if (!aClassName) return Nil;
// NO unconnected, YES class handler
return look_up_class(aClassName, NO, YES);
}
/***********************************************************************
* objc_getRequiredClass.
* Same as objc_getClass, but kills the process if the class is not found.
* This is used by ZeroLink, where failing to find a class would be a
* compile-time link error without ZeroLink.
**********************************************************************/
id objc_getRequiredClass(const char *aClassName)
{
id cls = objc_getClass(aClassName);
if (!cls) _objc_fatal("link error: class '%s' not found.", aClassName);
return cls;
}
/***********************************************************************
* objc_lookUpClass. Return the id of the named class.
* If the class does not exist, call _objc_classLoader, which may create
* a new class.
*
* Formerly objc_getClassWithoutWarning ()
**********************************************************************/
id objc_lookUpClass(const char *aClassName)
{
if (!aClassName) return Nil;
// NO unconnected, NO class handler
return look_up_class(aClassName, NO, NO);
}
/***********************************************************************
* objc_getFutureClass. Return the id of the named class.
* If the class does not exist, return an uninitialized class
* structure that will be used for the class when and if it
* does get loaded.
* Not thread safe.
**********************************************************************/
Class objc_getFutureClass(const char *name)
{
Class cls;
// YES unconnected, NO class handler
// (unconnected is OK because it will someday be the real class)
cls = (Class)look_up_class(name, YES, NO);
if (cls) {
if (PrintFuture) {
_objc_inform("FUTURE: found }
return cls;
}
// No class or future class with that name yet. Make one.
// fixme not thread-safe with respect to
// simultaneous library load or getFutureClass.
return _objc_allocateFutureClass(name);
}
/***********************************************************************
* objc_getMetaClass. Return the id of the meta class the named class.
* Warning: doesn't work if aClassName is the name of a posed-for class's isa!
**********************************************************************/
id objc_getMetaClass(const char *aClassName)
{
Class cls;
if (!aClassName) return Nil;
cls = (Class)objc_getClass (aClassName);
if (!cls)
{
_objc_inform ("class ` return Nil;
}
return (id)cls->isa;
}
/***********************************************************************
* _nameForHeader.
**********************************************************************/
PRIVATE_EXTERN const char *_nameForHeader(const headerType *header)
{
return _getObjcHeaderName ((headerType *) header);
}
/***********************************************************************
* _objc_appendHeader. Add a newly-constructed header_info to the list.
**********************************************************************/
PRIVATE_EXTERN void _objc_appendHeader(header_info *hi)
{
// Add the header to the header list.
// The header is appended to the list, to preserve the bottom-up order.
HeaderCount++;
hi->next = NULL;
if (!FirstHeader) {
// list is empty
FirstHeader = LastHeader = hi;
} else {
if (!LastHeader) {
// list is not empty, but LastHeader is invalid - recompute it
LastHeader = FirstHeader;
while (LastHeader->next) LastHeader = LastHeader->next;
}
// LastHeader is now valid
LastHeader->next = hi;
LastHeader = hi;
}
}
/***********************************************************************
* _objc_RemoveHeader
* Remove the given header from the header list.
* FirstHeader is updated.
* LastHeader is set to NULL. Any code that uses LastHeader must
* detect this NULL and recompute LastHeader by traversing the list.
**********************************************************************/
PRIVATE_EXTERN void _objc_removeHeader(header_info *hi)
{
header_info **hiP;
for (hiP = &FirstHeader; *hiP != NULL; hiP = &(**hiP).next) {
if (*hiP == hi) {
header_info *deadHead = *hiP;
// Remove from the linked list (updating FirstHeader if necessary).
*hiP = (**hiP).next;
// Update LastHeader if necessary.
if (LastHeader == deadHead) {
LastHeader = NULL; // will be recomputed next time it's used
}
HeaderCount--;
break;
}
}
}
/***********************************************************************
* environ_init
* Read environment variables that affect the runtime.
* Also print environment variable help, if requested.
**********************************************************************/
PRIVATE_EXTERN void environ_init(void)
{
#if SUPPORT_ENVIRON
int PrintHelp = (getenv("OBJC_HELP") != NULL);
int PrintOptions = (getenv("OBJC_PRINT_OPTIONS") != NULL);
int secure = issetugid();
if (secure) {
// All environment variables are ignored when setuid or setgid.
// This includes OBJC_HELP and OBJC_PRINT_OPTIONS themselves.
}
else {
if (PrintHelp) {
_objc_inform("Objective-C runtime debugging. Set variable=YES to enable.");
_objc_inform("OBJC_HELP: describe available environment variables");
if (PrintOptions) {
_objc_inform("OBJC_HELP is set");
}
_objc_inform("OBJC_PRINT_OPTIONS: list which options are set");
}
if (PrintOptions) {
_objc_inform("OBJC_PRINT_OPTIONS is set");
}
}
#define OPTION(var, env, help) \
if ( var == -1 ) { \
char *value = getenv(#env); \
var = value != NULL && !strcmp("YES", value); \
if (secure) { \
if (var) _objc_inform(#env " ignored when running setuid or setgid"); \
var = 0; \
} else { \
if (PrintHelp) _objc_inform(#env ": " help); \
if (PrintOptions && var) _objc_inform(#env " is set"); \
} \
}
OPTION(PrintImages, OBJC_PRINT_IMAGES,
"log image and library names as they are loaded");
OPTION(PrintLoading, OBJC_PRINT_LOAD_METHODS,
"log calls to class and category +load methods");
OPTION(PrintInitializing, OBJC_PRINT_INITIALIZE_METHODS,
"log calls to class +initialize methods");
OPTION(PrintResolving, OBJC_PRINT_RESOLVED_METHODS,
"log methods created by +resolveClassMethod: and +resolveInstanceMethod:");
OPTION(PrintConnecting, OBJC_PRINT_CLASS_SETUP,
"log progress of class and category setup");
OPTION(PrintProtocols, OBJC_PRINT_PROTOCOL_SETUP,
"log progress of protocol setup");
OPTION(PrintIvars, OBJC_PRINT_IVAR_SETUP,
"log processing of non-fragile ivars");
OPTION(PrintVtables, OBJC_PRINT_VTABLE_SETUP,
"log processing of class vtables");
OPTION(PrintVtableImages, OBJC_PRINT_VTABLE_IMAGES,
"print vtable images showing overridden methods");
OPTION(PrintCaches, OBJC_PRINT_CACHE_SETUP,
"log processing of method caches");
OPTION(PrintFuture, OBJC_PRINT_FUTURE_CLASSES,
"log use of future classes for toll-free bridging");
OPTION(PrintRTP, OBJC_PRINT_RTP,
"log initialization of the Objective-C runtime pages");
OPTION(PrintGC, OBJC_PRINT_GC,
"log some GC operations");
OPTION(PrintPreopt, OBJC_PRINT_PREOPTIMIZATION,
"log preoptimization courtesy of dyld shared cache");
OPTION(PrintCxxCtors, OBJC_PRINT_CXX_CTORS,
"log calls to C++ ctors and dtors for instance variables");
OPTION(PrintExceptions, OBJC_PRINT_EXCEPTIONS,
"log exception handling");
OPTION(PrintExceptionThrow, OBJC_PRINT_EXCEPTION_THROW,
"log backtrace of every objc_exception_throw()");
OPTION(PrintAltHandlers, OBJC_PRINT_ALT_HANDLERS,
"log processing of exception alt handlers");
OPTION(PrintReplacedMethods, OBJC_PRINT_REPLACED_METHODS,
"log methods replaced by category implementations");
OPTION(PrintDeprecation, OBJC_PRINT_DEPRECATION_WARNINGS,
"warn about calls to deprecated runtime functions");
OPTION(PrintPoolHiwat, OBJC_PRINT_POOL_HIGHWATER,
"print high-water marks for autorelease pools");
OPTION(DebugUnload, OBJC_DEBUG_UNLOAD,
"warn about poorly-behaving bundles when unloaded");
OPTION(DebugFragileSuperclasses, OBJC_DEBUG_FRAGILE_SUPERCLASSES,
"warn about subclasses that may have been broken by subsequent changes to superclasses");
OPTION(DebugFinalizers, OBJC_DEBUG_FINALIZERS,
"warn about classes that implement -dealloc but not -finalize");
OPTION(DebugNilSync, OBJC_DEBUG_NIL_SYNC,
"warn about @synchronized(nil), which does no synchronization");
OPTION(DebugNonFragileIvars, OBJC_DEBUG_NONFRAGILE_IVARS,
"capriciously rearrange non-fragile ivars");
OPTION(DebugAltHandlers, OBJC_DEBUG_ALT_HANDLERS,
"record more info about bad alt handler use");
OPTION(UseInternalZone, OBJC_USE_INTERNAL_ZONE,
"allocate runtime data in a dedicated malloc zone");
OPTION(DisableGC, OBJC_DISABLE_GC,
"force GC OFF, even if the executable wants it on");
OPTION(DisableVtables, OBJC_DISABLE_VTABLES,
"disable vtable dispatch");
OPTION(DisablePreopt, OBJC_DISABLE_PREOPTIMIZATION,
"disable preoptimization courtesy of dyld shared cache");
#undef OPTION
#endif
}
/***********************************************************************
* logReplacedMethod
* OBJC_PRINT_REPLACED_METHODS implementation
**********************************************************************/
PRIVATE_EXTERN void
logReplacedMethod(const char *className, SEL s,
BOOL isMeta, const char *catName,
IMP oldImp, IMP newImp)
{
const char *oldImage = "??";
const char *newImage = "??";
// Silently ignore +load replacement because category +load is special
if (s == SEL_load) return;
#if TARGET_OS_WIN32
// don't know dladdr()/dli_fname equivalent
#else
Dl_info dl;
if (dladdr(oldImp, &dl) && dl.dli_fname) oldImage = dl.dli_fname;
if (dladdr(newImp, &dl) && dl.dli_fname) newImage = dl.dli_fname;
#endif
_objc_inform("REPLACED: isMeta ? '+' : '-', className, sel_getName(s),
catName ? "by category " : "", catName ? catName : "",
oldImp, oldImage, newImp, newImage);
}
/***********************************************************************
* objc_setMultithreaded.
**********************************************************************/
void objc_setMultithreaded (BOOL flag)
{
OBJC_WARN_DEPRECATED;
// Nothing here. Thread synchronization in the runtime is always active.
}
/***********************************************************************
* _objc_fetch_pthread_data
* Fetch objc's pthread data for this thread.
* If the data doesn't exist yet and create is NO, return NULL.
* If the data doesn't exist yet and create is YES, allocate and return it.
**********************************************************************/
PRIVATE_EXTERN _objc_pthread_data *_objc_fetch_pthread_data(BOOL create)
{
_objc_pthread_data *data;
data = tls_get(_objc_pthread_key);
if (!data && create) {
data = _calloc_internal(1, sizeof(_objc_pthread_data));
tls_set(_objc_pthread_key, data);
}
return data;
}
/***********************************************************************
* _objc_pthread_destroyspecific
* Destructor for objc's per-thread data.
* arg shouldn't be NULL, but we check anyway.
**********************************************************************/
extern void _destroyInitializingClassList(struct _objc_initializing_classes *list);
PRIVATE_EXTERN void _objc_pthread_destroyspecific(void *arg)
{
_objc_pthread_data *data = (_objc_pthread_data *)arg;
if (data != NULL) {
_destroyInitializingClassList(data->initializingClasses);
_destroySyncCache(data->syncCache);
_destroyAltHandlerList(data->handlerList);
// add further cleanup here...
_free_internal(data);
}
}
PRIVATE_EXTERN void tls_init(void)
{
#if SUPPORT_DIRECT_THREAD_KEYS
_objc_pthread_key = TLS_DIRECT_KEY;
pthread_key_init_np(TLS_DIRECT_KEY, &_objc_pthread_destroyspecific);
#else
_objc_pthread_key = tls_create(&_objc_pthread_destroyspecific);
#endif
}
/***********************************************************************
* _objcInit
* Former library initializer. This function is now merely a placeholder
* for external callers. All runtime initialization has now been moved
* to map_images() and _objc_init.
**********************************************************************/
void _objcInit(void)
{
// do nothing
}
#if !(TARGET_OS_WIN32 || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE)
/***********************************************************************
* _objc_setNilReceiver
**********************************************************************/
id _objc_setNilReceiver(id newNilReceiver)
{
id oldNilReceiver;
oldNilReceiver = _objc_nilReceiver;
_objc_nilReceiver = newNilReceiver;
return oldNilReceiver;
}
/***********************************************************************
* _objc_getNilReceiver
**********************************************************************/
id _objc_getNilReceiver(void)
{
return _objc_nilReceiver;
}
#endif
/***********************************************************************
* objc_setForwardHandler
**********************************************************************/
void objc_setForwardHandler(void *fwd, void *fwd_stret)
{
_objc_forward_handler = fwd;
_objc_forward_stret_handler = fwd_stret;
}
#if defined(__i386__) || defined(__x86_64__)
/**********************************************************************
* objc_branch_size
* Returns the number of BYTES needed
* for a branch from entry to target.
**********************************************************************/
PRIVATE_EXTERN size_t objc_branch_size(void *entry, void *target)
{
return objc_cond_branch_size(entry, target, COND_ALWAYS);
}
PRIVATE_EXTERN size_t
objc_cond_branch_size(void *entry, void *target, unsigned cond)
{
// For simplicity, always use 32-bit relative jumps.
if (cond == COND_ALWAYS) return 5;
else return 6;
}
/**********************************************************************
* objc_write_branch
* Writes at entry an i386 branch instruction sequence that branches to target.
* The sequence written will be objc_branch_size(entry, target) BYTES.
* Returns the number of BYTES written.
**********************************************************************/
PRIVATE_EXTERN size_t objc_write_branch(void *entry, void *target)
{
return objc_write_cond_branch(entry, target, COND_ALWAYS);
}
PRIVATE_EXTERN size_t
objc_write_cond_branch(void *entry, void *target, unsigned cond)
{
uint8_t *address = (uint8_t *)entry; // instructions written to here
intptr_t destination = (intptr_t)target; // branch dest as absolute address
intptr_t displacement = (intptr_t)destination - ((intptr_t)address + objc_cond_branch_size(entry, target, cond)); // branch dest as relative offset
// For simplicity, always use 32-bit relative jumps
if (cond != COND_ALWAYS) {
*address++ = 0x0f; // Jcc prefix
}
*address++ = cond;
*address++ = displacement & 0xff;
*address++ = (displacement >> 8) & 0xff;
*address++ = (displacement >> 16) & 0xff;
*address++ = (displacement >> 24) & 0xff;
return address - (uint8_t *)entry;
}
// defined __i386__
#endif
#if !__OBJC2__
// GrP fixme
extern Class _objc_getOrigClass(const char *name);
#endif
const char *class_getImageName(Class cls)
{
#if TARGET_OS_WIN32
TCHAR *szFileName;
DWORD charactersCopied;
Class origCls;
HMODULE classModule;
BOOL res;
#endif
if (!cls) return NULL;
#if !__OBJC2__
cls = _objc_getOrigClass(_class_getName(cls));
#endif
#if TARGET_OS_WIN32
charactersCopied = 0;
szFileName = malloc(MAX_PATH * sizeof(TCHAR));
origCls = objc_getOrigClass(class_getName(cls));
classModule = NULL;
res = GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, (LPCTSTR)origCls, &classModule);
if (res && classModule) {
charactersCopied = GetModuleFileName(classModule, szFileName, MAX_PATH * sizeof(TCHAR));
}
if (classModule) FreeLibrary(classModule);
if (charactersCopied) {
return (const char *)szFileName;
} else
free(szFileName);
return NULL;
#else
return dyld_image_path_containing_address(cls);
#endif
}
const char **objc_copyImageNames(unsigned int *outCount)
{
header_info *hi;
int count = 0;
int max = HeaderCount;
#if TARGET_OS_WIN32
const TCHAR **names = calloc(max+1, sizeof(TCHAR *));
#else
const char **names = calloc(max+1, sizeof(char *));
#endif
for (hi = FirstHeader; hi != NULL && count < max; hi = hi->next) {
#if TARGET_OS_WIN32
if (hi->os.moduleName) {
names[count++] = hi->os.moduleName;
}
#else
if (hi->os.dl_info.dli_fname) {
names[count++] = hi->os.dl_info.dli_fname;
}
#endif
}
names[count] = NULL;
if (count == 0) {
// Return NULL instead of empty list if there are no images
free((void *)names);
names = NULL;
}
if (outCount) *outCount = count;
return names;
}
/**********************************************************************
*
**********************************************************************/
const char **
objc_copyClassNamesForImage(const char *image, unsigned int *outCount)
{
header_info *hi;
if (!image) {
if (outCount) *outCount = 0;
return NULL;
}
// Find the image.
for (hi = FirstHeader; hi != NULL; hi = hi->next) {
#if TARGET_OS_WIN32
if (0 == wcscmp((TCHAR *)image, hi->os.moduleName)) break;
#else
if (0 == strcmp(image, hi->os.dl_info.dli_fname)) break;
#endif
}
if (!hi) {
if (outCount) *outCount = 0;
return NULL;
}
return _objc_copyClassNamesForImage(hi, outCount);
}
/**********************************************************************
* Fast Enumeration Support
**********************************************************************/
static void (*enumerationMutationHandler)(id);
/**********************************************************************
* objc_enumerationMutation
* called by compiler when a mutation is detected during foreach iteration
**********************************************************************/
void objc_enumerationMutation(id object) {
if (enumerationMutationHandler == nil) {
_objc_fatal("mutation detected during 'for(... in ...)' enumeration of object }
(*enumerationMutationHandler)(object);
}
/**********************************************************************
* objc_setEnumerationMutationHandler
* an entry point to customize mutation error handing
**********************************************************************/
void objc_setEnumerationMutationHandler(void (*handler)(id)) {
enumerationMutationHandler = handler;
}
/**********************************************************************
* Associative Reference Support
**********************************************************************/
#if SUPPORT_GC
PRIVATE_EXTERN id objc_getAssociatedObject_gc(id object, const void *key) {
return auto_zone_get_associative_ref(gc_zone, object, (void *)key);
}
#endif
PRIVATE_EXTERN id objc_getAssociatedObject_non_gc(id object, const void *key) {
return _object_get_associative_reference(object, (void *)key);
}
id objc_getAssociatedObject(id object, const void *key) {
#if SUPPORT_GC
if (UseGC) {
return auto_zone_get_associative_ref(gc_zone, object, (void *)key);
} else
#endif
{
return _object_get_associative_reference(object, (void *)key);
}
}
#if SUPPORT_GC
PRIVATE_EXTERN void objc_setAssociatedObject_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) {
value = objc_msgSend(value, SEL_copy);
}
auto_zone_set_associative_ref(gc_zone, object, (void *)key, value);
}
#endif
PRIVATE_EXTERN void objc_setAssociatedObject_non_gc(id object, const void *key, id value, objc_AssociationPolicy policy) {
_object_set_associative_reference(object, (void *)key, value, policy);
}
void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) {
#if SUPPORT_GC
if (UseGC) {
if ((policy & OBJC_ASSOCIATION_COPY_NONATOMIC) == OBJC_ASSOCIATION_COPY_NONATOMIC) {
value = objc_msgSend(value, SEL_copy);
}
auto_zone_set_associative_ref(gc_zone, object, (void *)key, value);
} else
#endif
{
// Note, creates a retained reference in non-GC.
_object_set_associative_reference(object, (void *)key, value, policy);
}
}
void objc_removeAssociatedObjects(id object) {
#if SUPPORT_GC
if (UseGC) {
auto_zone_erase_associative_refs(gc_zone, object);
} else
#endif
{
if (_class_instancesHaveAssociatedObjects(_object_getClass(object))) _object_remove_assocations(object);
}
}
BOOL class_instancesHaveAssociatedObjects(Class cls) {
return _class_instancesHaveAssociatedObjects(cls);
}
/**********************************************************************
* Debugger mode
*
* Debugger mode is used when gdb wants to call runtime functions
* and other methods while other threads are stopped. The runtime
* provides best-effort functionality while avoiding deadlocks
* with the stopped threads. gdb is responsible for ensuring that all
* threads but one stay stopped.
*
* When debugger mode starts, the runtime acquires as many locks as
* it can. Any locks that can't be acquired are off-limits until
* debugger mode ends. The locking functions in objc-os.h check each
* operation and halt if a disallowed lock is used; gdb catches that
* trap and cleans up.
*
* Each ABI is responsible for tracking its locks. Any lock not
* handled there is a potential gdb deadlock.
**********************************************************************/
#if SUPPORT_DEBUGGER_MODE
PRIVATE_EXTERN int DebuggerMode = DEBUGGER_OFF;
PRIVATE_EXTERN objc_thread_t DebuggerModeThread = 0;
static int DebuggerModeCount;
/**********************************************************************
* gdb_objc_startDebuggerMode
* Start debugger mode by taking locks. Return 0 if not enough locks
* could be acquired.
**********************************************************************/
int gdb_objc_startDebuggerMode(uint32_t flags)
{
BOOL wantFull = flags & OBJC_DEBUGMODE_FULL;
if (! DebuggerMode) {
// Start debugger mode
int mode = startDebuggerMode(); // Do this FIRST
if (mode == DEBUGGER_OFF) {
// sorry
return 0;
}
else if (mode == DEBUGGER_PARTIAL && wantFull) {
// not good enough
endDebuggerMode();
return 0;
}
else {
// w00t
DebuggerMode = mode;
DebuggerModeCount = 1;
DebuggerModeThread = thread_self();
return 1;
}
}
else if (DebuggerMode == DEBUGGER_PARTIAL && wantFull) {
// Debugger mode already active, but not as requested - sorry
return 0;
}
else {
// Debugger mode already active as requested
if (thread_self() == DebuggerModeThread) {
DebuggerModeCount++;
return 1;
} else {
_objc_inform("DEBUGGER MODE: debugger is buggy: can't run "
"debugger mode from two threads!");
return 0;
}
}
}
/**********************************************************************
* gdb_objc_endDebuggerMode
* Relinquish locks and end debugger mode.
**********************************************************************/
void gdb_objc_endDebuggerMode(void)
{
if (DebuggerMode && thread_self() == DebuggerModeThread) {
if (--DebuggerModeCount == 0) {
DebuggerMode = NO;
DebuggerModeThread = 0;
endDebuggerMode(); // Do this LAST
}
} else {
_objc_inform("DEBUGGER MODE: debugger is buggy: debugger mode "
"not active for this thread!");
}
}
/**********************************************************************
* gdb_objc_debuggerModeFailure
* Breakpoint hook for gdb when debugger mode can't finish something
**********************************************************************/
void gdb_objc_debuggerModeFailure(void)
{
_objc_fatal("DEBUGGER MODE: failed");
}
// SUPPORT_DEBUGGER_MODE
#endif