浏览代码

userdata can have multiple user values

Roberto Ierusalimschy 7 年之前
父节点
当前提交
ca6fe7449a
共有 8 个文件被更改,包括 121 次插入80 次删除
  1. 26 9
      lapi.c
  2. 9 4
      ldblib.c
  3. 17 16
      lgc.c
  4. 34 26
      lobject.h
  5. 8 5
      lstring.c
  6. 2 5
      lstring.h
  7. 15 10
      ltests.c
  8. 10 5
      lua.h

+ 26 - 9
lapi.c

@@ -10,6 +10,7 @@
 #include "lprefix.h"
 
 
+#include <limits.h>
 #include <stdarg.h>
 #include <string.h>
 
@@ -733,15 +734,23 @@ LUA_API int lua_getmetatable (lua_State *L, int objindex) {
 }
 
 
-LUA_API int lua_getuservalue (lua_State *L, int idx) {
+LUA_API int lua_getiuservalue (lua_State *L, int idx, int n) {
   TValue *o;
+  int t;
   lua_lock(L);
   o = index2value(L, idx);
   api_check(L, ttisfulluserdata(o), "full userdata expected");
-  getuservalue(L, uvalue(o), s2v(L->top));
+  if (n <= 0 || n > uvalue(o)->nuvalue) {
+    setnilvalue(s2v(L->top));
+    t = LUA_TNONE;
+  }
+  else {
+    setobj2s(L, L->top, &uvalue(o)->uv[n - 1].uv);
+    t = ttnov(s2v(L->top));
+  }
   api_incr_top(L);
   lua_unlock(L);
-  return ttnov(s2v(L->top - 1));
+  return t;
 }
 
 
@@ -903,16 +912,23 @@ LUA_API int lua_setmetatable (lua_State *L, int objindex) {
 }
 
 
-LUA_API void lua_setuservalue (lua_State *L, int idx) {
+LUA_API int lua_setiuservalue (lua_State *L, int idx, int n) {
   TValue *o;
+  int res;
   lua_lock(L);
   api_checknelems(L, 1);
   o = index2value(L, idx);
   api_check(L, ttisfulluserdata(o), "full userdata expected");
-  setuservalue(L, uvalue(o), s2v(L->top - 1));
-  luaC_barrier(L, gcvalue(o), s2v(L->top - 1));
-  L->top--;
+  if (!(0 < n && n <= uvalue(o)->nuvalue))
+    res = 0;
+  else {
+    setobj(L, &uvalue(o)->uv[n - 1].uv, s2v(L->top - 1));
+    luaC_barrierback(L, gcvalue(o), s2v(L->top - 1));
+    L->top--;
+    res = 1;
+  }
   lua_unlock(L);
+  return res;
 }
 
 
@@ -1231,10 +1247,11 @@ LUA_API void lua_setallocf (lua_State *L, lua_Alloc f, void *ud) {
 }
 
 
-LUA_API void *lua_newuserdata (lua_State *L, size_t size) {
+LUA_API void *lua_newuserdatauv (lua_State *L, size_t size, int nuvalue) {
   Udata *u;
   lua_lock(L);
-  u = luaS_newudata(L, size);
+  api_check(L, 0 <= nuvalue && nuvalue < USHRT_MAX, "invalid value");
+  u = luaS_newudata(L, size, nuvalue);
   setuvalue(L, s2v(L->top), u);
   api_incr_top(L);
   luaC_checkGC(L);

+ 9 - 4
ldblib.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ldblib.c,v 1.151 2015/11/23 11:29:43 roberto Exp roberto $
+** $Id: ldblib.c,v 1.152 2018/02/17 19:29:29 roberto Exp roberto $
 ** Interface from Lua to its debug API
 ** See Copyright Notice in lua.h
 */
@@ -64,19 +64,24 @@ static int db_setmetatable (lua_State *L) {
 
 
 static int db_getuservalue (lua_State *L) {
+  int n = luaL_optinteger(L, 2, 1);
   if (lua_type(L, 1) != LUA_TUSERDATA)
     lua_pushnil(L);
-  else
-    lua_getuservalue(L, 1);
+  else if (lua_getiuservalue(L, 1, n) != LUA_TNONE) {
+    lua_pushboolean(L, 1);
+    return 2;
+  }
   return 1;
 }
 
 
 static int db_setuservalue (lua_State *L) {
+  int n = luaL_optinteger(L, 3, 1);
   luaL_checktype(L, 1, LUA_TUSERDATA);
   luaL_checkany(L, 2);
   lua_settop(L, 2);
-  lua_setuservalue(L, 1);
+  if (!lua_setiuservalue(L, 1, n))
+    lua_pushnil(L);
   return 1;
 }
 

+ 17 - 16
lgc.c

@@ -113,6 +113,7 @@ static lu_mem atomic (lua_State *L);
 static GCObject **getgclist (GCObject *o) {
   switch (o->tt) {
     case LUA_TTABLE: return &gco2t(o)->gclist;
+    case LUA_TUSERDATA: return &gco2u(o)->gclist;
     case LUA_TLCL: return &gco2lcl(o)->gclist;
     case LUA_TCCL: return &gco2ccl(o)->gclist;
     case LUA_TTHREAD: return &gco2th(o)->gclist;
@@ -269,7 +270,6 @@ GCObject *luaC_newobj (lua_State *L, int tt, size_t sz) {
 ** to avoid barriers, as their values will be revisited by the thread.)
 */
 static void reallymarkobject (global_State *g, GCObject *o) {
- reentry:
   white2gray(o);
   switch (o->tt) {
     case LUA_TSHRSTR:
@@ -277,17 +277,6 @@ static void reallymarkobject (global_State *g, GCObject *o) {
       gray2black(o);
       break;
     }
-    case LUA_TUSERDATA: {
-      TValue uvalue;
-      markobjectN(g, gco2u(o)->metatable);  /* mark its metatable */
-      gray2black(o);
-      getuservalue(g->mainthread, gco2u(o), &uvalue);
-      if (valiswhite(&uvalue)) {  /* markvalue(g, &uvalue); */
-        o = gcvalue(&uvalue);
-        goto reentry;
-      }
-      break;
-    }
     case LUA_TUPVAL: {
       UpVal *uv = gco2upv(o);
       if (!upisopen(uv))  /* open upvalues are kept gray */
@@ -296,7 +285,7 @@ static void reallymarkobject (global_State *g, GCObject *o) {
       break;
     }
     case LUA_TLCL: case LUA_TCCL: case LUA_TTABLE:
-    case LUA_TTHREAD: case LUA_TPROTO: {
+    case LUA_TUSERDATA: case LUA_TTHREAD: case LUA_TPROTO: {
       linkobjgclist(o, g->gray);
       break;
     }
@@ -602,6 +591,15 @@ static int traversethread (global_State *g, lua_State *th) {
 }
 
 
+static int traverseudata (global_State *g, Udata *u) {
+  int i;
+  markobjectN(g, u->metatable);  /* mark its metatable */
+  for (i = 0; i < u->nuvalue; i++)
+    markvalue(g, &u->uv[i].uv);
+  return 1 + u->nuvalue;
+}
+
+
 /*
 ** traverse one gray object, turning it to black (except for threads,
 ** which are always gray).
@@ -612,6 +610,7 @@ static lu_mem propagatemark (global_State *g) {
   g->gray = *getgclist(o);  /* remove from 'gray' list */
   switch (o->tt) {
     case LUA_TTABLE: return traversetable(g, gco2t(o));
+    case LUA_TUSERDATA: return traverseudata(g, gco2u(o));
     case LUA_TLCL: return traverseLclosure(g, gco2lcl(o));
     case LUA_TCCL: return traverseCclosure(g, gco2ccl(o));
     case LUA_TPROTO: return traverseproto(g, gco2p(o));
@@ -742,9 +741,11 @@ static void freeobj (lua_State *L, GCObject *o) {
     case LUA_TTHREAD:
       luaE_freethread(L, gco2th(o));
       break;
-    case LUA_TUSERDATA:
-      luaM_freemem(L, o, sizeudata(gco2u(o)));
+    case LUA_TUSERDATA: {
+      Udata *u = gco2u(o);
+      luaM_freemem(L, o, sizeudata(u->nuvalue, u->len));
       break;
+    }
     case LUA_TSHRSTR:
       luaS_remove(L, gco2ts(o));  /* remove it from hash table */
       luaM_freemem(L, o, sizelstring(gco2ts(o)->shrlen));
@@ -1065,7 +1066,7 @@ static GCObject **correctgraylist (GCObject **p) {
   GCObject *curr;
   while ((curr = *p) != NULL) {
     switch (curr->tt) {
-      case LUA_TTABLE: {
+      case LUA_TTABLE: case LUA_TUSERDATA: {
         GCObject **next = getgclist(curr);
         if (getage(curr) == G_TOUCHED1) {  /* touched in this cycle? */
           lua_assert(isgray(curr));

+ 34 - 26
lobject.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lobject.h,v 2.132 2018/01/28 12:07:53 roberto Exp roberto $
+** $Id: lobject.h,v 2.133 2018/01/28 15:13:26 roberto Exp roberto $
 ** Type definitions for Lua objects
 ** See Copyright Notice in lua.h
 */
@@ -365,46 +365,52 @@ typedef union UTString {
 
 
 /*
-** Header for userdata; memory area follows the end of this structure
-** (aligned according to 'UUdata'; see next).
+** {==================================================================
+** Userdata
+** ===================================================================
+*/
+
+/* Ensures that addresses after this type are always fully aligned. */
+typedef union UValue {
+  TValue uv;
+  LUAI_MAXALIGN;  /* ensures maximum alignment for udata bytes */
+} UValue;
+
+
+/*
+** Header for userdata; memory area follows the end of this structure.
 */
 typedef struct Udata {
   CommonHeader;
-  lu_byte ttuv_;  /* user value's tag */
-  struct Table *metatable;
+  unsigned short nuvalue;  /* number of user values */
   size_t len;  /* number of bytes */
-  union Value user_;  /* user value */
+  struct Table *metatable;
+  GCObject *gclist;
+  UValue uv[1];  /* user values */
 } Udata;
 
 
-/*
-** Ensures that address after this type is always fully aligned.
-*/
-typedef union UUdata {
-  LUAI_MAXALIGN;  /* ensures maximum alignment for 'local' udata */
-  Udata uv;
-} UUdata;
-
+/* computes the offset of the memory area of a userdata */
+#define udatamemoffset(nuv)  (sizeof(Udata) + (sizeof(UValue) * ((nuv) - 1)))
 
 /*
-**  Get the address of memory block inside 'Udata'.
-** (Access to 'ttuv_' ensures that value is really a 'Udata'.)
+**  Get the address of the memory block inside 'Udata'.
 */
-#define getudatamem(u)  \
-  check_exp(sizeof((u)->ttuv_), (cast_charp(u) + sizeof(UUdata)))
+#define getudatamem(u)	(cast_charp(u) + udatamemoffset((u)->nuvalue))
 
-#define setuservalue(L,u,o) \
-	{ const TValue *io=(o); Udata *iu = (u); \
-	  iu->user_ = io->value_; iu->ttuv_ = rttype(io); \
-	  checkliveness(L,io); }
+/* computes the size of a userdata */
+#define sizeudata(nuv,nb)	(udatamemoffset(nuv) + (nb))
 
+/* }================================================================== */
 
-#define getuservalue(L,u,o) \
-	{ TValue *io=(o); const Udata *iu = (u); \
-	  io->value_ = iu->user_; settt_(io, iu->ttuv_); \
-	  checkliveness(L,io); }
 
 
+/*
+** {==================================================================
+** Prototypes
+** ===================================================================
+*/
+
 /*
 ** Description of an upvalue for function prototypes
 */
@@ -471,6 +477,8 @@ typedef struct Proto {
   GCObject *gclist;
 } Proto;
 
+/* }================================================================== */
+
 
 
 /*

+ 8 - 5
lstring.c

@@ -1,5 +1,5 @@
 /*
-** $Id: lstring.c,v 2.63 2018/01/28 15:13:26 roberto Exp roberto $
+** $Id: lstring.c,v 2.64 2018/02/15 18:06:24 roberto Exp roberto $
 ** String table (keeps all strings handled by Lua)
 ** See Copyright Notice in lua.h
 */
@@ -265,16 +265,19 @@ TString *luaS_new (lua_State *L, const char *str) {
 }
 
 
-Udata *luaS_newudata (lua_State *L, size_t s) {
+Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue) {
   Udata *u;
+  int i;
   GCObject *o;
-  if (s > MAX_SIZE - sizeof(Udata))
+  if (s > MAX_SIZE - udatamemoffset(nuvalue))
     luaM_toobig(L);
-  o = luaC_newobj(L, LUA_TUSERDATA, sizeludata(s));
+  o = luaC_newobj(L, LUA_TUSERDATA, sizeudata(nuvalue, s));
   u = gco2u(o);
   u->len = s;
+  u->nuvalue = nuvalue;
   u->metatable = NULL;
-  setuservalue(L, u, luaO_nilobject);
+  for (i = 0; i < nuvalue; i++)
+    setnilvalue(&u->uv[i].uv);
   return u;
 }
 

+ 2 - 5
lstring.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lstring.h,v 1.62 2017/07/27 13:50:16 roberto Exp roberto $
+** $Id: lstring.h,v 1.63 2017/11/23 19:29:04 roberto Exp roberto $
 ** String table (keep all strings handled by Lua)
 ** See Copyright Notice in lua.h
 */
@@ -21,9 +21,6 @@
 
 #define sizelstring(l)  (sizeof(union UTString) + ((l) + 1) * sizeof(char))
 
-#define sizeludata(l)	(sizeof(union UUdata) + (l))
-#define sizeudata(u)	sizeludata((u)->len)
-
 #define luaS_newliteral(L, s)	(luaS_newlstr(L, "" s, \
                                  (sizeof(s)/sizeof(char))-1))
 
@@ -47,7 +44,7 @@ LUAI_FUNC void luaS_resize (lua_State *L, int newsize);
 LUAI_FUNC void luaS_clearcache (global_State *g);
 LUAI_FUNC void luaS_init (lua_State *L);
 LUAI_FUNC void luaS_remove (lua_State *L, TString *ts);
-LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s);
+LUAI_FUNC Udata *luaS_newudata (lua_State *L, size_t s, int nuvalue);
 LUAI_FUNC TString *luaS_newlstr (lua_State *L, const char *str, size_t l);
 LUAI_FUNC TString *luaS_new (lua_State *L, const char *str);
 LUAI_FUNC TString *luaS_createlngstrobj (lua_State *L, size_t l);

+ 15 - 10
ltests.c

@@ -1,5 +1,5 @@
 /*
-** $Id: ltests.c,v 2.239 2018/01/09 11:24:12 roberto Exp roberto $
+** $Id: ltests.c,v 2.240 2018/01/28 15:13:26 roberto Exp roberto $
 ** Internal Module for Debugging of the Lua Implementation
 ** See Copyright Notice in lua.h
 */
@@ -265,6 +265,15 @@ static void checktable (global_State *g, Table *h) {
 }
 
 
+static void checkudata (global_State *g, Udata *u) {
+  int i;
+  GCObject *hgc = obj2gco(u);
+  checkobjref(g, hgc, u->metatable);
+  for (i = 0; i < u->nuvalue; i++)
+    checkvalref(g, hgc, &u->uv[i].uv);
+}
+
+
 /*
 ** All marks are conditional because a GC may happen while the
 ** prototype is still being created
@@ -287,7 +296,6 @@ static void checkproto (global_State *g, Proto *f) {
 }
 
 
-
 static void checkCclosure (global_State *g, CClosure *cl) {
   GCObject *clgc = obj2gco(cl);
   int i;
@@ -344,11 +352,7 @@ static void checkstack (global_State *g, lua_State *L1) {
 static void checkrefs (global_State *g, GCObject *o) {
   switch (o->tt) {
     case LUA_TUSERDATA: {
-      TValue uservalue;
-      Table *mt = gco2u(o)->metatable;
-      checkobjref(g, o, mt);
-      getuservalue(g->mainthread, gco2u(o), &uservalue);
-      checkvalref(g, o, &uservalue);
+      checkudata(g, gco2u(o));
       break;
     }
     case LUA_TUPVAL: {
@@ -728,7 +732,7 @@ static int gc_color (lua_State *L) {
     GCObject *obj = gcvalue(o);
     lua_pushstring(L, isdead(G(L), obj) ? "dead" :
                       iswhite(obj) ? "white" :
-                      isblack(obj) ? "black" : "grey");
+                      isblack(obj) ? "black" : "gray");
   }
   return 1;
 }
@@ -919,8 +923,9 @@ static int upvalue (lua_State *L) {
 
 
 static int newuserdata (lua_State *L) {
-  size_t size = cast_sizet(luaL_checkinteger(L, 1));
-  char *p = cast_charp(lua_newuserdata(L, size));
+  size_t size = cast_sizet(luaL_optinteger(L, 1, 0));
+  int nuv = luaL_optinteger(L, 2, 0);
+  char *p = cast_charp(lua_newuserdatauv(L, size, nuv));
   while (size--) *p++ = '\0';
   return 1;
 }

+ 10 - 5
lua.h

@@ -1,5 +1,5 @@
 /*
-** $Id: lua.h,v 1.339 2017/11/07 13:25:26 roberto Exp roberto $
+** $Id: lua.h,v 1.340 2018/02/17 19:29:29 roberto Exp roberto $
 ** Lua - A Scripting Language
 ** Lua.org, PUC-Rio, Brazil (http://www.lua.org)
 ** See Copyright Notice at the end of this file
@@ -247,9 +247,9 @@ LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
 LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);
 
 LUA_API void  (lua_createtable) (lua_State *L, int narr, int nrec);
-LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
+LUA_API void *(lua_newuserdatauv) (lua_State *L, size_t sz, int nuvalue);
 LUA_API int   (lua_getmetatable) (lua_State *L, int objindex);
-LUA_API int  (lua_getuservalue) (lua_State *L, int idx);
+LUA_API int  (lua_getiuservalue) (lua_State *L, int idx, int n);
 
 
 /*
@@ -263,7 +263,7 @@ LUA_API void  (lua_rawset) (lua_State *L, int idx);
 LUA_API void  (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
 LUA_API void  (lua_rawsetp) (lua_State *L, int idx, const void *p);
 LUA_API int   (lua_setmetatable) (lua_State *L, int objindex);
-LUA_API void  (lua_setuservalue) (lua_State *L, int idx);
+LUA_API int   (lua_setiuservalue) (lua_State *L, int idx, int n);
 
 
 /*
@@ -380,7 +380,7 @@ LUA_API void      (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
 
 /*
 ** {==============================================================
-** compatibility macros for unsigned conversions
+** compatibility macros
 ** ===============================================================
 */
 #if defined(LUA_COMPAT_APIINTCASTS)
@@ -390,6 +390,11 @@ LUA_API void      (lua_setallocf) (lua_State *L, lua_Alloc f, void *ud);
 #define lua_tounsigned(L,i)	lua_tounsignedx(L,(i),NULL)
 
 #endif
+
+#define lua_newuserdata(L,s)	lua_newuserdatauv(L,s,1)
+#define lua_getuservalue(L,idx)	lua_getiuservalue(L,idx,1)
+#define lua_setuservalue(L,idx)	lua_setiuservalue(L,idx,1)
+
 /* }============================================================== */
 
 /*