Update to `0.2.0+luau519`

This commit is contained in:
Alex Orlenko 2022-03-19 19:24:34 +00:00
parent 1a8f5dcfcf
commit bbb35d7de1
No known key found for this signature in database
GPG Key ID: 4C150C250863B96D
24 changed files with 391 additions and 223 deletions

View File

@ -1,6 +1,6 @@
[package]
name = "luau0-src"
version = "0.1.0+luau516"
version = "0.2.0+luau519"
authors = ["Aleksandr Orlenko <zxteam@protonmail.com>"]
edition = "2018"
repository = "https://github.com/khvzak/luau-src-rs"

View File

@ -11,19 +11,11 @@
LUAU_FASTINTVARIABLE(LuauRecursionLimit, 1000)
LUAU_FASTINTVARIABLE(LuauParseErrorLimit, 100)
LUAU_FASTFLAGVARIABLE(LuauParseSingletonTypes, false)
LUAU_FASTFLAGVARIABLE(LuauParseTypeAliasDefaults, false)
LUAU_FASTFLAGVARIABLE(LuauParseAllHotComments, false)
LUAU_FASTFLAGVARIABLE(LuauTableFieldFunctionDebugname, false)
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)
: location(location)
, message(message)
@ -147,54 +139,13 @@ ParseResult Parser::parse(const char* buffer, size_t bufferSize, AstNameTable& n
{
LUAU_TIMETRACE_SCOPE("Parser::parse", "Parser");
Parser p(buffer, bufferSize, names, allocator, FFlag::LuauParseAllHotComments ? options : ParseOptions());
Parser p(buffer, bufferSize, names, allocator, options);
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)};
}
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)};
}
return ParseResult{root, std::move(p.hotcomments), std::move(p.parseErrors), std::move(p.commentLocations)};
}
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[Lexeme::Type::Eof] = 1;
if (FFlag::LuauParseAllHotComments)
lexer.setSkipComments(true);
// required for lookahead() to work across a comment boundary and for nextLexeme() to work when captureComments is false
lexer.setSkipComments(true);
// read first lexeme
// read first lexeme (any hot comments get .header = true)
LUAU_ASSERT(hotcommentHeader);
nextLexeme();
// 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)
name = Name(nameError, lexer.current().location);
auto [generics, genericPacks] = parseGenericTypeList(/* withDefaultValues= */ FFlag::LuauParseTypeAliasDefaults);
auto [generics, genericPacks] = parseGenericTypeList(/* withDefaultValues= */ true);
expectAndConsume('=', "type alias");
@ -2832,49 +2784,31 @@ void Parser::nextLexeme()
{
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();
commentLocations.push_back(Comment{lexeme.type, lexeme.location});
const char* text = lexeme.data;
// 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;
unsigned int end = lexeme.length;
while (end > 0 && isSpace(text[end - 1]))
--end;
// 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 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;
hotcomments.push_back({hotcommentHeader, lexeme.location, std::string(text + 1, text + end)});
}
type = lexer.next(/* skipComments= */ false).type;
}
}
else

View File

@ -4,8 +4,6 @@
#include "Luau/Bytecode.h"
#include "Luau/Compiler.h"
LUAU_FASTFLAGVARIABLE(LuauCompileSelectBuiltin2, false)
namespace Luau
{
namespace Compile
@ -64,7 +62,7 @@ int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& options)
if (builtin.isGlobal("unpack"))
return LBF_TABLE_UNPACK;
if (FFlag::LuauCompileSelectBuiltin2 && builtin.isGlobal("select"))
if (builtin.isGlobal("select"))
return LBF_SELECT_VARARG;
if (builtin.object == "math")

View File

