luau/VM/src/ltm.cpp

159 lines
3.6 KiB
C++
Raw Normal View History

// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
#include "ltm.h"
#include "lstate.h"
#include "lstring.h"
2022-03-18 00:46:04 +00:00
#include "ludata.h"
#include "ltable.h"
#include "lgc.h"
#include <string.h>
// clang-format off
const char* const luaT_typenames[] = {
2022-08-04 23:35:33 +01:00
// ORDER TYPE
"nil",
"boolean",
"userdata",
"number",
"vector",
"string",
"table",
"function",
"userdata",
"thread",
Sync to upstream/release/600 (#1076) ### What's Changed - Improve readability of unions and intersections by limiting the number of elements of those types that can be presented on a single line (gated under `FFlag::LuauToStringSimpleCompositeTypesSingleLine`) - Adds a new option to the compiler `--record-stats` to record and output compilation statistics - `if...then...else` expressions are now optimized into `AND/OR` form when possible. ### VM - Add a new `buffer` type to Luau based on the [buffer RFC](https://github.com/Roblox/luau/pull/739) and additional C API functions to work with it; this release does not include the library. - Internal C API to work with string buffers has been updated to align with Lua version more closely ### Native Codegen - Added support for new X64 instruction (rev) and new A64 instruction (bswap) in the assembler - Simplified the way numerical loop condition is translated to IR ### New Type Solver - Operator inference now handled by type families - Created a new system called `Type Paths` to explain why subtyping tests fail in order to improve the quality of error messages. - Systematic changes to implement Data Flow analysis in the new solver (`Breadcrumb` removed and replaced with `RefinementKey`) --- Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Vighnesh Vijay <vvijay@roblox.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> --------- Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com> Co-authored-by: Vyacheslav Egorov <vegorov@roblox.com> Co-authored-by: Andy Friesen <afriesen@roblox.com> Co-authored-by: Lily Brown <lbrown@roblox.com> Co-authored-by: Aaron Weiss <aaronweiss@roblox.com> Co-authored-by: Alexander McCord <amccord@roblox.com> Co-authored-by: Aviral Goel <agoel@roblox.com>
2023-10-21 02:10:30 +01:00
"buffer",
};
const char* const luaT_eventname[] = {
2022-08-04 23:35:33 +01:00
// ORDER TM
"__index",
"__newindex",
"__mode",
"__namecall",
2022-05-06 01:03:43 +01:00
"__call",
"__iter",
2022-07-01 00:52:43 +01:00
"__len",
"__eq",
"__add",
"__sub",
"__mul",
"__div",
"__idiv",
"__mod",
"__pow",
"__unm",
"__lt",
"__le",
"__concat",
"__type",
"__metatable",
};
// clang-format on
static_assert(sizeof(luaT_typenames) / sizeof(luaT_typenames[0]) == LUA_T_COUNT, "luaT_typenames size mismatch");
static_assert(sizeof(luaT_eventname) / sizeof(luaT_eventname[0]) == TM_N, "luaT_eventname size mismatch");
2022-05-06 01:03:43 +01:00
static_assert(TM_EQ < 8, "fasttm optimization stores a bitfield with metamethods in a byte");
void luaT_init(lua_State* L)
{
int i;
for (i = 0; i < LUA_T_COUNT; i++)
{
L->global->ttname[i] = luaS_new(L, luaT_typenames[i]);
2022-08-04 23:35:33 +01:00
luaS_fix(L->global->ttname[i]); // never collect these names
}
for (i = 0; i < TM_N; i++)
{
L->global->tmname[i] = luaS_new(L, luaT_eventname[i]);
2022-08-04 23:35:33 +01:00
luaS_fix(L->global->tmname[i]); // never collect these names
}
}
/*
** function to be used with macro "fasttm": optimized for absence of
** tag methods.
*/
const TValue* luaT_gettm(Table* events, TMS event, TString* ename)
{
const TValue* tm = luaH_getstr(events, ename);
LUAU_ASSERT(event <= TM_EQ);
if (ttisnil(tm))
2022-08-04 23:35:33 +01:00
{ // no tag method?
events->tmcache |= cast_byte(1u << event); // cache this fact
return NULL;
}
else
return tm;
}
const TValue* luaT_gettmbyobj(lua_State* L, const TValue* o, TMS event)
{
/*
NB: Tag-methods were replaced by meta-methods in Lua 5.0, but the
old names are still around (this function, for example).
*/
Table* mt;
switch (ttype(o))
{
case LUA_TTABLE:
mt = hvalue(o)->metatable;
break;
case LUA_TUSERDATA:
mt = uvalue(o)->metatable;
break;
default:
mt = L->global->mt[ttype(o)];
}
return (mt ? luaH_getstr(mt, L->global->tmname[event]) : luaO_nilobject);
}
const TString* luaT_objtypenamestr(lua_State* L, const TValue* o)
{
2022-03-18 00:46:04 +00:00
if (ttisuserdata(o) && uvalue(o)->tag != UTAG_PROXY && uvalue(o)->metatable)
{
const TValue* type = luaH_getstr(uvalue(o)->metatable, L->global->tmname[TM_TYPE]);
if (ttisstring(type))
return tsvalue(type);
}
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>
2023-12-14 23:05:51 +00:00
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)])
{
const TValue* type = luaH_getstr(mt, L->global->tmname[TM_TYPE]);
if (ttisstring(type))
return tsvalue(type);
}
return L->global->ttname[ttype(o)];
}
const char* luaT_objtypename(lua_State* L, const TValue* o)
{
return getstr(luaT_objtypenamestr(L, o));
}