EXC_BAD_ACCESS error when accessing Lua user data in iOS - objective-c

I am writing an iOS library to embed Lua in games and have encountered a problem regarding userdata. I want users to be able to treat my library objects as normal tables (in Lua scripts) to set attributes and for these attributes to be available to the base objects in the library. For instances a user script may have
line = display.newLine
line.width = 3
Then the width field should be accessible from within the library (Objective C/C) code.
I have this working, sort of, but after running for a few seconds I get an EXC_BAD_ACCESS error, so obviously I am accessing a freed object or have some other type of memory corruption, but I can't seem to figure out why.
I have trimmed down my code to just one example to reproduce the error. First I have a base Objective C object that implements the library functionality. The header is shown below:
#import "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#interface GeminiLine : NSObject {
int selfRef;
int propertyTableRef;
lua_State *L;
}
#property (nonatomic) int propertyTableRef;
-(id)initWithLuaState:(lua_State *)luaStat;
-(double)getDoubleForKey:(const char*) key withDefault:(double)dflt;
#end
This class keeps a reference to the lua_State object and integer Lua references to it's corresponding Lua userdata and uservalue (the tables associated with the userdata). The reference propertyTableRef is used to access the object attributes (uservalue table).
The implementation is given below:
#import "GeminiLine.h"
#implementation GeminiLine
#synthesize propertyTableRef;
-(id)initWithLuaState:(lua_State *)luaState {
self = [super init];
if (self) {
L = luaState;
}
return self;
}
-(double)getDoubleForKey:(const char*) key withDefault:(double)dflt {
lua_rawgeti(L, LUA_REGISTRYINDEX, propertyTableRef);
//lua_pushstring(L, key);
//lua_gettable(L, -2);
lua_getfield(L, -1, key);
if (lua_isnil(L, -1)) {
return dflt;
}
return lua_tonumber(L, -1);
}
-(void)dealloc {
luaL_unref(L, LUA_REGISTRYINDEX, propertyTableRef);
[super dealloc];
}
#end
The only non-lifecycle method here is the getDoubleForKey method, which accesses the Lua uservalue associated with the userdata for the object.
The C code to bind the object to Lua is given here:
///////////// lines ///////////////////////////
static int newLine(lua_State *L){
NSLog(#"Creating new line...");
GeminiLine *line = [[GeminiLine alloc] initWithLuaState:L];
GeminiLine **lLine = (GeminiLine **)lua_newuserdata(L, sizeof(GeminiLine *));
*lLine = line;
[Gemini shared].line = line;
luaL_getmetatable(L, GEMINI_LINE_LUA_KEY);
lua_setmetatable(L, -2);
// append a lua table to this user data to allow the user to store values in it
lua_newtable(L);
lua_pushvalue(L, -1); // make a copy of the table becaue the next line pops the top value
// store a reference to this table so we can access it later
line.propertyTableRef = luaL_ref(L, LUA_REGISTRYINDEX);
// set the table as the user value for the Lua object
lua_setuservalue(L, -2);
NSLog(#"New line created.");
return 1;
}
static int lineGC (lua_State *L){
NSLog(#"lineGC called");
GeminiLine **line = (GeminiLine **)luaL_checkudata(L, 1, GEMINI_LINE_LUA_KEY);
[*line release];
return 0;
}
static int lineIndex( lua_State* L )
{
NSLog(#"Calling lineIndex()");
/* object, key */
/* first check the environment */
lua_getuservalue( L, -2 );
if(lua_isnil(L,-1)){
NSLog(#"user value for user data is nil");
}
lua_pushvalue( L, -2 );
lua_rawget( L, -2 );
if( lua_isnoneornil( L, -1 ) == 0 )
{
return 1;
}
lua_pop( L, 2 );
/* second check the metatable */
lua_getmetatable( L, -2 );
lua_pushvalue( L, -2 );
lua_rawget( L, -2 );
/* nil or otherwise, we return 1 here */
return 1;
}
// this function gets called with the table on the bottom of the stack, the index to assign to next,
// and the value to be assigned on top
static int lineNewIndex( lua_State* L )
{
NSLog(#"Calling lineNewIndex()");
int top = lua_gettop(L);
NSLog(#"stack has %d values", top);
lua_getuservalue( L, -3 );
/* object, key, value */
lua_pushvalue(L, -3);
lua_pushvalue(L,-3);
lua_rawset( L, -3 );
NSLog(#"Finished lineNewIndex()");
return 0;
}
// the mappings for the library functions
static const struct luaL_Reg displayLib_f [] = {
{"newLine", newLine},
{NULL, NULL}
};
// mappings for the line methods
static const struct luaL_Reg line_m [] = {
{"__gc", lineGC},
{"__index", lineIndex},
{"__newindex", lineNewIndex},
{NULL, NULL}
};
int luaopen_display_lib (lua_State *L){
// create meta tables for our various types /////////
// lines
luaL_newmetatable(L, GEMINI_LINE_LUA_KEY);
lua_pushvalue(L, -1);
luaL_setfuncs(L, line_m, 0);
/////// finished with metatables ///////////
// create the table for this library and popuplate it with our functions
luaL_newlib(L, displayLib_f);
return 1;
}
The key methods here are newLine and lineNewIndex. In newLine I create the objective C GeminiLine object corresponding to the Lua object and store a pointer to it in Lua userdata. I also store a pointer to the new object in a singleton Gemini object, which is the object that provides the main program with access to executing Lua scripts. This class is given here:
Gemini *singleton = nil;
#interface Gemini () {
#private
lua_State *L;
}
#end
#implementation Gemini
#synthesize line;
- (id)init
{
self = [super init];
if (self) {
L = luaL_newstate();
luaL_openlibs(L);
}
return self;
}
+(Gemini *)shared {
if (singleton == nil) {
singleton = [[Gemini alloc] init];
}
return singleton;
}
-(void)execute:(NSString *)filename {
int err;
lua_settop(L, 0);
NSString *luaFilePath = [[NSBundle mainBundle] pathForResource:filename ofType:#"lua"];
setLuaPath(L, [luaFilePath stringByDeletingLastPathComponent]);
err = luaL_loadfile(L, [luaFilePath cStringUsingEncoding:[NSString defaultCStringEncoding]]);
if (0 != err) {
luaL_error(L, "cannot compile lua file: %s",
lua_tostring(L, -1));
return;
}
err = lua_pcall(L, 0, 0, 0);
if (0 != err) {
luaL_error(L, "cannot run lua file: %s",
lua_tostring(L, -1));
return;
}
}
#end
For my test program I have created an application using the single view template. I modified the applicationDidFinishLaunching method on the AppDelegate to call a test script as follows:
-(void) update {
double width = [[Gemini shared].line getDoubleForKey:"width" withDefault:5.0];
NSLog(#"width = %f", width);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
[[Gemini shared] execute:#"test"];
timer = [NSTimer scheduledTimerWithTimeInterval:0.01
target:self
selector:#selector(update)
userInfo:nil
repeats:YES];
....
I have also included a timer that fires 100 times per second and an update method as its target. The update method retrieves the attribute set in the lua script, width, and logs it with NSLog.
The test.lua script I am using is given below:
display = require('display')
line = display.newLine()
line.width = 3;
Now when I run this code, it executes correctly for several seconds, printing out the correct message and appropriate line width, but then it fails with an EXC_BAD_ACCESS error on the NSLog(#"width = %f", width); line of the update method. At first I thought maybe the line object was being garbage collected, but the lineGC method would log this and it does not. So I'm convinced the problem is in the way I am using the uservalue of my Lua userdata, either in setup or access.
Can anyone see an error in the way I have implemented this?
EDIT
To confirm that my userdata isn't being garbage collected, I disabled the GC before even loading the script using lua_gc(L, LUA_GCSTOP, 0);. Still get exactly the same problem.
I forgot to mention earlier that I'm using Lua 5.2.
Turning on every memory debugging flag using "Edit scheme" indicates that the error is happening in the following Lua code base function on the call to setsvalue2s, which is actually a macro:
LUA_API void lua_getfield (lua_State *L, int idx, const char *k) {
StkId t;
lua_lock(L);
t = index2addr(L, idx);
api_checkvalidindex(L, t);
setsvalue2s(L, L->top, luaS_new(L, k));
api_incr_top(L);
luaV_gettable(L, t, L->top - 1, L->top - 1);
lua_unlock(L);
}

I've run into similar issues. My guess, is that the Lua memory manager (or ObjC manager) is releasing the object. It works correctly for a few seconds because it will not have been garbage collected.

I'm pretty sure I have the answer to my own question now. The problem is in the getDoubleForKey method:
-(double)getDoubleForKey:(const char*) key withDefault:(double)dflt {
lua_rawgeti(L, LUA_REGISTRYINDEX, propertyTableRef);
//lua_pushstring(L, key);
//lua_gettable(L, -2);
lua_getfield(L, -1, key);
if (lua_isnil(L, -1)) {
return dflt;
}
return lua_tonumber(L, -1);
}
I'm new to Lua and had not realized that I needed to empty the stack after making calls like this. When my library functions are invoked by Lua there is no need, but here I am making the call so Lua does not bail me out.
I found this out by printing out the stack size at the top of the method and seeing it increasing with every call. Eventually the stack got so big that bad things happened. The solution is to empty out the stack before exiting the method:
-(double)getDoubleForKey:(const char*) key withDefault:(double)dflt {
lua_rawgeti(L, LUA_REGISTRYINDEX, propertyTableRef);
lua_getfield(L, -1, key);
if (lua_isnil(L, -1)) {
lua_pop(L,2);
return dflt;
}
double rval = lua_tonumber(L, -1);
lua_pop(L, 2);
return rval;
}

Related

Is it possible to generate a wrapper for each method invocation in Objective-C?

I have a project with a lot of classes.
I want to log (e.g. to stderr) invocations of each selector in runtime.
My main requirement is not to change the existing code, so I can't just log the function's params at the start of each call.
If some method is invoked during program execution, e.g.
#implementation Class1
// ...
- (int)someFunc:(Class2*) a andClass3:(Class3*)b
{
}
// ...
#end
I want to replace it with something like:
- (int)someFuncWrapper:(Class2*) a andClass3:(Class3*)b
{
NSLog(#"- (int)someFuncWrapper:a andClass3:b <= a=%#, ab=%#", a, b);
return [someFunc: a andClass3:b];
}
Is it possible?
I've read of method swizzling, KVO, forward messaging.
My current approach with method swizzling causes infinite recursion:
- (int)funcToSwizzle:(int)a andB:(int)b
{
int r = a+b;
NSLog(#"funcToSwizzle: %d", r);
return r;
}
- (void)doSimpleSwizzling
{
NSLog(#"r1 = %d", [self funcToSwizzle:10 andB:20]);
Class curClass = NSClassFromString(#"HPTracer");
unsigned int methodCount = 0;
Method *methods = class_copyMethodList( curClass, &methodCount);
for (int i=0; i<methodCount; ++i)
{
SEL originalSelector = method_getName(methods[i]);
if ( strcmp("funcToSwizzle:andB:", sel_getName(originalSelector)) == 0 )
{
Method m1 = class_getInstanceMethod(curClass, originalSelector);
id block3 = ^(id self, int a, int b) {
NSLog(#"My block: %d", a*b);
// get current implementation of "funcToSwizzle".
// copy it. store that "IMP"/"void *" etc
return [self funcToSwizzle:a andB:b];
};
IMP imp3 = imp_implementationWithBlock(block3);
method_setImplementation(m1, imp3);
}
}
NSLog(#"r2 = %d", [self funcToSwizzle:10 andB:20]);
}
And I'm afraid it's impossible to generate a block3 or some method in runtime. There's NSSelectorFromString but no ImplementationFromString.
UPD
I looked at DTrace util, it seems very powerful, but doesn't fit my needs.
It requires disabling SIP on Mac OS, and is either impossible on iOS or possible on jailbreaked device.
What I need from methods interceptions is creating a stable custom "framework" for both Debug and production build modes.

Lua - Get instance of caller when using arguments using a (objective-)c bridge

I am trying to create a simple bridge between lua and my 'native' code. Using the following code I am adding an LuaObject class so that it can used from the lua code.
-(instancetype) init
{
if((self = [super init]))
{
// temp for testing script
L = luaL_newstate();
luaL_openlibs(L);
[self registerClazz:[LuaObject class]];
[self pushFunction:getObjectWithName name:#"getObjectWithName"];
}
return self;
}
-(void) pushFunction:(lua_CFunction)function name:(NSString*)name
{
lua_pushcfunction(L, function);
lua_setglobal(L, [name cStringUsingEncoding:NSASCIIStringEncoding]);
}
int getObjectWithName(lua_State *luaState)
{
NSString *name = [NSString stringWithUTF8String:lua_tostring(luaState, 1)];
lua_pop(luaState, 1);
LuaObject *luaObject = [objectMap objectForKey:name]
void *luaUserdataPtr = lua_newuserdata(luaState, sizeof(LuaObject*));
void *luaObjectPtr = (__bridge_retained void *)luaObjectPtr;
memcpy(ptr, &luaObjectPtr, sizeof(LuaObject*));
luaL_getmetatable(luaState, "LuaObject");
lua_setmetatable(luaState, -2);
return 1;
}
-(void) registerClazz:(Class)clazz
{
luaL_Reg methods[] = {
{ "talk", &proxyLuaObjectCall },
{ "say", &proxyLuaObjectCall },
{ NULL, NULL }
};
luaL_newmetatable(L, "LuaObject");
luaL_newlib(L, methods);
lua_setfield(L, -2, "__index");
lua_setmetatable(L, -2);
}
int proxyLuaObjectCall(lua_State *luaState, void* caller)
{
luaL_checkudata(luaState, 0, "LuaObject");
}
Now when proxyLuaObjectCall is called from lua, I want to be able to retrieve the instance of the LuaObject on which the method is being called. Above code works perfectly when calling a method without any arguments. But when calling a method that has any arguments the code fails with the error
bad argument #0 to '' (LuaObject expected, got table).
For example when using the following code in lua:
This works perfectly:
myObject = getObjectWithName("nominator");
myObject.talk();
This fails miserably
myObject = getObjectWithName("nominator");
myObject.say("And the winner is");
// Result: bad argument #0 to 'say' (LuaObject expected, got table).
--
myObject = getObjectWithName("nominator");
myObject.say("And the winner is", "Joan");
// Result: bad argument #0 to 'say' (LuaObject expected, got string).
I have tried changing the index when calling a method that has arguments but nothing on the stack contains a reference to the LuaObject instance.
What am I doing wrong here? What should I do to retrieve the instance of the LuaObject on which the method is called?
Everything looks good to me, but you probably should be calling it using the method notation:
myObject = getObjectWithName("nominator");
myObject:say("And the winner is");
myObject:say("") is the same as myObject.say(myObject, ""), which is what your API seems to expect.
On the other hand, #siffiejoe may be right as according to this PiL example, it should probably be luaL_checkudata(luaState, 1, "LuaObject"). In fact, you may need both of these changes to make it work.

EXC_BAD_ACCESS with NSDate in a C++ file, why?

So, I have to add a functionality to an old .cpp file.It's huge. So rewriting it in Objective C is not an option. Instead, I have added the necessary functionality using Objective-C (because I need a lot of the NSDate/NSDateFormatter functions). It worked fine. BUT, when calling the getter (on my view controller) I get this error: EXC_BAD_ACCESS.
Here is a fragment of the code:
//.h file -----------------
// C/C++ headers
#import <Foundation/NSDate.h>
#import <Foundation/NSDateFormatter.h>
namespace MySpace {
class Session {
private:
// C++ stuff
NSDate * startTime;
public:
// C++ stuff
NSDate * getStartTime();
Session(NSDate * startTime );
};
}
// .cpp file -----------------
using namespace MySpace;
Session:Session (NSDate * startTime) {
// unrelated code
if (startTime == nil ){
startTime = [NSDate date];
}
setStartTime( startTime);
// unrelated code
}
void Session::setStartTime( NSDate * startTime){
this->startTime = [startTime copy];
}
NSDate * Session::getStartTime() {
return this->startTime; // here I get the EXC_BAD_ACCESS
}
The entire project is compiled as Objective-C++ and ARC enabled. I believe this issue is caused because the member 'startTime' is released by ARC, and, when I call the getter, it points to nil?
How may I solve this problem?
Thanks.
Try that:
NSDate * Session::getStartTime() {
if (this == NULL) return nil;
return this->startTime; // here I get the EXC_BAD_ACCESS
}
The change makes getStartTime immune to a NULL this pointer.
Does that helps? If so, somewhere, you are using a dangling Session* pointer.
Step 2
Not that. That then:
#interface MyNSDate: NSDate
#end
#implementation MyNSDate
- (id) init
{
self = [super init];
if ( self == nil ) return nil;
NSLog( #"MyNSDate %# initialized", self );
return self;
}
- (void) dealloc
{
// ARC: no super call
NSLog( #"MyNSDate %# deallocated", self );
}
#end
And replace the NSDate* in your class with MyNSDate. Check the messages, breakpoint in dealloc... You should be able to find out when the date is deallocated, appropriate or not, or rules out that hypothesis.
The other idea that crossed my mind is the missing copy construcoperators. If you are copying your Session between ARC and non-ARC compilation units, it may break. You shouldn't do that, but hey, it happens.
Session::Session( const Session& rhs )
{
this->startTime = [rhs.startTime copy];
}
Session& Session::operator=( const Session& rhs )
{
if ( this->startTime != rhs.startTime )
{
this->startTime = [rhs.startTime copy];
}
return *this;
}

Multiple methods warning

I'm currently learning Objective C and in the process I've made the silly little program below. The program compiles fine - however I get the warning "multiple methods named '-setName:' found".
I've only interfaced and implemented the method once.
What does this warning mean, and how do I correct it?
#import <Foundation/Foundation.h>
// these are the three yoga-exercises we can perform
typedef enum {
kCobra,
kUniversal,
kDog
} ExerciseName;
// translating our variables into human
NSString *nameExercise (ExerciseName nameExercise)
{
switch (nameExercise) {
case kCobra:
return #"Cobra Pose";
break;
case kUniversal:
return #"Universal Stretch";
break;
case kDog:
return #"Dog Pose";
break;
}
return #"no clue!";
} // nameExercise
#interface Exercise : NSObject
{
ExerciseName name;
}
-(void) setName: (ExerciseName) name;
-(void) exerciseDo;
#end
#implementation Exercise
-(void) setName: (ExerciseName) n {
name = n;
} // setName
-(void) exerciseDo {
NSLog(#"Exercise: %#",
nameExercise(name));
}
#end
void executeExercises(id exercises[], int count) {
int i;
for(i=0; i<count; i++) {
id exercise = exercises[i];
[exercise exerciseDo];
}
}
int main (int argc, const char * argv[]) {
id exercises[1];
exercises[0] = [Exercise new]; // initiating an object of class Exercise
[exercises[0] setName:kDog];
executeExercises(exercises, 1);
return 0;
} //main
the meaning of the message is that there are multiple selectors with the name setName: in the translation (that is, it is declared in at least on other place among all included headers). the compiler may choose the wrong selector (which can introduce undefined behavior).
you can typically correct the problem using one (or more) of the following approaches:
1) rename the method to a unique name: e.g. setExerciseName may be ok, if not used in other translations.
2) match the signature of the other selector. e.g. setName:(NSString *)name
3) use type safety:
Exercise * ex = [Exercise new];
[ex setName:kCobra];
4) cast the variable to the type: [(Exercise*)exercise setName:kCobra];
5) restore the type with a new variable: Exercise * ex = exercise;
since you have declared the var as an id, you have erased the type, and it means that the object may respond any visible selector. in general, you should not erase the type in this manner, except when truly necessary.
the best approach i see is a combination of 1 and 3:
[ex setExerciseName:kCobra];

Calling Obj-C From MonoTouch Using Selectors

Well I'm completely stumped - I cannot cal from Mono into Obj-C code using Selectors either. So as a last ditch attempt I'm posting the code:
#implementation MonoWrapper
- (id)init {
self = [super init];
if (self) {
NSBundle *main = [NSBundle mainBundle];
NSString *path = [main bundlePath];
const char *c_path = [path UTF8String];
[main autorelease];
[path autorelease];
chdir (c_path);
setenv ("MONO_PATH", c_path, 1);
setenv ("MONO_XMLSERIALIZER_THS", "no", 1);
setenv ("DYLD_BIND_AT_LAUNCH", "1", 1);
setenv ("MONO_REFLECTION_SERIALIZER", "yes", 1);
_domain = mono_jit_init_version ("MonoTouch", "v2.0.50727");
MonoAssembly *assembly = mono_assembly_open("PhoneGap.dll", NULL);
MonoImage *image = mono_assembly_get_image(assembly);
MonoClass *class = mono_class_from_name(image, "PhoneGap", "PhoneGap");
MonoMethodDesc *methodDesc = mono_method_desc_new("PhoneGap.PhoneGap:getInt", TRUE);
_callbackMethod = mono_method_desc_search_in_class(methodDesc, class);
/* allocate memory for the object */
_instance = mono_object_new (_domain, class);
/* execute the default argument-less constructor */
mono_runtime_object_init (_instance);
}
// Done
return self;
}
- (void)DoSomething {
int jim = 0;
}
- (int)multiplyA:(int)a {
void *params[] = { self, #selector(DoSomething), &a };
MonoObject *result = mono_runtime_invoke(_callbackMethod, _instance, params, NULL);
int n = *(int*)mono_object_unbox (result);
return n;
}
#end
And in MonoTouch:
using System;
using MonoTouch.ObjCRuntime;
namespace PhoneGap
{
public class PhoneGap
{
public PhoneGap ()
{
}
public int getInt(IntPtr instance, IntPtr sel, int val) {
Messaging.void_objc_msgSend (instance, sel);
return val * 2;
}
}
}
Can anyone tell me how to get the Target instance handle in Mono and how to get the Selector?
Thanks
James
The target/instance IntPtr is a pointer to the native instance. You would get this when you alloc the class.
For class/static method, the target/instance IntPtr is the native class descriptor. You can get this by creating a MonoMac.ObjcRuntime.Class and using its Handle property to get the native class descriptor.
The selector IntPtr is a pointer to a selector. You can get this by creating a MonoMac.ObjcRuntime.Selector and using its Handle property to get the native selector.
When creating a wrapper class for an NSObject, you would need to subclass NSObject and use the MonoMac.Foundation.Register attribute to set its Objc-C name. Then, when you new up the wrapper, it would alloc and init the underlying native instance and you'd be able to get that from the NSObject's Handle property. This also means that you can "unwrap" pointers to NSObjects to get the 1:1 managed wrapper using MonoMac.ObjcRuntime.Runtime.GetNSObject (IntPtr ptr).
In general, you're probably better off using the btouch tools to generate the binding for you.