Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
branch: master
Fetching contributors…

Cannot retrieve contributors at this time

810 lines (635 sloc) 29.981 kb
/*
* wax_instance.c
* Lua
*
* Created by ProbablyInteractive on 5/18/09.
* Copyright 2009 Probably Interactive. All rights reserved.
*
*/
#import "wax_instance.h"
#import "wax_class.h"
#import "wax.h"
#import "wax_helpers.h"
#import "lauxlib.h"
#import "lobject.h"
static int __index(lua_State *L);
static int __newindex(lua_State *L);
static int __gc(lua_State *L);
static int __tostring(lua_State *L);
static int __eq(lua_State *L);
static int methods(lua_State *L);
static int methodClosure(lua_State *L);
static int superMethodClosure(lua_State *L);
static int customInitMethodClosure(lua_State *L);
static BOOL overrideMethod(lua_State *L, wax_instance_userdata *instanceUserdata);
static int pcallUserdata(lua_State *L, id self, SEL selector, va_list args);
static const struct luaL_Reg metaFunctions[] = {
{"__index", __index},
{"__newindex", __newindex},
{"__gc", __gc},
{"__tostring", __tostring},
{"__eq", __eq},
{NULL, NULL}
};
static const struct luaL_Reg functions[] = {
{"methods", methods},
{NULL, NULL}
};
int luaopen_wax_instance(lua_State *L) {
BEGIN_STACK_MODIFY(L);
luaL_newmetatable(L, WAX_INSTANCE_METATABLE_NAME);
luaL_register(L, NULL, metaFunctions);
luaL_register(L, WAX_INSTANCE_METATABLE_NAME, functions);
END_STACK_MODIFY(L, 0)
return 1;
}
#pragma mark Instance Utils
#pragma mark -------------------
// Creates userdata object for obj-c instance/class and pushes it onto the stack
wax_instance_userdata *wax_instance_create(lua_State *L, id instance, BOOL isClass) {
BEGIN_STACK_MODIFY(L)
// Does user data already exist?
wax_instance_pushUserdata(L, instance);
if (lua_isnil(L, -1)) {
//wax_log(LOG_GC, @"Creating %@ for %@(%p)", isClass ? @"class" : @"instance", [instance class], instance);
lua_pop(L, 1); // pop nil stack
}
else {
//wax_log(LOG_GC, @"Found existing userdata %@ for %@(%p)", isClass ? @"class" : @"instance", [instance class], instance);
return lua_touserdata(L, -1);
}
size_t nbytes = sizeof(wax_instance_userdata);
wax_instance_userdata *instanceUserdata = (wax_instance_userdata *)lua_newuserdata(L, nbytes);
instanceUserdata->instance = instance;
instanceUserdata->isClass = isClass;
instanceUserdata->isSuper = nil;
instanceUserdata->actAsSuper = NO;
if (!isClass) {
//wax_log(LOG_GC, @"Retaining %@ for %@(%p -> %p)", isClass ? @"class" : @"instance", [instance class], instance, instanceUserdata);
[instanceUserdata->instance retain];
}
// set the metatable
luaL_getmetatable(L, WAX_INSTANCE_METATABLE_NAME);
lua_setmetatable(L, -2);
// give it a nice clean environment
lua_newtable(L);
lua_setfenv(L, -2);
wax_instance_pushUserdataTable(L);
// register the userdata table in the metatable (so we can access it from obj-c)
//wax_log(LOG_GC, @"Storing reference of %@ to userdata table %@(%p -> %p)", isClass ? @"class" : @"instance", [instance class], instance, instanceUserdata);
lua_pushlightuserdata(L, instanceUserdata->instance);
lua_pushvalue(L, -3); // Push userdata
lua_rawset(L, -3);
lua_pop(L, 1); // Pop off userdata table
wax_instance_pushStrongUserdataTable(L);
lua_pushlightuserdata(L, instanceUserdata->instance);
lua_pushvalue(L, -3); // Push userdata
lua_rawset(L, -3);
//wax_log(LOG_GC, @"Storing reference to strong userdata table %@(%p -> %p)", [instance class], instance, instanceUserdata);
lua_pop(L, 1); // Pop off strong userdata table
END_STACK_MODIFY(L, 1)
return instanceUserdata;
}
// Creates pseudo-super userdata object for obj-c instance and pushes it onto the stack
wax_instance_userdata *wax_instance_createSuper(lua_State *L, wax_instance_userdata *instanceUserdata) {
BEGIN_STACK_MODIFY(L)
size_t nbytes = sizeof(wax_instance_userdata);
wax_instance_userdata *superInstanceUserdata = (wax_instance_userdata *)lua_newuserdata(L, nbytes);
superInstanceUserdata->instance = instanceUserdata->instance;
superInstanceUserdata->isClass = instanceUserdata->isClass;
superInstanceUserdata->actAsSuper = YES;
// isSuper not only stores whether the class is a super, but it also contains the value of the next superClass
if (instanceUserdata->isSuper) {
superInstanceUserdata->isSuper = [instanceUserdata->isSuper superclass];
}
else {
superInstanceUserdata->isSuper = [instanceUserdata->instance superclass];
}
// set the metatable
luaL_getmetatable(L, WAX_INSTANCE_METATABLE_NAME);
lua_setmetatable(L, -2);
wax_instance_pushUserdata(L, instanceUserdata->instance);
if (lua_isnil(L, -1)) { // instance has no lua object, push empty env table (This shouldn't happen, tempted to remove it)
lua_pop(L, 1); // Remove nil and superclass userdata
lua_newtable(L);
}
else {
lua_getfenv(L, -1);
lua_remove(L, -2); // Remove nil and superclass userdata
}
// Give it the instance's metatable
lua_setfenv(L, -2);
END_STACK_MODIFY(L, 1)
return superInstanceUserdata;
}
// The userdata table holds weak references too all the instance userdata
// created. This is used to manage all instances of Objective-C objects created
// via Lua so we can release/gc them when both Lua and Objective-C are finished with
// them.
void wax_instance_pushUserdataTable(lua_State *L) {
BEGIN_STACK_MODIFY(L)
static const char* userdataTableName = "__wax_userdata";
luaL_getmetatable(L, WAX_INSTANCE_METATABLE_NAME);
lua_getfield(L, -1, userdataTableName);
if (lua_isnil(L, -1)) { // Create new userdata table, add it to metatable
lua_pop(L, 1); // Remove nil
lua_pushstring(L, userdataTableName); // Table name
lua_newtable(L);
lua_rawset(L, -3); // Add userdataTableName table to WAX_INSTANCE_METATABLE_NAME
lua_getfield(L, -1, userdataTableName);
lua_pushvalue(L, -1);
lua_setmetatable(L, -2); // userdataTable is it's own metatable
lua_pushstring(L, "v");
lua_setfield(L, -2, "__mode"); // Make weak table
}
END_STACK_MODIFY(L, 1)
}
// Holds strong references to userdata created by wax... if the retain count dips below
// 2, then we can remove it because we know obj-c doesn't care about it anymore
void wax_instance_pushStrongUserdataTable(lua_State *L) {
BEGIN_STACK_MODIFY(L)
static const char* userdataTableName = "__wax_strong_userdata";
luaL_getmetatable(L, WAX_INSTANCE_METATABLE_NAME);
lua_getfield(L, -1, userdataTableName);
if (lua_isnil(L, -1)) { // Create new userdata table, add it to metatable
lua_pop(L, 1); // Remove nil
lua_pushstring(L, userdataTableName); // Table name
lua_newtable(L);
lua_rawset(L, -3); // Add userdataTableName table to WAX_INSTANCE_METATABLE_NAME
lua_getfield(L, -1, userdataTableName);
}
END_STACK_MODIFY(L, 1)
}
// First look in the object's userdata for the function, then look in the object's class's userdata
BOOL wax_instance_pushFunction(lua_State *L, id self, SEL selector) {
BEGIN_STACK_MODIFY(L)
wax_instance_pushUserdata(L, self);
if (lua_isnil(L, -1)) {
END_STACK_MODIFY(L, 0)
return NO; // userdata doesn't exist
}
lua_getfenv(L, -1);
wax_pushMethodNameFromSelector(L, selector);
lua_rawget(L, -2);
BOOL result = YES;
if (!lua_isfunction(L, -1)) { // function not found in userdata
lua_pop(L, 3); // Remove userdata, env and non-function
if ([self class] == self) { // This is a class, not an instance
result = wax_instance_pushFunction(L, [self superclass], selector); // Check to see if the super classes know about this function
}
else {
result = wax_instance_pushFunction(L, [self class], selector);
}
}
END_STACK_MODIFY(L, 1)
return result;
}
// Retrieves associated userdata for an object from the wax instance userdata table
void wax_instance_pushUserdata(lua_State *L, id object) {
BEGIN_STACK_MODIFY(L);
wax_instance_pushUserdataTable(L);
lua_pushlightuserdata(L, object);
lua_rawget(L, -2);
lua_remove(L, -2); // remove userdataTable
END_STACK_MODIFY(L, 1)
}
BOOL wax_instance_isWaxClass(id instance) {
// If this is a wax class, or an instance of a wax class, it has the userdata ivar set
return class_getInstanceVariable([instance class], WAX_CLASS_INSTANCE_USERDATA_IVAR_NAME) != nil;
}
#pragma mark Override Metatable Functions
#pragma mark ---------------------------------
static int __index(lua_State *L) {
wax_instance_userdata *instanceUserdata = (wax_instance_userdata *)luaL_checkudata(L, 1, WAX_INSTANCE_METATABLE_NAME);
if (lua_isstring(L, 2) && strcmp("super", lua_tostring(L, 2)) == 0) { // call to super!
wax_instance_createSuper(L, instanceUserdata);
return 1;
}
// Check instance userdata, unless we are acting like a super
if (!instanceUserdata->actAsSuper) {
lua_getfenv(L, -2);
lua_pushvalue(L, -2);
lua_rawget(L, 3);
}
else {
lua_pushnil(L);
}
// Check instance's class userdata, or if it is a super, check the super's class data
Class classToCheck = instanceUserdata->actAsSuper ? instanceUserdata->isSuper : [instanceUserdata->instance class];
while (lua_isnil(L, -1) && wax_instance_isWaxClass(classToCheck)) {
// Keep checking superclasses if they are waxclasses, we want to treat those like they are lua
lua_pop(L, 1);
wax_instance_pushUserdata(L, classToCheck);
// If there is no userdata for this instance's class, then leave the nil on the stack and don't anything else
if (!lua_isnil(L, -1)) {
lua_getfenv(L, -1);
lua_pushvalue(L, 2);
lua_rawget(L, -2);
lua_remove(L, -2); // Get rid of the userdata env
lua_remove(L, -2); // Get rid of the userdata
}
classToCheck = class_getSuperclass(classToCheck);
}
if (lua_isnil(L, -1)) { // If we are calling a super class, or if we couldn't find the index in the userdata environment table, assume it is defined in obj-c classes
SEL foundSelectors[2] = {nil, nil};
BOOL foundSelector = wax_selectorForInstance(instanceUserdata, foundSelectors, lua_tostring(L, 2), NO);
if (foundSelector) { // If the class has a method with this name, push as a closure
lua_pushstring(L, sel_getName(foundSelectors[0]));
foundSelectors[1] ? lua_pushstring(L, sel_getName(foundSelectors[1])) : lua_pushnil(L);
lua_pushcclosure(L, instanceUserdata->actAsSuper ? superMethodClosure : methodClosure, 2);
}
}
else if (!instanceUserdata->isSuper && instanceUserdata->isClass && wax_isInitMethod(lua_tostring(L, 2))) { // Is this an init method create in lua?
lua_pushcclosure(L, customInitMethodClosure, 1);
}
// Always reset this, an object only acts like a super ONE TIME!
instanceUserdata->actAsSuper = NO;
return 1;
}
static int __newindex(lua_State *L) {
wax_instance_userdata *instanceUserdata = (wax_instance_userdata *)luaL_checkudata(L, 1, WAX_INSTANCE_METATABLE_NAME);
// If this already exists in a protocol, or superclass make sure it will call the lua functions
if (instanceUserdata->isClass && lua_type(L, 3) == LUA_TFUNCTION) {
overrideMethod(L, instanceUserdata);
}
// Add value to the userdata's environment table
lua_getfenv(L, 1);
lua_insert(L, 2);
lua_rawset(L, 2);
return 0;
}
static int __gc(lua_State *L) {
wax_instance_userdata *instanceUserdata = (wax_instance_userdata *)luaL_checkudata(L, 1, WAX_INSTANCE_METATABLE_NAME);
//wax_log(LOG_GC, @"Releasing %@ %@(%p)", instanceUserdata->isClass ? @"Class" : @"Instance", [instanceUserdata->instance class], instanceUserdata->instance);
if (!instanceUserdata->isClass && !instanceUserdata->isSuper) {
// This seems like a stupid hack. But...
// If we want to call methods on an object durring gc, we have to readd
// the instance/userdata to the userdata table. Why? Because it is
// removed from the weak table before GC is called.
wax_instance_pushUserdataTable(L);
lua_pushlightuserdata(L, instanceUserdata->instance);
lua_pushvalue(L, -3);
lua_rawset(L, -3);
[instanceUserdata->instance release];
lua_pushlightuserdata(L, instanceUserdata->instance);
lua_pushnil(L);
lua_rawset(L, -3);
lua_pop(L, 1);
}
return 0;
}
static int __tostring(lua_State *L) {
wax_instance_userdata *instanceUserdata = (wax_instance_userdata *)luaL_checkudata(L, 1, WAX_INSTANCE_METATABLE_NAME);
lua_pushstring(L, [[NSString stringWithFormat:@"(%p => %p) %@", instanceUserdata, instanceUserdata->instance, instanceUserdata->instance] UTF8String]);
return 1;
}
static int __eq(lua_State *L) {
wax_instance_userdata *o1 = (wax_instance_userdata *)luaL_checkudata(L, 1, WAX_INSTANCE_METATABLE_NAME);
wax_instance_userdata *o2 = (wax_instance_userdata *)luaL_checkudata(L, 2, WAX_INSTANCE_METATABLE_NAME);
lua_pushboolean(L, [o1->instance isEqual:o2->instance]);
return 1;
}
#pragma mark Userdata Functions
#pragma mark -----------------------
static int methods(lua_State *L) {
wax_instance_userdata *instanceUserdata = (wax_instance_userdata *)luaL_checkudata(L, 1, WAX_INSTANCE_METATABLE_NAME);
uint count;
Method *methods = class_copyMethodList([instanceUserdata->instance class], &count);
lua_newtable(L);
for (int i = 0; i < count; i++) {
Method method = methods[i];
lua_pushstring(L, sel_getName(method_getName(method)));
lua_rawseti(L, -2, i + 1);
}
return 1;
}
#pragma mark Function Closures
#pragma mark ----------------------
static int methodClosure(lua_State *L) {
if (![[NSThread currentThread] isEqual:[NSThread mainThread]]) NSLog(@"METHODCLOSURE: OH NO SEPERATE THREAD");
wax_instance_userdata *instanceUserdata = (wax_instance_userdata *)luaL_checkudata(L, 1, WAX_INSTANCE_METATABLE_NAME);
const char *selectorName = luaL_checkstring(L, lua_upvalueindex(1));
// If the only arg is 'self' and there is a selector with no args. USE IT!
if (lua_gettop(L) == 1 && lua_isstring(L, lua_upvalueindex(2))) {
selectorName = luaL_checkstring(L, lua_upvalueindex(2));
}
SEL selector = sel_getUid(selectorName);
id instance = instanceUserdata->instance;
BOOL autoAlloc = NO;
// If init is called on a class, auto-allocate it.
if (instanceUserdata->isClass && wax_isInitMethod(selectorName)) {
autoAlloc = YES;
instance = [instance alloc];
}
NSMethodSignature *signature = [instance methodSignatureForSelector:selector];
if (!signature) {
const char *className = [NSStringFromClass([instance class]) UTF8String];
luaL_error(L, "'%s' has no method selector '%s'", className, selectorName);
}
NSInvocation *invocation = nil;
invocation = [NSInvocation invocationWithMethodSignature:signature];
[invocation setTarget:instance];
[invocation setSelector:selector];
int objcArgumentCount = [signature numberOfArguments] - 2; // skip the hidden self and _cmd argument
int luaArgumentCount = lua_gettop(L) - 1;
if (objcArgumentCount > luaArgumentCount && !wax_instance_isWaxClass(instance)) {
luaL_error(L, "Not Enough arguments given! Method named '%s' requires %d argument(s), you gave %d. (Make sure you used ':' to call the method)", selectorName, objcArgumentCount + 1, lua_gettop(L));
}
void **arguements = calloc(sizeof(void*), objcArgumentCount);
for (int i = 0; i < objcArgumentCount; i++) {
arguements[i] = wax_copyToObjc(L, [signature getArgumentTypeAtIndex:i + 2], i + 2, nil);
[invocation setArgument:arguements[i] atIndex:i + 2];
}
@try {
[invocation invoke];
}
@catch (NSException *exception) {
luaL_error(L, "Error invoking method '%s' on '%s' because %s", selector, class_getName([instance class]), [[exception description] UTF8String]);
}
for (int i = 0; i < objcArgumentCount; i++) {
free(arguements[i]);
}
free(arguements);
int methodReturnLength = [signature methodReturnLength];
if (methodReturnLength > 0) {
void *buffer = calloc(1, methodReturnLength);
[invocation getReturnValue:buffer];
wax_fromObjc(L, [signature methodReturnType], buffer);
if (autoAlloc) {
if (lua_isnil(L, -1)) {
// The init method returned nil... means initialization failed!
// Remove it from the userdataTable (We don't ever want to clean up after this... it should have cleaned up after itself)
wax_instance_pushUserdataTable(L);
lua_pushlightuserdata(L, instance);
lua_pushnil(L);
lua_rawset(L, -3);
lua_pop(L, 1); // Pop the userdataTable
lua_pushnil(L);
[instance release];
}
else {
wax_instance_userdata *returnedInstanceUserdata = (wax_instance_userdata *)lua_topointer(L, -1);
if (returnedInstanceUserdata) { // Could return nil
[returnedInstanceUserdata->instance release]; // Wax automatically retains a copy of the object, so the alloc needs to be released
}
}
}
free(buffer);
}
return 1;
}
static int superMethodClosure(lua_State *L) {
wax_instance_userdata *instanceUserdata = (wax_instance_userdata *)luaL_checkudata(L, 1, WAX_INSTANCE_METATABLE_NAME);
const char *selectorName = luaL_checkstring(L, lua_upvalueindex(1));
// If the only arg is 'self' and there is a selector with no args. USE IT!
if (lua_gettop(L) == 1 && lua_isstring(L, lua_upvalueindex(2))) {
selectorName = luaL_checkstring(L, lua_upvalueindex(2));
}
SEL selector = sel_getUid(selectorName);
// Super Swizzle
Method selfMethod = class_getInstanceMethod([instanceUserdata->instance class], selector);
Method superMethod = class_getInstanceMethod(instanceUserdata->isSuper, selector);
if (superMethod && selfMethod != superMethod) { // Super's got what you're looking for
IMP selfMethodImp = method_getImplementation(selfMethod);
IMP superMethodImp = method_getImplementation(superMethod);
method_setImplementation(selfMethod, superMethodImp);
methodClosure(L);
method_setImplementation(selfMethod, selfMethodImp); // Swap back to self's original method
}
else {
methodClosure(L);
}
return 1;
}
static int customInitMethodClosure(lua_State *L) {
wax_instance_userdata *classInstanceUserdata = (wax_instance_userdata *)luaL_checkudata(L, 1, WAX_INSTANCE_METATABLE_NAME);
wax_instance_userdata *instanceUserdata = nil;
id instance = nil;
BOOL shouldRelease = NO;
if (classInstanceUserdata->isClass) {
shouldRelease = YES;
instance = [classInstanceUserdata->instance alloc];
instanceUserdata = wax_instance_create(L, instance, NO);
lua_replace(L, 1); // replace the old userdata with the new one!
}
else {
luaL_error(L, "I WAS TOLD THIS WAS A CUSTOM INIT METHOD. BUT YOU LIED TO ME");
return -1;
}
lua_pushvalue(L, lua_upvalueindex(1)); // Grab the function!
lua_insert(L, 1); // push it up top
if (wax_pcall(L, lua_gettop(L) - 1, 1)) {
const char* errorString = lua_tostring(L, -1);
luaL_error(L, "Custom init method on '%s' failed.\n%s", class_getName([instanceUserdata->instance class]), errorString);
}
if (shouldRelease) {
[instance release];
}
if (lua_isnil(L, -1)) { // The init method returned nil... return the instanceUserdata instead
luaL_error(L, "Init method must return the self");
}
return 1;
}
#pragma mark Override Methods
#pragma mark ---------------------
static int pcallUserdata(lua_State *L, id self, SEL selector, va_list args) {
BEGIN_STACK_MODIFY(L)
if (![[NSThread currentThread] isEqual:[NSThread mainThread]]) NSLog(@"PCALLUSERDATA: OH NO SEPERATE THREAD");
// A WaxClass could have been created via objective-c (like via NSKeyUnarchiver)
// In this case, no lua object was ever associated with it, so we've got to
// create one.
if (wax_instance_isWaxClass(self)) {
BOOL isClass = self == [self class];
wax_instance_create(L, self, isClass); // If it already exists, then it will just return without doing anything
lua_pop(L, 1); // Pops userdata off
}
// Find the function... could be in the object or in the class
if (!wax_instance_pushFunction(L, self, selector)) {
lua_pushfstring(L, "Could not find function named \"%s\" associated with object %s(%p).(It may have been released by the GC)", selector, class_getName([self class]), self);
goto error; // function not found in userdata...
}
// Push userdata as the first argument
wax_fromInstance(L, self);
if (lua_isnil(L, -1)) {
lua_pushfstring(L, "Could not convert '%s' into lua", class_getName([self class]));
goto error;
}
NSMethodSignature *signature = [self methodSignatureForSelector:selector];
int nargs = [signature numberOfArguments] - 1; // Don't send in the _cmd argument, only self
int nresults = [signature methodReturnLength] ? 1 : 0;
for (int i = 2; i < [signature numberOfArguments]; i++) { // start at 2 because to skip the automatic self and _cmd arugments
const char *type = [signature getArgumentTypeAtIndex:i];
int size = wax_fromObjc(L, type, args);
args += size; // HACK! Since va_arg requires static type, I manually increment the args
}
if (wax_pcall(L, nargs, nresults)) { // Userdata will allways be the first object sent to the function
goto error;
}
END_STACK_MODIFY(L, nresults)
return nresults;
error:
END_STACK_MODIFY(L, 1)
return -1;
}
#define WAX_METHOD_NAME(_type_) wax_##_type_##_call
#define WAX_METHOD(_type_) \
static _type_ WAX_METHOD_NAME(_type_)(id self, SEL _cmd, ...) { \
va_list args; \
va_start(args, _cmd); \
va_list args_copy; \
va_copy(args_copy, args); \
/* Grab the static L... this is a hack */ \
lua_State *L = wax_currentLuaState(); \
BEGIN_STACK_MODIFY(L); \
int result = pcallUserdata(L, self, _cmd, args_copy); \
va_end(args_copy); \
va_end(args); \
if (result == -1) { \
luaL_error(L, "Error calling '%s' on '%s'\n%s", _cmd, [[self description] UTF8String], lua_tostring(L, -1)); \
} \
else if (result == 0) { \
_type_ returnValue; \
bzero(&returnValue, sizeof(_type_)); \
END_STACK_MODIFY(L, 0) \
return returnValue; \
} \
\
NSMethodSignature *signature = [self methodSignatureForSelector:_cmd]; \
_type_ *pReturnValue = (_type_ *)wax_copyToObjc(L, [signature methodReturnType], -1, nil); \
_type_ returnValue = *pReturnValue; \
free(pReturnValue); \
END_STACK_MODIFY(L, 0) \
return returnValue; \
}
typedef struct _buffer_16 {char b[16];} buffer_16;
WAX_METHOD(buffer_16)
WAX_METHOD(id)
WAX_METHOD(int)
WAX_METHOD(long)
WAX_METHOD(float)
WAX_METHOD(BOOL)
// Only allow classes to do this
static BOOL overrideMethod(lua_State *L, wax_instance_userdata *instanceUserdata) {
BEGIN_STACK_MODIFY(L);
BOOL success = NO;
const char *methodName = lua_tostring(L, 2);
SEL foundSelectors[2] = {nil, nil};
wax_selectorForInstance(instanceUserdata, foundSelectors, methodName, YES);
SEL selector = foundSelectors[0];
if (foundSelectors[1]) {
//NSLog(@"Found two selectors that match %s. Defaulting to %s over %s", methodName, foundSelectors[0], foundSelectors[1]);
}
Class klass = [instanceUserdata->instance class];
char *typeDescription = nil;
char *returnType = nil;
Method method = class_getInstanceMethod(klass, selector);
if (method) { // Is method defined in the superclass?
typeDescription = (char *)method_getTypeEncoding(method);
returnType = method_copyReturnType(method);
}
else { // Is this method implementing a protocol?
Class currentClass = klass;
while (!returnType && [currentClass superclass] != [currentClass class]) { // Walk up the object heirarchy
uint count;
Protocol **protocols = class_copyProtocolList(currentClass, &count);
SEL possibleSelectors[2];
wax_selectorsForName(methodName, possibleSelectors);
for (int i = 0; !returnType && i < count; i++) {
Protocol *protocol = protocols[i];
struct objc_method_description m_description;
for (int j = 0; !returnType && j < 2; j++) {
selector = possibleSelectors[j];
if (!selector) continue; // There may be only one acceptable selector sent back
m_description = protocol_getMethodDescription(protocol, selector, YES, YES);
if (!m_description.name) m_description = protocol_getMethodDescription(protocol, selector, NO, YES); // Check if it is not a "required" method
if (m_description.name) {
typeDescription = m_description.types;
returnType = method_copyReturnType((Method)&m_description);
}
}
}
free(protocols);
currentClass = [currentClass superclass];
}
}
if (returnType) { // Matching method found! Create an Obj-C method on the
if (!instanceUserdata->isClass) {
luaL_error(L, "Trying to override method '%s' on an instance. You can only override classes", methodName);
}
const char *simplifiedReturnType = wax_removeProtocolEncodings(returnType);
IMP imp;
switch (simplifiedReturnType[0]) {
case WAX_TYPE_VOID:
case WAX_TYPE_ID:
imp = (IMP)WAX_METHOD_NAME(id);
break;
case WAX_TYPE_CHAR:
case WAX_TYPE_INT:
case WAX_TYPE_SHORT:
case WAX_TYPE_UNSIGNED_CHAR:
case WAX_TYPE_UNSIGNED_INT:
case WAX_TYPE_UNSIGNED_SHORT:
imp = (IMP)WAX_METHOD_NAME(int);
break;
case WAX_TYPE_LONG:
case WAX_TYPE_LONG_LONG:
case WAX_TYPE_UNSIGNED_LONG:
case WAX_TYPE_UNSIGNED_LONG_LONG:
imp = (IMP)WAX_METHOD_NAME(long);
break;
case WAX_TYPE_FLOAT:
imp = (IMP)WAX_METHOD_NAME(float);
break;
case WAX_TYPE_C99_BOOL:
imp = (IMP)WAX_METHOD_NAME(BOOL);
break;
case WAX_TYPE_STRUCT: {
int size = wax_sizeOfTypeDescription(simplifiedReturnType);
switch (size) {
case 16:
imp = (IMP)WAX_METHOD_NAME(buffer_16);
break;
default:
luaL_error(L, "Trying to override a method that has a struct return type of size '%d'. There is no implementation for this size yet.", size);
return NO;
break;
}
break;
}
default:
luaL_error(L, "Can't override method with return type %s", simplifiedReturnType);
return NO;
break;
}
id metaclass = objc_getMetaClass(object_getClassName(klass));
success = class_addMethod(klass, selector, imp, typeDescription) && class_addMethod(metaclass, selector, imp, typeDescription);
if (returnType) free(returnType);
}
else {
SEL possibleSelectors[2];
wax_selectorsForName(methodName, possibleSelectors);
success = YES;
for (int i = 0; i < 2; i++) {
selector = possibleSelectors[i];
if (!selector) continue; // There may be only one acceptable selector sent back
int argCount = 0;
char *match = (char *)sel_getName(selector);
while ((match = strchr(match, ':'))) {
match += 1; // Skip past the matched char
argCount++;
}
size_t typeDescriptionSize = 3 + argCount;
typeDescription = calloc(typeDescriptionSize + 1, sizeof(char));
memset(typeDescription, '@', typeDescriptionSize);
typeDescription[2] = ':'; // Never forget _cmd!
IMP imp = (IMP)WAX_METHOD_NAME(id);
id metaclass = objc_getMetaClass(object_getClassName(klass));
success = success &&
class_addMethod(klass, possibleSelectors[i], imp, typeDescription) &&
class_addMethod(metaclass, possibleSelectors[i], imp, typeDescription);
free(typeDescription);
}
}
END_STACK_MODIFY(L, 1)
return success;
}
Jump to Line
Something went wrong with that request. Please try again.