lua-users home
lua-l archive

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


I came across a problem when writing some serialization code for
functions with upvalues. The problem is that you cannot determine if
two functions share the same upvalue, and thus, when deserialized, you
cannot keep this functionality. This lead me to add two api functions
(which I hope the Lua authors may add :), and two debug functions. One
function, lua_upvalue() returns a pointer to the upvalue's location.
The debug function debug.upvalue() returns a light userdatam that is
this pointer (this allows for comparisons of function upvalues to see
if they are the same). The next function is lua_shareupvalue() which
takes two function indices and two upvalue indices and sets the
upvalue for function 1 at index n1 to be the upvalue for function 2 at
index n2 (you can see the code/patch attached).

In all, this allows for the following functionality:

----- upvalue.lua -----

local a, b = {}, {}
function geta()
  return a;
end
function getb()
  return b;
end

function seta(val)
  print("setting local a to ", val)
  a = val
end

function setb(val)
  print("setting local b to ", val)
  b = val
end

print(geta(), getb()) --> table: 0x806f068        table: 0x806fc30

print(debug.upvalue(geta, 1), debug.upvalue(getb, 1)) --> userdata:
0x80701d0     userdata: 0x80703b8

print(debug.shareupvalue(geta, 1, getb, 1)); --> true

print(debug.upvalue(geta, 1), debug.upvalue(getb, 1)) --> userdata:
0x80703b8     userdata: 0x80703b8
print(debug.upvalue(seta, 1), debug.upvalue(setb, 1)) --> userdata:
0x80701d0     userdata: 0x80703b8
print(geta(), getb()) --> table: 0x806fc30        table: 0x806fc30

seta({}) setb({})
--> setting local a to      table: 0x806fdf0
--> setting local b to      table: 0x806fe60

print(geta(), getb()) --> table: 0x806fe60        table: 0x806fe60

----- end upvalue.lua ------


Additionally, I believe this gives a lot of power to Lua debuggers
that previously wouldn't know this information.

Thoughts?

Cheers,

-- 
-Patrick Donnelly

"One of the lessons of history is that nothing is often a good thing
to do and always a clever thing to say."

-Will Durant
--- ../realsrc/lapi.c	2008-02-15 19:04:41.000000000 -0700
+++ lapi.c	2008-02-26 22:43:45.000000000 -0700
@@ -1051,6 +1051,42 @@
 }
 
 
+static UpVal **auxl_upvalue (lua_State *L, int funcindex, int n)
+{
+  Closure *f;
+  StkId fi;
+  UpVal **u;
+  lua_lock(L);
+  fi = index2adr(L, funcindex);
+  if (!ttisfunction(fi) || lua_iscfunction(L, funcindex))
+    return NULL; /* must be function, cfunctions don't have real upvalues */
+  f = clvalue(fi);
+  Proto *p = f->l.p;
+  if (!(1 <= n && n <= p->sizeupvalues)) return NULL;
+  u = &(f->l.upvals[n-1]);
+  lua_unlock(L);
+  return u;
+}
+
+LUA_API void *lua_upvalue (lua_State *L, int funcindex, int n)
+{
+  return (void *) *(auxl_upvalue(L, funcindex, n));
+}
+
+LUA_API int lua_shareupvalue (lua_State *L, int funcindex1, int n1,
+    int funcindex2, int n2)
+{
+  UpVal **u1, **u2;
+  lua_lock(L);
+  u1 = auxl_upvalue(L, funcindex1, n1);
+  u2 = auxl_upvalue(L, funcindex2, n2);
+  if (!(u1 && u2))
+    return 0; /* one is not valid */
+  *u1 = *u2;
+  return 1;
+}
+
+
 LUA_API const char *lua_getupvalue (lua_State *L, int funcindex, int n) {
   const char *name;
   TValue *val;


--- ../realsrc/ldblib.c	2008-02-15 19:04:41.000000000 -0700
+++ ldblib.c	2008-02-26 22:46:09.000000000 -0700
@@ -198,7 +198,30 @@
   return auxupvalue(L, 0);
 }
 
+static int db_upvalue (lua_State *L) {
+  void *p;
+  luaL_checktype(L, 1, LUA_TFUNCTION);
+  p = lua_upvalue(L, 1, luaL_checkint(L, 2));
+  if (p)
+    lua_pushlightuserdata(L, p);
+  else 
+    lua_pushnil(L);
+  return 1;
+}
 
+static int db_shareupvalue (lua_State *L) {
+  int n1, n2;
+  luaL_checktype(L, 1, LUA_TFUNCTION);
+  luaL_checktype(L, 3, LUA_TFUNCTION);
+  if (lua_iscfunction(L, 1) || lua_iscfunction(L, 3)) {
+    lua_pushboolean(L, 0);
+    return 1; /* cannot touch C upvalues from Lua */
+  }
+  n1 = luaL_checkint(L, 2);
+  n2 = luaL_checkint(L, 4);
+  lua_pushboolean(L, lua_shareupvalue(L, 1, n1, 3, n2));
+  return 1;
+}
 
 static const char KEY_HOOK = 'h';
 
@@ -385,6 +408,8 @@
   {"setlocal", db_setlocal},
   {"setmetatable", db_setmetatable},
   {"setupvalue", db_setupvalue},
+  {"upvalue", db_upvalue},
+  {"shareupvalue", db_shareupvalue},
   {"traceback", db_errorfb},
   {NULL, NULL}
 };