completely untested, speculative mockup of fatuserdata

This commit is contained in:
LoganDark 2021-11-13 06:32:19 -08:00
parent a6a2b86c9b
commit fc82c8cec2
No known key found for this signature in database
GPG key ID: B8C37CEDE1AC60EA
8 changed files with 63 additions and 10 deletions

View file

@ -65,6 +65,7 @@ enum lua_Type
LUA_TLIGHTUSERDATA, LUA_TLIGHTUSERDATA,
LUA_TFATUSERDATA,
LUA_TNUMBER, LUA_TNUMBER,
LUA_TVECTOR, LUA_TVECTOR,
@ -166,6 +167,7 @@ LUA_API void lua_pushcfunction(
lua_State* L, lua_CFunction fn, const char* debugname = NULL, int nup = 0, lua_Continuation cont = NULL); lua_State* L, lua_CFunction fn, const char* debugname = NULL, int nup = 0, lua_Continuation cont = NULL);
LUA_API void lua_pushboolean(lua_State* L, int b); LUA_API void lua_pushboolean(lua_State* L, int b);
LUA_API void lua_pushlightuserdata(lua_State* L, void* p); LUA_API void lua_pushlightuserdata(lua_State* L, void* p);
LUA_API void lua_pushfatuserdata(lua_State* L);
LUA_API int lua_pushthread(lua_State* L); LUA_API int lua_pushthread(lua_State* L);
/* /*

View file

@ -102,6 +102,14 @@
/* maximum number of captures supported by pattern matching */ /* maximum number of captures supported by pattern matching */
#define LUA_MAXCAPTURES 32 #define LUA_MAXCAPTURES 32
/*
@@ LUAU_FATUSERDATA_WORDS defines the amount of extra space embedded in
@* fatuserdata values.
** This inflates every value in the VM, so use it carefully.
** At least 1 is required to store the `vector` value type.
*/
#define LUAU_FATUSERDATA_WORDS 1
/* }================================================================== */ /* }================================================================== */
/* Default number printing format and the string length limit */ /* Default number printing format and the string length limit */

View file

@ -447,6 +447,8 @@ int lua_objlen(lua_State* L, int idx)
return tsvalue(o)->len; return tsvalue(o)->len;
case LUA_TUSERDATA: case LUA_TUSERDATA:
return uvalue(o)->len; return uvalue(o)->len;
case LUA_TFATUSERDATA:
return LUAU_FATUSERDATA_WORDS;
case LUA_TTABLE: case LUA_TTABLE:
return luaH_getn(hvalue(o)); return luaH_getn(hvalue(o));
case LUA_TNUMBER: case LUA_TNUMBER:
@ -473,6 +475,7 @@ void* lua_touserdata(lua_State* L, int idx)
case LUA_TUSERDATA: case LUA_TUSERDATA:
return uvalue(o)->data; return uvalue(o)->data;
case LUA_TLIGHTUSERDATA: case LUA_TLIGHTUSERDATA:
case LUA_TFATUSERDATA:
return pvalue(o); return pvalue(o);
default: default:
return NULL; return NULL;
@ -625,6 +628,15 @@ void lua_pushlightuserdata(lua_State* L, void* p)
return; return;
} }
const int UNINITIALIZED_FATUSERDATA[LUAU_FATUSERDATA_WORDS] = {};
void lua_pushfatuserdata(lua_State* L)
{
setmpvalue(L->top, 0, &UNINITIALIZED_FATUSERDATA);
api_incr_top(L);
return;
}
int lua_pushthread(lua_State* L) int lua_pushthread(lua_State* L)
{ {
luaC_checkthreadsleep(L); luaC_checkthreadsleep(L);
@ -740,6 +752,9 @@ int lua_getmetatable(lua_State* L, int objindex)
case LUA_TUSERDATA: case LUA_TUSERDATA:
mt = uvalue(obj)->metatable; mt = uvalue(obj)->metatable;
break; break;
case LUA_TFATUSERDATA:
mt = (Table*)(mpvalue(obj));
break;
default: default:
mt = L->global->mt[ttype(obj)]; mt = L->global->mt[ttype(obj)];
break; break;
@ -864,6 +879,13 @@ int lua_setmetatable(lua_State* L, int objindex)
luaC_objbarrier(L, uvalue(obj), mt); luaC_objbarrier(L, uvalue(obj), mt);
break; break;
} }
case LUA_TFATUSERDATA:
{
mpvalue(obj) = (void*)mt;
if (mt)
luaC_objbarrier(L, uvalue(obj), mt);
break;
}
default: default:
{ {
L->global->mt[ttype(obj)] = mt; L->global->mt[ttype(obj)] = mt;

View file

@ -42,6 +42,8 @@ LUAU_FASTFLAG(LuauArrayBoundary)
checkconsistency(o); \ checkconsistency(o); \
if (iscollectable(o) && iswhite(gcvalue(o))) \ if (iscollectable(o) && iswhite(gcvalue(o))) \
reallymarkobject(g, gcvalue(o)); \ reallymarkobject(g, gcvalue(o)); \
else if (ttisfatuserdata(o) && mpvalue(o) && iswhite((GCObject*)mpvalue(o))) \
reallymarkobject(g, (GCObject*)mpvalue(o)); \
} }
#define markobject(g, t) \ #define markobject(g, t) \

