v0.3.5+luau535
This commit is contained in:
parent
3320c56ff5
commit
b7ad6f4b91
|
@ -1,6 +1,6 @@
|
|||
[package]
|
||||
name = "luau0-src"
|
||||
version = "0.3.4+luau533"
|
||||
version = "0.3.5+luau535"
|
||||
authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"]
|
||||
edition = "2021"
|
||||
repository = "https://github.com/khvzak/luau-src-rs"
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
|
||||
#include <algorithm>
|
||||
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
|
||||
// Warning: If you are introducing new syntax, ensure that it is behind a separate
|
||||
// flag so that we don't break production games by reverting syntax changes.
|
||||
// See docs/SyntaxChanges.md for an explanation.
|
||||
|
@ -12,7 +15,18 @@ LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000)
|
|||
LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauParserFunctionKeywordAsTypeHelp, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauReturnTypeTokenConfusion, false)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauFixNamedFunctionParse, false)
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseWrongNamedType, false)
|
||||
|
||||
bool lua_telemetry_parsed_named_non_function_type = false;
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauErrorParseIntegerIssues, false)
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseIntegerIssues, false)
|
||||
|
||||
bool lua_telemetry_parsed_out_of_range_bin_integer = false;
|
||||
bool lua_telemetry_parsed_out_of_range_hex_integer = false;
|
||||
bool lua_telemetry_parsed_double_prefix_hex_integer = false;
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
@ -1119,10 +1133,9 @@ AstTypePack* Parser::parseTypeList(TempVector<AstType*>& result, TempVector<std:
|
|||
|
||||
std::optional<AstTypeList> Parser::parseOptionalReturnTypeAnnotation()
|
||||
{
|
||||
if (options.allowTypeAnnotations &&
|
||||
(lexer.current().type == ':' || (FFlag::LuauReturnTypeTokenConfusion && lexer.current().type == Lexeme::SkinnyArrow)))
|
||||
if (options.allowTypeAnnotations && (lexer.current().type == ':' || lexer.current().type == Lexeme::SkinnyArrow))
|
||||
{
|
||||
if (FFlag::LuauReturnTypeTokenConfusion && lexer.current().type == Lexeme::SkinnyArrow)
|
||||
if (lexer.current().type == Lexeme::SkinnyArrow)
|
||||
report(lexer.current().location, "Function return type annotations are written after ':' instead of '->'");
|
||||
|
||||
nextLexeme();
|
||||
|
@ -1330,7 +1343,7 @@ AstTypeOrPack Parser::parseFunctionTypeAnnotation(bool allowPack)
|
|||
{
|
||||
incrementRecursionCounter("type annotation");
|
||||
|
||||
bool monomorphic = lexer.current().type != '<';
|
||||
bool forceFunctionType = lexer.current().type == '<';
|
||||
|
||||
Lexeme begin = lexer.current();
|
||||
|
||||
|
@ -1355,21 +1368,30 @@ AstTypeOrPack Parser::parseFunctionTypeAnnotation(bool allowPack)
|
|||
|
||||
AstArray<AstType*> paramTypes = copy(params);
|
||||
|
||||
bool returnTypeIntroducer =
|
||||
FFlag::LuauReturnTypeTokenConfusion ? lexer.current().type == Lexeme::SkinnyArrow || lexer.current().type == ':' : false;
|
||||
if (FFlag::LuauFixNamedFunctionParse && !names.empty())
|
||||
forceFunctionType = true;
|
||||
|
||||
bool returnTypeIntroducer = lexer.current().type == Lexeme::SkinnyArrow || lexer.current().type == ':';
|
||||
|
||||
// Not a function at all. Just a parenthesized type. Or maybe a type pack with a single element
|
||||
if (params.size() == 1 && !varargAnnotation && monomorphic &&
|
||||
(FFlag::LuauReturnTypeTokenConfusion ? !returnTypeIntroducer : lexer.current().type != Lexeme::SkinnyArrow))
|
||||
if (params.size() == 1 && !varargAnnotation && !forceFunctionType && !returnTypeIntroducer)
|
||||
{
|
||||
if (DFFlag::LuaReportParseWrongNamedType && !names.empty())
|
||||
lua_telemetry_parsed_named_non_function_type = true;
|
||||
|
||||
if (allowPack)
|
||||
return {{}, allocator.alloc<AstTypePackExplicit>(begin.location, AstTypeList{paramTypes, nullptr})};
|
||||
else
|
||||
return {params[0], {}};
|
||||
}
|
||||
|
||||
if ((FFlag::LuauReturnTypeTokenConfusion ? !returnTypeIntroducer : lexer.current().type != Lexeme::SkinnyArrow) && monomorphic && allowPack)
|
||||
if (!forceFunctionType && !returnTypeIntroducer && allowPack)
|
||||
{
|
||||
if (DFFlag::LuaReportParseWrongNamedType && !names.empty())
|
||||
lua_telemetry_parsed_named_non_function_type = true;
|
||||
|
||||
return {{}, allocator.alloc<AstTypePackExplicit>(begin.location, AstTypeList{paramTypes, varargAnnotation})};
|
||||
}
|
||||
|
||||
AstArray<std::optional<AstArgumentName>> paramNames = copy(names);
|
||||
|
||||
|
@ -1382,7 +1404,7 @@ AstType* Parser::parseFunctionTypeAnnotationTail(const Lexeme& begin, AstArray<A
|
|||
{
|
||||
incrementRecursionCounter("type annotation");
|
||||
|
||||
if (FFlag::LuauReturnTypeTokenConfusion && lexer.current().type == ':')
|
||||
if (lexer.current().type == ':')
|
||||
{
|
||||
report(lexer.current().location, "Return types in function type annotations are written after '->' instead of ':'");
|
||||
lexer.next();
|
||||
|
@ -2010,7 +2032,63 @@ AstExpr* Parser::parseAssertionExpr()
|
|||
return expr;
|
||||
}
|
||||
|
||||
static bool parseNumber(double& result, const char* data)
|
||||
static const char* parseInteger(double& result, const char* data, int base)
|
||||
{
|
||||
char* end = nullptr;
|
||||
unsigned long long value = strtoull(data, &end, base);
|
||||
|
||||
if (value == ULLONG_MAX && errno == ERANGE)
|
||||
{
|
||||
// 'errno' might have been set before we called 'strtoull', but we don't want the overhead of resetting a TLS variable on each call
|
||||
// so we only reset it when we get a result that might be an out-of-range error and parse again to make sure
|
||||
errno = 0;
|
||||
value = strtoull(data, &end, base);
|
||||
|
||||
if (errno == ERANGE)
|
||||
{
|
||||
if (DFFlag::LuaReportParseIntegerIssues)
|
||||
{
|
||||
if (base == 2)
|
||||
lua_telemetry_parsed_out_of_range_bin_integer = true;
|
||||
else
|
||||
lua_telemetry_parsed_out_of_range_hex_integer = true;
|
||||
}
|
||||
|
||||
if (FFlag::LuauErrorParseIntegerIssues)
|
||||
return "Integer number value is out of range";
|
||||
}
|
||||
}
|
||||
|
||||
result = double(value);
|
||||
return *end == 0 ? nullptr : "Malformed number";
|
||||
}
|
||||
|
||||
static const char* parseNumber(double& result, const char* data)
|
||||
{
|
||||
// binary literal
|
||||
if (data[0] == '0' && (data[1] == 'b' || data[1] == 'B') && data[2])
|
||||
return parseInteger(result, data + 2, 2);
|
||||
|
||||
// hexadecimal literal
|
||||
if (data[0] == '0' && (data[1] == 'x' || data[1] == 'X') && data[2])
|
||||
{
|
||||
if (DFFlag::LuaReportParseIntegerIssues && data[2] == '0' && (data[3] == 'x' || data[3] == 'X'))
|
||||
lua_telemetry_parsed_double_prefix_hex_integer = true;
|
||||
|
||||
if (FFlag::LuauErrorParseIntegerIssues)
|
||||
return parseInteger(result, data, 16); // keep prefix, it's handled by 'strtoull'
|
||||
else
|
||||
return parseInteger(result, data + 2, 16);
|
||||
}
|
||||
|
||||
char* end = nullptr;
|
||||
double value = strtod(data, &end);
|
||||
|
||||
result = value;
|
||||
return *end == 0 ? nullptr : "Malformed number";
|
||||
}
|
||||
|
||||
static bool parseNumber_DEPRECATED(double& result, const char* data)
|
||||
{
|
||||
// binary literal
|
||||
if (data[0] == '0' && (data[1] == 'b' || data[1] == 'B') && data[2])
|
||||
|
@ -2080,18 +2158,37 @@ AstExpr* Parser::parseSimpleExpr()
|
|||
scratchData.erase(std::remove(scratchData.begin(), scratchData.end(), '_'), scratchData.end());
|
||||
}
|
||||
|
||||
double value = 0;
|
||||
if (parseNumber(value, scratchData.c_str()))
|
||||
if (DFFlag::LuaReportParseIntegerIssues || FFlag::LuauErrorParseIntegerIssues)
|
||||
{
|
||||
nextLexeme();
|
||||
double value = 0;
|
||||
if (const char* error = parseNumber(value, scratchData.c_str()))
|
||||
{
|
||||
nextLexeme();
|
||||
|
||||
return allocator.alloc<AstExprConstantNumber>(start, value);
|
||||
return reportExprError(start, {}, "%s", error);
|
||||
}
|
||||
else
|
||||
{
|
||||
nextLexeme();
|
||||
|
||||
return allocator.alloc<AstExprConstantNumber>(start, value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
nextLexeme();
|
||||
double value = 0;
|
||||
if (parseNumber_DEPRECATED(value, scratchData.c_str()))
|
||||
{
|
||||
nextLexeme();
|
||||
|
||||
return reportExprError(start, {}, "Malformed number");
|
||||
return allocator.alloc<AstExprConstantNumber>(start, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
nextLexeme();
|
||||
|
||||
return reportExprError(start, {}, "Malformed number");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (lexer.current().type == Lexeme::RawString || lexer.current().type == Lexeme::QuotedString)
|
||||
|
|
|
@ -276,7 +276,7 @@ enum LuauOpcode
|
|||
// FORGLOOP: adjust loop variables for one iteration of a generic for loop, jump back to the loop header if loop needs to continue
|
||||
// A: target register; generic for loops assume a register layout [generator, state, index, variables...]
|
||||
// D: jump offset (-32768..32767)
|
||||
// AUX: variable count (1..255)
|
||||
// AUX: variable count (1..255) in the low 8 bits, high bit indicates whether to use ipairs-style traversal in the fast path
|
||||
// loop variables are adjusted by calling generator(state, index) and expecting it to return a tuple that's copied to the user variables
|
||||
// the first variable is then copied into index; generator/state are immutable, index isn't visible to user code
|
||||
LOP_FORGLOOP,
|
||||
|
@ -490,6 +490,9 @@ enum LuauBuiltinFunction
|
|||
|
||||
// select(_, ...)
|
||||
LBF_SELECT_VARARG,
|
||||
|
||||
// rawlen
|
||||
LBF_RAWLEN,
|
||||
};
|
||||
|
||||
// Capture type, used in LOP_CAPTURE
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
#include "Luau/Bytecode.h"
|
||||
#include "Luau/Compiler.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileRawlen, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
namespace Compile
|
||||
|
@ -58,6 +60,8 @@ int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& options)
|
|||
return LBF_RAWGET;
|
||||
if (builtin.isGlobal("rawequal"))
|
||||
return LBF_RAWEQUAL;
|
||||
if (FFlag::LuauCompileRawlen && builtin.isGlobal("rawlen"))
|
||||
return LBF_RAWLEN;
|
||||
|
||||
if (builtin.isGlobal("unpack"))
|
||||
return LBF_TABLE_UNPACK;
|
||||
|
|
|
@ -1302,20 +1302,22 @@ void BytecodeBuilder::validate() const
|
|||
|
||||
case LOP_FORNPREP:
|
||||
case LOP_FORNLOOP:
|
||||
VREG(LUAU_INSN_A(insn) + 2); // for loop protocol: A, A+1, A+2 are used for iteration
|
||||
// for loop protocol: A, A+1, A+2 are used for iteration
|
||||
VREG(LUAU_INSN_A(insn) + 2);
|
||||
VJUMP(LUAU_INSN_D(insn));
|
||||
break;
|
||||
|
||||
case LOP_FORGPREP:
|
||||
VREG(LUAU_INSN_A(insn) + 2 + 1); // forg loop protocol: A, A+1, A+2 are used for iteration protocol; A+3, ... are loop variables
|
||||
// forg loop protocol: A, A+1, A+2 are used for iteration protocol; A+3, ... are loop variables
|
||||
VREG(LUAU_INSN_A(insn) + 2 + 1);
|
||||
VJUMP(LUAU_INSN_D(insn));
|
||||
break;
|
||||
|
||||
case LOP_FORGLOOP:
|
||||
VREG(
|
||||
LUAU_INSN_A(insn) + 2 + insns[i + 1]); // forg loop protocol: A, A+1, A+2 are used for iteration protocol; A+3, ... are loop variables
|
||||
// forg loop protocol: A, A+1, A+2 are used for iteration protocol; A+3, ... are loop variables
|
||||
VREG(LUAU_INSN_A(insn) + 2 + uint8_t(insns[i + 1]));
|
||||
VJUMP(LUAU_INSN_D(insn));
|
||||
LUAU_ASSERT(insns[i + 1] >= 1);
|
||||
LUAU_ASSERT(uint8_t(insns[i + 1]) >= 1);
|
||||
break;
|
||||
|
||||
case LOP_FORGPREP_INEXT:
|
||||
|
@ -1679,7 +1681,8 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result,
|
|||
break;
|
||||
|
||||
case LOP_FORGLOOP:
|
||||
formatAppend(result, "FORGLOOP R%d L%d %d\n", LUAU_INSN_A(insn), targetLabel, *code++);
|
||||
formatAppend(result, "FORGLOOP R%d L%d %d%s\n", LUAU_INSN_A(insn), targetLabel, uint8_t(*code), int(*code) < 0 ? " [inext]" : "");
|
||||
code++;
|
||||
break;
|
||||
|
||||
case LOP_FORGPREP_INEXT:
|
||||
|
|
|
@ -23,6 +23,8 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineThreshold, 25)
|
|||
LUAU_FASTINTVARIABLE(LuauCompileInlineThresholdMaxBoost, 300)
|
||||
LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauCompileNoIpairs, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -2665,7 +2667,7 @@ struct Compiler
|
|||
if (builtin.isGlobal("ipairs")) // for .. in ipairs(t)
|
||||
{
|
||||
skipOp = LOP_FORGPREP_INEXT;
|
||||
loopOp = LOP_FORGLOOP_INEXT;
|
||||
loopOp = FFlag::LuauCompileNoIpairs ? LOP_FORGLOOP : LOP_FORGLOOP_INEXT;
|
||||
}
|
||||
else if (builtin.isGlobal("pairs")) // for .. in pairs(t)
|
||||
{
|
||||
|
@ -2709,8 +2711,16 @@ struct Compiler
|
|||
|
||||
bytecode.emitAD(loopOp, regs, 0);
|
||||
|
||||
if (FFlag::LuauCompileNoIpairs)
|
||||
{
|
||||
// TODO: remove loopOp as it's a constant now
|
||||
LUAU_ASSERT(loopOp == LOP_FORGLOOP);
|
||||
|
||||
// FORGLOOP uses aux to encode variable count and fast path flag for ipairs traversal in the high bit
|
||||
bytecode.emitAux((skipOp == LOP_FORGPREP_INEXT ? 0x80000000 : 0) | uint32_t(stat->vars.size));
|
||||
}
|
||||
// note: FORGLOOP needs variable count encoded in AUX field, other loop instructions assume a fixed variable count
|
||||
if (loopOp == LOP_FORGLOOP)
|
||||
else if (loopOp == LOP_FORGLOOP)
|
||||
bytecode.emitAux(uint32_t(stat->vars.size));
|
||||
|
||||
size_t endLabel = bytecode.emitLabel();
|
||||
|
@ -3341,7 +3351,7 @@ struct Compiler
|
|||
std::vector<AstLocal*> upvals;
|
||||
};
|
||||
|
||||
struct ReturnVisitor: AstVisitor
|
||||
struct ReturnVisitor : AstVisitor
|
||||
{
|
||||
Compiler* self;
|
||||
bool returnsOne = true;
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauLenTM)
|
||||
|
||||
static void writestring(const char* s, size_t l)
|
||||
{
|
||||
fwrite(s, 1, l, stdout);
|
||||
|
@ -178,6 +180,18 @@ static int luaB_rawset(lua_State* L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int luaB_rawlen(lua_State* L)
|
||||
{
|
||||
if (!FFlag::LuauLenTM)
|
||||
luaL_error(L, "'rawlen' is not available");
|
||||
|
||||
int tt = lua_type(L, 1);
|
||||
luaL_argcheck(L, tt == LUA_TTABLE || tt == LUA_TSTRING, 1, "table or string expected");
|
||||
int len = lua_objlen(L, 1);
|
||||
lua_pushinteger(L, len);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int luaB_gcinfo(lua_State* L)
|
||||
{
|
||||
lua_pushinteger(L, lua_gc(L, LUA_GCCOUNT, 0));
|
||||
|
@ -428,6 +442,7 @@ static const luaL_Reg base_funcs[] = {
|
|||
{"rawequal", luaB_rawequal},
|
||||
{"rawget", luaB_rawget},
|
||||
{"rawset", luaB_rawset},
|
||||
{"rawlen", luaB_rawlen},
|
||||
{"select", luaB_select},
|
||||
{"setfenv", luaB_setfenv},
|
||||
{"setmetatable", luaB_setmetatable},
|
||||
|
|
|
@ -1117,6 +1117,27 @@ static int luauF_select(lua_State* L, StkId res, TValue* arg0, int nresults, Stk
|
|||
return -1;
|
||||
}
|
||||
|
||||
static int luauF_rawlen(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||
{
|
||||
if (nparams >= 1 && nresults <= 1)
|
||||
{
|
||||
if (ttistable(arg0))
|
||||
{
|
||||
Table* h = hvalue(arg0);
|
||||
setnvalue(res, double(luaH_getn(h)));
|
||||
return 1;
|
||||
}
|
||||
else if (ttisstring(arg0))
|
||||
{
|
||||
TString* ts = tsvalue(arg0);
|
||||
setnvalue(res, double(ts->len));
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
luau_FastFunction luauF_table[256] = {
|
||||
NULL,
|
||||
luauF_assert,
|
||||
|
@ -1188,4 +1209,6 @@ luau_FastFunction luauF_table[256] = {
|
|||
luauF_countrz,
|
||||
|
||||
luauF_select,
|
||||
|
||||
luauF_rawlen,
|
||||
};
|
||||
|
|
|
@ -19,6 +19,7 @@ LUAI_FUNC l_noret luaG_concaterror(lua_State* L, StkId p1, StkId p2);
|
|||
LUAI_FUNC l_noret luaG_aritherror(lua_State* L, const TValue* p1, const TValue* p2, TMS op);
|
||||
LUAI_FUNC l_noret luaG_ordererror(lua_State* L, const TValue* p1, const TValue* p2, TMS op);
|
||||
LUAI_FUNC l_noret luaG_indexerror(lua_State* L, const TValue* p1, const TValue* p2);
|
||||
|
||||
LUAI_FUNC LUA_PRINTF_ATTR(2, 3) l_noret luaG_runerrorL(lua_State* L, const char* fmt, ...);
|
||||
LUAI_FUNC void luaG_pusherror(lua_State* L, const char* error);
|
||||
|
||||
|
|
|
@ -108,9 +108,9 @@ static LuaNode* hashvec(const Table* t, const float* v)
|
|||
memcpy(i, v, sizeof(i));
|
||||
|
||||
// convert -0 to 0 to make sure they hash to the same value
|
||||
i[0] = (i[0] == 0x8000000) ? 0 : i[0];
|
||||
i[1] = (i[1] == 0x8000000) ? 0 : i[1];
|
||||
i[2] = (i[2] == 0x8000000) ? 0 : i[2];
|
||||
i[0] = (i[0] == 0x80000000) ? 0 : i[0];
|
||||
i[1] = (i[1] == 0x80000000) ? 0 : i[1];
|
||||
i[2] = (i[2] == 0x80000000) ? 0 : i[2];
|
||||
|
||||
// scramble bits to make sure that integer coordinates have entropy in lower bits
|
||||
i[0] ^= i[0] >> 17;
|
||||
|
@ -121,7 +121,7 @@ static LuaNode* hashvec(const Table* t, const float* v)
|
|||
unsigned int h = (i[0] * 73856093) ^ (i[1] * 19349663) ^ (i[2] * 83492791);
|
||||
|
||||
#if LUA_VECTOR_SIZE == 4
|
||||
i[3] = (i[3] == 0x8000000) ? 0 : i[3];
|
||||
i[3] = (i[3] == 0x80000000) ? 0 : i[3];
|
||||
i[3] ^= i[3] >> 17;
|
||||
h ^= i[3] * 39916801;
|
||||
#endif
|
||||
|
|
|
@ -39,6 +39,7 @@ const char* const luaT_eventname[] = {
|
|||
"__namecall",
|
||||
"__call",
|
||||
"__iter",
|
||||
"__len",
|
||||
|
||||
"__eq",
|
||||
|
||||
|
@ -52,7 +53,6 @@ const char* const luaT_eventname[] = {
|
|||
"__unm",
|
||||
|
||||
|
||||
"__len",
|
||||
"__lt",
|
||||
"__le",
|
||||
"__concat",
|
||||
|
|
|
@ -18,6 +18,7 @@ typedef enum
|
|||
TM_NAMECALL,
|
||||
TM_CALL,
|
||||
TM_ITER,
|
||||
TM_LEN,
|
||||
|
||||
TM_EQ, /* last tag method with `fast' access */
|
||||
|
||||
|
@ -31,7 +32,6 @@ typedef enum
|
|||
TM_UNM,
|
||||
|
||||
|
||||
TM_LEN,
|
||||
TM_LT,
|
||||
TM_LE,
|
||||
TM_CONCAT,
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauLenTM, false)
|
||||
|
||||
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
||||
#ifdef __clang__
|
||||
#if __has_warning("-Wc99-designator")
|
||||
|
@ -638,20 +640,16 @@ static void luau_execute(lua_State* L)
|
|||
VM_PATCH_C(pc - 2, L->cachedslot);
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke Lua calls via __index metamethod
|
||||
VM_PROTECT(luaV_gettable(L, rb, kv, ra));
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke Lua calls via __index metamethod
|
||||
VM_PROTECT(luaV_gettable(L, rb, kv, ra));
|
||||
VM_NEXT();
|
||||
|
||||
// fall through to slow path
|
||||
}
|
||||
|
||||
// fall through to slow path
|
||||
}
|
||||
|
||||
// slow-path, may invoke Lua calls via __index metamethod
|
||||
VM_PROTECT(luaV_gettable(L, rb, kv, ra));
|
||||
VM_NEXT();
|
||||
}
|
||||
|
||||
VM_CASE(LOP_SETTABLEKS)
|
||||
|
@ -751,19 +749,13 @@ static void luau_execute(lua_State* L)
|
|||
setobj2s(L, ra, &h->array[unsigned(index - 1)]);
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
{
|
||||
// slow-path: handles out of bounds array lookups and non-integer numeric keys
|
||||
VM_PROTECT(luaV_gettable(L, rb, rc, ra));
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// slow-path: handles non-array table lookup as well as __index MT calls
|
||||
VM_PROTECT(luaV_gettable(L, rb, rc, ra));
|
||||
VM_NEXT();
|
||||
|
||||
// fall through to slow path
|
||||
}
|
||||
|
||||
// slow-path: handles out of bounds array lookups, non-integer numeric keys, non-array table lookup, __index MT calls
|
||||
VM_PROTECT(luaV_gettable(L, rb, rc, ra));
|
||||
VM_NEXT();
|
||||
}
|
||||
|
||||
VM_CASE(LOP_SETTABLE)
|
||||
|
@ -788,19 +780,13 @@ static void luau_execute(lua_State* L)
|
|||
luaC_barriert(L, h, ra);
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
{
|
||||
// slow-path: handles out of bounds array assignments and non-integer numeric keys
|
||||
VM_PROTECT(luaV_settable(L, rb, rc, ra));
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// slow-path: handles non-array table access as well as __newindex MT calls
|
||||
VM_PROTECT(luaV_settable(L, rb, rc, ra));
|
||||
VM_NEXT();
|
||||
|
||||
// fall through to slow path
|
||||
}
|
||||
|
||||
// slow-path: handles out of bounds array assignments, non-integer numeric keys, non-array table access, __newindex MT calls
|
||||
VM_PROTECT(luaV_settable(L, rb, rc, ra));
|
||||
VM_NEXT();
|
||||
}
|
||||
|
||||
VM_CASE(LOP_GETTABLEN)
|
||||
|
@ -820,6 +806,8 @@ static void luau_execute(lua_State* L)
|
|||
setobj2s(L, ra, &h->array[c]);
|
||||
VM_NEXT();
|
||||
}
|
||||
|
||||
// fall through to slow path
|
||||
}
|
||||
|
||||
// slow-path: handles out of bounds array lookups
|
||||
|
@ -847,6 +835,8 @@ static void luau_execute(lua_State* L)
|
|||
luaC_barriert(L, h, ra);
|
||||
VM_NEXT();
|
||||
}
|
||||
|
||||
// fall through to slow path
|
||||
}
|
||||
|
||||
// slow-path: handles out of bounds array lookups
|
||||
|
@ -2082,13 +2072,25 @@ static void luau_execute(lua_State* L)
|
|||
// fast-path #1: tables
|
||||
if (ttistable(rb))
|
||||
{
|
||||
setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));
|
||||
VM_NEXT();
|
||||
Table* h = hvalue(rb);
|
||||
|
||||
if (!FFlag::LuauLenTM || fastnotm(h->metatable, TM_LEN))
|
||||
{
|
||||
setnvalue(ra, cast_num(luaH_getn(h)));
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
{
|
||||
// slow-path, may invoke C/Lua via metamethods
|
||||
VM_PROTECT(luaV_dolen(L, ra, rb));
|
||||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
// fast-path #2: strings (not very important but easy to do)
|
||||
else if (ttisstring(rb))
|
||||
{
|
||||
setnvalue(ra, cast_num(tsvalue(rb)->len));
|
||||
TString* ts = tsvalue(rb);
|
||||
setnvalue(ra, cast_num(ts->len));
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
|
@ -2162,8 +2164,10 @@ static void luau_execute(lua_State* L)
|
|||
if (!ttisnumber(ra + 0) || !ttisnumber(ra + 1) || !ttisnumber(ra + 2))
|
||||
{
|
||||
// slow-path: can convert arguments to numbers and trigger Lua errors
|
||||
// Note: this doesn't reallocate stack so we don't need to recompute ra
|
||||
VM_PROTECT(luau_prepareFORN(L, ra + 0, ra + 1, ra + 2));
|
||||
// Note: this doesn't reallocate stack so we don't need to recompute ra/base
|
||||
VM_PROTECT_PC();
|
||||
|
||||
luau_prepareFORN(L, ra + 0, ra + 1, ra + 2);
|
||||
}
|
||||
|
||||
double limit = nvalue(ra + 0);
|
||||
|
@ -2226,6 +2230,15 @@ static void luau_execute(lua_State* L)
|
|||
|
||||
VM_PROTECT(luaD_call(L, ra, 3));
|
||||
L->top = L->ci->top;
|
||||
|
||||
/* recompute ra since stack might have been reallocated */
|
||||
ra = VM_REG(LUAU_INSN_A(insn));
|
||||
|
||||
/* protect against __iter returning nil, since nil is used as a marker for builtin iteration in FORGLOOP */
|
||||
if (ttisnil(ra))
|
||||
{
|
||||
VM_PROTECT(luaG_typeerror(L, ra, "call"));
|
||||
}
|
||||
}
|
||||
else if (fasttm(L, mt, TM_CALL))
|
||||
{
|
||||
|
@ -2258,27 +2271,38 @@ static void luau_execute(lua_State* L)
|
|||
uint32_t aux = *pc;
|
||||
|
||||
// fast-path: builtin table iteration
|
||||
if (ttisnil(ra) && ttistable(ra + 1) && ttislightuserdata(ra + 2))
|
||||
// note: ra=nil guarantees ra+1=table and ra+2=userdata because of the setup by FORGPREP* opcodes
|
||||
// TODO: remove the table check per guarantee above
|
||||
if (ttisnil(ra) && ttistable(ra + 1))
|
||||
{
|
||||
Table* h = hvalue(ra + 1);
|
||||
int index = int(reinterpret_cast<uintptr_t>(pvalue(ra + 2)));
|
||||
|
||||
int sizearray = h->sizearray;
|
||||
int sizenode = 1 << h->lsizenode;
|
||||
|
||||
// clear extra variables since we might have more than two
|
||||
if (LUAU_UNLIKELY(aux > 2))
|
||||
// note: while aux encodes ipairs bit, when set we always use 2 variables, so it's safe to check this via a signed comparison
|
||||
if (LUAU_UNLIKELY(int(aux) > 2))
|
||||
for (int i = 2; i < int(aux); ++i)
|
||||
setnilvalue(ra + 3 + i);
|
||||
|
||||
// terminate ipairs-style traversal early when encountering nil
|
||||
if (int(aux) < 0 && (unsigned(index) >= unsigned(sizearray) || ttisnil(&h->array[index])))
|
||||
{
|
||||
pc++;
|
||||
VM_NEXT();
|
||||
}
|
||||
|
||||
// first we advance index through the array portion
|
||||
while (unsigned(index) < unsigned(sizearray))
|
||||
{
|
||||
if (!ttisnil(&h->array[index]))
|
||||
TValue* e = &h->array[index];
|
||||
|
||||
if (!ttisnil(e))
|
||||
{
|
||||
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(index + 1)));
|
||||
setnvalue(ra + 3, double(index + 1));
|
||||
setobj2s(L, ra + 4, &h->array[index]);
|
||||
setobj2s(L, ra + 4, e);
|
||||
|
||||
pc += LUAU_INSN_D(insn);
|
||||
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
|
||||
|
@ -2288,6 +2312,8 @@ static void luau_execute(lua_State* L)
|
|||
index++;
|
||||
}
|
||||
|
||||
int sizenode = 1 << h->lsizenode;
|
||||
|
||||
// then we advance index through the hash portion
|
||||
while (unsigned(index - sizearray) < unsigned(sizenode))
|
||||
{
|
||||
|
@ -2321,7 +2347,7 @@ static void luau_execute(lua_State* L)
|
|||
L->top = ra + 3 + 3; /* func + 2 args (state and index) */
|
||||
LUAU_ASSERT(L->top <= L->stack_last);
|
||||
|
||||
VM_PROTECT(luaD_call(L, ra + 3, aux));
|
||||
VM_PROTECT(luaD_call(L, ra + 3, uint8_t(aux)));
|
||||
L->top = L->ci->top;
|
||||
|
||||
// recompute ra since stack might have been reallocated
|
||||
|
|
|
@ -10,6 +10,9 @@
|
|||
#include "lnumutils.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauLenTM)
|
||||
|
||||
/* limit for table tag-method chains (to avoid loops) */
|
||||
#define MAXTAGLOOP 100
|
||||
|
@ -51,7 +54,7 @@ const float* luaV_tovector(const TValue* obj)
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
static void callTMres(lua_State* L, StkId res, const TValue* f, const TValue* p1, const TValue* p2)
|
||||
static StkId callTMres(lua_State* L, StkId res, const TValue* f, const TValue* p1, const TValue* p2)
|
||||
{
|
||||
ptrdiff_t result = savestack(L, res);
|
||||
// using stack room beyond top is technically safe here, but for very complicated reasons:
|
||||
|
@ -71,6 +74,7 @@ static void callTMres(lua_State* L, StkId res, const TValue* f, const TValue* p1
|
|||
res = restorestack(L, result);
|
||||
L->top--;
|
||||
setobjs2s(L, res, L->top);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void callTM(lua_State* L, const TValue* f, const TValue* p1, const TValue* p2, const TValue* p3)
|
||||
|
@ -472,22 +476,56 @@ void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TM
|
|||
|
||||
void luaV_dolen(lua_State* L, StkId ra, const TValue* rb)
|
||||
{
|
||||
if (!FFlag::LuauLenTM)
|
||||
{
|
||||
switch (ttype(rb))
|
||||
{
|
||||
case LUA_TTABLE:
|
||||
{
|
||||
setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));
|
||||
break;
|
||||
}
|
||||
case LUA_TSTRING:
|
||||
{
|
||||
setnvalue(ra, cast_num(tsvalue(rb)->len));
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{ /* try metamethod */
|
||||
if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))
|
||||
luaG_typeerror(L, rb, "get length of");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const TValue* tm = NULL;
|
||||
switch (ttype(rb))
|
||||
{
|
||||
case LUA_TTABLE:
|
||||
{
|
||||
setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));
|
||||
Table* h = hvalue(rb);
|
||||
if ((tm = fasttm(L, h->metatable, TM_LEN)) == NULL)
|
||||
{
|
||||
setnvalue(ra, cast_num(luaH_getn(h)));
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LUA_TSTRING:
|
||||
{
|
||||
setnvalue(ra, cast_num(tsvalue(rb)->len));
|
||||
break;
|
||||
TString* ts = tsvalue(rb);
|
||||
setnvalue(ra, cast_num(ts->len));
|
||||
return;
|
||||
}
|
||||
default:
|
||||
{ /* try metamethod */
|
||||
if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))
|
||||
luaG_typeerror(L, rb, "get length of");
|
||||
}
|
||||
tm = luaT_gettmbyobj(L, rb, TM_LEN);
|
||||
}
|
||||
|
||||
if (ttisnil(tm))
|
||||
luaG_typeerror(L, rb, "get length of");
|
||||
|
||||
StkId res = callTMres(L, ra, tm, rb, luaO_nilobject);
|
||||
if (!ttisnumber(res))
|
||||
luaG_runerror(L, "'__len' must return a number"); /* note, we can't access rb since stack may have been reallocated */
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue