mirror of
https://github.com/luau-lang/luau.git
synced 2024-12-12 21:10:37 +00:00
Sync to upstream/release/521 (#443)
This commit is contained in:
parent
ba60730e0f
commit
4c1f208d7a
20 changed files with 222 additions and 119 deletions
|
@ -9,7 +9,6 @@
|
|||
#include <algorithm>
|
||||
|
||||
LUAU_FASTFLAG(LuauAssertStripsFalsyTypes)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableCloneType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSetMetaTableArgsCheck, false)
|
||||
|
||||
/** FIXME: Many of these type definitions are not quite completely accurate.
|
||||
|
@ -289,9 +288,7 @@ void registerBuiltinTypes(TypeChecker& typeChecker)
|
|||
{
|
||||
// tabTy is a generic table type which we can't express via declaration syntax yet
|
||||
ttv->props["freeze"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.freeze");
|
||||
|
||||
if (FFlag::LuauTableCloneType)
|
||||
ttv->props["clone"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.clone");
|
||||
ttv->props["clone"] = makeProperty(makeFunction(arena, std::nullopt, {tabTy}, {tabTy}), "@luau/global/table.clone");
|
||||
|
||||
attachMagicFunction(ttv->props["pack"].type, magicFunctionPack);
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@ LUAU_FASTFLAGVARIABLE(LuauWeakEqConstraint, false) // Eventually removed as fals
|
|||
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauRecursiveTypeParameterRestriction, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauGenericFunctionsDontCacheTypeParams, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauInferStatFunction, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSealExports, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauDiscriminableUnions2, false)
|
||||
|
@ -34,7 +35,7 @@ LUAU_FASTFLAGVARIABLE(LuauExpectedTypesOfProperties, false)
|
|||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauOnlyMutateInstantiatedTables, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauPropertiesGetExpectedType, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStatFunctionSimplify2, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauStatFunctionSimplify3, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauUnsealedTableLiteral, false)
|
||||
LUAU_FASTFLAG(LuauTypeMismatchModuleName)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTwoPassAliasDefinitionFix, false)
|
||||
|
@ -463,7 +464,18 @@ void TypeChecker::checkBlock(const ScopePtr& scope, const AstStatBlock& block)
|
|||
}
|
||||
else if (auto fun = (*protoIter)->as<AstStatFunction>())
|
||||
{
|
||||
auto pair = checkFunctionSignature(scope, subLevel, *fun->func, fun->name->location, std::nullopt);
|
||||
std::optional<TypeId> expectedType;
|
||||
|
||||
if (FFlag::LuauInferStatFunction && !fun->func->self)
|
||||
{
|
||||
if (auto name = fun->name->as<AstExprIndexName>())
|
||||
{
|
||||
TypeId exprTy = checkExpr(scope, *name->expr).type;
|
||||
expectedType = getIndexTypeFromType(scope, exprTy, name->index.value, name->indexLocation, false);
|
||||
}
|
||||
}
|
||||
|
||||
auto pair = checkFunctionSignature(scope, subLevel, *fun->func, fun->name->location, expectedType);
|
||||
auto [funTy, funScope] = pair;
|
||||
|
||||
functionDecls[*protoIter] = pair;
|
||||
|
@ -1103,7 +1115,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
|
|||
scope->bindings[name->local] = {anyIfNonstrict(quantify(funScope, ty, name->local->location)), name->local->location};
|
||||
return;
|
||||
}
|
||||
else if (auto name = function.name->as<AstExprIndexName>(); name && FFlag::LuauStatFunctionSimplify2)
|
||||
else if (auto name = function.name->as<AstExprIndexName>(); name && FFlag::LuauStatFunctionSimplify3)
|
||||
{
|
||||
TypeId exprTy = checkExpr(scope, *name->expr).type;
|
||||
TableTypeVar* ttv = getMutableTableType(exprTy);
|
||||
|
@ -1116,7 +1128,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
|
|||
}
|
||||
else if (ttv->state == TableState::Sealed)
|
||||
{
|
||||
if (!ttv->indexer || !isPrim(ttv->indexer->indexType, PrimitiveTypeVar::String))
|
||||
if (!getIndexTypeFromType(scope, exprTy, name->index.value, name->indexLocation, false))
|
||||
reportError(TypeError{function.location, CannotExtendTable{exprTy, CannotExtendTable::Property, name->index.value}});
|
||||
}
|
||||
|
||||
|
@ -1141,7 +1153,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
|
|||
if (ttv && ttv->state != TableState::Sealed)
|
||||
ttv->props[name->index.value] = {follow(quantify(funScope, ty, name->indexLocation)), /* deprecated */ false, {}, name->indexLocation};
|
||||
}
|
||||
else if (FFlag::LuauStatFunctionSimplify2)
|
||||
else if (FFlag::LuauStatFunctionSimplify3)
|
||||
{
|
||||
LUAU_ASSERT(function.name->is<AstExprError>());
|
||||
|
||||
|
@ -1151,7 +1163,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
|
|||
}
|
||||
else if (function.func->self)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify2);
|
||||
LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify3);
|
||||
|
||||
AstExprIndexName* indexName = function.name->as<AstExprIndexName>();
|
||||
if (!indexName)
|
||||
|
@ -1190,7 +1202,7 @@ void TypeChecker::check(const ScopePtr& scope, TypeId ty, const ScopePtr& funSco
|
|||
}
|
||||
else
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify2);
|
||||
LUAU_ASSERT(!FFlag::LuauStatFunctionSimplify3);
|
||||
|
||||
TypeId leftType = checkLValueBinding(scope, *function.name);
|
||||
|
||||
|
@ -2985,7 +2997,7 @@ TypeId TypeChecker::checkFunctionName(const ScopePtr& scope, AstExpr& funName, T
|
|||
return errorRecoveryType(scope);
|
||||
}
|
||||
|
||||
if (FFlag::LuauStatFunctionSimplify2)
|
||||
if (FFlag::LuauStatFunctionSimplify3)
|
||||
{
|
||||
if (lhsType->persistent)
|
||||
return errorRecoveryType(scope);
|
||||
|
|
|
@ -130,8 +130,8 @@ ThreadContext& getThreadContext();
|
|||
|
||||
struct Scope
|
||||
{
|
||||
explicit Scope(ThreadContext& context, uint16_t token)
|
||||
: context(context)
|
||||
explicit Scope(uint16_t token)
|
||||
: context(getThreadContext())
|
||||
{
|
||||
if (!FFlag::DebugLuauTimeTracing)
|
||||
return;
|
||||
|
@ -152,8 +152,8 @@ struct Scope
|
|||
|
||||
struct OptionalTailScope
|
||||
{
|
||||
explicit OptionalTailScope(ThreadContext& context, uint16_t token, uint32_t threshold)
|
||||
: context(context)
|
||||
explicit OptionalTailScope(uint16_t token, uint32_t threshold)
|
||||
: context(getThreadContext())
|
||||
, token(token)
|
||||
, threshold(threshold)
|
||||
{
|
||||
|
@ -188,27 +188,27 @@ struct OptionalTailScope
|
|||
uint32_t pos;
|
||||
};
|
||||
|
||||
LUAU_NOINLINE std::pair<uint16_t, Luau::TimeTrace::ThreadContext&> createScopeData(const char* name, const char* category);
|
||||
LUAU_NOINLINE uint16_t createScopeData(const char* name, const char* category);
|
||||
|
||||
} // namespace TimeTrace
|
||||
} // namespace Luau
|
||||
|
||||
// Regular scope
|
||||
#define LUAU_TIMETRACE_SCOPE(name, category) \
|
||||
static auto lttScopeStatic = Luau::TimeTrace::createScopeData(name, category); \
|
||||
Luau::TimeTrace::Scope lttScope(lttScopeStatic.second, lttScopeStatic.first)
|
||||
static uint16_t lttScopeStatic = Luau::TimeTrace::createScopeData(name, category); \
|
||||
Luau::TimeTrace::Scope lttScope(lttScopeStatic)
|
||||
|
||||
// A scope without nested scopes that may be skipped if the time it took is less than the threshold
|
||||
#define LUAU_TIMETRACE_OPTIONAL_TAIL_SCOPE(name, category, microsec) \
|
||||
static auto lttScopeStaticOptTail = Luau::TimeTrace::createScopeData(name, category); \
|
||||
Luau::TimeTrace::OptionalTailScope lttScope(lttScopeStaticOptTail.second, lttScopeStaticOptTail.first, microsec)
|
||||
static uint16_t lttScopeStaticOptTail = Luau::TimeTrace::createScopeData(name, category); \
|
||||
Luau::TimeTrace::OptionalTailScope lttScope(lttScopeStaticOptTail, microsec)
|
||||
|
||||
// Extra key/value data can be added to regular scopes
|
||||
#define LUAU_TIMETRACE_ARGUMENT(name, value) \
|
||||
do \
|
||||
{ \
|
||||
if (FFlag::DebugLuauTimeTracing) \
|
||||
lttScopeStatic.second.eventArgument(name, value); \
|
||||
lttScope.context.eventArgument(name, value); \
|
||||
} while (false)
|
||||
|
||||
#else
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#include <limits.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauParseLocationIgnoreCommentSkip, false)
|
||||
|
||||
namespace Luau
|
||||
{
|
||||
|
||||
|
@ -352,6 +354,8 @@ const Lexeme& Lexer::next()
|
|||
|
||||
const Lexeme& Lexer::next(bool skipComments)
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
// in skipComments mode we reject valid comments
|
||||
do
|
||||
{
|
||||
|
@ -359,9 +363,11 @@ const Lexeme& Lexer::next(bool skipComments)
|
|||
while (isSpace(peekch()))
|
||||
consume();
|
||||
|
||||
prevLocation = lexeme.location;
|
||||
if (!FFlag::LuauParseLocationIgnoreCommentSkip || first)
|
||||
prevLocation = lexeme.location;
|
||||
|
||||
lexeme = readNext();
|
||||
first = false;
|
||||
} while (skipComments && (lexeme.type == Lexeme::Comment || lexeme.type == Lexeme::BlockComment));
|
||||
|
||||
return lexeme;
|
||||
|
|
|
@ -246,10 +246,9 @@ ThreadContext& getThreadContext()
|
|||
return context;
|
||||
}
|
||||
|
||||
std::pair<uint16_t, Luau::TimeTrace::ThreadContext&> createScopeData(const char* name, const char* category)
|
||||
uint16_t createScopeData(const char* name, const char* category)
|
||||
{
|
||||
uint16_t token = createToken(Luau::TimeTrace::getGlobalContext(), name, category);
|
||||
return {token, Luau::TimeTrace::getThreadContext()};
|
||||
return createToken(Luau::TimeTrace::getGlobalContext(), name, category);
|
||||
}
|
||||
} // namespace TimeTrace
|
||||
} // namespace Luau
|
||||
|
|
|
@ -376,8 +376,7 @@ enum LuauOpcode
|
|||
enum LuauBytecodeTag
|
||||
{
|
||||
// Bytecode version
|
||||
LBC_VERSION = 1,
|
||||
LBC_VERSION_FUTURE = 2, // TODO: This will be removed in favor of LBC_VERSION with LuauBytecodeV2Force
|
||||
LBC_VERSION = 2,
|
||||
// Types of constant table entries
|
||||
LBC_CONSTANT_NIL = 0,
|
||||
LBC_CONSTANT_BOOLEAN,
|
||||
|
|
|
@ -508,7 +508,7 @@ uint32_t BytecodeBuilder::getDebugPC() const
|
|||
void BytecodeBuilder::finalize()
|
||||
{
|
||||
LUAU_ASSERT(bytecode.empty());
|
||||
bytecode = char(LBC_VERSION_FUTURE);
|
||||
bytecode = char(LBC_VERSION);
|
||||
|
||||
writeStringTable(bytecode);
|
||||
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
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)
|
||||
|
||||
|
@ -337,15 +335,8 @@ const char* luaL_findtable(lua_State* L, int idx, const char* fname, int szhint)
|
|||
|
||||
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));
|
||||
}
|
||||
const TValue* obj = luaA_toobject(L, idx);
|
||||
return luaT_objtypename(L, obj);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
|
@ -11,8 +11,6 @@
|
|||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
LUAU_DYNAMIC_FASTFLAG(LuauMorePreciseLuaLTypeName)
|
||||
|
||||
static void writestring(const char* s, size_t l)
|
||||
{
|
||||
fwrite(s, 1, l, stdout);
|
||||
|
@ -189,31 +187,16 @@ static int luaB_gcinfo(lua_State* L)
|
|||
static int luaB_type(lua_State* L)
|
||||
{
|
||||
luaL_checkany(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));
|
||||
}
|
||||
/* resulting name doesn't differentiate between userdata types */
|
||||
lua_pushstring(L, lua_typename(L, lua_type(L, 1)));
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int luaB_typeof(lua_State* L)
|
||||
{
|
||||
luaL_checkany(L, 1);
|
||||
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));
|
||||
}
|
||||
/* resulting name returns __type if specified unless the input is a newproxy-created userdata */
|
||||
lua_pushstring(L, luaL_typename(L, 1));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauBytecodeV2Force)
|
||||
|
||||
static const char* getfuncname(Closure* f);
|
||||
|
||||
static int currentpc(lua_State* L, CallInfo* ci)
|
||||
|
@ -91,16 +89,6 @@ const char* lua_setlocal(lua_State* L, int level, int n)
|
|||
return name;
|
||||
}
|
||||
|
||||
static int getlinedefined(Proto* p)
|
||||
{
|
||||
if (FFlag::LuauBytecodeV2Force)
|
||||
return p->linedefined;
|
||||
else if (p->linedefined >= 0)
|
||||
return p->linedefined;
|
||||
else
|
||||
return luaG_getline(p, 0);
|
||||
}
|
||||
|
||||
static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f, CallInfo* ci)
|
||||
{
|
||||
int status = 1;
|
||||
|
@ -120,7 +108,7 @@ static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f,
|
|||
{
|
||||
ar->source = getstr(f->l.p->source);
|
||||
ar->what = "Lua";
|
||||
ar->linedefined = getlinedefined(f->l.p);
|
||||
ar->linedefined = f->l.p->linedefined;
|
||||
}
|
||||
luaO_chunkid(ar->short_src, ar->source, LUA_IDSIZE);
|
||||
break;
|
||||
|
@ -133,7 +121,7 @@ static int auxgetinfo(lua_State* L, const char* what, lua_Debug* ar, Closure* f,
|
|||
}
|
||||
else
|
||||
{
|
||||
ar->currentline = f->isC ? -1 : getlinedefined(f->l.p);
|
||||
ar->currentline = f->isC ? -1 : f->l.p->linedefined;
|
||||
}
|
||||
|
||||
break;
|
||||
|
@ -424,7 +412,7 @@ static void getcoverage(Proto* p, int depth, int* buffer, size_t size, void* con
|
|||
}
|
||||
|
||||
const char* debugname = p->debugname ? getstr(p->debugname) : NULL;
|
||||
int linedefined = getlinedefined(p);
|
||||
int linedefined = p->linedefined;
|
||||
|
||||
callback(context, debugname, linedefined, depth, buffer, size);
|
||||
|
||||
|
|
|
@ -10,12 +10,12 @@ union GCObject;
|
|||
#define luaM_newgco(L, t, size, memcat) cast_to(t*, luaM_newgco_(L, size, memcat))
|
||||
#define luaM_freegco(L, p, size, memcat, page) luaM_freegco_(L, obj2gco(p), size, memcat, page)
|
||||
|
||||
#define luaM_arraysize_(n, e) ((cast_to(size_t, (n)) <= SIZE_MAX / (e)) ? (n) * (e) : (luaM_toobig(L), SIZE_MAX))
|
||||
#define luaM_arraysize_(L, n, e) ((cast_to(size_t, (n)) <= SIZE_MAX / (e)) ? (n) * (e) : (luaM_toobig(L), SIZE_MAX))
|
||||
|
||||
#define luaM_newarray(L, n, t, memcat) cast_to(t*, luaM_new_(L, luaM_arraysize_(n, sizeof(t)), memcat))
|
||||
#define luaM_newarray(L, n, t, memcat) cast_to(t*, luaM_new_(L, luaM_arraysize_(L, n, sizeof(t)), memcat))
|
||||
#define luaM_freearray(L, b, n, t, memcat) luaM_free_(L, (b), (n) * sizeof(t), memcat)
|
||||
#define luaM_reallocarray(L, v, oldn, n, t, memcat) \
|
||||
((v) = cast_to(t*, luaM_realloc_(L, v, (oldn) * sizeof(t), luaM_arraysize_(n, sizeof(t)), memcat)))
|
||||
((v) = cast_to(t*, luaM_realloc_(L, v, (oldn) * sizeof(t), luaM_arraysize_(L, n, sizeof(t)), memcat)))
|
||||
|
||||
LUAI_FUNC void* luaM_new_(lua_State* L, size_t nsize, uint8_t memcat);
|
||||
LUAI_FUNC GCObject* luaM_newgco_(lua_State* L, size_t nsize, uint8_t memcat);
|
||||
|
|
|
@ -2,17 +2,26 @@
|
|||
// This code is based on Lua 5.x implementation licensed under MIT License; see lua_LICENSE.txt for details
|
||||
|
||||
/*
|
||||
** Implementation of tables (aka arrays, objects, or hash tables).
|
||||
** Tables keep its elements in two parts: an array part and a hash part.
|
||||
** Non-negative integer keys are all candidates to be kept in the array
|
||||
** part. The actual size of the array is the largest `n' such that at
|
||||
** least half the slots between 0 and n are in use.
|
||||
** Hash uses a mix of chained scatter table with Brent's variation.
|
||||
** A main invariant of these tables is that, if an element is not
|
||||
** in its main position (i.e. the `original' position that its hash gives
|
||||
** to it), then the colliding element is in its own main position.
|
||||
** Hence even when the load factor reaches 100%, performance remains good.
|
||||
*/
|
||||
* Implementation of tables (aka arrays, objects, or hash tables).
|
||||
*
|
||||
* Tables keep the elements in two parts: an array part and a hash part.
|
||||
* Integer keys >=1 are all candidates to be kept in the array part. The actual size of the array is the
|
||||
* largest n such that at least half the slots between 0 and n are in use.
|
||||
* Hash uses a mix of chained scatter table with Brent's variation.
|
||||
*
|
||||
* A main invariant of these tables is that, if an element is not in its main position (i.e. the original
|
||||
* position that its hash gives to it), then the colliding element is in its own main position.
|
||||
* Hence even when the load factor reaches 100%, performance remains good.
|
||||
*
|
||||
* Table keys can be arbitrary values unless they contain NaN. Keys are hashed and compared using raw equality,
|
||||
* so even if the key is a userdata with an overridden __eq, it's not used during hash lookups.
|
||||
*
|
||||
* Each table has a "boundary", defined as the index k where t[k] ~= nil and t[k+1] == nil. The boundary can be
|
||||
* computed using a binary search and can be adjusted when the table is modified; crucially, Luau enforces an
|
||||
* invariant where the boundary must be in the array part - this enforces a consistent iteration order through the
|
||||
* prefix of the table when using pairs(), and allows to implement algorithms that access elements in 1..#t range
|
||||
* more efficiently.
|
||||
*/
|
||||
|
||||
#include "ltable.h"
|
||||
|
||||
|
@ -25,6 +34,7 @@
|
|||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableRehashRework, false)
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableNewBoundary, false)
|
||||
|
||||
// max size of both array and hash part is 2^MAXBITS
|
||||
#define MAXBITS 26
|
||||
|
@ -460,7 +470,20 @@ static void rehash(lua_State* L, Table* t, const TValue* ek)
|
|||
totaluse++;
|
||||
/* compute new size for array part */
|
||||
int na = computesizes(nums, &nasize);
|
||||
/* enforce the boundary invariant; for performance, only do hash lookups if we must */
|
||||
if (FFlag::LuauTableNewBoundary)
|
||||
{
|
||||
bool tbound = t->node != dummynode || nasize < t->sizearray;
|
||||
int ekindex = ttisnumber(ek) ? arrayindex(nvalue(ek)) : -1;
|
||||
/* move the array size up until the boundary is guaranteed to be inside the array part */
|
||||
while (nasize + 1 == ekindex || (tbound && !ttisnil(luaH_getnum(t, nasize + 1))))
|
||||
{
|
||||
nasize++;
|
||||
na++;
|
||||
}
|
||||
}
|
||||
/* resize the table to new computed sizes */
|
||||
LUAU_ASSERT(na <= totaluse);
|
||||
resize(L, t, nasize, totaluse - na);
|
||||
}
|
||||
|
||||
|
@ -520,10 +543,18 @@ static LuaNode* getfreepos(Table* t)
|
|||
*/
|
||||
static TValue* newkey(lua_State* L, Table* t, const TValue* key)
|
||||
{
|
||||
/* enforce boundary invariant */
|
||||
if (FFlag::LuauTableNewBoundary && ttisnumber(key) && nvalue(key) == t->sizearray + 1)
|
||||
{
|
||||
rehash(L, t, key); /* grow table */
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
LuaNode* mp = mainposition(t, key);
|
||||
if (!ttisnil(gval(mp)) || mp == dummynode)
|
||||
{
|
||||
LuaNode* othern;
|
||||
LuaNode* n = getfreepos(t); /* get a free place */
|
||||
if (n == NULL)
|
||||
{ /* cannot find a free place? */
|
||||
|
@ -542,7 +573,7 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
|
|||
LUAU_ASSERT(n != dummynode);
|
||||
TValue mk;
|
||||
getnodekey(L, &mk, mp);
|
||||
othern = mainposition(t, &mk);
|
||||
LuaNode* othern = mainposition(t, &mk);
|
||||
if (othern != mp)
|
||||
{ /* is colliding node out of its main position? */
|
||||
/* yes; move colliding node into free position */
|
||||
|
@ -704,6 +735,7 @@ TValue* luaH_setstr(lua_State* L, Table* t, TString* key)
|
|||
|
||||
static LUAU_NOINLINE int unbound_search(Table* t, unsigned int j)
|
||||
{
|
||||
LUAU_ASSERT(!FFlag::LuauTableNewBoundary);
|
||||
unsigned int i = j; /* i is zero or a present index */
|
||||
j++;
|
||||
/* find `i' and `j' such that i is present and j is not */
|
||||
|
@ -788,6 +820,12 @@ int luaH_getn(Table* t)
|
|||
maybesetaboundary(t, boundary);
|
||||
return boundary;
|
||||
}
|
||||
else if (FFlag::LuauTableNewBoundary)
|
||||
{
|
||||
/* validate boundary invariant */
|
||||
LUAU_ASSERT(t->node == dummynode || ttisnil(luaH_getnum(t, j + 1)));
|
||||
return j;
|
||||
}
|
||||
/* else must find a boundary in hash part */
|
||||
else if (t->node == dummynode) /* hash part is empty? */
|
||||
return j; /* that is easy... */
|
||||
|
|
|
@ -10,7 +10,9 @@
|
|||
#include "ldebug.h"
|
||||
#include "lvm.h"
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauTableClone, false)
|
||||
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuauTableMoveTelemetry2, false)
|
||||
|
||||
void (*lua_table_move_telemetry)(lua_State* L, int f, int e, int t, int nf, int nt);
|
||||
|
||||
static int foreachi(lua_State* L)
|
||||
{
|
||||
|
@ -197,6 +199,29 @@ static int tmove(lua_State* L)
|
|||
int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */
|
||||
luaL_checktype(L, tt, LUA_TTABLE);
|
||||
|
||||
void (*telemetrycb)(lua_State* L, int f, int e, int t, int nf, int nt) = lua_table_move_telemetry;
|
||||
|
||||
if (DFFlag::LuauTableMoveTelemetry2 && telemetrycb)
|
||||
{
|
||||
int nf = lua_objlen(L, 1);
|
||||
int nt = lua_objlen(L, tt);
|
||||
|
||||
bool report = false;
|
||||
|
||||
// source index range must be in bounds in source table unless the table is empty (permits 1..#t moves)
|
||||
if (!(f == 1 || (f >= 1 && f <= nf)))
|
||||
report = true;
|
||||
if (!(e == nf || (e >= 1 && e <= nf)))
|
||||
report = true;
|
||||
|
||||
// destination index must be in bounds in dest table or be exactly at the first empty element (permits concats)
|
||||
if (!(t == nt + 1 || (t >= 1 && t <= nt)))
|
||||
report = true;
|
||||
|
||||
if (report)
|
||||
telemetrycb(L, f, e, t, nf, nt);
|
||||
}
|
||||
|
||||
if (e >= f)
|
||||
{ /* otherwise, nothing to move */
|
||||
luaL_argcheck(L, f > 0 || e < INT_MAX + f, 3, "too many elements to move");
|
||||
|
@ -512,9 +537,6 @@ static int tisfrozen(lua_State* L)
|
|||
|
||||
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");
|
||||
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAG(LuauTableNewBoundary)
|
||||
|
||||
// Disable c99-designator to avoid the warning in CGOTO dispatch table
|
||||
#ifdef __clang__
|
||||
#if __has_warning("-Wc99-designator")
|
||||
|
@ -2266,9 +2268,9 @@ static void luau_execute(lua_State* L)
|
|||
VM_NEXT();
|
||||
}
|
||||
}
|
||||
else if (h->lsizenode == 0 && ttisnil(gval(h->node)))
|
||||
else if (FFlag::LuauTableNewBoundary || (h->lsizenode == 0 && ttisnil(gval(h->node))))
|
||||
{
|
||||
// hash part is empty: fallthrough to exit
|
||||
// fallthrough to exit
|
||||
VM_NEXT();
|
||||
}
|
||||
else
|
||||
|
|
|
@ -13,8 +13,6 @@
|
|||
|
||||
#include <string.h>
|
||||
|
||||
LUAU_FASTFLAGVARIABLE(LuauBytecodeV2Force, false)
|
||||
|
||||
// TODO: RAII deallocation doesn't work for longjmp builds if a memory error happens
|
||||
template<typename T>
|
||||
struct TempBuffer
|
||||
|
@ -156,12 +154,11 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
|
|||
return 1;
|
||||
}
|
||||
|
||||
if (FFlag::LuauBytecodeV2Force ? (version != LBC_VERSION_FUTURE) : (version != LBC_VERSION && version != LBC_VERSION_FUTURE))
|
||||
if (version != LBC_VERSION)
|
||||
{
|
||||
char chunkid[LUA_IDSIZE];
|
||||
luaO_chunkid(chunkid, chunkname, LUA_IDSIZE);
|
||||
lua_pushfstring(L, "%s: bytecode version mismatch (expected %d, got %d)", chunkid,
|
||||
FFlag::LuauBytecodeV2Force ? LBC_VERSION_FUTURE : LBC_VERSION, version);
|
||||
lua_pushfstring(L, "%s: bytecode version mismatch (expected %d, got %d)", chunkid, LBC_VERSION, version);
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -292,11 +289,7 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
|
|||
p->p[j] = protos[fid];
|
||||
}
|
||||
|
||||
if (FFlag::LuauBytecodeV2Force || version == LBC_VERSION_FUTURE)
|
||||
p->linedefined = readVarInt(data, size, offset);
|
||||
else
|
||||
p->linedefined = -1;
|
||||
|
||||
p->linedefined = readVarInt(data, size, offset);
|
||||
p->debugname = readString(strings, data, size, offset);
|
||||
|
||||
uint8_t lineinfo = read<uint8_t>(data, size, offset);
|
||||
|
|
|
@ -14,7 +14,6 @@
|
|||
|
||||
LUAU_FASTFLAG(LuauTraceTypesInNonstrictMode2)
|
||||
LUAU_FASTFLAG(LuauSetMetatableDoesNotTimeTravel)
|
||||
LUAU_FASTFLAG(LuauTableCloneType)
|
||||
|
||||
using namespace Luau;
|
||||
|
||||
|
@ -262,7 +261,7 @@ TEST_CASE_FIXTURE(ACFixture, "get_member_completions")
|
|||
|
||||
auto ac = autocomplete('1');
|
||||
|
||||
CHECK_EQ(FFlag::LuauTableCloneType ? 17 : 16, ac.entryMap.size());
|
||||
CHECK_EQ(17, ac.entryMap.size());
|
||||
CHECK(ac.entryMap.count("find"));
|
||||
CHECK(ac.entryMap.count("pack"));
|
||||
CHECK(!ac.entryMap.count("math"));
|
||||
|
@ -2221,7 +2220,7 @@ TEST_CASE_FIXTURE(ACFixture, "autocompleteSource")
|
|||
|
||||
auto ac = autocompleteSource(frontend, source, Position{1, 24}, nullCallback).result;
|
||||
|
||||
CHECK_EQ(FFlag::LuauTableCloneType ? 17 : 16, ac.entryMap.size());
|
||||
CHECK_EQ(17, ac.entryMap.size());
|
||||
CHECK(ac.entryMap.count("find"));
|
||||
CHECK(ac.entryMap.count("pack"));
|
||||
CHECK(!ac.entryMap.count("math"));
|
||||
|
|
|
@ -241,8 +241,6 @@ TEST_CASE("Math")
|
|||
|
||||
TEST_CASE("Table")
|
||||
{
|
||||
ScopedFastFlag sff("LuauTableClone", true);
|
||||
|
||||
runConformance("nextvar.lua");
|
||||
}
|
||||
|
||||
|
@ -467,8 +465,6 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
|
|||
|
||||
TEST_CASE("Types")
|
||||
{
|
||||
ScopedFastFlag sff("LuauTableCloneType", true);
|
||||
|
||||
runConformance("types.lua", [](lua_State* L) {
|
||||
Luau::NullModuleResolver moduleResolver;
|
||||
Luau::InternalErrorReporter iceHandler;
|
||||
|
|
|
@ -1604,6 +1604,20 @@ TEST_CASE_FIXTURE(Fixture, "end_extent_of_functions_unions_and_intersections")
|
|||
CHECK_EQ((Position{3, 42}), block->body.data[2]->location.end);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "end_extent_doesnt_consume_comments")
|
||||
{
|
||||
ScopedFastFlag luauParseLocationIgnoreCommentSkip{"LuauParseLocationIgnoreCommentSkip", true};
|
||||
|
||||
AstStatBlock* block = parse(R"(
|
||||
type F = number
|
||||
--comment
|
||||
print('hello')
|
||||
)");
|
||||
|
||||
REQUIRE_EQ(2, block->body.size);
|
||||
CHECK_EQ((Position{1, 23}), block->body.data[0]->location.end);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "parse_error_loop_control")
|
||||
{
|
||||
matchParseError("break", "break statement must be inside a loop");
|
||||
|
|
|
@ -1270,7 +1270,7 @@ caused by:
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_decl_quantify_right_type")
|
||||
{
|
||||
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify2", true};
|
||||
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify3", true};
|
||||
|
||||
fileResolver.source["game/isAMagicMock"] = R"(
|
||||
--!nonstrict
|
||||
|
@ -1294,7 +1294,7 @@ end
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_decl_non_self_sealed_overwrite")
|
||||
{
|
||||
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify2", true};
|
||||
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify3", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
function string.len(): number
|
||||
|
@ -1302,7 +1302,40 @@ function string.len(): number
|
|||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERRORS(result);
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
|
||||
// if 'string' library property was replaced with an internal module type, it will be freed and the next check will crash
|
||||
frontend.clear();
|
||||
|
||||
result = check(R"(
|
||||
print(string.len('hello'))
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_NO_ERRORS(result);
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_decl_non_self_sealed_overwrite_2")
|
||||
{
|
||||
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify3", true};
|
||||
ScopedFastFlag inferStatFunction{"LuauInferStatFunction", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t: { f: ((x: number) -> number)? } = {}
|
||||
|
||||
function t.f(x)
|
||||
print(x + 5)
|
||||
return x .. "asd"
|
||||
end
|
||||
|
||||
t.f = function(x)
|
||||
print(x + 5)
|
||||
return x .. "asd"
|
||||
end
|
||||
)");
|
||||
|
||||
LUAU_REQUIRE_ERROR_COUNT(2, result);
|
||||
CHECK_EQ(toString(result.errors[0]), R"(Type 'string' could not be converted into 'number')");
|
||||
CHECK_EQ(toString(result.errors[1]), R"(Type 'string' could not be converted into 'number')");
|
||||
}
|
||||
|
||||
TEST_CASE_FIXTURE(Fixture, "strict_mode_ok_with_missing_arguments")
|
||||
|
@ -1319,7 +1352,7 @@ TEST_CASE_FIXTURE(Fixture, "strict_mode_ok_with_missing_arguments")
|
|||
|
||||
TEST_CASE_FIXTURE(Fixture, "function_statement_sealed_table_assignment_through_indexer")
|
||||
{
|
||||
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify2", true};
|
||||
ScopedFastFlag statFunctionSimplify{"LuauStatFunctionSimplify3", true};
|
||||
|
||||
CheckResult result = check(R"(
|
||||
local t: {[string]: () -> number} = {}
|
||||
|
|
|
@ -550,4 +550,35 @@ do
|
|||
assert(not pcall(table.clone, 42))
|
||||
end
|
||||
|
||||
-- test boundary invariant maintenance during rehash
|
||||
do
|
||||
local arr = table.create(5, 42)
|
||||
|
||||
arr[1] = nil
|
||||
arr.a = 'a' -- trigger rehash
|
||||
|
||||
assert(#arr == 5) -- technically 0 is also valid, but it happens to be 5 because array capacity is 5
|
||||
end
|
||||
|
||||
-- test boundary invariant maintenance when replacing hash keys
|
||||
do
|
||||
local arr = {}
|
||||
arr.a = 'a'
|
||||
arr.a = nil
|
||||
arr[1] = 1 -- should rehash and resize array part, otherwise # won't find the boundary in array part
|
||||
|
||||
assert(#arr == 1)
|
||||
end
|
||||
|
||||
-- test boundary invariant maintenance when table is filled from the end
|
||||
do
|
||||
local arr = {}
|
||||
for i=5,2,-1 do
|
||||
arr[i] = i
|
||||
assert(#arr == 0)
|
||||
end
|
||||
arr[1] = 1
|
||||
assert(#arr == 5)
|
||||
end
|
||||
|
||||
return"OK"
|
||||
|
|
Loading…
Reference in a new issue