v0.4.1+luau553
This commit is contained in:
parent
cea5950520
commit
a2c416bbb9
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "luau0-src"
|
name = "luau0-src"
|
||||||
version = "0.4.0+luau548"
|
version = "0.4.1+luau553"
|
||||||
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"
|
||||||
|
|
|
@ -58,6 +58,8 @@ struct Comment
|
||||||
struct ParseResult
|
struct ParseResult
|
||||||
{
|
{
|
||||||
AstStatBlock* root;
|
AstStatBlock* root;
|
||||||
|
size_t lines = 0;
|
||||||
|
|
||||||
std::vector<HotComment> hotcomments;
|
std::vector<HotComment> hotcomments;
|
||||||
std::vector<ParseError> errors;
|
std::vector<ParseError> errors;
|
||||||
|
|
||||||
|
|
|
@ -302,8 +302,8 @@ private:
|
||||||
AstStatError* reportStatError(const Location& location, const AstArray<AstExpr*>& expressions, const AstArray<AstStat*>& statements,
|
AstStatError* reportStatError(const Location& location, const AstArray<AstExpr*>& expressions, const AstArray<AstStat*>& statements,
|
||||||
const char* format, ...) LUAU_PRINTF_ATTR(5, 6);
|
const char* format, ...) LUAU_PRINTF_ATTR(5, 6);
|
||||||
AstExprError* reportExprError(const Location& location, const AstArray<AstExpr*>& expressions, const char* format, ...) LUAU_PRINTF_ATTR(4, 5);
|
AstExprError* reportExprError(const Location& location, const AstArray<AstExpr*>& expressions, const char* format, ...) LUAU_PRINTF_ATTR(4, 5);
|
||||||
AstTypeError* reportTypeAnnotationError(const Location& location, const AstArray<AstType*>& types, bool isMissing, const char* format, ...)
|
AstTypeError* reportTypeAnnotationError(const Location& location, const AstArray<AstType*>& types, const char* format, ...)
|
||||||
LUAU_PRINTF_ATTR(5, 6);
|
LUAU_PRINTF_ATTR(4, 5);
|
||||||
// `parseErrorLocation` is associated with the parser error
|
// `parseErrorLocation` is associated with the parser error
|
||||||
// `astErrorLocation` is associated with the AstTypeError created
|
// `astErrorLocation` is associated with the AstTypeError created
|
||||||
// It can be useful to have different error locations so that the parse error can include the next lexeme, while the AstTypeError can precisely
|
// It can be useful to have different error locations so that the parse error can include the next lexeme, while the AstTypeError can precisely
|
||||||
|
|
|
@ -1,17 +1,13 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "Luau/Common.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
|
||||||
#define LUAU_PRINTF_ATTR(fmt, arg) __attribute__((format(printf, fmt, arg)))
|
|
||||||
#else
|
|
||||||
#define LUAU_PRINTF_ATTR(fmt, arg)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
|
@ -641,8 +641,8 @@ Lexeme Lexer::readInterpolatedStringSection(Position start, Lexeme::Type formatT
|
||||||
return brokenDoubleBrace;
|
return brokenDoubleBrace;
|
||||||
}
|
}
|
||||||
|
|
||||||
Lexeme lexemeOutput(Location(start, position()), Lexeme::InterpStringBegin, &buffer[startOffset], offset - startOffset);
|
|
||||||
consume();
|
consume();
|
||||||
|
Lexeme lexemeOutput(Location(start, position()), Lexeme::InterpStringBegin, &buffer[startOffset], offset - startOffset - 1);
|
||||||
return lexemeOutput;
|
return lexemeOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,11 @@ LUAU_FASTFLAGVARIABLE(LuauErrorDoubleHexPrefix, false)
|
||||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseIntegerIssues, false)
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseIntegerIssues, false)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauInterpolatedStringBaseSupport, false)
|
LUAU_FASTFLAGVARIABLE(LuauInterpolatedStringBaseSupport, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypeAnnotationLocationChange, false)
|
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCommaParenWarnings, false)
|
LUAU_FASTFLAGVARIABLE(LuauCommaParenWarnings, false)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTableConstructorRecovery, false)
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauParserErrorsOnMissingDefaultTypePackArgument, false)
|
||||||
|
|
||||||
bool lua_telemetry_parsed_out_of_range_bin_integer = 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_out_of_range_hex_integer = false;
|
||||||
|
@ -164,15 +166,16 @@ ParseResult Parser::parse(const char* buffer, size_t bufferSize, AstNameTable& n
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
AstStatBlock* root = p.parseChunk();
|
AstStatBlock* root = p.parseChunk();
|
||||||
|
size_t lines = p.lexer.current().location.end.line + (bufferSize > 0 && buffer[bufferSize - 1] != '\n');
|
||||||
|
|
||||||
return ParseResult{root, std::move(p.hotcomments), std::move(p.parseErrors), std::move(p.commentLocations)};
|
return ParseResult{root, lines, std::move(p.hotcomments), std::move(p.parseErrors), std::move(p.commentLocations)};
|
||||||
}
|
}
|
||||||
catch (ParseError& err)
|
catch (ParseError& err)
|
||||||
{
|
{
|
||||||
// when catching a fatal error, append it to the list of non-fatal errors and return
|
// when catching a fatal error, append it to the list of non-fatal errors and return
|
||||||
p.parseErrors.push_back(err);
|
p.parseErrors.push_back(err);
|
||||||
|
|
||||||
return ParseResult{nullptr, {}, p.parseErrors};
|
return ParseResult{nullptr, 0, {}, p.parseErrors};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -811,9 +814,8 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod()
|
||||||
|
|
||||||
if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr)
|
if (args.size() == 0 || args[0].name.name != "self" || args[0].annotation != nullptr)
|
||||||
{
|
{
|
||||||
return AstDeclaredClassProp{fnName.name,
|
return AstDeclaredClassProp{
|
||||||
reportTypeAnnotationError(Location(start, end), {}, /*isMissing*/ false, "'self' must be present as the unannotated first parameter"),
|
fnName.name, reportTypeAnnotationError(Location(start, end), {}, "'self' must be present as the unannotated first parameter"), true};
|
||||||
true};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skip the first index.
|
// Skip the first index.
|
||||||
|
@ -824,8 +826,7 @@ AstDeclaredClassProp Parser::parseDeclaredClassMethod()
|
||||||
if (args[i].annotation)
|
if (args[i].annotation)
|
||||||
vars.push_back(args[i].annotation);
|
vars.push_back(args[i].annotation);
|
||||||
else
|
else
|
||||||
vars.push_back(reportTypeAnnotationError(
|
vars.push_back(reportTypeAnnotationError(Location(start, end), {}, "All declaration parameters aside from 'self' must be annotated"));
|
||||||
Location(start, end), {}, /*isMissing*/ false, "All declaration parameters aside from 'self' must be annotated"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vararg && !varargAnnotation)
|
if (vararg && !varargAnnotation)
|
||||||
|
@ -905,6 +906,25 @@ AstStat* Parser::parseDeclaration(const Location& start)
|
||||||
{
|
{
|
||||||
props.push_back(parseDeclaredClassMethod());
|
props.push_back(parseDeclaredClassMethod());
|
||||||
}
|
}
|
||||||
|
else if (lexer.current().type == '[')
|
||||||
|
{
|
||||||
|
const Lexeme begin = lexer.current();
|
||||||
|
nextLexeme(); // [
|
||||||
|
|
||||||
|
std::optional<AstArray<char>> chars = parseCharArray();
|
||||||
|
|
||||||
|
expectMatchAndConsume(']', begin);
|
||||||
|
expectAndConsume(':', "property type annotation");
|
||||||
|
AstType* type = parseTypeAnnotation();
|
||||||
|
|
||||||
|
// TODO: since AstName conains a char*, it can't contain null
|
||||||
|
bool containsNull = chars && (strnlen(chars->data, chars->size) < chars->size);
|
||||||
|
|
||||||
|
if (chars && !containsNull)
|
||||||
|
props.push_back(AstDeclaredClassProp{AstName(chars->data), type, false});
|
||||||
|
else
|
||||||
|
report(begin.location, "String literal contains malformed escape sequence");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Name propName = parseName("property name");
|
Name propName = parseName("property name");
|
||||||
|
@ -1518,7 +1538,7 @@ AstType* Parser::parseTypeAnnotation(TempVector<AstType*>& parts, const Location
|
||||||
|
|
||||||
if (isUnion && isIntersection)
|
if (isUnion && isIntersection)
|
||||||
{
|
{
|
||||||
return reportTypeAnnotationError(Location(begin, parts.back()->location), copy(parts), /*isMissing*/ false,
|
return reportTypeAnnotationError(Location(begin, parts.back()->location), copy(parts),
|
||||||
"Mixing union and intersection types is not allowed; consider wrapping in parentheses.");
|
"Mixing union and intersection types is not allowed; consider wrapping in parentheses.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1604,18 +1624,18 @@ AstTypeOrPack Parser::parseSimpleTypeAnnotation(bool allowPack)
|
||||||
return {allocator.alloc<AstTypeSingletonString>(start, svalue)};
|
return {allocator.alloc<AstTypeSingletonString>(start, svalue)};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
return {reportTypeAnnotationError(start, {}, /*isMissing*/ false, "String literal contains malformed escape sequence")};
|
return {reportTypeAnnotationError(start, {}, "String literal contains malformed escape sequence")};
|
||||||
}
|
}
|
||||||
else if (lexer.current().type == Lexeme::InterpStringBegin || lexer.current().type == Lexeme::InterpStringSimple)
|
else if (lexer.current().type == Lexeme::InterpStringBegin || lexer.current().type == Lexeme::InterpStringSimple)
|
||||||
{
|
{
|
||||||
parseInterpString();
|
parseInterpString();
|
||||||
|
|
||||||
return {reportTypeAnnotationError(start, {}, /*isMissing*/ false, "Interpolated string literals cannot be used as types")};
|
return {reportTypeAnnotationError(start, {}, "Interpolated string literals cannot be used as types")};
|
||||||
}
|
}
|
||||||
else if (lexer.current().type == Lexeme::BrokenString)
|
else if (lexer.current().type == Lexeme::BrokenString)
|
||||||
{
|
{
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
return {reportTypeAnnotationError(start, {}, /*isMissing*/ false, "Malformed string")};
|
return {reportTypeAnnotationError(start, {}, "Malformed string")};
|
||||||
}
|
}
|
||||||
else if (lexer.current().type == Lexeme::Name)
|
else if (lexer.current().type == Lexeme::Name)
|
||||||
{
|
{
|
||||||
|
@ -1674,14 +1694,12 @@ AstTypeOrPack Parser::parseSimpleTypeAnnotation(bool allowPack)
|
||||||
{
|
{
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
|
||||||
return {reportTypeAnnotationError(start, {}, /*isMissing*/ false,
|
return {reportTypeAnnotationError(start, {},
|
||||||
"Using 'function' as a type annotation is not supported, consider replacing with a function type annotation e.g. '(...any) -> "
|
"Using 'function' as a type annotation is not supported, consider replacing with a function type annotation e.g. '(...any) -> "
|
||||||
"...any'"),
|
"...any'"),
|
||||||
{}};
|
{}};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
if (FFlag::LuauTypeAnnotationLocationChange)
|
|
||||||
{
|
{
|
||||||
// For a missing type annotation, capture 'space' between last token and the next one
|
// For a missing type annotation, capture 'space' between last token and the next one
|
||||||
Location astErrorlocation(lexer.previousLocation().end, start.begin);
|
Location astErrorlocation(lexer.previousLocation().end, start.begin);
|
||||||
|
@ -1689,18 +1707,7 @@ AstTypeOrPack Parser::parseSimpleTypeAnnotation(bool allowPack)
|
||||||
// Including the current lexeme also makes the parse error consistent with other parse errors returned by Luau.
|
// Including the current lexeme also makes the parse error consistent with other parse errors returned by Luau.
|
||||||
Location parseErrorLocation(lexer.previousLocation().end, start.end);
|
Location parseErrorLocation(lexer.previousLocation().end, start.end);
|
||||||
return {
|
return {
|
||||||
reportMissingTypeAnnotationError(parseErrorLocation, astErrorlocation, "Expected type, got %s", lexer.current().toString().c_str()),
|
reportMissingTypeAnnotationError(parseErrorLocation, astErrorlocation, "Expected type, got %s", lexer.current().toString().c_str()), {}};
|
||||||
{}};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Location location = lexer.current().location;
|
|
||||||
|
|
||||||
// For a missing type annotation, capture 'space' between last token and the next one
|
|
||||||
location = Location(lexer.previousLocation().end, lexer.current().location.begin);
|
|
||||||
|
|
||||||
return {reportTypeAnnotationError(location, {}, /*isMissing*/ true, "Expected type, got %s", lexer.current().toString().c_str()), {}};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2306,9 +2313,13 @@ AstExpr* Parser::parseTableConstructor()
|
||||||
|
|
||||||
MatchLexeme matchBrace = lexer.current();
|
MatchLexeme matchBrace = lexer.current();
|
||||||
expectAndConsume('{', "table literal");
|
expectAndConsume('{', "table literal");
|
||||||
|
unsigned lastElementIndent = 0;
|
||||||
|
|
||||||
while (lexer.current().type != '}')
|
while (lexer.current().type != '}')
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauTableConstructorRecovery)
|
||||||
|
lastElementIndent = lexer.current().location.begin.column;
|
||||||
|
|
||||||
if (lexer.current().type == '[')
|
if (lexer.current().type == '[')
|
||||||
{
|
{
|
||||||
MatchLexeme matchLocationBracket = lexer.current();
|
MatchLexeme matchLocationBracket = lexer.current();
|
||||||
|
@ -2353,9 +2364,13 @@ AstExpr* Parser::parseTableConstructor()
|
||||||
{
|
{
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
}
|
}
|
||||||
else
|
else if (FFlag::LuauTableConstructorRecovery && (lexer.current().type == '[' || lexer.current().type == Lexeme::Name) &&
|
||||||
|
lexer.current().location.begin.column == lastElementIndent)
|
||||||
|
{
|
||||||
|
report(lexer.current().location, "Expected ',' after table constructor element");
|
||||||
|
}
|
||||||
|
else if (lexer.current().type != '}')
|
||||||
{
|
{
|
||||||
if (lexer.current().type != '}')
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2490,7 +2505,7 @@ std::pair<AstArray<AstGenericType>, AstArray<AstGenericTypePack>> Parser::parseG
|
||||||
|
|
||||||
namePacks.push_back({name, nameLocation, typePack});
|
namePacks.push_back({name, nameLocation, typePack});
|
||||||
}
|
}
|
||||||
else if (lexer.current().type == '(')
|
else if (!FFlag::LuauParserErrorsOnMissingDefaultTypePackArgument && lexer.current().type == '(')
|
||||||
{
|
{
|
||||||
auto [type, typePack] = parseTypeOrPackAnnotation();
|
auto [type, typePack] = parseTypeOrPackAnnotation();
|
||||||
|
|
||||||
|
@ -2499,6 +2514,15 @@ std::pair<AstArray<AstGenericType>, AstArray<AstGenericTypePack>> Parser::parseG
|
||||||
|
|
||||||
namePacks.push_back({name, nameLocation, typePack});
|
namePacks.push_back({name, nameLocation, typePack});
|
||||||
}
|
}
|
||||||
|
else if (FFlag::LuauParserErrorsOnMissingDefaultTypePackArgument)
|
||||||
|
{
|
||||||
|
auto [type, typePack] = parseTypeOrPackAnnotation();
|
||||||
|
|
||||||
|
if (type)
|
||||||
|
report(type->location, "Expected type pack after '=', got type");
|
||||||
|
|
||||||
|
namePacks.push_back({name, nameLocation, typePack});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -3014,27 +3038,18 @@ AstExprError* Parser::reportExprError(const Location& location, const AstArray<A
|
||||||
return allocator.alloc<AstExprError>(location, expressions, unsigned(parseErrors.size() - 1));
|
return allocator.alloc<AstExprError>(location, expressions, unsigned(parseErrors.size() - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
AstTypeError* Parser::reportTypeAnnotationError(const Location& location, const AstArray<AstType*>& types, bool isMissing, const char* format, ...)
|
AstTypeError* Parser::reportTypeAnnotationError(const Location& location, const AstArray<AstType*>& types, const char* format, ...)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauTypeAnnotationLocationChange)
|
|
||||||
{
|
|
||||||
// Missing type annotations should be using `reportMissingTypeAnnotationError` when LuauTypeAnnotationLocationChange is enabled
|
|
||||||
// Note: `isMissing` can be removed once FFlag::LuauTypeAnnotationLocationChange is removed since it will always be true.
|
|
||||||
LUAU_ASSERT(!isMissing);
|
|
||||||
}
|
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
report(location, format, args);
|
report(location, format, args);
|
||||||
va_end(args);
|
va_end(args);
|
||||||
|
|
||||||
return allocator.alloc<AstTypeError>(location, types, isMissing, unsigned(parseErrors.size() - 1));
|
return allocator.alloc<AstTypeError>(location, types, false, unsigned(parseErrors.size() - 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
AstTypeError* Parser::reportMissingTypeAnnotationError(const Location& parseErrorLocation, const Location& astErrorLocation, const char* format, ...)
|
AstTypeError* Parser::reportMissingTypeAnnotationError(const Location& parseErrorLocation, const Location& astErrorLocation, const char* format, ...)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauTypeAnnotationLocationChange);
|
|
||||||
|
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, format);
|
va_start(args, format);
|
||||||
report(parseErrorLocation, format, args);
|
report(parseErrorLocation, format, args);
|
||||||
|
|
|
@ -20,6 +20,10 @@
|
||||||
#define LUAU_DEBUGBREAK() __builtin_trap()
|
#define LUAU_DEBUGBREAK() __builtin_trap()
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__BYTE_ORDER__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
|
||||||
|
#define LUAU_BIG_ENDIAN
|
||||||
|
#endif
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -122,3 +126,9 @@ FValue<T>* FValue<T>::list = nullptr;
|
||||||
{ \
|
{ \
|
||||||
Luau::FValue<int> flag(#flag, def, true); \
|
Luau::FValue<int> flag(#flag, def, true); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(__GNUC__)
|
||||||
|
#define LUAU_PRINTF_ATTR(fmt, arg) __attribute__((format(printf, fmt, arg)))
|
||||||
|
#else
|
||||||
|
#define LUAU_PRINTF_ATTR(fmt, arg)
|
||||||
|
#endif
|
||||||
|
|
|
@ -11,9 +11,10 @@ inline bool isFlagExperimental(const char* flag)
|
||||||
// Flags in this list are disabled by default in various command-line tools. They may have behavior that is not fully final,
|
// Flags in this list are disabled by default in various command-line tools. They may have behavior that is not fully final,
|
||||||
// or critical bugs that are found after the code has been submitted.
|
// or critical bugs that are found after the code has been submitted.
|
||||||
static const char* kList[] = {
|
static const char* kList[] = {
|
||||||
"LuauLowerBoundsCalculation",
|
|
||||||
"LuauInterpolatedStringBaseSupport",
|
"LuauInterpolatedStringBaseSupport",
|
||||||
"LuauInstantiateInSubtyping", // requires some fixes to lua-apps code
|
"LuauInstantiateInSubtyping", // requires some fixes to lua-apps code
|
||||||
|
"LuauOptionalNextKey", // waiting for a fix to land in lua-apps
|
||||||
|
"LuauTryhardAnd", // waiting for a fix in graphql-lua -> apollo-client-lia -> lua-apps
|
||||||
// makes sure we always have at least one entry
|
// makes sure we always have at least one entry
|
||||||
nullptr,
|
nullptr,
|
||||||
};
|
};
|
||||||
|
|
|
@ -102,6 +102,11 @@ public:
|
||||||
|
|
||||||
void setDumpSource(const std::string& source);
|
void setDumpSource(const std::string& source);
|
||||||
|
|
||||||
|
bool needsDebugRemarks() const
|
||||||
|
{
|
||||||
|
return (dumpFlags & Dump_Remarks) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
const std::string& getBytecode() const
|
const std::string& getBytecode() const
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(!bytecode.empty()); // did you forget to call finalize?
|
LUAU_ASSERT(!bytecode.empty()); // did you forget to call finalize?
|
||||||
|
@ -110,6 +115,9 @@ public:
|
||||||
|
|
||||||
std::string dumpFunction(uint32_t id) const;
|
std::string dumpFunction(uint32_t id) const;
|
||||||
std::string dumpEverything() const;
|
std::string dumpEverything() const;
|
||||||
|
std::string dumpSourceRemarks() const;
|
||||||
|
|
||||||
|
void annotateInstruction(std::string& result, uint32_t fid, uint32_t instpos) const;
|
||||||
|
|
||||||
static uint32_t getImportId(int32_t id0);
|
static uint32_t getImportId(int32_t id0);
|
||||||
static uint32_t getImportId(int32_t id0, int32_t id1);
|
static uint32_t getImportId(int32_t id0, int32_t id1);
|
||||||
|
@ -173,6 +181,7 @@ private:
|
||||||
|
|
||||||
std::string dump;
|
std::string dump;
|
||||||
std::string dumpname;
|
std::string dumpname;
|
||||||
|
std::vector<int> dumpinstoffs;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DebugLocal
|
struct DebugLocal
|
||||||
|
@ -243,12 +252,15 @@ private:
|
||||||
|
|
||||||
uint32_t dumpFlags = 0;
|
uint32_t dumpFlags = 0;
|
||||||
std::vector<std::string> dumpSource;
|
std::vector<std::string> dumpSource;
|
||||||
|
std::vector<std::pair<int, std::string>> dumpRemarks;
|
||||||
|
|
||||||
std::string (BytecodeBuilder::*dumpFunctionPtr)() const = nullptr;
|
std::string (BytecodeBuilder::*dumpFunctionPtr)(std::vector<int>&) const = nullptr;
|
||||||
|
|
||||||
void validate() const;
|
void validate() const;
|
||||||
|
void validateInstructions() const;
|
||||||
|
void validateVariadic() const;
|
||||||
|
|
||||||
std::string dumpCurrentFunction() const;
|
std::string dumpCurrentFunction(std::vector<int>& dumpinstoffs) const;
|
||||||
void dumpInstruction(const uint32_t* opcode, std::string& output, int targetLabel) const;
|
void dumpInstruction(const uint32_t* opcode, std::string& output, int targetLabel) const;
|
||||||
|
|
||||||
void writeFunction(std::string& ss, uint32_t id) const;
|
void writeFunction(std::string& ss, uint32_t id) const;
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
#include "Luau/Bytecode.h"
|
#include "Luau/Bytecode.h"
|
||||||
#include "Luau/Compiler.h"
|
#include "Luau/Compiler.h"
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompileBuiltinMT, false)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace Compile
|
namespace Compile
|
||||||
|
@ -66,13 +64,10 @@ static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& op
|
||||||
if (builtin.isGlobal("select"))
|
if (builtin.isGlobal("select"))
|
||||||
return LBF_SELECT_VARARG;
|
return LBF_SELECT_VARARG;
|
||||||
|
|
||||||
if (FFlag::LuauCompileBuiltinMT)
|
|
||||||
{
|
|
||||||
if (builtin.isGlobal("getmetatable"))
|
if (builtin.isGlobal("getmetatable"))
|
||||||
return LBF_GETMETATABLE;
|
return LBF_GETMETATABLE;
|
||||||
if (builtin.isGlobal("setmetatable"))
|
if (builtin.isGlobal("setmetatable"))
|
||||||
return LBF_SETMETATABLE;
|
return LBF_SETMETATABLE;
|
||||||
}
|
|
||||||
|
|
||||||
if (builtin.object == "math")
|
if (builtin.object == "math")
|
||||||
{
|
{
|
||||||
|
|
|
@ -269,7 +269,7 @@ void BytecodeBuilder::endFunction(uint8_t maxstacksize, uint8_t numupvalues)
|
||||||
|
|
||||||
// this call is indirect to make sure we only gain link time dependency on dumpCurrentFunction when needed
|
// this call is indirect to make sure we only gain link time dependency on dumpCurrentFunction when needed
|
||||||
if (dumpFunctionPtr)
|
if (dumpFunctionPtr)
|
||||||
func.dump = (this->*dumpFunctionPtr)();
|
func.dump = (this->*dumpFunctionPtr)(func.dumpinstoffs);
|
||||||
|
|
||||||
insns.clear();
|
insns.clear();
|
||||||
lines.clear();
|
lines.clear();
|
||||||
|
@ -572,6 +572,7 @@ void BytecodeBuilder::addDebugRemark(const char* format, ...)
|
||||||
debugRemarkBuffer += '\0';
|
debugRemarkBuffer += '\0';
|
||||||
|
|
||||||
debugRemarks.emplace_back(uint32_t(insns.size()), uint32_t(offset));
|
debugRemarks.emplace_back(uint32_t(insns.size()), uint32_t(offset));
|
||||||
|
dumpRemarks.emplace_back(debugLine, debugRemarkBuffer.c_str() + offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BytecodeBuilder::finalize()
|
void BytecodeBuilder::finalize()
|
||||||
|
@ -1077,6 +1078,12 @@ uint8_t BytecodeBuilder::getVersion()
|
||||||
|
|
||||||
#ifdef LUAU_ASSERTENABLED
|
#ifdef LUAU_ASSERTENABLED
|
||||||
void BytecodeBuilder::validate() const
|
void BytecodeBuilder::validate() const
|
||||||
|
{
|
||||||
|
validateInstructions();
|
||||||
|
validateVariadic();
|
||||||
|
}
|
||||||
|
|
||||||
|
void BytecodeBuilder::validateInstructions() const
|
||||||
{
|
{
|
||||||
#define VREG(v) LUAU_ASSERT(unsigned(v) < func.maxstacksize)
|
#define VREG(v) LUAU_ASSERT(unsigned(v) < func.maxstacksize)
|
||||||
#define VREGRANGE(v, count) LUAU_ASSERT(unsigned(v + (count < 0 ? 0 : count)) <= func.maxstacksize)
|
#define VREGRANGE(v, count) LUAU_ASSERT(unsigned(v + (count < 0 ? 0 : count)) <= func.maxstacksize)
|
||||||
|
@ -1089,26 +1096,27 @@ void BytecodeBuilder::validate() const
|
||||||
|
|
||||||
const Function& func = functions[currentFunction];
|
const Function& func = functions[currentFunction];
|
||||||
|
|
||||||
// first pass: tag instruction offsets so that we can validate jumps
|
// tag instruction offsets so that we can validate jumps
|
||||||
std::vector<uint8_t> insnvalid(insns.size(), false);
|
std::vector<uint8_t> insnvalid(insns.size(), 0);
|
||||||
|
|
||||||
for (size_t i = 0; i < insns.size();)
|
for (size_t i = 0; i < insns.size();)
|
||||||
{
|
{
|
||||||
uint8_t op = LUAU_INSN_OP(insns[i]);
|
uint32_t insn = insns[i];
|
||||||
|
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn));
|
||||||
|
|
||||||
insnvalid[i] = true;
|
insnvalid[i] = true;
|
||||||
|
|
||||||
i += getOpLength(LuauOpcode(op));
|
i += getOpLength(op);
|
||||||
LUAU_ASSERT(i <= insns.size());
|
LUAU_ASSERT(i <= insns.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> openCaptures;
|
std::vector<uint8_t> openCaptures;
|
||||||
|
|
||||||
// second pass: validate the rest of the bytecode
|
// validate individual instructions
|
||||||
for (size_t i = 0; i < insns.size();)
|
for (size_t i = 0; i < insns.size();)
|
||||||
{
|
{
|
||||||
uint32_t insn = insns[i];
|
uint32_t insn = insns[i];
|
||||||
uint8_t op = LUAU_INSN_OP(insn);
|
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn));
|
||||||
|
|
||||||
switch (op)
|
switch (op)
|
||||||
{
|
{
|
||||||
|
@ -1451,7 +1459,7 @@ void BytecodeBuilder::validate() const
|
||||||
LUAU_ASSERT(!"Unsupported opcode");
|
LUAU_ASSERT(!"Unsupported opcode");
|
||||||
}
|
}
|
||||||
|
|
||||||
i += getOpLength(LuauOpcode(op));
|
i += getOpLength(op);
|
||||||
LUAU_ASSERT(i <= insns.size());
|
LUAU_ASSERT(i <= insns.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1468,6 +1476,126 @@ void BytecodeBuilder::validate() const
|
||||||
#undef VCONSTANY
|
#undef VCONSTANY
|
||||||
#undef VJUMP
|
#undef VJUMP
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BytecodeBuilder::validateVariadic() const
|
||||||
|
{
|
||||||
|
// validate MULTRET sequences: instructions that produce a variadic sequence and consume one must come in pairs
|
||||||
|
// we classify instructions into four groups: producers, consumers, neutral and others
|
||||||
|
// any producer (an instruction that produces more than one value) must be followed by 0 or more neutral instructions
|
||||||
|
// and a consumer (that consumes more than one value); these form a variadic sequence.
|
||||||
|
// except for producer, no instruction in the variadic sequence may be a jump target.
|
||||||
|
// from the execution perspective, producer adjusts L->top to point to one past the last result, neutral instructions
|
||||||
|
// leave L->top unmodified, and consumer adjusts L->top back to the stack frame end.
|
||||||
|
// consumers invalidate all values after L->top after they execute (which we currently don't validate)
|
||||||
|
bool variadicSeq = false;
|
||||||
|
|
||||||
|
std::vector<uint8_t> insntargets(insns.size(), 0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < insns.size();)
|
||||||
|
{
|
||||||
|
uint32_t insn = insns[i];
|
||||||
|
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn));
|
||||||
|
|
||||||
|
int target = getJumpTarget(insn, uint32_t(i));
|
||||||
|
|
||||||
|
if (target >= 0 && !isFastCall(op))
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(unsigned(target) < insns.size());
|
||||||
|
|
||||||
|
insntargets[target] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
i += getOpLength(op);
|
||||||
|
LUAU_ASSERT(i <= insns.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t i = 0; i < insns.size();)
|
||||||
|
{
|
||||||
|
uint32_t insn = insns[i];
|
||||||
|
LuauOpcode op = LuauOpcode(LUAU_INSN_OP(insn));
|
||||||
|
|
||||||
|
if (variadicSeq)
|
||||||
|
{
|
||||||
|
// no instruction inside the sequence, including the consumer, may be a jump target
|
||||||
|
// this guarantees uninterrupted L->top adjustment flow
|
||||||
|
LUAU_ASSERT(!insntargets[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (op == LOP_CALL)
|
||||||
|
{
|
||||||
|
// note: calls may end one variadic sequence and start a new one
|
||||||
|
|
||||||
|
if (LUAU_INSN_B(insn) == 0)
|
||||||
|
{
|
||||||
|
// consumer instruction ens a variadic sequence
|
||||||
|
LUAU_ASSERT(variadicSeq);
|
||||||
|
variadicSeq = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// CALL is not a neutral instruction so it can't be present in a variadic sequence unless it's a consumer
|
||||||
|
LUAU_ASSERT(!variadicSeq);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (LUAU_INSN_C(insn) == 0)
|
||||||
|
{
|
||||||
|
// producer instruction starts a variadic sequence
|
||||||
|
LUAU_ASSERT(!variadicSeq);
|
||||||
|
variadicSeq = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (op == LOP_GETVARARGS && LUAU_INSN_B(insn) == 0)
|
||||||
|
{
|
||||||
|
// producer instruction starts a variadic sequence
|
||||||
|
LUAU_ASSERT(!variadicSeq);
|
||||||
|
variadicSeq = true;
|
||||||
|
}
|
||||||
|
else if ((op == LOP_RETURN && LUAU_INSN_B(insn) == 0) || (op == LOP_SETLIST && LUAU_INSN_C(insn) == 0))
|
||||||
|
{
|
||||||
|
// consumer instruction ends a variadic sequence
|
||||||
|
LUAU_ASSERT(variadicSeq);
|
||||||
|
variadicSeq = false;
|
||||||
|
}
|
||||||
|
else if (op == LOP_FASTCALL)
|
||||||
|
{
|
||||||
|
int callTarget = int(i + LUAU_INSN_C(insn) + 1);
|
||||||
|
LUAU_ASSERT(unsigned(callTarget) < insns.size() && LUAU_INSN_OP(insns[callTarget]) == LOP_CALL);
|
||||||
|
|
||||||
|
if (LUAU_INSN_B(insns[callTarget]) == 0)
|
||||||
|
{
|
||||||
|
// consumer instruction ends a variadic sequence; however, we can't terminate it yet because future analysis of CALL will do it
|
||||||
|
// during FASTCALL fallback, the instructions between this and CALL consumer are going to be executed before L->top so they must
|
||||||
|
// be neutral; as such, we will defer termination of variadic sequence until CALL analysis
|
||||||
|
LUAU_ASSERT(variadicSeq);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// FASTCALL is not a neutral instruction so it can't be present in a variadic sequence unless it's linked to CALL consumer
|
||||||
|
LUAU_ASSERT(!variadicSeq);
|
||||||
|
}
|
||||||
|
|
||||||
|
// note: if FASTCALL is linked to a CALL producer, the instructions between FASTCALL and CALL are technically not part of an executed
|
||||||
|
// variadic sequence since they are never executed if FASTCALL does anything, so it's okay to skip their validation until CALL
|
||||||
|
// (we can't simply start a variadic sequence here because that would trigger assertions during linked CALL validation)
|
||||||
|
}
|
||||||
|
else if (op == LOP_CLOSEUPVALS || op == LOP_NAMECALL || op == LOP_GETIMPORT || op == LOP_MOVE || op == LOP_GETUPVAL || op == LOP_GETGLOBAL ||
|
||||||
|
op == LOP_GETTABLEKS || op == LOP_COVERAGE)
|
||||||
|
{
|
||||||
|
// instructions inside a variadic sequence must be neutral (can't change L->top)
|
||||||
|
// while there are many neutral instructions like this, here we check that the instruction is one of the few
|
||||||
|
// that we'd expect to exist in FASTCALL fallback sequences or between consecutive CALLs for encoding reasons
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(!variadicSeq);
|
||||||
|
}
|
||||||
|
|
||||||
|
i += getOpLength(op);
|
||||||
|
LUAU_ASSERT(i <= insns.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
LUAU_ASSERT(!variadicSeq);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, int targetLabel) const
|
void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result, int targetLabel) const
|
||||||
|
@ -1799,7 +1927,7 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string BytecodeBuilder::dumpCurrentFunction() const
|
std::string BytecodeBuilder::dumpCurrentFunction(std::vector<int>& dumpinstoffs) const
|
||||||
{
|
{
|
||||||
if ((dumpFlags & Dump_Code) == 0)
|
if ((dumpFlags & Dump_Code) == 0)
|
||||||
return std::string();
|
return std::string();
|
||||||
|
@ -1849,11 +1977,15 @@ std::string BytecodeBuilder::dumpCurrentFunction() const
|
||||||
if (labels[i] == 0)
|
if (labels[i] == 0)
|
||||||
labels[i] = nextLabel++;
|
labels[i] = nextLabel++;
|
||||||
|
|
||||||
|
dumpinstoffs.resize(insns.size() + 1, -1);
|
||||||
|
|
||||||
for (size_t i = 0; i < insns.size();)
|
for (size_t i = 0; i < insns.size();)
|
||||||
{
|
{
|
||||||
const uint32_t* code = &insns[i];
|
const uint32_t* code = &insns[i];
|
||||||
uint8_t op = LUAU_INSN_OP(*code);
|
uint8_t op = LUAU_INSN_OP(*code);
|
||||||
|
|
||||||
|
dumpinstoffs[i] = int(result.size());
|
||||||
|
|
||||||
if (op == LOP_PREPVARARGS)
|
if (op == LOP_PREPVARARGS)
|
||||||
{
|
{
|
||||||
// Don't emit function header in bytecode - it's used for call dispatching and doesn't contain "interesting" information
|
// Don't emit function header in bytecode - it's used for call dispatching and doesn't contain "interesting" information
|
||||||
|
@ -1896,6 +2028,8 @@ std::string BytecodeBuilder::dumpCurrentFunction() const
|
||||||
LUAU_ASSERT(i <= insns.size());
|
LUAU_ASSERT(i <= insns.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dumpinstoffs[insns.size()] = int(result.size());
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1949,4 +2083,62 @@ std::string BytecodeBuilder::dumpEverything() const
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string BytecodeBuilder::dumpSourceRemarks() const
|
||||||
|
{
|
||||||
|
std::string result;
|
||||||
|
|
||||||
|
size_t nextRemark = 0;
|
||||||
|
|
||||||
|
std::vector<std::pair<int, std::string>> remarks = dumpRemarks;
|
||||||
|
std::sort(remarks.begin(), remarks.end());
|
||||||
|
|
||||||
|
for (size_t i = 0; i < dumpSource.size(); ++i)
|
||||||
|
{
|
||||||
|
const std::string& line = dumpSource[i];
|
||||||
|
|
||||||
|
size_t indent = 0;
|
||||||
|
while (indent < line.length() && (line[indent] == ' ' || line[indent] == '\t'))
|
||||||
|
indent++;
|
||||||
|
|
||||||
|
while (nextRemark < remarks.size() && remarks[nextRemark].first == int(i + 1))
|
||||||
|
{
|
||||||
|
formatAppend(result, "%.*s-- remark: %s\n", int(indent), line.c_str(), remarks[nextRemark].second.c_str());
|
||||||
|
nextRemark++;
|
||||||
|
|
||||||
|
// skip duplicate remarks (due to inlining/unrolling)
|
||||||
|
while (nextRemark < remarks.size() && remarks[nextRemark] == remarks[nextRemark - 1])
|
||||||
|
nextRemark++;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += line;
|
||||||
|
|
||||||
|
if (i + 1 < dumpSource.size())
|
||||||
|
result += '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void BytecodeBuilder::annotateInstruction(std::string& result, uint32_t fid, uint32_t instpos) const
|
||||||
|
{
|
||||||
|
if ((dumpFlags & Dump_Code) == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LUAU_ASSERT(fid < functions.size());
|
||||||
|
|
||||||
|
const Function& function = functions[fid];
|
||||||
|
const std::string& dump = function.dump;
|
||||||
|
const std::vector<int>& dumpinstoffs = function.dumpinstoffs;
|
||||||
|
|
||||||
|
uint32_t next = instpos + 1;
|
||||||
|
|
||||||
|
LUAU_ASSERT(next < dumpinstoffs.size());
|
||||||
|
|
||||||
|
// Skip locations of multi-dword instructions
|
||||||
|
while (next < dumpinstoffs.size() && dumpinstoffs[next] == -1)
|
||||||
|
next++;
|
||||||
|
|
||||||
|
formatAppend(result, "%.*s", dumpinstoffs[next] - dumpinstoffs[instpos], dump.data() + dumpinstoffs[instpos]);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Luau
|
} // namespace Luau
|
||||||
|
|
|
@ -709,6 +709,17 @@ struct Compiler
|
||||||
if (const int* id = builtins.find(expr))
|
if (const int* id = builtins.find(expr))
|
||||||
bfid = *id;
|
bfid = *id;
|
||||||
|
|
||||||
|
if (bfid >= 0 && bytecode.needsDebugRemarks())
|
||||||
|
{
|
||||||
|
Builtin builtin = getBuiltin(expr->func, globals, variables);
|
||||||
|
bool lastMult = expr->args.size > 0 && isExprMultRet(expr->args.data[expr->args.size - 1]);
|
||||||
|
|
||||||
|
if (builtin.object.value)
|
||||||
|
bytecode.addDebugRemark("builtin %s.%s/%d%s", builtin.object.value, builtin.method.value, int(expr->args.size), lastMult ? "+" : "");
|
||||||
|
else if (builtin.method.value)
|
||||||
|
bytecode.addDebugRemark("builtin %s/%d%s", builtin.method.value, int(expr->args.size), lastMult ? "+" : "");
|
||||||
|
}
|
||||||
|
|
||||||
if (bfid == LBF_SELECT_VARARG)
|
if (bfid == LBF_SELECT_VARARG)
|
||||||
{
|
{
|
||||||
// Optimization: compile select(_, ...) as FASTCALL1; the builtin will read variadic arguments directly
|
// Optimization: compile select(_, ...) as FASTCALL1; the builtin will read variadic arguments directly
|
||||||
|
@ -918,6 +929,9 @@ struct Compiler
|
||||||
shared = int16_t(cid);
|
shared = int16_t(cid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (shared < 0)
|
||||||
|
bytecode.addDebugRemark("allocation: closure with %d upvalues", int(captures.size()));
|
||||||
|
|
||||||
if (shared >= 0)
|
if (shared >= 0)
|
||||||
bytecode.emitAD(LOP_DUPCLOSURE, target, shared);
|
bytecode.emitAD(LOP_DUPCLOSURE, target, shared);
|
||||||
else
|
else
|
||||||
|
@ -1599,6 +1613,8 @@ struct Compiler
|
||||||
{
|
{
|
||||||
TableShape shape = tableShapes[expr];
|
TableShape shape = tableShapes[expr];
|
||||||
|
|
||||||
|
bytecode.addDebugRemark("allocation: table hash %d", shape.hashSize);
|
||||||
|
|
||||||
bytecode.emitABC(LOP_NEWTABLE, target, encodeHashSize(shape.hashSize), 0);
|
bytecode.emitABC(LOP_NEWTABLE, target, encodeHashSize(shape.hashSize), 0);
|
||||||
bytecode.emitAux(shape.arraySize);
|
bytecode.emitAux(shape.arraySize);
|
||||||
return;
|
return;
|
||||||
|
@ -1671,6 +1687,8 @@ struct Compiler
|
||||||
if (tid < 0)
|
if (tid < 0)
|
||||||
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
|
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
|
||||||
|
|
||||||
|
bytecode.addDebugRemark("allocation: table template %d", hashSize);
|
||||||
|
|
||||||
if (tid < 32768)
|
if (tid < 32768)
|
||||||
{
|
{
|
||||||
bytecode.emitAD(LOP_DUPTABLE, reg, int16_t(tid));
|
bytecode.emitAD(LOP_DUPTABLE, reg, int16_t(tid));
|
||||||
|
@ -1690,8 +1708,17 @@ struct Compiler
|
||||||
bool trailingVarargs = last && last->kind == AstExprTable::Item::List && last->value->is<AstExprVarargs>();
|
bool trailingVarargs = last && last->kind == AstExprTable::Item::List && last->value->is<AstExprVarargs>();
|
||||||
LUAU_ASSERT(!trailingVarargs || arraySize > 0);
|
LUAU_ASSERT(!trailingVarargs || arraySize > 0);
|
||||||
|
|
||||||
|
unsigned int arrayAllocation = arraySize - trailingVarargs + indexSize;
|
||||||
|
|
||||||
|
if (hashSize == 0)
|
||||||
|
bytecode.addDebugRemark("allocation: table array %d", arrayAllocation);
|
||||||
|
else if (arrayAllocation == 0)
|
||||||
|
bytecode.addDebugRemark("allocation: table hash %d", hashSize);
|
||||||
|
else
|
||||||
|
bytecode.addDebugRemark("allocation: table hash %d array %d", hashSize, arrayAllocation);
|
||||||
|
|
||||||
bytecode.emitABC(LOP_NEWTABLE, reg, uint8_t(encodedHashSize), 0);
|
bytecode.emitABC(LOP_NEWTABLE, reg, uint8_t(encodedHashSize), 0);
|
||||||
bytecode.emitAux(arraySize - trailingVarargs + indexSize);
|
bytecode.emitAux(arrayAllocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int arrayChunkSize = std::min(16u, arraySize);
|
unsigned int arrayChunkSize = std::min(16u, arraySize);
|
||||||
|
|
|
@ -133,4 +133,4 @@
|
||||||
|
|
||||||
#define LUA_VECTOR_SIZE 3 // must be 3 or 4
|
#define LUA_VECTOR_SIZE 3 // must be 3 or 4
|
||||||
|
|
||||||
#define LUA_EXTRA_SIZE LUA_VECTOR_SIZE - 2
|
#define LUA_EXTRA_SIZE (LUA_VECTOR_SIZE - 2)
|
||||||
|
|
|
@ -263,11 +263,14 @@ static int luauF_log(lua_State* L, StkId res, TValue* arg0, int nresults, StkId
|
||||||
|
|
||||||
static int luauF_max(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
static int luauF_max(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||||
{
|
{
|
||||||
if (nparams >= 1 && nresults <= 1 && ttisnumber(arg0))
|
if (nparams >= 2 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args))
|
||||||
{
|
{
|
||||||
double r = nvalue(arg0);
|
double a1 = nvalue(arg0);
|
||||||
|
double a2 = nvalue(args);
|
||||||
|
|
||||||
for (int i = 2; i <= nparams; ++i)
|
double r = (a2 > a1) ? a2 : a1;
|
||||||
|
|
||||||
|
for (int i = 3; i <= nparams; ++i)
|
||||||
{
|
{
|
||||||
if (!ttisnumber(args + (i - 2)))
|
if (!ttisnumber(args + (i - 2)))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -286,11 +289,14 @@ static int luauF_max(lua_State* L, StkId res, TValue* arg0, int nresults, StkId
|
||||||
|
|
||||||
static int luauF_min(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
static int luauF_min(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||||
{
|
{
|
||||||
if (nparams >= 1 && nresults <= 1 && ttisnumber(arg0))
|
if (nparams >= 2 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args))
|
||||||
{
|
{
|
||||||
double r = nvalue(arg0);
|
double a1 = nvalue(arg0);
|
||||||
|
double a2 = nvalue(args);
|
||||||
|
|
||||||
for (int i = 2; i <= nparams; ++i)
|
double r = (a2 < a1) ? a2 : a1;
|
||||||
|
|
||||||
|
for (int i = 3; i <= nparams; ++i)
|
||||||
{
|
{
|
||||||
if (!ttisnumber(args + (i - 2)))
|
if (!ttisnumber(args + (i - 2)))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -439,22 +445,18 @@ static int luauF_arshift(lua_State* L, StkId res, TValue* arg0, int nresults, St
|
||||||
|
|
||||||
static int luauF_band(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
static int luauF_band(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||||
{
|
{
|
||||||
if (nparams >= 1 && nresults <= 1)
|
if (nparams >= 2 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args))
|
||||||
{
|
|
||||||
uint32_t r = ~0u;
|
|
||||||
|
|
||||||
if (!ttisnumber(arg0))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
double a1 = nvalue(arg0);
|
double a1 = nvalue(arg0);
|
||||||
unsigned u;
|
double a2 = nvalue(args);
|
||||||
luai_num2unsigned(u, a1);
|
|
||||||
|
|
||||||
r &= u;
|
unsigned u1, u2;
|
||||||
}
|
luai_num2unsigned(u1, a1);
|
||||||
|
luai_num2unsigned(u2, a2);
|
||||||
|
|
||||||
for (int i = 2; i <= nparams; ++i)
|
uint32_t r = u1 & u2;
|
||||||
|
|
||||||
|
for (int i = 3; i <= nparams; ++i)
|
||||||
{
|
{
|
||||||
if (!ttisnumber(args + (i - 2)))
|
if (!ttisnumber(args + (i - 2)))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -492,22 +494,18 @@ static int luauF_bnot(lua_State* L, StkId res, TValue* arg0, int nresults, StkId
|
||||||
|
|
||||||
static int luauF_bor(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
static int luauF_bor(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||||
{
|
{
|
||||||
if (nparams >= 1 && nresults <= 1)
|
if (nparams >= 2 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args))
|
||||||
{
|
|
||||||
uint32_t r = 0;
|
|
||||||
|
|
||||||
if (!ttisnumber(arg0))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
double a1 = nvalue(arg0);
|
double a1 = nvalue(arg0);
|
||||||
unsigned u;
|
double a2 = nvalue(args);
|
||||||
luai_num2unsigned(u, a1);
|
|
||||||
|
|
||||||
r |= u;
|
unsigned u1, u2;
|
||||||
}
|
luai_num2unsigned(u1, a1);
|
||||||
|
luai_num2unsigned(u2, a2);
|
||||||
|
|
||||||
for (int i = 2; i <= nparams; ++i)
|
uint32_t r = u1 | u2;
|
||||||
|
|
||||||
|
for (int i = 3; i <= nparams; ++i)
|
||||||
{
|
{
|
||||||
if (!ttisnumber(args + (i - 2)))
|
if (!ttisnumber(args + (i - 2)))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -528,22 +526,18 @@ static int luauF_bor(lua_State* L, StkId res, TValue* arg0, int nresults, StkId
|
||||||
|
|
||||||
static int luauF_bxor(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
static int luauF_bxor(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||||
{
|
{
|
||||||
if (nparams >= 1 && nresults <= 1)
|
if (nparams >= 2 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args))
|
||||||
{
|
|
||||||
uint32_t r = 0;
|
|
||||||
|
|
||||||
if (!ttisnumber(arg0))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
double a1 = nvalue(arg0);
|
double a1 = nvalue(arg0);
|
||||||
unsigned u;
|
double a2 = nvalue(args);
|
||||||
luai_num2unsigned(u, a1);
|
|
||||||
|
|
||||||
r ^= u;
|
unsigned u1, u2;
|
||||||
}
|
luai_num2unsigned(u1, a1);
|
||||||
|
luai_num2unsigned(u2, a2);
|
||||||
|
|
||||||
for (int i = 2; i <= nparams; ++i)
|
uint32_t r = u1 ^ u2;
|
||||||
|
|
||||||
|
for (int i = 3; i <= nparams; ++i)
|
||||||
{
|
{
|
||||||
if (!ttisnumber(args + (i - 2)))
|
if (!ttisnumber(args + (i - 2)))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -564,22 +558,18 @@ static int luauF_bxor(lua_State* L, StkId res, TValue* arg0, int nresults, StkId
|
||||||
|
|
||||||
static int luauF_btest(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
static int luauF_btest(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||||
{
|
{
|
||||||
if (nparams >= 1 && nresults <= 1)
|
if (nparams >= 2 && nresults <= 1 && ttisnumber(arg0) && ttisnumber(args))
|
||||||
{
|
|
||||||
uint32_t r = ~0u;
|
|
||||||
|
|
||||||
if (!ttisnumber(arg0))
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
{
|
{
|
||||||
double a1 = nvalue(arg0);
|
double a1 = nvalue(arg0);
|
||||||
unsigned u;
|
double a2 = nvalue(args);
|
||||||
luai_num2unsigned(u, a1);
|
|
||||||
|
|
||||||
r &= u;
|
unsigned u1, u2;
|
||||||
}
|
luai_num2unsigned(u1, a1);
|
||||||
|
luai_num2unsigned(u2, a2);
|
||||||
|
|
||||||
for (int i = 2; i <= nparams; ++i)
|
uint32_t r = u1 & u2;
|
||||||
|
|
||||||
|
for (int i = 3; i <= nparams; ++i)
|
||||||
{
|
{
|
||||||
if (!ttisnumber(args + (i - 2)))
|
if (!ttisnumber(args + (i - 2)))
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1249,7 +1239,12 @@ static int luauF_setmetatable(lua_State* L, StkId res, TValue* arg0, int nresult
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
luau_FastFunction luauF_table[256] = {
|
static int luauF_missing(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const luau_FastFunction luauF_table[256] = {
|
||||||
NULL,
|
NULL,
|
||||||
luauF_assert,
|
luauF_assert,
|
||||||
|
|
||||||
|
@ -1327,4 +1322,20 @@ luau_FastFunction luauF_table[256] = {
|
||||||
|
|
||||||
luauF_getmetatable,
|
luauF_getmetatable,
|
||||||
luauF_setmetatable,
|
luauF_setmetatable,
|
||||||
|
|
||||||
|
// When adding builtins, add them above this line; what follows is 64 "dummy" entries with luauF_missing fallback.
|
||||||
|
// This is important so that older versions of the runtime that don't support newer builtins automatically fall back via luauF_missing.
|
||||||
|
// Given the builtin addition velocity this should always provide a larger compatibility window than bytecode versions suggest.
|
||||||
|
#define MISSING8 luauF_missing, luauF_missing, luauF_missing, luauF_missing, luauF_missing, luauF_missing, luauF_missing, luauF_missing
|
||||||
|
|
||||||
|
MISSING8,
|
||||||
|
MISSING8,
|
||||||
|
MISSING8,
|
||||||
|
MISSING8,
|
||||||
|
MISSING8,
|
||||||
|
MISSING8,
|
||||||
|
MISSING8,
|
||||||
|
MISSING8,
|
||||||
|
|
||||||
|
#undef MISSING8
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,4 +6,4 @@
|
||||||
|
|
||||||
typedef int (*luau_FastFunction)(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams);
|
typedef int (*luau_FastFunction)(lua_State* L, StkId res, TValue* arg0, int nresults, StkId args, int nparams);
|
||||||
|
|
||||||
extern luau_FastFunction luauF_table[256];
|
extern const luau_FastFunction luauF_table[256];
|
||||||
|
|
|
@ -12,8 +12,6 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauFasterGetInfo, false)
|
|
||||||
|
|
||||||
static const char* getfuncname(Closure* f);
|
static const char* getfuncname(Closure* f);
|
||||||
|
|
||||||
static int currentpc(lua_State* L, CallInfo* ci)
|
static int currentpc(lua_State* L, CallInfo* ci)
|
||||||
|
@ -105,7 +103,6 @@ static Closure* auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closur
|
||||||
ar->source = "=[C]";
|
ar->source = "=[C]";
|
||||||
ar->what = "C";
|
ar->what = "C";
|
||||||
ar->linedefined = -1;
|
ar->linedefined = -1;
|
||||||
if (FFlag::LuauFasterGetInfo)
|
|
||||||
ar->short_src = "[C]";
|
ar->short_src = "[C]";
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -114,14 +111,8 @@ static Closure* auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closur
|
||||||
ar->source = getstr(source);
|
ar->source = getstr(source);
|
||||||
ar->what = "Lua";
|
ar->what = "Lua";
|
||||||
ar->linedefined = f->l.p->linedefined;
|
ar->linedefined = f->l.p->linedefined;
|
||||||
if (FFlag::LuauFasterGetInfo)
|
|
||||||
ar->short_src = luaO_chunkid(ar->ssbuf, sizeof(ar->ssbuf), getstr(source), source->len);
|
ar->short_src = luaO_chunkid(ar->ssbuf, sizeof(ar->ssbuf), getstr(source), source->len);
|
||||||
}
|
}
|
||||||
if (!FFlag::LuauFasterGetInfo)
|
|
||||||
{
|
|
||||||
luaO_chunkid(ar->ssbuf, LUA_IDSIZE, ar->source, 0);
|
|
||||||
ar->short_src = ar->ssbuf;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'l':
|
case 'l':
|
||||||
|
@ -194,8 +185,6 @@ int lua_getinfo(lua_State* L, int level, const char* what, lua_Debug* ar)
|
||||||
f = clvalue(ci->func);
|
f = clvalue(ci->func);
|
||||||
}
|
}
|
||||||
if (f)
|
if (f)
|
||||||
{
|
|
||||||
if (FFlag::LuauFasterGetInfo)
|
|
||||||
{
|
{
|
||||||
// auxgetinfo fills ar and optionally requests to put closure on stack
|
// auxgetinfo fills ar and optionally requests to put closure on stack
|
||||||
if (Closure* fcl = auxgetinfo(L, what, ar, f, ci))
|
if (Closure* fcl = auxgetinfo(L, what, ar, f, ci))
|
||||||
|
@ -205,17 +194,6 @@ int lua_getinfo(lua_State* L, int level, const char* what, lua_Debug* ar)
|
||||||
incr_top(L);
|
incr_top(L);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
auxgetinfo(L, what, ar, f, ci);
|
|
||||||
if (strchr(what, 'f'))
|
|
||||||
{
|
|
||||||
luaC_threadbarrier(L);
|
|
||||||
setclvalue(L, L->top, f);
|
|
||||||
incr_top(L);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return f ? 1 : 0;
|
return f ? 1 : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,10 +13,6 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauFasterGetInfo)
|
|
||||||
|
|
||||||
const TValue luaO_nilobject_ = {{NULL}, {0}, LUA_TNIL};
|
const TValue luaO_nilobject_ = {{NULL}, {0}, LUA_TNIL};
|
||||||
|
|
||||||
int luaO_log2(unsigned int x)
|
int luaO_log2(unsigned int x)
|
||||||
|
@ -122,8 +118,6 @@ const char* luaO_pushfstring(lua_State* L, const char* fmt, ...)
|
||||||
const char* luaO_chunkid(char* buf, size_t buflen, const char* source, size_t srclen)
|
const char* luaO_chunkid(char* buf, size_t buflen, const char* source, size_t srclen)
|
||||||
{
|
{
|
||||||
if (*source == '=')
|
if (*source == '=')
|
||||||
{
|
|
||||||
if (FFlag::LuauFasterGetInfo)
|
|
||||||
{
|
{
|
||||||
if (srclen <= buflen)
|
if (srclen <= buflen)
|
||||||
return source + 1;
|
return source + 1;
|
||||||
|
@ -131,18 +125,7 @@ const char* luaO_chunkid(char* buf, size_t buflen, const char* source, size_t sr
|
||||||
memcpy(buf, source + 1, buflen - 1);
|
memcpy(buf, source + 1, buflen - 1);
|
||||||
buf[buflen - 1] = '\0';
|
buf[buflen - 1] = '\0';
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
source++; // skip the `='
|
|
||||||
size_t len = strlen(source);
|
|
||||||
size_t dstlen = len < buflen ? len : buflen - 1;
|
|
||||||
memcpy(buf, source, dstlen);
|
|
||||||
buf[dstlen] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (*source == '@')
|
else if (*source == '@')
|
||||||
{
|
|
||||||
if (FFlag::LuauFasterGetInfo)
|
|
||||||
{
|
{
|
||||||
if (srclen <= buflen)
|
if (srclen <= buflen)
|
||||||
return source + 1;
|
return source + 1;
|
||||||
|
@ -152,21 +135,6 @@ const char* luaO_chunkid(char* buf, size_t buflen, const char* source, size_t sr
|
||||||
buf[buflen - 1] = '\0';
|
buf[buflen - 1] = '\0';
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
size_t l;
|
|
||||||
source++; // skip the `@'
|
|
||||||
buflen -= sizeof("...");
|
|
||||||
l = strlen(source);
|
|
||||||
strcpy(buf, "");
|
|
||||||
if (l > buflen)
|
|
||||||
{
|
|
||||||
source += (l - buflen); // get last part of file name
|
|
||||||
strcat(buf, "...");
|
|
||||||
}
|
|
||||||
strcat(buf, source);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{ // buf = [string "string"]
|
{ // buf = [string "string"]
|
||||||
size_t len = strcspn(source, "\n\r"); // stop at first newline
|
size_t len = strcspn(source, "\n\r"); // stop at first newline
|
||||||
buflen -= sizeof("[string \"...\"]");
|
buflen -= sizeof("[string \"...\"]");
|
||||||
|
|
|
@ -290,6 +290,7 @@ typedef struct Proto
|
||||||
int sizelineinfo;
|
int sizelineinfo;
|
||||||
int linegaplog2;
|
int linegaplog2;
|
||||||
int linedefined;
|
int linedefined;
|
||||||
|
int bytecodeid;
|
||||||
|
|
||||||
|
|
||||||
uint8_t nups; // number of upvalues
|
uint8_t nups; // number of upvalues
|
||||||
|
|
|
@ -100,6 +100,12 @@ static void close_state(lua_State* L)
|
||||||
LUAU_ASSERT(g->memcatbytes[0] == sizeof(LG));
|
LUAU_ASSERT(g->memcatbytes[0] == sizeof(LG));
|
||||||
for (int i = 1; i < LUA_MEMORY_CATEGORIES; i++)
|
for (int i = 1; i < LUA_MEMORY_CATEGORIES; i++)
|
||||||
LUAU_ASSERT(g->memcatbytes[i] == 0);
|
LUAU_ASSERT(g->memcatbytes[i] == 0);
|
||||||
|
|
||||||
|
#if LUA_CUSTOM_EXECUTION
|
||||||
|
if (L->global->ecb.close)
|
||||||
|
L->global->ecb.close(L);
|
||||||
|
#endif
|
||||||
|
|
||||||
(*g->frealloc)(g->ud, L, sizeof(LG), 0);
|
(*g->frealloc)(g->ud, L, sizeof(LG), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,18 +146,15 @@ struct GCMetrics
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if LUA_CUSTOM_EXECUTION
|
|
||||||
|
|
||||||
// Callbacks that can be used to to redirect code execution from Luau bytecode VM to a custom implementation (AoT/JiT/sandboxing/...)
|
// Callbacks that can be used to to redirect code execution from Luau bytecode VM to a custom implementation (AoT/JiT/sandboxing/...)
|
||||||
typedef struct lua_ExecutionCallbacks
|
struct lua_ExecutionCallbacks
|
||||||
{
|
{
|
||||||
void* context;
|
void* context;
|
||||||
|
void (*close)(lua_State* L); // called when global VM state is closed
|
||||||
void (*destroy)(lua_State* L, Proto* proto); // called when function is destroyed
|
void (*destroy)(lua_State* L, Proto* proto); // called when function is destroyed
|
||||||
int (*enter)(lua_State* L, Proto* proto); // called when function is about to start/resume (when execdata is present), return 0 to exit VM
|
int (*enter)(lua_State* L, Proto* proto); // called when function is about to start/resume (when execdata is present), return 0 to exit VM
|
||||||
void (*setbreakpoint)(lua_State* L, Proto* proto, int line); // called when a breakpoint is set in a function
|
void (*setbreakpoint)(lua_State* L, Proto* proto, int line); // called when a breakpoint is set in a function
|
||||||
} lua_ExecutionCallbacks;
|
};
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** `global state', shared by all threads of this state
|
** `global state', shared by all threads of this state
|
||||||
|
|
|
@ -756,6 +756,8 @@ reentry:
|
||||||
Proto* pv = cl->l.p->p[LUAU_INSN_D(insn)];
|
Proto* pv = cl->l.p->p[LUAU_INSN_D(insn)];
|
||||||
LUAU_ASSERT(unsigned(LUAU_INSN_D(insn)) < unsigned(cl->l.p->sizep));
|
LUAU_ASSERT(unsigned(LUAU_INSN_D(insn)) < unsigned(cl->l.p->sizep));
|
||||||
|
|
||||||
|
VM_PROTECT_PC(); // luaF_newLclosure may fail due to OOM
|
||||||
|
|
||||||
// note: we save closure to stack early in case the code below wants to capture it by value
|
// note: we save closure to stack early in case the code below wants to capture it by value
|
||||||
Closure* ncl = luaF_newLclosure(L, pv->nups, cl->env, pv);
|
Closure* ncl = luaF_newLclosure(L, pv->nups, cl->env, pv);
|
||||||
setclvalue(L, ra, ncl);
|
setclvalue(L, ra, ncl);
|
||||||
|
@ -2054,6 +2056,8 @@ reentry:
|
||||||
int b = LUAU_INSN_B(insn);
|
int b = LUAU_INSN_B(insn);
|
||||||
uint32_t aux = *pc++;
|
uint32_t aux = *pc++;
|
||||||
|
|
||||||
|
VM_PROTECT_PC(); // luaH_new may fail due to OOM
|
||||||
|
|
||||||
sethvalue(L, ra, luaH_new(L, aux, b == 0 ? 0 : (1 << (b - 1))));
|
sethvalue(L, ra, luaH_new(L, aux, b == 0 ? 0 : (1 << (b - 1))));
|
||||||
VM_PROTECT(luaC_checkGC(L));
|
VM_PROTECT(luaC_checkGC(L));
|
||||||
VM_NEXT();
|
VM_NEXT();
|
||||||
|
@ -2065,6 +2069,8 @@ reentry:
|
||||||
StkId ra = VM_REG(LUAU_INSN_A(insn));
|
StkId ra = VM_REG(LUAU_INSN_A(insn));
|
||||||
TValue* kv = VM_KV(LUAU_INSN_D(insn));
|
TValue* kv = VM_KV(LUAU_INSN_D(insn));
|
||||||
|
|
||||||
|
VM_PROTECT_PC(); // luaH_clone may fail due to OOM
|
||||||
|
|
||||||
sethvalue(L, ra, luaH_clone(L, hvalue(kv)));
|
sethvalue(L, ra, luaH_clone(L, hvalue(kv)));
|
||||||
VM_PROTECT(luaC_checkGC(L));
|
VM_PROTECT(luaC_checkGC(L));
|
||||||
VM_NEXT();
|
VM_NEXT();
|
||||||
|
@ -2086,12 +2092,17 @@ reentry:
|
||||||
|
|
||||||
Table* h = hvalue(ra);
|
Table* h = hvalue(ra);
|
||||||
|
|
||||||
|
// TODO: we really don't need this anymore
|
||||||
if (!ttistable(ra))
|
if (!ttistable(ra))
|
||||||
return; // temporary workaround to weaken a rather powerful exploitation primitive in case of a MITM attack on bytecode
|
return; // temporary workaround to weaken a rather powerful exploitation primitive in case of a MITM attack on bytecode
|
||||||
|
|
||||||
int last = index + c - 1;
|
int last = index + c - 1;
|
||||||
if (last > h->sizearray)
|
if (last > h->sizearray)
|
||||||
|
{
|
||||||
|
VM_PROTECT_PC(); // luaH_resizearray may fail due to OOM
|
||||||
|
|
||||||
luaH_resizearray(L, h, last);
|
luaH_resizearray(L, h, last);
|
||||||
|
}
|
||||||
|
|
||||||
TValue* array = h->array;
|
TValue* array = h->array;
|
||||||
|
|
||||||
|
@ -2183,7 +2194,8 @@ reentry:
|
||||||
// protect against __iter returning nil, since nil is used as a marker for builtin iteration in FORGLOOP
|
// protect against __iter returning nil, since nil is used as a marker for builtin iteration in FORGLOOP
|
||||||
if (ttisnil(ra))
|
if (ttisnil(ra))
|
||||||
{
|
{
|
||||||
VM_PROTECT(luaG_typeerror(L, ra, "call"));
|
VM_PROTECT_PC(); // next call always errors
|
||||||
|
luaG_typeerror(L, ra, "call");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (fasttm(L, mt, TM_CALL))
|
else if (fasttm(L, mt, TM_CALL))
|
||||||
|
@ -2200,7 +2212,8 @@ reentry:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
VM_PROTECT(luaG_typeerror(L, ra, "iterate over"));
|
VM_PROTECT_PC(); // next call always errors
|
||||||
|
luaG_typeerror(L, ra, "iterate over");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2323,7 +2336,8 @@ reentry:
|
||||||
}
|
}
|
||||||
else if (!ttisfunction(ra))
|
else if (!ttisfunction(ra))
|
||||||
{
|
{
|
||||||
VM_PROTECT(luaG_typeerror(L, ra, "iterate over"));
|
VM_PROTECT_PC(); // next call always errors
|
||||||
|
luaG_typeerror(L, ra, "iterate over");
|
||||||
}
|
}
|
||||||
|
|
||||||
pc += LUAU_INSN_D(insn);
|
pc += LUAU_INSN_D(insn);
|
||||||
|
@ -2351,7 +2365,8 @@ reentry:
|
||||||
}
|
}
|
||||||
else if (!ttisfunction(ra))
|
else if (!ttisfunction(ra))
|
||||||
{
|
{
|
||||||
VM_PROTECT(luaG_typeerror(L, ra, "iterate over"));
|
VM_PROTECT_PC(); // next call always errors
|
||||||
|
luaG_typeerror(L, ra, "iterate over");
|
||||||
}
|
}
|
||||||
|
|
||||||
pc += LUAU_INSN_D(insn);
|
pc += LUAU_INSN_D(insn);
|
||||||
|
@ -2402,6 +2417,8 @@ reentry:
|
||||||
|
|
||||||
Closure* kcl = clvalue(kv);
|
Closure* kcl = clvalue(kv);
|
||||||
|
|
||||||
|
VM_PROTECT_PC(); // luaF_newLclosure may fail due to OOM
|
||||||
|
|
||||||
// clone closure if the environment is not shared
|
// clone closure if the environment is not shared
|
||||||
// note: we save closure to stack early in case the code below wants to capture it by value
|
// note: we save closure to stack early in case the code below wants to capture it by value
|
||||||
Closure* ncl = (kcl->env == cl->env) ? kcl : luaF_newLclosure(L, kcl->nupvalues, cl->env, kcl->l.p);
|
Closure* ncl = (kcl->env == cl->env) ? kcl : luaF_newLclosure(L, kcl->nupvalues, cl->env, kcl->l.p);
|
||||||
|
@ -2528,15 +2545,18 @@ reentry:
|
||||||
nparams = (nparams == LUA_MULTRET) ? int(L->top - ra - 1) : nparams;
|
nparams = (nparams == LUA_MULTRET) ? int(L->top - ra - 1) : nparams;
|
||||||
|
|
||||||
luau_FastFunction f = luauF_table[bfid];
|
luau_FastFunction f = luauF_table[bfid];
|
||||||
|
LUAU_ASSERT(f);
|
||||||
|
|
||||||
if (cl->env->safeenv && f)
|
if (cl->env->safeenv)
|
||||||
{
|
{
|
||||||
VM_PROTECT_PC();
|
VM_PROTECT_PC(); // f may fail due to OOM
|
||||||
|
|
||||||
int n = f(L, ra, ra + 1, nresults, ra + 2, nparams);
|
int n = f(L, ra, ra + 1, nresults, ra + 2, nparams);
|
||||||
|
|
||||||
if (n >= 0)
|
if (n >= 0)
|
||||||
{
|
{
|
||||||
|
// when nresults != MULTRET, L->top might be pointing to the middle of stack frame if nparams is equal to MULTRET
|
||||||
|
// instead of restoring L->top to L->ci->top if nparams is MULTRET, we do it unconditionally to skip an extra check
|
||||||
L->top = (nresults == LUA_MULTRET) ? ra + n : L->ci->top;
|
L->top = (nresults == LUA_MULTRET) ? ra + n : L->ci->top;
|
||||||
|
|
||||||
pc += skip + 1; // skip instructions that compute function as well as CALL
|
pc += skip + 1; // skip instructions that compute function as well as CALL
|
||||||
|
@ -2604,16 +2624,18 @@ reentry:
|
||||||
int nresults = LUAU_INSN_C(call) - 1;
|
int nresults = LUAU_INSN_C(call) - 1;
|
||||||
|
|
||||||
luau_FastFunction f = luauF_table[bfid];
|
luau_FastFunction f = luauF_table[bfid];
|
||||||
|
LUAU_ASSERT(f);
|
||||||
|
|
||||||
if (cl->env->safeenv && f)
|
if (cl->env->safeenv)
|
||||||
{
|
{
|
||||||
VM_PROTECT_PC();
|
VM_PROTECT_PC(); // f may fail due to OOM
|
||||||
|
|
||||||
int n = f(L, ra, arg, nresults, NULL, nparams);
|
int n = f(L, ra, arg, nresults, NULL, nparams);
|
||||||
|
|
||||||
if (n >= 0)
|
if (n >= 0)
|
||||||
{
|
{
|
||||||
L->top = (nresults == LUA_MULTRET) ? ra + n : L->ci->top;
|
if (nresults == LUA_MULTRET)
|
||||||
|
L->top = ra + n;
|
||||||
|
|
||||||
pc += skip + 1; // skip instructions that compute function as well as CALL
|
pc += skip + 1; // skip instructions that compute function as well as CALL
|
||||||
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
|
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
|
||||||
|
@ -2652,16 +2674,18 @@ reentry:
|
||||||
int nresults = LUAU_INSN_C(call) - 1;
|
int nresults = LUAU_INSN_C(call) - 1;
|
||||||
|
|
||||||
luau_FastFunction f = luauF_table[bfid];
|
luau_FastFunction f = luauF_table[bfid];
|
||||||
|
LUAU_ASSERT(f);
|
||||||
|
|
||||||
if (cl->env->safeenv && f)
|
if (cl->env->safeenv)
|
||||||
{
|
{
|
||||||
VM_PROTECT_PC();
|
VM_PROTECT_PC(); // f may fail due to OOM
|
||||||
|
|
||||||
int n = f(L, ra, arg1, nresults, arg2, nparams);
|
int n = f(L, ra, arg1, nresults, arg2, nparams);
|
||||||
|
|
||||||
if (n >= 0)
|
if (n >= 0)
|
||||||
{
|
{
|
||||||
L->top = (nresults == LUA_MULTRET) ? ra + n : L->ci->top;
|
if (nresults == LUA_MULTRET)
|
||||||
|
L->top = ra + n;
|
||||||
|
|
||||||
pc += skip + 1; // skip instructions that compute function as well as CALL
|
pc += skip + 1; // skip instructions that compute function as well as CALL
|
||||||
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
|
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
|
||||||
|
@ -2700,16 +2724,18 @@ reentry:
|
||||||
int nresults = LUAU_INSN_C(call) - 1;
|
int nresults = LUAU_INSN_C(call) - 1;
|
||||||
|
|
||||||
luau_FastFunction f = luauF_table[bfid];
|
luau_FastFunction f = luauF_table[bfid];
|
||||||
|
LUAU_ASSERT(f);
|
||||||
|
|
||||||
if (cl->env->safeenv && f)
|
if (cl->env->safeenv)
|
||||||
{
|
{
|
||||||
VM_PROTECT_PC();
|
VM_PROTECT_PC(); // f may fail due to OOM
|
||||||
|
|
||||||
int n = f(L, ra, arg1, nresults, arg2, nparams);
|
int n = f(L, ra, arg1, nresults, arg2, nparams);
|
||||||
|
|
||||||
if (n >= 0)
|
if (n >= 0)
|
||||||
{
|
{
|
||||||
L->top = (nresults == LUA_MULTRET) ? ra + n : L->ci->top;
|
if (nresults == LUA_MULTRET)
|
||||||
|
L->top = ra + n;
|
||||||
|
|
||||||
pc += skip + 1; // skip instructions that compute function as well as CALL
|
pc += skip + 1; // skip instructions that compute function as well as CALL
|
||||||
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
|
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
|
||||||
|
|
|
@ -192,6 +192,7 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
|
||||||
{
|
{
|
||||||
Proto* p = luaF_newproto(L);
|
Proto* p = luaF_newproto(L);
|
||||||
p->source = source;
|
p->source = source;
|
||||||
|
p->bytecodeid = int(i);
|
||||||
|
|
||||||
p->maxstacksize = read<uint8_t>(data, size, offset);
|
p->maxstacksize = read<uint8_t>(data, size, offset);
|
||||||
p->numparams = read<uint8_t>(data, size, offset);
|
p->numparams = read<uint8_t>(data, size, offset);
|
||||||
|
|
Loading…
Reference in New Issue