You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

215 lines
5.9 KiB
C

#include "lock.h"
#include <stdint.h>
#include <dlfcn.h>
#define PROFILE
__thread id objc_msg_sender;
static struct objc_slot nil_slot = { Nil, Nil, "", 1, (IMP)nil_method };
typedef struct objc_slot *Slot_t;
Slot_t objc_msg_lookup_sender(id *receiver, SEL selector, id sender);
// Default implementations of the two new hooks. Return NULL.
static id objc_proxy_lookup_null(id receiver, SEL op) { return nil; }
static Slot_t objc_msg_forward3_null(id receiver, SEL op) { return &nil_slot; }
id (*objc_proxy_lookup)(id receiver, SEL op) = objc_proxy_lookup_null;
Slot_t (*objc_msg_forward3)(id receiver, SEL op) = objc_msg_forward3_null;
static inline
Slot_t objc_msg_lookup_internal(id *receiver, SEL selector, id sender)
{
Slot_t result = sarray_get_safe((*receiver)->class_pointer->dtable,
PTR_TO_IDX(selector->sel_id));
if (0 == result)
{
Class class = (*receiver)->class_pointer;
void *dtable = dtable_for_class(class);
/* Install the dtable if it hasn't already been initialized. */
if (dtable == __objc_uninstalled_dtable)
{
__objc_init_install_dtable (*receiver, selector);
dtable = dtable_for_class(class);
result = sarray_get_safe(dtable, PTR_TO_IDX(selector->sel_id));
if (0 == result)
{
objc_mutex_lock(__objc_runtime_mutex);
dtable = dtable_for_class(class);
if (dtable == __objc_uninstalled_dtable)
{
__objc_install_dispatch_table_for_class(class);
dtable = dtable_for_class(class);
}
objc_mutex_unlock(__objc_runtime_mutex);
result = sarray_get_safe(dtable, PTR_TO_IDX(selector->sel_id));
}
}
else
{
// Check again incase another thread updated the dtable while we
// weren't looking
result = sarray_get_safe(dtable, PTR_TO_IDX(selector->sel_id));
}
if (0 == result)
{
id newReceiver = objc_proxy_lookup(*receiver, selector);
// If some other library wants us to play forwarding games, try again
// with the new object.
if (nil != newReceiver)
{
*receiver = newReceiver;
return objc_msg_lookup_sender(receiver, selector, sender);
}
if (0 == result)
{
result = objc_msg_forward3(*receiver, selector);
}
}
}
return result;
}
Slot_t (*objc_plane_lookup)(id *receiver, SEL op, id sender) =
objc_msg_lookup_internal;
/**
* New Objective-C lookup function. This permits the lookup to modify the
* receiver and also supports multi-dimensional dispatch based on the sender.
*/
Slot_t objc_msg_lookup_sender(id *receiver, SEL selector, id sender)
{
//fprintf(stderr, "Looking up slot %s\n", sel_get_name(selector));
// Returning a nil slot allows the caller to cache the lookup for nil too,
// although this is not particularly useful because the nil method can be
// inlined trivially.
if(*receiver == nil)
{
return &nil_slot;
}
/*
* The self pointer is invalid in some code. This test is disabled until
* we can guarantee that it is not (e.g. with GCKit)
if (__builtin_expect(sender == nil
||
(sender->class_pointer->info & (*receiver)->class_pointer->info & _CLS_PLANE_AWARE),1))
*/
{
return objc_msg_lookup_internal(receiver, selector, sender);
}
// If we are in plane-aware code
void *senderPlaneID = *((void**)sender - 1);
void *receiverPlaneID = *((void**)receiver - 1);
if (senderPlaneID == receiverPlaneID)
{
//fprintf(stderr, "Intraplane message\n");
return objc_msg_lookup_internal(receiver, selector, sender);
}
return objc_plane_lookup(receiver, selector, sender);
}
Slot_t objc_slot_lookup_super(Super_t super, SEL selector)
{
id receiver = super->self;
if (receiver)
{
Class class = super->class;
Slot_t result = sarray_get_safe(dtable_for_class(class),
PTR_TO_IDX(selector->sel_id));
if (0 == result)
{
// Dtable should always be installed in the superclass
assert(dtable_for_class(class) != __objc_uninstalled_dtable);
result = &nil_slot;
}
return result;
}
else
{
return &nil_slot;
}
}
#ifdef PROFILE
/**
* Mutex used to protect non-thread-safe parts of the profiling subsystem.
*/
static mutex_t profileLock;
/**
* File used for writing the profiling symbol table.
*/
static FILE *profileSymbols;
/**
* File used for writing the profiling data.
*/
static FILE *profileData;
struct profile_info
{
const char *module;
int32_t callsite;
IMP method;
};
static void __objc_profile_init(void)
{
INIT_LOCK(profileLock);
profileSymbols = fopen("objc_profile.symbols", "a");
profileData = fopen("objc_profile.data", "a");
// Write markers indicating a new run.
fprintf(profileSymbols, "=== NEW TRACE ===\n");
struct profile_info profile_data = { 0, 0, 0 };
fwrite(&profile_data, sizeof(profile_data), 1, profileData);
}
void objc_profile_write_symbols(char **symbols)
{
if (NULL == profileData)
{
LOCK(__objc_runtime_mutex);
if (NULL == profileData)
{
__objc_profile_init();
}
UNLOCK(__objc_runtime_mutex);
}
LOCK(&profileLock);
while(*symbols)
{
char *address = *(symbols++);
char *symbol = *(symbols++);
fprintf(profileSymbols, "%zx %s\n", (size_t)address, symbol);
}
UNLOCK(&profileLock);
fflush(profileSymbols);
}
/**
* Profiling version of the slot lookup. This takes a unique ID for the module
* and the callsite as extra arguments. The type of the receiver and the
* address of the resulting function are then logged to a file. These can then
* be used to determine whether adding slot caching is worthwhile, and whether
* any of the resulting methods should be speculatively inlined.
*/
void objc_msg_profile(id receiver, IMP method,
const char *module, int32_t callsite)
{
// Initialize the logging lazily. This prevents us from wasting any memory
// when we are not profiling.
if (NULL == profileData)
{
LOCK(__objc_runtime_mutex);
if (NULL == profileData)
{
__objc_profile_init();
}
UNLOCK(__objc_runtime_mutex);
}
struct profile_info profile_data = { module, callsite, method };
fwrite(&profile_data, sizeof(profile_data), 1, profileData);
}
#endif