v0.3.4+luau533
This commit is contained in:
parent
0691157f4c
commit
3320c56ff5
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "luau0-src"
|
name = "luau0-src"
|
||||||
version = "0.3.3+luau531"
|
version = "0.3.4+luau533"
|
||||||
authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"]
|
authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
repository = "https://github.com/khvzak/luau-src-rs"
|
repository = "https://github.com/khvzak/luau-src-rs"
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
// Creating the bytecode is outside the scope of this file and is handled by bytecode builder (BytecodeBuilder.h) and bytecode compiler (Compiler.h)
|
// Creating the bytecode is outside the scope of this file and is handled by bytecode builder (BytecodeBuilder.h) and bytecode compiler (Compiler.h)
|
||||||
// Note that ALL enums declared in this file are order-sensitive since the values are baked into bytecode that needs to be processed by legacy clients.
|
// Note that ALL enums declared in this file are order-sensitive since the values are baked into bytecode that needs to be processed by legacy clients.
|
||||||
|
|
||||||
// Bytecode definitions
|
// # Bytecode definitions
|
||||||
// Bytecode instructions are using "word code" - each instruction is one or many 32-bit words.
|
// Bytecode instructions are using "word code" - each instruction is one or many 32-bit words.
|
||||||
// The first word in the instruction is always the instruction header, and *must* contain the opcode (enum below) in the least significant byte.
|
// The first word in the instruction is always the instruction header, and *must* contain the opcode (enum below) in the least significant byte.
|
||||||
//
|
//
|
||||||
|
@ -19,7 +19,7 @@
|
||||||
// Instruction word is sometimes followed by one extra word, indicated as AUX - this is just a 32-bit word and is decoded according to the specification for each opcode.
|
// Instruction word is sometimes followed by one extra word, indicated as AUX - this is just a 32-bit word and is decoded according to the specification for each opcode.
|
||||||
// For each opcode the encoding is *static* - that is, based on the opcode you know a-priory how large the instruction is, with the exception of NEWCLOSURE
|
// For each opcode the encoding is *static* - that is, based on the opcode you know a-priory how large the instruction is, with the exception of NEWCLOSURE
|
||||||
|
|
||||||
// Bytecode indices
|
// # Bytecode indices
|
||||||
// Bytecode instructions commonly refer to integer values that define offsets or indices for various entities. For each type, there's a maximum encodable value.
|
// Bytecode instructions commonly refer to integer values that define offsets or indices for various entities. For each type, there's a maximum encodable value.
|
||||||
// Note that in some cases, the compiler will set a lower limit than the maximum encodable value is to prevent fragile code into bumping against the limits whenever we change the compilation details.
|
// Note that in some cases, the compiler will set a lower limit than the maximum encodable value is to prevent fragile code into bumping against the limits whenever we change the compilation details.
|
||||||
// Additionally, in some specific instructions such as ANDK, the limit on the encoded value is smaller; this means that if a value is larger, a different instruction must be selected.
|
// Additionally, in some specific instructions such as ANDK, the limit on the encoded value is smaller; this means that if a value is larger, a different instruction must be selected.
|
||||||
|
@ -29,6 +29,15 @@
|
||||||
// Constants: 0-2^23-1. Constants are stored in a table allocated with each proto; to allow for future bytecode tweaks the encodable value is limited to 23 bits.
|
// Constants: 0-2^23-1. Constants are stored in a table allocated with each proto; to allow for future bytecode tweaks the encodable value is limited to 23 bits.
|
||||||
// Closures: 0-2^15-1. Closures are created from child protos via a child index; the limit is for the number of closures immediately referenced in each function.
|
// Closures: 0-2^15-1. Closures are created from child protos via a child index; the limit is for the number of closures immediately referenced in each function.
|
||||||
// Jumps: -2^23..2^23. Jump offsets are specified in word increments, so jumping over an instruction may sometimes require an offset of 2 or more.
|
// Jumps: -2^23..2^23. Jump offsets are specified in word increments, so jumping over an instruction may sometimes require an offset of 2 or more.
|
||||||
|
|
||||||
|
// # Bytecode versions
|
||||||
|
// Bytecode serialized format embeds a version number, that dictates both the serialized form as well as the allowed instructions. As long as the bytecode version falls into supported
|
||||||
|
// range (indicated by LBC_BYTECODE_MIN / LBC_BYTECODE_MAX) and was produced by Luau compiler, it should load and execute correctly.
|
||||||
|
//
|
||||||
|
// Note that Luau runtime doesn't provide indefinite bytecode compatibility: support for older versions gets removed over time. As such, bytecode isn't a durable storage format and it's expected
|
||||||
|
// that Luau users can recompile bytecode from source on Luau version upgrades if necessary.
|
||||||
|
|
||||||
|
// Bytecode opcode, part of the instruction header
|
||||||
enum LuauOpcode
|
enum LuauOpcode
|
||||||
{
|
{
|
||||||
// NOP: noop
|
// NOP: noop
|
||||||
|
@ -380,8 +389,10 @@ enum LuauOpcode
|
||||||
// Bytecode tags, used internally for bytecode encoded as a string
|
// Bytecode tags, used internally for bytecode encoded as a string
|
||||||
enum LuauBytecodeTag
|
enum LuauBytecodeTag
|
||||||
{
|
{
|
||||||
// Bytecode version
|
// Bytecode version; runtime supports [MIN, MAX], compiler emits TARGET by default but may emit a higher version when flags are enabled
|
||||||
LBC_VERSION = 2,
|
LBC_VERSION_MIN = 2,
|
||||||
|
LBC_VERSION_MAX = 2,
|
||||||
|
LBC_VERSION_TARGET = 2,
|
||||||
// Types of constant table entries
|
// Types of constant table entries
|
||||||
LBC_CONSTANT_NIL = 0,
|
LBC_CONSTANT_NIL = 0,
|
||||||
LBC_CONSTANT_BOOLEAN,
|
LBC_CONSTANT_BOOLEAN,
|
||||||
|
|
|
@ -119,6 +119,8 @@ public:
|
||||||
|
|
||||||
static std::string getError(const std::string& message);
|
static std::string getError(const std::string& message);
|
||||||
|
|
||||||
|
static uint8_t getVersion();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Constant
|
struct Constant
|
||||||
{
|
{
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static_assert(LBC_VERSION_TARGET >= LBC_VERSION_MIN && LBC_VERSION_TARGET <= LBC_VERSION_MAX, "Invalid bytecode version setup");
|
||||||
|
static_assert(LBC_VERSION_MAX <= 127, "Bytecode version should be 7-bit so that we can extend the serialization to use varint transparently");
|
||||||
|
|
||||||
static const uint32_t kMaxConstantCount = 1 << 23;
|
static const uint32_t kMaxConstantCount = 1 << 23;
|
||||||
static const uint32_t kMaxClosureCount = 1 << 15;
|
static const uint32_t kMaxClosureCount = 1 << 15;
|
||||||
|
|
||||||
|
@ -572,7 +575,10 @@ void BytecodeBuilder::finalize()
|
||||||
bytecode.reserve(capacity);
|
bytecode.reserve(capacity);
|
||||||
|
|
||||||
// assemble final bytecode blob
|
// assemble final bytecode blob
|
||||||
bytecode = char(LBC_VERSION);
|
uint8_t version = getVersion();
|
||||||
|
LUAU_ASSERT(version >= LBC_VERSION_MIN && version <= LBC_VERSION_MAX);
|
||||||
|
|
||||||
|
bytecode = char(version);
|
||||||
|
|
||||||
writeStringTable(bytecode);
|
writeStringTable(bytecode);
|
||||||
|
|
||||||
|
@ -1040,7 +1046,7 @@ void BytecodeBuilder::expandJumps()
|
||||||
|
|
||||||
std::string BytecodeBuilder::getError(const std::string& message)
|
std::string BytecodeBuilder::getError(const std::string& message)
|
||||||
{
|
{
|
||||||
// 0 acts as a special marker for error bytecode (it's equal to LBC_VERSION for valid bytecode blobs)
|
// 0 acts as a special marker for error bytecode (it's equal to LBC_VERSION_TARGET for valid bytecode blobs)
|
||||||
std::string result;
|
std::string result;
|
||||||
result += char(0);
|
result += char(0);
|
||||||
result += message;
|
result += message;
|
||||||
|
@ -1048,6 +1054,12 @@ std::string BytecodeBuilder::getError(const std::string& message)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t BytecodeBuilder::getVersion()
|
||||||
|
{
|
||||||
|
// This function usually returns LBC_VERSION_TARGET but may sometimes return a higher number (within LBC_VERSION_MIN/MAX) under fast flags
|
||||||
|
return LBC_VERSION_TARGET;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef LUAU_ASSERTENABLED
|
#ifdef LUAU_ASSERTENABLED
|
||||||
void BytecodeBuilder::validate() const
|
void BytecodeBuilder::validate() const
|
||||||
{
|
{
|
||||||
|
@ -1075,6 +1087,8 @@ void BytecodeBuilder::validate() const
|
||||||
LUAU_ASSERT(i <= insns.size());
|
LUAU_ASSERT(i <= insns.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> openCaptures;
|
||||||
|
|
||||||
// second pass: validate the rest of the bytecode
|
// second pass: validate the rest of the bytecode
|
||||||
for (size_t i = 0; i < insns.size();)
|
for (size_t i = 0; i < insns.size();)
|
||||||
{
|
{
|
||||||
|
@ -1121,6 +1135,8 @@ void BytecodeBuilder::validate() const
|
||||||
|
|
||||||
case LOP_CLOSEUPVALS:
|
case LOP_CLOSEUPVALS:
|
||||||
VREG(LUAU_INSN_A(insn));
|
VREG(LUAU_INSN_A(insn));
|
||||||
|
while (openCaptures.size() && openCaptures.back() >= LUAU_INSN_A(insn))
|
||||||
|
openCaptures.pop_back();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LOP_GETIMPORT:
|
case LOP_GETIMPORT:
|
||||||
|
@ -1388,8 +1404,12 @@ void BytecodeBuilder::validate() const
|
||||||
switch (LUAU_INSN_A(insn))
|
switch (LUAU_INSN_A(insn))
|
||||||
{
|
{
|
||||||
case LCT_VAL:
|
case LCT_VAL:
|
||||||
|
VREG(LUAU_INSN_B(insn));
|
||||||
|
break;
|
||||||
|
|
||||||
case LCT_REF:
|
case LCT_REF:
|
||||||
VREG(LUAU_INSN_B(insn));
|
VREG(LUAU_INSN_B(insn));
|
||||||
|
openCaptures.push_back(LUAU_INSN_B(insn));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case LCT_UPVAL:
|
case LCT_UPVAL:
|
||||||
|
@ -1409,6 +1429,12 @@ void BytecodeBuilder::validate() const
|
||||||
LUAU_ASSERT(i <= insns.size());
|
LUAU_ASSERT(i <= insns.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all CAPTURE REF instructions must have a CLOSEUPVALS instruction after them in the bytecode stream
|
||||||
|
// this doesn't guarantee safety as it doesn't perform basic block based analysis, but if this fails
|
||||||
|
// then the bytecode is definitely unsafe to run since the compiler won't generate backwards branches
|
||||||
|
// except for loop edges
|
||||||
|
LUAU_ASSERT(openCaptures.empty());
|
||||||
|
|
||||||
#undef VREG
|
#undef VREG
|
||||||
#undef VREGEND
|
#undef VREGEND
|
||||||
#undef VUPVAL
|
#undef VUPVAL
|
||||||
|
|
|
@ -16,8 +16,6 @@
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompileIterNoPairs, false)
|
|
||||||
|
|
||||||
LUAU_FASTINTVARIABLE(LuauCompileLoopUnrollThreshold, 25)
|
LUAU_FASTINTVARIABLE(LuauCompileLoopUnrollThreshold, 25)
|
||||||
LUAU_FASTINTVARIABLE(LuauCompileLoopUnrollThresholdMaxBoost, 300)
|
LUAU_FASTINTVARIABLE(LuauCompileLoopUnrollThresholdMaxBoost, 300)
|
||||||
|
|
||||||
|
@ -246,6 +244,14 @@ struct Compiler
|
||||||
f.canInline = true;
|
f.canInline = true;
|
||||||
f.stackSize = stackSize;
|
f.stackSize = stackSize;
|
||||||
f.costModel = modelCost(func->body, func->args.data, func->args.size);
|
f.costModel = modelCost(func->body, func->args.data, func->args.size);
|
||||||
|
|
||||||
|
// track functions that only ever return a single value so that we can convert multret calls to fixedret calls
|
||||||
|
if (allPathsEndWithReturn(func->body))
|
||||||
|
{
|
||||||
|
ReturnVisitor returnVisitor(this);
|
||||||
|
stat->visit(&returnVisitor);
|
||||||
|
f.returnsOne = returnVisitor.returnsOne;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
upvals.clear(); // note: instead of std::move above, we copy & clear to preserve capacity for future pushes
|
upvals.clear(); // note: instead of std::move above, we copy & clear to preserve capacity for future pushes
|
||||||
|
@ -260,6 +266,19 @@ struct Compiler
|
||||||
{
|
{
|
||||||
if (AstExprCall* expr = node->as<AstExprCall>())
|
if (AstExprCall* expr = node->as<AstExprCall>())
|
||||||
{
|
{
|
||||||
|
// Optimization: convert multret calls to functions that always return one value to fixedret calls; this facilitates inlining
|
||||||
|
if (options.optimizationLevel >= 2)
|
||||||
|
{
|
||||||
|
AstExprFunction* func = getFunctionExpr(expr->func);
|
||||||
|
Function* fi = func ? functions.find(func) : nullptr;
|
||||||
|
|
||||||
|
if (fi && fi->returnsOne)
|
||||||
|
{
|
||||||
|
compileExprTemp(node, target);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We temporarily swap out regTop to have targetTop work correctly...
|
// We temporarily swap out regTop to have targetTop work correctly...
|
||||||
// This is a crude hack but it's necessary for correctness :(
|
// This is a crude hack but it's necessary for correctness :(
|
||||||
RegScope rs(this, target);
|
RegScope rs(this, target);
|
||||||
|
@ -447,7 +466,9 @@ struct Compiler
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: we can compile multret functions if all returns of the function are multret as well
|
// we can't inline multret functions because the caller expects L->top to be adjusted:
|
||||||
|
// - inlined return compiles to a JUMP, and we don't have an instruction that adjusts L->top arbitrarily
|
||||||
|
// - even if we did, right now all L->top adjustments are immediately consumed by the next instruction, and for now we want to preserve that
|
||||||
if (multRet)
|
if (multRet)
|
||||||
{
|
{
|
||||||
bytecode.addDebugRemark("inlining failed: can't convert fixed returns to multret");
|
bytecode.addDebugRemark("inlining failed: can't convert fixed returns to multret");
|
||||||
|
@ -492,7 +513,7 @@ struct Compiler
|
||||||
size_t oldLocals = localStack.size();
|
size_t oldLocals = localStack.size();
|
||||||
|
|
||||||
// note that we push the frame early; this is needed to block recursive inline attempts
|
// note that we push the frame early; this is needed to block recursive inline attempts
|
||||||
inlineFrames.push_back({func, target, targetCount});
|
inlineFrames.push_back({func, oldLocals, target, targetCount});
|
||||||
|
|
||||||
// evaluate all arguments; note that we don't emit code for constant arguments (relying on constant folding)
|
// evaluate all arguments; note that we don't emit code for constant arguments (relying on constant folding)
|
||||||
for (size_t i = 0; i < func->args.size; ++i)
|
for (size_t i = 0; i < func->args.size; ++i)
|
||||||
|
@ -593,6 +614,8 @@ struct Compiler
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < targetCount; ++i)
|
for (size_t i = 0; i < targetCount; ++i)
|
||||||
bytecode.emitABC(LOP_LOADNIL, uint8_t(target + i), 0, 0);
|
bytecode.emitABC(LOP_LOADNIL, uint8_t(target + i), 0, 0);
|
||||||
|
|
||||||
|
closeLocals(oldLocals);
|
||||||
}
|
}
|
||||||
|
|
||||||
popLocals(oldLocals);
|
popLocals(oldLocals);
|
||||||
|
@ -2355,6 +2378,8 @@ struct Compiler
|
||||||
|
|
||||||
compileExprListTemp(stat->list, frame.target, frame.targetCount, /* targetTop= */ false);
|
compileExprListTemp(stat->list, frame.target, frame.targetCount, /* targetTop= */ false);
|
||||||
|
|
||||||
|
closeLocals(frame.localOffset);
|
||||||
|
|
||||||
if (!fallthrough)
|
if (!fallthrough)
|
||||||
{
|
{
|
||||||
size_t jumpLabel = bytecode.emitLabel();
|
size_t jumpLabel = bytecode.emitLabel();
|
||||||
|
@ -2645,7 +2670,7 @@ struct Compiler
|
||||||
else if (builtin.isGlobal("pairs")) // for .. in pairs(t)
|
else if (builtin.isGlobal("pairs")) // for .. in pairs(t)
|
||||||
{
|
{
|
||||||
skipOp = LOP_FORGPREP_NEXT;
|
skipOp = LOP_FORGPREP_NEXT;
|
||||||
loopOp = FFlag::LuauCompileIterNoPairs ? LOP_FORGLOOP : LOP_FORGLOOP_NEXT;
|
loopOp = LOP_FORGLOOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (stat->values.size == 2)
|
else if (stat->values.size == 2)
|
||||||
|
@ -2655,7 +2680,7 @@ struct Compiler
|
||||||
if (builtin.isGlobal("next")) // for .. in next,t
|
if (builtin.isGlobal("next")) // for .. in next,t
|
||||||
{
|
{
|
||||||
skipOp = LOP_FORGPREP_NEXT;
|
skipOp = LOP_FORGPREP_NEXT;
|
||||||
loopOp = FFlag::LuauCompileIterNoPairs ? LOP_FORGLOOP : LOP_FORGLOOP_NEXT;
|
loopOp = LOP_FORGLOOP;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3316,6 +3341,48 @@ struct Compiler
|
||||||
std::vector<AstLocal*> upvals;
|
std::vector<AstLocal*> upvals;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ReturnVisitor: AstVisitor
|
||||||
|
{
|
||||||
|
Compiler* self;
|
||||||
|
bool returnsOne = true;
|
||||||
|
|
||||||
|
ReturnVisitor(Compiler* self)
|
||||||
|
: self(self)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(AstExpr* expr) override
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool visit(AstStatReturn* stat) override
|
||||||
|
{
|
||||||
|
if (stat->list.size == 1)
|
||||||
|
{
|
||||||
|
AstExpr* value = stat->list.data[0];
|
||||||
|
|
||||||
|
if (AstExprCall* expr = value->as<AstExprCall>())
|
||||||
|
{
|
||||||
|
AstExprFunction* func = self->getFunctionExpr(expr->func);
|
||||||
|
Function* fi = func ? self->functions.find(func) : nullptr;
|
||||||
|
|
||||||
|
returnsOne &= fi && fi->returnsOne;
|
||||||
|
}
|
||||||
|
else if (value->is<AstExprVarargs>())
|
||||||
|
{
|
||||||
|
returnsOne = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
returnsOne = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
struct RegScope
|
struct RegScope
|
||||||
{
|
{
|
||||||
RegScope(Compiler* self)
|
RegScope(Compiler* self)
|
||||||
|
@ -3351,6 +3418,7 @@ struct Compiler
|
||||||
uint64_t costModel = 0;
|
uint64_t costModel = 0;
|
||||||
unsigned int stackSize = 0;
|
unsigned int stackSize = 0;
|
||||||
bool canInline = false;
|
bool canInline = false;
|
||||||
|
bool returnsOne = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Local
|
struct Local
|
||||||
|
@ -3384,6 +3452,8 @@ struct Compiler
|
||||||
{
|
{
|
||||||
AstExprFunction* func;
|
AstExprFunction* func;
|
||||||
|
|
||||||
|
size_t localOffset;
|
||||||
|
|
||||||
uint8_t target;
|
uint8_t target;
|
||||||
uint8_t targetCount;
|
uint8_t targetCount;
|
||||||
|
|
||||||
|
|
|
@ -418,7 +418,7 @@ typedef struct Table
|
||||||
CommonHeader;
|
CommonHeader;
|
||||||
|
|
||||||
|
|
||||||
uint8_t flags; /* 1<<p means tagmethod(p) is not present */
|
uint8_t tmcache; /* 1<<p means tagmethod(p) is not present */
|
||||||
uint8_t readonly; /* sandboxing feature to prohibit writes to table */
|
uint8_t readonly; /* sandboxing feature to prohibit writes to table */
|
||||||
uint8_t safeenv; /* environment doesn't share globals with other scripts */
|
uint8_t safeenv; /* environment doesn't share globals with other scripts */
|
||||||
uint8_t lsizenode; /* log2 of size of `node' array */
|
uint8_t lsizenode; /* log2 of size of `node' array */
|
||||||
|
|
|
@ -45,7 +45,7 @@ static_assert(TKey{{NULL}, {0}, LUA_TNIL, MAXSIZE - 1}.next == MAXSIZE - 1, "not
|
||||||
static_assert(TKey{{NULL}, {0}, 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");
|
||||||
|
|
||||||
// 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->tmcache = 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 = {
|
||||||
|
@ -479,7 +479,7 @@ Table* luaH_new(lua_State* L, int narray, int nhash)
|
||||||
Table* t = luaM_newgco(L, Table, sizeof(Table), L->activememcat);
|
Table* t = luaM_newgco(L, Table, sizeof(Table), L->activememcat);
|
||||||
luaC_init(L, t, LUA_TTABLE);
|
luaC_init(L, t, LUA_TTABLE);
|
||||||
t->metatable = NULL;
|
t->metatable = NULL;
|
||||||
t->flags = cast_byte(~0);
|
t->tmcache = cast_byte(~0);
|
||||||
t->array = NULL;
|
t->array = NULL;
|
||||||
t->sizearray = 0;
|
t->sizearray = 0;
|
||||||
t->lastfree = 0;
|
t->lastfree = 0;
|
||||||
|
@ -778,7 +778,7 @@ Table* luaH_clone(lua_State* L, Table* tt)
|
||||||
Table* t = luaM_newgco(L, Table, sizeof(Table), L->activememcat);
|
Table* t = luaM_newgco(L, Table, sizeof(Table), L->activememcat);
|
||||||
luaC_init(L, t, LUA_TTABLE);
|
luaC_init(L, t, LUA_TTABLE);
|
||||||
t->metatable = tt->metatable;
|
t->metatable = tt->metatable;
|
||||||
t->flags = tt->flags;
|
t->tmcache = tt->tmcache;
|
||||||
t->array = NULL;
|
t->array = NULL;
|
||||||
t->sizearray = 0;
|
t->sizearray = 0;
|
||||||
t->lsizenode = 0;
|
t->lsizenode = 0;
|
||||||
|
@ -835,5 +835,5 @@ void luaH_clear(Table* tt)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* back to empty -> no tag methods present */
|
/* back to empty -> no tag methods present */
|
||||||
tt->flags = cast_byte(~0);
|
tt->tmcache = cast_byte(~0);
|
||||||
}
|
}
|
||||||
|
|
|
@ -88,8 +88,8 @@ const TValue* luaT_gettm(Table* events, TMS event, TString* ename)
|
||||||
const TValue* tm = luaH_getstr(events, ename);
|
const TValue* tm = luaH_getstr(events, ename);
|
||||||
LUAU_ASSERT(event <= TM_EQ);
|
LUAU_ASSERT(event <= TM_EQ);
|
||||||
if (ttisnil(tm))
|
if (ttisnil(tm))
|
||||||
{ /* no tag method? */
|
{ /* no tag method? */
|
||||||
events->flags |= cast_byte(1u << event); /* cache this fact */
|
events->tmcache |= cast_byte(1u << event); /* cache this fact */
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -41,10 +41,10 @@ typedef enum
|
||||||
} TMS;
|
} TMS;
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
#define gfasttm(g, et, e) ((et) == NULL ? NULL : ((et)->flags & (1u << (e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
|
#define gfasttm(g, et, e) ((et) == NULL ? NULL : ((et)->tmcache & (1u << (e))) ? NULL : luaT_gettm(et, e, (g)->tmname[e]))
|
||||||
|
|
||||||
#define fasttm(l, et, e) gfasttm(l->global, et, e)
|
#define fasttm(l, et, e) gfasttm(l->global, et, e)
|
||||||
#define fastnotm(et, e) ((et) == NULL || ((et)->flags & (1u << (e))))
|
#define fastnotm(et, e) ((et) == NULL || ((et)->tmcache & (1u << (e))))
|
||||||
|
|
||||||
LUAI_DATA const char* const luaT_typenames[];
|
LUAI_DATA const char* const luaT_typenames[];
|
||||||
LUAI_DATA const char* const luaT_eventname[];
|
LUAI_DATA const char* const luaT_eventname[];
|
||||||
|
|
|
@ -26,6 +26,8 @@ void luaU_freeudata(lua_State* L, Udata* u, lua_Page* page)
|
||||||
{
|
{
|
||||||
void (*dtor)(lua_State*, void*) = nullptr;
|
void (*dtor)(lua_State*, void*) = nullptr;
|
||||||
dtor = L->global->udatagc[u->tag];
|
dtor = L->global->udatagc[u->tag];
|
||||||
|
// TODO: access to L here is highly unsafe since this is called during internal GC traversal
|
||||||
|
// certain operations such as lua_getthreaddata are okay, but by and large this risks crashes on improper use
|
||||||
if (dtor)
|
if (dtor)
|
||||||
dtor(L, u->data);
|
dtor(L, u->data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -154,11 +154,11 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (version != LBC_VERSION)
|
if (version < LBC_VERSION_MIN || version > LBC_VERSION_MAX)
|
||||||
{
|
{
|
||||||
char chunkid[LUA_IDSIZE];
|
char chunkid[LUA_IDSIZE];
|
||||||
luaO_chunkid(chunkid, chunkname, LUA_IDSIZE);
|
luaO_chunkid(chunkid, chunkname, LUA_IDSIZE);
|
||||||
lua_pushfstring(L, "%s: bytecode version mismatch (expected %d, got %d)", chunkid, LBC_VERSION, version);
|
lua_pushfstring(L, "%s: bytecode version mismatch (expected [%d..%d], got %d)", chunkid, LBC_VERSION_MIN, LBC_VERSION_MAX, version);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue