Update to `0.2.0+luau519`
This commit is contained in:
parent
1a8f5dcfcf
commit
bbb35d7de1
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "luau0-src"
|
name = "luau0-src"
|
||||||
version = "0.1.0+luau516"
|
version = "0.2.0+luau519"
|
||||||
authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"]
|
authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"]
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
repository = "https://github.com/khvzak/luau-src-rs"
|
repository = "https://github.com/khvzak/luau-src-rs"
|
||||||
|
|
|
@ -11,19 +11,11 @@
|
||||||
LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000)
|
LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000)
|
||||||
LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseSingletonTypes, false)
|
LUAU_FASTFLAGVARIABLE(LuauParseSingletonTypes, false)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseTypeAliasDefaults, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauParseAllHotComments, false)
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTableFieldFunctionDebugname, false)
|
LUAU_FASTFLAGVARIABLE(LuauTableFieldFunctionDebugname, false)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
static bool isComment(const Lexeme& lexeme)
|
|
||||||
{
|
|
||||||
LUAU_ASSERT(!FFlag::LuauParseAllHotComments);
|
|
||||||
return lexeme.type == Lexeme::Comment || lexeme.type == Lexeme::BlockComment;
|
|
||||||
}
|
|
||||||
|
|
||||||
ParseError::ParseError(const Location& location, const std::string& message)
|
ParseError::ParseError(const Location& location, const std::string& message)
|
||||||
: location(location)
|
: location(location)
|
||||||
, message(message)
|
, message(message)
|
||||||
|
@ -147,54 +139,13 @@ ParseResult Parser::parse(const char* buffer, size_t bufferSize, AstNameTable& n
|
||||||
{
|
{
|
||||||
LUAU_TIMETRACE_SCOPE("Parser::parse", "Parser");
|
LUAU_TIMETRACE_SCOPE("Parser::parse", "Parser");
|
||||||
|
|
||||||
Parser p(buffer, bufferSize, names, allocator, FFlag::LuauParseAllHotComments ? options : ParseOptions());
|
Parser p(buffer, bufferSize, names, allocator, options);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (FFlag::LuauParseAllHotComments)
|
AstStatBlock* root = p.parseChunk();
|
||||||
{
|
|
||||||
AstStatBlock* root = p.parseChunk();
|
|
||||||
|
|
||||||
return ParseResult{root, std::move(p.hotcomments), std::move(p.parseErrors), std::move(p.commentLocations)};
|
return ParseResult{root, std::move(p.hotcomments), std::move(p.parseErrors), std::move(p.commentLocations)};
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
std::vector<HotComment> hotcomments;
|
|
||||||
|
|
||||||
while (isComment(p.lexer.current()) || p.lexer.current().type == Lexeme::BrokenComment)
|
|
||||||
{
|
|
||||||
const char* text = p.lexer.current().data;
|
|
||||||
unsigned int length = p.lexer.current().length;
|
|
||||||
|
|
||||||
if (length && text[0] == '!')
|
|
||||||
{
|
|
||||||
unsigned int end = length;
|
|
||||||
while (end > 0 && isSpace(text[end - 1]))
|
|
||||||
--end;
|
|
||||||
|
|
||||||
hotcomments.push_back({true, p.lexer.current().location, std::string(text + 1, text + end)});
|
|
||||||
}
|
|
||||||
|
|
||||||
const Lexeme::Type type = p.lexer.current().type;
|
|
||||||
const Location loc = p.lexer.current().location;
|
|
||||||
|
|
||||||
if (options.captureComments)
|
|
||||||
p.commentLocations.push_back(Comment{type, loc});
|
|
||||||
|
|
||||||
if (type == Lexeme::BrokenComment)
|
|
||||||
break;
|
|
||||||
|
|
||||||
p.lexer.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
p.lexer.setSkipComments(true);
|
|
||||||
|
|
||||||
p.options = options;
|
|
||||||
|
|
||||||
AstStatBlock* root = p.parseChunk();
|
|
||||||
|
|
||||||
return ParseResult{root, hotcomments, p.parseErrors, std::move(p.commentLocations)};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (ParseError& err)
|
catch (ParseError& err)
|
||||||
{
|
{
|
||||||
|
@ -226,10 +177,11 @@ Parser::Parser(const char* buffer, size_t bufferSize, AstNameTable& names, Alloc
|
||||||
matchRecoveryStopOnToken.assign(Lexeme::Type::Reserved_END, 0);
|
matchRecoveryStopOnToken.assign(Lexeme::Type::Reserved_END, 0);
|
||||||
matchRecoveryStopOnToken[Lexeme::Type::Eof] = 1;
|
matchRecoveryStopOnToken[Lexeme::Type::Eof] = 1;
|
||||||
|
|
||||||
if (FFlag::LuauParseAllHotComments)
|
// required for lookahead() to work across a comment boundary and for nextLexeme() to work when captureComments is false
|
||||||
lexer.setSkipComments(true);
|
lexer.setSkipComments(true);
|
||||||
|
|
||||||
// read first lexeme
|
// read first lexeme (any hot comments get .header = true)
|
||||||
|
LUAU_ASSERT(hotcommentHeader);
|
||||||
nextLexeme();
|
nextLexeme();
|
||||||
|
|
||||||
// all hot comments parsed after the first non-comment lexeme are special in that they don't affect type checking / linting mode
|
// all hot comments parsed after the first non-comment lexeme are special in that they don't affect type checking / linting mode
|
||||||
|
@ -779,7 +731,7 @@ AstStat* Parser::parseTypeAlias(const Location& start, bool exported)
|
||||||
if (!name)
|
if (!name)
|
||||||
name = Name(nameError, lexer.current().location);
|
name = Name(nameError, lexer.current().location);
|
||||||
|
|
||||||
auto [generics, genericPacks] = parseGenericTypeList(/* withDefaultValues= */ FFlag::LuauParseTypeAliasDefaults);
|
auto [generics, genericPacks] = parseGenericTypeList(/* withDefaultValues= */ true);
|
||||||
|
|
||||||
expectAndConsume('=', "type alias");
|
expectAndConsume('=', "type alias");
|
||||||
|
|
||||||
|
@ -2832,49 +2784,31 @@ void Parser::nextLexeme()
|
||||||
{
|
{
|
||||||
if (options.captureComments)
|
if (options.captureComments)
|
||||||
{
|
{
|
||||||
if (FFlag::LuauParseAllHotComments)
|
Lexeme::Type type = lexer.next(/* skipComments= */ false).type;
|
||||||
|
|
||||||
|
while (type == Lexeme::BrokenComment || type == Lexeme::Comment || type == Lexeme::BlockComment)
|
||||||
{
|
{
|
||||||
Lexeme::Type type = lexer.next(/* skipComments= */ false).type;
|
const Lexeme& lexeme = lexer.current();
|
||||||
|
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
||||||
|
|
||||||
while (type == Lexeme::BrokenComment || type == Lexeme::Comment || type == Lexeme::BlockComment)
|
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
|
||||||
|
// The parser will turn this into a proper syntax error.
|
||||||
|
if (lexeme.type == Lexeme::BrokenComment)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Comments starting with ! are called "hot comments" and contain directives for type checking / linting
|
||||||
|
if (lexeme.type == Lexeme::Comment && lexeme.length && lexeme.data[0] == '!')
|
||||||
{
|
{
|
||||||
const Lexeme& lexeme = lexer.current();
|
const char* text = lexeme.data;
|
||||||
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
|
||||||
|
|
||||||
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
|
unsigned int end = lexeme.length;
|
||||||
// The parser will turn this into a proper syntax error.
|
while (end > 0 && isSpace(text[end - 1]))
|
||||||
if (lexeme.type == Lexeme::BrokenComment)
|
--end;
|
||||||
return;
|
|
||||||
|
|
||||||
// Comments starting with ! are called "hot comments" and contain directives for type checking / linting
|
hotcomments.push_back({hotcommentHeader, lexeme.location, std::string(text + 1, text + end)});
|
||||||
if (lexeme.type == Lexeme::Comment && lexeme.length && lexeme.data[0] == '!')
|
|
||||||
{
|
|
||||||
const char* text = lexeme.data;
|
|
||||||
|
|
||||||
unsigned int end = lexeme.length;
|
|
||||||
while (end > 0 && isSpace(text[end - 1]))
|
|
||||||
--end;
|
|
||||||
|
|
||||||
hotcomments.push_back({hotcommentHeader, lexeme.location, std::string(text + 1, text + end)});
|
|
||||||
}
|
|
||||||
|
|
||||||
type = lexer.next(/* skipComments= */ false).type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
while (true)
|
|
||||||
{
|
|
||||||
const Lexeme& lexeme = lexer.next(/*skipComments*/ false);
|
|
||||||
// Subtlety: Broken comments are weird because we record them as comments AND pass them to the parser as a lexeme.
|
|
||||||
// The parser will turn this into a proper syntax error.
|
|
||||||
if (lexeme.type == Lexeme::BrokenComment)
|
|
||||||
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
|
||||||
if (isComment(lexeme))
|
|
||||||
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
|
|
||||||
else
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type = lexer.next(/* skipComments= */ false).type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
|
@ -4,8 +4,6 @@
|
||||||
#include "Luau/Bytecode.h"
|
#include "Luau/Bytecode.h"
|
||||||
#include "Luau/Compiler.h"
|
#include "Luau/Compiler.h"
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauCompileSelectBuiltin2, false)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace Compile
|
namespace Compile
|
||||||
|
@ -64,7 +62,7 @@ int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& options)
|
||||||
if (builtin.isGlobal("unpack"))
|
if (builtin.isGlobal("unpack"))
|
||||||
return LBF_TABLE_UNPACK;
|
return LBF_TABLE_UNPACK;
|
||||||
|
|
||||||
if (FFlag::LuauCompileSelectBuiltin2 && builtin.isGlobal("select"))
|
if (builtin.isGlobal("select"))
|
||||||
return LBF_SELECT_VARARG;
|
return LBF_SELECT_VARARG;
|
||||||
|
|
||||||
if (builtin.object == "math")
|
if (builtin.object == "math")
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
#include <bitset>
|
#include <bitset>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauCompileSelectBuiltin2)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -265,7 +263,6 @@ struct Compiler
|
||||||
|
|
||||||
void compileExprSelectVararg(AstExprCall* expr, uint8_t target, uint8_t targetCount, bool targetTop, bool multRet, uint8_t regs)
|
void compileExprSelectVararg(AstExprCall* expr, uint8_t target, uint8_t targetCount, bool targetTop, bool multRet, uint8_t regs)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauCompileSelectBuiltin2);
|
|
||||||
LUAU_ASSERT(targetCount == 1);
|
LUAU_ASSERT(targetCount == 1);
|
||||||
LUAU_ASSERT(!expr->self);
|
LUAU_ASSERT(!expr->self);
|
||||||
LUAU_ASSERT(expr->args.size == 2 && expr->args.data[1]->is<AstExprVarargs>());
|
LUAU_ASSERT(expr->args.size == 2 && expr->args.data[1]->is<AstExprVarargs>());
|
||||||
|
@ -407,7 +404,6 @@ struct Compiler
|
||||||
|
|
||||||
if (bfid == LBF_SELECT_VARARG)
|
if (bfid == LBF_SELECT_VARARG)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauCompileSelectBuiltin2);
|
|
||||||
// Optimization: compile select(_, ...) as FASTCALL1; the builtin will read variadic arguments directly
|
// Optimization: compile select(_, ...) as FASTCALL1; the builtin will read variadic arguments directly
|
||||||
// note: for now we restrict this to single-return expressions since our runtime code doesn't deal with general cases
|
// note: for now we restrict this to single-return expressions since our runtime code doesn't deal with general cases
|
||||||
if (multRet == false && targetCount == 1 && expr->args.size == 2 && expr->args.data[1]->is<AstExprVarargs>())
|
if (multRet == false && targetCount == 1 && expr->args.size == 2 && expr->args.data[1]->is<AstExprVarargs>())
|
||||||
|
|
|
@ -172,9 +172,12 @@ LUA_API const char* lua_pushvfstring(lua_State* L, const char* fmt, va_list argp
|
||||||
LUA_API LUA_PRINTF_ATTR(2, 3) const char* lua_pushfstringL(lua_State* L, const char* fmt, ...);
|
LUA_API LUA_PRINTF_ATTR(2, 3) const char* lua_pushfstringL(lua_State* L, const char* fmt, ...);
|
||||||
LUA_API void lua_pushcclosurek(lua_State* L, lua_CFunction fn, const char* debugname, int nup, lua_Continuation cont);
|
LUA_API void lua_pushcclosurek(lua_State* L, lua_CFunction fn, const char* debugname, int nup, lua_Continuation cont);
|
||||||
LUA_API void lua_pushboolean(lua_State* L, int b);
|
LUA_API void lua_pushboolean(lua_State* L, int b);
|
||||||
LUA_API void lua_pushlightuserdata(lua_State* L, void* p);
|
|
||||||
LUA_API int lua_pushthread(lua_State* L);
|
LUA_API int lua_pushthread(lua_State* L);
|
||||||
|
|
||||||
|
LUA_API void lua_pushlightuserdata(lua_State* L, void* p);
|
||||||
|
LUA_API void* lua_newuserdatatagged(lua_State* L, size_t sz, int tag);
|
||||||
|
LUA_API void* lua_newuserdatadtor(lua_State* L, size_t sz, void (*dtor)(void*));
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** get functions (Lua -> stack)
|
** get functions (Lua -> stack)
|
||||||
*/
|
*/
|
||||||
|
@ -189,8 +192,6 @@ LUA_API void lua_setreadonly(lua_State* L, int idx, int enabled);
|
||||||
LUA_API int lua_getreadonly(lua_State* L, int idx);
|
LUA_API int lua_getreadonly(lua_State* L, int idx);
|
||||||
LUA_API void lua_setsafeenv(lua_State* L, int idx, int enabled);
|
LUA_API void lua_setsafeenv(lua_State* L, int idx, int enabled);
|
||||||
|
|
||||||
LUA_API void* lua_newuserdatatagged(lua_State* L, size_t sz, int tag);
|
|
||||||
LUA_API void* lua_newuserdatadtor(lua_State* L, size_t sz, void (*dtor)(void*));
|
|
||||||
LUA_API int lua_getmetatable(lua_State* L, int objindex);
|
LUA_API int lua_getmetatable(lua_State* L, int objindex);
|
||||||
LUA_API void lua_getfenv(lua_State* L, int idx);
|
LUA_API void lua_getfenv(lua_State* L, int idx);
|
||||||
|
|
||||||
|
@ -229,19 +230,46 @@ LUA_API void lua_setthreaddata(lua_State* L, void* data);
|
||||||
|
|
||||||
enum lua_GCOp
|
enum lua_GCOp
|
||||||
{
|
{
|
||||||
|
/* stop and resume incremental garbage collection */
|
||||||
LUA_GCSTOP,
|
LUA_GCSTOP,
|
||||||
LUA_GCRESTART,
|
LUA_GCRESTART,
|
||||||
|
|
||||||
|
/* run a full GC cycle; not recommended for latency sensitive applications */
|
||||||
LUA_GCCOLLECT,
|
LUA_GCCOLLECT,
|
||||||
|
|
||||||
|
/* return the heap size in KB and the remainder in bytes */
|
||||||
LUA_GCCOUNT,
|
LUA_GCCOUNT,
|
||||||
LUA_GCCOUNTB,
|
LUA_GCCOUNTB,
|
||||||
|
|
||||||
|
/* return 1 if GC is active (not stopped); note that GC may not be actively collecting even if it's running */
|
||||||
LUA_GCISRUNNING,
|
LUA_GCISRUNNING,
|
||||||
|
|
||||||
// garbage collection is handled by 'assists' that perform some amount of GC work matching pace of allocation
|
/*
|
||||||
// explicit GC steps allow to perform some amount of work at custom points to offset the need for GC assists
|
** perform an explicit GC step, with the step size specified in KB
|
||||||
// note that GC might also be paused for some duration (until bytes allocated meet the threshold)
|
**
|
||||||
// if an explicit step is performed during this pause, it will trigger the start of the next collection cycle
|
** garbage collection is handled by 'assists' that perform some amount of GC work matching pace of allocation
|
||||||
|
** explicit GC steps allow to perform some amount of work at custom points to offset the need for GC assists
|
||||||
|
** note that GC might also be paused for some duration (until bytes allocated meet the threshold)
|
||||||
|
** if an explicit step is performed during this pause, it will trigger the start of the next collection cycle
|
||||||
|
*/
|
||||||
LUA_GCSTEP,
|
LUA_GCSTEP,
|
||||||
|
|
||||||
|
/*
|
||||||
|
** tune GC parameters G (goal), S (step multiplier) and step size (usually best left ignored)
|
||||||
|
**
|
||||||
|
** garbage collection is incremental and tries to maintain the heap size to balance memory and performance overhead
|
||||||
|
** this overhead is determined by G (goal) which is the ratio between total heap size and the amount of live data in it
|
||||||
|
** G is specified in percentages; by default G=200% which means that the heap is allowed to grow to ~2x the size of live data.
|
||||||
|
**
|
||||||
|
** collector tries to collect S% of allocated bytes by interrupting the application after step size bytes were allocated.
|
||||||
|
** when S is too small, collector may not be able to catch up and the effective goal that can be reached will be larger.
|
||||||
|
** S is specified in percentages; by default S=200% which means that collector will run at ~2x the pace of allocations.
|
||||||
|
**
|
||||||
|
** it is recommended to set S in the interval [100 / (G - 100), 100 + 100 / (G - 100))] with a minimum value of 150%; for example:
|
||||||
|
** - for G=200%, S should be in the interval [150%, 200%]
|
||||||
|
** - for G=150%, S should be in the interval [200%, 300%]
|
||||||
|
** - for G=125%, S should be in the interval [400%, 500%]
|
||||||
|
*/
|
||||||
LUA_GCSETGOAL,
|
LUA_GCSETGOAL,
|
||||||
LUA_GCSETSTEPMUL,
|
LUA_GCSETSTEPMUL,
|
||||||
LUA_GCSETSTEPSIZE,
|
LUA_GCSETSTEPSIZE,
|
||||||
|
@ -249,6 +277,14 @@ enum lua_GCOp
|
||||||
|
|
||||||
LUA_API int lua_gc(lua_State* L, int what, int data);
|
LUA_API int lua_gc(lua_State* L, int what, int data);
|
||||||
|
|
||||||
|
/*
|
||||||
|
** memory statistics
|
||||||
|
** all allocated bytes are attributed to the memory category of the running thread (0..LUA_MEMORY_CATEGORIES-1)
|
||||||
|
*/
|
||||||
|
|
||||||
|
LUA_API void lua_setmemcat(lua_State* L, int category);
|
||||||
|
LUA_API size_t lua_totalbytes(lua_State* L, int category);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** miscellaneous functions
|
** miscellaneous functions
|
||||||
*/
|
*/
|
||||||
|
@ -265,6 +301,8 @@ LUA_API double lua_clock();
|
||||||
|
|
||||||
LUA_API void lua_setuserdatadtor(lua_State* L, int tag, void (*dtor)(void*));
|
LUA_API void lua_setuserdatadtor(lua_State* L, int tag, void (*dtor)(void*));
|
||||||
|
|
||||||
|
LUA_API void lua_clonefunction(lua_State* L, int idx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** reference system, can be used to pin objects
|
** reference system, can be used to pin objects
|
||||||
*/
|
*/
|
||||||
|
@ -324,6 +362,7 @@ typedef struct lua_Debug lua_Debug; /* activation record */
|
||||||
/* Functions to be called by the debugger in specific events */
|
/* Functions to be called by the debugger in specific events */
|
||||||
typedef void (*lua_Hook)(lua_State* L, lua_Debug* ar);
|
typedef void (*lua_Hook)(lua_State* L, lua_Debug* ar);
|
||||||
|
|
||||||
|
LUA_API int lua_stackdepth(lua_State* L);
|
||||||
LUA_API int lua_getinfo(lua_State* L, int level, const char* what, lua_Debug* ar);
|
LUA_API int lua_getinfo(lua_State* L, int level, const char* what, lua_Debug* ar);
|
||||||
LUA_API int lua_getargument(lua_State* L, int level, int n);
|
LUA_API int lua_getargument(lua_State* L, int level, int n);
|
||||||
LUA_API const char* lua_getlocal(lua_State* L, int level, int n);
|
LUA_API const char* lua_getlocal(lua_State* L, int level, int n);
|
||||||
|
|
|
@ -59,33 +59,6 @@
|
||||||
#define LUA_IDSIZE 256
|
#define LUA_IDSIZE 256
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/*
|
|
||||||
@@ LUAI_GCGOAL defines the desired top heap size in relation to the live heap
|
|
||||||
@* size at the end of the GC cycle
|
|
||||||
** CHANGE it if you want the GC to run faster or slower (higher values
|
|
||||||
** mean larger GC pauses which mean slower collection.) You can also change
|
|
||||||
** this value dynamically.
|
|
||||||
*/
|
|
||||||
#ifndef LUAI_GCGOAL
|
|
||||||
#define LUAI_GCGOAL 200 /* 200% (allow heap to double compared to live heap size) */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
@@ LUAI_GCSTEPMUL / LUAI_GCSTEPSIZE define the default speed of garbage collection
|
|
||||||
@* relative to memory allocation.
|
|
||||||
** Every LUAI_GCSTEPSIZE KB allocated, incremental collector collects LUAI_GCSTEPSIZE
|
|
||||||
** times LUAI_GCSTEPMUL% bytes.
|
|
||||||
** CHANGE it if you want to change the granularity of the garbage
|
|
||||||
** collection.
|
|
||||||
*/
|
|
||||||
#ifndef LUAI_GCSTEPMUL
|
|
||||||
#define LUAI_GCSTEPMUL 200 /* GC runs 'twice the speed' of memory allocation */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef LUAI_GCSTEPSIZE
|
|
||||||
#define LUAI_GCSTEPSIZE 1 /* GC runs every KB of memory allocation */
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* LUA_MINSTACK is the guaranteed number of Lua stack slots available to a C function */
|
/* LUA_MINSTACK is the guaranteed number of Lua stack slots available to a C function */
|
||||||
#ifndef LUA_MINSTACK
|
#ifndef LUA_MINSTACK
|
||||||
#define LUA_MINSTACK 20
|
#define LUA_MINSTACK 20
|
||||||
|
|
|
@ -54,6 +54,8 @@ LUALIB_API lua_State* luaL_newstate(void);
|
||||||
|
|
||||||
LUALIB_API const char* luaL_findtable(lua_State* L, int idx, const char* fname, int szhint);
|
LUALIB_API const char* luaL_findtable(lua_State* L, int idx, const char* fname, int szhint);
|
||||||
|
|
||||||
|
LUALIB_API const char* luaL_typename(lua_State* L, int idx);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** ===============================================================
|
** ===============================================================
|
||||||
** some useful macros
|
** some useful macros
|
||||||
|
@ -66,8 +68,6 @@ LUALIB_API const char* luaL_findtable(lua_State* L, int idx, const char* fname,
|
||||||
#define luaL_checkstring(L, n) (luaL_checklstring(L, (n), NULL))
|
#define luaL_checkstring(L, n) (luaL_checklstring(L, (n), NULL))
|
||||||
#define luaL_optstring(L, n, d) (luaL_optlstring(L, (n), (d), NULL))
|
#define luaL_optstring(L, n, d) (luaL_optlstring(L, (n), (d), NULL))
|
||||||
|
|
||||||
#define luaL_typename(L, i) lua_typename(L, lua_type(L, (i)))
|
|
||||||
|
|
||||||
#define luaL_getmetatable(L, n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
|
#define luaL_getmetatable(L, n) (lua_getfield(L, LUA_REGISTRYINDEX, (n)))
|
||||||
|
|
||||||
#define luaL_opt(L, f, n, d) (lua_isnoneornil(L, (n)) ? (d) : f(L, (n)))
|
#define luaL_opt(L, f, n, d) (lua_isnoneornil(L, (n)) ? (d) : f(L, (n)))
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauGcForwardMetatableBarrier, false)
|
LUAU_FASTFLAG(LuauGcAdditionalStats)
|
||||||
|
|
||||||
const char* lua_ident = "$Lua: Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio $\n"
|
const char* lua_ident = "$Lua: Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio $\n"
|
||||||
"$Authors: R. Ierusalimschy, L. H. de Figueiredo & W. Celes $\n"
|
"$Authors: R. Ierusalimschy, L. H. de Figueiredo & W. Celes $\n"
|
||||||
|
@ -35,8 +35,8 @@ const char* luau_ident = "$Luau: Copyright (C) 2019-2022 Roblox Corporation $\n"
|
||||||
|
|
||||||
static Table* getcurrenv(lua_State* L)
|
static Table* getcurrenv(lua_State* L)
|
||||||
{
|
{
|
||||||
if (L->ci == L->base_ci) /* no enclosing function? */
|
if (L->ci == L->base_ci) /* no enclosing function? */
|
||||||
return L->gt; /* use global table as environment */
|
return L->gt; /* use global table as environment */
|
||||||
else
|
else
|
||||||
return curr_func(L)->env;
|
return curr_func(L)->env;
|
||||||
}
|
}
|
||||||
|
@ -876,16 +876,7 @@ int lua_setmetatable(lua_State* L, int objindex)
|
||||||
luaG_runerror(L, "Attempt to modify a readonly table");
|
luaG_runerror(L, "Attempt to modify a readonly table");
|
||||||
hvalue(obj)->metatable = mt;
|
hvalue(obj)->metatable = mt;
|
||||||
if (mt)
|
if (mt)
|
||||||
{
|
luaC_objbarrier(L, hvalue(obj), mt);
|
||||||
if (FFlag::LuauGcForwardMetatableBarrier)
|
|
||||||
{
|
|
||||||
luaC_objbarrier(L, hvalue(obj), mt);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
luaC_objbarriert(L, hvalue(obj), mt);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case LUA_TUSERDATA:
|
case LUA_TUSERDATA:
|
||||||
|
@ -1069,6 +1060,8 @@ int lua_gc(lua_State* L, int what, int data)
|
||||||
g->GCthreshold = 0;
|
g->GCthreshold = 0;
|
||||||
|
|
||||||
bool waspaused = g->gcstate == GCSpause;
|
bool waspaused = g->gcstate == GCSpause;
|
||||||
|
double startmarktime = g->gcstats.currcycle.marktime;
|
||||||
|
double startsweeptime = g->gcstats.currcycle.sweeptime;
|
||||||
|
|
||||||
// track how much work the loop will actually perform
|
// track how much work the loop will actually perform
|
||||||
size_t actualwork = 0;
|
size_t actualwork = 0;
|
||||||
|
@ -1086,6 +1079,31 @@ int lua_gc(lua_State* L, int what, int data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauGcAdditionalStats)
|
||||||
|
{
|
||||||
|
// record explicit step statistics
|
||||||
|
GCCycleStats* cyclestats = g->gcstate == GCSpause ? &g->gcstats.lastcycle : &g->gcstats.currcycle;
|
||||||
|
|
||||||
|
double totalmarktime = cyclestats->marktime - startmarktime;
|
||||||
|
double totalsweeptime = cyclestats->sweeptime - startsweeptime;
|
||||||
|
|
||||||
|
if (totalmarktime > 0.0)
|
||||||
|
{
|
||||||
|
cyclestats->markexplicitsteps++;
|
||||||
|
|
||||||
|
if (totalmarktime > cyclestats->markmaxexplicittime)
|
||||||
|
cyclestats->markmaxexplicittime = totalmarktime;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (totalsweeptime > 0.0)
|
||||||
|
{
|
||||||
|
cyclestats->sweepexplicitsteps++;
|
||||||
|
|
||||||
|
if (totalsweeptime > cyclestats->sweepmaxexplicittime)
|
||||||
|
cyclestats->sweepmaxexplicittime = totalsweeptime;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if cycle hasn't finished, advance threshold forward for the amount of extra work performed
|
// if cycle hasn't finished, advance threshold forward for the amount of extra work performed
|
||||||
if (g->gcstate != GCSpause)
|
if (g->gcstate != GCSpause)
|
||||||
{
|
{
|
||||||
|
@ -1170,7 +1188,7 @@ void lua_concat(lua_State* L, int n)
|
||||||
|
|
||||||
void* lua_newuserdatatagged(lua_State* L, size_t sz, int tag)
|
void* lua_newuserdatatagged(lua_State* L, size_t sz, int tag)
|
||||||
{
|
{
|
||||||
api_check(L, unsigned(tag) < LUA_UTAG_LIMIT);
|
api_check(L, unsigned(tag) < LUA_UTAG_LIMIT || tag == UTAG_PROXY);
|
||||||
luaC_checkGC(L);
|
luaC_checkGC(L);
|
||||||
luaC_checkthreadsleep(L);
|
luaC_checkthreadsleep(L);
|
||||||
Udata* u = luaU_newudata(L, sz, tag);
|
Udata* u = luaU_newudata(L, sz, tag);
|
||||||
|
@ -1299,7 +1317,31 @@ void lua_setuserdatadtor(lua_State* L, int tag, void (*dtor)(void*))
|
||||||
L->global->udatagc[tag] = dtor;
|
L->global->udatagc[tag] = dtor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lua_clonefunction(lua_State* L, int idx)
|
||||||
|
{
|
||||||
|
StkId p = index2addr(L, idx);
|
||||||
|
api_check(L, isLfunction(p));
|
||||||
|
|
||||||
|
luaC_checkthreadsleep(L);
|
||||||
|
|
||||||
|
Closure* cl = clvalue(p);
|
||||||
|
Closure* newcl = luaF_newLclosure(L, 0, L->gt, cl->l.p);
|
||||||
|
setclvalue(L, L->top - 1, newcl);
|
||||||
|
}
|
||||||
|
|
||||||
lua_Callbacks* lua_callbacks(lua_State* L)
|
lua_Callbacks* lua_callbacks(lua_State* L)
|
||||||
{
|
{
|
||||||
return &L->global->cb;
|
return &L->global->cb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void lua_setmemcat(lua_State* L, int category)
|
||||||
|
{
|
||||||
|
api_check(L, unsigned(category) < LUA_MEMORY_CATEGORIES);
|
||||||
|
L->activememcat = uint8_t(category);
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t lua_totalbytes(lua_State* L, int category)
|
||||||
|
{
|
||||||
|
api_check(L, category < LUA_MEMORY_CATEGORIES);
|
||||||
|
return category < 0 ? L->global->totalbytes : L->global->memcatbytes[category];
|
||||||
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSchubfach)
|
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauMorePreciseLuaLTypeName, false)
|
||||||
|
|
||||||
/* convert a stack index to positive */
|
/* convert a stack index to positive */
|
||||||
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
|
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
|
||||||
|
@ -335,6 +335,19 @@ const char* luaL_findtable(lua_State* L, int idx, const char* fname, int szhint)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* luaL_typename(lua_State* L, int idx)
|
||||||
|
{
|
||||||
|
if (DFFlag::LuauMorePreciseLuaLTypeName)
|
||||||
|
{
|
||||||
|
const TValue* obj = luaA_toobject(L, idx);
|
||||||
|
return luaT_objtypename(L, obj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return lua_typename(L, lua_type(L, idx));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** {======================================================
|
** {======================================================
|
||||||
** Generic Buffer manipulation
|
** Generic Buffer manipulation
|
||||||
|
@ -480,18 +493,13 @@ const char* luaL_tolstring(lua_State* L, int idx, size_t* len)
|
||||||
switch (lua_type(L, idx))
|
switch (lua_type(L, idx))
|
||||||
{
|
{
|
||||||
case LUA_TNUMBER:
|
case LUA_TNUMBER:
|
||||||
if (FFlag::LuauSchubfach)
|
{
|
||||||
{
|
double n = lua_tonumber(L, idx);
|
||||||
double n = lua_tonumber(L, idx);
|
char s[LUAI_MAXNUM2STR];
|
||||||
char s[LUAI_MAXNUM2STR];
|
char* e = luai_num2str(s, n);
|
||||||
char* e = luai_num2str(s, n);
|
lua_pushlstring(L, s, e - s);
|
||||||
lua_pushlstring(L, s, e - s);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
lua_pushstring(L, lua_tostring(L, idx));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
case LUA_TSTRING:
|
case LUA_TSTRING:
|
||||||
lua_pushvalue(L, idx);
|
lua_pushvalue(L, idx);
|
||||||
break;
|
break;
|
||||||
|
@ -505,29 +513,18 @@ const char* luaL_tolstring(lua_State* L, int idx, size_t* len)
|
||||||
{
|
{
|
||||||
const float* v = lua_tovector(L, idx);
|
const float* v = lua_tovector(L, idx);
|
||||||
|
|
||||||
if (FFlag::LuauSchubfach)
|
char s[LUAI_MAXNUM2STR * LUA_VECTOR_SIZE];
|
||||||
|
char* e = s;
|
||||||
|
for (int i = 0; i < LUA_VECTOR_SIZE; ++i)
|
||||||
{
|
{
|
||||||
char s[LUAI_MAXNUM2STR * LUA_VECTOR_SIZE];
|
if (i != 0)
|
||||||
char* e = s;
|
|
||||||
for (int i = 0; i < LUA_VECTOR_SIZE; ++i)
|
|
||||||
{
|
{
|
||||||
if (i != 0)
|
*e++ = ',';
|
||||||
{
|
*e++ = ' ';
|
||||||
*e++ = ',';
|
|
||||||
*e++ = ' ';
|
|
||||||
}
|
|
||||||
e = luai_num2str(e, v[i]);
|
|
||||||
}
|
}
|
||||||
lua_pushlstring(L, s, e - s);
|
e = luai_num2str(e, v[i]);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
#if LUA_VECTOR_SIZE == 4
|
|
||||||
lua_pushfstring(L, LUA_NUMBER_FMT ", " LUA_NUMBER_FMT ", " LUA_NUMBER_FMT ", " LUA_NUMBER_FMT, v[0], v[1], v[2], v[3]);
|
|
||||||
#else
|
|
||||||
lua_pushfstring(L, LUA_NUMBER_FMT ", " LUA_NUMBER_FMT ", " LUA_NUMBER_FMT, v[0], v[1], v[2]);
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
lua_pushlstring(L, s, e - s);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -5,11 +5,14 @@
|
||||||
#include "lstate.h"
|
#include "lstate.h"
|
||||||
#include "lapi.h"
|
#include "lapi.h"
|
||||||
#include "ldo.h"
|
#include "ldo.h"
|
||||||
|
#include "ludata.h"
|
||||||
|
|
||||||
#include <ctype.h>
|
#include <ctype.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
LUAU_DYNAMIC_FASTFLAG(LuauMorePreciseLuaLTypeName)
|
||||||
|
|
||||||
static void writestring(const char* s, size_t l)
|
static void writestring(const char* s, size_t l)
|
||||||
{
|
{
|
||||||
fwrite(s, 1, l, stdout);
|
fwrite(s, 1, l, stdout);
|
||||||
|
@ -186,15 +189,31 @@ static int luaB_gcinfo(lua_State* L)
|
||||||
static int luaB_type(lua_State* L)
|
static int luaB_type(lua_State* L)
|
||||||
{
|
{
|
||||||
luaL_checkany(L, 1);
|
luaL_checkany(L, 1);
|
||||||
lua_pushstring(L, luaL_typename(L, 1));
|
if (DFFlag::LuauMorePreciseLuaLTypeName)
|
||||||
|
{
|
||||||
|
/* resulting name doesn't differentiate between userdata types */
|
||||||
|
lua_pushstring(L, lua_typename(L, lua_type(L, 1)));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lua_pushstring(L, luaL_typename(L, 1));
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int luaB_typeof(lua_State* L)
|
static int luaB_typeof(lua_State* L)
|
||||||
{
|
{
|
||||||
luaL_checkany(L, 1);
|
luaL_checkany(L, 1);
|
||||||
const TValue* obj = luaA_toobject(L, 1);
|
if (DFFlag::LuauMorePreciseLuaLTypeName)
|
||||||
lua_pushstring(L, luaT_objtypename(L, obj));
|
{
|
||||||
|
/* resulting name returns __type if specified unless the input is a newproxy-created userdata */
|
||||||
|
lua_pushstring(L, luaL_typename(L, 1));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const TValue* obj = luaA_toobject(L, 1);
|
||||||
|
lua_pushstring(L, luaT_objtypename(L, obj));
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,7 +422,7 @@ static int luaB_newproxy(lua_State* L)
|
||||||
|
|
||||||
bool needsmt = lua_toboolean(L, 1);
|
bool needsmt = lua_toboolean(L, 1);
|
||||||
|
|
||||||
lua_newuserdata(L, 0);
|
lua_newuserdatatagged(L, 0, UTAG_PROXY);
|
||||||
|
|
||||||
if (needsmt)
|
if (needsmt)
|
||||||
{
|
{
|
||||||
|
|
|
@ -168,6 +168,11 @@ static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f,
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int lua_stackdepth(lua_State* L)
|
||||||
|
{
|
||||||
|
return int(L->ci - L->base_ci);
|
||||||
|
}
|
||||||
|
|
||||||
int lua_getinfo(lua_State* L, int level, const char* what, lua_Debug* ar)
|
int lua_getinfo(lua_State* L, int level, const char* what, lua_Debug* ar)
|
||||||
{
|
{
|
||||||
int status = 0;
|
int status = 0;
|
||||||
|
|
|
@ -11,6 +11,8 @@
|
||||||
#include "lmem.h"
|
#include "lmem.h"
|
||||||
#include "ludata.h"
|
#include "ludata.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauGcAdditionalStats, false)
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
#define GC_SWEEPMAX 40
|
#define GC_SWEEPMAX 40
|
||||||
|
@ -53,17 +55,28 @@ static void recordGcStateTime(global_State* g, int startgcstate, double seconds,
|
||||||
case GCSpause:
|
case GCSpause:
|
||||||
// record root mark time if we have switched to next state
|
// record root mark time if we have switched to next state
|
||||||
if (g->gcstate == GCSpropagate)
|
if (g->gcstate == GCSpropagate)
|
||||||
|
{
|
||||||
g->gcstats.currcycle.marktime += seconds;
|
g->gcstats.currcycle.marktime += seconds;
|
||||||
|
|
||||||
|
if (FFlag::LuauGcAdditionalStats && assist)
|
||||||
|
g->gcstats.currcycle.markassisttime += seconds;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case GCSpropagate:
|
case GCSpropagate:
|
||||||
case GCSpropagateagain:
|
case GCSpropagateagain:
|
||||||
g->gcstats.currcycle.marktime += seconds;
|
g->gcstats.currcycle.marktime += seconds;
|
||||||
|
|
||||||
|
if (FFlag::LuauGcAdditionalStats && assist)
|
||||||
|
g->gcstats.currcycle.markassisttime += seconds;
|
||||||
break;
|
break;
|
||||||
case GCSatomic:
|
case GCSatomic:
|
||||||
g->gcstats.currcycle.atomictime += seconds;
|
g->gcstats.currcycle.atomictime += seconds;
|
||||||
break;
|
break;
|
||||||
case GCSsweep:
|
case GCSsweep:
|
||||||
g->gcstats.currcycle.sweeptime += seconds;
|
g->gcstats.currcycle.sweeptime += seconds;
|
||||||
|
|
||||||
|
if (FFlag::LuauGcAdditionalStats && assist)
|
||||||
|
g->gcstats.currcycle.sweepassisttime += seconds;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LUAU_ASSERT(!"Unexpected GC state");
|
LUAU_ASSERT(!"Unexpected GC state");
|
||||||
|
@ -78,7 +91,7 @@ static void recordGcStateTime(global_State* g, int startgcstate, double seconds,
|
||||||
static void startGcCycleStats(global_State* g)
|
static void startGcCycleStats(global_State* g)
|
||||||
{
|
{
|
||||||
g->gcstats.currcycle.starttimestamp = lua_clock();
|
g->gcstats.currcycle.starttimestamp = lua_clock();
|
||||||
g->gcstats.currcycle.waittime = g->gcstats.currcycle.starttimestamp - g->gcstats.lastcycle.endtimestamp;
|
g->gcstats.currcycle.pausetime = g->gcstats.currcycle.starttimestamp - g->gcstats.lastcycle.endtimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void finishGcCycleStats(global_State* g)
|
static void finishGcCycleStats(global_State* g)
|
||||||
|
@ -585,10 +598,21 @@ static size_t atomic(lua_State* L)
|
||||||
LUAU_ASSERT(g->gcstate == GCSatomic);
|
LUAU_ASSERT(g->gcstate == GCSatomic);
|
||||||
|
|
||||||
size_t work = 0;
|
size_t work = 0;
|
||||||
|
double currts = lua_clock();
|
||||||
|
double prevts = currts;
|
||||||
|
|
||||||
/* remark occasional upvalues of (maybe) dead threads */
|
/* remark occasional upvalues of (maybe) dead threads */
|
||||||
work += remarkupvals(g);
|
work += remarkupvals(g);
|
||||||
/* traverse objects caught by write barrier and by 'remarkupvals' */
|
/* traverse objects caught by write barrier and by 'remarkupvals' */
|
||||||
work += propagateall(g);
|
work += propagateall(g);
|
||||||
|
|
||||||
|
if (FFlag::LuauGcAdditionalStats)
|
||||||
|
{
|
||||||
|
currts = lua_clock();
|
||||||
|
g->gcstats.currcycle.atomictimeupval += currts - prevts;
|
||||||
|
prevts = currts;
|
||||||
|
}
|
||||||
|
|
||||||
/* remark weak tables */
|
/* remark weak tables */
|
||||||
g->gray = g->weak;
|
g->gray = g->weak;
|
||||||
g->weak = NULL;
|
g->weak = NULL;
|
||||||
|
@ -596,16 +620,41 @@ static size_t atomic(lua_State* L)
|
||||||
markobject(g, L); /* mark running thread */
|
markobject(g, L); /* mark running thread */
|
||||||
markmt(g); /* mark basic metatables (again) */
|
markmt(g); /* mark basic metatables (again) */
|
||||||
work += propagateall(g);
|
work += propagateall(g);
|
||||||
|
|
||||||
|
if (FFlag::LuauGcAdditionalStats)
|
||||||
|
{
|
||||||
|
currts = lua_clock();
|
||||||
|
g->gcstats.currcycle.atomictimeweak += currts - prevts;
|
||||||
|
prevts = currts;
|
||||||
|
}
|
||||||
|
|
||||||
/* remark gray again */
|
/* remark gray again */
|
||||||
g->gray = g->grayagain;
|
g->gray = g->grayagain;
|
||||||
g->grayagain = NULL;
|
g->grayagain = NULL;
|
||||||
work += propagateall(g);
|
work += propagateall(g);
|
||||||
work += cleartable(L, g->weak); /* remove collected objects from weak tables */
|
|
||||||
|
if (FFlag::LuauGcAdditionalStats)
|
||||||
|
{
|
||||||
|
currts = lua_clock();
|
||||||
|
g->gcstats.currcycle.atomictimegray += currts - prevts;
|
||||||
|
prevts = currts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* remove collected objects from weak tables */
|
||||||
|
work += cleartable(L, g->weak);
|
||||||
g->weak = NULL;
|
g->weak = NULL;
|
||||||
|
|
||||||
|
if (FFlag::LuauGcAdditionalStats)
|
||||||
|
{
|
||||||
|
currts = lua_clock();
|
||||||
|
g->gcstats.currcycle.atomictimeclear += currts - prevts;
|
||||||
|
}
|
||||||
|
|
||||||
/* flip current white */
|
/* flip current white */
|
||||||
g->currentwhite = cast_byte(otherwhite(g));
|
g->currentwhite = cast_byte(otherwhite(g));
|
||||||
g->sweepgcopage = g->allgcopages;
|
g->sweepgcopage = g->allgcopages;
|
||||||
g->gcstate = GCSsweep;
|
g->gcstate = GCSsweep;
|
||||||
|
|
||||||
return work;
|
return work;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -693,6 +742,9 @@ static size_t gcstep(lua_State* L, size_t limit)
|
||||||
|
|
||||||
if (!g->gray)
|
if (!g->gray)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauGcAdditionalStats)
|
||||||
|
g->gcstats.currcycle.propagatework = g->gcstats.currcycle.explicitwork + g->gcstats.currcycle.assistwork;
|
||||||
|
|
||||||
// perform one iteration over 'gray again' list
|
// perform one iteration over 'gray again' list
|
||||||
g->gray = g->grayagain;
|
g->gray = g->grayagain;
|
||||||
g->grayagain = NULL;
|
g->grayagain = NULL;
|
||||||
|
@ -710,6 +762,10 @@ static size_t gcstep(lua_State* L, size_t limit)
|
||||||
|
|
||||||
if (!g->gray) /* no more `gray' objects */
|
if (!g->gray) /* no more `gray' objects */
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauGcAdditionalStats)
|
||||||
|
g->gcstats.currcycle.propagateagainwork =
|
||||||
|
g->gcstats.currcycle.explicitwork + g->gcstats.currcycle.assistwork - g->gcstats.currcycle.propagatework;
|
||||||
|
|
||||||
g->gcstate = GCSatomic;
|
g->gcstate = GCSatomic;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -811,6 +867,12 @@ static size_t getheaptrigger(global_State* g, size_t heapgoal)
|
||||||
void luaC_step(lua_State* L, bool assist)
|
void luaC_step(lua_State* L, bool assist)
|
||||||
{
|
{
|
||||||
global_State* g = L->global;
|
global_State* g = L->global;
|
||||||
|
|
||||||
|
if (assist)
|
||||||
|
g->gcstats.currcycle.assistrequests += g->gcstepsize;
|
||||||
|
else
|
||||||
|
g->gcstats.currcycle.explicitrequests += g->gcstepsize;
|
||||||
|
|
||||||
int lim = (g->gcstepsize / 100) * g->gcstepmul; /* how much to work */
|
int lim = (g->gcstepsize / 100) * g->gcstepmul; /* how much to work */
|
||||||
LUAU_ASSERT(g->totalbytes >= g->GCthreshold);
|
LUAU_ASSERT(g->totalbytes >= g->GCthreshold);
|
||||||
size_t debt = g->totalbytes - g->GCthreshold;
|
size_t debt = g->totalbytes - g->GCthreshold;
|
||||||
|
@ -833,6 +895,11 @@ void luaC_step(lua_State* L, bool assist)
|
||||||
|
|
||||||
recordGcStateTime(g, lastgcstate, lua_clock() - lasttimestamp, assist);
|
recordGcStateTime(g, lastgcstate, lua_clock() - lasttimestamp, assist);
|
||||||
|
|
||||||
|
if (lastgcstate == GCSpropagate)
|
||||||
|
g->gcstats.currcycle.markrequests += g->gcstepsize;
|
||||||
|
else if (lastgcstate == GCSsweep)
|
||||||
|
g->gcstats.currcycle.sweeprequests += g->gcstepsize;
|
||||||
|
|
||||||
// at the end of the last cycle
|
// at the end of the last cycle
|
||||||
if (g->gcstate == GCSpause)
|
if (g->gcstate == GCSpause)
|
||||||
{
|
{
|
||||||
|
@ -844,6 +911,9 @@ void luaC_step(lua_State* L, bool assist)
|
||||||
|
|
||||||
finishGcCycleStats(g);
|
finishGcCycleStats(g);
|
||||||
|
|
||||||
|
if (FFlag::LuauGcAdditionalStats)
|
||||||
|
g->gcstats.currcycle.starttotalsizebytes = g->totalbytes;
|
||||||
|
|
||||||
g->gcstats.currcycle.heapgoalsizebytes = heapgoal;
|
g->gcstats.currcycle.heapgoalsizebytes = heapgoal;
|
||||||
g->gcstats.currcycle.heaptriggersizebytes = heaptrigger;
|
g->gcstats.currcycle.heaptriggersizebytes = heaptrigger;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,13 @@
|
||||||
#include "lobject.h"
|
#include "lobject.h"
|
||||||
#include "lstate.h"
|
#include "lstate.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
** Default settings for GC tunables (settable via lua_gc)
|
||||||
|
*/
|
||||||
|
#define LUAI_GCGOAL 200 /* 200% (allow heap to double compared to live heap size) */
|
||||||
|
#define LUAI_GCSTEPMUL 200 /* GC runs 'twice the speed' of memory allocation */
|
||||||
|
#define LUAI_GCSTEPSIZE 1 /* GC runs every KB of memory allocation */
|
||||||
|
|
||||||
/*
|
/*
|
||||||
** Possible states of the Garbage Collector
|
** Possible states of the Garbage Collector
|
||||||
*/
|
*/
|
||||||
|
@ -111,13 +118,6 @@
|
||||||
luaC_barrierf(L, obj2gco(p), obj2gco(o)); \
|
luaC_barrierf(L, obj2gco(p), obj2gco(o)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: remove with FFlagLuauGcForwardMetatableBarrier
|
|
||||||
#define luaC_objbarriert(L, t, o) \
|
|
||||||
{ \
|
|
||||||
if (isblack(obj2gco(t)) && iswhite(obj2gco(o))) \
|
|
||||||
luaC_barriertable(L, t, obj2gco(o)); \
|
|
||||||
}
|
|
||||||
|
|
||||||
#define luaC_upvalbarrier(L, uv, tv) \
|
#define luaC_upvalbarrier(L, uv, tv) \
|
||||||
{ \
|
{ \
|
||||||
if (iscollectable(tv) && iswhite(gcvalue(tv)) && (!(uv) || ((UpVal*)uv)->v != &((UpVal*)uv)->u.value)) \
|
if (iscollectable(tv) && iswhite(gcvalue(tv)) && (!(uv) || ((UpVal*)uv)->v != &((UpVal*)uv)->u.value)) \
|
||||||
|
|
|
@ -98,7 +98,7 @@
|
||||||
*/
|
*/
|
||||||
#if defined(__APPLE__)
|
#if defined(__APPLE__)
|
||||||
#define ABISWITCH(x64, ms32, gcc32) (sizeof(void*) == 8 ? x64 : gcc32)
|
#define ABISWITCH(x64, ms32, gcc32) (sizeof(void*) == 8 ? x64 : gcc32)
|
||||||
#elif defined(__i386__)
|
#elif defined(__i386__) && !defined(_MSC_VER)
|
||||||
#define ABISWITCH(x64, ms32, gcc32) (gcc32)
|
#define ABISWITCH(x64, ms32, gcc32) (gcc32)
|
||||||
#else
|
#else
|
||||||
// Android somehow uses a similar ABI to MSVC, *not* to iOS...
|
// Android somehow uses a similar ABI to MSVC, *not* to iOS...
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#include "lcommon.h"
|
#include "lcommon.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <stdio.h> // TODO: Remove with LuauSchubfach
|
|
||||||
|
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
#include <intrin.h>
|
#include <intrin.h>
|
||||||
|
@ -18,8 +17,6 @@
|
||||||
|
|
||||||
// The code uses the notation from the paper for local variables where appropriate, and refers to paper sections/figures/results.
|
// The code uses the notation from the paper for local variables where appropriate, and refers to paper sections/figures/results.
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauSchubfach, false)
|
|
||||||
|
|
||||||
// 9.8.2. Precomputed table for 128-bit overestimates of powers of 10 (see figure 3 for table bounds)
|
// 9.8.2. Precomputed table for 128-bit overestimates of powers of 10 (see figure 3 for table bounds)
|
||||||
// To avoid storing 616 128-bit numbers directly we use a technique inspired by Dragonbox implementation and store 16 consecutive
|
// To avoid storing 616 128-bit numbers directly we use a technique inspired by Dragonbox implementation and store 16 consecutive
|
||||||
// powers using a 128-bit baseline and a bitvector with 1-bit scale and 3-bit offset for the delta between each entry and base*5^k
|
// powers using a 128-bit baseline and a bitvector with 1-bit scale and 3-bit offset for the delta between each entry and base*5^k
|
||||||
|
@ -275,12 +272,6 @@ inline char* trimzero(char* end)
|
||||||
|
|
||||||
char* luai_num2str(char* buf, double n)
|
char* luai_num2str(char* buf, double n)
|
||||||
{
|
{
|
||||||
if (!FFlag::LuauSchubfach)
|
|
||||||
{
|
|
||||||
snprintf(buf, LUAI_MAXNUM2STR, LUA_NUMBER_FMT, n);
|
|
||||||
return buf + strlen(buf);
|
|
||||||
}
|
|
||||||
|
|
||||||
// IEEE-754
|
// IEEE-754
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
|
|
|
@ -55,7 +55,6 @@ LUAU_FASTMATH_END
|
||||||
#define luai_num2unsigned(i, n) ((i) = (unsigned)(long long)(n))
|
#define luai_num2unsigned(i, n) ((i) = (unsigned)(long long)(n))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define LUA_NUMBER_FMT "%.14g" /* TODO: Remove with LuauSchubfach */
|
|
||||||
#define LUAI_MAXNUM2STR 48
|
#define LUAI_MAXNUM2STR 48
|
||||||
|
|
||||||
LUAI_FUNC char* luai_num2str(char* buf, double n);
|
LUAI_FUNC char* luai_num2str(char* buf, double n);
|
||||||
|
|
|
@ -77,25 +77,46 @@ typedef struct CallInfo
|
||||||
|
|
||||||
struct GCCycleStats
|
struct GCCycleStats
|
||||||
{
|
{
|
||||||
|
size_t starttotalsizebytes = 0;
|
||||||
size_t heapgoalsizebytes = 0;
|
size_t heapgoalsizebytes = 0;
|
||||||
size_t heaptriggersizebytes = 0;
|
size_t heaptriggersizebytes = 0;
|
||||||
|
|
||||||
double waittime = 0.0; // time from end of the last cycle to the start of a new one
|
double pausetime = 0.0; // time from end of the last cycle to the start of a new one
|
||||||
|
|
||||||
double starttimestamp = 0.0;
|
double starttimestamp = 0.0;
|
||||||
double endtimestamp = 0.0;
|
double endtimestamp = 0.0;
|
||||||
|
|
||||||
double marktime = 0.0;
|
double marktime = 0.0;
|
||||||
|
double markassisttime = 0.0;
|
||||||
|
double markmaxexplicittime = 0.0;
|
||||||
|
size_t markexplicitsteps = 0;
|
||||||
|
size_t markrequests = 0;
|
||||||
|
|
||||||
double atomicstarttimestamp = 0.0;
|
double atomicstarttimestamp = 0.0;
|
||||||
size_t atomicstarttotalsizebytes = 0;
|
size_t atomicstarttotalsizebytes = 0;
|
||||||
double atomictime = 0.0;
|
double atomictime = 0.0;
|
||||||
|
|
||||||
|
// specific atomic stage parts
|
||||||
|
double atomictimeupval = 0.0;
|
||||||
|
double atomictimeweak = 0.0;
|
||||||
|
double atomictimegray = 0.0;
|
||||||
|
double atomictimeclear = 0.0;
|
||||||
|
|
||||||
double sweeptime = 0.0;
|
double sweeptime = 0.0;
|
||||||
|
double sweepassisttime = 0.0;
|
||||||
|
double sweepmaxexplicittime = 0.0;
|
||||||
|
size_t sweepexplicitsteps = 0;
|
||||||
|
size_t sweeprequests = 0;
|
||||||
|
|
||||||
|
size_t assistrequests = 0;
|
||||||
|
size_t explicitrequests = 0;
|
||||||
|
|
||||||
size_t assistwork = 0;
|
size_t assistwork = 0;
|
||||||
size_t explicitwork = 0;
|
size_t explicitwork = 0;
|
||||||
|
|
||||||
|
size_t propagatework = 0;
|
||||||
|
size_t propagateagainwork = 0;
|
||||||
|
|
||||||
size_t endtotalsizebytes = 0;
|
size_t endtotalsizebytes = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ void luaS_resize(lua_State* L, int newsize)
|
||||||
{
|
{
|
||||||
TString* p = tb->hash[i];
|
TString* p = tb->hash[i];
|
||||||
while (p)
|
while (p)
|
||||||
{ /* for each node in the list */
|
{ /* for each node in the list */
|
||||||
TString* next = p->next; /* save next */
|
TString* next = p->next; /* save next */
|
||||||
unsigned int h = p->hash;
|
unsigned int h = p->hash;
|
||||||
int h1 = lmod(h, newsize); /* new position */
|
int h1 = lmod(h, newsize); /* new position */
|
||||||
|
|
|
@ -24,6 +24,8 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTableRehashRework, false)
|
||||||
|
|
||||||
// max size of both array and hash part is 2^MAXBITS
|
// max size of both array and hash part is 2^MAXBITS
|
||||||
#define MAXBITS 26
|
#define MAXBITS 26
|
||||||
#define MAXSIZE (1 << MAXBITS)
|
#define MAXSIZE (1 << MAXBITS)
|
||||||
|
@ -351,6 +353,22 @@ static void setnodevector(lua_State* L, Table* t, int size)
|
||||||
t->lastfree = size; /* all positions are free */
|
t->lastfree = size; /* all positions are free */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static TValue* newkey(lua_State* L, Table* t, const TValue* key);
|
||||||
|
|
||||||
|
static TValue* arrayornewkey(lua_State* L, Table* t, const TValue* key)
|
||||||
|
{
|
||||||
|
if (ttisnumber(key))
|
||||||
|
{
|
||||||
|
int k;
|
||||||
|
double n = nvalue(key);
|
||||||
|
luai_num2int(k, n);
|
||||||
|
if (luai_numeq(cast_num(k), n) && cast_to(unsigned int, k - 1) < cast_to(unsigned int, t->sizearray))
|
||||||
|
return &t->array[k - 1];
|
||||||
|
}
|
||||||
|
|
||||||
|
return newkey(L, t, key);
|
||||||
|
}
|
||||||
|
|
||||||
static void resize(lua_State* L, Table* t, int nasize, int nhsize)
|
static void resize(lua_State* L, Table* t, int nasize, int nhsize)
|
||||||
{
|
{
|
||||||
if (nasize > MAXSIZE || nhsize > MAXSIZE)
|
if (nasize > MAXSIZE || nhsize > MAXSIZE)
|
||||||
|
@ -369,22 +387,50 @@ static void resize(lua_State* L, Table* t, int nasize, int nhsize)
|
||||||
for (int i = nasize; i < oldasize; i++)
|
for (int i = nasize; i < oldasize; i++)
|
||||||
{
|
{
|
||||||
if (!ttisnil(&t->array[i]))
|
if (!ttisnil(&t->array[i]))
|
||||||
setobjt2t(L, luaH_setnum(L, t, i + 1), &t->array[i]);
|
{
|
||||||
|
if (FFlag::LuauTableRehashRework)
|
||||||
|
{
|
||||||
|
TValue ok;
|
||||||
|
setnvalue(&ok, cast_num(i + 1));
|
||||||
|
setobjt2t(L, newkey(L, t, &ok), &t->array[i]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setobjt2t(L, luaH_setnum(L, t, i + 1), &t->array[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/* shrink array */
|
/* shrink array */
|
||||||
luaM_reallocarray(L, t->array, oldasize, nasize, TValue, t->memcat);
|
luaM_reallocarray(L, t->array, oldasize, nasize, TValue, t->memcat);
|
||||||
}
|
}
|
||||||
/* re-insert elements from hash part */
|
/* re-insert elements from hash part */
|
||||||
for (int i = twoto(oldhsize) - 1; i >= 0; i--)
|
if (FFlag::LuauTableRehashRework)
|
||||||
{
|
{
|
||||||
LuaNode* old = nold + i;
|
for (int i = twoto(oldhsize) - 1; i >= 0; i--)
|
||||||
if (!ttisnil(gval(old)))
|
|
||||||
{
|
{
|
||||||
TValue ok;
|
LuaNode* old = nold + i;
|
||||||
getnodekey(L, &ok, old);
|
if (!ttisnil(gval(old)))
|
||||||
setobjt2t(L, luaH_set(L, t, &ok), gval(old));
|
{
|
||||||
|
TValue ok;
|
||||||
|
getnodekey(L, &ok, old);
|
||||||
|
setobjt2t(L, arrayornewkey(L, t, &ok), gval(old));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int i = twoto(oldhsize) - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
LuaNode* old = nold + i;
|
||||||
|
if (!ttisnil(gval(old)))
|
||||||
|
{
|
||||||
|
TValue ok;
|
||||||
|
getnodekey(L, &ok, old);
|
||||||
|
setobjt2t(L, luaH_set(L, t, &ok), gval(old));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (nold != dummynode)
|
if (nold != dummynode)
|
||||||
luaM_freearray(L, nold, twoto(oldhsize), LuaNode, t->memcat); /* free old array */
|
luaM_freearray(L, nold, twoto(oldhsize), LuaNode, t->memcat); /* free old array */
|
||||||
}
|
}
|
||||||
|
@ -447,7 +493,8 @@ void luaH_free(lua_State* L, Table* t, lua_Page* page)
|
||||||
{
|
{
|
||||||
if (t->node != dummynode)
|
if (t->node != dummynode)
|
||||||
luaM_freearray(L, t->node, sizenode(t), LuaNode, t->memcat);
|
luaM_freearray(L, t->node, sizenode(t), LuaNode, t->memcat);
|
||||||
luaM_freearray(L, t->array, t->sizearray, TValue, t->memcat);
|
if (t->array)
|
||||||
|
luaM_freearray(L, t->array, t->sizearray, TValue, t->memcat);
|
||||||
luaM_freegco(L, t, sizeof(Table), t->memcat, page);
|
luaM_freegco(L, t, sizeof(Table), t->memcat, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -481,7 +528,16 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
|
||||||
if (n == NULL)
|
if (n == NULL)
|
||||||
{ /* cannot find a free place? */
|
{ /* cannot find a free place? */
|
||||||
rehash(L, t, key); /* grow table */
|
rehash(L, t, key); /* grow table */
|
||||||
return luaH_set(L, t, key); /* re-insert key into grown table */
|
|
||||||
|
if (!FFlag::LuauTableRehashRework)
|
||||||
|
{
|
||||||
|
return luaH_set(L, t, key); /* re-insert key into grown table */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// after rehash, numeric keys might be located in the new array part, but won't be found in the node part
|
||||||
|
return arrayornewkey(L, t, key);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
LUAU_ASSERT(n != dummynode);
|
LUAU_ASSERT(n != dummynode);
|
||||||
TValue mk;
|
TValue mk;
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
|
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
|
||||||
#include "lualib.h"
|
#include "lualib.h"
|
||||||
|
|
||||||
|
#include "lapi.h"
|
||||||
#include "lstate.h"
|
#include "lstate.h"
|
||||||
#include "ltable.h"
|
#include "ltable.h"
|
||||||
#include "lstring.h"
|
#include "lstring.h"
|
||||||
|
@ -9,6 +10,8 @@
|
||||||
#include "ldebug.h"
|
#include "ldebug.h"
|
||||||
#include "lvm.h"
|
#include "lvm.h"
|
||||||
|
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauTableClone, false)
|
||||||
|
|
||||||
static int foreachi(lua_State* L)
|
static int foreachi(lua_State* L)
|
||||||
{
|
{
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
@ -507,6 +510,23 @@ static int tisfrozen(lua_State* L)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int tclone(lua_State* L)
|
||||||
|
{
|
||||||
|
if (!FFlag::LuauTableClone)
|
||||||
|
luaG_runerror(L, "table.clone is not available");
|
||||||
|
|
||||||
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
luaL_argcheck(L, !luaL_getmetafield(L, 1, "__metatable"), 1, "table has a protected metatable");
|
||||||
|
|
||||||
|
Table* tt = luaH_clone(L, hvalue(L->base));
|
||||||
|
|
||||||
|
TValue v;
|
||||||
|
sethvalue(L, &v, tt);
|
||||||
|
luaA_pushobject(L, &v);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
static const luaL_Reg tab_funcs[] = {
|
static const luaL_Reg tab_funcs[] = {
|
||||||
{"concat", tconcat},
|
{"concat", tconcat},
|
||||||
{"foreach", foreach},
|
{"foreach", foreach},
|
||||||
|
@ -524,6 +544,7 @@ static const luaL_Reg tab_funcs[] = {
|
||||||
{"clear", tclear},
|
{"clear", tclear},
|
||||||
{"freeze", tfreeze},
|
{"freeze", tfreeze},
|
||||||
{"isfrozen", tisfrozen},
|
{"isfrozen", tisfrozen},
|
||||||
|
{"clone", tclone},
|
||||||
{NULL, NULL},
|
{NULL, NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
#include "lstate.h"
|
#include "lstate.h"
|
||||||
#include "lstring.h"
|
#include "lstring.h"
|
||||||
|
#include "ludata.h"
|
||||||
#include "ltable.h"
|
#include "ltable.h"
|
||||||
#include "lgc.h"
|
#include "lgc.h"
|
||||||
|
|
||||||
|
@ -116,7 +117,7 @@ const TValue* luaT_gettmbyobj(lua_State* L, const TValue* o, TMS event)
|
||||||
|
|
||||||
const TString* luaT_objtypenamestr(lua_State* L, const TValue* o)
|
const TString* luaT_objtypenamestr(lua_State* L, const TValue* o)
|
||||||
{
|
{
|
||||||
if (ttisuserdata(o) && uvalue(o)->tag && uvalue(o)->metatable)
|
if (ttisuserdata(o) && uvalue(o)->tag != UTAG_PROXY && uvalue(o)->metatable)
|
||||||
{
|
{
|
||||||
const TValue* type = luaH_getstr(uvalue(o)->metatable, L->global->tmname[TM_TYPE]);
|
const TValue* type = luaH_getstr(uvalue(o)->metatable, L->global->tmname[TM_TYPE]);
|
||||||
|
|
||||||
|
|
|
@ -22,13 +22,11 @@ Udata* luaU_newudata(lua_State* L, size_t s, int tag)
|
||||||
|
|
||||||
void luaU_freeudata(lua_State* L, Udata* u, lua_Page* page)
|
void luaU_freeudata(lua_State* L, Udata* u, lua_Page* page)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(u->tag < LUA_UTAG_LIMIT || u->tag == UTAG_IDTOR);
|
|
||||||
|
|
||||||
void (*dtor)(void*) = nullptr;
|
void (*dtor)(void*) = nullptr;
|
||||||
if (u->tag == UTAG_IDTOR)
|
if (u->tag < LUA_UTAG_LIMIT)
|
||||||
memcpy(&dtor, &u->data + u->len - sizeof(dtor), sizeof(dtor));
|
|
||||||
else if (u->tag)
|
|
||||||
dtor = L->global->udatagc[u->tag];
|
dtor = L->global->udatagc[u->tag];
|
||||||
|
else if (u->tag == UTAG_IDTOR)
|
||||||
|
memcpy(&dtor, &u->data + u->len - sizeof(dtor), sizeof(dtor));
|
||||||
|
|
||||||
if (dtor)
|
if (dtor)
|
||||||
dtor(u->data);
|
dtor(u->data);
|
||||||
|
|
|
@ -7,6 +7,9 @@
|
||||||
/* special tag value is used for user data with inline dtors */
|
/* special tag value is used for user data with inline dtors */
|
||||||
#define UTAG_IDTOR LUA_UTAG_LIMIT
|
#define UTAG_IDTOR LUA_UTAG_LIMIT
|
||||||
|
|
||||||
|
/* special tag value is used for newproxy-created user data (all other user data objects are host-exposed) */
|
||||||
|
#define UTAG_PROXY (LUA_UTAG_LIMIT + 1)
|
||||||
|
|
||||||
#define sizeudata(len) (offsetof(Udata, data) + len)
|
#define sizeudata(len) (offsetof(Udata, data) + len)
|
||||||
|
|
||||||
LUAI_FUNC Udata* luaU_newudata(lua_State* L, size_t s, int tag);
|
LUAI_FUNC Udata* luaU_newudata(lua_State* L, size_t s, int tag);
|
||||||
|
|
|
@ -77,6 +77,11 @@
|
||||||
if (LUAU_UNLIKELY(!!interrupt)) \
|
if (LUAU_UNLIKELY(!!interrupt)) \
|
||||||
{ /* the interrupt hook is called right before we advance pc */ \
|
{ /* the interrupt hook is called right before we advance pc */ \
|
||||||
VM_PROTECT(L->ci->savedpc++; interrupt(L, -1)); \
|
VM_PROTECT(L->ci->savedpc++; interrupt(L, -1)); \
|
||||||
|
if (L->status != 0) \
|
||||||
|
{ \
|
||||||
|
L->ci->savedpc--; \
|
||||||
|
goto exit; \
|
||||||
|
} \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue