lua-users home
lua-l archive

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


I was looking for ways to free up ram on embedded systems using lua and ran across e-lua
specifically the LuaTinyRam patches I looked through the source and replicated it on my side 
to see what kind of ram savings it brought around 10-15k on this specific application pretty good!

Anyways, having hand applied the patches I realized how much of a maintenance nightmare 
these changes would add, So I came up with something I think might work well and would like
to get some opinions.

My Idea is to use a form of late binding basically instead of luaL_register adding tables instead 
it would store the address of the table and add an __index and __pairs method to the meta table
this is lua 5.1 so I also had to change the pairs function a bit


[lauxlib.c]
------------------------------------------------------------------------------------
static int lookup_latebind_func(lua_State *L)
{
  const luaL_Reg *l;
  /* name is top of stack */
  const char * name = lua_tostring (L, -1);

  luaL_argcheck(L, lua_istable(L, -2), -2, "table expected");
  lua_getmetatable (L, -2);
  
  /* lookup our virtual function(s) */
  for(int i = lua_objlen(L, -1); i > 0; i--) {
    lua_rawgeti (L, -1, i);
    l = (const luaL_Reg *) lua_touserdata (L, -1);
    lua_pop(L, 1);

    if(!l)
      break;

    for (; l->name; l++) {
      if(!name || strcmp(name, l->name) == 0) {
        /* if we use a function it gets added to the base table*/
        lua_pushcclosure(L, l->func, 0);

        if(name) {
          lua_pushvalue(L, -1); /* dupe closure */
          lua_setfield (L, -5, l->name);
          /* returns the closure */
          return 1;
        }
        else
          lua_setfield (L, -4, l->name);
      }
    }
  }
  lua_pop(L, 2); /* base table is top of stack */
  return 0;
}


static int latebind_func_pairs(lua_State *L)
{
  const luaL_Reg *l;

  /* base table is top of stack */
  luaL_argcheck(L, lua_istable(L, -1), -1, "table expected");
  lua_getmetatable (L, -1);

  lua_getglobal(L, "pairs");  /* function to be called / returned */

  /* clone the base table */
  lua_newtable(L);
  lua_pushnil(L);
  while(lua_next(L, -5) != 0) { 
    lua_pushvalue(L, -2); /* dupe key */
    lua_insert(L, -2);
    lua_settable(L, -4);
  }

  /* add our dynamic functions */
  for(int i = lua_objlen(L, -3); i > 0; i--) {
    lua_rawgeti (L, -3, i);
    l = (const luaL_Reg *) lua_touserdata (L, -1);
    lua_pop(L, 1);

    if(!l)
      break;

    for (; l->name; l++)
    {  
      lua_pushcclosure(L, l->func, 0);
      lua_setfield (L, -2, l->name);
    }
  }

  lua_call(L, 1, 3);

  return 3;
}


LUALIB_API void (luaL_register_late) (lua_State *L, const char *libname,
                                const luaL_Reg *l) {
  if(!libname)
  {
    /* if there is no libname register normally */
    luaI_openlib(L, libname, l, 0);
    return;
  }
    
  static const struct luaL_reg virt_lib [] =
  {
    {"__latebind", lookup_latebind_func},
    {NULL, NULL}
  };

  luaI_openlib(L, libname, virt_lib, 0);

  if(!lua_getmetatable(L, -1))
    lua_createtable(L, 5, 2); //-2

  lua_pushcfunction(L, latebind_func_pairs);
  lua_setfield(L, -2, "__pairs");

  lua_pushcfunction(L, lookup_latebind_func);
  lua_setfield(L, -2, "__index");

  lua_pushinteger(L, lua_objlen(L, -1) + 1);
  lua_pushlightuserdata (L, (void *) l);
  lua_settable(L, -3);
  
  lua_setmetatable(L, -2);
}

[lbaselib.c]
-------------------------------------------------------------------------------------
static int luaB_pairs (lua_State *L) {
  if (!luaL_getmetafield(L, 1, "__pairs")) {  /* no metamethod? */
    luaL_checktype(L, 1, LUA_TTABLE);
    lua_pushvalue(L, lua_upvalueindex(1));  /* return generator, */
    lua_pushvalue(L, 1);  /* state, */
    lua_pushnil(L);  /* and initial value */
  }
  else {
    lua_pushvalue(L, 1);  /* argument 'self' to metamethod */
    lua_call(L, 1, 3);  /* get 3 values from metamethod */
  }
  
  return 3;
}


By adding the __pairs metamethod it allows us to still lookup functions within the tables
when functions are called within a latebound table the __index metamethod adds them
to the table and returns the closure this makes it faster after the first call to a function

The __latebind key allows a way to see which tables are latebound and if desired
bind all the functions by passing nil as the name __latebind(nil, t)