Search code examples
clualuac

Passing array to C as argument in the stack


I use Lua for arrays manipulating; arrays are simple binary data:

local ram_ctx = {0,0,0,0,0,0,0,0,0}

I want to pass it to GUI written in C. The problem is if I pass it directly like func(ram_ctx), Lua function seems to stop executing after a call. Corresponding C function are not executed (it can be empty). But if I make global array in Lua and access it with lua_getglobal - everything seems to be OK. What I'm doing wrong or is it possible at all? It is not OK to pass array name as argument in order to call it as global array

Lua code:

function device_init()
--set_callback(1000000000000, 0)
local array = {0xFF,0,0,0,0,0,0}
--create_memory_popup("Test")
set_memory_popup(array)
end

Here is C code I try to use:

static int32_t lua_set_popup_memory (lua_State *L) 
{  
  int32_t argnum = lua_gettop(L);
  if (1 != argnum)
  {
    out_error("Function %s expects 1 argument got %d\n", __PRETTY_FUNCTION__, argnum);
    return 0;
  }  
if (0 == lua_istable(L, -1))
{
    out_log("No array found");
    return 0;
}
int32_t a_size = lua_rawlen(L, -1);
uint8_t *buf = calloc(1, a_size);
for (int i=1;;i++)
{    
    lua_rawgeti(L,-1, i);
    if (lua_isnil(L,-1)) 
        break;
    buf[i] = lua_tonumber(L,-1);
    lua_pop(L, 1);
}
set_popup_memory(memory_popup, 0, buf, a_size);
free(buf);
  return 0;  
}

Solution

  • I doubt that someone can actually diagnose the problem in question without full example, but here is idiomatic way to handle Lua-to-C calls and some comments on the code itself:

    static int // not int32_t
    lua_set_popup_memory(lua_State *L)
    {
        luaL_checktype(L, 1, LUA_TTABLE);
        // let alone excessive arguments (idiomatic), or do:
        lua_settop(L, 1);
    
        int a_size = lua_rawlen(L, 1); // absolute indexing for arguments
        uint8_t *buf = malloc((size_t)a_size);
    
        for (int i = 1; i <= a_size; i++) {
            lua_pushinteger(L, i);
            lua_gettable(L, 1); // always give a chance to metamethods
            // OTOH, metamethods are already broken here with lua_rawlen()
            // if you are on 5.2, use lua_len()
    
            if (lua_isnil(L, -1)) { // relative indexing for "locals"
                a_size = i-1; // fix actual size (e.g. 4th nil means a_size==3)
                break;
            }
    
            if (!lua_isnumber(L, -1)) // optional check
                return luaL_error(L, "item %d invalid (number required, got %s)",
                                  i, luaL_typename(L, -1));
    
            lua_Integer b = lua_tointeger(L, -1);
    
            if (b < 0 || b > UINT8_MAX) // optional
                return luaL_error(L, "item %d out of range", i);
    
            buf[i-1] = b; // Lua is 1-based, C is 0-based
            lua_pop(L, 1);
        }
    
        set_popup_memory(memory_popup, 0, buf, a_size);
        free(buf);
    
        return 0;
    }
    

    Please note that lua_CFunction is defined as int (*)(lua_State *), so return type of int32_t may (and most likely will) cause problems on non-32-bit platforms. Also, original code is probably overflowing buf[i], because C indexes start with zero, not 1. And there is one more obvious issue: lua_rawlen() may return the index greater than loop counts to (e.g. array with nil-holes), causing unneeded zeroes being passed to set_popup_memory (assuming priority of first-nil method over table length).

    Not sure about out_error, the use of Lua errors may give cleaner diagnostics, especially when entry point was called with lua_pcall's traceback argument.

    This code snippet was not actually tested.