diff --git a/lapi.c b/lapi.c index 1ec649b0..78ae7e0b 100644 --- a/lapi.c +++ b/lapi.c @@ -10,6 +10,7 @@ #include "lprefix.h" +#include #include #include @@ -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); diff --git a/ldblib.c b/ldblib.c index c1e26e0d..32da9697 100644 --- a/ldblib.c +++ b/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; } diff --git a/lgc.c b/lgc.c index 77ccae0a..3421bd8a 100644 --- a/lgc.c +++ b/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)); diff --git a/lobject.h b/lobject.h index 01bd39fa..03396f11 100644 --- a/lobject.h +++ b/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,45 +365,51 @@ 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; +/* computes the offset of the memory area of a userdata */ +#define udatamemoffset(nuv) (sizeof(Udata) + (sizeof(UValue) * ((nuv) - 1))) + /* -** Ensures that address after this type is always fully aligned. +** Get the address of the memory block inside 'Udata'. */ -typedef union UUdata { - LUAI_MAXALIGN; /* ensures maximum alignment for 'local' udata */ - Udata uv; -} UUdata; +#define getudatamem(u) (cast_charp(u) + udatamemoffset((u)->nuvalue)) + +/* computes the size of a userdata */ +#define sizeudata(nuv,nb) (udatamemoffset(nuv) + (nb)) + +/* }================================================================== */ + /* -** Get the address of memory block inside 'Udata'. -** (Access to 'ttuv_' ensures that value is really a 'Udata'.) +** {================================================================== +** Prototypes +** =================================================================== */ -#define getudatamem(u) \ - check_exp(sizeof((u)->ttuv_), (cast_charp(u) + sizeof(UUdata))) - -#define setuservalue(L,u,o) \ - { const TValue *io=(o); Udata *iu = (u); \ - iu->user_ = io->value_; iu->ttuv_ = rttype(io); \ - checkliveness(L,io); } - - -#define getuservalue(L,u,o) \ - { TValue *io=(o); const Udata *iu = (u); \ - io->value_ = iu->user_; settt_(io, iu->ttuv_); \ - checkliveness(L,io); } - /* ** Description of an upvalue for function prototypes @@ -471,6 +477,8 @@ typedef struct Proto { GCObject *gclist; } Proto; +/* }================================================================== */ + /* diff --git a/lstring.c b/lstring.c index fbc3e02d..29a08212 100644 --- a/lstring.c +++ b/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; } diff --git a/lstring.h b/lstring.h index a994fe1e..2be5a3ff 100644 --- a/lstring.h +++ b/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); diff --git a/ltests.c b/ltests.c index 24753d5f..97d1abd4 100644 --- a/ltests.c +++ b/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; } diff --git a/lua.h b/lua.h index d6277b4e..8fb2ccbe 100644 --- a/lua.h +++ b/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) + /* }============================================================== */ /*