2021-10-29 21:25:12 +01:00
|
|
|
// 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 "lvm.h"
|
|
|
|
|
|
|
|
#include "lstate.h"
|
|
|
|
#include "ltable.h"
|
|
|
|
#include "lfunc.h"
|
|
|
|
#include "lstring.h"
|
|
|
|
#include "lgc.h"
|
|
|
|
#include "lmem.h"
|
|
|
|
#include "lbytecode.h"
|
2021-12-01 18:44:38 +00:00
|
|
|
#include "lapi.h"
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
|
2021-11-05 02:34:35 +00:00
|
|
|
// TODO: RAII deallocation doesn't work for longjmp builds if a memory error happens
|
2021-11-05 15:47:21 +00:00
|
|
|
template<typename T>
|
2021-11-05 02:34:35 +00:00
|
|
|
struct TempBuffer
|
|
|
|
{
|
|
|
|
lua_State* L;
|
|
|
|
T* data;
|
|
|
|
size_t count;
|
|
|
|
|
|
|
|
TempBuffer(lua_State* L, size_t count)
|
|
|
|
: L(L)
|
|
|
|
, data(luaM_newarray(L, count, T, 0))
|
|
|
|
, count(count)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2024-03-15 23:37:39 +00:00
|
|
|
TempBuffer(const TempBuffer&) = delete;
|
|
|
|
TempBuffer(TempBuffer&&) = delete;
|
|
|
|
|
|
|
|
TempBuffer& operator=(const TempBuffer&) = delete;
|
|
|
|
TempBuffer& operator=(TempBuffer&&) = delete;
|
|
|
|
|
|
|
|
~TempBuffer() noexcept
|
2021-11-05 02:34:35 +00:00
|
|
|
{
|
|
|
|
luaM_freearray(L, data, count, T, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
T& operator[](size_t index)
|
|
|
|
{
|
|
|
|
LUAU_ASSERT(index < count);
|
|
|
|
return data[index];
|
|
|
|
}
|
|
|
|
};
|
2021-10-29 21:25:12 +01:00
|
|
|
|
2024-03-15 23:37:39 +00:00
|
|
|
struct ScopedSetGCThreshold
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ScopedSetGCThreshold(global_State* global, size_t newThreshold) noexcept
|
|
|
|
: global{global}
|
|
|
|
{
|
2024-04-05 21:45:09 +01:00
|
|
|
originalThreshold = global->GCthreshold;
|
|
|
|
global->GCthreshold = newThreshold;
|
2024-03-15 23:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ScopedSetGCThreshold(const ScopedSetGCThreshold&) = delete;
|
|
|
|
ScopedSetGCThreshold(ScopedSetGCThreshold&&) = delete;
|
|
|
|
|
|
|
|
ScopedSetGCThreshold& operator=(const ScopedSetGCThreshold&) = delete;
|
|
|
|
ScopedSetGCThreshold& operator=(ScopedSetGCThreshold&&) = delete;
|
|
|
|
|
|
|
|
~ScopedSetGCThreshold() noexcept
|
|
|
|
{
|
2024-04-05 21:45:09 +01:00
|
|
|
global->GCthreshold = originalThreshold;
|
2024-03-15 23:37:39 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
global_State* global = nullptr;
|
|
|
|
size_t originalThreshold = 0;
|
|
|
|
};
|
|
|
|
|
2023-05-25 22:36:34 +01:00
|
|
|
void luaV_getimport(lua_State* L, Table* env, TValue* k, StkId res, uint32_t id, bool propagatenil)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2023-05-25 22:36:34 +01:00
|
|
|
int count = id >> 30;
|
|
|
|
LUAU_ASSERT(count > 0);
|
|
|
|
|
|
|
|
int id0 = int(id >> 20) & 1023;
|
|
|
|
int id1 = int(id >> 10) & 1023;
|
|
|
|
int id2 = int(id) & 1023;
|
|
|
|
|
|
|
|
// after the first call to luaV_gettable, res may be invalid, and env may (sometimes) be garbage collected
|
|
|
|
// we take care to not use env again and to restore res before every consecutive use
|
|
|
|
ptrdiff_t resp = savestack(L, res);
|
|
|
|
|
|
|
|
// global lookup for id0
|
|
|
|
TValue g;
|
|
|
|
sethvalue(L, &g, env);
|
|
|
|
luaV_gettable(L, &g, &k[id0], res);
|
|
|
|
|
|
|
|
// table lookup for id1
|
|
|
|
if (count < 2)
|
|
|
|
return;
|
|
|
|
|
|
|
|
res = restorestack(L, resp);
|
|
|
|
if (!propagatenil || !ttisnil(res))
|
|
|
|
luaV_gettable(L, res, &k[id1], res);
|
|
|
|
|
|
|
|
// table lookup for id2
|
|
|
|
if (count < 3)
|
|
|
|
return;
|
|
|
|
|
|
|
|
res = restorestack(L, resp);
|
|
|
|
if (!propagatenil || !ttisnil(res))
|
|
|
|
luaV_gettable(L, res, &k[id2], res);
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
template<typename T>
|
|
|
|
static T read(const char* data, size_t size, size_t& offset)
|
|
|
|
{
|
|
|
|
T result;
|
|
|
|
memcpy(&result, data + offset, sizeof(T));
|
|
|
|
offset += sizeof(T);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int readVarInt(const char* data, size_t size, size_t& offset)
|
|
|
|
{
|
|
|
|
unsigned int result = 0;
|
|
|
|
unsigned int shift = 0;
|
|
|
|
|
|
|
|
uint8_t byte;
|
|
|
|
|
|
|
|
do
|
|
|
|
{
|
|
|
|
byte = read<uint8_t>(data, size, offset);
|
|
|
|
result |= (byte & 127) << shift;
|
|
|
|
shift += 7;
|
|
|
|
} while (byte & 128);
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2021-11-05 02:34:35 +00:00
|
|
|
static TString* readString(TempBuffer<TString*>& strings, const char* data, size_t size, size_t& offset)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
|
|
|
unsigned int id = readVarInt(data, size, offset);
|
|
|
|
|
|
|
|
return id == 0 ? NULL : strings[id - 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
static void resolveImportSafe(lua_State* L, Table* env, TValue* k, uint32_t id)
|
|
|
|
{
|
|
|
|
struct ResolveImport
|
|
|
|
{
|
|
|
|
TValue* k;
|
|
|
|
uint32_t id;
|
|
|
|
|
|
|
|
static void run(lua_State* L, void* ud)
|
|
|
|
{
|
|
|
|
ResolveImport* self = static_cast<ResolveImport*>(ud);
|
|
|
|
|
|
|
|
// note: we call getimport with nil propagation which means that accesses to table chains like A.B.C will resolve in nil
|
|
|
|
// this is technically not necessary but it reduces the number of exceptions when loading scripts that rely on getfenv/setfenv for global
|
|
|
|
// injection
|
2023-06-16 18:35:18 +01:00
|
|
|
// allocate a stack slot so that we can do table lookups
|
|
|
|
luaD_checkstack(L, 1);
|
|
|
|
setnilvalue(L->top);
|
|
|
|
L->top++;
|
2023-05-25 22:36:34 +01:00
|
|
|
|
2023-06-16 18:35:18 +01:00
|
|
|
luaV_getimport(L, L->gt, self->k, L->top - 1, self->id, /* propagatenil= */ true);
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ResolveImport ri = {k, id};
|
2022-02-18 01:18:01 +00:00
|
|
|
if (L->gt->safeenv)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
|
|
|
// luaD_pcall will make sure that if any C/Lua calls during import resolution fail, the thread state is restored back
|
|
|
|
int oldTop = lua_gettop(L);
|
|
|
|
int status = luaD_pcall(L, &ResolveImport::run, &ri, savestack(L, L->top), 0);
|
|
|
|
LUAU_ASSERT(oldTop + 1 == lua_gettop(L)); // if an error occurred, luaD_pcall saves it on stack
|
|
|
|
|
|
|
|
if (status != 0)
|
|
|
|
{
|
|
|
|
// replace error object with nil
|
|
|
|
setnilvalue(L->top - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
setnilvalue(L->top);
|
|
|
|
L->top++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-05-31 20:18:18 +01:00
|
|
|
static void remapUserdataTypes(char* data, size_t size, uint8_t* userdataRemapping, uint32_t count)
|
|
|
|
{
|
|
|
|
size_t offset = 0;
|
|
|
|
|
|
|
|
uint32_t typeSize = readVarInt(data, size, offset);
|
|
|
|
uint32_t upvalCount = readVarInt(data, size, offset);
|
|
|
|
uint32_t localCount = readVarInt(data, size, offset);
|
|
|
|
|
|
|
|
if (typeSize != 0)
|
|
|
|
{
|
|
|
|
uint8_t* types = (uint8_t*)data + offset;
|
|
|
|
|
|
|
|
// Skip two bytes of function type introduction
|
|
|
|
for (uint32_t i = 2; i < typeSize; i++)
|
|
|
|
{
|
|
|
|
uint32_t index = uint32_t(types[i] - LBC_TYPE_TAGGED_USERDATA_BASE);
|
|
|
|
|
|
|
|
if (index < count)
|
|
|
|
types[i] = userdataRemapping[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += typeSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (upvalCount != 0)
|
|
|
|
{
|
|
|
|
uint8_t* types = (uint8_t*)data + offset;
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < upvalCount; i++)
|
|
|
|
{
|
|
|
|
uint32_t index = uint32_t(types[i] - LBC_TYPE_TAGGED_USERDATA_BASE);
|
|
|
|
|
|
|
|
if (index < count)
|
|
|
|
types[i] = userdataRemapping[index];
|
|
|
|
}
|
|
|
|
|
|
|
|
offset += upvalCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (localCount != 0)
|
|
|
|
{
|
|
|
|
for (uint32_t i = 0; i < localCount; i++)
|
|
|
|
{
|
|
|
|
uint32_t index = uint32_t(data[offset] - LBC_TYPE_TAGGED_USERDATA_BASE);
|
|
|
|
|
|
|
|
if (index < count)
|
|
|
|
data[offset] = userdataRemapping[index];
|
|
|
|
|
|
|
|
offset += 2;
|
|
|
|
readVarInt(data, size, offset);
|
|
|
|
readVarInt(data, size, offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
LUAU_ASSERT(offset == size);
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size, int env)
|
|
|
|
{
|
|
|
|
size_t offset = 0;
|
|
|
|
|
|
|
|
uint8_t version = read<uint8_t>(data, size, offset);
|
|
|
|
|
2023-06-16 18:35:18 +01:00
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
// 0 means the rest of the bytecode is the error message
|
2022-01-07 01:46:53 +00:00
|
|
|
if (version == 0)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2022-10-07 01:23:29 +01:00
|
|
|
char chunkbuf[LUA_IDSIZE];
|
|
|
|
const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), chunkname, strlen(chunkname));
|
2022-01-07 01:46:53 +00:00
|
|
|
lua_pushfstring(L, "%s%.*s", chunkid, int(size - offset), data + offset);
|
|
|
|
return 1;
|
|
|
|
}
|
2021-10-29 21:25:12 +01:00
|
|
|
|
2022-06-24 02:56:00 +01:00
|
|
|
if (version < LBC_VERSION_MIN || version > LBC_VERSION_MAX)
|
2022-01-07 01:46:53 +00:00
|
|
|
{
|
2022-10-07 01:23:29 +01:00
|
|
|
char chunkbuf[LUA_IDSIZE];
|
|
|
|
const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), chunkname, strlen(chunkname));
|
2022-06-24 02:56:00 +01:00
|
|
|
lua_pushfstring(L, "%s: bytecode version mismatch (expected [%d..%d], got %d)", chunkid, LBC_VERSION_MIN, LBC_VERSION_MAX, version);
|
2021-10-29 21:25:12 +01:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-07-07 21:10:48 +01:00
|
|
|
// we will allocate a fair amount of memory so check GC before we do
|
2023-08-11 15:42:37 +01:00
|
|
|
luaC_checkGC(L);
|
2023-07-07 21:10:48 +01:00
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
// pause GC for the duration of deserialization - some objects we're creating aren't rooted
|
2024-03-15 23:37:39 +00:00
|
|
|
const ScopedSetGCThreshold pauseGC{L->global, SIZE_MAX};
|
2021-10-29 21:25:12 +01:00
|
|
|
|
2021-12-01 18:44:38 +00:00
|
|
|
// env is 0 for current environment and a stack index otherwise
|
2022-02-18 01:18:01 +00:00
|
|
|
Table* envt = (env == 0) ? L->gt : hvalue(luaA_toobject(L, env));
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
TString* source = luaS_new(L, chunkname);
|
|
|
|
|
2023-07-07 21:10:48 +01:00
|
|
|
uint8_t typesversion = 0;
|
2023-06-16 18:35:18 +01:00
|
|
|
|
|
|
|
if (version >= 4)
|
|
|
|
{
|
2023-07-07 21:10:48 +01:00
|
|
|
typesversion = read<uint8_t>(data, size, offset);
|
2024-05-31 20:18:18 +01:00
|
|
|
|
2024-07-08 22:57:06 +01:00
|
|
|
if (typesversion < LBC_TYPE_VERSION_MIN || typesversion > LBC_TYPE_VERSION_MAX)
|
2024-05-31 20:18:18 +01:00
|
|
|
{
|
2024-07-08 22:57:06 +01:00
|
|
|
char chunkbuf[LUA_IDSIZE];
|
|
|
|
const char* chunkid = luaO_chunkid(chunkbuf, sizeof(chunkbuf), chunkname, strlen(chunkname));
|
2024-08-02 15:30:04 +01:00
|
|
|
lua_pushfstring(
|
|
|
|
L, "%s: bytecode type version mismatch (expected [%d..%d], got %d)", chunkid, LBC_TYPE_VERSION_MIN, LBC_TYPE_VERSION_MAX, typesversion
|
|
|
|
);
|
2024-07-08 22:57:06 +01:00
|
|
|
return 1;
|
2024-05-31 20:18:18 +01:00
|
|
|
}
|
2023-06-16 18:35:18 +01:00
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
// string table
|
|
|
|
unsigned int stringCount = readVarInt(data, size, offset);
|
2021-11-05 02:34:35 +00:00
|
|
|
TempBuffer<TString*> strings(L, stringCount);
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
for (unsigned int i = 0; i < stringCount; ++i)
|
|
|
|
{
|
|
|
|
unsigned int length = readVarInt(data, size, offset);
|
|
|
|
|
|
|
|
strings[i] = luaS_newlstr(L, data + offset, length);
|
|
|
|
offset += length;
|
|
|
|
}
|
|
|
|
|
2024-05-31 20:18:18 +01:00
|
|
|
// userdata type remapping table
|
|
|
|
// for unknown userdata types, the entry will remap to common 'userdata' type
|
|
|
|
const uint32_t userdataTypeLimit = LBC_TYPE_TAGGED_USERDATA_END - LBC_TYPE_TAGGED_USERDATA_BASE;
|
|
|
|
uint8_t userdataRemapping[userdataTypeLimit];
|
|
|
|
|
2024-07-08 22:57:06 +01:00
|
|
|
if (typesversion == 3)
|
2024-05-31 20:18:18 +01:00
|
|
|
{
|
|
|
|
memset(userdataRemapping, LBC_TYPE_USERDATA, userdataTypeLimit);
|
|
|
|
|
|
|
|
uint8_t index = read<uint8_t>(data, size, offset);
|
|
|
|
|
|
|
|
while (index != 0)
|
|
|
|
{
|
|
|
|
TString* name = readString(strings, data, size, offset);
|
|
|
|
|
|
|
|
if (uint32_t(index - 1) < userdataTypeLimit)
|
|
|
|
{
|
|
|
|
if (auto cb = L->global->ecb.gettypemapping)
|
|
|
|
userdataRemapping[index - 1] = cb(L, getstr(name), name->len);
|
|
|
|
}
|
|
|
|
|
|
|
|
index = read<uint8_t>(data, size, offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
// proto table
|
|
|
|
unsigned int protoCount = readVarInt(data, size, offset);
|
2021-11-05 02:34:35 +00:00
|
|
|
TempBuffer<Proto*> protos(L, protoCount);
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
for (unsigned int i = 0; i < protoCount; ++i)
|
|
|
|
{
|
|
|
|
Proto* p = luaF_newproto(L);
|
|
|
|
p->source = source;
|
2022-10-14 20:48:41 +01:00
|
|
|
p->bytecodeid = int(i);
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
p->maxstacksize = read<uint8_t>(data, size, offset);
|
|
|
|
p->numparams = read<uint8_t>(data, size, offset);
|
|
|
|
p->nups = read<uint8_t>(data, size, offset);
|
|
|
|
p->is_vararg = read<uint8_t>(data, size, offset);
|
|
|
|
|
2023-06-16 18:35:18 +01:00
|
|
|
if (version >= 4)
|
|
|
|
{
|
2023-07-14 19:08:53 +01:00
|
|
|
p->flags = read<uint8_t>(data, size, offset);
|
2023-06-16 18:35:18 +01:00
|
|
|
|
2024-06-21 00:37:55 +01:00
|
|
|
if (typesversion == 1)
|
2023-06-16 18:35:18 +01:00
|
|
|
{
|
2024-06-21 00:37:55 +01:00
|
|
|
uint32_t typesize = readVarInt(data, size, offset);
|
2024-04-25 23:26:09 +01:00
|
|
|
|
2024-06-21 00:37:55 +01:00
|
|
|
if (typesize)
|
2024-04-25 23:26:09 +01:00
|
|
|
{
|
2024-06-21 00:37:55 +01:00
|
|
|
uint8_t* types = (uint8_t*)data + offset;
|
2023-06-16 18:35:18 +01:00
|
|
|
|
2024-06-21 00:37:55 +01:00
|
|
|
LUAU_ASSERT(typesize == unsigned(2 + p->numparams));
|
|
|
|
LUAU_ASSERT(types[0] == LBC_TYPE_FUNCTION);
|
|
|
|
LUAU_ASSERT(types[1] == p->numparams);
|
2023-06-16 18:35:18 +01:00
|
|
|
|
2024-06-21 00:37:55 +01:00
|
|
|
// transform v1 into v2 format
|
|
|
|
int headersize = typesize > 127 ? 4 : 3;
|
2024-05-31 20:18:18 +01:00
|
|
|
|
2024-06-21 00:37:55 +01:00
|
|
|
p->typeinfo = luaM_newarray(L, headersize + typesize, uint8_t, p->memcat);
|
|
|
|
p->sizetypeinfo = headersize + typesize;
|
|
|
|
|
|
|
|
if (headersize == 4)
|
|
|
|
{
|
|
|
|
p->typeinfo[0] = (typesize & 127) | (1 << 7);
|
|
|
|
p->typeinfo[1] = typesize >> 7;
|
|
|
|
p->typeinfo[2] = 0;
|
|
|
|
p->typeinfo[3] = 0;
|
2024-04-25 23:26:09 +01:00
|
|
|
}
|
2024-06-21 00:37:55 +01:00
|
|
|
else
|
|
|
|
{
|
|
|
|
p->typeinfo[0] = uint8_t(typesize);
|
|
|
|
p->typeinfo[1] = 0;
|
|
|
|
p->typeinfo[2] = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(p->typeinfo + headersize, types, typesize);
|
2024-04-25 23:26:09 +01:00
|
|
|
}
|
2024-06-21 00:37:55 +01:00
|
|
|
|
|
|
|
offset += typesize;
|
2023-06-16 18:35:18 +01:00
|
|
|
}
|
2024-07-08 22:57:06 +01:00
|
|
|
else if (typesversion == 2 || typesversion == 3)
|
2024-04-25 23:26:09 +01:00
|
|
|
{
|
|
|
|
uint32_t typesize = readVarInt(data, size, offset);
|
2023-07-07 21:10:48 +01:00
|
|
|
|
2024-06-21 00:37:55 +01:00
|
|
|
if (typesize)
|
2024-04-25 23:26:09 +01:00
|
|
|
{
|
|
|
|
uint8_t* types = (uint8_t*)data + offset;
|
|
|
|
|
|
|
|
p->typeinfo = luaM_newarray(L, typesize, uint8_t, p->memcat);
|
2024-06-21 00:37:55 +01:00
|
|
|
p->sizetypeinfo = typesize;
|
2024-04-25 23:26:09 +01:00
|
|
|
memcpy(p->typeinfo, types, typesize);
|
2024-06-21 00:37:55 +01:00
|
|
|
offset += typesize;
|
2024-04-25 23:26:09 +01:00
|
|
|
|
2024-07-08 22:57:06 +01:00
|
|
|
if (typesversion == 3)
|
2024-06-21 00:37:55 +01:00
|
|
|
{
|
|
|
|
remapUserdataTypes((char*)(uint8_t*)p->typeinfo, p->sizetypeinfo, userdataRemapping, userdataTypeLimit);
|
|
|
|
}
|
|
|
|
}
|
2024-04-25 23:26:09 +01:00
|
|
|
}
|
2023-06-16 18:35:18 +01:00
|
|
|
}
|
|
|
|
|
2024-04-05 21:45:09 +01:00
|
|
|
const int sizecode = readVarInt(data, size, offset);
|
|
|
|
p->code = luaM_newarray(L, sizecode, Instruction, p->memcat);
|
|
|
|
p->sizecode = sizecode;
|
2024-03-15 23:37:39 +00:00
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
for (int j = 0; j < p->sizecode; ++j)
|
|
|
|
p->code[j] = read<uint32_t>(data, size, offset);
|
|
|
|
|
2023-05-25 22:36:34 +01:00
|
|
|
p->codeentry = p->code;
|
|
|
|
|
2024-04-05 21:45:09 +01:00
|
|
|
const int sizek = readVarInt(data, size, offset);
|
|
|
|
p->k = luaM_newarray(L, sizek, TValue, p->memcat);
|
|
|
|
p->sizek = sizek;
|
2021-10-29 21:25:12 +01:00
|
|
|
|
2024-04-05 21:45:09 +01:00
|
|
|
// Initialize the constants to nil to ensure they have a valid state
|
|
|
|
// in the event that some operation in the following loop fails with
|
|
|
|
// an exception.
|
|
|
|
for (int j = 0; j < p->sizek; ++j)
|
2021-10-29 21:25:12 +01:00
|
|
|
{
|
2024-04-05 21:45:09 +01:00
|
|
|
setnilvalue(&p->k[j]);
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int j = 0; j < p->sizek; ++j)
|
|
|
|
{
|
|
|
|
switch (read<uint8_t>(data, size, offset))
|
|
|
|
{
|
|
|
|
case LBC_CONSTANT_NIL:
|
2024-04-05 21:45:09 +01:00
|
|
|
// All constants have already been pre-initialized to nil
|
2021-10-29 21:25:12 +01:00
|
|
|
break;
|
|
|
|
|
|
|
|
case LBC_CONSTANT_BOOLEAN:
|
|
|
|
{
|
|
|
|
uint8_t v = read<uint8_t>(data, size, offset);
|
|
|
|
setbvalue(&p->k[j], v);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case LBC_CONSTANT_NUMBER:
|
|
|
|
{
|
|
|
|
double v = read<double>(data, size, offset);
|
|
|
|
setnvalue(&p->k[j], v);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
Optimize vector literals by storing them in the constant table (#1096)
With this optimization, built-in vector constructor calls with 3/4 arguments are detected by the compiler and turned into vector constants when the arguments are constant numbers.
Requires optimization level 2 because built-ins are not folded otherwise by the compiler.
Bytecode version is bumped because of the new constant type, but old bytecode versions can still be loaded.
The following synthetic benchmark shows ~6.6x improvement.
```
local v
for i = 1, 10000000 do
v = vector(1, 2, 3)
v = vector(1, 2, 3)
v = vector(1, 2, 3)
v = vector(1, 2, 3)
v = vector(1, 2, 3)
v = vector(1, 2, 3)
v = vector(1, 2, 3)
v = vector(1, 2, 3)
v = vector(1, 2, 3)
v = vector(1, 2, 3)
end
```
Also tried a more real world scenario and could see a few percent improvement.
Added a new fast flag LuauVectorLiterals for enabling the feature.
---------
Co-authored-by: Petri Häkkinen <petrih@rmd.remedy.fi>
Co-authored-by: vegorov-rbx <75688451+vegorov-rbx@users.noreply.github.com>
Co-authored-by: Arseny Kapoulkine <arseny.kapoulkine@gmail.com>
2023-11-17 12:54:32 +00:00
|
|
|
case LBC_CONSTANT_VECTOR:
|
|
|
|
{
|
|
|
|
float x = read<float>(data, size, offset);
|
|
|
|
float y = read<float>(data, size, offset);
|
|
|
|
float z = read<float>(data, size, offset);
|
|
|
|
float w = read<float>(data, size, offset);
|
|
|
|
(void)w;
|
|
|
|
setvvalue(&p->k[j], x, y, z, w);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
case LBC_CONSTANT_STRING:
|
|
|
|
{
|
|
|
|
TString* v = readString(strings, data, size, offset);
|
2022-09-29 23:23:10 +01:00
|
|
|
setsvalue(L, &p->k[j], v);
|
2021-10-29 21:25:12 +01:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case LBC_CONSTANT_IMPORT:
|
|
|
|
{
|
|
|
|
uint32_t iid = read<uint32_t>(data, size, offset);
|
|
|
|
resolveImportSafe(L, envt, p->k, iid);
|
|
|
|
setobj(L, &p->k[j], L->top - 1);
|
|
|
|
L->top--;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case LBC_CONSTANT_TABLE:
|
|
|
|
{
|
|
|
|
int keys = readVarInt(data, size, offset);
|
|
|
|
Table* h = luaH_new(L, 0, keys);
|
|
|
|
for (int i = 0; i < keys; ++i)
|
|
|
|
{
|
|
|
|
int key = readVarInt(data, size, offset);
|
|
|
|
TValue* val = luaH_set(L, h, &p->k[key]);
|
|
|
|
setnvalue(val, 0.0);
|
|
|
|
}
|
|
|
|
sethvalue(L, &p->k[j], h);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case LBC_CONSTANT_CLOSURE:
|
|
|
|
{
|
|
|
|
uint32_t fid = readVarInt(data, size, offset);
|
|
|
|
Closure* cl = luaF_newLclosure(L, protos[fid]->nups, envt, protos[fid]);
|
|
|
|
cl->preload = (cl->nupvalues > 0);
|
|
|
|
setclvalue(L, &p->k[j], cl);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
default:
|
|
|
|
LUAU_ASSERT(!"Unexpected constant kind");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-04-05 21:45:09 +01:00
|
|
|
const int sizep = readVarInt(data, size, offset);
|
|
|
|
p->p = luaM_newarray(L, sizep, Proto*, p->memcat);
|
|
|
|
p->sizep = sizep;
|
2024-03-15 23:37:39 +00:00
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
for (int j = 0; j < p->sizep; ++j)
|
|
|
|
{
|
|
|
|
uint32_t fid = readVarInt(data, size, offset);
|
|
|
|
p->p[j] = protos[fid];
|
|
|
|
}
|
|
|
|
|
2022-03-31 22:01:51 +01:00
|
|
|
p->linedefined = readVarInt(data, size, offset);
|
2021-10-29 21:25:12 +01:00
|
|
|
p->debugname = readString(strings, data, size, offset);
|
|
|
|
|
|
|
|
uint8_t lineinfo = read<uint8_t>(data, size, offset);
|
|
|
|
|
|
|
|
if (lineinfo)
|
|
|
|
{
|
|
|
|
p->linegaplog2 = read<uint8_t>(data, size, offset);
|
|
|
|
|
|
|
|
int intervals = ((p->sizecode - 1) >> p->linegaplog2) + 1;
|
|
|
|
int absoffset = (p->sizecode + 3) & ~3;
|
|
|
|
|
2024-04-05 21:45:09 +01:00
|
|
|
const int sizelineinfo = absoffset + intervals * sizeof(int);
|
|
|
|
p->lineinfo = luaM_newarray(L, sizelineinfo, uint8_t, p->memcat);
|
|
|
|
p->sizelineinfo = sizelineinfo;
|
2024-03-15 23:37:39 +00:00
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
p->abslineinfo = (int*)(p->lineinfo + absoffset);
|
|
|
|
|
|
|
|
uint8_t lastoffset = 0;
|
|
|
|
for (int j = 0; j < p->sizecode; ++j)
|
|
|
|
{
|
|
|
|
lastoffset += read<uint8_t>(data, size, offset);
|
|
|
|
p->lineinfo[j] = lastoffset;
|
|
|
|
}
|
|
|
|
|
2022-01-07 01:46:53 +00:00
|
|
|
int lastline = 0;
|
2021-10-29 21:25:12 +01:00
|
|
|
for (int j = 0; j < intervals; ++j)
|
|
|
|
{
|
2022-01-07 01:46:53 +00:00
|
|
|
lastline += read<int32_t>(data, size, offset);
|
|
|
|
p->abslineinfo[j] = lastline;
|
2021-10-29 21:25:12 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
uint8_t debuginfo = read<uint8_t>(data, size, offset);
|
|
|
|
|
|
|
|
if (debuginfo)
|
|
|
|
{
|
2024-04-05 21:45:09 +01:00
|
|
|
const int sizelocvars = readVarInt(data, size, offset);
|
|
|
|
p->locvars = luaM_newarray(L, sizelocvars, LocVar, p->memcat);
|
|
|
|
p->sizelocvars = sizelocvars;
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
for (int j = 0; j < p->sizelocvars; ++j)
|
|
|
|
{
|
|
|
|
p->locvars[j].varname = readString(strings, data, size, offset);
|
|
|
|
p->locvars[j].startpc = readVarInt(data, size, offset);
|
|
|
|
p->locvars[j].endpc = readVarInt(data, size, offset);
|
|
|
|
p->locvars[j].reg = read<uint8_t>(data, size, offset);
|
|
|
|
}
|
|
|
|
|
2024-04-05 21:45:09 +01:00
|
|
|
const int sizeupvalues = readVarInt(data, size, offset);
|
2024-04-25 23:26:09 +01:00
|
|
|
LUAU_ASSERT(sizeupvalues == p->nups);
|
|
|
|
|
2024-04-05 21:45:09 +01:00
|
|
|
p->upvalues = luaM_newarray(L, sizeupvalues, TString*, p->memcat);
|
|
|
|
p->sizeupvalues = sizeupvalues;
|
2021-10-29 21:25:12 +01:00
|
|
|
|
|
|
|
for (int j = 0; j < p->sizeupvalues; ++j)
|
|
|
|
{
|
|
|
|
p->upvalues[j] = readString(strings, data, size, offset);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protos[i] = p;
|
|
|
|
}
|
|
|
|
|
|
|
|
// "main" proto is pushed to Lua stack
|
|
|
|
uint32_t mainid = readVarInt(data, size, offset);
|
|
|
|
Proto* main = protos[mainid];
|
|
|
|
|
2022-08-25 22:53:50 +01:00
|
|
|
luaC_threadbarrier(L);
|
2021-11-05 15:47:21 +00:00
|
|
|
|
2021-10-29 21:25:12 +01:00
|
|
|
Closure* cl = luaF_newLclosure(L, 0, envt, main);
|
|
|
|
setclvalue(L, L->top, cl);
|
|
|
|
incr_top(L);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|