@ -15,8 +15,6 @@
#include <bitset>
#include <math.h>
LUAU_FASTFLAG(LuauCompileSelectBuiltin2)
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)
{
LUAU_ASSERT(FFlag::LuauCompileSelectBuiltin2);
LUAU_ASSERT(targetCount == 1);
LUAU_ASSERT(!expr->self);
LUAU_ASSERT(expr->args.size == 2 && expr->args.data[1]->is<AstExprVarargs>());
@ -407,7 +404,6 @@ struct Compiler
if (bfid == LBF_SELECT_VARARG)
{
LUAU_ASSERT(FFlag::LuauCompileSelectBuiltin2);
// 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
if (multRet == false && targetCount == 1 && expr->args.size == 2 && expr->args.data[1]->is<AstExprVarargs>())

View File

@ -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 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_pushlightuserdata(lua_State* L, void* p);
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)
*/
@ -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 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 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
{
/* stop and resume incremental garbage collection */
LUA_GCSTOP,
LUA_GCRESTART,
/* run a full GC cycle; not recommended for latency sensitive applications */
LUA_GCCOLLECT,
/* return the heap size in KB and the remainder in bytes */
LUA_GCCOUNT,
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,
// 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
/*
** perform an explicit GC step, with the step size specified in KB
**
** 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,
/*
** 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_GCSETSTEPMUL,
LUA_GCSETSTEPSIZE,
@ -249,6 +277,14 @@ enum lua_GCOp
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
*/
@ -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_clonefunction(lua_State* L, int idx);
/*
** 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 */
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_getargument(lua_State* L, int level, int n);
LUA_API const char* lua_getlocal(lua_State* L, int level, int n);

View File

@ -59,33 +59,6 @@
#define LUA_IDSIZE 256
#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 */
#ifndef LUA_MINSTACK
#define LUA_MINSTACK 20

View File

@ -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_typename(lua_State* L, int idx);
/*
** ===============================================================
** 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_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_opt(L, f, n, d) (lua_isnoneornil(L, (n)) ? (d) : f(L, (n)))

View File

@ -14,7 +14,7 @@
#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"
"$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)
{
if (L->ci == L->base_ci) /* no enclosing function? */
return L->gt; /* use global table as environment */
if (L->ci == L->base_ci) /* no enclosing function? */
return L->gt; /* use global table as environment */
else
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");
hvalue(obj)->metatable = mt;
if (mt)
{
if (FFlag::LuauGcForwardMetatableBarrier)
{
luaC_objbarrier(L, hvalue(obj), mt);
}
else
{
luaC_objbarriert(L, hvalue(obj), mt);
}
}
luaC_objbarrier(L, hvalue(obj), mt);
break;
}
case LUA_TUSERDATA:
@ -1069,6 +1060,8 @@ int lua_gc(lua_State* L, int what, int data)
g->GCthreshold = 0;
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
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 (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)
{
api_check(L, unsigned(tag) < LUA_UTAG_LIMIT);
api_check(L, unsigned(tag) < LUA_UTAG_LIMIT || tag == UTAG_PROXY);
luaC_checkGC(L);
luaC_checkthreadsleep(L);
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;
}
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)
{
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];
}

View File

@ -11,7 +11,7 @@
#include <string.h>
LUAU_FASTFLAG(LuauSchubfach)
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauMorePreciseLuaLTypeName, false)
/* convert a stack index to positive */
#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;
}
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
@ -480,18 +493,13 @@ const char* luaL_tolstring(lua_State* L, int idx, size_t* len)
switch (lua_type(L, idx))
{
case LUA_TNUMBER:
if (FFlag::LuauSchubfach)
{
double n = lua_tonumber(L, idx);
char s[LUAI_MAXNUM2STR];
char* e = luai_num2str(s, n);
lua_pushlstring(L, s, e - s);
}
else
{
lua_pushstring(L, lua_tostring(L, idx));
}
{
double n = lua_tonumber(L, idx);
char s[LUAI_MAXNUM2STR];
char* e = luai_num2str(s, n);
lua_pushlstring(L, s, e - s);
break;
}
case LUA_TSTRING:
lua_pushvalue(L, idx);
break;
@ -505,29 +513,18 @@ const char* luaL_tolstring(lua_State* L, int idx, size_t* len)
{
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];
char* e = s;
for (int i = 0; i < LUA_VECTOR_SIZE; ++i)
if (i != 0)
{
if (i != 0)
{
*e++ = ',';
*e++ = ' ';
}
e = luai_num2str(e, v[i]);
*e++ = ',';
*e++ = ' ';
}
lua_pushlstring(L, s, e - s);
}
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
e = luai_num2str(e, v[i]);
}
lua_pushlstring(L, s, e - s);
break;
}
default:

View File

