lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


I have an...interesting bug in my program: when I finish calling a C
function from a Lua script, I get this:

Program received signal EXC_BAD_ACCESS, Could not access memory.
0x00024698 in lua_gettop () at /usr/include/gcc/darwin/3.3/c++/iostream:76
76        // For construction of filebuffers for cout, cin, cerr, clog et. al.

The odd thing is that I've successfully called this C function before,
many times in fact. I've looked to see what could be different about
the specific (repeatable) case that I'm dealing with, and have turned
up nothing. So I'm turning it over to you guys, along with all the
context I can muster. Any assistance would be greatly appreciated.

First off, here's the Lua initialization. This is done on a per-sprite
basis (I'm writing a scripted game engine):

---
//Initialize a bunch of Lua stuff.
void Sprite::setupLua(void)
{
    L = lua_open();
    luaopen_base(L);
    luaopen_table(L);
    luaopen_io(L);
    luaopen_string(L);
    luaopen_math(L);
    luaopen_debug(L);

    lua_pushcfunction(L, Sprite::spawnSprite);
    lua_setglobal(L, "spawnSprite");
    lua_pushcfunction(L, Sprite::getSprite);
    lua_setglobal(L, "getSprite");

    int error = luaL_loadfile(L, scriptFile.c_str()) ||
                    (lua_pcall(L, 0, 0, 0));
    if (error)
    {
        fprintf(stderr, "%s\n", lua_tostring(L, -1));
        lua_pop(L, 1);  /* pop error message from the stack */
        exit(111);
    }

    lua_getglobal(L, "init");
    pushState();
    callLua(1, 1, "init");
    popState("init");
}
---

Now, here's pushState and popState. They're designed to clean up some
of the mess caused by dealing with the Lua stack. In lieu of actually
keeping track of the stack's state, I just empty it out in popState;
this should actually work just fine as pushState occurs before every
Lua "transaction" and popState occurs after; no residual information
is required. Unless I'm missing something...? Regardless, I only
started doing this when I discovered that I had upwards of 200 objects
on the stack; this was in the process of tracking down the bug I'm
currently working on.

---
//Push the current sprite state onto the Lua stack, as a table.
void Sprite::pushState(void)
{
    lua_newtable(L);
    setfield(L, "dead", 0);
    setfield(L, "vx", vel.x);
    setfield(L, "vy", vel.y);
    setfield(L, "x", boundingBox.getCenter().x);
    setfield(L, "y", boundingBox.getCenter().y);
    setfield(L, "anim", currentAnimation);
    setfield(L, "advanceFrames", 0);
}

//Pop the current sprite state off of the Lua stack.
void Sprite::popState(const char* fun)
{
    if (!lua_istable(L, -1))
    {
        cerr << "Error: script " << scriptFile << ", function " << fun <<
            " did not return a valid sprite state\n";
        exit(169);
    }
    //Short-circuit: if the sprite is dying, then we don't care about the rest.
    int dead = (int) getfield(L, "dead");
    if (dead)
    {
        world->killSprite(this);
        return;
    }
    vel.x = getfield(L, "vx");
    vel.y = getfield(L, "vy");
    //(x, y) is actually the location of the sprite's center, not its upper
    //corner.
    Coord newLoc;
    newLoc.x = getfield(L, "x");
    newLoc.y = getfield(L, "y");
    boundingBox.moveCenterTo(newLoc);
    currentAnimation = (int) getfield(L, "anim");
    int anim = (int) getfield(L, "advanceFrames");
    animations[currentAnimation]->advanceTime(anim);

    //Clean off the stack
    lua_settop(L, 0);
}
---

Here's the spawnSprite function. It's pretty simple; it just
translates the Lua stack into something that normal C++ functions can
understand, and then hands responsibility off to the World class.

---
//Tell the world to spawn a new sprite. 
int Sprite::spawnSprite(lua_State* L)
{
    int id = (int) luaL_checknumber(L, 1);
    int layer = (int) luaL_checknumber(L, 2);
    int uniqueId = (int) luaL_checknumber(L, 3);
    double x = (double) luaL_checknumber(L, 4);
    double y = (double) luaL_checknumber(L, 5);
    world->spawnSprite(id, layer, uniqueId, x, y);
    return 0;
}
---

Finally, here's the call to spawnSprite in the Lua script:

--
function update (state)
    --Spawn a bullet
    if (timer == 0) then
        spawnSprite(4, 0, 0, state.x, state.y)
        timer = 50
    end
    --Don't go off the edges of the screen
    if (state.x < 50 or state.x > 590) then
        state.vx = -state.vx
    end
    timer = timer - 1
    state.x = state.x + state.vx
    state.y = state.y + state.vy
    return state
end

Now, I've traced this program through in gdb. It works perfectly up to
through "return 0" point. Then I get that error I mentioned before.
According to `top`, I'm only using about 110MB of memory for the
entire program, so I shouldn't actually be out of memory. I'd love to
hear any ideas you might have. Hell, any constructive criticism about
my overall style is appreciated. I've a long ways to go before this
project is presentable to the general populace, and I'd love to be
speeded on my way.

-Chris Weisiger