mirror of
https://github.com/luau-lang/luau.git
synced 2024-11-15 14:25:44 +08:00
Add tagged lightuserdata (#1087)
This change adds support for tagged lightuserdata and optional custom typenames for lightuserdata. Background: Lightuserdata is an efficient representation for many kinds of unmanaged handles and resources in a game engine. However, currently the VM only supports one kind of lightuserdata, which makes it problematic in practice. For example, it's not possible to distinguish between different kinds of lightuserdata in Lua bindings, which can lead to unsafe practices and even crashes when a wrong kind of lightuserdata is passed to a binding function. Tagged lightuserdata work similarly to tagged userdata, i.e. they allow checking the tag quickly using lua_tolightuserdatatagged (or lua_lightuserdatatag). The tag is stored in the 'extra' field of TValue so it will add no cost to the (untagged) lightuserdata type. Alternatives would be to use full userdata values or use bitpacking to embed type information into lightuserdata on application level. Unfortunately these options are not that great in practice: full userdata have major performance implications and bitpacking fails in cases where full 64 bits are already used (e.g. pointers or 64-bit hashes). Lightuserdata names are not strictly necessary but they are rather convenient when debugging Lua code. More precise error messages and tostring returning more specific typename are useful to have in practice (e.g. "resource" or "entity" instead of the more generic "userdata"). Impl note: I did not add support for renaming tags in lua_setlightuserdataname as I'm not sure if it's possible to free fixed strings. If it's simple enough, maybe we should allow renaming (although I can't think of a specific need for it)? --------- Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
This commit is contained in:
parent
c26d820902
commit
2173938eb0
@ -71,7 +71,7 @@ bool forgLoopTableIter(lua_State* L, Table* h, int index, TValue* ra)
|
|||||||
|
|
||||||
if (!ttisnil(e))
|
if (!ttisnil(e))
|
||||||
{
|
{
|
||||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(index + 1)));
|
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(index + 1)), LU_TAG_ITERATOR);
|
||||||
setnvalue(ra + 3, double(index + 1));
|
setnvalue(ra + 3, double(index + 1));
|
||||||
setobj2s(L, ra + 4, e);
|
setobj2s(L, ra + 4, e);
|
||||||
|
|
||||||
@ -90,7 +90,7 @@ bool forgLoopTableIter(lua_State* L, Table* h, int index, TValue* ra)
|
|||||||
|
|
||||||
if (!ttisnil(gval(n)))
|
if (!ttisnil(gval(n)))
|
||||||
{
|
{
|
||||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(index + 1)));
|
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(index + 1)), LU_TAG_ITERATOR);
|
||||||
getnodekey(L, ra + 3, n);
|
getnodekey(L, ra + 3, n);
|
||||||
setobj(L, ra + 4, gval(n));
|
setobj(L, ra + 4, gval(n));
|
||||||
|
|
||||||
@ -115,7 +115,7 @@ bool forgLoopNodeIter(lua_State* L, Table* h, int index, TValue* ra)
|
|||||||
|
|
||||||
if (!ttisnil(gval(n)))
|
if (!ttisnil(gval(n)))
|
||||||
{
|
{
|
||||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(index + 1)));
|
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(index + 1)), LU_TAG_ITERATOR);
|
||||||
getnodekey(L, ra + 3, n);
|
getnodekey(L, ra + 3, n);
|
||||||
setobj(L, ra + 4, gval(n));
|
setobj(L, ra + 4, gval(n));
|
||||||
|
|
||||||
@ -697,7 +697,7 @@ const Instruction* executeFORGPREP(lua_State* L, const Instruction* pc, StkId ba
|
|||||||
{
|
{
|
||||||
// set up registers for builtin iteration
|
// set up registers for builtin iteration
|
||||||
setobj2s(L, ra + 1, ra);
|
setobj2s(L, ra + 1, ra);
|
||||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)));
|
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)), LU_TAG_ITERATOR);
|
||||||
setnilvalue(ra);
|
setnilvalue(ra);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -159,9 +159,11 @@ LUA_API const char* lua_namecallatom(lua_State* L, int* atom);
|
|||||||
LUA_API int lua_objlen(lua_State* L, int idx);
|
LUA_API int lua_objlen(lua_State* L, int idx);
|
||||||
LUA_API lua_CFunction lua_tocfunction(lua_State* L, int idx);
|
LUA_API lua_CFunction lua_tocfunction(lua_State* L, int idx);
|
||||||
LUA_API void* lua_tolightuserdata(lua_State* L, int idx);
|
LUA_API void* lua_tolightuserdata(lua_State* L, int idx);
|
||||||
|
LUA_API void* lua_tolightuserdatatagged(lua_State* L, int idx, int tag);
|
||||||
LUA_API void* lua_touserdata(lua_State* L, int idx);
|
LUA_API void* lua_touserdata(lua_State* L, int idx);
|
||||||
LUA_API void* lua_touserdatatagged(lua_State* L, int idx, int tag);
|
LUA_API void* lua_touserdatatagged(lua_State* L, int idx, int tag);
|
||||||
LUA_API int lua_userdatatag(lua_State* L, int idx);
|
LUA_API int lua_userdatatag(lua_State* L, int idx);
|
||||||
|
LUA_API int lua_lightuserdatatag(lua_State* L, int idx);
|
||||||
LUA_API lua_State* lua_tothread(lua_State* L, int idx);
|
LUA_API lua_State* lua_tothread(lua_State* L, int idx);
|
||||||
LUA_API void* lua_tobuffer(lua_State* L, int idx, size_t* len);
|
LUA_API void* lua_tobuffer(lua_State* L, int idx, size_t* len);
|
||||||
LUA_API const void* lua_topointer(lua_State* L, int idx);
|
LUA_API const void* lua_topointer(lua_State* L, int idx);
|
||||||
@ -186,7 +188,7 @@ LUA_API void lua_pushcclosurek(lua_State* L, lua_CFunction fn, const char* debug
|
|||||||
LUA_API void lua_pushboolean(lua_State* L, int b);
|
LUA_API void lua_pushboolean(lua_State* L, int b);
|
||||||
LUA_API int lua_pushthread(lua_State* L);
|
LUA_API int lua_pushthread(lua_State* L);
|
||||||
|
|
||||||
LUA_API void lua_pushlightuserdata(lua_State* L, void* p);
|
LUA_API void lua_pushlightuserdatatagged(lua_State* L, void* p, int tag);
|
||||||
LUA_API void* lua_newuserdatatagged(lua_State* L, size_t sz, int tag);
|
LUA_API void* lua_newuserdatatagged(lua_State* L, size_t sz, int tag);
|
||||||
LUA_API void* lua_newuserdatadtor(lua_State* L, size_t sz, void (*dtor)(void*));
|
LUA_API void* lua_newuserdatadtor(lua_State* L, size_t sz, void (*dtor)(void*));
|
||||||
|
|
||||||
@ -323,6 +325,9 @@ typedef void (*lua_Destructor)(lua_State* L, void* userdata);
|
|||||||
LUA_API void lua_setuserdatadtor(lua_State* L, int tag, lua_Destructor dtor);
|
LUA_API void lua_setuserdatadtor(lua_State* L, int tag, lua_Destructor dtor);
|
||||||
LUA_API lua_Destructor lua_getuserdatadtor(lua_State* L, int tag);
|
LUA_API lua_Destructor lua_getuserdatadtor(lua_State* L, int tag);
|
||||||
|
|
||||||
|
LUA_API void lua_setlightuserdataname(lua_State* L, int tag, const char* name);
|
||||||
|
LUA_API const char* lua_getlightuserdataname(lua_State* L, int tag);
|
||||||
|
|
||||||
LUA_API void lua_clonefunction(lua_State* L, int idx);
|
LUA_API void lua_clonefunction(lua_State* L, int idx);
|
||||||
|
|
||||||
LUA_API void lua_cleartable(lua_State* L, int idx);
|
LUA_API void lua_cleartable(lua_State* L, int idx);
|
||||||
@ -370,6 +375,7 @@ LUA_API void lua_unref(lua_State* L, int ref);
|
|||||||
#define lua_pushliteral(L, s) lua_pushlstring(L, "" s, (sizeof(s) / sizeof(char)) - 1)
|
#define lua_pushliteral(L, s) lua_pushlstring(L, "" s, (sizeof(s) / sizeof(char)) - 1)
|
||||||
#define lua_pushcfunction(L, fn, debugname) lua_pushcclosurek(L, fn, debugname, 0, NULL)
|
#define lua_pushcfunction(L, fn, debugname) lua_pushcclosurek(L, fn, debugname, 0, NULL)
|
||||||
#define lua_pushcclosure(L, fn, debugname, nup) lua_pushcclosurek(L, fn, debugname, nup, NULL)
|
#define lua_pushcclosure(L, fn, debugname, nup) lua_pushcclosurek(L, fn, debugname, nup, NULL)
|
||||||
|
#define lua_pushlightuserdata(L, p) lua_pushlightuserdatatagged(L, p, 0)
|
||||||
|
|
||||||
#define lua_setglobal(L, s) lua_setfield(L, LUA_GLOBALSINDEX, (s))
|
#define lua_setglobal(L, s) lua_setfield(L, LUA_GLOBALSINDEX, (s))
|
||||||
#define lua_getglobal(L, s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
|
#define lua_getglobal(L, s) lua_getfield(L, LUA_GLOBALSINDEX, (s))
|
||||||
|
@ -101,6 +101,11 @@
|
|||||||
#define LUA_UTAG_LIMIT 128
|
#define LUA_UTAG_LIMIT 128
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// number of valid Lua lightuserdata tags
|
||||||
|
#ifndef LUA_LUTAG_LIMIT
|
||||||
|
#define LUA_LUTAG_LIMIT 128
|
||||||
|
#endif
|
||||||
|
|
||||||
// upper bound for number of size classes used by page allocator
|
// upper bound for number of size classes used by page allocator
|
||||||
#ifndef LUA_SIZECLASSES
|
#ifndef LUA_SIZECLASSES
|
||||||
#define LUA_SIZECLASSES 32
|
#define LUA_SIZECLASSES 32
|
||||||
|
@ -505,6 +505,12 @@ void* lua_tolightuserdata(lua_State* L, int idx)
|
|||||||
return (!ttislightuserdata(o)) ? NULL : pvalue(o);
|
return (!ttislightuserdata(o)) ? NULL : pvalue(o);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* lua_tolightuserdatatagged(lua_State* L, int idx, int tag)
|
||||||
|
{
|
||||||
|
StkId o = index2addr(L, idx);
|
||||||
|
return (!ttislightuserdata(o) || lightuserdatatag(o) != tag) ? NULL : pvalue(o);
|
||||||
|
}
|
||||||
|
|
||||||
void* lua_touserdata(lua_State* L, int idx)
|
void* lua_touserdata(lua_State* L, int idx)
|
||||||
{
|
{
|
||||||
StkId o = index2addr(L, idx);
|
StkId o = index2addr(L, idx);
|
||||||
@ -530,6 +536,14 @@ int lua_userdatatag(lua_State* L, int idx)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lua_lightuserdatatag(lua_State* L, int idx)
|
||||||
|
{
|
||||||
|
StkId o = index2addr(L, idx);
|
||||||
|
if (ttislightuserdata(o))
|
||||||
|
return lightuserdatatag(o);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
lua_State* lua_tothread(lua_State* L, int idx)
|
lua_State* lua_tothread(lua_State* L, int idx)
|
||||||
{
|
{
|
||||||
StkId o = index2addr(L, idx);
|
StkId o = index2addr(L, idx);
|
||||||
@ -665,9 +679,10 @@ void lua_pushboolean(lua_State* L, int b)
|
|||||||
api_incr_top(L);
|
api_incr_top(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lua_pushlightuserdata(lua_State* L, void* p)
|
void lua_pushlightuserdatatagged(lua_State* L, void* p, int tag)
|
||||||
{
|
{
|
||||||
setpvalue(L->top, p);
|
api_check(L, unsigned(tag) < LUA_LUTAG_LIMIT);
|
||||||
|
setpvalue(L->top, p, tag);
|
||||||
api_incr_top(L);
|
api_incr_top(L);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1412,6 +1427,24 @@ lua_Destructor lua_getuserdatadtor(lua_State* L, int tag)
|
|||||||
return L->global->udatagc[tag];
|
return L->global->udatagc[tag];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lua_setlightuserdataname(lua_State* L, int tag, const char* name)
|
||||||
|
{
|
||||||
|
api_check(L, unsigned(tag) < LUA_LUTAG_LIMIT);
|
||||||
|
api_check(L, !L->global->lightuserdataname[tag]); // renaming not supported
|
||||||
|
if (!L->global->lightuserdataname[tag])
|
||||||
|
{
|
||||||
|
L->global->lightuserdataname[tag] = luaS_new(L, name);
|
||||||
|
luaS_fix(L->global->lightuserdataname[tag]); // never collect these names
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* lua_getlightuserdataname(lua_State* L, int tag)
|
||||||
|
{
|
||||||
|
api_check(L, unsigned(tag) < LUA_LUTAG_LIMIT);
|
||||||
|
const TString* name = L->global->lightuserdataname[tag];
|
||||||
|
return name ? getstr(name) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void lua_clonefunction(lua_State* L, int idx)
|
void lua_clonefunction(lua_State* L, int idx)
|
||||||
{
|
{
|
||||||
luaC_checkGC(L);
|
luaC_checkGC(L);
|
||||||
|
@ -48,7 +48,7 @@ int luaO_rawequalObj(const TValue* t1, const TValue* t2)
|
|||||||
case LUA_TBOOLEAN:
|
case LUA_TBOOLEAN:
|
||||||
return bvalue(t1) == bvalue(t2); // boolean true must be 1 !!
|
return bvalue(t1) == bvalue(t2); // boolean true must be 1 !!
|
||||||
case LUA_TLIGHTUSERDATA:
|
case LUA_TLIGHTUSERDATA:
|
||||||
return pvalue(t1) == pvalue(t2);
|
return pvalue(t1) == pvalue(t2) && (!FFlag::TaggedLuData || lightuserdatatag(t1) == lightuserdatatag(t2));
|
||||||
default:
|
default:
|
||||||
LUAU_ASSERT(iscollectable(t1));
|
LUAU_ASSERT(iscollectable(t1));
|
||||||
return gcvalue(t1) == gcvalue(t2);
|
return gcvalue(t1) == gcvalue(t2);
|
||||||
@ -71,7 +71,7 @@ int luaO_rawequalKey(const TKey* t1, const TValue* t2)
|
|||||||
case LUA_TBOOLEAN:
|
case LUA_TBOOLEAN:
|
||||||
return bvalue(t1) == bvalue(t2); // boolean true must be 1 !!
|
return bvalue(t1) == bvalue(t2); // boolean true must be 1 !!
|
||||||
case LUA_TLIGHTUSERDATA:
|
case LUA_TLIGHTUSERDATA:
|
||||||
return pvalue(t1) == pvalue(t2);
|
return pvalue(t1) == pvalue(t2) && (!FFlag::TaggedLuData || lightuserdatatag(t1) == lightuserdatatag(t2));
|
||||||
default:
|
default:
|
||||||
LUAU_ASSERT(iscollectable(t1));
|
LUAU_ASSERT(iscollectable(t1));
|
||||||
return gcvalue(t1) == gcvalue(t2);
|
return gcvalue(t1) == gcvalue(t2);
|
||||||
|
@ -80,6 +80,11 @@ typedef struct lua_TValue
|
|||||||
|
|
||||||
#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
|
#define l_isfalse(o) (ttisnil(o) || (ttisboolean(o) && bvalue(o) == 0))
|
||||||
|
|
||||||
|
#define lightuserdatatag(o) check_exp(ttislightuserdata(o), (o)->extra[0])
|
||||||
|
|
||||||
|
// Internal tags used by the VM
|
||||||
|
#define LU_TAG_ITERATOR LUA_UTAG_LIMIT
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** for internal debug only
|
** for internal debug only
|
||||||
*/
|
*/
|
||||||
@ -120,10 +125,11 @@ typedef struct lua_TValue
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define setpvalue(obj, x) \
|
#define setpvalue(obj, x, tag) \
|
||||||
{ \
|
{ \
|
||||||
TValue* i_o = (obj); \
|
TValue* i_o = (obj); \
|
||||||
i_o->value.p = (x); \
|
i_o->value.p = (x); \
|
||||||
|
i_o->extra[0] = (tag); \
|
||||||
i_o->tt = LUA_TLIGHTUSERDATA; \
|
i_o->tt = LUA_TLIGHTUSERDATA; \
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,3 +498,5 @@ LUAI_FUNC int luaO_str2d(const char* s, double* result);
|
|||||||
LUAI_FUNC const char* luaO_pushvfstring(lua_State* L, const char* fmt, va_list argp);
|
LUAI_FUNC const char* luaO_pushvfstring(lua_State* L, const char* fmt, va_list argp);
|
||||||
LUAI_FUNC const char* luaO_pushfstring(lua_State* L, const char* fmt, ...);
|
LUAI_FUNC const char* luaO_pushfstring(lua_State* L, const char* fmt, ...);
|
||||||
LUAI_FUNC const char* luaO_chunkid(char* buf, size_t buflen, const char* source, size_t srclen);
|
LUAI_FUNC const char* luaO_chunkid(char* buf, size_t buflen, const char* source, size_t srclen);
|
||||||
|
|
||||||
|
LUAU_FASTFLAG(TaggedLuData)
|
||||||
|
@ -210,6 +210,8 @@ lua_State* lua_newstate(lua_Alloc f, void* ud)
|
|||||||
g->mt[i] = NULL;
|
g->mt[i] = NULL;
|
||||||
for (i = 0; i < LUA_UTAG_LIMIT; i++)
|
for (i = 0; i < LUA_UTAG_LIMIT; i++)
|
||||||
g->udatagc[i] = NULL;
|
g->udatagc[i] = NULL;
|
||||||
|
for (i = 0; i < LUA_LUTAG_LIMIT; i++)
|
||||||
|
g->lightuserdataname[i] = NULL;
|
||||||
for (i = 0; i < LUA_MEMORY_CATEGORIES; i++)
|
for (i = 0; i < LUA_MEMORY_CATEGORIES; i++)
|
||||||
g->memcatbytes[i] = 0;
|
g->memcatbytes[i] = 0;
|
||||||
|
|
||||||
|
@ -214,6 +214,8 @@ typedef struct global_State
|
|||||||
|
|
||||||
void (*udatagc[LUA_UTAG_LIMIT])(lua_State*, void*); // for each userdata tag, a gc callback to be called immediately before freeing memory
|
void (*udatagc[LUA_UTAG_LIMIT])(lua_State*, void*); // for each userdata tag, a gc callback to be called immediately before freeing memory
|
||||||
|
|
||||||
|
TString* lightuserdataname[LUA_LUTAG_LIMIT]; // names for tagged lightuserdata
|
||||||
|
|
||||||
GCStats gcstats;
|
GCStats gcstats;
|
||||||
|
|
||||||
#ifdef LUAI_GCMETRICS
|
#ifdef LUAI_GCMETRICS
|
||||||
|
@ -129,6 +129,18 @@ const TString* luaT_objtypenamestr(lua_State* L, const TValue* o)
|
|||||||
if (ttisstring(type))
|
if (ttisstring(type))
|
||||||
return tsvalue(type);
|
return tsvalue(type);
|
||||||
}
|
}
|
||||||
|
else if (FFlag::TaggedLuData && ttislightuserdata(o))
|
||||||
|
{
|
||||||
|
int tag = lightuserdatatag(o);
|
||||||
|
|
||||||
|
if (unsigned(tag) < LUA_LUTAG_LIMIT)
|
||||||
|
{
|
||||||
|
const TString* name = L->global->lightuserdataname[tag];
|
||||||
|
|
||||||
|
if (name)
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
}
|
||||||
else if (Table* mt = L->global->mt[ttype(o)])
|
else if (Table* mt = L->global->mt[ttype(o)])
|
||||||
{
|
{
|
||||||
const TValue* type = luaH_getstr(mt, L->global->tmname[TM_TYPE]);
|
const TValue* type = luaH_getstr(mt, L->global->tmname[TM_TYPE]);
|
||||||
|
@ -135,6 +135,8 @@
|
|||||||
// Does VM support native execution via ExecutionCallbacks? We mostly assume it does but keep the define to make it easy to quantify the cost.
|
// Does VM support native execution via ExecutionCallbacks? We mostly assume it does but keep the define to make it easy to quantify the cost.
|
||||||
#define VM_HAS_NATIVE 1
|
#define VM_HAS_NATIVE 1
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(TaggedLuData, false)
|
||||||
|
|
||||||
LUAU_NOINLINE void luau_callhook(lua_State* L, lua_Hook hook, void* userdata)
|
LUAU_NOINLINE void luau_callhook(lua_State* L, lua_Hook hook, void* userdata)
|
||||||
{
|
{
|
||||||
ptrdiff_t base = savestack(L, L->base);
|
ptrdiff_t base = savestack(L, L->base);
|
||||||
@ -1110,7 +1112,7 @@ reentry:
|
|||||||
VM_NEXT();
|
VM_NEXT();
|
||||||
|
|
||||||
case LUA_TLIGHTUSERDATA:
|
case LUA_TLIGHTUSERDATA:
|
||||||
pc += pvalue(ra) == pvalue(rb) ? LUAU_INSN_D(insn) : 1;
|
pc += (pvalue(ra) == pvalue(rb) && (!FFlag::TaggedLuData || lightuserdatatag(ra) == lightuserdatatag(rb))) ? LUAU_INSN_D(insn) : 1;
|
||||||
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
|
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
|
||||||
VM_NEXT();
|
VM_NEXT();
|
||||||
|
|
||||||
@ -1225,7 +1227,7 @@ reentry:
|
|||||||
VM_NEXT();
|
VM_NEXT();
|
||||||
|
|
||||||
case LUA_TLIGHTUSERDATA:
|
case LUA_TLIGHTUSERDATA:
|
||||||
pc += pvalue(ra) != pvalue(rb) ? LUAU_INSN_D(insn) : 1;
|
pc += (pvalue(ra) != pvalue(rb) || (FFlag::TaggedLuData && lightuserdatatag(ra) != lightuserdatatag(rb))) ? LUAU_INSN_D(insn) : 1;
|
||||||
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
|
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
|
||||||
VM_NEXT();
|
VM_NEXT();
|
||||||
|
|
||||||
@ -2296,7 +2298,7 @@ reentry:
|
|||||||
{
|
{
|
||||||
// set up registers for builtin iteration
|
// set up registers for builtin iteration
|
||||||
setobj2s(L, ra + 1, ra);
|
setobj2s(L, ra + 1, ra);
|
||||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)));
|
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)), LU_TAG_ITERATOR);
|
||||||
setnilvalue(ra);
|
setnilvalue(ra);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -2348,7 +2350,7 @@ reentry:
|
|||||||
|
|
||||||
if (!ttisnil(e))
|
if (!ttisnil(e))
|
||||||
{
|
{
|
||||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(index + 1)));
|
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(index + 1)), LU_TAG_ITERATOR);
|
||||||
setnvalue(ra + 3, double(index + 1));
|
setnvalue(ra + 3, double(index + 1));
|
||||||
setobj2s(L, ra + 4, e);
|
setobj2s(L, ra + 4, e);
|
||||||
|
|
||||||
@ -2369,7 +2371,7 @@ reentry:
|
|||||||
|
|
||||||
if (!ttisnil(gval(n)))
|
if (!ttisnil(gval(n)))
|
||||||
{
|
{
|
||||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(index + 1)));
|
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(index + 1)), LU_TAG_ITERATOR);
|
||||||
getnodekey(L, ra + 3, n);
|
getnodekey(L, ra + 3, n);
|
||||||
setobj2s(L, ra + 4, gval(n));
|
setobj2s(L, ra + 4, gval(n));
|
||||||
|
|
||||||
@ -2421,7 +2423,7 @@ reentry:
|
|||||||
{
|
{
|
||||||
setnilvalue(ra);
|
setnilvalue(ra);
|
||||||
// ra+1 is already the table
|
// ra+1 is already the table
|
||||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)));
|
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)), LU_TAG_ITERATOR);
|
||||||
}
|
}
|
||||||
else if (!ttisfunction(ra))
|
else if (!ttisfunction(ra))
|
||||||
{
|
{
|
||||||
@ -2450,7 +2452,7 @@ reentry:
|
|||||||
{
|
{
|
||||||
setnilvalue(ra);
|
setnilvalue(ra);
|
||||||
// ra+1 is already the table
|
// ra+1 is already the table
|
||||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)));
|
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)), LU_TAG_ITERATOR);
|
||||||
}
|
}
|
||||||
else if (!ttisfunction(ra))
|
else if (!ttisfunction(ra))
|
||||||
{
|
{
|
||||||
|
@ -288,7 +288,7 @@ int luaV_equalval(lua_State* L, const TValue* t1, const TValue* t2)
|
|||||||
case LUA_TBOOLEAN:
|
case LUA_TBOOLEAN:
|
||||||
return bvalue(t1) == bvalue(t2); // true must be 1 !!
|
return bvalue(t1) == bvalue(t2); // true must be 1 !!
|
||||||
case LUA_TLIGHTUSERDATA:
|
case LUA_TLIGHTUSERDATA:
|
||||||
return pvalue(t1) == pvalue(t2);
|
return pvalue(t1) == pvalue(t2) && (!FFlag::TaggedLuData || lightuserdatatag(t1) == lightuserdatatag(t2));
|
||||||
case LUA_TUSERDATA:
|
case LUA_TUSERDATA:
|
||||||
{
|
{
|
||||||
tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, TM_EQ);
|
tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, TM_EQ);
|
||||||
|
@ -32,6 +32,7 @@ LUAU_FASTFLAG(LuauBufferDefinitions);
|
|||||||
LUAU_FASTFLAG(LuauCodeGenFixByteLower);
|
LUAU_FASTFLAG(LuauCodeGenFixByteLower);
|
||||||
LUAU_FASTFLAG(LuauCompileBufferAnnotation);
|
LUAU_FASTFLAG(LuauCompileBufferAnnotation);
|
||||||
LUAU_FASTFLAG(LuauLoopInterruptFix);
|
LUAU_FASTFLAG(LuauLoopInterruptFix);
|
||||||
|
LUAU_FASTFLAG(TaggedLuData);
|
||||||
LUAU_DYNAMIC_FASTFLAG(LuauStricterUtf8);
|
LUAU_DYNAMIC_FASTFLAG(LuauStricterUtf8);
|
||||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit);
|
LUAU_FASTINT(CodegenHeuristicsInstructionLimit);
|
||||||
|
|
||||||
@ -1700,6 +1701,32 @@ TEST_CASE("UserdataApi")
|
|||||||
CHECK(dtorhits == 42);
|
CHECK(dtorhits == 42);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE("LightuserdataApi")
|
||||||
|
{
|
||||||
|
ScopedFastFlag taggedLuData{FFlag::TaggedLuData, true};
|
||||||
|
|
||||||
|
StateRef globalState(luaL_newstate(), lua_close);
|
||||||
|
lua_State* L = globalState.get();
|
||||||
|
|
||||||
|
void* value = (void*)0x12345678;
|
||||||
|
|
||||||
|
lua_pushlightuserdatatagged(L, value, 1);
|
||||||
|
CHECK(lua_lightuserdatatag(L, -1) == 1);
|
||||||
|
CHECK(lua_tolightuserdatatagged(L, -1, 0) == nullptr);
|
||||||
|
CHECK(lua_tolightuserdatatagged(L, -1, 1) == value);
|
||||||
|
|
||||||
|
lua_setlightuserdataname(L, 1, "id");
|
||||||
|
CHECK(!lua_getlightuserdataname(L, 0));
|
||||||
|
CHECK(strcmp(lua_getlightuserdataname(L, 1), "id") == 0);
|
||||||
|
CHECK(strcmp(luaL_typename(L, -1), "id") == 0);
|
||||||
|
|
||||||
|
lua_pushlightuserdatatagged(L, value, 0);
|
||||||
|
lua_pushlightuserdatatagged(L, value, 1);
|
||||||
|
CHECK(lua_rawequal(L, -1, -2) == 0);
|
||||||
|
|
||||||
|
globalState.reset();
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("Iter")
|
TEST_CASE("Iter")
|
||||||
{
|
{
|
||||||
runConformance("iter.lua");
|
runConformance("iter.lua");
|
||||||
|
Loading…
Reference in New Issue
Block a user