@ -5,11 +5,14 @@
#include "lstate.h"
#include "lapi.h"
#include "ldo.h"
#include "ludata.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
LUAU_DYNAMIC_FASTFLAG(LuauMorePreciseLuaLTypeName)
static void writestring(const char* s, size_t l)
{
fwrite(s, 1, l, stdout);
@ -186,15 +189,31 @@ static int luaB_gcinfo(lua_State* L)
static int luaB_type(lua_State* L)
{
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;
}
static int luaB_typeof(lua_State* L)
{
luaL_checkany(L, 1);
const TValue* obj = luaA_toobject(L, 1);
lua_pushstring(L, luaT_objtypename(L, obj));
if (DFFlag::LuauMorePreciseLuaLTypeName)
{
/* 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;
}
@ -403,7 +422,7 @@ static int luaB_newproxy(lua_State* L)
bool needsmt = lua_toboolean(L, 1);
lua_newuserdata(L, 0);
lua_newuserdatatagged(L, 0, UTAG_PROXY);
if (needsmt)
{

View File

@ -168,6 +168,11 @@ static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f,
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 status = 0;

View File

@ -11,6 +11,8 @@
#include "lmem.h"
#include "ludata.h"
LUAU_FASTFLAGVARIABLE(LuauGcAdditionalStats, false)
#include <string.h>
#define GC_SWEEPMAX 40
@ -53,17 +55,28 @@ static void recordGcStateTime(global_State* g, int startgcstate, double seconds,
case GCSpause:
// record root mark time if we have switched to next state
if (g->gcstate == GCSpropagate)
{
g->gcstats.currcycle.marktime += seconds;
if (FFlag::LuauGcAdditionalStats && assist)
g->gcstats.currcycle.markassisttime += seconds;
}
break;
case GCSpropagate:
case GCSpropagateagain:
g->gcstats.currcycle.marktime += seconds;
if (FFlag::LuauGcAdditionalStats && assist)
g->gcstats.currcycle.markassisttime += seconds;
break;
case GCSatomic:
g->gcstats.currcycle.atomictime += seconds;
break;
case GCSsweep:
g->gcstats.currcycle.sweeptime += seconds;
if (FFlag::LuauGcAdditionalStats && assist)
g->gcstats.currcycle.sweepassisttime += seconds;
break;
default:
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)
{
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)
@ -585,10 +598,21 @@ static size_t atomic(lua_State* L)
LUAU_ASSERT(g->gcstate == GCSatomic);
size_t work = 0;
double currts = lua_clock();
double prevts = currts;
/* remark occasional upvalues of (maybe) dead threads */
work += remarkupvals(g);
/* traverse objects caught by write barrier and by 'remarkupvals' */
work += propagateall(g);
if (FFlag::LuauGcAdditionalStats)
{
currts = lua_clock();
g->gcstats.currcycle.atomictimeupval += currts - prevts;
prevts = currts;
}
/* remark weak tables */
g->gray = g->weak;
g->weak = NULL;
@ -596,16 +620,41 @@ static size_t atomic(lua_State* L)
markobject(g, L); /* mark running thread */
markmt(g); /* mark basic metatables (again) */
work += propagateall(g);
if (FFlag::LuauGcAdditionalStats)
{
currts = lua_clock();
g->gcstats.currcycle.atomictimeweak += currts - prevts;
prevts = currts;
}
/* remark gray again */
g->gray = g->grayagain;
g->grayagain = NULL;
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;
if (FFlag::LuauGcAdditionalStats)
{
currts = lua_clock();
g->gcstats.currcycle.atomictimeclear += currts - prevts;
}
/* flip current white */
g->currentwhite = cast_byte(otherwhite(g));
g->sweepgcopage = g->allgcopages;
g->gcstate = GCSsweep;
return work;
}
@ -693,6 +742,9 @@ static size_t gcstep(lua_State* L, size_t limit)
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
g->gray = g->grayagain;
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 (FFlag::LuauGcAdditionalStats)
g->gcstats.currcycle.propagateagainwork =
g->gcstats.currcycle.explicitwork + g->gcstats.currcycle.assistwork - g->gcstats.currcycle.propagatework;
g->gcstate = GCSatomic;
}
break;
@ -811,6 +867,12 @@ static size_t getheaptrigger(global_State* g, size_t heapgoal)
void luaC_step(lua_State* L, bool assist)
{
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 */
LUAU_ASSERT(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);
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
if (g->gcstate == GCSpause)
{
@ -844,6 +911,9 @@ void luaC_step(lua_State* L, bool assist)
finishGcCycleStats(g);
if (FFlag::LuauGcAdditionalStats)
g->gcstats.currcycle.starttotalsizebytes = g->totalbytes;
g->gcstats.currcycle.heapgoalsizebytes = heapgoal;
g->gcstats.currcycle.heaptriggersizebytes = heaptrigger;
}

View File

@ -6,6 +6,13 @@
#include "lobject.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
*/
@ -111,13 +118,6 @@
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) \
{ \
if (iscollectable(tv) && iswhite(gcvalue(tv)) && (!(uv) || ((UpVal*)uv)->v != &((UpVal*)uv)->u.value)) \

View File

@ -98,7 +98,7 @@
*/
#if defined(__APPLE__)
#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)
#else
// Android somehow uses a similar ABI to MSVC, *not* to iOS...

View File

@ -6,7 +6,6 @@
#include "lcommon.h"
#include <string.h>
#include <stdio.h> // TODO: Remove with LuauSchubfach
#ifdef _MSC_VER
#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.
LUAU_FASTFLAGVARIABLE(LuauSchubfach, false)
// 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
// 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)
{
if (!FFlag::LuauSchubfach)
{
snprintf(buf, LUAI_MAXNUM2STR, LUA_NUMBER_FMT, n);
return buf + strlen(buf);
}
// IEEE-754
union
{

View File

@ -55,7 +55,6 @@ LUAU_FASTMATH_END
#define luai_num2unsigned(i, n) ((i) = (unsigned)(long long)(n))
#endif
#define LUA_NUMBER_FMT "%.14g" /* TODO: Remove with LuauSchubfach */
#define LUAI_MAXNUM2STR 48
LUAI_FUNC char* luai_num2str(char* buf, double n);

View File

@ -77,25 +77,46 @@ typedef struct CallInfo
struct GCCycleStats
{
size_t starttotalsizebytes = 0;
size_t heapgoalsizebytes = 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 endtimestamp = 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;
size_t atomicstarttotalsizebytes = 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 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 explicitwork = 0;
size_t propagatework = 0;
size_t propagateagainwork = 0;
size_t endtotalsizebytes = 0;
};

View File

@ -53,7 +53,7 @@ void luaS_resize(lua_State* L, int newsize)
{
TString* p = tb->hash[i];
while (p)
{ /* for each node in the list */
{ /* for each node in the list */
TString* next = p->next; /* save next */
unsigned int h = p->hash;
int h1 = lmod(h, newsize); /* new position */

View File

@ -24,6 +24,8 @@
#include <string.h>
LUAU_FASTFLAGVARIABLE(LuauTableRehashRework, false)
// max size of both array and hash part is 2^MAXBITS
#define MAXBITS 26
#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 */
}
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)
{
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++)
{
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 */
luaM_reallocarray(L, t->array, oldasize, nasize, TValue, t->memcat);
}
/* re-insert elements from hash part */
for (int i = twoto(oldhsize) - 1; i >= 0; i--)
if (FFlag::LuauTableRehashRework)
{
LuaNode* old = nold + i;
if (!ttisnil(gval(old)))
for (int i = twoto(oldhsize) - 1; i >= 0; i--)
{
TValue ok;
getnodekey(L, &ok, old);
setobjt2t(L, luaH_set(L, t, &ok), gval(old));
LuaNode* old = nold + i;
if (!ttisnil(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)
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)
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);
}
@ -481,7 +528,16 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
if (n == NULL)
{ /* cannot find a free place? */
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);
TValue mk;

View File

@ -2,6 +2,7 @@
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
#include "lualib.h"
#include "lapi.h"
#include "lstate.h"
#include "ltable.h"
#include "lstring.h"
@ -9,6 +10,8 @@
#include "ldebug.h"
#include "lvm.h"
LUAU_FASTFLAGVARIABLE(LuauTableClone, false)
static int foreachi(lua_State* L)
{
luaL_checktype(L, 1, LUA_TTABLE);
@ -507,6 +510,23 @@ static int tisfrozen(lua_State* L)
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[] = {
{"concat", tconcat},
{"foreach", foreach},
@ -524,6 +544,7 @@ static const luaL_Reg tab_funcs[] = {
{"clear", tclear},
{"freeze", tfreeze},
{"isfrozen", tisfrozen},
{"clone", tclone},
{NULL, NULL},
};

View File

@ -4,6 +4,7 @@
#include "lstate.h"
#include "lstring.h"
#include "ludata.h"
#include "ltable.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)
{
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]);

View File

@ -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)
{
LUAU_ASSERT(u->tag < LUA_UTAG_LIMIT || u->tag == UTAG_IDTOR);
void (*dtor)(void*) = nullptr;
if (u->tag == UTAG_IDTOR)
memcpy(&dtor, &u->data + u->len - sizeof(dtor), sizeof(dtor));
else if (u->tag)
if (u->tag < LUA_UTAG_LIMIT)
dtor = L->global->udatagc[u->tag];
else if (u->tag == UTAG_IDTOR)
memcpy(&dtor, &u->data + u->len - sizeof(dtor), sizeof(dtor));
if (dtor)
dtor(u->data);

View File

@ -7,6 +7,9 @@
/* special tag value is used for user data with inline dtors */
#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)
LUAI_FUNC Udata* luaU_newudata(lua_State* L, size_t s, int tag);

View File

@ -77,6 +77,11 @@
if (LUAU_UNLIKELY(!!interrupt)) \
{ /* the interrupt hook is called right before we advance pc */ \
VM_PROTECT(L->ci->savedpc++; interrupt(L, -1)); \
if (L->status != 0) \
{ \
L->ci->savedpc--; \
goto exit; \
} \
} \
}
#endif