View file

@ -33,11 +33,16 @@
#define ABISWITCH(x64, ms32, gcc32) (sizeof(void*) == 8 ? x64 : ms32) #define ABISWITCH(x64, ms32, gcc32) (sizeof(void*) == 8 ? x64 : ms32)
#endif #endif
// Padding makes it difficult to verify these statically, we'd basically be mirroring the compiler's layout algorithm
// So instead, only assert these when LUAU_FATUSERDATA_WORDS is 1, which is the go-to default/embedded configuration
#if LUAU_FATUSERDATA_WORDS == 1
static_assert(sizeof(TValue) == ABISWITCH(16, 16, 16), "size mismatch for value"); static_assert(sizeof(TValue) == ABISWITCH(16, 16, 16), "size mismatch for value");
static_assert(sizeof(LuaNode) == ABISWITCH(32, 32, 32), "size mismatch for table entry");
#endif
static_assert(offsetof(TString, data) == ABISWITCH(24, 20, 20), "size mismatch for string header"); static_assert(offsetof(TString, data) == ABISWITCH(24, 20, 20), "size mismatch for string header");
static_assert(offsetof(Udata, data) == ABISWITCH(24, 16, 16), "size mismatch for userdata header"); static_assert(offsetof(Udata, data) == ABISWITCH(24, 16, 16), "size mismatch for userdata header");
static_assert(sizeof(Table) == ABISWITCH(56, 36, 36), "size mismatch for table header"); static_assert(sizeof(Table) == ABISWITCH(56, 36, 36), "size mismatch for table header");
static_assert(sizeof(LuaNode) == ABISWITCH(32, 32, 32), "size mismatch for table entry");
const size_t kSizeClasses = LUA_SIZECLASSES; const size_t kSizeClasses = LUA_SIZECLASSES;
const size_t kMaxSmallSize = 512; const size_t kMaxSmallSize = 512;

View file

@ -47,7 +47,7 @@ typedef union
typedef struct lua_TValue typedef struct lua_TValue
{ {
Value value; Value value;
int extra; int extra[LUAU_FATUSERDATA_WORDS];
int tt; int tt;
} TValue; } TValue;
@ -61,6 +61,7 @@ typedef struct lua_TValue
#define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA) #define ttisuserdata(o) (ttype(o) == LUA_TUSERDATA)
#define ttisthread(o) (ttype(o) == LUA_TTHREAD) #define ttisthread(o) (ttype(o) == LUA_TTHREAD)
#define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA) #define ttislightuserdata(o) (ttype(o) == LUA_TLIGHTUSERDATA)
#define ttisfatuserdata(o) (ttype(o) == LUA_TFATUSERDATA)
#define ttisvector(o) (ttype(o) == LUA_TVECTOR) #define ttisvector(o) (ttype(o) == LUA_TVECTOR)
#define ttisupval(o) (ttype(o) == LUA_TUPVAL) #define ttisupval(o) (ttype(o) == LUA_TUPVAL)
@ -68,6 +69,7 @@ typedef struct lua_TValue
#define ttype(o) ((o)->tt) #define ttype(o) ((o)->tt)
#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc) #define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p) #define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
#define mpvalue(o) check_exp(ttisfatuserdata(o), (o)->value.p)
#define nvalue(o) check_exp(ttisnumber(o), (o)->value.n) #define nvalue(o) check_exp(ttisnumber(o), (o)->value.n)
#define vvalue(o) check_exp(ttisvector(o), (o)->value.v) #define vvalue(o) check_exp(ttisvector(o), (o)->value.v)
#define tsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts) #define tsvalue(o) check_exp(ttisstring(o), &(o)->value.gc->ts)
@ -122,6 +124,14 @@ typedef struct lua_TValue
i_o->tt = LUA_TLIGHTUSERDATA; \ i_o->tt = LUA_TLIGHTUSERDATA; \
} }
#define setmpvalue(obj, mt, data) \
{ \
TValue* i_o = (obj); \
i_o->value.p = (void*)(mt); \
memcpy(&i_o->extra, data, LUAU_FATUSERDATA_WORDS * sizeof(int)); \
i_o->tt = LUA_TLIGHTUSERDATA; \
}
#define setbvalue(obj, x) \ #define setbvalue(obj, x) \
{ \ { \
TValue* i_o = (obj); \ TValue* i_o = (obj); \
@ -364,7 +374,7 @@ typedef struct Closure
typedef struct TKey typedef struct TKey
{ {
::Value value; ::Value value;
int extra; int extra[LUAU_FATUSERDATA_WORDS];
unsigned tt : 4; unsigned tt : 4;
int next : 28; /* for chaining */ int next : 28; /* for chaining */
} TKey; } TKey;
@ -381,7 +391,7 @@ typedef struct LuaNode
LuaNode* n_ = (node); \ LuaNode* n_ = (node); \
const TValue* i_o = (obj); \ const TValue* i_o = (obj); \
n_->key.value = i_o->value; \ n_->key.value = i_o->value; \
n_->key.extra = i_o->extra; \ memcpy(&n_->key.extra, &i_o->extra, LUAU_FATUSERDATA_WORDS * sizeof(int)); \
n_->key.tt = i_o->tt; \ n_->key.tt = i_o->tt; \
checkliveness(L->global, i_o); \ checkliveness(L->global, i_o); \
} }
@ -392,7 +402,7 @@ typedef struct LuaNode
TValue* i_o = (obj); \ TValue* i_o = (obj); \
const LuaNode* n_ = (node); \ const LuaNode* n_ = (node); \
i_o->value = n_->key.value; \ i_o->value = n_->key.value; \
i_o->extra = n_->key.extra; \ memcpy(&i_o->extra, &n_->key.extra, LUAU_FATUSERDATA_WORDS * sizeof(int)); \
i_o->tt = n_->key.tt; \ i_o->tt = n_->key.tt; \
checkliveness(L->global, i_o); \ checkliveness(L->global, i_o); \
} }

View file

@ -32,17 +32,17 @@ LUAU_FASTFLAGVARIABLE(LuauArrayBoundary, false)
static_assert(offsetof(LuaNode, val) == 0, "Unexpected Node memory layout, pointer cast in gval2slot is incorrect"); static_assert(offsetof(LuaNode, val) == 0, "Unexpected Node memory layout, pointer cast in gval2slot is incorrect");
// TKey is bitpacked for memory efficiency so we need to validate bit counts for worst case // TKey is bitpacked for memory efficiency so we need to validate bit counts for worst case
static_assert(TKey{{NULL}, 0, LUA_TDEADKEY, 0}.tt == LUA_TDEADKEY, "not enough bits for tt"); static_assert(TKey{{NULL}, {}, LUA_TDEADKEY, 0}.tt == LUA_TDEADKEY, "not enough bits for tt");
static_assert(TKey{{NULL}, 0, LUA_TNIL, MAXSIZE - 1}.next == MAXSIZE - 1, "not enough bits for next"); static_assert(TKey{{NULL}, {}, LUA_TNIL, MAXSIZE - 1}.next == MAXSIZE - 1, "not enough bits for next");
static_assert(TKey{{NULL}, 0, LUA_TNIL, -(MAXSIZE - 1)}.next == -(MAXSIZE - 1), "not enough bits for next"); static_assert(TKey{{NULL}, {}, LUA_TNIL, -(MAXSIZE - 1)}.next == -(MAXSIZE - 1), "not enough bits for next");
// reset cache of absent metamethods, cache is updated in luaT_gettm // reset cache of absent metamethods, cache is updated in luaT_gettm
#define invalidateTMcache(t) t->flags = 0 #define invalidateTMcache(t) t->flags = 0
// empty hash data points to dummynode so that we can always dereference it // empty hash data points to dummynode so that we can always dereference it
const LuaNode luaH_dummynode = { const LuaNode luaH_dummynode = {
{{NULL}, 0, LUA_TNIL}, /* value */ {{NULL}, {}, LUA_TNIL}, /* value */
{{NULL}, 0, LUA_TNIL, 0} /* key */ {{NULL}, {}, LUA_TNIL, 0} /* key */
}; };
#define dummynode (&luaH_dummynode) #define dummynode (&luaH_dummynode)

View file

@ -16,6 +16,7 @@ const char* const luaT_typenames[] = {
"boolean", "boolean",
"userdata",
"userdata", "userdata",
"number", "number",
"vector", "vector",
@ -108,6 +109,9 @@ const TValue* luaT_gettmbyobj(lua_State* L, const TValue* o, TMS event)
case LUA_TUSERDATA: case LUA_TUSERDATA:
mt = uvalue(o)->metatable; mt = uvalue(o)->metatable;
break; break;
case LUA_TFATUSERDATA:
mt = (Table*)(mpvalue(o));
break;
default: default:
mt = L->global->mt[ttype(o)]; mt = L->global->mt[ttype(o)];
} }