mirror of
https://github.com/luau-lang/luau.git
synced 2025-01-19 09:18:07 +00:00
Merge branch 'upstream' into merge
This commit is contained in:
commit
c6e2b536d4
64 changed files with 2217 additions and 686 deletions
|
@ -420,6 +420,11 @@ public:
|
||||||
void throwUserCancelError() const;
|
void throwUserCancelError() const;
|
||||||
|
|
||||||
ToStringOptions opts;
|
ToStringOptions opts;
|
||||||
|
|
||||||
|
void fillInDiscriminantTypes(
|
||||||
|
NotNull<const Constraint> constraint,
|
||||||
|
const std::vector<std::optional<TypeId>>& discriminantTypes
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
|
void dump(NotNull<Scope> rootScope, struct ToStringOptions& opts);
|
||||||
|
|
|
@ -119,7 +119,14 @@ struct TypeFunctionVariadicTypePack
|
||||||
TypeFunctionTypeId type;
|
TypeFunctionTypeId type;
|
||||||
};
|
};
|
||||||
|
|
||||||
using TypeFunctionTypePackVariant = Variant<TypeFunctionTypePack, TypeFunctionVariadicTypePack>;
|
struct TypeFunctionGenericTypePack
|
||||||
|
{
|
||||||
|
bool isNamed = false;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
|
using TypeFunctionTypePackVariant = Variant<TypeFunctionTypePack, TypeFunctionVariadicTypePack, TypeFunctionGenericTypePack>;
|
||||||
|
|
||||||
struct TypeFunctionTypePackVar
|
struct TypeFunctionTypePackVar
|
||||||
{
|
{
|
||||||
|
@ -135,6 +142,9 @@ struct TypeFunctionTypePackVar
|
||||||
|
|
||||||
struct TypeFunctionFunctionType
|
struct TypeFunctionFunctionType
|
||||||
{
|
{
|
||||||
|
std::vector<TypeFunctionTypeId> generics;
|
||||||
|
std::vector<TypeFunctionTypePackId> genericPacks;
|
||||||
|
|
||||||
TypeFunctionTypePackId argTypes;
|
TypeFunctionTypePackId argTypes;
|
||||||
TypeFunctionTypePackId retTypes;
|
TypeFunctionTypePackId retTypes;
|
||||||
};
|
};
|
||||||
|
@ -210,6 +220,14 @@ struct TypeFunctionClassType
|
||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct TypeFunctionGenericType
|
||||||
|
{
|
||||||
|
bool isNamed = false;
|
||||||
|
bool isPack = false;
|
||||||
|
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
using TypeFunctionTypeVariant = Luau::Variant<
|
using TypeFunctionTypeVariant = Luau::Variant<
|
||||||
TypeFunctionPrimitiveType,
|
TypeFunctionPrimitiveType,
|
||||||
TypeFunctionAnyType,
|
TypeFunctionAnyType,
|
||||||
|
@ -221,7 +239,8 @@ using TypeFunctionTypeVariant = Luau::Variant<
|
||||||
TypeFunctionNegationType,
|
TypeFunctionNegationType,
|
||||||
TypeFunctionFunctionType,
|
TypeFunctionFunctionType,
|
||||||
TypeFunctionTableType,
|
TypeFunctionTableType,
|
||||||
TypeFunctionClassType>;
|
TypeFunctionClassType,
|
||||||
|
TypeFunctionGenericType>;
|
||||||
|
|
||||||
struct TypeFunctionType
|
struct TypeFunctionType
|
||||||
{
|
{
|
||||||
|
|
|
@ -13,8 +13,6 @@
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauDocumentationAtPosition)
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -518,7 +516,6 @@ static std::optional<DocumentationSymbol> getMetatableDocumentation(
|
||||||
const AstName& index
|
const AstName& index
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(FFlag::LuauDocumentationAtPosition);
|
|
||||||
auto indexIt = mtable->props.find("__index");
|
auto indexIt = mtable->props.find("__index");
|
||||||
if (indexIt == mtable->props.end())
|
if (indexIt == mtable->props.end())
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
|
@ -574,8 +571,6 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (const ClassType* ctv = get<ClassType>(parentTy))
|
else if (const ClassType* ctv = get<ClassType>(parentTy))
|
||||||
{
|
|
||||||
if (FFlag::LuauDocumentationAtPosition)
|
|
||||||
{
|
{
|
||||||
while (ctv)
|
while (ctv)
|
||||||
{
|
{
|
||||||
|
@ -594,25 +589,7 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
|
||||||
ctv = ctv->parent ? Luau::get<Luau::ClassType>(*ctv->parent) : nullptr;
|
ctv = ctv->parent ? Luau::get<Luau::ClassType>(*ctv->parent) : nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else if (const PrimitiveType* ptv = get<PrimitiveType>(parentTy); ptv && ptv->metatable)
|
||||||
{
|
|
||||||
if (auto propIt = ctv->props.find(indexName->index.value); propIt != ctv->props.end())
|
|
||||||
{
|
|
||||||
if (FFlag::LuauSolverV2)
|
|
||||||
{
|
|
||||||
if (auto ty = propIt->second.readTy)
|
|
||||||
return checkOverloadedDocumentationSymbol(module, *ty, parentExpr, propIt->second.documentationSymbol);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
return checkOverloadedDocumentationSymbol(
|
|
||||||
module, propIt->second.type(), parentExpr, propIt->second.documentationSymbol
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (FFlag::LuauDocumentationAtPosition)
|
|
||||||
{
|
|
||||||
if (const PrimitiveType* ptv = get<PrimitiveType>(parentTy); ptv && ptv->metatable)
|
|
||||||
{
|
{
|
||||||
if (auto mtable = get<TableType>(*ptv->metatable))
|
if (auto mtable = get<TableType>(*ptv->metatable))
|
||||||
{
|
{
|
||||||
|
@ -622,7 +599,6 @@ std::optional<DocumentationSymbol> getDocumentationSymbolAtPosition(const Source
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (AstExprFunction* fn = targetExpr->as<AstExprFunction>())
|
else if (AstExprFunction* fn = targetExpr->as<AstExprFunction>())
|
||||||
{
|
{
|
||||||
// Handle event connection-like structures where we have
|
// Handle event connection-like structures where we have
|
||||||
|
|
|
@ -31,6 +31,7 @@
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauTypestateBuiltins2)
|
LUAU_FASTFLAGVARIABLE(LuauTypestateBuiltins2)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauStringFormatArityFix)
|
LUAU_FASTFLAGVARIABLE(LuauStringFormatArityFix)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauStringFormatErrorSuppression)
|
||||||
LUAU_FASTFLAG(AutocompleteRequirePathSuggestions2)
|
LUAU_FASTFLAG(AutocompleteRequirePathSuggestions2)
|
||||||
LUAU_FASTFLAG(LuauVectorDefinitionsExtra)
|
LUAU_FASTFLAG(LuauVectorDefinitionsExtra)
|
||||||
|
|
||||||
|
@ -631,13 +632,32 @@ static void dcrMagicFunctionTypeCheckFormat(MagicFunctionTypeCheckContext contex
|
||||||
Location location = context.callSite->args.data[i + (calledWithSelf ? 0 : paramOffset)]->location;
|
Location location = context.callSite->args.data[i + (calledWithSelf ? 0 : paramOffset)]->location;
|
||||||
// use subtyping instead here
|
// use subtyping instead here
|
||||||
SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope);
|
SubtypingResult result = context.typechecker->subtyping->isSubtype(actualTy, expectedTy, context.checkScope);
|
||||||
|
|
||||||
if (!result.isSubtype)
|
if (!result.isSubtype)
|
||||||
|
{
|
||||||
|
if (FFlag::LuauStringFormatErrorSuppression)
|
||||||
|
{
|
||||||
|
switch (shouldSuppressErrors(NotNull{&context.typechecker->normalizer}, actualTy))
|
||||||
|
{
|
||||||
|
case ErrorSuppression::Suppress:
|
||||||
|
break;
|
||||||
|
case ErrorSuppression::NormalizationFailed:
|
||||||
|
break;
|
||||||
|
case ErrorSuppression::DoNotSuppress:
|
||||||
|
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
|
||||||
|
|
||||||
|
if (!reasonings.suppressed)
|
||||||
|
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
|
Reasonings reasonings = context.typechecker->explainReasonings(actualTy, expectedTy, location, result);
|
||||||
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
|
context.typechecker->reportError(TypeMismatch{expectedTy, actualTy, reasonings.toString()}, location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static std::vector<TypeId> parsePatternString(NotNull<BuiltinTypes> builtinTypes, const char* data, size_t size)
|
static std::vector<TypeId> parsePatternString(NotNull<BuiltinTypes> builtinTypes, const char* data, size_t size)
|
||||||
{
|
{
|
||||||
|
|
|
@ -37,6 +37,7 @@ LUAU_FASTFLAG(LuauNewSolverPopulateTableLocations)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer)
|
LUAU_FASTFLAGVARIABLE(LuauAllowNilAssignmentToIndexer)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunNoExtraConstraint)
|
LUAU_FASTFLAG(LuauUserTypeFunNoExtraConstraint)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -1153,6 +1154,42 @@ bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNul
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ConstraintSolver::fillInDiscriminantTypes(
|
||||||
|
NotNull<const Constraint> constraint,
|
||||||
|
const std::vector<std::optional<TypeId>>& discriminantTypes
|
||||||
|
)
|
||||||
|
{
|
||||||
|
for (std::optional<TypeId> ty : discriminantTypes)
|
||||||
|
{
|
||||||
|
if (!ty)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// If the discriminant type has been transmuted, we need to unblock them.
|
||||||
|
if (!isBlocked(*ty))
|
||||||
|
{
|
||||||
|
unblock(*ty, constraint->location);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauRemoveNotAnyHack)
|
||||||
|
{
|
||||||
|
// We bind any unused discriminants to the `*no-refine*` type indicating that it can be safely ignored.
|
||||||
|
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->noRefineType);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We use `any` here because the discriminant type may be pointed at by both branches,
|
||||||
|
// where the discriminant type is not negated, and the other where it is negated, i.e.
|
||||||
|
// `unknown ~ unknown` and `~unknown ~ never`, so `T & unknown ~ T` and `T & ~unknown ~ never`
|
||||||
|
// v.s.
|
||||||
|
// `any ~ any` and `~any ~ any`, so `T & any ~ T` and `T & ~any ~ T`
|
||||||
|
//
|
||||||
|
// In practice, users cannot negate `any`, so this is an implementation detail we can always change.
|
||||||
|
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->anyType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint)
|
bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<const Constraint> constraint)
|
||||||
{
|
{
|
||||||
TypeId fn = follow(c.fn);
|
TypeId fn = follow(c.fn);
|
||||||
|
@ -1168,6 +1205,8 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
{
|
{
|
||||||
emplaceTypePack<BoundTypePack>(asMutable(c.result), builtinTypes->anyTypePack);
|
emplaceTypePack<BoundTypePack>(asMutable(c.result), builtinTypes->anyTypePack);
|
||||||
unblock(c.result, constraint->location);
|
unblock(c.result, constraint->location);
|
||||||
|
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
||||||
|
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1175,12 +1214,16 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
if (get<ErrorType>(fn))
|
if (get<ErrorType>(fn))
|
||||||
{
|
{
|
||||||
bind(constraint, c.result, builtinTypes->errorRecoveryTypePack());
|
bind(constraint, c.result, builtinTypes->errorRecoveryTypePack());
|
||||||
|
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
||||||
|
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (get<NeverType>(fn))
|
if (get<NeverType>(fn))
|
||||||
{
|
{
|
||||||
bind(constraint, c.result, builtinTypes->neverTypePack);
|
bind(constraint, c.result, builtinTypes->neverTypePack);
|
||||||
|
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
||||||
|
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1261,6 +1304,13 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
emplace<FreeTypePack>(constraint, c.result, constraint->scope);
|
emplace<FreeTypePack>(constraint, c.result, constraint->scope);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
||||||
|
{
|
||||||
|
fillInDiscriminantTypes(constraint, c.discriminantTypes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// NOTE: This is the body of the `fillInDiscriminantTypes` helper.
|
||||||
for (std::optional<TypeId> ty : c.discriminantTypes)
|
for (std::optional<TypeId> ty : c.discriminantTypes)
|
||||||
{
|
{
|
||||||
if (!ty)
|
if (!ty)
|
||||||
|
@ -1290,6 +1340,8 @@ bool ConstraintSolver::tryDispatch(const FunctionCallConstraint& c, NotNull<cons
|
||||||
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->anyType);
|
emplaceType<BoundType>(asMutable(follow(*ty)), builtinTypes->anyType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
OverloadResolver resolver{
|
OverloadResolver resolver{
|
||||||
builtinTypes,
|
builtinTypes,
|
||||||
|
|
|
@ -2,14 +2,14 @@
|
||||||
#include "Luau/BuiltinDefinitions.h"
|
#include "Luau/BuiltinDefinitions.h"
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauVectorDefinitionsExtra)
|
LUAU_FASTFLAGVARIABLE(LuauVectorDefinitionsExtra)
|
||||||
LUAU_FASTFLAG(LuauBufferBitMethods)
|
|
||||||
LUAU_FASTFLAG(LuauVector2Constructor)
|
LUAU_FASTFLAG(LuauVector2Constructor)
|
||||||
|
LUAU_FASTFLAG(LuauBufferBitMethods2)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauMathMapDefinition)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
|
||||||
// TODO: there has to be a better way, like splitting up per library
|
static const std::string kBuiltinDefinitionLuaSrcChecked_DEPRECATED = R"BUILTIN_SRC(
|
||||||
static const std::string kBuiltinDefinitionLuaSrcChecked = R"BUILTIN_SRC(
|
|
||||||
|
|
||||||
declare bit32: {
|
declare bit32: {
|
||||||
band: @checked (...number) -> number,
|
band: @checked (...number) -> number,
|
||||||
|
@ -202,6 +202,228 @@ declare function unpack<V>(tab: {V}, i: number?, j: number?): ...V
|
||||||
|
|
||||||
)BUILTIN_SRC";
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionBaseSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
|
@checked declare function require(target: any): any
|
||||||
|
|
||||||
|
@checked declare function getfenv(target: any): { [string]: any }
|
||||||
|
|
||||||
|
declare _G: any
|
||||||
|
declare _VERSION: string
|
||||||
|
|
||||||
|
declare function gcinfo(): number
|
||||||
|
|
||||||
|
declare function print<T...>(...: T...)
|
||||||
|
|
||||||
|
declare function type<T>(value: T): string
|
||||||
|
declare function typeof<T>(value: T): string
|
||||||
|
|
||||||
|
-- `assert` has a magic function attached that will give more detailed type information
|
||||||
|
declare function assert<T>(value: T, errorMessage: string?): T
|
||||||
|
declare function error<T>(message: T, level: number?): never
|
||||||
|
|
||||||
|
declare function tostring<T>(value: T): string
|
||||||
|
declare function tonumber<T>(value: T, radix: number?): number?
|
||||||
|
|
||||||
|
declare function rawequal<T1, T2>(a: T1, b: T2): boolean
|
||||||
|
declare function rawget<K, V>(tab: {[K]: V}, k: K): V
|
||||||
|
declare function rawset<K, V>(tab: {[K]: V}, k: K, v: V): {[K]: V}
|
||||||
|
declare function rawlen<K, V>(obj: {[K]: V} | string): number
|
||||||
|
|
||||||
|
declare function setfenv<T..., R...>(target: number | (T...) -> R..., env: {[string]: any}): ((T...) -> R...)?
|
||||||
|
|
||||||
|
declare function ipairs<V>(tab: {V}): (({V}, number) -> (number?, V), {V}, number)
|
||||||
|
|
||||||
|
declare function pcall<A..., R...>(f: (A...) -> R..., ...: A...): (boolean, R...)
|
||||||
|
|
||||||
|
-- FIXME: The actual type of `xpcall` is:
|
||||||
|
-- <E, A..., R1..., R2...>(f: (A...) -> R1..., err: (E) -> R2..., A...) -> (true, R1...) | (false, R2...)
|
||||||
|
-- Since we can't represent the return value, we use (boolean, R1...).
|
||||||
|
declare function xpcall<E, A..., R1..., R2...>(f: (A...) -> R1..., err: (E) -> R2..., ...: A...): (boolean, R1...)
|
||||||
|
|
||||||
|
-- `select` has a magic function attached to provide more detailed type information
|
||||||
|
declare function select<A...>(i: string | number, ...: A...): ...any
|
||||||
|
|
||||||
|
-- FIXME: This type is not entirely correct - `loadstring` returns a function or
|
||||||
|
-- (nil, string).
|
||||||
|
declare function loadstring<A...>(src: string, chunkname: string?): (((A...) -> any)?, string?)
|
||||||
|
|
||||||
|
@checked declare function newproxy(mt: boolean?): any
|
||||||
|
|
||||||
|
-- Cannot use `typeof` here because it will produce a polytype when we expect a monotype.
|
||||||
|
declare function unpack<V>(tab: {V}, i: number?, j: number?): ...V
|
||||||
|
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionBit32Src = R"BUILTIN_SRC(
|
||||||
|
|
||||||
|
declare bit32: {
|
||||||
|
band: @checked (...number) -> number,
|
||||||
|
bor: @checked (...number) -> number,
|
||||||
|
bxor: @checked (...number) -> number,
|
||||||
|
btest: @checked (number, ...number) -> boolean,
|
||||||
|
rrotate: @checked (x: number, disp: number) -> number,
|
||||||
|
lrotate: @checked (x: number, disp: number) -> number,
|
||||||
|
lshift: @checked (x: number, disp: number) -> number,
|
||||||
|
arshift: @checked (x: number, disp: number) -> number,
|
||||||
|
rshift: @checked (x: number, disp: number) -> number,
|
||||||
|
bnot: @checked (x: number) -> number,
|
||||||
|
extract: @checked (n: number, field: number, width: number?) -> number,
|
||||||
|
replace: @checked (n: number, v: number, field: number, width: number?) -> number,
|
||||||
|
countlz: @checked (n: number) -> number,
|
||||||
|
countrz: @checked (n: number) -> number,
|
||||||
|
byteswap: @checked (n: number) -> number,
|
||||||
|
}
|
||||||
|
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionMathSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
|
declare math: {
|
||||||
|
frexp: @checked (n: number) -> (number, number),
|
||||||
|
ldexp: @checked (s: number, e: number) -> number,
|
||||||
|
fmod: @checked (x: number, y: number) -> number,
|
||||||
|
modf: @checked (n: number) -> (number, number),
|
||||||
|
pow: @checked (x: number, y: number) -> number,
|
||||||
|
exp: @checked (n: number) -> number,
|
||||||
|
|
||||||
|
ceil: @checked (n: number) -> number,
|
||||||
|
floor: @checked (n: number) -> number,
|
||||||
|
abs: @checked (n: number) -> number,
|
||||||
|
sqrt: @checked (n: number) -> number,
|
||||||
|
|
||||||
|
log: @checked (n: number, base: number?) -> number,
|
||||||
|
log10: @checked (n: number) -> number,
|
||||||
|
|
||||||
|
rad: @checked (n: number) -> number,
|
||||||
|
deg: @checked (n: number) -> number,
|
||||||
|
|
||||||
|
sin: @checked (n: number) -> number,
|
||||||
|
cos: @checked (n: number) -> number,
|
||||||
|
tan: @checked (n: number) -> number,
|
||||||
|
sinh: @checked (n: number) -> number,
|
||||||
|
cosh: @checked (n: number) -> number,
|
||||||
|
tanh: @checked (n: number) -> number,
|
||||||
|
atan: @checked (n: number) -> number,
|
||||||
|
acos: @checked (n: number) -> number,
|
||||||
|
asin: @checked (n: number) -> number,
|
||||||
|
atan2: @checked (y: number, x: number) -> number,
|
||||||
|
|
||||||
|
min: @checked (number, ...number) -> number,
|
||||||
|
max: @checked (number, ...number) -> number,
|
||||||
|
|
||||||
|
pi: number,
|
||||||
|
huge: number,
|
||||||
|
|
||||||
|
randomseed: @checked (seed: number) -> (),
|
||||||
|
random: @checked (number?, number?) -> number,
|
||||||
|
|
||||||
|
sign: @checked (n: number) -> number,
|
||||||
|
clamp: @checked (n: number, min: number, max: number) -> number,
|
||||||
|
noise: @checked (x: number, y: number?, z: number?) -> number,
|
||||||
|
round: @checked (n: number) -> number,
|
||||||
|
map: @checked (x: number, inmin: number, inmax: number, outmin: number, outmax: number) -> number,
|
||||||
|
lerp: @checked (a: number, b: number, t: number) -> number,
|
||||||
|
}
|
||||||
|
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionOsSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
|
type DateTypeArg = {
|
||||||
|
year: number,
|
||||||
|
month: number,
|
||||||
|
day: number,
|
||||||
|
hour: number?,
|
||||||
|
min: number?,
|
||||||
|
sec: number?,
|
||||||
|
isdst: boolean?,
|
||||||
|
}
|
||||||
|
|
||||||
|
type DateTypeResult = {
|
||||||
|
year: number,
|
||||||
|
month: number,
|
||||||
|
wday: number,
|
||||||
|
yday: number,
|
||||||
|
day: number,
|
||||||
|
hour: number,
|
||||||
|
min: number,
|
||||||
|
sec: number,
|
||||||
|
isdst: boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
declare os: {
|
||||||
|
time: (time: DateTypeArg?) -> number,
|
||||||
|
date: ((formatString: "*t" | "!*t", time: number?) -> DateTypeResult) & ((formatString: string?, time: number?) -> string),
|
||||||
|
difftime: (t2: DateTypeResult | number, t1: DateTypeResult | number) -> number,
|
||||||
|
clock: () -> number,
|
||||||
|
}
|
||||||
|
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionCoroutineSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
|
declare coroutine: {
|
||||||
|
create: <A..., R...>(f: (A...) -> R...) -> thread,
|
||||||
|
resume: <A..., R...>(co: thread, A...) -> (boolean, R...),
|
||||||
|
running: () -> thread,
|
||||||
|
status: @checked (co: thread) -> "dead" | "running" | "normal" | "suspended",
|
||||||
|
wrap: <A..., R...>(f: (A...) -> R...) -> ((A...) -> R...),
|
||||||
|
yield: <A..., R...>(A...) -> R...,
|
||||||
|
isyieldable: () -> boolean,
|
||||||
|
close: @checked (co: thread) -> (boolean, any)
|
||||||
|
}
|
||||||
|
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionTableSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
|
declare table: {
|
||||||
|
concat: <V>(t: {V}, sep: string?, i: number?, j: number?) -> string,
|
||||||
|
insert: (<V>(t: {V}, value: V) -> ()) & (<V>(t: {V}, pos: number, value: V) -> ()),
|
||||||
|
maxn: <V>(t: {V}) -> number,
|
||||||
|
remove: <V>(t: {V}, number?) -> V?,
|
||||||
|
sort: <V>(t: {V}, comp: ((V, V) -> boolean)?) -> (),
|
||||||
|
create: <V>(count: number, value: V?) -> {V},
|
||||||
|
find: <V>(haystack: {V}, needle: V, init: number?) -> number?,
|
||||||
|
|
||||||
|
unpack: <V>(list: {V}, i: number?, j: number?) -> ...V,
|
||||||
|
pack: <V>(...V) -> { n: number, [number]: V },
|
||||||
|
|
||||||
|
getn: <V>(t: {V}) -> number,
|
||||||
|
foreach: <K, V>(t: {[K]: V}, f: (K, V) -> ()) -> (),
|
||||||
|
foreachi: <V>({V}, (number, V) -> ()) -> (),
|
||||||
|
|
||||||
|
move: <V>(src: {V}, a: number, b: number, t: number, dst: {V}?) -> {V},
|
||||||
|
clear: <K, V>(table: {[K]: V}) -> (),
|
||||||
|
|
||||||
|
isfrozen: <K, V>(t: {[K]: V}) -> boolean,
|
||||||
|
}
|
||||||
|
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionDebugSrc = R"BUILTIN_SRC(
|
||||||
|
|
||||||
|
declare debug: {
|
||||||
|
info: (<R...>(thread: thread, level: number, options: string) -> R...) & (<R...>(level: number, options: string) -> R...) & (<A..., R1..., R2...>(func: (A...) -> R1..., options: string) -> R2...),
|
||||||
|
traceback: ((message: string?, level: number?) -> string) & ((thread: thread, message: string?, level: number?) -> string),
|
||||||
|
}
|
||||||
|
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
|
static const std::string kBuiltinDefinitionUtf8Src = R"BUILTIN_SRC(
|
||||||
|
|
||||||
|
declare utf8: {
|
||||||
|
char: @checked (...number) -> string,
|
||||||
|
charpattern: string,
|
||||||
|
codes: @checked (str: string) -> ((string, number) -> (number, number), string, number),
|
||||||
|
codepoint: @checked (str: string, i: number?, j: number?) -> ...number,
|
||||||
|
len: @checked (s: string, i: number?, j: number?) -> (number?, number?),
|
||||||
|
offset: @checked (s: string, n: number?, i: number?) -> number,
|
||||||
|
}
|
||||||
|
|
||||||
|
)BUILTIN_SRC";
|
||||||
|
|
||||||
static const std::string kBuiltinDefinitionBufferSrc_DEPRECATED = R"BUILTIN_SRC(
|
static const std::string kBuiltinDefinitionBufferSrc_DEPRECATED = R"BUILTIN_SRC(
|
||||||
--- Buffer API
|
--- Buffer API
|
||||||
declare buffer: {
|
declare buffer: {
|
||||||
|
@ -380,9 +602,20 @@ declare vector: {
|
||||||
|
|
||||||
std::string getBuiltinDefinitionSource()
|
std::string getBuiltinDefinitionSource()
|
||||||
{
|
{
|
||||||
std::string result = kBuiltinDefinitionLuaSrcChecked;
|
std::string result = FFlag::LuauMathMapDefinition ? kBuiltinDefinitionBaseSrc : kBuiltinDefinitionLuaSrcChecked_DEPRECATED;
|
||||||
|
|
||||||
result += FFlag::LuauBufferBitMethods ? kBuiltinDefinitionBufferSrc : kBuiltinDefinitionBufferSrc_DEPRECATED;
|
if (FFlag::LuauMathMapDefinition)
|
||||||
|
{
|
||||||
|
result += kBuiltinDefinitionBit32Src;
|
||||||
|
result += kBuiltinDefinitionMathSrc;
|
||||||
|
result += kBuiltinDefinitionOsSrc;
|
||||||
|
result += kBuiltinDefinitionCoroutineSrc;
|
||||||
|
result += kBuiltinDefinitionTableSrc;
|
||||||
|
result += kBuiltinDefinitionDebugSrc;
|
||||||
|
result += kBuiltinDefinitionUtf8Src;
|
||||||
|
}
|
||||||
|
|
||||||
|
result += FFlag::LuauBufferBitMethods2 ? kBuiltinDefinitionBufferSrc : kBuiltinDefinitionBufferSrc_DEPRECATED;
|
||||||
|
|
||||||
if (FFlag::LuauVectorDefinitionsExtra)
|
if (FFlag::LuauVectorDefinitionsExtra)
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,6 +18,8 @@ LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixInner)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunPrintToError)
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunPrintToError)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixNoReadWrite)
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunFixNoReadWrite)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunThreadBuffer)
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunThreadBuffer)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunGenerics)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauUserTypeFunCloneTail)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -161,6 +163,8 @@ static std::string getTag(lua_State* L, TypeFunctionTypeId ty)
|
||||||
return "function";
|
return "function";
|
||||||
else if (get<TypeFunctionClassType>(ty))
|
else if (get<TypeFunctionClassType>(ty))
|
||||||
return "class";
|
return "class";
|
||||||
|
else if (FFlag::LuauUserTypeFunGenerics && get<TypeFunctionGenericType>(ty))
|
||||||
|
return "generic";
|
||||||
|
|
||||||
LUAU_UNREACHABLE();
|
LUAU_UNREACHABLE();
|
||||||
luaL_error(L, "VM encountered unexpected type variant when determining tag");
|
luaL_error(L, "VM encountered unexpected type variant when determining tag");
|
||||||
|
@ -267,6 +271,20 @@ static int createSingleton(lua_State* L)
|
||||||
luaL_error(L, "types.singleton: can't create singleton from `%s` type", lua_typename(L, 1));
|
luaL_error(L, "types.singleton: can't create singleton from `%s` type", lua_typename(L, 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Luau: `types.generic(name: string, ispack: boolean?) -> type
|
||||||
|
// Create a generic type with the specified type. If an optinal boolean is set to true, result is a generic pack
|
||||||
|
static int createGeneric(lua_State* L)
|
||||||
|
{
|
||||||
|
const char* name = luaL_checkstring(L, 1);
|
||||||
|
bool isPack = luaL_optboolean(L, 2, false);
|
||||||
|
|
||||||
|
if (strlen(name) == 0)
|
||||||
|
luaL_error(L, "types.generic: generic name cannot be empty");
|
||||||
|
|
||||||
|
allocTypeUserData(L, TypeFunctionGenericType{/* isNamed */ true, isPack, name});
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Luau: `self:value() -> type`
|
// Luau: `self:value() -> type`
|
||||||
// Returns the value of a singleton
|
// Returns the value of a singleton
|
||||||
static int getSingletonValue(lua_State* L)
|
static int getSingletonValue(lua_State* L)
|
||||||
|
@ -780,9 +798,159 @@ static int setTableMetatable(lua_State* L)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Luau: `types.newfunction(parameters: {head: {type}?, tail: type?}, returns: {head: {type}?, tail: type?}) -> type`
|
static std::tuple<std::vector<TypeFunctionTypeId>, std::vector<TypeFunctionTypePackId>> getGenerics(lua_State* L, int idx, const char* fname)
|
||||||
// Returns the type instance representing a function
|
{
|
||||||
static int createFunction(lua_State* L)
|
std::vector<TypeFunctionTypeId> types;
|
||||||
|
std::vector<TypeFunctionTypePackId> packs;
|
||||||
|
|
||||||
|
if (lua_istable(L, idx))
|
||||||
|
{
|
||||||
|
lua_pushvalue(L, idx);
|
||||||
|
|
||||||
|
for (int i = 1; i <= lua_objlen(L, -1); i++)
|
||||||
|
{
|
||||||
|
lua_pushinteger(L, i);
|
||||||
|
lua_gettable(L, -2);
|
||||||
|
|
||||||
|
if (lua_isnil(L, -1))
|
||||||
|
{
|
||||||
|
lua_pop(L, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeFunctionTypeId ty = getTypeUserData(L, -1);
|
||||||
|
|
||||||
|
if (auto gty = get<TypeFunctionGenericType>(ty))
|
||||||
|
{
|
||||||
|
if (gty->isPack)
|
||||||
|
{
|
||||||
|
packs.push_back(allocateTypeFunctionTypePack(L, TypeFunctionGenericTypePack{gty->isNamed, gty->name}));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!packs.empty())
|
||||||
|
luaL_error(L, "%s: generic type cannot follow a generic pack", fname);
|
||||||
|
|
||||||
|
types.push_back(ty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
luaL_error(L, "%s: table member was not a generic type", fname);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
else if (!lua_isnoneornil(L, idx))
|
||||||
|
{
|
||||||
|
luaL_typeerrorL(L, idx, "table");
|
||||||
|
}
|
||||||
|
|
||||||
|
return {types, packs};
|
||||||
|
}
|
||||||
|
|
||||||
|
static TypeFunctionTypePackId getTypePack(lua_State* L, int headIdx, int tailIdx)
|
||||||
|
{
|
||||||
|
TypeFunctionTypePackId result = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{});
|
||||||
|
|
||||||
|
std::vector<TypeFunctionTypeId> head;
|
||||||
|
|
||||||
|
if (lua_istable(L, headIdx))
|
||||||
|
{
|
||||||
|
lua_pushvalue(L, headIdx);
|
||||||
|
|
||||||
|
for (int i = 1; i <= lua_objlen(L, -1); i++)
|
||||||
|
{
|
||||||
|
lua_pushinteger(L, i);
|
||||||
|
lua_gettable(L, -2);
|
||||||
|
|
||||||
|
if (lua_isnil(L, -1))
|
||||||
|
{
|
||||||
|
lua_pop(L, 1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
head.push_back(getTypeUserData(L, -1));
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_pop(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TypeFunctionTypePackId> tail;
|
||||||
|
|
||||||
|
if (auto type = optionalTypeUserData(L, tailIdx))
|
||||||
|
{
|
||||||
|
if (auto gty = get<TypeFunctionGenericType>(*type); gty && gty->isPack)
|
||||||
|
tail = allocateTypeFunctionTypePack(L, TypeFunctionGenericTypePack{gty->isNamed, gty->name});
|
||||||
|
else
|
||||||
|
tail = allocateTypeFunctionTypePack(L, TypeFunctionVariadicTypePack{*type});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (head.size() == 0 && tail.has_value())
|
||||||
|
result = *tail;
|
||||||
|
else
|
||||||
|
result = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{head, tail});
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void pushTypePack(lua_State* L, TypeFunctionTypePackId tp)
|
||||||
|
{
|
||||||
|
if (auto tftp = get<TypeFunctionTypePack>(tp))
|
||||||
|
{
|
||||||
|
lua_createtable(L, 0, 2);
|
||||||
|
|
||||||
|
if (!tftp->head.empty())
|
||||||
|
{
|
||||||
|
lua_createtable(L, int(tftp->head.size()), 0);
|
||||||
|
int pos = 1;
|
||||||
|
|
||||||
|
for (auto el : tftp->head)
|
||||||
|
{
|
||||||
|
allocTypeUserData(L, el->type);
|
||||||
|
lua_rawseti(L, -2, pos++);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_setfield(L, -2, "head");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tftp->tail.has_value())
|
||||||
|
{
|
||||||
|
if (auto tfvp = get<TypeFunctionVariadicTypePack>(*tftp->tail))
|
||||||
|
allocTypeUserData(L, tfvp->type->type);
|
||||||
|
else if (auto tfgp = get<TypeFunctionGenericTypePack>(*tftp->tail))
|
||||||
|
allocTypeUserData(L, TypeFunctionGenericType{tfgp->isNamed, true, tfgp->name});
|
||||||
|
else
|
||||||
|
luaL_error(L, "unsupported type pack type");
|
||||||
|
|
||||||
|
lua_setfield(L, -2, "tail");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (auto tfvp = get<TypeFunctionVariadicTypePack>(tp))
|
||||||
|
{
|
||||||
|
lua_createtable(L, 0, 1);
|
||||||
|
|
||||||
|
allocTypeUserData(L, tfvp->type->type);
|
||||||
|
lua_setfield(L, -2, "tail");
|
||||||
|
}
|
||||||
|
else if (auto tfgp = get<TypeFunctionGenericTypePack>(tp))
|
||||||
|
{
|
||||||
|
lua_createtable(L, 0, 1);
|
||||||
|
|
||||||
|
allocTypeUserData(L, TypeFunctionGenericType{tfgp->isNamed, true, tfgp->name});
|
||||||
|
lua_setfield(L, -2, "tail");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
luaL_error(L, "unsupported type pack type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int createFunction_DEPRECATED(lua_State* L)
|
||||||
{
|
{
|
||||||
int argumentCount = lua_gettop(L);
|
int argumentCount = lua_gettop(L);
|
||||||
if (argumentCount > 2)
|
if (argumentCount > 2)
|
||||||
|
@ -870,7 +1038,62 @@ static int createFunction(lua_State* L)
|
||||||
else if (!lua_isnoneornil(L, 2))
|
else if (!lua_isnoneornil(L, 2))
|
||||||
luaL_typeerrorL(L, 2, "table");
|
luaL_typeerrorL(L, 2, "table");
|
||||||
|
|
||||||
allocTypeUserData(L, TypeFunctionFunctionType{argTypes, retTypes});
|
allocTypeUserData(L, TypeFunctionFunctionType{{}, {}, argTypes, retTypes});
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Luau: `types.newfunction(parameters: {head: {type}?, tail: type?}, returns: {head: {type}?, tail: type?}, generics: {type}?) -> type`
|
||||||
|
// Returns the type instance representing a function
|
||||||
|
static int createFunction(lua_State* L)
|
||||||
|
{
|
||||||
|
int argumentCount = lua_gettop(L);
|
||||||
|
if (argumentCount > 3)
|
||||||
|
luaL_error(L, "types.newfunction: expected 0-3 arguments, but got %d", argumentCount);
|
||||||
|
|
||||||
|
TypeFunctionTypePackId argTypes = nullptr;
|
||||||
|
|
||||||
|
if (lua_istable(L, 1))
|
||||||
|
{
|
||||||
|
lua_getfield(L, 1, "head");
|
||||||
|
lua_getfield(L, 1, "tail");
|
||||||
|
|
||||||
|
argTypes = getTypePack(L, -2, -1);
|
||||||
|
|
||||||
|
lua_pop(L, 2);
|
||||||
|
}
|
||||||
|
else if (!lua_isnoneornil(L, 1))
|
||||||
|
{
|
||||||
|
luaL_typeerrorL(L, 1, "table");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
argTypes = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{});
|
||||||
|
}
|
||||||
|
|
||||||
|
TypeFunctionTypePackId retTypes = nullptr;
|
||||||
|
|
||||||
|
if (lua_istable(L, 2))
|
||||||
|
{
|
||||||
|
lua_getfield(L, 2, "head");
|
||||||
|
lua_getfield(L, 2, "tail");
|
||||||
|
|
||||||
|
retTypes = getTypePack(L, -2, -1);
|
||||||
|
|
||||||
|
lua_pop(L, 2);
|
||||||
|
}
|
||||||
|
else if (!lua_isnoneornil(L, 2))
|
||||||
|
{
|
||||||
|
luaL_typeerrorL(L, 2, "table");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
retTypes = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{});
|
||||||
|
}
|
||||||
|
|
||||||
|
auto [genericTypes, genericPacks] = getGenerics(L, 3, "types.newfunction");
|
||||||
|
|
||||||
|
allocTypeUserData(L, TypeFunctionFunctionType{std::move(genericTypes), std::move(genericPacks), argTypes, retTypes});
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -888,6 +1111,12 @@ static int setFunctionParameters(lua_State* L)
|
||||||
if (!tfft)
|
if (!tfft)
|
||||||
luaL_error(L, "type.setparameters: expected self to be a function, but got %s instead", getTag(L, self).c_str());
|
luaL_error(L, "type.setparameters: expected self to be a function, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
if (FFlag::LuauUserTypeFunGenerics)
|
||||||
|
{
|
||||||
|
tfft->argTypes = getTypePack(L, 2, 3);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
std::vector<TypeFunctionTypeId> head{};
|
std::vector<TypeFunctionTypeId> head{};
|
||||||
if (lua_istable(L, 2))
|
if (lua_istable(L, 2))
|
||||||
{
|
{
|
||||||
|
@ -920,6 +1149,7 @@ static int setFunctionParameters(lua_State* L)
|
||||||
tfft->argTypes = *tail;
|
tfft->argTypes = *tail;
|
||||||
else // Make argTypes a type pack
|
else // Make argTypes a type pack
|
||||||
tfft->argTypes = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{head, tail});
|
tfft->argTypes = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{head, tail});
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -937,6 +1167,12 @@ static int getFunctionParameters(lua_State* L)
|
||||||
if (!tfft)
|
if (!tfft)
|
||||||
luaL_error(L, "type.parameters: expected self to be a function, but got %s instead", getTag(L, self).c_str());
|
luaL_error(L, "type.parameters: expected self to be a function, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
if (FFlag::LuauUserTypeFunGenerics)
|
||||||
|
{
|
||||||
|
pushTypePack(L, tfft->argTypes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if (auto tftp = get<TypeFunctionTypePack>(tfft->argTypes))
|
if (auto tftp = get<TypeFunctionTypePack>(tfft->argTypes))
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
@ -983,6 +1219,8 @@ static int getFunctionParameters(lua_State* L)
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_createtable(L, 0, 0);
|
lua_createtable(L, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -999,6 +1237,12 @@ static int setFunctionReturns(lua_State* L)
|
||||||
if (!tfft)
|
if (!tfft)
|
||||||
luaL_error(L, "type.setreturns: expected self to be a function, but got %s instead", getTag(L, self).c_str());
|
luaL_error(L, "type.setreturns: expected self to be a function, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
if (FFlag::LuauUserTypeFunGenerics)
|
||||||
|
{
|
||||||
|
tfft->retTypes = getTypePack(L, 2, 3);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
std::vector<TypeFunctionTypeId> head{};
|
std::vector<TypeFunctionTypeId> head{};
|
||||||
if (lua_istable(L, 2))
|
if (lua_istable(L, 2))
|
||||||
{
|
{
|
||||||
|
@ -1031,6 +1275,7 @@ static int setFunctionReturns(lua_State* L)
|
||||||
tfft->retTypes = *tail;
|
tfft->retTypes = *tail;
|
||||||
else // Make retTypes a type pack
|
else // Make retTypes a type pack
|
||||||
tfft->retTypes = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{head, tail});
|
tfft->retTypes = allocateTypeFunctionTypePack(L, TypeFunctionTypePack{head, tail});
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -1048,6 +1293,12 @@ static int getFunctionReturns(lua_State* L)
|
||||||
if (!tfft)
|
if (!tfft)
|
||||||
luaL_error(L, "type.returns: expected self to be a function, but got %s instead", getTag(L, self).c_str());
|
luaL_error(L, "type.returns: expected self to be a function, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
if (FFlag::LuauUserTypeFunGenerics)
|
||||||
|
{
|
||||||
|
pushTypePack(L, tfft->retTypes);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
if (auto tftp = get<TypeFunctionTypePack>(tfft->retTypes))
|
if (auto tftp = get<TypeFunctionTypePack>(tfft->retTypes))
|
||||||
{
|
{
|
||||||
int size = 0;
|
int size = 0;
|
||||||
|
@ -1094,6 +1345,57 @@ static int getFunctionReturns(lua_State* L)
|
||||||
}
|
}
|
||||||
|
|
||||||
lua_createtable(L, 0, 0);
|
lua_createtable(L, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Luau: `self:setgenerics(generics: {type}?)`
|
||||||
|
static int setFunctionGenerics(lua_State* L)
|
||||||
|
{
|
||||||
|
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||||
|
auto tfft = getMutable<TypeFunctionFunctionType>(self);
|
||||||
|
if (!tfft)
|
||||||
|
luaL_error(L, "type.setgenerics: expected self to be a function, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
int argumentCount = lua_gettop(L);
|
||||||
|
if (argumentCount > 3)
|
||||||
|
luaL_error(L, "type.setgenerics: expected 3 arguments, but got %d", argumentCount);
|
||||||
|
|
||||||
|
auto [genericTypes, genericPacks] = getGenerics(L, 2, "types.setgenerics");
|
||||||
|
|
||||||
|
tfft->generics = std::move(genericTypes);
|
||||||
|
tfft->genericPacks = std::move(genericPacks);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Luau: `self:generics() -> {type}`
|
||||||
|
static int getFunctionGenerics(lua_State* L)
|
||||||
|
{
|
||||||
|
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||||
|
auto tfft = get<TypeFunctionFunctionType>(self);
|
||||||
|
if (!tfft)
|
||||||
|
luaL_error(L, "type.generics: expected self to be a function, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
lua_createtable(L, int(tfft->generics.size()) + int(tfft->genericPacks.size()), 0);
|
||||||
|
|
||||||
|
int pos = 1;
|
||||||
|
|
||||||
|
for (const auto& el : tfft->generics)
|
||||||
|
{
|
||||||
|
allocTypeUserData(L, el->type);
|
||||||
|
lua_rawseti(L, -2, pos++);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& el : tfft->genericPacks)
|
||||||
|
{
|
||||||
|
auto gty = get<TypeFunctionGenericTypePack>(el);
|
||||||
|
LUAU_ASSERT(gty);
|
||||||
|
allocTypeUserData(L, TypeFunctionGenericType{gty->isNamed, true, gty->name});
|
||||||
|
lua_rawseti(L, -2, pos++);
|
||||||
|
}
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1119,6 +1421,36 @@ static int getClassParent(lua_State* L)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Luau: `self:name() -> string?`
|
||||||
|
// Returns the name of the generic or 'nil' if the generic is unnamed
|
||||||
|
static int getGenericName(lua_State* L)
|
||||||
|
{
|
||||||
|
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||||
|
auto tfgt = get<TypeFunctionGenericType>(self);
|
||||||
|
if (!tfgt)
|
||||||
|
luaL_error(L, "type.name: expected self to be a generic, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
if (tfgt->isNamed)
|
||||||
|
lua_pushstring(L, tfgt->name.c_str());
|
||||||
|
else
|
||||||
|
lua_pushnil(L);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Luau: `self:ispack() -> boolean`
|
||||||
|
// Returns true if the generic is a pack
|
||||||
|
static int getGenericIsPack(lua_State* L)
|
||||||
|
{
|
||||||
|
TypeFunctionTypeId self = getTypeUserData(L, 1);
|
||||||
|
auto tfgt = get<TypeFunctionGenericType>(self);
|
||||||
|
if (!tfgt)
|
||||||
|
luaL_error(L, "type.ispack: expected self to be a generic, but got %s instead", getTag(L, self).c_str());
|
||||||
|
|
||||||
|
lua_pushboolean(L, tfgt->isPack);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
// Luau: `self:properties() -> {[type]: { read: type?, write: type? }}`
|
// Luau: `self:properties() -> {[type]: { read: type?, write: type? }}`
|
||||||
// Returns the properties of a table or class type
|
// Returns the properties of a table or class type
|
||||||
static int getProps(lua_State* L)
|
static int getProps(lua_State* L)
|
||||||
|
@ -1388,7 +1720,7 @@ static int checkTag(lua_State* L)
|
||||||
|
|
||||||
TypeFunctionTypeId deepClone(NotNull<TypeFunctionRuntime> runtime, TypeFunctionTypeId ty); // Forward declaration
|
TypeFunctionTypeId deepClone(NotNull<TypeFunctionRuntime> runtime, TypeFunctionTypeId ty); // Forward declaration
|
||||||
|
|
||||||
// Luau: `types.copy(arg: string) -> type`
|
// Luau: `types.copy(arg: type) -> type`
|
||||||
// Returns a deep copy of the argument
|
// Returns a deep copy of the argument
|
||||||
static int deepCopy(lua_State* L)
|
static int deepCopy(lua_State* L)
|
||||||
{
|
{
|
||||||
|
@ -1438,8 +1770,9 @@ void registerTypesLibrary(lua_State* L)
|
||||||
{"unionof", createUnion},
|
{"unionof", createUnion},
|
||||||
{"intersectionof", createIntersection},
|
{"intersectionof", createIntersection},
|
||||||
{"newtable", createTable},
|
{"newtable", createTable},
|
||||||
{"newfunction", createFunction},
|
{"newfunction", FFlag::LuauUserTypeFunGenerics ? createFunction : createFunction_DEPRECATED},
|
||||||
{"copy", deepCopy},
|
{"copy", deepCopy},
|
||||||
|
{FFlag::LuauUserTypeFunGenerics ? "generic" : nullptr, FFlag::LuauUserTypeFunGenerics ? createGeneric : nullptr},
|
||||||
|
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
@ -1504,6 +1837,8 @@ void registerTypeUserData(lua_State* L)
|
||||||
{"parameters", getFunctionParameters},
|
{"parameters", getFunctionParameters},
|
||||||
{"setreturns", setFunctionReturns},
|
{"setreturns", setFunctionReturns},
|
||||||
{"returns", getFunctionReturns},
|
{"returns", getFunctionReturns},
|
||||||
|
{"setgenerics", setFunctionGenerics},
|
||||||
|
{"generics", getFunctionGenerics},
|
||||||
|
|
||||||
// Union and Intersection type methods
|
// Union and Intersection type methods
|
||||||
{"components", getComponents},
|
{"components", getComponents},
|
||||||
|
@ -1511,6 +1846,14 @@ void registerTypeUserData(lua_State* L)
|
||||||
// Class type methods
|
// Class type methods
|
||||||
{"parent", getClassParent},
|
{"parent", getClassParent},
|
||||||
|
|
||||||
|
// Function type methods (cont.)
|
||||||
|
{FFlag::LuauUserTypeFunGenerics ? "setgenerics" : nullptr, FFlag::LuauUserTypeFunGenerics ? setFunctionGenerics : nullptr},
|
||||||
|
{FFlag::LuauUserTypeFunGenerics ? "generics" : nullptr, FFlag::LuauUserTypeFunGenerics ? getFunctionGenerics : nullptr},
|
||||||
|
|
||||||
|
// Generic type methods
|
||||||
|
{FFlag::LuauUserTypeFunGenerics ? "name" : nullptr, FFlag::LuauUserTypeFunGenerics ? getGenericName : nullptr},
|
||||||
|
{FFlag::LuauUserTypeFunGenerics ? "ispack" : nullptr, FFlag::LuauUserTypeFunGenerics ? getGenericIsPack : nullptr},
|
||||||
|
|
||||||
{nullptr, nullptr}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1776,6 +2119,27 @@ bool areEqual(SeenSet& seen, const TypeFunctionFunctionType& lhs, const TypeFunc
|
||||||
if (seenSetContains(seen, &lhs, &rhs))
|
if (seenSetContains(seen, &lhs, &rhs))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (FFlag::LuauUserTypeFunGenerics)
|
||||||
|
{
|
||||||
|
if (lhs.generics.size() != rhs.generics.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (auto l = lhs.generics.begin(), r = rhs.generics.begin(); l != lhs.generics.end() && r != rhs.generics.end(); ++l, ++r)
|
||||||
|
{
|
||||||
|
if (!areEqual(seen, **l, **r))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lhs.genericPacks.size() != rhs.genericPacks.size())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (auto l = lhs.genericPacks.begin(), r = rhs.genericPacks.begin(); l != lhs.genericPacks.end() && r != rhs.genericPacks.end(); ++l, ++r)
|
||||||
|
{
|
||||||
|
if (!areEqual(seen, **l, **r))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (bool(lhs.argTypes) != bool(rhs.argTypes))
|
if (bool(lhs.argTypes) != bool(rhs.argTypes))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -1876,6 +2240,16 @@ bool areEqual(SeenSet& seen, const TypeFunctionType& lhs, const TypeFunctionType
|
||||||
return areEqual(seen, *lf, *rf);
|
return areEqual(seen, *lf, *rf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauUserTypeFunGenerics)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const TypeFunctionGenericType* lg = get<TypeFunctionGenericType>(&lhs);
|
||||||
|
const TypeFunctionGenericType* rg = get<TypeFunctionGenericType>(&rhs);
|
||||||
|
if (lg && rg)
|
||||||
|
return lg->isNamed == rg->isNamed && lg->isPack == rg->isPack && lg->name == rg->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1922,6 +2296,16 @@ bool areEqual(SeenSet& seen, const TypeFunctionTypePackVar& lhs, const TypeFunct
|
||||||
return areEqual(seen, *lv, *rv);
|
return areEqual(seen, *lv, *rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (FFlag::LuauUserTypeFunGenerics)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
const TypeFunctionGenericTypePack* lg = get<TypeFunctionGenericTypePack>(&lhs);
|
||||||
|
const TypeFunctionGenericTypePack* rg = get<TypeFunctionGenericTypePack>(&rhs);
|
||||||
|
if (lg && rg)
|
||||||
|
return lg->isNamed == rg->isNamed && lg->name == rg->name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2146,10 +2530,14 @@ private:
|
||||||
else if (auto f = get<TypeFunctionFunctionType>(ty))
|
else if (auto f = get<TypeFunctionFunctionType>(ty))
|
||||||
{
|
{
|
||||||
TypeFunctionTypePackId emptyTypePack = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{});
|
TypeFunctionTypePackId emptyTypePack = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{});
|
||||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{emptyTypePack, emptyTypePack});
|
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{{}, {}, emptyTypePack, emptyTypePack});
|
||||||
}
|
}
|
||||||
else if (auto c = get<TypeFunctionClassType>(ty))
|
else if (auto c = get<TypeFunctionClassType>(ty))
|
||||||
target = ty; // Don't copy a class since they are immutable
|
target = ty; // Don't copy a class since they are immutable
|
||||||
|
else if (auto g = get<TypeFunctionGenericType>(ty); FFlag::LuauUserTypeFunGenerics && g)
|
||||||
|
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionGenericType{g->isNamed, g->isPack, g->name});
|
||||||
|
else
|
||||||
|
LUAU_ASSERT(!"Unknown type");
|
||||||
|
|
||||||
types[ty] = target;
|
types[ty] = target;
|
||||||
queue.emplace_back(ty, target);
|
queue.emplace_back(ty, target);
|
||||||
|
@ -2167,6 +2555,10 @@ private:
|
||||||
target = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{{}});
|
target = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{{}});
|
||||||
else if (auto vPack = get<TypeFunctionVariadicTypePack>(tp))
|
else if (auto vPack = get<TypeFunctionVariadicTypePack>(tp))
|
||||||
target = typeFunctionRuntime->typePackArena.allocate(TypeFunctionVariadicTypePack{});
|
target = typeFunctionRuntime->typePackArena.allocate(TypeFunctionVariadicTypePack{});
|
||||||
|
else if (auto gPack = get<TypeFunctionGenericTypePack>(tp); gPack && FFlag::LuauUserTypeFunGenerics)
|
||||||
|
target = typeFunctionRuntime->typePackArena.allocate(TypeFunctionGenericTypePack{gPack->isNamed, gPack->name});
|
||||||
|
else
|
||||||
|
LUAU_ASSERT(!"Unknown type");
|
||||||
|
|
||||||
packs[tp] = target;
|
packs[tp] = target;
|
||||||
queue.emplace_back(tp, target);
|
queue.emplace_back(tp, target);
|
||||||
|
@ -2197,6 +2589,9 @@ private:
|
||||||
cloneChildren(f1, f2);
|
cloneChildren(f1, f2);
|
||||||
else if (auto [c1, c2] = std::tuple{getMutable<TypeFunctionClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
|
else if (auto [c1, c2] = std::tuple{getMutable<TypeFunctionClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
|
||||||
cloneChildren(c1, c2);
|
cloneChildren(c1, c2);
|
||||||
|
else if (auto [g1, g2] = std::tuple{getMutable<TypeFunctionGenericType>(ty), getMutable<TypeFunctionGenericType>(tfti)};
|
||||||
|
FFlag::LuauUserTypeFunGenerics && g1 && g2)
|
||||||
|
cloneChildren(g1, g2);
|
||||||
else
|
else
|
||||||
LUAU_ASSERT(!"Unknown pair?"); // First and argument should always represent the same types
|
LUAU_ASSERT(!"Unknown pair?"); // First and argument should always represent the same types
|
||||||
}
|
}
|
||||||
|
@ -2208,6 +2603,9 @@ private:
|
||||||
else if (auto [vPack1, vPack2] = std::tuple{getMutable<TypeFunctionVariadicTypePack>(tp), getMutable<TypeFunctionVariadicTypePack>(tftp)};
|
else if (auto [vPack1, vPack2] = std::tuple{getMutable<TypeFunctionVariadicTypePack>(tp), getMutable<TypeFunctionVariadicTypePack>(tftp)};
|
||||||
vPack1 && vPack2)
|
vPack1 && vPack2)
|
||||||
cloneChildren(vPack1, vPack2);
|
cloneChildren(vPack1, vPack2);
|
||||||
|
else if (auto [gPack1, gPack2] = std::tuple{getMutable<TypeFunctionGenericTypePack>(tp), getMutable<TypeFunctionGenericTypePack>(tftp)};
|
||||||
|
FFlag::LuauUserTypeFunGenerics && gPack1 && gPack2)
|
||||||
|
cloneChildren(gPack1, gPack2);
|
||||||
else
|
else
|
||||||
LUAU_ASSERT(!"Unknown pair?"); // First and argument should always represent the same types
|
LUAU_ASSERT(!"Unknown pair?"); // First and argument should always represent the same types
|
||||||
}
|
}
|
||||||
|
@ -2288,6 +2686,17 @@ private:
|
||||||
|
|
||||||
void cloneChildren(TypeFunctionFunctionType* f1, TypeFunctionFunctionType* f2)
|
void cloneChildren(TypeFunctionFunctionType* f1, TypeFunctionFunctionType* f2)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauUserTypeFunGenerics)
|
||||||
|
{
|
||||||
|
f2->generics.reserve(f1->generics.size());
|
||||||
|
for (auto ty : f1->generics)
|
||||||
|
f2->generics.push_back(shallowClone(ty));
|
||||||
|
|
||||||
|
f2->genericPacks.reserve(f1->genericPacks.size());
|
||||||
|
for (auto tp : f1->genericPacks)
|
||||||
|
f2->genericPacks.push_back(shallowClone(tp));
|
||||||
|
}
|
||||||
|
|
||||||
f2->argTypes = shallowClone(f1->argTypes);
|
f2->argTypes = shallowClone(f1->argTypes);
|
||||||
f2->retTypes = shallowClone(f1->retTypes);
|
f2->retTypes = shallowClone(f1->retTypes);
|
||||||
}
|
}
|
||||||
|
@ -2297,16 +2706,32 @@ private:
|
||||||
// noop.
|
// noop.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cloneChildren(TypeFunctionGenericType* g1, TypeFunctionGenericType* g2)
|
||||||
|
{
|
||||||
|
// noop.
|
||||||
|
}
|
||||||
|
|
||||||
void cloneChildren(TypeFunctionTypePack* t1, TypeFunctionTypePack* t2)
|
void cloneChildren(TypeFunctionTypePack* t1, TypeFunctionTypePack* t2)
|
||||||
{
|
{
|
||||||
for (TypeFunctionTypeId& ty : t1->head)
|
for (TypeFunctionTypeId& ty : t1->head)
|
||||||
t2->head.push_back(shallowClone(ty));
|
t2->head.push_back(shallowClone(ty));
|
||||||
|
|
||||||
|
if (FFlag::LuauUserTypeFunCloneTail)
|
||||||
|
{
|
||||||
|
if (t1->tail)
|
||||||
|
t2->tail = shallowClone(*t1->tail);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void cloneChildren(TypeFunctionVariadicTypePack* v1, TypeFunctionVariadicTypePack* v2)
|
void cloneChildren(TypeFunctionVariadicTypePack* v1, TypeFunctionVariadicTypePack* v2)
|
||||||
{
|
{
|
||||||
v2->type = shallowClone(v1->type);
|
v2->type = shallowClone(v1->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cloneChildren(TypeFunctionGenericTypePack* g1, TypeFunctionGenericTypePack* g2)
|
||||||
|
{
|
||||||
|
// noop.
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeFunctionTypeId deepClone(NotNull<TypeFunctionRuntime> runtime, TypeFunctionTypeId ty)
|
TypeFunctionTypeId deepClone(NotNull<TypeFunctionRuntime> runtime, TypeFunctionTypeId ty)
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000);
|
LUAU_DYNAMIC_FASTINTVARIABLE(LuauTypeFunctionSerdeIterationLimit, 100'000);
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunThreadBuffer)
|
LUAU_FASTFLAG(LuauUserTypeFunThreadBuffer)
|
||||||
|
LUAU_FASTFLAG(LuauUserTypeFunGenerics)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -221,13 +222,22 @@ private:
|
||||||
else if (auto f = get<FunctionType>(ty))
|
else if (auto f = get<FunctionType>(ty))
|
||||||
{
|
{
|
||||||
TypeFunctionTypePackId emptyTypePack = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{});
|
TypeFunctionTypePackId emptyTypePack = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{});
|
||||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{emptyTypePack, emptyTypePack});
|
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionFunctionType{{}, {}, emptyTypePack, emptyTypePack});
|
||||||
}
|
}
|
||||||
else if (auto c = get<ClassType>(ty))
|
else if (auto c = get<ClassType>(ty))
|
||||||
{
|
{
|
||||||
state->classesSerialized[c->name] = ty;
|
state->classesSerialized[c->name] = ty;
|
||||||
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, c->name});
|
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionClassType{{}, std::nullopt, std::nullopt, std::nullopt, c->name});
|
||||||
}
|
}
|
||||||
|
else if (auto g = get<GenericType>(ty); FFlag::LuauUserTypeFunGenerics && g)
|
||||||
|
{
|
||||||
|
Name name = g->name;
|
||||||
|
|
||||||
|
if (!g->explicitName)
|
||||||
|
name = format("g%d", g->index);
|
||||||
|
|
||||||
|
target = typeFunctionRuntime->typeArena.allocate(TypeFunctionGenericType{g->explicitName, false, name});
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::string error = format("Argument of type %s is not currently serializable by type functions", toString(ty).c_str());
|
std::string error = format("Argument of type %s is not currently serializable by type functions", toString(ty).c_str());
|
||||||
|
@ -252,6 +262,15 @@ private:
|
||||||
target = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{{}});
|
target = typeFunctionRuntime->typePackArena.allocate(TypeFunctionTypePack{{}});
|
||||||
else if (auto vPack = get<VariadicTypePack>(tp))
|
else if (auto vPack = get<VariadicTypePack>(tp))
|
||||||
target = typeFunctionRuntime->typePackArena.allocate(TypeFunctionVariadicTypePack{});
|
target = typeFunctionRuntime->typePackArena.allocate(TypeFunctionVariadicTypePack{});
|
||||||
|
else if (auto gPack = get<GenericTypePack>(tp); FFlag::LuauUserTypeFunGenerics && gPack)
|
||||||
|
{
|
||||||
|
Name name = gPack->name;
|
||||||
|
|
||||||
|
if (!gPack->explicitName)
|
||||||
|
name = format("g%d", gPack->index);
|
||||||
|
|
||||||
|
target = typeFunctionRuntime->typePackArena.allocate(TypeFunctionGenericTypePack{gPack->explicitName, name});
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::string error = format("Argument of type pack %s is not currently serializable by type functions", toString(tp).c_str());
|
std::string error = format("Argument of type pack %s is not currently serializable by type functions", toString(tp).c_str());
|
||||||
|
@ -289,6 +308,9 @@ private:
|
||||||
serializeChildren(f1, f2);
|
serializeChildren(f1, f2);
|
||||||
else if (auto [c1, c2] = std::tuple{get<ClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
|
else if (auto [c1, c2] = std::tuple{get<ClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
|
||||||
serializeChildren(c1, c2);
|
serializeChildren(c1, c2);
|
||||||
|
else if (auto [g1, g2] = std::tuple{get<GenericType>(ty), getMutable<TypeFunctionGenericType>(tfti)};
|
||||||
|
FFlag::LuauUserTypeFunGenerics && g1 && g2)
|
||||||
|
serializeChildren(g1, g2);
|
||||||
else
|
else
|
||||||
{ // Either this or ty and tfti do not represent the same type
|
{ // Either this or ty and tfti do not represent the same type
|
||||||
std::string error = format("Argument of type %s is not currently serializable by type functions", toString(ty).c_str());
|
std::string error = format("Argument of type %s is not currently serializable by type functions", toString(ty).c_str());
|
||||||
|
@ -302,6 +324,9 @@ private:
|
||||||
serializeChildren(tPack1, tPack2);
|
serializeChildren(tPack1, tPack2);
|
||||||
else if (auto [vPack1, vPack2] = std::tuple{get<VariadicTypePack>(tp), getMutable<TypeFunctionVariadicTypePack>(tftp)}; vPack1 && vPack2)
|
else if (auto [vPack1, vPack2] = std::tuple{get<VariadicTypePack>(tp), getMutable<TypeFunctionVariadicTypePack>(tftp)}; vPack1 && vPack2)
|
||||||
serializeChildren(vPack1, vPack2);
|
serializeChildren(vPack1, vPack2);
|
||||||
|
else if (auto [gPack1, gPack2] = std::tuple{get<GenericTypePack>(tp), getMutable<TypeFunctionGenericTypePack>(tftp)};
|
||||||
|
FFlag::LuauUserTypeFunGenerics && gPack1 && gPack2)
|
||||||
|
serializeChildren(gPack1, gPack2);
|
||||||
else
|
else
|
||||||
{ // Either this or ty and tfti do not represent the same type
|
{ // Either this or ty and tfti do not represent the same type
|
||||||
std::string error = format("Argument of type pack %s is not currently serializable by type functions", toString(tp).c_str());
|
std::string error = format("Argument of type pack %s is not currently serializable by type functions", toString(tp).c_str());
|
||||||
|
@ -391,6 +416,17 @@ private:
|
||||||
|
|
||||||
void serializeChildren(const FunctionType* f1, TypeFunctionFunctionType* f2)
|
void serializeChildren(const FunctionType* f1, TypeFunctionFunctionType* f2)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauUserTypeFunGenerics)
|
||||||
|
{
|
||||||
|
f2->generics.reserve(f1->generics.size());
|
||||||
|
for (auto ty : f1->generics)
|
||||||
|
f2->generics.push_back(shallowSerialize(ty));
|
||||||
|
|
||||||
|
f2->genericPacks.reserve(f1->genericPacks.size());
|
||||||
|
for (auto tp : f1->genericPacks)
|
||||||
|
f2->genericPacks.push_back(shallowSerialize(tp));
|
||||||
|
}
|
||||||
|
|
||||||
f2->argTypes = shallowSerialize(f1->argTypes);
|
f2->argTypes = shallowSerialize(f1->argTypes);
|
||||||
f2->retTypes = shallowSerialize(f1->retTypes);
|
f2->retTypes = shallowSerialize(f1->retTypes);
|
||||||
}
|
}
|
||||||
|
@ -420,6 +456,11 @@ private:
|
||||||
c2->parent = shallowSerialize(*c1->parent);
|
c2->parent = shallowSerialize(*c1->parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void serializeChildren(const GenericType* g1, TypeFunctionGenericType* g2)
|
||||||
|
{
|
||||||
|
// noop.
|
||||||
|
}
|
||||||
|
|
||||||
void serializeChildren(const TypePack* t1, TypeFunctionTypePack* t2)
|
void serializeChildren(const TypePack* t1, TypeFunctionTypePack* t2)
|
||||||
{
|
{
|
||||||
for (const TypeId& ty : t1->head)
|
for (const TypeId& ty : t1->head)
|
||||||
|
@ -433,6 +474,25 @@ private:
|
||||||
{
|
{
|
||||||
v2->type = shallowSerialize(v1->ty);
|
v2->type = shallowSerialize(v1->ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void serializeChildren(const GenericTypePack* v1, TypeFunctionGenericTypePack* v2)
|
||||||
|
{
|
||||||
|
// noop.
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename T>
|
||||||
|
struct SerializedGeneric
|
||||||
|
{
|
||||||
|
bool isNamed = false;
|
||||||
|
std::string name;
|
||||||
|
T type = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SerializedFunctionScope
|
||||||
|
{
|
||||||
|
size_t oldQueueSize = 0;
|
||||||
|
TypeFunctionFunctionType* function = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Complete inverse of TypeFunctionSerializer
|
// Complete inverse of TypeFunctionSerializer
|
||||||
|
@ -453,6 +513,15 @@ class TypeFunctionDeserializer
|
||||||
// second must be PrimitiveType; else there should be an error
|
// second must be PrimitiveType; else there should be an error
|
||||||
std::vector<std::tuple<TypeFunctionKind, Kind>> queue;
|
std::vector<std::tuple<TypeFunctionKind, Kind>> queue;
|
||||||
|
|
||||||
|
// Generic types and packs currently in scope
|
||||||
|
// Generics are resolved by name even if runtime generic type pointers are different
|
||||||
|
// Multiple names mapping to the same generic can be in scope for nested generic functions
|
||||||
|
std::vector<SerializedGeneric<TypeId>> genericTypes;
|
||||||
|
std::vector<SerializedGeneric<TypePackId>> genericPacks;
|
||||||
|
|
||||||
|
// To track when generics go out of scope, we have a list of queue positions at which a specific function has introduced generics
|
||||||
|
std::vector<SerializedFunctionScope> functionScopes;
|
||||||
|
|
||||||
SeenTypes types; // Mapping of TypeFunctionTypeIds that have been shallow deserialized to TypeIds
|
SeenTypes types; // Mapping of TypeFunctionTypeIds that have been shallow deserialized to TypeIds
|
||||||
SeenTypePacks packs; // Mapping of TypeFunctionTypePackIds that have been shallow deserialized to TypePackIds
|
SeenTypePacks packs; // Mapping of TypeFunctionTypePackIds that have been shallow deserialized to TypePackIds
|
||||||
|
|
||||||
|
@ -520,6 +589,16 @@ private:
|
||||||
queue.pop_back();
|
queue.pop_back();
|
||||||
|
|
||||||
deserializeChildren(tfti, ty);
|
deserializeChildren(tfti, ty);
|
||||||
|
|
||||||
|
if (FFlag::LuauUserTypeFunGenerics)
|
||||||
|
{
|
||||||
|
// If we have completed working on all children of a function, remove the generic parameters from scope
|
||||||
|
if (!functionScopes.empty() && queue.size() == functionScopes.back().oldQueueSize && state->errors.empty())
|
||||||
|
{
|
||||||
|
closeFunctionScope(functionScopes.back().function);
|
||||||
|
functionScopes.pop_back();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -552,6 +631,21 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void closeFunctionScope(TypeFunctionFunctionType* f)
|
||||||
|
{
|
||||||
|
if (!f->generics.empty())
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(genericTypes.size() >= f->generics.size());
|
||||||
|
genericTypes.erase(genericTypes.begin() + int(genericTypes.size() - f->generics.size()), genericTypes.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!f->genericPacks.empty())
|
||||||
|
{
|
||||||
|
LUAU_ASSERT(genericPacks.size() >= f->genericPacks.size());
|
||||||
|
genericPacks.erase(genericPacks.begin() + int(genericPacks.size() - f->genericPacks.size()), genericPacks.end());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
TypeId shallowDeserialize(TypeFunctionTypeId ty)
|
TypeId shallowDeserialize(TypeFunctionTypeId ty)
|
||||||
{
|
{
|
||||||
if (auto it = find(ty))
|
if (auto it = find(ty))
|
||||||
|
@ -631,6 +725,33 @@ private:
|
||||||
else
|
else
|
||||||
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious class type is being deserialized");
|
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious class type is being deserialized");
|
||||||
}
|
}
|
||||||
|
else if (auto g = get<TypeFunctionGenericType>(ty); FFlag::LuauUserTypeFunGenerics && g)
|
||||||
|
{
|
||||||
|
if (g->isPack)
|
||||||
|
{
|
||||||
|
state->errors.push_back(format("Generic type pack '%s...' cannot be placed in a type position", g->name.c_str()));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto it = std::find_if(
|
||||||
|
genericTypes.rbegin(),
|
||||||
|
genericTypes.rend(),
|
||||||
|
[&](const SerializedGeneric<TypeId>& el)
|
||||||
|
{
|
||||||
|
return g->isNamed == el.isNamed && g->name == el.name;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (it == genericTypes.rend())
|
||||||
|
{
|
||||||
|
state->errors.push_back(format("Generic type '%s' is not in a scope of the active generic function", g->name.c_str()));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
target = it->type;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious type is being deserialized");
|
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious type is being deserialized");
|
||||||
|
|
||||||
|
@ -647,11 +768,36 @@ private:
|
||||||
// Create a shallow deserialization
|
// Create a shallow deserialization
|
||||||
TypePackId target = {};
|
TypePackId target = {};
|
||||||
if (auto tPack = get<TypeFunctionTypePack>(tp))
|
if (auto tPack = get<TypeFunctionTypePack>(tp))
|
||||||
|
{
|
||||||
target = state->ctx->arena->addTypePack(TypePack{});
|
target = state->ctx->arena->addTypePack(TypePack{});
|
||||||
|
}
|
||||||
else if (auto vPack = get<TypeFunctionVariadicTypePack>(tp))
|
else if (auto vPack = get<TypeFunctionVariadicTypePack>(tp))
|
||||||
|
{
|
||||||
target = state->ctx->arena->addTypePack(VariadicTypePack{});
|
target = state->ctx->arena->addTypePack(VariadicTypePack{});
|
||||||
|
}
|
||||||
|
else if (auto gPack = get<TypeFunctionGenericTypePack>(tp); FFlag::LuauUserTypeFunGenerics && gPack)
|
||||||
|
{
|
||||||
|
auto it = std::find_if(
|
||||||
|
genericPacks.rbegin(),
|
||||||
|
genericPacks.rend(),
|
||||||
|
[&](const SerializedGeneric<TypePackId>& el)
|
||||||
|
{
|
||||||
|
return gPack->isNamed == el.isNamed && gPack->name == el.name;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (it == genericPacks.rend())
|
||||||
|
{
|
||||||
|
state->errors.push_back(format("Generic type pack '%s...' is not in a scope of the active generic function", gPack->name.c_str()));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
target = it->type;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious type is being deserialized");
|
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious type is being deserialized");
|
||||||
|
}
|
||||||
|
|
||||||
packs[tp] = target;
|
packs[tp] = target;
|
||||||
queue.emplace_back(tp, target);
|
queue.emplace_back(tp, target);
|
||||||
|
@ -686,6 +832,9 @@ private:
|
||||||
deserializeChildren(f2, f1);
|
deserializeChildren(f2, f1);
|
||||||
else if (auto [c1, c2] = std::tuple{getMutable<ClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
|
else if (auto [c1, c2] = std::tuple{getMutable<ClassType>(ty), getMutable<TypeFunctionClassType>(tfti)}; c1 && c2)
|
||||||
deserializeChildren(c2, c1);
|
deserializeChildren(c2, c1);
|
||||||
|
else if (auto [g1, g2] = std::tuple{getMutable<GenericType>(ty), getMutable<TypeFunctionGenericType>(tfti)};
|
||||||
|
FFlag::LuauUserTypeFunGenerics && g1 && g2)
|
||||||
|
deserializeChildren(g2, g1);
|
||||||
else
|
else
|
||||||
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious type is being deserialized");
|
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious type is being deserialized");
|
||||||
}
|
}
|
||||||
|
@ -697,6 +846,9 @@ private:
|
||||||
else if (auto [vPack1, vPack2] = std::tuple{getMutable<VariadicTypePack>(tp), getMutable<TypeFunctionVariadicTypePack>(tftp)};
|
else if (auto [vPack1, vPack2] = std::tuple{getMutable<VariadicTypePack>(tp), getMutable<TypeFunctionVariadicTypePack>(tftp)};
|
||||||
vPack1 && vPack2)
|
vPack1 && vPack2)
|
||||||
deserializeChildren(vPack2, vPack1);
|
deserializeChildren(vPack2, vPack1);
|
||||||
|
else if (auto [gPack1, gPack2] = std::tuple{getMutable<GenericTypePack>(tp), getMutable<TypeFunctionGenericTypePack>(tftp)};
|
||||||
|
FFlag::LuauUserTypeFunGenerics && gPack1 && gPack2)
|
||||||
|
deserializeChildren(gPack2, gPack1);
|
||||||
else
|
else
|
||||||
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious type is being deserialized");
|
state->ctx->ice->ice("Deserializing user defined type function arguments: mysterious type is being deserialized");
|
||||||
}
|
}
|
||||||
|
@ -780,6 +932,64 @@ private:
|
||||||
|
|
||||||
void deserializeChildren(TypeFunctionFunctionType* f2, FunctionType* f1)
|
void deserializeChildren(TypeFunctionFunctionType* f2, FunctionType* f1)
|
||||||
{
|
{
|
||||||
|
if (FFlag::LuauUserTypeFunGenerics)
|
||||||
|
{
|
||||||
|
functionScopes.push_back({queue.size(), f2});
|
||||||
|
|
||||||
|
std::set<std::pair<bool, std::string>> genericNames;
|
||||||
|
|
||||||
|
// Introduce generic function parameters into scope
|
||||||
|
for (auto ty : f2->generics)
|
||||||
|
{
|
||||||
|
auto gty = get<TypeFunctionGenericType>(ty);
|
||||||
|
LUAU_ASSERT(gty && !gty->isPack);
|
||||||
|
|
||||||
|
std::pair<bool, std::string> nameKey = std::make_pair(gty->isNamed, gty->name);
|
||||||
|
|
||||||
|
// Duplicates are not allowed
|
||||||
|
if (genericNames.find(nameKey) != genericNames.end())
|
||||||
|
{
|
||||||
|
state->errors.push_back(format("Duplicate type parameter '%s'", gty->name.c_str()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
genericNames.insert(nameKey);
|
||||||
|
|
||||||
|
TypeId mapping = state->ctx->arena->addTV(Type(gty->isNamed ? GenericType{state->ctx->scope.get(), gty->name} : GenericType{}));
|
||||||
|
genericTypes.push_back({gty->isNamed, gty->name, mapping});
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto tp : f2->genericPacks)
|
||||||
|
{
|
||||||
|
auto gtp = get<TypeFunctionGenericTypePack>(tp);
|
||||||
|
LUAU_ASSERT(gtp);
|
||||||
|
|
||||||
|
std::pair<bool, std::string> nameKey = std::make_pair(gtp->isNamed, gtp->name);
|
||||||
|
|
||||||
|
// Duplicates are not allowed
|
||||||
|
if (genericNames.find(nameKey) != genericNames.end())
|
||||||
|
{
|
||||||
|
state->errors.push_back(format("Duplicate type parameter '%s'", gtp->name.c_str()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
genericNames.insert(nameKey);
|
||||||
|
|
||||||
|
TypePackId mapping =
|
||||||
|
state->ctx->arena->addTypePack(TypePackVar(gtp->isNamed ? GenericTypePack{state->ctx->scope.get(), gtp->name} : GenericTypePack{})
|
||||||
|
);
|
||||||
|
genericPacks.push_back({gtp->isNamed, gtp->name, mapping});
|
||||||
|
}
|
||||||
|
|
||||||
|
f1->generics.reserve(f2->generics.size());
|
||||||
|
for (auto ty : f2->generics)
|
||||||
|
f1->generics.push_back(shallowDeserialize(ty));
|
||||||
|
|
||||||
|
f1->genericPacks.reserve(f2->genericPacks.size());
|
||||||
|
for (auto tp : f2->genericPacks)
|
||||||
|
f1->genericPacks.push_back(shallowDeserialize(tp));
|
||||||
|
}
|
||||||
|
|
||||||
if (f2->argTypes)
|
if (f2->argTypes)
|
||||||
f1->argTypes = shallowDeserialize(f2->argTypes);
|
f1->argTypes = shallowDeserialize(f2->argTypes);
|
||||||
|
|
||||||
|
@ -792,6 +1002,11 @@ private:
|
||||||
// noop.
|
// noop.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deserializeChildren(TypeFunctionGenericType* g2, GenericType* g1)
|
||||||
|
{
|
||||||
|
// noop.
|
||||||
|
}
|
||||||
|
|
||||||
void deserializeChildren(TypeFunctionTypePack* t2, TypePack* t1)
|
void deserializeChildren(TypeFunctionTypePack* t2, TypePack* t1)
|
||||||
{
|
{
|
||||||
for (TypeFunctionTypeId& ty : t2->head)
|
for (TypeFunctionTypeId& ty : t2->head)
|
||||||
|
@ -805,6 +1020,11 @@ private:
|
||||||
{
|
{
|
||||||
v1->ty = shallowDeserialize(v2->type);
|
v1->ty = shallowDeserialize(v2->type);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void deserializeChildren(TypeFunctionGenericTypePack* v2, GenericTypePack* v1)
|
||||||
|
{
|
||||||
|
// noop.
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TypeFunctionTypeId serialize(TypeId ty, TypeFunctionRuntimeBuilderState* state)
|
TypeFunctionTypeId serialize(TypeId ty, TypeFunctionRuntimeBuilderState* state)
|
||||||
|
|
|
@ -116,7 +116,7 @@ private:
|
||||||
AstStat* parseFor();
|
AstStat* parseFor();
|
||||||
|
|
||||||
// funcname ::= Name {`.' Name} [`:' Name]
|
// funcname ::= Name {`.' Name} [`:' Name]
|
||||||
AstExpr* parseFunctionName(Location start, bool& hasself, AstName& debugname);
|
AstExpr* parseFunctionName(Location start_DEPRECATED, bool& hasself, AstName& debugname);
|
||||||
|
|
||||||
// function funcname funcbody
|
// function funcname funcbody
|
||||||
LUAU_FORCEINLINE AstStat* parseFunctionStat(const AstArray<AstAttr*>& attributes = {nullptr, 0});
|
LUAU_FORCEINLINE AstStat* parseFunctionStat(const AstArray<AstAttr*>& attributes = {nullptr, 0});
|
||||||
|
|
|
@ -22,6 +22,7 @@ LUAU_FASTFLAGVARIABLE(LuauAllowFragmentParsing)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauAllowComplexTypesInGenericParams)
|
LUAU_FASTFLAGVARIABLE(LuauAllowComplexTypesInGenericParams)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForTableTypes)
|
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForTableTypes)
|
||||||
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForClassNames)
|
LUAU_FASTFLAGVARIABLE(LuauErrorRecoveryForClassNames)
|
||||||
|
LUAU_FASTFLAGVARIABLE(LuauFixFunctionNameStartPosition)
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
|
@ -638,7 +639,7 @@ AstStat* Parser::parseFor()
|
||||||
}
|
}
|
||||||
|
|
||||||
// funcname ::= Name {`.' Name} [`:' Name]
|
// funcname ::= Name {`.' Name} [`:' Name]
|
||||||
AstExpr* Parser::parseFunctionName(Location start, bool& hasself, AstName& debugname)
|
AstExpr* Parser::parseFunctionName(Location start_DEPRECATED, bool& hasself, AstName& debugname)
|
||||||
{
|
{
|
||||||
if (lexer.current().type == Lexeme::Name)
|
if (lexer.current().type == Lexeme::Name)
|
||||||
debugname = AstName(lexer.current().name);
|
debugname = AstName(lexer.current().name);
|
||||||
|
@ -658,7 +659,9 @@ AstExpr* Parser::parseFunctionName(Location start, bool& hasself, AstName& debug
|
||||||
// while we could concatenate the name chain, for now let's just write the short name
|
// while we could concatenate the name chain, for now let's just write the short name
|
||||||
debugname = name.name;
|
debugname = name.name;
|
||||||
|
|
||||||
expr = allocator.alloc<AstExprIndexName>(Location(start, name.location), expr, name.name, name.location, opPosition, '.');
|
expr = allocator.alloc<AstExprIndexName>(
|
||||||
|
Location(FFlag::LuauFixFunctionNameStartPosition ? expr->location : start_DEPRECATED, name.location), expr, name.name, name.location, opPosition, '.'
|
||||||
|
);
|
||||||
|
|
||||||
// note: while the parser isn't recursive here, we're generating recursive structures of unbounded depth
|
// note: while the parser isn't recursive here, we're generating recursive structures of unbounded depth
|
||||||
incrementRecursionCounter("function name");
|
incrementRecursionCounter("function name");
|
||||||
|
@ -677,7 +680,9 @@ AstExpr* Parser::parseFunctionName(Location start, bool& hasself, AstName& debug
|
||||||
// while we could concatenate the name chain, for now let's just write the short name
|
// while we could concatenate the name chain, for now let's just write the short name
|
||||||
debugname = name.name;
|
debugname = name.name;
|
||||||
|
|
||||||
expr = allocator.alloc<AstExprIndexName>(Location(start, name.location), expr, name.name, name.location, opPosition, ':');
|
expr = allocator.alloc<AstExprIndexName>(
|
||||||
|
Location(FFlag::LuauFixFunctionNameStartPosition ? expr->location : start_DEPRECATED, name.location), expr, name.name, name.location, opPosition, ':'
|
||||||
|
);
|
||||||
|
|
||||||
hasself = true;
|
hasself = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Luau/CodeGen.h"
|
#include "Luau/CodeGenOptions.h"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <algorithm>
|
#include "Luau/CodeGenCommon.h"
|
||||||
|
#include "Luau/CodeGenOptions.h"
|
||||||
|
#include "Luau/LoweringStats.h"
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
@ -12,25 +15,11 @@
|
||||||
|
|
||||||
struct lua_State;
|
struct lua_State;
|
||||||
|
|
||||||
#if defined(__x86_64__) || defined(_M_X64)
|
|
||||||
#define CODEGEN_TARGET_X64
|
|
||||||
#elif defined(__aarch64__) || defined(_M_ARM64)
|
|
||||||
#define CODEGEN_TARGET_A64
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Luau
|
namespace Luau
|
||||||
{
|
{
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
{
|
{
|
||||||
|
|
||||||
enum CodeGenFlags
|
|
||||||
{
|
|
||||||
// Only run native codegen for modules that have been marked with --!native
|
|
||||||
CodeGen_OnlyNativeModules = 1 << 0,
|
|
||||||
// Run native codegen for functions that the compiler considers not profitable
|
|
||||||
CodeGen_ColdFunctions = 1 << 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
// These enum values can be reported through telemetry.
|
// These enum values can be reported through telemetry.
|
||||||
// To ensure consistency, changes should be additive.
|
// To ensure consistency, changes should be additive.
|
||||||
enum class CodeGenCompilationResult
|
enum class CodeGenCompilationResult
|
||||||
|
@ -72,106 +61,6 @@ struct CompilationResult
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct IrBuilder;
|
|
||||||
struct IrOp;
|
|
||||||
|
|
||||||
using HostVectorOperationBytecodeType = uint8_t (*)(const char* member, size_t memberLength);
|
|
||||||
using HostVectorAccessHandler = bool (*)(IrBuilder& builder, const char* member, size_t memberLength, int resultReg, int sourceReg, int pcpos);
|
|
||||||
using HostVectorNamecallHandler =
|
|
||||||
bool (*)(IrBuilder& builder, const char* member, size_t memberLength, int argResReg, int sourceReg, int params, int results, int pcpos);
|
|
||||||
|
|
||||||
enum class HostMetamethod
|
|
||||||
{
|
|
||||||
Add,
|
|
||||||
Sub,
|
|
||||||
Mul,
|
|
||||||
Div,
|
|
||||||
Idiv,
|
|
||||||
Mod,
|
|
||||||
Pow,
|
|
||||||
Minus,
|
|
||||||
Equal,
|
|
||||||
LessThan,
|
|
||||||
LessEqual,
|
|
||||||
Length,
|
|
||||||
Concat,
|
|
||||||
};
|
|
||||||
|
|
||||||
using HostUserdataOperationBytecodeType = uint8_t (*)(uint8_t type, const char* member, size_t memberLength);
|
|
||||||
using HostUserdataMetamethodBytecodeType = uint8_t (*)(uint8_t lhsTy, uint8_t rhsTy, HostMetamethod method);
|
|
||||||
using HostUserdataAccessHandler =
|
|
||||||
bool (*)(IrBuilder& builder, uint8_t type, const char* member, size_t memberLength, int resultReg, int sourceReg, int pcpos);
|
|
||||||
using HostUserdataMetamethodHandler =
|
|
||||||
bool (*)(IrBuilder& builder, uint8_t lhsTy, uint8_t rhsTy, int resultReg, IrOp lhs, IrOp rhs, HostMetamethod method, int pcpos);
|
|
||||||
using HostUserdataNamecallHandler = bool (*)(
|
|
||||||
IrBuilder& builder,
|
|
||||||
uint8_t type,
|
|
||||||
const char* member,
|
|
||||||
size_t memberLength,
|
|
||||||
int argResReg,
|
|
||||||
int sourceReg,
|
|
||||||
int params,
|
|
||||||
int results,
|
|
||||||
int pcpos
|
|
||||||
);
|
|
||||||
|
|
||||||
struct HostIrHooks
|
|
||||||
{
|
|
||||||
// Suggest result type of a vector field access
|
|
||||||
HostVectorOperationBytecodeType vectorAccessBytecodeType = nullptr;
|
|
||||||
|
|
||||||
// Suggest result type of a vector function namecall
|
|
||||||
HostVectorOperationBytecodeType vectorNamecallBytecodeType = nullptr;
|
|
||||||
|
|
||||||
// Handle vector value field access
|
|
||||||
// 'sourceReg' is guaranteed to be a vector
|
|
||||||
// Guards should take a VM exit to 'pcpos'
|
|
||||||
HostVectorAccessHandler vectorAccess = nullptr;
|
|
||||||
|
|
||||||
// Handle namecall performed on a vector value
|
|
||||||
// 'sourceReg' (self argument) is guaranteed to be a vector
|
|
||||||
// All other arguments can be of any type
|
|
||||||
// Guards should take a VM exit to 'pcpos'
|
|
||||||
HostVectorNamecallHandler vectorNamecall = nullptr;
|
|
||||||
|
|
||||||
// Suggest result type of a userdata field access
|
|
||||||
HostUserdataOperationBytecodeType userdataAccessBytecodeType = nullptr;
|
|
||||||
|
|
||||||
// Suggest result type of a metamethod call
|
|
||||||
HostUserdataMetamethodBytecodeType userdataMetamethodBytecodeType = nullptr;
|
|
||||||
|
|
||||||
// Suggest result type of a userdata namecall
|
|
||||||
HostUserdataOperationBytecodeType userdataNamecallBytecodeType = nullptr;
|
|
||||||
|
|
||||||
// Handle userdata value field access
|
|
||||||
// 'sourceReg' is guaranteed to be a userdata, but tag has to be checked
|
|
||||||
// Write to 'resultReg' might invalidate 'sourceReg'
|
|
||||||
// Guards should take a VM exit to 'pcpos'
|
|
||||||
HostUserdataAccessHandler userdataAccess = nullptr;
|
|
||||||
|
|
||||||
// Handle metamethod operation on a userdata value
|
|
||||||
// 'lhs' and 'rhs' operands can be VM registers of constants
|
|
||||||
// Operand types have to be checked and userdata operand tags have to be checked
|
|
||||||
// Write to 'resultReg' might invalidate source operands
|
|
||||||
// Guards should take a VM exit to 'pcpos'
|
|
||||||
HostUserdataMetamethodHandler userdataMetamethod = nullptr;
|
|
||||||
|
|
||||||
// Handle namecall performed on a userdata value
|
|
||||||
// 'sourceReg' (self argument) is guaranteed to be a userdata, but tag has to be checked
|
|
||||||
// All other arguments can be of any type
|
|
||||||
// Guards should take a VM exit to 'pcpos'
|
|
||||||
HostUserdataNamecallHandler userdataNamecall = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CompilationOptions
|
|
||||||
{
|
|
||||||
unsigned int flags = 0;
|
|
||||||
HostIrHooks hooks;
|
|
||||||
|
|
||||||
// null-terminated array of userdata types names that might have custom lowering
|
|
||||||
const char* const* userdataTypes = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CompilationStats
|
struct CompilationStats
|
||||||
{
|
{
|
||||||
size_t bytecodeSizeBytes = 0;
|
size_t bytecodeSizeBytes = 0;
|
||||||
|
@ -184,8 +73,6 @@ struct CompilationStats
|
||||||
uint32_t functionsBound = 0;
|
uint32_t functionsBound = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
using AllocationCallback = void(void* context, void* oldPointer, size_t oldSize, void* newPointer, size_t newSize);
|
|
||||||
|
|
||||||
bool isSupported();
|
bool isSupported();
|
||||||
|
|
||||||
class SharedCodeGenContext;
|
class SharedCodeGenContext;
|
||||||
|
@ -249,153 +136,6 @@ CompilationResult compile(const ModuleId& moduleId, lua_State* L, int idx, unsig
|
||||||
CompilationResult compile(lua_State* L, int idx, const CompilationOptions& options, CompilationStats* stats = nullptr);
|
CompilationResult compile(lua_State* L, int idx, const CompilationOptions& options, CompilationStats* stats = nullptr);
|
||||||
CompilationResult compile(const ModuleId& moduleId, lua_State* L, int idx, const CompilationOptions& options, CompilationStats* stats = nullptr);
|
CompilationResult compile(const ModuleId& moduleId, lua_State* L, int idx, const CompilationOptions& options, CompilationStats* stats = nullptr);
|
||||||
|
|
||||||
using AnnotatorFn = void (*)(void* context, std::string& result, int fid, int instpos);
|
|
||||||
|
|
||||||
// Output "#" before IR blocks and instructions
|
|
||||||
enum class IncludeIrPrefix
|
|
||||||
{
|
|
||||||
No,
|
|
||||||
Yes
|
|
||||||
};
|
|
||||||
|
|
||||||
// Output user count and last use information of blocks and instructions
|
|
||||||
enum class IncludeUseInfo
|
|
||||||
{
|
|
||||||
No,
|
|
||||||
Yes
|
|
||||||
};
|
|
||||||
|
|
||||||
// Output CFG informations like block predecessors, successors and etc
|
|
||||||
enum class IncludeCfgInfo
|
|
||||||
{
|
|
||||||
No,
|
|
||||||
Yes
|
|
||||||
};
|
|
||||||
|
|
||||||
// Output VM register live in/out information for blocks
|
|
||||||
enum class IncludeRegFlowInfo
|
|
||||||
{
|
|
||||||
No,
|
|
||||||
Yes
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AssemblyOptions
|
|
||||||
{
|
|
||||||
enum Target
|
|
||||||
{
|
|
||||||
Host,
|
|
||||||
A64,
|
|
||||||
A64_NoFeatures,
|
|
||||||
X64_Windows,
|
|
||||||
X64_SystemV,
|
|
||||||
};
|
|
||||||
|
|
||||||
Target target = Host;
|
|
||||||
|
|
||||||
CompilationOptions compilationOptions;
|
|
||||||
|
|
||||||
bool outputBinary = false;
|
|
||||||
|
|
||||||
bool includeAssembly = false;
|
|
||||||
bool includeIr = false;
|
|
||||||
bool includeOutlinedCode = false;
|
|
||||||
bool includeIrTypes = false;
|
|
||||||
|
|
||||||
IncludeIrPrefix includeIrPrefix = IncludeIrPrefix::Yes;
|
|
||||||
IncludeUseInfo includeUseInfo = IncludeUseInfo::Yes;
|
|
||||||
IncludeCfgInfo includeCfgInfo = IncludeCfgInfo::Yes;
|
|
||||||
IncludeRegFlowInfo includeRegFlowInfo = IncludeRegFlowInfo::Yes;
|
|
||||||
|
|
||||||
// Optional annotator function can be provided to describe each instruction, it takes function id and sequential instruction id
|
|
||||||
AnnotatorFn annotator = nullptr;
|
|
||||||
void* annotatorContext = nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BlockLinearizationStats
|
|
||||||
{
|
|
||||||
unsigned int constPropInstructionCount = 0;
|
|
||||||
double timeSeconds = 0.0;
|
|
||||||
|
|
||||||
BlockLinearizationStats& operator+=(const BlockLinearizationStats& that)
|
|
||||||
{
|
|
||||||
this->constPropInstructionCount += that.constPropInstructionCount;
|
|
||||||
this->timeSeconds += that.timeSeconds;
|
|
||||||
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
BlockLinearizationStats operator+(const BlockLinearizationStats& other) const
|
|
||||||
{
|
|
||||||
BlockLinearizationStats result(*this);
|
|
||||||
result += other;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
enum FunctionStatsFlags
|
|
||||||
{
|
|
||||||
// Enable stats collection per function
|
|
||||||
FunctionStats_Enable = 1 << 0,
|
|
||||||
// Compute function bytecode summary
|
|
||||||
FunctionStats_BytecodeSummary = 1 << 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct FunctionStats
|
|
||||||
{
|
|
||||||
std::string name;
|
|
||||||
int line = -1;
|
|
||||||
unsigned bcodeCount = 0;
|
|
||||||
unsigned irCount = 0;
|
|
||||||
unsigned asmCount = 0;
|
|
||||||
unsigned asmSize = 0;
|
|
||||||
std::vector<std::vector<unsigned>> bytecodeSummary;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct LoweringStats
|
|
||||||
{
|
|
||||||
unsigned totalFunctions = 0;
|
|
||||||
unsigned skippedFunctions = 0;
|
|
||||||
int spillsToSlot = 0;
|
|
||||||
int spillsToRestore = 0;
|
|
||||||
unsigned maxSpillSlotsUsed = 0;
|
|
||||||
unsigned blocksPreOpt = 0;
|
|
||||||
unsigned blocksPostOpt = 0;
|
|
||||||
unsigned maxBlockInstructions = 0;
|
|
||||||
|
|
||||||
int regAllocErrors = 0;
|
|
||||||
int loweringErrors = 0;
|
|
||||||
|
|
||||||
BlockLinearizationStats blockLinearizationStats;
|
|
||||||
|
|
||||||
unsigned functionStatsFlags = 0;
|
|
||||||
std::vector<FunctionStats> functions;
|
|
||||||
|
|
||||||
LoweringStats operator+(const LoweringStats& other) const
|
|
||||||
{
|
|
||||||
LoweringStats result(*this);
|
|
||||||
result += other;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
LoweringStats& operator+=(const LoweringStats& that)
|
|
||||||
{
|
|
||||||
this->totalFunctions += that.totalFunctions;
|
|
||||||
this->skippedFunctions += that.skippedFunctions;
|
|
||||||
this->spillsToSlot += that.spillsToSlot;
|
|
||||||
this->spillsToRestore += that.spillsToRestore;
|
|
||||||
this->maxSpillSlotsUsed = std::max(this->maxSpillSlotsUsed, that.maxSpillSlotsUsed);
|
|
||||||
this->blocksPreOpt += that.blocksPreOpt;
|
|
||||||
this->blocksPostOpt += that.blocksPostOpt;
|
|
||||||
this->maxBlockInstructions = std::max(this->maxBlockInstructions, that.maxBlockInstructions);
|
|
||||||
this->regAllocErrors += that.regAllocErrors;
|
|
||||||
this->loweringErrors += that.loweringErrors;
|
|
||||||
this->blockLinearizationStats += that.blockLinearizationStats;
|
|
||||||
if (this->functionStatsFlags & FunctionStats_Enable)
|
|
||||||
this->functions.insert(this->functions.end(), that.functions.begin(), that.functions.end());
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Generates assembly for target function and all inner functions
|
// Generates assembly for target function and all inner functions
|
||||||
std::string getAssembly(lua_State* L, int idx, AssemblyOptions options = {}, LoweringStats* stats = nullptr);
|
std::string getAssembly(lua_State* L, int idx, AssemblyOptions options = {}, LoweringStats* stats = nullptr);
|
||||||
|
|
||||||
|
|
|
@ -10,3 +10,9 @@
|
||||||
#else
|
#else
|
||||||
#define CODEGEN_ASSERT(expr) (void)sizeof(!!(expr))
|
#define CODEGEN_ASSERT(expr) (void)sizeof(!!(expr))
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(__x86_64__) || defined(_M_X64)
|
||||||
|
#define CODEGEN_TARGET_X64
|
||||||
|
#elif defined(__aarch64__) || defined(_M_ARM64)
|
||||||
|
#define CODEGEN_TARGET_A64
|
||||||
|
#endif
|
||||||
|
|
188
CodeGen/include/Luau/CodeGenOptions.h
Normal file
188
CodeGen/include/Luau/CodeGenOptions.h
Normal file
|
@ -0,0 +1,188 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace Luau
|
||||||
|
{
|
||||||
|
namespace CodeGen
|
||||||
|
{
|
||||||
|
|
||||||
|
enum CodeGenFlags
|
||||||
|
{
|
||||||
|
// Only run native codegen for modules that have been marked with --!native
|
||||||
|
CodeGen_OnlyNativeModules = 1 << 0,
|
||||||
|
// Run native codegen for functions that the compiler considers not profitable
|
||||||
|
CodeGen_ColdFunctions = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
using AllocationCallback = void(void* context, void* oldPointer, size_t oldSize, void* newPointer, size_t newSize);
|
||||||
|
|
||||||
|
struct IrBuilder;
|
||||||
|
struct IrOp;
|
||||||
|
|
||||||
|
using HostVectorOperationBytecodeType = uint8_t (*)(const char* member, size_t memberLength);
|
||||||
|
using HostVectorAccessHandler = bool (*)(IrBuilder& builder, const char* member, size_t memberLength, int resultReg, int sourceReg, int pcpos);
|
||||||
|
using HostVectorNamecallHandler =
|
||||||
|
bool (*)(IrBuilder& builder, const char* member, size_t memberLength, int argResReg, int sourceReg, int params, int results, int pcpos);
|
||||||
|
|
||||||
|
enum class HostMetamethod
|
||||||
|
{
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Idiv,
|
||||||
|
Mod,
|
||||||
|
Pow,
|
||||||
|
Minus,
|
||||||
|
Equal,
|
||||||
|
LessThan,
|
||||||
|
LessEqual,
|
||||||
|
Length,
|
||||||
|
Concat,
|
||||||
|
};
|
||||||
|
|
||||||
|
using HostUserdataOperationBytecodeType = uint8_t (*)(uint8_t type, const char* member, size_t memberLength);
|
||||||
|
using HostUserdataMetamethodBytecodeType = uint8_t (*)(uint8_t lhsTy, uint8_t rhsTy, HostMetamethod method);
|
||||||
|
using HostUserdataAccessHandler =
|
||||||
|
bool (*)(IrBuilder& builder, uint8_t type, const char* member, size_t memberLength, int resultReg, int sourceReg, int pcpos);
|
||||||
|
using HostUserdataMetamethodHandler =
|
||||||
|
bool (*)(IrBuilder& builder, uint8_t lhsTy, uint8_t rhsTy, int resultReg, IrOp lhs, IrOp rhs, HostMetamethod method, int pcpos);
|
||||||
|
using HostUserdataNamecallHandler = bool (*)(
|
||||||
|
IrBuilder& builder,
|
||||||
|
uint8_t type,
|
||||||
|
const char* member,
|
||||||
|
size_t memberLength,
|
||||||
|
int argResReg,
|
||||||
|
int sourceReg,
|
||||||
|
int params,
|
||||||
|
int results,
|
||||||
|
int pcpos
|
||||||
|
);
|
||||||
|
|
||||||
|
struct HostIrHooks
|
||||||
|
{
|
||||||
|
// Suggest result type of a vector field access
|
||||||
|
HostVectorOperationBytecodeType vectorAccessBytecodeType = nullptr;
|
||||||
|
|
||||||
|
// Suggest result type of a vector function namecall
|
||||||
|
HostVectorOperationBytecodeType vectorNamecallBytecodeType = nullptr;
|
||||||
|
|
||||||
|
// Handle vector value field access
|
||||||
|
// 'sourceReg' is guaranteed to be a vector
|
||||||
|
// Guards should take a VM exit to 'pcpos'
|
||||||
|
HostVectorAccessHandler vectorAccess = nullptr;
|
||||||
|
|
||||||
|
// Handle namecall performed on a vector value
|
||||||
|
// 'sourceReg' (self argument) is guaranteed to be a vector
|
||||||
|
// All other arguments can be of any type
|
||||||
|
// Guards should take a VM exit to 'pcpos'
|
||||||
|
HostVectorNamecallHandler vectorNamecall = nullptr;
|
||||||
|
|
||||||
|
// Suggest result type of a userdata field access
|
||||||
|
HostUserdataOperationBytecodeType userdataAccessBytecodeType = nullptr;
|
||||||
|
|
||||||
|
// Suggest result type of a metamethod call
|
||||||
|
HostUserdataMetamethodBytecodeType userdataMetamethodBytecodeType = nullptr;
|
||||||
|
|
||||||
|
// Suggest result type of a userdata namecall
|
||||||
|
HostUserdataOperationBytecodeType userdataNamecallBytecodeType = nullptr;
|
||||||
|
|
||||||
|
// Handle userdata value field access
|
||||||
|
// 'sourceReg' is guaranteed to be a userdata, but tag has to be checked
|
||||||
|
// Write to 'resultReg' might invalidate 'sourceReg'
|
||||||
|
// Guards should take a VM exit to 'pcpos'
|
||||||
|
HostUserdataAccessHandler userdataAccess = nullptr;
|
||||||
|
|
||||||
|
// Handle metamethod operation on a userdata value
|
||||||
|
// 'lhs' and 'rhs' operands can be VM registers of constants
|
||||||
|
// Operand types have to be checked and userdata operand tags have to be checked
|
||||||
|
// Write to 'resultReg' might invalidate source operands
|
||||||
|
// Guards should take a VM exit to 'pcpos'
|
||||||
|
HostUserdataMetamethodHandler userdataMetamethod = nullptr;
|
||||||
|
|
||||||
|
// Handle namecall performed on a userdata value
|
||||||
|
// 'sourceReg' (self argument) is guaranteed to be a userdata, but tag has to be checked
|
||||||
|
// All other arguments can be of any type
|
||||||
|
// Guards should take a VM exit to 'pcpos'
|
||||||
|
HostUserdataNamecallHandler userdataNamecall = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CompilationOptions
|
||||||
|
{
|
||||||
|
unsigned int flags = 0;
|
||||||
|
HostIrHooks hooks;
|
||||||
|
|
||||||
|
// null-terminated array of userdata types names that might have custom lowering
|
||||||
|
const char* const* userdataTypes = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
using AnnotatorFn = void (*)(void* context, std::string& result, int fid, int instpos);
|
||||||
|
|
||||||
|
// Output "#" before IR blocks and instructions
|
||||||
|
enum class IncludeIrPrefix
|
||||||
|
{
|
||||||
|
No,
|
||||||
|
Yes
|
||||||
|
};
|
||||||
|
|
||||||
|
// Output user count and last use information of blocks and instructions
|
||||||
|
enum class IncludeUseInfo
|
||||||
|
{
|
||||||
|
No,
|
||||||
|
Yes
|
||||||
|
};
|
||||||
|
|
||||||
|
// Output CFG informations like block predecessors, successors and etc
|
||||||
|
enum class IncludeCfgInfo
|
||||||
|
{
|
||||||
|
No,
|
||||||
|
Yes
|
||||||
|
};
|
||||||
|
|
||||||
|
// Output VM register live in/out information for blocks
|
||||||
|
enum class IncludeRegFlowInfo
|
||||||
|
{
|
||||||
|
No,
|
||||||
|
Yes
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AssemblyOptions
|
||||||
|
{
|
||||||
|
enum Target
|
||||||
|
{
|
||||||
|
Host,
|
||||||
|
A64,
|
||||||
|
A64_NoFeatures,
|
||||||
|
X64_Windows,
|
||||||
|
X64_SystemV,
|
||||||
|
};
|
||||||
|
|
||||||
|
Target target = Host;
|
||||||
|
|
||||||
|
CompilationOptions compilationOptions;
|
||||||
|
|
||||||
|
bool outputBinary = false;
|
||||||
|
|
||||||
|
bool includeAssembly = false;
|
||||||
|
bool includeIr = false;
|
||||||
|
bool includeOutlinedCode = false;
|
||||||
|
bool includeIrTypes = false;
|
||||||
|
|
||||||
|
IncludeIrPrefix includeIrPrefix = IncludeIrPrefix::Yes;
|
||||||
|
IncludeUseInfo includeUseInfo = IncludeUseInfo::Yes;
|
||||||
|
IncludeCfgInfo includeCfgInfo = IncludeCfgInfo::Yes;
|
||||||
|
IncludeRegFlowInfo includeRegFlowInfo = IncludeRegFlowInfo::Yes;
|
||||||
|
|
||||||
|
// Optional annotator function can be provided to describe each instruction, it takes function id and sequential instruction id
|
||||||
|
AnnotatorFn annotator = nullptr;
|
||||||
|
void* annotatorContext = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace CodeGen
|
||||||
|
} // namespace Luau
|
|
@ -20,6 +20,8 @@ namespace Luau
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
{
|
{
|
||||||
|
|
||||||
|
struct LoweringStats;
|
||||||
|
|
||||||
// IR extensions to LuauBuiltinFunction enum (these only exist inside IR, and start from 256 to avoid collisions)
|
// IR extensions to LuauBuiltinFunction enum (these only exist inside IR, and start from 256 to avoid collisions)
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
|
@ -67,18 +69,18 @@ enum class IrCmd : uint8_t
|
||||||
LOAD_ENV,
|
LOAD_ENV,
|
||||||
|
|
||||||
// Get pointer (TValue) to table array at index
|
// Get pointer (TValue) to table array at index
|
||||||
// A: pointer (Table)
|
// A: pointer (LuaTable)
|
||||||
// B: int
|
// B: int
|
||||||
GET_ARR_ADDR,
|
GET_ARR_ADDR,
|
||||||
|
|
||||||
// Get pointer (LuaNode) to table node element at the active cached slot index
|
// Get pointer (LuaNode) to table node element at the active cached slot index
|
||||||
// A: pointer (Table)
|
// A: pointer (LuaTable)
|
||||||
// B: unsigned int (pcpos)
|
// B: unsigned int (pcpos)
|
||||||
// C: Kn
|
// C: Kn
|
||||||
GET_SLOT_NODE_ADDR,
|
GET_SLOT_NODE_ADDR,
|
||||||
|
|
||||||
// Get pointer (LuaNode) to table node element at the main position of the specified key hash
|
// Get pointer (LuaNode) to table node element at the main position of the specified key hash
|
||||||
// A: pointer (Table)
|
// A: pointer (LuaTable)
|
||||||
// B: unsigned int (hash)
|
// B: unsigned int (hash)
|
||||||
GET_HASH_NODE_ADDR,
|
GET_HASH_NODE_ADDR,
|
||||||
|
|
||||||
|
@ -273,7 +275,7 @@ enum class IrCmd : uint8_t
|
||||||
JUMP_SLOT_MATCH,
|
JUMP_SLOT_MATCH,
|
||||||
|
|
||||||
// Get table length
|
// Get table length
|
||||||
// A: pointer (Table)
|
// A: pointer (LuaTable)
|
||||||
TABLE_LEN,
|
TABLE_LEN,
|
||||||
|
|
||||||
// Get string length
|
// Get string length
|
||||||
|
@ -286,11 +288,11 @@ enum class IrCmd : uint8_t
|
||||||
NEW_TABLE,
|
NEW_TABLE,
|
||||||
|
|
||||||
// Duplicate a table
|
// Duplicate a table
|
||||||
// A: pointer (Table)
|
// A: pointer (LuaTable)
|
||||||
DUP_TABLE,
|
DUP_TABLE,
|
||||||
|
|
||||||
// Insert an integer key into a table and return the pointer to inserted value (TValue)
|
// Insert an integer key into a table and return the pointer to inserted value (TValue)
|
||||||
// A: pointer (Table)
|
// A: pointer (LuaTable)
|
||||||
// B: int (key)
|
// B: int (key)
|
||||||
TABLE_SETNUM,
|
TABLE_SETNUM,
|
||||||
|
|
||||||
|
@ -430,13 +432,13 @@ enum class IrCmd : uint8_t
|
||||||
CHECK_TRUTHY,
|
CHECK_TRUTHY,
|
||||||
|
|
||||||
// Guard against readonly table
|
// Guard against readonly table
|
||||||
// A: pointer (Table)
|
// A: pointer (LuaTable)
|
||||||
// B: block/vmexit/undef
|
// B: block/vmexit/undef
|
||||||
// When undef is specified instead of a block, execution is aborted on check failure
|
// When undef is specified instead of a block, execution is aborted on check failure
|
||||||
CHECK_READONLY,
|
CHECK_READONLY,
|
||||||
|
|
||||||
// Guard against table having a metatable
|
// Guard against table having a metatable
|
||||||
// A: pointer (Table)
|
// A: pointer (LuaTable)
|
||||||
// B: block/vmexit/undef
|
// B: block/vmexit/undef
|
||||||
// When undef is specified instead of a block, execution is aborted on check failure
|
// When undef is specified instead of a block, execution is aborted on check failure
|
||||||
CHECK_NO_METATABLE,
|
CHECK_NO_METATABLE,
|
||||||
|
@ -447,7 +449,7 @@ enum class IrCmd : uint8_t
|
||||||
CHECK_SAFE_ENV,
|
CHECK_SAFE_ENV,
|
||||||
|
|
||||||
// Guard against index overflowing the table array size
|
// Guard against index overflowing the table array size
|
||||||
// A: pointer (Table)
|
// A: pointer (LuaTable)
|
||||||
// B: int (index)
|
// B: int (index)
|
||||||
// C: block/vmexit/undef
|
// C: block/vmexit/undef
|
||||||
// When undef is specified instead of a block, execution is aborted on check failure
|
// When undef is specified instead of a block, execution is aborted on check failure
|
||||||
|
@ -503,11 +505,11 @@ enum class IrCmd : uint8_t
|
||||||
BARRIER_OBJ,
|
BARRIER_OBJ,
|
||||||
|
|
||||||
// Handle GC write barrier (backwards) for a write into a table
|
// Handle GC write barrier (backwards) for a write into a table
|
||||||
// A: pointer (Table)
|
// A: pointer (LuaTable)
|
||||||
BARRIER_TABLE_BACK,
|
BARRIER_TABLE_BACK,
|
||||||
|
|
||||||
// Handle GC write barrier (forward) for a write into a table
|
// Handle GC write barrier (forward) for a write into a table
|
||||||
// A: pointer (Table)
|
// A: pointer (LuaTable)
|
||||||
// B: Rn (TValue that was written to the object)
|
// B: Rn (TValue that was written to the object)
|
||||||
// C: tag/undef (tag of the value that was written)
|
// C: tag/undef (tag of the value that was written)
|
||||||
BARRIER_TABLE_FORWARD,
|
BARRIER_TABLE_FORWARD,
|
||||||
|
@ -1049,6 +1051,8 @@ struct IrFunction
|
||||||
|
|
||||||
CfgInfo cfg;
|
CfgInfo cfg;
|
||||||
|
|
||||||
|
LoweringStats* stats = nullptr;
|
||||||
|
|
||||||
IrBlock& blockOp(IrOp op)
|
IrBlock& blockOp(IrOp op)
|
||||||
{
|
{
|
||||||
CODEGEN_ASSERT(op.kind == IrOpKind::Block);
|
CODEGEN_ASSERT(op.kind == IrOpKind::Block);
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "Luau/IrData.h"
|
#include "Luau/IrData.h"
|
||||||
#include "Luau/CodeGen.h"
|
#include "Luau/CodeGenOptions.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
103
CodeGen/include/Luau/LoweringStats.h
Normal file
103
CodeGen/include/Luau/LoweringStats.h
Normal file
|
@ -0,0 +1,103 @@
|
||||||
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Luau
|
||||||
|
{
|
||||||
|
namespace CodeGen
|
||||||
|
{
|
||||||
|
|
||||||
|
struct BlockLinearizationStats
|
||||||
|
{
|
||||||
|
unsigned int constPropInstructionCount = 0;
|
||||||
|
double timeSeconds = 0.0;
|
||||||
|
|
||||||
|
BlockLinearizationStats& operator+=(const BlockLinearizationStats& that)
|
||||||
|
{
|
||||||
|
this->constPropInstructionCount += that.constPropInstructionCount;
|
||||||
|
this->timeSeconds += that.timeSeconds;
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockLinearizationStats operator+(const BlockLinearizationStats& other) const
|
||||||
|
{
|
||||||
|
BlockLinearizationStats result(*this);
|
||||||
|
result += other;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
enum FunctionStatsFlags
|
||||||
|
{
|
||||||
|
// Enable stats collection per function
|
||||||
|
FunctionStats_Enable = 1 << 0,
|
||||||
|
// Compute function bytecode summary
|
||||||
|
FunctionStats_BytecodeSummary = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct FunctionStats
|
||||||
|
{
|
||||||
|
std::string name;
|
||||||
|
int line = -1;
|
||||||
|
unsigned bcodeCount = 0;
|
||||||
|
unsigned irCount = 0;
|
||||||
|
unsigned asmCount = 0;
|
||||||
|
unsigned asmSize = 0;
|
||||||
|
std::vector<std::vector<unsigned>> bytecodeSummary;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LoweringStats
|
||||||
|
{
|
||||||
|
unsigned totalFunctions = 0;
|
||||||
|
unsigned skippedFunctions = 0;
|
||||||
|
int spillsToSlot = 0;
|
||||||
|
int spillsToRestore = 0;
|
||||||
|
unsigned maxSpillSlotsUsed = 0;
|
||||||
|
unsigned blocksPreOpt = 0;
|
||||||
|
unsigned blocksPostOpt = 0;
|
||||||
|
unsigned maxBlockInstructions = 0;
|
||||||
|
|
||||||
|
int regAllocErrors = 0;
|
||||||
|
int loweringErrors = 0;
|
||||||
|
|
||||||
|
BlockLinearizationStats blockLinearizationStats;
|
||||||
|
|
||||||
|
unsigned functionStatsFlags = 0;
|
||||||
|
std::vector<FunctionStats> functions;
|
||||||
|
|
||||||
|
LoweringStats operator+(const LoweringStats& other) const
|
||||||
|
{
|
||||||
|
LoweringStats result(*this);
|
||||||
|
result += other;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
LoweringStats& operator+=(const LoweringStats& that)
|
||||||
|
{
|
||||||
|
this->totalFunctions += that.totalFunctions;
|
||||||
|
this->skippedFunctions += that.skippedFunctions;
|
||||||
|
this->spillsToSlot += that.spillsToSlot;
|
||||||
|
this->spillsToRestore += that.spillsToRestore;
|
||||||
|
this->maxSpillSlotsUsed = std::max(this->maxSpillSlotsUsed, that.maxSpillSlotsUsed);
|
||||||
|
this->blocksPreOpt += that.blocksPreOpt;
|
||||||
|
this->blocksPostOpt += that.blocksPostOpt;
|
||||||
|
this->maxBlockInstructions = std::max(this->maxBlockInstructions, that.maxBlockInstructions);
|
||||||
|
|
||||||
|
this->regAllocErrors += that.regAllocErrors;
|
||||||
|
this->loweringErrors += that.loweringErrors;
|
||||||
|
|
||||||
|
this->blockLinearizationStats += that.blockLinearizationStats;
|
||||||
|
|
||||||
|
if (this->functionStatsFlags & FunctionStats_Enable)
|
||||||
|
this->functions.insert(this->functions.end(), that.functions.begin(), that.functions.end());
|
||||||
|
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace CodeGen
|
||||||
|
} // namespace Luau
|
|
@ -2,7 +2,7 @@
|
||||||
#include "Luau/BytecodeAnalysis.h"
|
#include "Luau/BytecodeAnalysis.h"
|
||||||
|
|
||||||
#include "Luau/BytecodeUtils.h"
|
#include "Luau/BytecodeUtils.h"
|
||||||
#include "Luau/CodeGen.h"
|
#include "Luau/CodeGenOptions.h"
|
||||||
#include "Luau/IrData.h"
|
#include "Luau/IrData.h"
|
||||||
#include "Luau/IrUtils.h"
|
#include "Luau/IrUtils.h"
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#include "Luau/CodeBlockUnwind.h"
|
#include "Luau/CodeBlockUnwind.h"
|
||||||
|
|
||||||
#include "Luau/CodeAllocator.h"
|
#include "Luau/CodeAllocator.h"
|
||||||
|
#include "Luau/CodeGenCommon.h"
|
||||||
#include "Luau/UnwindBuilder.h"
|
#include "Luau/UnwindBuilder.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include "CodeGenLower.h"
|
#include "CodeGenLower.h"
|
||||||
|
|
||||||
#include "Luau/Common.h"
|
#include "Luau/CodeGenCommon.h"
|
||||||
#include "Luau/CodeAllocator.h"
|
#include "Luau/CodeAllocator.h"
|
||||||
#include "Luau/CodeBlockUnwind.h"
|
#include "Luau/CodeBlockUnwind.h"
|
||||||
#include "Luau/IrBuilder.h"
|
#include "Luau/IrBuilder.h"
|
||||||
|
@ -44,6 +44,7 @@
|
||||||
LUAU_FASTFLAGVARIABLE(DebugCodegenNoOpt)
|
LUAU_FASTFLAGVARIABLE(DebugCodegenNoOpt)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugCodegenOptSize)
|
LUAU_FASTFLAGVARIABLE(DebugCodegenOptSize)
|
||||||
LUAU_FASTFLAGVARIABLE(DebugCodegenSkipNumbering)
|
LUAU_FASTFLAGVARIABLE(DebugCodegenSkipNumbering)
|
||||||
|
LUAU_FASTFLAGVARIABLE(CodegenWiderLoweringStats)
|
||||||
|
|
||||||
// Per-module IR instruction count limit
|
// Per-module IR instruction count limit
|
||||||
LUAU_FASTINTVARIABLE(CodegenHeuristicsInstructionLimit, 1'048'576) // 1 M
|
LUAU_FASTINTVARIABLE(CodegenHeuristicsInstructionLimit, 1'048'576) // 1 M
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/CodeGen.h"
|
|
||||||
#include "Luau/BytecodeAnalysis.h"
|
#include "Luau/BytecodeAnalysis.h"
|
||||||
#include "Luau/BytecodeUtils.h"
|
#include "Luau/BytecodeUtils.h"
|
||||||
#include "Luau/BytecodeSummary.h"
|
#include "Luau/BytecodeSummary.h"
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include "CodeGenLower.h"
|
#include "CodeGenLower.h"
|
||||||
#include "CodeGenX64.h"
|
#include "CodeGenX64.h"
|
||||||
|
|
||||||
|
#include "Luau/CodeGenCommon.h"
|
||||||
#include "Luau/CodeBlockUnwind.h"
|
#include "Luau/CodeBlockUnwind.h"
|
||||||
#include "Luau/UnwindBuilder.h"
|
#include "Luau/UnwindBuilder.h"
|
||||||
#include "Luau/UnwindBuilderDwarf2.h"
|
#include "Luau/UnwindBuilderDwarf2.h"
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "Luau/IrBuilder.h"
|
#include "Luau/IrBuilder.h"
|
||||||
#include "Luau/IrDump.h"
|
#include "Luau/IrDump.h"
|
||||||
#include "Luau/IrUtils.h"
|
#include "Luau/IrUtils.h"
|
||||||
|
#include "Luau/LoweringStats.h"
|
||||||
#include "Luau/OptimizeConstProp.h"
|
#include "Luau/OptimizeConstProp.h"
|
||||||
#include "Luau/OptimizeDeadStore.h"
|
#include "Luau/OptimizeDeadStore.h"
|
||||||
#include "Luau/OptimizeFinalX64.h"
|
#include "Luau/OptimizeFinalX64.h"
|
||||||
|
@ -24,6 +25,7 @@
|
||||||
LUAU_FASTFLAG(DebugCodegenNoOpt)
|
LUAU_FASTFLAG(DebugCodegenNoOpt)
|
||||||
LUAU_FASTFLAG(DebugCodegenOptSize)
|
LUAU_FASTFLAG(DebugCodegenOptSize)
|
||||||
LUAU_FASTFLAG(DebugCodegenSkipNumbering)
|
LUAU_FASTFLAG(DebugCodegenSkipNumbering)
|
||||||
|
LUAU_FASTFLAG(CodegenWiderLoweringStats)
|
||||||
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
LUAU_FASTINT(CodegenHeuristicsInstructionLimit)
|
||||||
LUAU_FASTINT(CodegenHeuristicsBlockLimit)
|
LUAU_FASTINT(CodegenHeuristicsBlockLimit)
|
||||||
LUAU_FASTINT(CodegenHeuristicsBlockInstructionLimit)
|
LUAU_FASTINT(CodegenHeuristicsBlockInstructionLimit)
|
||||||
|
@ -298,6 +300,9 @@ inline bool lowerFunction(
|
||||||
CodeGenCompilationResult& codeGenCompilationResult
|
CodeGenCompilationResult& codeGenCompilationResult
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
if (FFlag::CodegenWiderLoweringStats)
|
||||||
|
ir.function.stats = stats;
|
||||||
|
|
||||||
killUnusedBlocks(ir.function);
|
killUnusedBlocks(ir.function);
|
||||||
|
|
||||||
unsigned preOptBlockCount = 0;
|
unsigned preOptBlockCount = 0;
|
||||||
|
|
|
@ -63,7 +63,7 @@ namespace Luau
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
{
|
{
|
||||||
|
|
||||||
bool forgLoopTableIter(lua_State* L, Table* h, int index, TValue* ra)
|
bool forgLoopTableIter(lua_State* L, LuaTable* h, int index, TValue* ra)
|
||||||
{
|
{
|
||||||
int sizearray = h->sizearray;
|
int sizearray = h->sizearray;
|
||||||
|
|
||||||
|
@ -106,7 +106,7 @@ bool forgLoopTableIter(lua_State* L, Table* h, int index, TValue* ra)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool forgLoopNodeIter(lua_State* L, Table* h, int index, TValue* ra)
|
bool forgLoopNodeIter(lua_State* L, LuaTable* h, int index, TValue* ra)
|
||||||
{
|
{
|
||||||
int sizearray = h->sizearray;
|
int sizearray = h->sizearray;
|
||||||
int sizenode = 1 << h->lsizenode;
|
int sizenode = 1 << h->lsizenode;
|
||||||
|
@ -233,7 +233,7 @@ Udata* newUserdata(lua_State* L, size_t s, int tag)
|
||||||
{
|
{
|
||||||
Udata* u = luaU_newudata(L, s, tag);
|
Udata* u = luaU_newudata(L, s, tag);
|
||||||
|
|
||||||
if (Table* h = L->global->udatamt[tag])
|
if (LuaTable* h = L->global->udatamt[tag])
|
||||||
{
|
{
|
||||||
// currently, we always allocate unmarked objects, so forward barrier can be skipped
|
// currently, we always allocate unmarked objects, so forward barrier can be skipped
|
||||||
LUAU_ASSERT(!isblack(obj2gco(u)));
|
LUAU_ASSERT(!isblack(obj2gco(u)));
|
||||||
|
@ -345,7 +345,7 @@ const Instruction* executeGETGLOBAL(lua_State* L, const Instruction* pc, StkId b
|
||||||
LUAU_ASSERT(ttisstring(kv));
|
LUAU_ASSERT(ttisstring(kv));
|
||||||
|
|
||||||
// fast-path should already have been checked, so we skip checking for it here
|
// fast-path should already have been checked, so we skip checking for it here
|
||||||
Table* h = cl->env;
|
LuaTable* h = cl->env;
|
||||||
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
||||||
|
|
||||||
// slow-path, may invoke Lua calls via __index metamethod
|
// slow-path, may invoke Lua calls via __index metamethod
|
||||||
|
@ -368,7 +368,7 @@ const Instruction* executeSETGLOBAL(lua_State* L, const Instruction* pc, StkId b
|
||||||
LUAU_ASSERT(ttisstring(kv));
|
LUAU_ASSERT(ttisstring(kv));
|
||||||
|
|
||||||
// fast-path should already have been checked, so we skip checking for it here
|
// fast-path should already have been checked, so we skip checking for it here
|
||||||
Table* h = cl->env;
|
LuaTable* h = cl->env;
|
||||||
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
||||||
|
|
||||||
// slow-path, may invoke Lua calls via __newindex metamethod
|
// slow-path, may invoke Lua calls via __newindex metamethod
|
||||||
|
@ -394,7 +394,7 @@ const Instruction* executeGETTABLEKS(lua_State* L, const Instruction* pc, StkId
|
||||||
// fast-path: built-in table
|
// fast-path: built-in table
|
||||||
if (ttistable(rb))
|
if (ttistable(rb))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(rb);
|
LuaTable* h = hvalue(rb);
|
||||||
|
|
||||||
// we ignore the fast path that checks for the cached slot since IrTranslation already checks for it.
|
// we ignore the fast path that checks for the cached slot since IrTranslation already checks for it.
|
||||||
|
|
||||||
|
@ -506,7 +506,7 @@ const Instruction* executeSETTABLEKS(lua_State* L, const Instruction* pc, StkId
|
||||||
// fast-path: built-in table
|
// fast-path: built-in table
|
||||||
if (ttistable(rb))
|
if (ttistable(rb))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(rb);
|
LuaTable* h = hvalue(rb);
|
||||||
|
|
||||||
// we ignore the fast path that checks for the cached slot since IrTranslation already checks for it.
|
// we ignore the fast path that checks for the cached slot since IrTranslation already checks for it.
|
||||||
|
|
||||||
|
@ -591,7 +591,7 @@ const Instruction* executeNAMECALL(lua_State* L, const Instruction* pc, StkId ba
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Table* mt = ttisuserdata(rb) ? uvalue(rb)->metatable : L->global->mt[ttype(rb)];
|
LuaTable* mt = ttisuserdata(rb) ? uvalue(rb)->metatable : L->global->mt[ttype(rb)];
|
||||||
const TValue* tmi = 0;
|
const TValue* tmi = 0;
|
||||||
|
|
||||||
// fast-path: metatable with __namecall
|
// fast-path: metatable with __namecall
|
||||||
|
@ -605,7 +605,7 @@ const Instruction* executeNAMECALL(lua_State* L, const Instruction* pc, StkId ba
|
||||||
}
|
}
|
||||||
else if ((tmi = fasttm(L, mt, TM_INDEX)) && ttistable(tmi))
|
else if ((tmi = fasttm(L, mt, TM_INDEX)) && ttistable(tmi))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(tmi);
|
LuaTable* h = hvalue(tmi);
|
||||||
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
||||||
LuaNode* n = &h->node[slot];
|
LuaNode* n = &h->node[slot];
|
||||||
|
|
||||||
|
@ -662,7 +662,7 @@ const Instruction* executeSETLIST(lua_State* L, const Instruction* pc, StkId bas
|
||||||
L->top = L->ci->top;
|
L->top = L->ci->top;
|
||||||
}
|
}
|
||||||
|
|
||||||
Table* h = hvalue(ra);
|
LuaTable* h = hvalue(ra);
|
||||||
|
|
||||||
// TODO: we really don't need this anymore
|
// TODO: we really don't need this anymore
|
||||||
if (!ttistable(ra))
|
if (!ttistable(ra))
|
||||||
|
@ -697,7 +697,7 @@ const Instruction* executeFORGPREP(lua_State* L, const Instruction* pc, StkId ba
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Table* mt = ttistable(ra) ? hvalue(ra)->metatable : ttisuserdata(ra) ? uvalue(ra)->metatable : cast_to(Table*, NULL);
|
LuaTable* mt = ttistable(ra) ? hvalue(ra)->metatable : ttisuserdata(ra) ? uvalue(ra)->metatable : cast_to(LuaTable*, NULL);
|
||||||
|
|
||||||
if (const TValue* fn = fasttm(L, mt, TM_ITER))
|
if (const TValue* fn = fasttm(L, mt, TM_ITER))
|
||||||
{
|
{
|
||||||
|
|
|
@ -8,8 +8,8 @@ namespace Luau
|
||||||
namespace CodeGen
|
namespace CodeGen
|
||||||
{
|
{
|
||||||
|
|
||||||
bool forgLoopTableIter(lua_State* L, Table* h, int index, TValue* ra);
|
bool forgLoopTableIter(lua_State* L, LuaTable* h, int index, TValue* ra);
|
||||||
bool forgLoopNodeIter(lua_State* L, Table* h, int index, TValue* ra);
|
bool forgLoopNodeIter(lua_State* L, LuaTable* h, int index, TValue* ra);
|
||||||
bool forgLoopNonTableFallback(lua_State* L, int insnA, int aux);
|
bool forgLoopNonTableFallback(lua_State* L, int insnA, int aux);
|
||||||
|
|
||||||
void forgPrepXnextFallback(lua_State* L, TValue* ra, int pc);
|
void forgPrepXnextFallback(lua_State* L, TValue* ra, int pc);
|
||||||
|
|
|
@ -120,12 +120,12 @@ void getTableNodeAtCachedSlot(AssemblyBuilderX64& build, RegisterX64 tmp, Regist
|
||||||
CODEGEN_ASSERT(tmp != node);
|
CODEGEN_ASSERT(tmp != node);
|
||||||
CODEGEN_ASSERT(table != node);
|
CODEGEN_ASSERT(table != node);
|
||||||
|
|
||||||
build.mov(node, qword[table + offsetof(Table, node)]);
|
build.mov(node, qword[table + offsetof(LuaTable, node)]);
|
||||||
|
|
||||||
// compute cached slot
|
// compute cached slot
|
||||||
build.mov(tmp, sCode);
|
build.mov(tmp, sCode);
|
||||||
build.movzx(dwordReg(tmp), byte[tmp + pcpos * sizeof(Instruction) + kOffsetOfInstructionC]);
|
build.movzx(dwordReg(tmp), byte[tmp + pcpos * sizeof(Instruction) + kOffsetOfInstructionC]);
|
||||||
build.and_(byteReg(tmp), byte[table + offsetof(Table, nodemask8)]);
|
build.and_(byteReg(tmp), byte[table + offsetof(LuaTable, nodemask8)]);
|
||||||
|
|
||||||
// LuaNode* n = &h->node[slot];
|
// LuaNode* n = &h->node[slot];
|
||||||
build.shl(dwordReg(tmp), kLuaNodeSizeLog2);
|
build.shl(dwordReg(tmp), kLuaNodeSizeLog2);
|
||||||
|
@ -282,7 +282,7 @@ void callBarrierTableFast(IrRegAllocX64& regs, AssemblyBuilderX64& build, Regist
|
||||||
IrCallWrapperX64 callWrap(regs, build);
|
IrCallWrapperX64 callWrap(regs, build);
|
||||||
callWrap.addArgument(SizeX64::qword, rState);
|
callWrap.addArgument(SizeX64::qword, rState);
|
||||||
callWrap.addArgument(SizeX64::qword, table, tableOp);
|
callWrap.addArgument(SizeX64::qword, table, tableOp);
|
||||||
callWrap.addArgument(SizeX64::qword, addr[table + offsetof(Table, gclist)]);
|
callWrap.addArgument(SizeX64::qword, addr[table + offsetof(LuaTable, gclist)]);
|
||||||
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaC_barrierback)]);
|
callWrap.call(qword[rNativeContext + offsetof(NativeContext, luaC_barrierback)]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -292,7 +292,7 @@ void emitInstSetList(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, int
|
||||||
Label skipResize;
|
Label skipResize;
|
||||||
|
|
||||||
// Resize if h->sizearray < last
|
// Resize if h->sizearray < last
|
||||||
build.cmp(dword[table + offsetof(Table, sizearray)], last);
|
build.cmp(dword[table + offsetof(LuaTable, sizearray)], last);
|
||||||
build.jcc(ConditionX64::NotBelow, skipResize);
|
build.jcc(ConditionX64::NotBelow, skipResize);
|
||||||
|
|
||||||
// Argument setup reordered to avoid conflicts
|
// Argument setup reordered to avoid conflicts
|
||||||
|
@ -309,7 +309,7 @@ void emitInstSetList(IrRegAllocX64& regs, AssemblyBuilderX64& build, int ra, int
|
||||||
RegisterX64 arrayDst = rdx;
|
RegisterX64 arrayDst = rdx;
|
||||||
RegisterX64 offset = rcx;
|
RegisterX64 offset = rcx;
|
||||||
|
|
||||||
build.mov(arrayDst, qword[table + offsetof(Table, array)]);
|
build.mov(arrayDst, qword[table + offsetof(LuaTable, array)]);
|
||||||
|
|
||||||
const int kUnrollSetListLimit = 4;
|
const int kUnrollSetListLimit = 4;
|
||||||
|
|
||||||
|
@ -380,7 +380,7 @@ void emitInstForGLoop(AssemblyBuilderX64& build, int ra, int aux, Label& loopRep
|
||||||
// &array[index]
|
// &array[index]
|
||||||
build.mov(dwordReg(elemPtr), dwordReg(index));
|
build.mov(dwordReg(elemPtr), dwordReg(index));
|
||||||
build.shl(dwordReg(elemPtr), kTValueSizeLog2);
|
build.shl(dwordReg(elemPtr), kTValueSizeLog2);
|
||||||
build.add(elemPtr, qword[table + offsetof(Table, array)]);
|
build.add(elemPtr, qword[table + offsetof(LuaTable, array)]);
|
||||||
|
|
||||||
// Clear extra variables since we might have more than two
|
// Clear extra variables since we might have more than two
|
||||||
for (int i = 2; i < aux; ++i)
|
for (int i = 2; i < aux; ++i)
|
||||||
|
@ -391,7 +391,7 @@ void emitInstForGLoop(AssemblyBuilderX64& build, int ra, int aux, Label& loopRep
|
||||||
// First we advance index through the array portion
|
// First we advance index through the array portion
|
||||||
// while (unsigned(index) < unsigned(sizearray))
|
// while (unsigned(index) < unsigned(sizearray))
|
||||||
Label arrayLoop = build.setLabel();
|
Label arrayLoop = build.setLabel();
|
||||||
build.cmp(dwordReg(index), dword[table + offsetof(Table, sizearray)]);
|
build.cmp(dwordReg(index), dword[table + offsetof(LuaTable, sizearray)]);
|
||||||
build.jcc(ConditionX64::NotBelow, skipArray);
|
build.jcc(ConditionX64::NotBelow, skipArray);
|
||||||
|
|
||||||
// If element is nil, we increment the index; if it's not, we still need 'index + 1' inside
|
// If element is nil, we increment the index; if it's not, we still need 'index + 1' inside
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "Luau/DenseHash.h"
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/IrData.h"
|
#include "Luau/IrData.h"
|
||||||
#include "Luau/IrUtils.h"
|
#include "Luau/IrUtils.h"
|
||||||
|
#include "Luau/LoweringStats.h"
|
||||||
|
|
||||||
#include "EmitCommonA64.h"
|
#include "EmitCommonA64.h"
|
||||||
#include "NativeState.h"
|
#include "NativeState.h"
|
||||||
|
@ -330,7 +331,7 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
case IrCmd::GET_ARR_ADDR:
|
case IrCmd::GET_ARR_ADDR:
|
||||||
{
|
{
|
||||||
inst.regA64 = regs.allocReuse(KindA64::x, index, {inst.a});
|
inst.regA64 = regs.allocReuse(KindA64::x, index, {inst.a});
|
||||||
build.ldr(inst.regA64, mem(regOp(inst.a), offsetof(Table, array)));
|
build.ldr(inst.regA64, mem(regOp(inst.a), offsetof(LuaTable, array)));
|
||||||
|
|
||||||
if (inst.b.kind == IrOpKind::Inst)
|
if (inst.b.kind == IrOpKind::Inst)
|
||||||
{
|
{
|
||||||
|
@ -376,11 +377,11 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
|
|
||||||
// C field can be shifted as long as it's at the most significant byte of the instruction word
|
// C field can be shifted as long as it's at the most significant byte of the instruction word
|
||||||
CODEGEN_ASSERT(kOffsetOfInstructionC == 3);
|
CODEGEN_ASSERT(kOffsetOfInstructionC == 3);
|
||||||
build.ldrb(temp2, mem(regOp(inst.a), offsetof(Table, nodemask8)));
|
build.ldrb(temp2, mem(regOp(inst.a), offsetof(LuaTable, nodemask8)));
|
||||||
build.and_(temp2, temp2, temp1w, -24);
|
build.and_(temp2, temp2, temp1w, -24);
|
||||||
|
|
||||||
// note: this may clobber inst.a, so it's important that we don't use it after this
|
// note: this may clobber inst.a, so it's important that we don't use it after this
|
||||||
build.ldr(inst.regA64, mem(regOp(inst.a), offsetof(Table, node)));
|
build.ldr(inst.regA64, mem(regOp(inst.a), offsetof(LuaTable, node)));
|
||||||
build.add(inst.regA64, inst.regA64, temp2x, kLuaNodeSizeLog2); // "zero extend" temp2 to get a larger shift (top 32 bits are zero)
|
build.add(inst.regA64, inst.regA64, temp2x, kLuaNodeSizeLog2); // "zero extend" temp2 to get a larger shift (top 32 bits are zero)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -393,13 +394,13 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
|
|
||||||
// hash & ((1 << lsizenode) - 1) == hash & ~(-1 << lsizenode)
|
// hash & ((1 << lsizenode) - 1) == hash & ~(-1 << lsizenode)
|
||||||
build.mov(temp1, -1);
|
build.mov(temp1, -1);
|
||||||
build.ldrb(temp2, mem(regOp(inst.a), offsetof(Table, lsizenode)));
|
build.ldrb(temp2, mem(regOp(inst.a), offsetof(LuaTable, lsizenode)));
|
||||||
build.lsl(temp1, temp1, temp2);
|
build.lsl(temp1, temp1, temp2);
|
||||||
build.mov(temp2, uintOp(inst.b));
|
build.mov(temp2, uintOp(inst.b));
|
||||||
build.bic(temp2, temp2, temp1);
|
build.bic(temp2, temp2, temp1);
|
||||||
|
|
||||||
// note: this may clobber inst.a, so it's important that we don't use it after this
|
// note: this may clobber inst.a, so it's important that we don't use it after this
|
||||||
build.ldr(inst.regA64, mem(regOp(inst.a), offsetof(Table, node)));
|
build.ldr(inst.regA64, mem(regOp(inst.a), offsetof(LuaTable, node)));
|
||||||
build.add(inst.regA64, inst.regA64, temp2x, kLuaNodeSizeLog2); // "zero extend" temp2 to get a larger shift (top 32 bits are zero)
|
build.add(inst.regA64, inst.regA64, temp2x, kLuaNodeSizeLog2); // "zero extend" temp2 to get a larger shift (top 32 bits are zero)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1075,10 +1076,10 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
RegisterA64 temp1 = regs.allocTemp(KindA64::x);
|
RegisterA64 temp1 = regs.allocTemp(KindA64::x);
|
||||||
RegisterA64 temp2 = regs.allocTemp(KindA64::w);
|
RegisterA64 temp2 = regs.allocTemp(KindA64::w);
|
||||||
|
|
||||||
build.ldr(temp1, mem(regOp(inst.a), offsetof(Table, metatable)));
|
build.ldr(temp1, mem(regOp(inst.a), offsetof(LuaTable, metatable)));
|
||||||
build.cbz(temp1, labelOp(inst.c)); // no metatable
|
build.cbz(temp1, labelOp(inst.c)); // no metatable
|
||||||
|
|
||||||
build.ldrb(temp2, mem(temp1, offsetof(Table, tmcache)));
|
build.ldrb(temp2, mem(temp1, offsetof(LuaTable, tmcache)));
|
||||||
build.tst(temp2, 1 << intOp(inst.b)); // can't use tbz/tbnz because their jump offsets are too short
|
build.tst(temp2, 1 << intOp(inst.b)); // can't use tbz/tbnz because their jump offsets are too short
|
||||||
build.b(ConditionA64::NotEqual, labelOp(inst.c)); // Equal = Zero after tst; tmcache caches *absence* of metamethods
|
build.b(ConditionA64::NotEqual, labelOp(inst.c)); // Equal = Zero after tst; tmcache caches *absence* of metamethods
|
||||||
|
|
||||||
|
@ -1515,7 +1516,7 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
{
|
{
|
||||||
Label fresh; // used when guard aborts execution or jumps to a VM exit
|
Label fresh; // used when guard aborts execution or jumps to a VM exit
|
||||||
RegisterA64 temp = regs.allocTemp(KindA64::w);
|
RegisterA64 temp = regs.allocTemp(KindA64::w);
|
||||||
build.ldrb(temp, mem(regOp(inst.a), offsetof(Table, readonly)));
|
build.ldrb(temp, mem(regOp(inst.a), offsetof(LuaTable, readonly)));
|
||||||
build.cbnz(temp, getTargetLabel(inst.b, fresh));
|
build.cbnz(temp, getTargetLabel(inst.b, fresh));
|
||||||
finalizeTargetLabel(inst.b, fresh);
|
finalizeTargetLabel(inst.b, fresh);
|
||||||
break;
|
break;
|
||||||
|
@ -1524,7 +1525,7 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
{
|
{
|
||||||
Label fresh; // used when guard aborts execution or jumps to a VM exit
|
Label fresh; // used when guard aborts execution or jumps to a VM exit
|
||||||
RegisterA64 temp = regs.allocTemp(KindA64::x);
|
RegisterA64 temp = regs.allocTemp(KindA64::x);
|
||||||
build.ldr(temp, mem(regOp(inst.a), offsetof(Table, metatable)));
|
build.ldr(temp, mem(regOp(inst.a), offsetof(LuaTable, metatable)));
|
||||||
build.cbnz(temp, getTargetLabel(inst.b, fresh));
|
build.cbnz(temp, getTargetLabel(inst.b, fresh));
|
||||||
finalizeTargetLabel(inst.b, fresh);
|
finalizeTargetLabel(inst.b, fresh);
|
||||||
break;
|
break;
|
||||||
|
@ -1535,7 +1536,7 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
RegisterA64 temp = regs.allocTemp(KindA64::x);
|
RegisterA64 temp = regs.allocTemp(KindA64::x);
|
||||||
RegisterA64 tempw = castReg(KindA64::w, temp);
|
RegisterA64 tempw = castReg(KindA64::w, temp);
|
||||||
build.ldr(temp, mem(rClosure, offsetof(Closure, env)));
|
build.ldr(temp, mem(rClosure, offsetof(Closure, env)));
|
||||||
build.ldrb(tempw, mem(temp, offsetof(Table, safeenv)));
|
build.ldrb(tempw, mem(temp, offsetof(LuaTable, safeenv)));
|
||||||
build.cbz(tempw, getTargetLabel(inst.a, fresh));
|
build.cbz(tempw, getTargetLabel(inst.a, fresh));
|
||||||
finalizeTargetLabel(inst.a, fresh);
|
finalizeTargetLabel(inst.a, fresh);
|
||||||
break;
|
break;
|
||||||
|
@ -1546,7 +1547,7 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
Label& fail = getTargetLabel(inst.c, fresh);
|
Label& fail = getTargetLabel(inst.c, fresh);
|
||||||
|
|
||||||
RegisterA64 temp = regs.allocTemp(KindA64::w);
|
RegisterA64 temp = regs.allocTemp(KindA64::w);
|
||||||
build.ldr(temp, mem(regOp(inst.a), offsetof(Table, sizearray)));
|
build.ldr(temp, mem(regOp(inst.a), offsetof(LuaTable, sizearray)));
|
||||||
|
|
||||||
if (inst.b.kind == IrOpKind::Inst)
|
if (inst.b.kind == IrOpKind::Inst)
|
||||||
{
|
{
|
||||||
|
@ -1773,7 +1774,7 @@ void IrLoweringA64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
size_t spills = regs.spill(build, index, {reg});
|
size_t spills = regs.spill(build, index, {reg});
|
||||||
build.mov(x1, reg);
|
build.mov(x1, reg);
|
||||||
build.mov(x0, rState);
|
build.mov(x0, rState);
|
||||||
build.add(x2, x1, uint16_t(offsetof(Table, gclist)));
|
build.add(x2, x1, uint16_t(offsetof(LuaTable, gclist)));
|
||||||
build.ldr(x3, mem(rNativeContext, offsetof(NativeContext, luaC_barrierback)));
|
build.ldr(x3, mem(rNativeContext, offsetof(NativeContext, luaC_barrierback)));
|
||||||
build.blr(x3);
|
build.blr(x3);
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "Luau/DenseHash.h"
|
#include "Luau/DenseHash.h"
|
||||||
#include "Luau/IrData.h"
|
#include "Luau/IrData.h"
|
||||||
#include "Luau/IrUtils.h"
|
#include "Luau/IrUtils.h"
|
||||||
|
#include "Luau/LoweringStats.h"
|
||||||
|
|
||||||
#include "Luau/IrCallWrapperX64.h"
|
#include "Luau/IrCallWrapperX64.h"
|
||||||
|
|
||||||
|
@ -159,13 +160,13 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
build.mov(dwordReg(inst.regX64), regOp(inst.b));
|
build.mov(dwordReg(inst.regX64), regOp(inst.b));
|
||||||
|
|
||||||
build.shl(dwordReg(inst.regX64), kTValueSizeLog2);
|
build.shl(dwordReg(inst.regX64), kTValueSizeLog2);
|
||||||
build.add(inst.regX64, qword[regOp(inst.a) + offsetof(Table, array)]);
|
build.add(inst.regX64, qword[regOp(inst.a) + offsetof(LuaTable, array)]);
|
||||||
}
|
}
|
||||||
else if (inst.b.kind == IrOpKind::Constant)
|
else if (inst.b.kind == IrOpKind::Constant)
|
||||||
{
|
{
|
||||||
inst.regX64 = regs.allocRegOrReuse(SizeX64::qword, index, {inst.a});
|
inst.regX64 = regs.allocRegOrReuse(SizeX64::qword, index, {inst.a});
|
||||||
|
|
||||||
build.mov(inst.regX64, qword[regOp(inst.a) + offsetof(Table, array)]);
|
build.mov(inst.regX64, qword[regOp(inst.a) + offsetof(LuaTable, array)]);
|
||||||
|
|
||||||
if (intOp(inst.b) != 0)
|
if (intOp(inst.b) != 0)
|
||||||
build.lea(inst.regX64, addr[inst.regX64 + intOp(inst.b) * sizeof(TValue)]);
|
build.lea(inst.regX64, addr[inst.regX64 + intOp(inst.b) * sizeof(TValue)]);
|
||||||
|
@ -193,9 +194,9 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
|
|
||||||
ScopedRegX64 tmp{regs, SizeX64::qword};
|
ScopedRegX64 tmp{regs, SizeX64::qword};
|
||||||
|
|
||||||
build.mov(inst.regX64, qword[regOp(inst.a) + offsetof(Table, node)]);
|
build.mov(inst.regX64, qword[regOp(inst.a) + offsetof(LuaTable, node)]);
|
||||||
build.mov(dwordReg(tmp.reg), 1);
|
build.mov(dwordReg(tmp.reg), 1);
|
||||||
build.mov(byteReg(shiftTmp.reg), byte[regOp(inst.a) + offsetof(Table, lsizenode)]);
|
build.mov(byteReg(shiftTmp.reg), byte[regOp(inst.a) + offsetof(LuaTable, lsizenode)]);
|
||||||
build.shl(dwordReg(tmp.reg), byteReg(shiftTmp.reg));
|
build.shl(dwordReg(tmp.reg), byteReg(shiftTmp.reg));
|
||||||
build.dec(dwordReg(tmp.reg));
|
build.dec(dwordReg(tmp.reg));
|
||||||
build.and_(dwordReg(tmp.reg), uintOp(inst.b));
|
build.and_(dwordReg(tmp.reg), uintOp(inst.b));
|
||||||
|
@ -954,13 +955,13 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
{
|
{
|
||||||
ScopedRegX64 tmp{regs, SizeX64::qword};
|
ScopedRegX64 tmp{regs, SizeX64::qword};
|
||||||
|
|
||||||
build.mov(tmp.reg, qword[regOp(inst.a) + offsetof(Table, metatable)]);
|
build.mov(tmp.reg, qword[regOp(inst.a) + offsetof(LuaTable, metatable)]);
|
||||||
regs.freeLastUseReg(function.instOp(inst.a), index); // Release before the call if it's the last use
|
regs.freeLastUseReg(function.instOp(inst.a), index); // Release before the call if it's the last use
|
||||||
|
|
||||||
build.test(tmp.reg, tmp.reg);
|
build.test(tmp.reg, tmp.reg);
|
||||||
build.jcc(ConditionX64::Zero, labelOp(inst.c)); // No metatable
|
build.jcc(ConditionX64::Zero, labelOp(inst.c)); // No metatable
|
||||||
|
|
||||||
build.test(byte[tmp.reg + offsetof(Table, tmcache)], 1 << intOp(inst.b));
|
build.test(byte[tmp.reg + offsetof(LuaTable, tmcache)], 1 << intOp(inst.b));
|
||||||
build.jcc(ConditionX64::NotZero, labelOp(inst.c)); // No tag method
|
build.jcc(ConditionX64::NotZero, labelOp(inst.c)); // No tag method
|
||||||
|
|
||||||
ScopedRegX64 tmp2{regs, SizeX64::qword};
|
ScopedRegX64 tmp2{regs, SizeX64::qword};
|
||||||
|
@ -1320,11 +1321,11 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IrCmd::CHECK_READONLY:
|
case IrCmd::CHECK_READONLY:
|
||||||
build.cmp(byte[regOp(inst.a) + offsetof(Table, readonly)], 0);
|
build.cmp(byte[regOp(inst.a) + offsetof(LuaTable, readonly)], 0);
|
||||||
jumpOrAbortOnUndef(ConditionX64::NotEqual, inst.b, next);
|
jumpOrAbortOnUndef(ConditionX64::NotEqual, inst.b, next);
|
||||||
break;
|
break;
|
||||||
case IrCmd::CHECK_NO_METATABLE:
|
case IrCmd::CHECK_NO_METATABLE:
|
||||||
build.cmp(qword[regOp(inst.a) + offsetof(Table, metatable)], 0);
|
build.cmp(qword[regOp(inst.a) + offsetof(LuaTable, metatable)], 0);
|
||||||
jumpOrAbortOnUndef(ConditionX64::NotEqual, inst.b, next);
|
jumpOrAbortOnUndef(ConditionX64::NotEqual, inst.b, next);
|
||||||
break;
|
break;
|
||||||
case IrCmd::CHECK_SAFE_ENV:
|
case IrCmd::CHECK_SAFE_ENV:
|
||||||
|
@ -1333,16 +1334,16 @@ void IrLoweringX64::lowerInst(IrInst& inst, uint32_t index, const IrBlock& next)
|
||||||
|
|
||||||
build.mov(tmp.reg, sClosure);
|
build.mov(tmp.reg, sClosure);
|
||||||
build.mov(tmp.reg, qword[tmp.reg + offsetof(Closure, env)]);
|
build.mov(tmp.reg, qword[tmp.reg + offsetof(Closure, env)]);
|
||||||
build.cmp(byte[tmp.reg + offsetof(Table, safeenv)], 0);
|
build.cmp(byte[tmp.reg + offsetof(LuaTable, safeenv)], 0);
|
||||||
|
|
||||||
jumpOrAbortOnUndef(ConditionX64::Equal, inst.a, next);
|
jumpOrAbortOnUndef(ConditionX64::Equal, inst.a, next);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case IrCmd::CHECK_ARRAY_SIZE:
|
case IrCmd::CHECK_ARRAY_SIZE:
|
||||||
if (inst.b.kind == IrOpKind::Inst)
|
if (inst.b.kind == IrOpKind::Inst)
|
||||||
build.cmp(dword[regOp(inst.a) + offsetof(Table, sizearray)], regOp(inst.b));
|
build.cmp(dword[regOp(inst.a) + offsetof(LuaTable, sizearray)], regOp(inst.b));
|
||||||
else if (inst.b.kind == IrOpKind::Constant)
|
else if (inst.b.kind == IrOpKind::Constant)
|
||||||
build.cmp(dword[regOp(inst.a) + offsetof(Table, sizearray)], intOp(inst.b));
|
build.cmp(dword[regOp(inst.a) + offsetof(LuaTable, sizearray)], intOp(inst.b));
|
||||||
else
|
else
|
||||||
CODEGEN_ASSERT(!"Unsupported instruction form");
|
CODEGEN_ASSERT(!"Unsupported instruction form");
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
#include "IrRegAllocA64.h"
|
#include "IrRegAllocA64.h"
|
||||||
|
|
||||||
#include "Luau/AssemblyBuilderA64.h"
|
#include "Luau/AssemblyBuilderA64.h"
|
||||||
#include "Luau/CodeGen.h"
|
|
||||||
#include "Luau/IrUtils.h"
|
#include "Luau/IrUtils.h"
|
||||||
|
#include "Luau/LoweringStats.h"
|
||||||
|
|
||||||
#include "BitUtils.h"
|
#include "BitUtils.h"
|
||||||
#include "EmitCommonA64.h"
|
#include "EmitCommonA64.h"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/IrRegAllocX64.h"
|
#include "Luau/IrRegAllocX64.h"
|
||||||
|
|
||||||
#include "Luau/CodeGen.h"
|
|
||||||
#include "Luau/IrUtils.h"
|
#include "Luau/IrUtils.h"
|
||||||
|
#include "Luau/LoweringStats.h"
|
||||||
|
|
||||||
#include "EmitCommonX64.h"
|
#include "EmitCommonX64.h"
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
#include "Luau/Bytecode.h"
|
#include "Luau/Bytecode.h"
|
||||||
#include "Luau/BytecodeUtils.h"
|
#include "Luau/BytecodeUtils.h"
|
||||||
#include "Luau/CodeGen.h"
|
#include "Luau/CodeGenOptions.h"
|
||||||
#include "Luau/IrBuilder.h"
|
#include "Luau/IrBuilder.h"
|
||||||
#include "Luau/IrUtils.h"
|
#include "Luau/IrUtils.h"
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/IrUtils.h"
|
#include "Luau/IrUtils.h"
|
||||||
|
|
||||||
|
#include "Luau/CodeGenOptions.h"
|
||||||
#include "Luau/IrBuilder.h"
|
#include "Luau/IrBuilder.h"
|
||||||
|
|
||||||
#include "BitUtils.h"
|
#include "BitUtils.h"
|
||||||
|
|
|
@ -44,25 +44,25 @@ struct NativeContext
|
||||||
void (*luaV_dolen)(lua_State* L, StkId ra, const TValue* rb) = nullptr;
|
void (*luaV_dolen)(lua_State* L, StkId ra, const TValue* rb) = nullptr;
|
||||||
void (*luaV_gettable)(lua_State* L, const TValue* t, TValue* key, StkId val) = nullptr;
|
void (*luaV_gettable)(lua_State* L, const TValue* t, TValue* key, StkId val) = nullptr;
|
||||||
void (*luaV_settable)(lua_State* L, const TValue* t, TValue* key, StkId val) = nullptr;
|
void (*luaV_settable)(lua_State* L, const TValue* t, TValue* key, StkId val) = nullptr;
|
||||||
void (*luaV_getimport)(lua_State* L, Table* env, TValue* k, StkId res, uint32_t id, bool propagatenil) = nullptr;
|
void (*luaV_getimport)(lua_State* L, LuaTable* env, TValue* k, StkId res, uint32_t id, bool propagatenil) = nullptr;
|
||||||
void (*luaV_concat)(lua_State* L, int total, int last) = nullptr;
|
void (*luaV_concat)(lua_State* L, int total, int last) = nullptr;
|
||||||
|
|
||||||
int (*luaH_getn)(Table* t) = nullptr;
|
int (*luaH_getn)(LuaTable* t) = nullptr;
|
||||||
Table* (*luaH_new)(lua_State* L, int narray, int lnhash) = nullptr;
|
LuaTable* (*luaH_new)(lua_State* L, int narray, int lnhash) = nullptr;
|
||||||
Table* (*luaH_clone)(lua_State* L, Table* tt) = nullptr;
|
LuaTable* (*luaH_clone)(lua_State* L, LuaTable* tt) = nullptr;
|
||||||
void (*luaH_resizearray)(lua_State* L, Table* t, int nasize) = nullptr;
|
void (*luaH_resizearray)(lua_State* L, LuaTable* t, int nasize) = nullptr;
|
||||||
TValue* (*luaH_setnum)(lua_State* L, Table* t, int key);
|
TValue* (*luaH_setnum)(lua_State* L, LuaTable* t, int key);
|
||||||
|
|
||||||
void (*luaC_barriertable)(lua_State* L, Table* t, GCObject* v) = nullptr;
|
void (*luaC_barriertable)(lua_State* L, LuaTable* t, GCObject* v) = nullptr;
|
||||||
void (*luaC_barrierf)(lua_State* L, GCObject* o, GCObject* v) = nullptr;
|
void (*luaC_barrierf)(lua_State* L, GCObject* o, GCObject* v) = nullptr;
|
||||||
void (*luaC_barrierback)(lua_State* L, GCObject* o, GCObject** gclist) = nullptr;
|
void (*luaC_barrierback)(lua_State* L, GCObject* o, GCObject** gclist) = nullptr;
|
||||||
size_t (*luaC_step)(lua_State* L, bool assist) = nullptr;
|
size_t (*luaC_step)(lua_State* L, bool assist) = nullptr;
|
||||||
|
|
||||||
void (*luaF_close)(lua_State* L, StkId level) = nullptr;
|
void (*luaF_close)(lua_State* L, StkId level) = nullptr;
|
||||||
UpVal* (*luaF_findupval)(lua_State* L, StkId level) = nullptr;
|
UpVal* (*luaF_findupval)(lua_State* L, StkId level) = nullptr;
|
||||||
Closure* (*luaF_newLclosure)(lua_State* L, int nelems, Table* e, Proto* p) = nullptr;
|
Closure* (*luaF_newLclosure)(lua_State* L, int nelems, LuaTable* e, Proto* p) = nullptr;
|
||||||
|
|
||||||
const TValue* (*luaT_gettm)(Table* events, TMS event, TString* ename) = nullptr;
|
const TValue* (*luaT_gettm)(LuaTable* events, TMS event, TString* ename) = nullptr;
|
||||||
const TString* (*luaT_objtypenamestr)(lua_State* L, const TValue* o) = nullptr;
|
const TString* (*luaT_objtypenamestr)(lua_State* L, const TValue* o) = nullptr;
|
||||||
|
|
||||||
double (*libm_exp)(double) = nullptr;
|
double (*libm_exp)(double) = nullptr;
|
||||||
|
@ -87,8 +87,8 @@ struct NativeContext
|
||||||
double (*libm_modf)(double, double*) = nullptr;
|
double (*libm_modf)(double, double*) = nullptr;
|
||||||
|
|
||||||
// Helper functions
|
// Helper functions
|
||||||
bool (*forgLoopTableIter)(lua_State* L, Table* h, int index, TValue* ra) = nullptr;
|
bool (*forgLoopTableIter)(lua_State* L, LuaTable* h, int index, TValue* ra) = nullptr;
|
||||||
bool (*forgLoopNodeIter)(lua_State* L, Table* h, int index, TValue* ra) = nullptr;
|
bool (*forgLoopNodeIter)(lua_State* L, LuaTable* h, int index, TValue* ra) = nullptr;
|
||||||
bool (*forgLoopNonTableFallback)(lua_State* L, int insnA, int aux) = nullptr;
|
bool (*forgLoopNonTableFallback)(lua_State* L, int insnA, int aux) = nullptr;
|
||||||
void (*forgPrepXnextFallback)(lua_State* L, TValue* ra, int pc) = nullptr;
|
void (*forgPrepXnextFallback)(lua_State* L, TValue* ra, int pc) = nullptr;
|
||||||
Closure* (*callProlog)(lua_State* L, TValue* ra, StkId argtop, int nresults) = nullptr;
|
Closure* (*callProlog)(lua_State* L, TValue* ra, StkId argtop, int nresults) = nullptr;
|
||||||
|
|
|
@ -78,6 +78,7 @@ target_sources(Luau.CodeGen PRIVATE
|
||||||
CodeGen/include/Luau/CodeBlockUnwind.h
|
CodeGen/include/Luau/CodeBlockUnwind.h
|
||||||
CodeGen/include/Luau/CodeGen.h
|
CodeGen/include/Luau/CodeGen.h
|
||||||
CodeGen/include/Luau/CodeGenCommon.h
|
CodeGen/include/Luau/CodeGenCommon.h
|
||||||
|
CodeGen/include/Luau/CodeGenOptions.h
|
||||||
CodeGen/include/Luau/ConditionA64.h
|
CodeGen/include/Luau/ConditionA64.h
|
||||||
CodeGen/include/Luau/ConditionX64.h
|
CodeGen/include/Luau/ConditionX64.h
|
||||||
CodeGen/include/Luau/IrAnalysis.h
|
CodeGen/include/Luau/IrAnalysis.h
|
||||||
|
@ -89,6 +90,7 @@ target_sources(Luau.CodeGen PRIVATE
|
||||||
CodeGen/include/Luau/IrUtils.h
|
CodeGen/include/Luau/IrUtils.h
|
||||||
CodeGen/include/Luau/IrVisitUseDef.h
|
CodeGen/include/Luau/IrVisitUseDef.h
|
||||||
CodeGen/include/Luau/Label.h
|
CodeGen/include/Luau/Label.h
|
||||||
|
CodeGen/include/Luau/LoweringStats.h
|
||||||
CodeGen/include/Luau/NativeProtoExecData.h
|
CodeGen/include/Luau/NativeProtoExecData.h
|
||||||
CodeGen/include/Luau/OperandX64.h
|
CodeGen/include/Luau/OperandX64.h
|
||||||
CodeGen/include/Luau/OptimizeConstProp.h
|
CodeGen/include/Luau/OptimizeConstProp.h
|
||||||
|
|
|
@ -64,7 +64,7 @@ const char* luau_ident = "$Luau: Copyright (C) 2019-2024 Roblox Corporation $\n"
|
||||||
ts->atom = L->global->cb.useratom ? L->global->cb.useratom(ts->data, ts->len) : -1; \
|
ts->atom = L->global->cb.useratom ? L->global->cb.useratom(ts->data, ts->len) : -1; \
|
||||||
}
|
}
|
||||||
|
|
||||||
static Table* getcurrenv(lua_State* L)
|
static LuaTable* 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
|
||||||
|
@ -762,7 +762,7 @@ void lua_setreadonly(lua_State* L, int objindex, int enabled)
|
||||||
{
|
{
|
||||||
const TValue* o = index2addr(L, objindex);
|
const TValue* o = index2addr(L, objindex);
|
||||||
api_check(L, ttistable(o));
|
api_check(L, ttistable(o));
|
||||||
Table* t = hvalue(o);
|
LuaTable* t = hvalue(o);
|
||||||
api_check(L, t != hvalue(registry(L)));
|
api_check(L, t != hvalue(registry(L)));
|
||||||
t->readonly = bool(enabled);
|
t->readonly = bool(enabled);
|
||||||
}
|
}
|
||||||
|
@ -771,7 +771,7 @@ int lua_getreadonly(lua_State* L, int objindex)
|
||||||
{
|
{
|
||||||
const TValue* o = index2addr(L, objindex);
|
const TValue* o = index2addr(L, objindex);
|
||||||
api_check(L, ttistable(o));
|
api_check(L, ttistable(o));
|
||||||
Table* t = hvalue(o);
|
LuaTable* t = hvalue(o);
|
||||||
int res = t->readonly;
|
int res = t->readonly;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -780,14 +780,14 @@ void lua_setsafeenv(lua_State* L, int objindex, int enabled)
|
||||||
{
|
{
|
||||||
const TValue* o = index2addr(L, objindex);
|
const TValue* o = index2addr(L, objindex);
|
||||||
api_check(L, ttistable(o));
|
api_check(L, ttistable(o));
|
||||||
Table* t = hvalue(o);
|
LuaTable* t = hvalue(o);
|
||||||
t->safeenv = bool(enabled);
|
t->safeenv = bool(enabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
int lua_getmetatable(lua_State* L, int objindex)
|
int lua_getmetatable(lua_State* L, int objindex)
|
||||||
{
|
{
|
||||||
luaC_threadbarrier(L);
|
luaC_threadbarrier(L);
|
||||||
Table* mt = NULL;
|
LuaTable* mt = NULL;
|
||||||
const TValue* obj = index2addr(L, objindex);
|
const TValue* obj = index2addr(L, objindex);
|
||||||
switch (ttype(obj))
|
switch (ttype(obj))
|
||||||
{
|
{
|
||||||
|
@ -894,7 +894,7 @@ int lua_setmetatable(lua_State* L, int objindex)
|
||||||
api_checknelems(L, 1);
|
api_checknelems(L, 1);
|
||||||
TValue* obj = index2addr(L, objindex);
|
TValue* obj = index2addr(L, objindex);
|
||||||
api_checkvalidindex(L, obj);
|
api_checkvalidindex(L, obj);
|
||||||
Table* mt = NULL;
|
LuaTable* mt = NULL;
|
||||||
if (!ttisnil(L->top - 1))
|
if (!ttisnil(L->top - 1))
|
||||||
{
|
{
|
||||||
api_check(L, ttistable(L->top - 1));
|
api_check(L, ttistable(L->top - 1));
|
||||||
|
@ -1214,7 +1214,7 @@ int lua_rawiter(lua_State* L, int idx, int iter)
|
||||||
api_check(L, ttistable(t));
|
api_check(L, ttistable(t));
|
||||||
api_check(L, iter >= 0);
|
api_check(L, iter >= 0);
|
||||||
|
|
||||||
Table* h = hvalue(t);
|
LuaTable* h = hvalue(t);
|
||||||
int sizearray = h->sizearray;
|
int sizearray = h->sizearray;
|
||||||
|
|
||||||
// first we advance iter through the array portion
|
// first we advance iter through the array portion
|
||||||
|
@ -1293,7 +1293,7 @@ void* lua_newuserdatataggedwithmetatable(lua_State* L, size_t sz, int tag)
|
||||||
// currently, we always allocate unmarked objects, so forward barrier can be skipped
|
// currently, we always allocate unmarked objects, so forward barrier can be skipped
|
||||||
LUAU_ASSERT(!isblack(obj2gco(u)));
|
LUAU_ASSERT(!isblack(obj2gco(u)));
|
||||||
|
|
||||||
Table* h = L->global->udatamt[tag];
|
LuaTable* h = L->global->udatamt[tag];
|
||||||
api_check(L, h != nullptr);
|
api_check(L, h != nullptr);
|
||||||
|
|
||||||
u->metatable = h;
|
u->metatable = h;
|
||||||
|
@ -1394,7 +1394,7 @@ int lua_ref(lua_State* L, int idx)
|
||||||
StkId p = index2addr(L, idx);
|
StkId p = index2addr(L, idx);
|
||||||
if (!ttisnil(p))
|
if (!ttisnil(p))
|
||||||
{
|
{
|
||||||
Table* reg = hvalue(registry(L));
|
LuaTable* reg = hvalue(registry(L));
|
||||||
|
|
||||||
if (g->registryfree != 0)
|
if (g->registryfree != 0)
|
||||||
{ // reuse existing slot
|
{ // reuse existing slot
|
||||||
|
@ -1421,7 +1421,7 @@ void lua_unref(lua_State* L, int ref)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
global_State* g = L->global;
|
global_State* g = L->global;
|
||||||
Table* reg = hvalue(registry(L));
|
LuaTable* reg = hvalue(registry(L));
|
||||||
TValue* slot = luaH_setnum(L, reg, ref);
|
TValue* slot = luaH_setnum(L, reg, ref);
|
||||||
setnvalue(slot, g->registryfree); // NB: no barrier needed because value isn't collectable
|
setnvalue(slot, g->registryfree); // NB: no barrier needed because value isn't collectable
|
||||||
g->registryfree = ref;
|
g->registryfree = ref;
|
||||||
|
@ -1462,7 +1462,7 @@ void lua_getuserdatametatable(lua_State* L, int tag)
|
||||||
api_check(L, unsigned(tag) < LUA_UTAG_LIMIT);
|
api_check(L, unsigned(tag) < LUA_UTAG_LIMIT);
|
||||||
luaC_threadbarrier(L);
|
luaC_threadbarrier(L);
|
||||||
|
|
||||||
if (Table* h = L->global->udatamt[tag])
|
if (LuaTable* h = L->global->udatamt[tag])
|
||||||
{
|
{
|
||||||
sethvalue(L, L->top, h);
|
sethvalue(L, L->top, h);
|
||||||
}
|
}
|
||||||
|
@ -1510,7 +1510,7 @@ void lua_cleartable(lua_State* L, int idx)
|
||||||
{
|
{
|
||||||
StkId t = index2addr(L, idx);
|
StkId t = index2addr(L, idx);
|
||||||
api_check(L, ttistable(t));
|
api_check(L, ttistable(t));
|
||||||
Table* tt = hvalue(t);
|
LuaTable* tt = hvalue(t);
|
||||||
if (tt->readonly)
|
if (tt->readonly)
|
||||||
luaG_readonlyerror(L);
|
luaG_readonlyerror(L);
|
||||||
luaH_clear(tt);
|
luaH_clear(tt);
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
LUAU_FASTFLAGVARIABLE(LuauBufferBitMethods)
|
LUAU_FASTFLAGVARIABLE(LuauBufferBitMethods2)
|
||||||
|
|
||||||
// while C API returns 'size_t' for binary compatibility in case of future extensions,
|
// while C API returns 'size_t' for binary compatibility in case of future extensions,
|
||||||
// in the current implementation, length and offset are limited to 31 bits
|
// in the current implementation, length and offset are limited to 31 bits
|
||||||
|
@ -370,7 +370,7 @@ static const luaL_Reg bufferlib[] = {
|
||||||
|
|
||||||
int luaopen_buffer(lua_State* L)
|
int luaopen_buffer(lua_State* L)
|
||||||
{
|
{
|
||||||
luaL_register(L, LUA_BUFFERLIBNAME, FFlag::LuauBufferBitMethods ? bufferlib : bufferlib_DEPRECATED);
|
luaL_register(L, LUA_BUFFERLIBNAME, FFlag::LuauBufferBitMethods2 ? bufferlib : bufferlib_DEPRECATED);
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1000,7 +1000,7 @@ static int luauF_rawset(lua_State* L, StkId res, TValue* arg0, int nresults, Stk
|
||||||
else if (ttisvector(key) && luai_vecisnan(vvalue(key)))
|
else if (ttisvector(key) && luai_vecisnan(vvalue(key)))
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
Table* t = hvalue(arg0);
|
LuaTable* t = hvalue(arg0);
|
||||||
if (t->readonly)
|
if (t->readonly)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -1017,7 +1017,7 @@ static int luauF_tinsert(lua_State* L, StkId res, TValue* arg0, int nresults, St
|
||||||
{
|
{
|
||||||
if (nparams == 2 && nresults <= 0 && ttistable(arg0))
|
if (nparams == 2 && nresults <= 0 && ttistable(arg0))
|
||||||
{
|
{
|
||||||
Table* t = hvalue(arg0);
|
LuaTable* t = hvalue(arg0);
|
||||||
if (t->readonly)
|
if (t->readonly)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
@ -1034,7 +1034,7 @@ static int luauF_tunpack(lua_State* L, StkId res, TValue* arg0, int nresults, St
|
||||||
{
|
{
|
||||||
if (nparams >= 1 && nresults < 0 && ttistable(arg0))
|
if (nparams >= 1 && nresults < 0 && ttistable(arg0))
|
||||||
{
|
{
|
||||||
Table* t = hvalue(arg0);
|
LuaTable* t = hvalue(arg0);
|
||||||
int n = -1;
|
int n = -1;
|
||||||
|
|
||||||
if (nparams == 1)
|
if (nparams == 1)
|
||||||
|
@ -1196,7 +1196,7 @@ static int luauF_rawlen(lua_State* L, StkId res, TValue* arg0, int nresults, Stk
|
||||||
{
|
{
|
||||||
if (ttistable(arg0))
|
if (ttistable(arg0))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(arg0);
|
LuaTable* h = hvalue(arg0);
|
||||||
setnvalue(res, double(luaH_getn(h)));
|
setnvalue(res, double(luaH_getn(h)));
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
@ -1240,7 +1240,7 @@ static int luauF_getmetatable(lua_State* L, StkId res, TValue* arg0, int nresult
|
||||||
{
|
{
|
||||||
if (nparams >= 1 && nresults <= 1)
|
if (nparams >= 1 && nresults <= 1)
|
||||||
{
|
{
|
||||||
Table* mt = NULL;
|
LuaTable* mt = NULL;
|
||||||
if (ttistable(arg0))
|
if (ttistable(arg0))
|
||||||
mt = hvalue(arg0)->metatable;
|
mt = hvalue(arg0)->metatable;
|
||||||
else if (ttisuserdata(arg0))
|
else if (ttisuserdata(arg0))
|
||||||
|
@ -1275,11 +1275,11 @@ static int luauF_setmetatable(lua_State* L, StkId res, TValue* arg0, int nresult
|
||||||
// note: setmetatable(_, nil) is rare so we use fallback for it to optimize the fast path
|
// note: setmetatable(_, nil) is rare so we use fallback for it to optimize the fast path
|
||||||
if (nparams >= 2 && nresults <= 1 && ttistable(arg0) && ttistable(args))
|
if (nparams >= 2 && nresults <= 1 && ttistable(arg0) && ttistable(args))
|
||||||
{
|
{
|
||||||
Table* t = hvalue(arg0);
|
LuaTable* t = hvalue(arg0);
|
||||||
if (t->readonly || t->metatable != NULL)
|
if (t->readonly || t->metatable != NULL)
|
||||||
return -1; // note: overwriting non-null metatable is very rare but it requires __metatable check
|
return -1; // note: overwriting non-null metatable is very rare but it requires __metatable check
|
||||||
|
|
||||||
Table* mt = hvalue(args);
|
LuaTable* mt = hvalue(args);
|
||||||
t->metatable = mt;
|
t->metatable = mt;
|
||||||
luaC_objbarrier(L, t, mt);
|
luaC_objbarrier(L, t, mt);
|
||||||
|
|
||||||
|
|
|
@ -55,7 +55,7 @@ Proto* luaF_newproto(lua_State* L)
|
||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
Closure* luaF_newLclosure(lua_State* L, int nelems, Table* e, Proto* p)
|
Closure* luaF_newLclosure(lua_State* L, int nelems, LuaTable* e, Proto* p)
|
||||||
{
|
{
|
||||||
Closure* c = luaM_newgco(L, Closure, sizeLclosure(nelems), L->activememcat);
|
Closure* c = luaM_newgco(L, Closure, sizeLclosure(nelems), L->activememcat);
|
||||||
luaC_init(L, c, LUA_TFUNCTION);
|
luaC_init(L, c, LUA_TFUNCTION);
|
||||||
|
@ -70,7 +70,7 @@ Closure* luaF_newLclosure(lua_State* L, int nelems, Table* e, Proto* p)
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
Closure* luaF_newCclosure(lua_State* L, int nelems, Table* e)
|
Closure* luaF_newCclosure(lua_State* L, int nelems, LuaTable* e)
|
||||||
{
|
{
|
||||||
Closure* c = luaM_newgco(L, Closure, sizeCclosure(nelems), L->activememcat);
|
Closure* c = luaM_newgco(L, Closure, sizeCclosure(nelems), L->activememcat);
|
||||||
luaC_init(L, c, LUA_TFUNCTION);
|
luaC_init(L, c, LUA_TFUNCTION);
|
||||||
|
|
|
@ -8,8 +8,8 @@
|
||||||
#define sizeLclosure(n) (offsetof(Closure, l.uprefs) + sizeof(TValue) * (n))
|
#define sizeLclosure(n) (offsetof(Closure, l.uprefs) + sizeof(TValue) * (n))
|
||||||
|
|
||||||
LUAI_FUNC Proto* luaF_newproto(lua_State* L);
|
LUAI_FUNC Proto* luaF_newproto(lua_State* L);
|
||||||
LUAI_FUNC Closure* luaF_newLclosure(lua_State* L, int nelems, Table* e, Proto* p);
|
LUAI_FUNC Closure* luaF_newLclosure(lua_State* L, int nelems, LuaTable* e, Proto* p);
|
||||||
LUAI_FUNC Closure* luaF_newCclosure(lua_State* L, int nelems, Table* e);
|
LUAI_FUNC Closure* luaF_newCclosure(lua_State* L, int nelems, LuaTable* e);
|
||||||
LUAI_FUNC UpVal* luaF_findupval(lua_State* L, StkId level);
|
LUAI_FUNC UpVal* luaF_findupval(lua_State* L, StkId level);
|
||||||
LUAI_FUNC void luaF_close(lua_State* L, StkId level);
|
LUAI_FUNC void luaF_close(lua_State* L, StkId level);
|
||||||
LUAI_FUNC void luaF_closeupval(lua_State* L, UpVal* uv, bool dead);
|
LUAI_FUNC void luaF_closeupval(lua_State* L, UpVal* uv, bool dead);
|
||||||
|
|
|
@ -244,7 +244,7 @@ static void reallymarkobject(global_State* g, GCObject* o)
|
||||||
}
|
}
|
||||||
case LUA_TUSERDATA:
|
case LUA_TUSERDATA:
|
||||||
{
|
{
|
||||||
Table* mt = gco2u(o)->metatable;
|
LuaTable* mt = gco2u(o)->metatable;
|
||||||
gray2black(o); // udata are never gray
|
gray2black(o); // udata are never gray
|
||||||
if (mt)
|
if (mt)
|
||||||
markobject(g, mt);
|
markobject(g, mt);
|
||||||
|
@ -292,7 +292,7 @@ static void reallymarkobject(global_State* g, GCObject* o)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* gettablemode(global_State* g, Table* h)
|
static const char* gettablemode(global_State* g, LuaTable* h)
|
||||||
{
|
{
|
||||||
const TValue* mode = gfasttm(g, h->metatable, TM_MODE);
|
const TValue* mode = gfasttm(g, h->metatable, TM_MODE);
|
||||||
|
|
||||||
|
@ -302,13 +302,13 @@ static const char* gettablemode(global_State* g, Table* h)
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int traversetable(global_State* g, Table* h)
|
static int traversetable(global_State* g, LuaTable* h)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
int weakkey = 0;
|
int weakkey = 0;
|
||||||
int weakvalue = 0;
|
int weakvalue = 0;
|
||||||
if (h->metatable)
|
if (h->metatable)
|
||||||
markobject(g, cast_to(Table*, h->metatable));
|
markobject(g, cast_to(LuaTable*, h->metatable));
|
||||||
|
|
||||||
// is there a weak mode?
|
// is there a weak mode?
|
||||||
if (const char* modev = gettablemode(g, h))
|
if (const char* modev = gettablemode(g, h))
|
||||||
|
@ -459,11 +459,11 @@ static size_t propagatemark(global_State* g)
|
||||||
{
|
{
|
||||||
case LUA_TTABLE:
|
case LUA_TTABLE:
|
||||||
{
|
{
|
||||||
Table* h = gco2h(o);
|
LuaTable* h = gco2h(o);
|
||||||
g->gray = h->gclist;
|
g->gray = h->gclist;
|
||||||
if (traversetable(g, h)) // table is weak?
|
if (traversetable(g, h)) // table is weak?
|
||||||
black2gray(o); // keep it gray
|
black2gray(o); // keep it gray
|
||||||
return sizeof(Table) + sizeof(TValue) * h->sizearray + sizeof(LuaNode) * sizenode(h);
|
return sizeof(LuaTable) + sizeof(TValue) * h->sizearray + sizeof(LuaNode) * sizenode(h);
|
||||||
}
|
}
|
||||||
case LUA_TFUNCTION:
|
case LUA_TFUNCTION:
|
||||||
{
|
{
|
||||||
|
@ -553,8 +553,8 @@ static size_t cleartable(lua_State* L, GCObject* l)
|
||||||
size_t work = 0;
|
size_t work = 0;
|
||||||
while (l)
|
while (l)
|
||||||
{
|
{
|
||||||
Table* h = gco2h(l);
|
LuaTable* h = gco2h(l);
|
||||||
work += sizeof(Table) + sizeof(TValue) * h->sizearray + sizeof(LuaNode) * sizenode(h);
|
work += sizeof(LuaTable) + sizeof(TValue) * h->sizearray + sizeof(LuaNode) * sizenode(h);
|
||||||
|
|
||||||
int i = h->sizearray;
|
int i = h->sizearray;
|
||||||
while (i--)
|
while (i--)
|
||||||
|
@ -1155,7 +1155,7 @@ void luaC_barrierf(lua_State* L, GCObject* o, GCObject* v)
|
||||||
makewhite(g, o); // mark as white just to avoid other barriers
|
makewhite(g, o); // mark as white just to avoid other barriers
|
||||||
}
|
}
|
||||||
|
|
||||||
void luaC_barriertable(lua_State* L, Table* t, GCObject* v)
|
void luaC_barriertable(lua_State* L, LuaTable* t, GCObject* v)
|
||||||
{
|
{
|
||||||
global_State* g = L->global;
|
global_State* g = L->global;
|
||||||
GCObject* o = obj2gco(t);
|
GCObject* o = obj2gco(t);
|
||||||
|
|
|
@ -131,7 +131,7 @@ LUAI_FUNC void luaC_fullgc(lua_State* L);
|
||||||
LUAI_FUNC void luaC_initobj(lua_State* L, GCObject* o, uint8_t tt);
|
LUAI_FUNC void luaC_initobj(lua_State* L, GCObject* o, uint8_t tt);
|
||||||
LUAI_FUNC void luaC_upvalclosed(lua_State* L, UpVal* uv);
|
LUAI_FUNC void luaC_upvalclosed(lua_State* L, UpVal* uv);
|
||||||
LUAI_FUNC void luaC_barrierf(lua_State* L, GCObject* o, GCObject* v);
|
LUAI_FUNC void luaC_barrierf(lua_State* L, GCObject* o, GCObject* v);
|
||||||
LUAI_FUNC void luaC_barriertable(lua_State* L, Table* t, GCObject* v);
|
LUAI_FUNC void luaC_barriertable(lua_State* L, LuaTable* t, GCObject* v);
|
||||||
LUAI_FUNC void luaC_barrierback(lua_State* L, GCObject* o, GCObject** gclist);
|
LUAI_FUNC void luaC_barrierback(lua_State* L, GCObject* o, GCObject** gclist);
|
||||||
LUAI_FUNC void luaC_validate(lua_State* L);
|
LUAI_FUNC void luaC_validate(lua_State* L);
|
||||||
LUAI_FUNC void luaC_dump(lua_State* L, void* file, const char* (*categoryName)(lua_State* L, uint8_t memcat));
|
LUAI_FUNC void luaC_dump(lua_State* L, void* file, const char* (*categoryName)(lua_State* L, uint8_t memcat));
|
||||||
|
|
|
@ -34,7 +34,7 @@ static void validateref(global_State* g, GCObject* f, TValue* v)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void validatetable(global_State* g, Table* h)
|
static void validatetable(global_State* g, LuaTable* h)
|
||||||
{
|
{
|
||||||
int sizenode = 1 << h->lsizenode;
|
int sizenode = 1 << h->lsizenode;
|
||||||
|
|
||||||
|
@ -290,9 +290,9 @@ static void dumpstring(FILE* f, TString* ts)
|
||||||
fprintf(f, "\"}");
|
fprintf(f, "\"}");
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dumptable(FILE* f, Table* h)
|
static void dumptable(FILE* f, LuaTable* h)
|
||||||
{
|
{
|
||||||
size_t size = sizeof(Table) + (h->node == &luaH_dummynode ? 0 : sizenode(h) * sizeof(LuaNode)) + h->sizearray * sizeof(TValue);
|
size_t size = sizeof(LuaTable) + (h->node == &luaH_dummynode ? 0 : sizenode(h) * sizeof(LuaNode)) + h->sizearray * sizeof(TValue);
|
||||||
|
|
||||||
fprintf(f, "{\"type\":\"table\",\"cat\":%d,\"size\":%d", h->memcat, int(size));
|
fprintf(f, "{\"type\":\"table\",\"cat\":%d,\"size\":%d", h->memcat, int(size));
|
||||||
|
|
||||||
|
@ -654,9 +654,9 @@ static void enumstring(EnumContext* ctx, TString* ts)
|
||||||
enumnode(ctx, obj2gco(ts), ts->len, NULL);
|
enumnode(ctx, obj2gco(ts), ts->len, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void enumtable(EnumContext* ctx, Table* h)
|
static void enumtable(EnumContext* ctx, LuaTable* h)
|
||||||
{
|
{
|
||||||
size_t size = sizeof(Table) + (h->node == &luaH_dummynode ? 0 : sizenode(h) * sizeof(LuaNode)) + h->sizearray * sizeof(TValue);
|
size_t size = sizeof(LuaTable) + (h->node == &luaH_dummynode ? 0 : sizenode(h) * sizeof(LuaNode)) + h->sizearray * sizeof(TValue);
|
||||||
|
|
||||||
// Provide a name for a special registry table
|
// Provide a name for a special registry table
|
||||||
enumnode(ctx, obj2gco(h), size, h == hvalue(registry(ctx->L)) ? "registry" : NULL);
|
enumnode(ctx, obj2gco(h), size, h == hvalue(registry(ctx->L)) ? "registry" : NULL);
|
||||||
|
@ -754,7 +754,7 @@ static void enumudata(EnumContext* ctx, Udata* u)
|
||||||
{
|
{
|
||||||
const char* name = NULL;
|
const char* name = NULL;
|
||||||
|
|
||||||
if (Table* h = u->metatable)
|
if (LuaTable* h = u->metatable)
|
||||||
{
|
{
|
||||||
if (h->node != &luaH_dummynode)
|
if (h->node != &luaH_dummynode)
|
||||||
{
|
{
|
||||||
|
|
|
@ -121,7 +121,7 @@ static_assert(sizeof(LuaNode) == ABISWITCH(32, 32, 32), "size mismatch for table
|
||||||
|
|
||||||
static_assert(offsetof(TString, data) == ABISWITCH(24, 20, 20), "size mismatch for string header");
|
static_assert(offsetof(TString, data) == ABISWITCH(24, 20, 20), "size mismatch for string header");
|
||||||
static_assert(offsetof(Udata, data) == ABISWITCH(16, 16, 12), "size mismatch for userdata header");
|
static_assert(offsetof(Udata, data) == ABISWITCH(16, 16, 12), "size mismatch for userdata header");
|
||||||
static_assert(sizeof(Table) == ABISWITCH(48, 32, 32), "size mismatch for table header");
|
static_assert(sizeof(LuaTable) == ABISWITCH(48, 32, 32), "size mismatch for table header");
|
||||||
static_assert(offsetof(Buffer, data) == ABISWITCH(8, 8, 8), "size mismatch for buffer header");
|
static_assert(offsetof(Buffer, data) == ABISWITCH(8, 8, 8), "size mismatch for buffer header");
|
||||||
|
|
||||||
const size_t kSizeClasses = LUA_SIZECLASSES;
|
const size_t kSizeClasses = LUA_SIZECLASSES;
|
||||||
|
|
|
@ -263,7 +263,7 @@ typedef struct Udata
|
||||||
|
|
||||||
int len;
|
int len;
|
||||||
|
|
||||||
struct Table* metatable;
|
struct LuaTable* metatable;
|
||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
|
@ -390,7 +390,7 @@ typedef struct Closure
|
||||||
uint8_t preload;
|
uint8_t preload;
|
||||||
|
|
||||||
GCObject* gclist;
|
GCObject* gclist;
|
||||||
struct Table* env;
|
struct LuaTable* env;
|
||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
|
@ -454,7 +454,7 @@ typedef struct LuaNode
|
||||||
}
|
}
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
typedef struct Table
|
typedef struct LuaTable
|
||||||
{
|
{
|
||||||
CommonHeader;
|
CommonHeader;
|
||||||
|
|
||||||
|
@ -473,11 +473,11 @@ typedef struct Table
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
struct Table* metatable;
|
struct LuaTable* metatable;
|
||||||
TValue* array; // array part
|
TValue* array; // array part
|
||||||
LuaNode* node;
|
LuaNode* node;
|
||||||
GCObject* gclist;
|
GCObject* gclist;
|
||||||
} Table;
|
} LuaTable;
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -198,7 +198,7 @@ typedef struct global_State
|
||||||
|
|
||||||
struct lua_State* mainthread;
|
struct lua_State* mainthread;
|
||||||
UpVal uvhead; // head of double-linked list of all open upvalues
|
UpVal uvhead; // head of double-linked list of all open upvalues
|
||||||
struct Table* mt[LUA_T_COUNT]; // metatables for basic types
|
struct LuaTable* mt[LUA_T_COUNT]; // metatables for basic types
|
||||||
TString* ttname[LUA_T_COUNT]; // names for basic types
|
TString* ttname[LUA_T_COUNT]; // names for basic types
|
||||||
TString* tmname[TM_N]; // array with tag-method names
|
TString* tmname[TM_N]; // array with tag-method names
|
||||||
|
|
||||||
|
@ -217,7 +217,7 @@ typedef struct global_State
|
||||||
lua_ExecutionCallbacks ecb;
|
lua_ExecutionCallbacks ecb;
|
||||||
|
|
||||||
void (*udatagc[LUA_UTAG_LIMIT])(lua_State*, void*); // for each userdata tag, a gc callback to be called immediately before freeing memory
|
void (*udatagc[LUA_UTAG_LIMIT])(lua_State*, void*); // for each userdata tag, a gc callback to be called immediately before freeing memory
|
||||||
Table* udatamt[LUA_UTAG_LIMIT]; // metatables for tagged userdata
|
LuaTable* udatamt[LUA_UTAG_LIMIT]; // metatables for tagged userdata
|
||||||
|
|
||||||
TString* lightuserdataname[LUA_LUTAG_LIMIT]; // names for tagged lightuserdata
|
TString* lightuserdataname[LUA_LUTAG_LIMIT]; // names for tagged lightuserdata
|
||||||
|
|
||||||
|
@ -266,7 +266,7 @@ struct lua_State
|
||||||
int cachedslot; // when table operations or INDEX/NEWINDEX is invoked from Luau, what is the expected slot for lookup?
|
int cachedslot; // when table operations or INDEX/NEWINDEX is invoked from Luau, what is the expected slot for lookup?
|
||||||
|
|
||||||
|
|
||||||
Table* gt; // table of globals
|
LuaTable* gt; // table of globals
|
||||||
UpVal* openupval; // list of open upvalues in this stack
|
UpVal* openupval; // list of open upvalues in this stack
|
||||||
GCObject* gclist;
|
GCObject* gclist;
|
||||||
|
|
||||||
|
@ -285,7 +285,7 @@ union GCObject
|
||||||
struct TString ts;
|
struct TString ts;
|
||||||
struct Udata u;
|
struct Udata u;
|
||||||
struct Closure cl;
|
struct Closure cl;
|
||||||
struct Table h;
|
struct LuaTable h;
|
||||||
struct Proto p;
|
struct Proto p;
|
||||||
struct UpVal uv;
|
struct UpVal uv;
|
||||||
struct lua_State th; // thread
|
struct lua_State th; // thread
|
||||||
|
|
|
@ -58,7 +58,7 @@ const LuaNode luaH_dummynode = {
|
||||||
#define hashstr(t, str) hashpow2(t, (str)->hash)
|
#define hashstr(t, str) hashpow2(t, (str)->hash)
|
||||||
#define hashboolean(t, p) hashpow2(t, p)
|
#define hashboolean(t, p) hashpow2(t, p)
|
||||||
|
|
||||||
static LuaNode* hashpointer(const Table* t, const void* p)
|
static LuaNode* hashpointer(const LuaTable* t, const void* p)
|
||||||
{
|
{
|
||||||
// we discard the high 32-bit portion of the pointer on 64-bit platforms as it doesn't carry much entropy anyway
|
// we discard the high 32-bit portion of the pointer on 64-bit platforms as it doesn't carry much entropy anyway
|
||||||
unsigned int h = unsigned(uintptr_t(p));
|
unsigned int h = unsigned(uintptr_t(p));
|
||||||
|
@ -73,7 +73,7 @@ static LuaNode* hashpointer(const Table* t, const void* p)
|
||||||
return hashpow2(t, h);
|
return hashpow2(t, h);
|
||||||
}
|
}
|
||||||
|
|
||||||
static LuaNode* hashnum(const Table* t, double n)
|
static LuaNode* hashnum(const LuaTable* t, double n)
|
||||||
{
|
{
|
||||||
static_assert(sizeof(double) == sizeof(unsigned int) * 2, "expected a 8-byte double");
|
static_assert(sizeof(double) == sizeof(unsigned int) * 2, "expected a 8-byte double");
|
||||||
unsigned int i[2];
|
unsigned int i[2];
|
||||||
|
@ -99,7 +99,7 @@ static LuaNode* hashnum(const Table* t, double n)
|
||||||
return hashpow2(t, h2);
|
return hashpow2(t, h2);
|
||||||
}
|
}
|
||||||
|
|
||||||
static LuaNode* hashvec(const Table* t, const float* v)
|
static LuaNode* hashvec(const LuaTable* t, const float* v)
|
||||||
{
|
{
|
||||||
unsigned int i[LUA_VECTOR_SIZE];
|
unsigned int i[LUA_VECTOR_SIZE];
|
||||||
memcpy(i, v, sizeof(i));
|
memcpy(i, v, sizeof(i));
|
||||||
|
@ -130,7 +130,7 @@ static LuaNode* hashvec(const Table* t, const float* v)
|
||||||
** returns the `main' position of an element in a table (that is, the index
|
** returns the `main' position of an element in a table (that is, the index
|
||||||
** of its hash value)
|
** of its hash value)
|
||||||
*/
|
*/
|
||||||
static LuaNode* mainposition(const Table* t, const TValue* key)
|
static LuaNode* mainposition(const LuaTable* t, const TValue* key)
|
||||||
{
|
{
|
||||||
switch (ttype(key))
|
switch (ttype(key))
|
||||||
{
|
{
|
||||||
|
@ -166,7 +166,7 @@ static int arrayindex(double key)
|
||||||
** elements in the array part, then elements in the hash part. The
|
** elements in the array part, then elements in the hash part. The
|
||||||
** beginning of a traversal is signalled by -1.
|
** beginning of a traversal is signalled by -1.
|
||||||
*/
|
*/
|
||||||
static int findindex(lua_State* L, Table* t, StkId key)
|
static int findindex(lua_State* L, LuaTable* t, StkId key)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
if (ttisnil(key))
|
if (ttisnil(key))
|
||||||
|
@ -194,7 +194,7 @@ static int findindex(lua_State* L, Table* t, StkId key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int luaH_next(lua_State* L, Table* t, StkId key)
|
int luaH_next(lua_State* L, LuaTable* t, StkId key)
|
||||||
{
|
{
|
||||||
int i = findindex(L, t, key); // find original element
|
int i = findindex(L, t, key); // find original element
|
||||||
for (i++; i < t->sizearray; i++)
|
for (i++; i < t->sizearray; i++)
|
||||||
|
@ -270,7 +270,7 @@ static int countint(double key, int* nums)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int numusearray(const Table* t, int* nums)
|
static int numusearray(const LuaTable* t, int* nums)
|
||||||
{
|
{
|
||||||
int lg;
|
int lg;
|
||||||
int ttlg; // 2^lg
|
int ttlg; // 2^lg
|
||||||
|
@ -298,7 +298,7 @@ static int numusearray(const Table* t, int* nums)
|
||||||
return ause;
|
return ause;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int numusehash(const Table* t, int* nums, int* pnasize)
|
static int numusehash(const LuaTable* t, int* nums, int* pnasize)
|
||||||
{
|
{
|
||||||
int totaluse = 0; // total number of elements
|
int totaluse = 0; // total number of elements
|
||||||
int ause = 0; // summation of `nums'
|
int ause = 0; // summation of `nums'
|
||||||
|
@ -317,7 +317,7 @@ static int numusehash(const Table* t, int* nums, int* pnasize)
|
||||||
return totaluse;
|
return totaluse;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setarrayvector(lua_State* L, Table* t, int size)
|
static void setarrayvector(lua_State* L, LuaTable* t, int size)
|
||||||
{
|
{
|
||||||
if (size > MAXSIZE)
|
if (size > MAXSIZE)
|
||||||
luaG_runerror(L, "table overflow");
|
luaG_runerror(L, "table overflow");
|
||||||
|
@ -328,7 +328,7 @@ static void setarrayvector(lua_State* L, Table* t, int size)
|
||||||
t->sizearray = size;
|
t->sizearray = size;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setnodevector(lua_State* L, Table* t, int size)
|
static void setnodevector(lua_State* L, LuaTable* t, int size)
|
||||||
{
|
{
|
||||||
int lsize;
|
int lsize;
|
||||||
if (size == 0)
|
if (size == 0)
|
||||||
|
@ -357,9 +357,9 @@ 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* newkey(lua_State* L, LuaTable* t, const TValue* key);
|
||||||
|
|
||||||
static TValue* arrayornewkey(lua_State* L, Table* t, const TValue* key)
|
static TValue* arrayornewkey(lua_State* L, LuaTable* t, const TValue* key)
|
||||||
{
|
{
|
||||||
if (ttisnumber(key))
|
if (ttisnumber(key))
|
||||||
{
|
{
|
||||||
|
@ -373,7 +373,7 @@ static TValue* arrayornewkey(lua_State* L, Table* t, const TValue* key)
|
||||||
return newkey(L, t, key);
|
return newkey(L, t, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resize(lua_State* L, Table* t, int nasize, int nhsize)
|
static void resize(lua_State* L, LuaTable* t, int nasize, int nhsize)
|
||||||
{
|
{
|
||||||
if (nasize > MAXSIZE || nhsize > MAXSIZE)
|
if (nasize > MAXSIZE || nhsize > MAXSIZE)
|
||||||
luaG_runerror(L, "table overflow");
|
luaG_runerror(L, "table overflow");
|
||||||
|
@ -424,7 +424,7 @@ static void resize(lua_State* L, Table* t, int nasize, int nhsize)
|
||||||
luaM_freearray(L, nold, twoto(oldhsize), LuaNode, t->memcat); // free old array
|
luaM_freearray(L, nold, twoto(oldhsize), LuaNode, t->memcat); // free old array
|
||||||
}
|
}
|
||||||
|
|
||||||
static int adjustasize(Table* t, int size, const TValue* ek)
|
static int adjustasize(LuaTable* t, int size, const TValue* ek)
|
||||||
{
|
{
|
||||||
bool tbound = t->node != dummynode || size < t->sizearray;
|
bool tbound = t->node != dummynode || size < t->sizearray;
|
||||||
int ekindex = ek && ttisnumber(ek) ? arrayindex(nvalue(ek)) : -1;
|
int ekindex = ek && ttisnumber(ek) ? arrayindex(nvalue(ek)) : -1;
|
||||||
|
@ -434,19 +434,19 @@ static int adjustasize(Table* t, int size, const TValue* ek)
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
void luaH_resizearray(lua_State* L, Table* t, int nasize)
|
void luaH_resizearray(lua_State* L, LuaTable* t, int nasize)
|
||||||
{
|
{
|
||||||
int nsize = (t->node == dummynode) ? 0 : sizenode(t);
|
int nsize = (t->node == dummynode) ? 0 : sizenode(t);
|
||||||
int asize = adjustasize(t, nasize, NULL);
|
int asize = adjustasize(t, nasize, NULL);
|
||||||
resize(L, t, asize, nsize);
|
resize(L, t, asize, nsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
void luaH_resizehash(lua_State* L, Table* t, int nhsize)
|
void luaH_resizehash(lua_State* L, LuaTable* t, int nhsize)
|
||||||
{
|
{
|
||||||
resize(L, t, t->sizearray, nhsize);
|
resize(L, t, t->sizearray, nhsize);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void rehash(lua_State* L, Table* t, const TValue* ek)
|
static void rehash(lua_State* L, LuaTable* t, const TValue* ek)
|
||||||
{
|
{
|
||||||
int nums[MAXBITS + 1]; // nums[i] = number of keys between 2^(i-1) and 2^i
|
int nums[MAXBITS + 1]; // nums[i] = number of keys between 2^(i-1) and 2^i
|
||||||
for (int i = 0; i <= MAXBITS; i++)
|
for (int i = 0; i <= MAXBITS; i++)
|
||||||
|
@ -491,9 +491,9 @@ static void rehash(lua_State* L, Table* t, const TValue* ek)
|
||||||
** }=============================================================
|
** }=============================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
Table* luaH_new(lua_State* L, int narray, int nhash)
|
LuaTable* luaH_new(lua_State* L, int narray, int nhash)
|
||||||
{
|
{
|
||||||
Table* t = luaM_newgco(L, Table, sizeof(Table), L->activememcat);
|
LuaTable* t = luaM_newgco(L, LuaTable, sizeof(LuaTable), L->activememcat);
|
||||||
luaC_init(L, t, LUA_TTABLE);
|
luaC_init(L, t, LUA_TTABLE);
|
||||||
t->metatable = NULL;
|
t->metatable = NULL;
|
||||||
t->tmcache = cast_byte(~0);
|
t->tmcache = cast_byte(~0);
|
||||||
|
@ -512,16 +512,16 @@ Table* luaH_new(lua_State* L, int narray, int nhash)
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
void luaH_free(lua_State* L, Table* t, lua_Page* page)
|
void luaH_free(lua_State* L, LuaTable* 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);
|
||||||
if (t->array)
|
if (t->array)
|
||||||
luaM_freearray(L, t->array, t->sizearray, TValue, t->memcat);
|
luaM_freearray(L, t->array, t->sizearray, TValue, t->memcat);
|
||||||
luaM_freegco(L, t, sizeof(Table), t->memcat, page);
|
luaM_freegco(L, t, sizeof(LuaTable), t->memcat, page);
|
||||||
}
|
}
|
||||||
|
|
||||||
static LuaNode* getfreepos(Table* t)
|
static LuaNode* getfreepos(LuaTable* t)
|
||||||
{
|
{
|
||||||
while (t->lastfree > 0)
|
while (t->lastfree > 0)
|
||||||
{
|
{
|
||||||
|
@ -541,7 +541,7 @@ static LuaNode* getfreepos(Table* t)
|
||||||
** put new key in its main position; otherwise (colliding node is in its main
|
** put new key in its main position; otherwise (colliding node is in its main
|
||||||
** position), new key goes to an empty position.
|
** position), new key goes to an empty position.
|
||||||
*/
|
*/
|
||||||
static TValue* newkey(lua_State* L, Table* t, const TValue* key)
|
static TValue* newkey(lua_State* L, LuaTable* t, const TValue* key)
|
||||||
{
|
{
|
||||||
// enforce boundary invariant
|
// enforce boundary invariant
|
||||||
if (ttisnumber(key) && nvalue(key) == t->sizearray + 1)
|
if (ttisnumber(key) && nvalue(key) == t->sizearray + 1)
|
||||||
|
@ -601,7 +601,7 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
|
||||||
/*
|
/*
|
||||||
** search function for integers
|
** search function for integers
|
||||||
*/
|
*/
|
||||||
const TValue* luaH_getnum(Table* t, int key)
|
const TValue* luaH_getnum(LuaTable* t, int key)
|
||||||
{
|
{
|
||||||
// (1 <= key && key <= t->sizearray)
|
// (1 <= key && key <= t->sizearray)
|
||||||
if (cast_to(unsigned int, key - 1) < cast_to(unsigned int, t->sizearray))
|
if (cast_to(unsigned int, key - 1) < cast_to(unsigned int, t->sizearray))
|
||||||
|
@ -627,7 +627,7 @@ const TValue* luaH_getnum(Table* t, int key)
|
||||||
/*
|
/*
|
||||||
** search function for strings
|
** search function for strings
|
||||||
*/
|
*/
|
||||||
const TValue* luaH_getstr(Table* t, TString* key)
|
const TValue* luaH_getstr(LuaTable* t, TString* key)
|
||||||
{
|
{
|
||||||
LuaNode* n = hashstr(t, key);
|
LuaNode* n = hashstr(t, key);
|
||||||
for (;;)
|
for (;;)
|
||||||
|
@ -644,7 +644,7 @@ const TValue* luaH_getstr(Table* t, TString* key)
|
||||||
/*
|
/*
|
||||||
** main search function
|
** main search function
|
||||||
*/
|
*/
|
||||||
const TValue* luaH_get(Table* t, const TValue* key)
|
const TValue* luaH_get(LuaTable* t, const TValue* key)
|
||||||
{
|
{
|
||||||
switch (ttype(key))
|
switch (ttype(key))
|
||||||
{
|
{
|
||||||
|
@ -677,7 +677,7 @@ const TValue* luaH_get(Table* t, const TValue* key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TValue* luaH_set(lua_State* L, Table* t, const TValue* key)
|
TValue* luaH_set(lua_State* L, LuaTable* t, const TValue* key)
|
||||||
{
|
{
|
||||||
const TValue* p = luaH_get(t, key);
|
const TValue* p = luaH_get(t, key);
|
||||||
invalidateTMcache(t);
|
invalidateTMcache(t);
|
||||||
|
@ -687,7 +687,7 @@ TValue* luaH_set(lua_State* L, Table* t, const TValue* key)
|
||||||
return luaH_newkey(L, t, key);
|
return luaH_newkey(L, t, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
TValue* luaH_newkey(lua_State* L, Table* t, const TValue* key)
|
TValue* luaH_newkey(lua_State* L, LuaTable* t, const TValue* key)
|
||||||
{
|
{
|
||||||
if (ttisnil(key))
|
if (ttisnil(key))
|
||||||
luaG_runerror(L, "table index is nil");
|
luaG_runerror(L, "table index is nil");
|
||||||
|
@ -698,7 +698,7 @@ TValue* luaH_newkey(lua_State* L, Table* t, const TValue* key)
|
||||||
return newkey(L, t, key);
|
return newkey(L, t, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
TValue* luaH_setnum(lua_State* L, Table* t, int key)
|
TValue* luaH_setnum(lua_State* L, LuaTable* t, int key)
|
||||||
{
|
{
|
||||||
// (1 <= key && key <= t->sizearray)
|
// (1 <= key && key <= t->sizearray)
|
||||||
if (cast_to(unsigned int, key - 1) < cast_to(unsigned int, t->sizearray))
|
if (cast_to(unsigned int, key - 1) < cast_to(unsigned int, t->sizearray))
|
||||||
|
@ -715,7 +715,7 @@ TValue* luaH_setnum(lua_State* L, Table* t, int key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TValue* luaH_setstr(lua_State* L, Table* t, TString* key)
|
TValue* luaH_setstr(lua_State* L, LuaTable* t, TString* key)
|
||||||
{
|
{
|
||||||
const TValue* p = luaH_getstr(t, key);
|
const TValue* p = luaH_getstr(t, key);
|
||||||
invalidateTMcache(t);
|
invalidateTMcache(t);
|
||||||
|
@ -729,7 +729,7 @@ TValue* luaH_setstr(lua_State* L, Table* t, TString* key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int updateaboundary(Table* t, int boundary)
|
static int updateaboundary(LuaTable* t, int boundary)
|
||||||
{
|
{
|
||||||
if (boundary < t->sizearray && ttisnil(&t->array[boundary - 1]))
|
if (boundary < t->sizearray && ttisnil(&t->array[boundary - 1]))
|
||||||
{
|
{
|
||||||
|
@ -752,7 +752,7 @@ static int updateaboundary(Table* t, int boundary)
|
||||||
** Try to find a boundary in table `t'. A `boundary' is an integer index
|
** Try to find a boundary in table `t'. A `boundary' is an integer index
|
||||||
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
|
** such that t[i] is non-nil and t[i+1] is nil (and 0 if t[1] is nil).
|
||||||
*/
|
*/
|
||||||
int luaH_getn(Table* t)
|
int luaH_getn(LuaTable* t)
|
||||||
{
|
{
|
||||||
int boundary = getaboundary(t);
|
int boundary = getaboundary(t);
|
||||||
|
|
||||||
|
@ -793,9 +793,9 @@ int luaH_getn(Table* t)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Table* luaH_clone(lua_State* L, Table* tt)
|
LuaTable* luaH_clone(lua_State* L, LuaTable* tt)
|
||||||
{
|
{
|
||||||
Table* t = luaM_newgco(L, Table, sizeof(Table), L->activememcat);
|
LuaTable* t = luaM_newgco(L, LuaTable, sizeof(LuaTable), L->activememcat);
|
||||||
luaC_init(L, t, LUA_TTABLE);
|
luaC_init(L, t, LUA_TTABLE);
|
||||||
t->metatable = tt->metatable;
|
t->metatable = tt->metatable;
|
||||||
t->tmcache = tt->tmcache;
|
t->tmcache = tt->tmcache;
|
||||||
|
@ -830,7 +830,7 @@ Table* luaH_clone(lua_State* L, Table* tt)
|
||||||
return t;
|
return t;
|
||||||
}
|
}
|
||||||
|
|
||||||
void luaH_clear(Table* tt)
|
void luaH_clear(LuaTable* tt)
|
||||||
{
|
{
|
||||||
// clear array part
|
// clear array part
|
||||||
for (int i = 0; i < tt->sizearray; ++i)
|
for (int i = 0; i < tt->sizearray; ++i)
|
||||||
|
|
|
@ -14,21 +14,21 @@
|
||||||
// reset cache of absent metamethods, cache is updated in luaT_gettm
|
// reset cache of absent metamethods, cache is updated in luaT_gettm
|
||||||
#define invalidateTMcache(t) t->tmcache = 0
|
#define invalidateTMcache(t) t->tmcache = 0
|
||||||
|
|
||||||
LUAI_FUNC const TValue* luaH_getnum(Table* t, int key);
|
LUAI_FUNC const TValue* luaH_getnum(LuaTable* t, int key);
|
||||||
LUAI_FUNC TValue* luaH_setnum(lua_State* L, Table* t, int key);
|
LUAI_FUNC TValue* luaH_setnum(lua_State* L, LuaTable* t, int key);
|
||||||
LUAI_FUNC const TValue* luaH_getstr(Table* t, TString* key);
|
LUAI_FUNC const TValue* luaH_getstr(LuaTable* t, TString* key);
|
||||||
LUAI_FUNC TValue* luaH_setstr(lua_State* L, Table* t, TString* key);
|
LUAI_FUNC TValue* luaH_setstr(lua_State* L, LuaTable* t, TString* key);
|
||||||
LUAI_FUNC const TValue* luaH_get(Table* t, const TValue* key);
|
LUAI_FUNC const TValue* luaH_get(LuaTable* t, const TValue* key);
|
||||||
LUAI_FUNC TValue* luaH_set(lua_State* L, Table* t, const TValue* key);
|
LUAI_FUNC TValue* luaH_set(lua_State* L, LuaTable* t, const TValue* key);
|
||||||
LUAI_FUNC TValue* luaH_newkey(lua_State* L, Table* t, const TValue* key);
|
LUAI_FUNC TValue* luaH_newkey(lua_State* L, LuaTable* t, const TValue* key);
|
||||||
LUAI_FUNC Table* luaH_new(lua_State* L, int narray, int lnhash);
|
LUAI_FUNC LuaTable* luaH_new(lua_State* L, int narray, int lnhash);
|
||||||
LUAI_FUNC void luaH_resizearray(lua_State* L, Table* t, int nasize);
|
LUAI_FUNC void luaH_resizearray(lua_State* L, LuaTable* t, int nasize);
|
||||||
LUAI_FUNC void luaH_resizehash(lua_State* L, Table* t, int nhsize);
|
LUAI_FUNC void luaH_resizehash(lua_State* L, LuaTable* t, int nhsize);
|
||||||
LUAI_FUNC void luaH_free(lua_State* L, Table* t, struct lua_Page* page);
|
LUAI_FUNC void luaH_free(lua_State* L, LuaTable* t, struct lua_Page* page);
|
||||||
LUAI_FUNC int luaH_next(lua_State* L, Table* t, StkId key);
|
LUAI_FUNC int luaH_next(lua_State* L, LuaTable* t, StkId key);
|
||||||
LUAI_FUNC int luaH_getn(Table* t);
|
LUAI_FUNC int luaH_getn(LuaTable* t);
|
||||||
LUAI_FUNC Table* luaH_clone(lua_State* L, Table* tt);
|
LUAI_FUNC LuaTable* luaH_clone(lua_State* L, LuaTable* tt);
|
||||||
LUAI_FUNC void luaH_clear(Table* tt);
|
LUAI_FUNC void luaH_clear(LuaTable* tt);
|
||||||
|
|
||||||
#define luaH_setslot(L, t, slot, key) (invalidateTMcache(t), (slot == luaO_nilobject ? luaH_newkey(L, t, key) : cast_to(TValue*, slot)))
|
#define luaH_setslot(L, t, slot, key) (invalidateTMcache(t), (slot == luaO_nilobject ? luaH_newkey(L, t, key) : cast_to(TValue*, slot)))
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ static int maxn(lua_State* L)
|
||||||
double max = 0;
|
double max = 0;
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
|
||||||
Table* t = hvalue(L->base);
|
LuaTable* t = hvalue(L->base);
|
||||||
|
|
||||||
for (int i = 0; i < t->sizearray; i++)
|
for (int i = 0; i < t->sizearray; i++)
|
||||||
{
|
{
|
||||||
|
@ -87,8 +87,8 @@ static int getn(lua_State* L)
|
||||||
|
|
||||||
static void moveelements(lua_State* L, int srct, int dstt, int f, int e, int t)
|
static void moveelements(lua_State* L, int srct, int dstt, int f, int e, int t)
|
||||||
{
|
{
|
||||||
Table* src = hvalue(L->base + (srct - 1));
|
LuaTable* src = hvalue(L->base + (srct - 1));
|
||||||
Table* dst = hvalue(L->base + (dstt - 1));
|
LuaTable* dst = hvalue(L->base + (dstt - 1));
|
||||||
|
|
||||||
if (dst->readonly)
|
if (dst->readonly)
|
||||||
luaG_readonlyerror(L);
|
luaG_readonlyerror(L);
|
||||||
|
@ -213,7 +213,7 @@ static int tmove(lua_State* L)
|
||||||
int n = e - f + 1; // number of elements to move
|
int n = e - f + 1; // number of elements to move
|
||||||
luaL_argcheck(L, t <= INT_MAX - n + 1, 4, "destination wrap around");
|
luaL_argcheck(L, t <= INT_MAX - n + 1, 4, "destination wrap around");
|
||||||
|
|
||||||
Table* dst = hvalue(L->base + (tt - 1));
|
LuaTable* dst = hvalue(L->base + (tt - 1));
|
||||||
|
|
||||||
if (dst->readonly) // also checked in moveelements, but this blocks resizes of r/o tables
|
if (dst->readonly) // also checked in moveelements, but this blocks resizes of r/o tables
|
||||||
luaG_readonlyerror(L);
|
luaG_readonlyerror(L);
|
||||||
|
@ -229,7 +229,7 @@ static int tmove(lua_State* L)
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void addfield(lua_State* L, luaL_Strbuf* b, int i, Table* t)
|
static void addfield(lua_State* L, luaL_Strbuf* b, int i, LuaTable* t)
|
||||||
{
|
{
|
||||||
if (t && unsigned(i - 1) < unsigned(t->sizearray) && ttisstring(&t->array[i - 1]))
|
if (t && unsigned(i - 1) < unsigned(t->sizearray) && ttisstring(&t->array[i - 1]))
|
||||||
{
|
{
|
||||||
|
@ -253,7 +253,7 @@ static int tconcat(lua_State* L)
|
||||||
int i = luaL_optinteger(L, 3, 1);
|
int i = luaL_optinteger(L, 3, 1);
|
||||||
int last = luaL_opt(L, luaL_checkinteger, 4, lua_objlen(L, 1));
|
int last = luaL_opt(L, luaL_checkinteger, 4, lua_objlen(L, 1));
|
||||||
|
|
||||||
Table* t = hvalue(L->base);
|
LuaTable* t = hvalue(L->base);
|
||||||
|
|
||||||
luaL_Strbuf b;
|
luaL_Strbuf b;
|
||||||
luaL_buffinit(L, &b);
|
luaL_buffinit(L, &b);
|
||||||
|
@ -274,7 +274,7 @@ static int tpack(lua_State* L)
|
||||||
int n = lua_gettop(L); // number of elements to pack
|
int n = lua_gettop(L); // number of elements to pack
|
||||||
lua_createtable(L, n, 1); // create result table
|
lua_createtable(L, n, 1); // create result table
|
||||||
|
|
||||||
Table* t = hvalue(L->top - 1);
|
LuaTable* t = hvalue(L->top - 1);
|
||||||
|
|
||||||
for (int i = 0; i < n; ++i)
|
for (int i = 0; i < n; ++i)
|
||||||
{
|
{
|
||||||
|
@ -292,7 +292,7 @@ static int tpack(lua_State* L)
|
||||||
static int tunpack(lua_State* L)
|
static int tunpack(lua_State* L)
|
||||||
{
|
{
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
Table* t = hvalue(L->base);
|
LuaTable* t = hvalue(L->base);
|
||||||
|
|
||||||
int i = luaL_optinteger(L, 2, 1);
|
int i = luaL_optinteger(L, 2, 1);
|
||||||
int e = luaL_opt(L, luaL_checkinteger, 3, lua_objlen(L, 1));
|
int e = luaL_opt(L, luaL_checkinteger, 3, lua_objlen(L, 1));
|
||||||
|
@ -335,7 +335,7 @@ static int sort_func(lua_State* L, const TValue* l, const TValue* r)
|
||||||
return !l_isfalse(L->top);
|
return !l_isfalse(L->top);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void sort_swap(lua_State* L, Table* t, int i, int j)
|
inline void sort_swap(lua_State* L, LuaTable* t, int i, int j)
|
||||||
{
|
{
|
||||||
TValue* arr = t->array;
|
TValue* arr = t->array;
|
||||||
int n = t->sizearray;
|
int n = t->sizearray;
|
||||||
|
@ -348,7 +348,7 @@ inline void sort_swap(lua_State* L, Table* t, int i, int j)
|
||||||
setobj2t(L, &arr[j], &temp);
|
setobj2t(L, &arr[j], &temp);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline int sort_less(lua_State* L, Table* t, int i, int j, SortPredicate pred)
|
inline int sort_less(lua_State* L, LuaTable* t, int i, int j, SortPredicate pred)
|
||||||
{
|
{
|
||||||
TValue* arr = t->array;
|
TValue* arr = t->array;
|
||||||
int n = t->sizearray;
|
int n = t->sizearray;
|
||||||
|
@ -363,7 +363,7 @@ inline int sort_less(lua_State* L, Table* t, int i, int j, SortPredicate pred)
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sort_siftheap(lua_State* L, Table* t, int l, int u, SortPredicate pred, int root)
|
static void sort_siftheap(lua_State* L, LuaTable* t, int l, int u, SortPredicate pred, int root)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(l <= u);
|
LUAU_ASSERT(l <= u);
|
||||||
int count = u - l + 1;
|
int count = u - l + 1;
|
||||||
|
@ -389,7 +389,7 @@ static void sort_siftheap(lua_State* L, Table* t, int l, int u, SortPredicate pr
|
||||||
sort_swap(L, t, l + root, l + lastleft);
|
sort_swap(L, t, l + root, l + lastleft);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sort_heap(lua_State* L, Table* t, int l, int u, SortPredicate pred)
|
static void sort_heap(lua_State* L, LuaTable* t, int l, int u, SortPredicate pred)
|
||||||
{
|
{
|
||||||
LUAU_ASSERT(l <= u);
|
LUAU_ASSERT(l <= u);
|
||||||
int count = u - l + 1;
|
int count = u - l + 1;
|
||||||
|
@ -404,7 +404,7 @@ static void sort_heap(lua_State* L, Table* t, int l, int u, SortPredicate pred)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void sort_rec(lua_State* L, Table* t, int l, int u, int limit, SortPredicate pred)
|
static void sort_rec(lua_State* L, LuaTable* t, int l, int u, int limit, SortPredicate pred)
|
||||||
{
|
{
|
||||||
// sort range [l..u] (inclusive, 0-based)
|
// sort range [l..u] (inclusive, 0-based)
|
||||||
while (l < u)
|
while (l < u)
|
||||||
|
@ -477,7 +477,7 @@ static void sort_rec(lua_State* L, Table* t, int l, int u, int limit, SortPredic
|
||||||
static int tsort(lua_State* L)
|
static int tsort(lua_State* L)
|
||||||
{
|
{
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
Table* t = hvalue(L->base);
|
LuaTable* t = hvalue(L->base);
|
||||||
int n = luaH_getn(t);
|
int n = luaH_getn(t);
|
||||||
if (t->readonly)
|
if (t->readonly)
|
||||||
luaG_readonlyerror(L);
|
luaG_readonlyerror(L);
|
||||||
|
@ -504,7 +504,7 @@ static int tcreate(lua_State* L)
|
||||||
if (!lua_isnoneornil(L, 2))
|
if (!lua_isnoneornil(L, 2))
|
||||||
{
|
{
|
||||||
lua_createtable(L, size, 0);
|
lua_createtable(L, size, 0);
|
||||||
Table* t = hvalue(L->top - 1);
|
LuaTable* t = hvalue(L->top - 1);
|
||||||
|
|
||||||
StkId v = L->base + 1;
|
StkId v = L->base + 1;
|
||||||
|
|
||||||
|
@ -530,7 +530,7 @@ static int tfind(lua_State* L)
|
||||||
if (init < 1)
|
if (init < 1)
|
||||||
luaL_argerror(L, 3, "index out of range");
|
luaL_argerror(L, 3, "index out of range");
|
||||||
|
|
||||||
Table* t = hvalue(L->base);
|
LuaTable* t = hvalue(L->base);
|
||||||
StkId v = L->base + 1;
|
StkId v = L->base + 1;
|
||||||
|
|
||||||
for (int i = init;; ++i)
|
for (int i = init;; ++i)
|
||||||
|
@ -554,7 +554,7 @@ static int tclear(lua_State* L)
|
||||||
{
|
{
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
|
|
||||||
Table* tt = hvalue(L->base);
|
LuaTable* tt = hvalue(L->base);
|
||||||
if (tt->readonly)
|
if (tt->readonly)
|
||||||
luaG_readonlyerror(L);
|
luaG_readonlyerror(L);
|
||||||
|
|
||||||
|
@ -587,7 +587,7 @@ static int tclone(lua_State* L)
|
||||||
luaL_checktype(L, 1, LUA_TTABLE);
|
luaL_checktype(L, 1, LUA_TTABLE);
|
||||||
luaL_argcheck(L, !luaL_getmetafield(L, 1, "__metatable"), 1, "table has a protected metatable");
|
luaL_argcheck(L, !luaL_getmetafield(L, 1, "__metatable"), 1, "table has a protected metatable");
|
||||||
|
|
||||||
Table* tt = luaH_clone(L, hvalue(L->base));
|
LuaTable* tt = luaH_clone(L, hvalue(L->base));
|
||||||
|
|
||||||
TValue v;
|
TValue v;
|
||||||
sethvalue(L, &v, tt);
|
sethvalue(L, &v, tt);
|
||||||
|
|
|
@ -86,7 +86,7 @@ void luaT_init(lua_State* L)
|
||||||
** function to be used with macro "fasttm": optimized for absence of
|
** function to be used with macro "fasttm": optimized for absence of
|
||||||
** tag methods.
|
** tag methods.
|
||||||
*/
|
*/
|
||||||
const TValue* luaT_gettm(Table* events, TMS event, TString* ename)
|
const TValue* luaT_gettm(LuaTable* events, TMS event, TString* ename)
|
||||||
{
|
{
|
||||||
const TValue* tm = luaH_getstr(events, ename);
|
const TValue* tm = luaH_getstr(events, ename);
|
||||||
LUAU_ASSERT(event <= TM_EQ);
|
LUAU_ASSERT(event <= TM_EQ);
|
||||||
|
@ -105,7 +105,7 @@ const TValue* luaT_gettmbyobj(lua_State* L, const TValue* o, TMS event)
|
||||||
NB: Tag-methods were replaced by meta-methods in Lua 5.0, but the
|
NB: Tag-methods were replaced by meta-methods in Lua 5.0, but the
|
||||||
old names are still around (this function, for example).
|
old names are still around (this function, for example).
|
||||||
*/
|
*/
|
||||||
Table* mt;
|
LuaTable* mt;
|
||||||
switch (ttype(o))
|
switch (ttype(o))
|
||||||
{
|
{
|
||||||
case LUA_TTABLE:
|
case LUA_TTABLE:
|
||||||
|
@ -147,7 +147,7 @@ const TString* luaT_objtypenamestr(lua_State* L, const TValue* o)
|
||||||
}
|
}
|
||||||
|
|
||||||
// For all types except userdata and table, a global metatable can be set with a global name override
|
// For all types except userdata and table, a global metatable can be set with a global name override
|
||||||
if (Table* mt = L->global->mt[ttype(o)])
|
if (LuaTable* mt = L->global->mt[ttype(o)])
|
||||||
{
|
{
|
||||||
const TValue* type = luaH_getstr(mt, L->global->tmname[TM_TYPE]);
|
const TValue* type = luaH_getstr(mt, L->global->tmname[TM_TYPE]);
|
||||||
|
|
||||||
|
|
|
@ -51,7 +51,7 @@ typedef enum
|
||||||
LUAI_DATA const char* const luaT_typenames[];
|
LUAI_DATA const char* const luaT_typenames[];
|
||||||
LUAI_DATA const char* const luaT_eventname[];
|
LUAI_DATA const char* const luaT_eventname[];
|
||||||
|
|
||||||
LUAI_FUNC const TValue* luaT_gettm(Table* events, TMS event, TString* ename);
|
LUAI_FUNC const TValue* luaT_gettm(LuaTable* events, TMS event, TString* ename);
|
||||||
LUAI_FUNC const TValue* luaT_gettmbyobj(lua_State* L, const TValue* o, TMS event);
|
LUAI_FUNC const TValue* luaT_gettmbyobj(lua_State* L, const TValue* o, TMS event);
|
||||||
|
|
||||||
LUAI_FUNC const TString* luaT_objtypenamestr(lua_State* L, const TValue* o);
|
LUAI_FUNC const TString* luaT_objtypenamestr(lua_State* L, const TValue* o);
|
||||||
|
|
|
@ -26,7 +26,7 @@ LUAI_FUNC int luaV_tostring(lua_State* L, StkId obj);
|
||||||
LUAI_FUNC void luaV_gettable(lua_State* L, const TValue* t, TValue* key, StkId val);
|
LUAI_FUNC void luaV_gettable(lua_State* L, const TValue* t, TValue* key, StkId val);
|
||||||
LUAI_FUNC void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val);
|
LUAI_FUNC void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val);
|
||||||
LUAI_FUNC void luaV_concat(lua_State* L, int total, int last);
|
LUAI_FUNC void luaV_concat(lua_State* L, int total, int last);
|
||||||
LUAI_FUNC void luaV_getimport(lua_State* L, Table* env, TValue* k, StkId res, uint32_t id, bool propagatenil);
|
LUAI_FUNC void luaV_getimport(lua_State* L, LuaTable* env, TValue* k, StkId res, uint32_t id, bool propagatenil);
|
||||||
LUAI_FUNC void luaV_prepareFORN(lua_State* L, StkId plimit, StkId pstep, StkId pinit);
|
LUAI_FUNC void luaV_prepareFORN(lua_State* L, StkId plimit, StkId pstep, StkId pinit);
|
||||||
LUAI_FUNC void luaV_callTM(lua_State* L, int nparams, int res);
|
LUAI_FUNC void luaV_callTM(lua_State* L, int nparams, int res);
|
||||||
LUAI_FUNC void luaV_tryfuncTM(lua_State* L, StkId func);
|
LUAI_FUNC void luaV_tryfuncTM(lua_State* L, StkId func);
|
||||||
|
|
|
@ -330,7 +330,7 @@ reentry:
|
||||||
LUAU_ASSERT(ttisstring(kv));
|
LUAU_ASSERT(ttisstring(kv));
|
||||||
|
|
||||||
// fast-path: value is in expected slot
|
// fast-path: value is in expected slot
|
||||||
Table* h = cl->env;
|
LuaTable* h = cl->env;
|
||||||
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
||||||
LuaNode* n = &h->node[slot];
|
LuaNode* n = &h->node[slot];
|
||||||
|
|
||||||
|
@ -361,7 +361,7 @@ reentry:
|
||||||
LUAU_ASSERT(ttisstring(kv));
|
LUAU_ASSERT(ttisstring(kv));
|
||||||
|
|
||||||
// fast-path: value is in expected slot
|
// fast-path: value is in expected slot
|
||||||
Table* h = cl->env;
|
LuaTable* h = cl->env;
|
||||||
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
||||||
LuaNode* n = &h->node[slot];
|
LuaNode* n = &h->node[slot];
|
||||||
|
|
||||||
|
@ -451,7 +451,7 @@ reentry:
|
||||||
// fast-path: built-in table
|
// fast-path: built-in table
|
||||||
if (LUAU_LIKELY(ttistable(rb)))
|
if (LUAU_LIKELY(ttistable(rb)))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(rb);
|
LuaTable* h = hvalue(rb);
|
||||||
|
|
||||||
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
||||||
LuaNode* n = &h->node[slot];
|
LuaNode* n = &h->node[slot];
|
||||||
|
@ -568,7 +568,7 @@ reentry:
|
||||||
// fast-path: built-in table
|
// fast-path: built-in table
|
||||||
if (LUAU_LIKELY(ttistable(rb)))
|
if (LUAU_LIKELY(ttistable(rb)))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(rb);
|
LuaTable* h = hvalue(rb);
|
||||||
|
|
||||||
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
||||||
LuaNode* n = &h->node[slot];
|
LuaNode* n = &h->node[slot];
|
||||||
|
@ -642,7 +642,7 @@ reentry:
|
||||||
// fast-path: array lookup
|
// fast-path: array lookup
|
||||||
if (ttistable(rb) && ttisnumber(rc))
|
if (ttistable(rb) && ttisnumber(rc))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(rb);
|
LuaTable* h = hvalue(rb);
|
||||||
|
|
||||||
double indexd = nvalue(rc);
|
double indexd = nvalue(rc);
|
||||||
int index = int(indexd);
|
int index = int(indexd);
|
||||||
|
@ -672,7 +672,7 @@ reentry:
|
||||||
// fast-path: array assign
|
// fast-path: array assign
|
||||||
if (ttistable(rb) && ttisnumber(rc))
|
if (ttistable(rb) && ttisnumber(rc))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(rb);
|
LuaTable* h = hvalue(rb);
|
||||||
|
|
||||||
double indexd = nvalue(rc);
|
double indexd = nvalue(rc);
|
||||||
int index = int(indexd);
|
int index = int(indexd);
|
||||||
|
@ -703,7 +703,7 @@ reentry:
|
||||||
// fast-path: array lookup
|
// fast-path: array lookup
|
||||||
if (ttistable(rb))
|
if (ttistable(rb))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(rb);
|
LuaTable* h = hvalue(rb);
|
||||||
|
|
||||||
if (LUAU_LIKELY(unsigned(c) < unsigned(h->sizearray) && !h->metatable))
|
if (LUAU_LIKELY(unsigned(c) < unsigned(h->sizearray) && !h->metatable))
|
||||||
{
|
{
|
||||||
|
@ -731,7 +731,7 @@ reentry:
|
||||||
// fast-path: array assign
|
// fast-path: array assign
|
||||||
if (ttistable(rb))
|
if (ttistable(rb))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(rb);
|
LuaTable* h = hvalue(rb);
|
||||||
|
|
||||||
if (LUAU_LIKELY(unsigned(c) < unsigned(h->sizearray) && !h->metatable && !h->readonly))
|
if (LUAU_LIKELY(unsigned(c) < unsigned(h->sizearray) && !h->metatable && !h->readonly))
|
||||||
{
|
{
|
||||||
|
@ -804,7 +804,7 @@ reentry:
|
||||||
|
|
||||||
if (LUAU_LIKELY(ttistable(rb)))
|
if (LUAU_LIKELY(ttistable(rb)))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(rb);
|
LuaTable* h = hvalue(rb);
|
||||||
// note: we can't use nodemask8 here because we need to query the main position of the table, and 8-bit nodemask8 only works
|
// note: we can't use nodemask8 here because we need to query the main position of the table, and 8-bit nodemask8 only works
|
||||||
// for predictive lookups
|
// for predictive lookups
|
||||||
LuaNode* n = &h->node[tsvalue(kv)->hash & (sizenode(h) - 1)];
|
LuaNode* n = &h->node[tsvalue(kv)->hash & (sizenode(h) - 1)];
|
||||||
|
@ -844,7 +844,7 @@ reentry:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Table* mt = ttisuserdata(rb) ? uvalue(rb)->metatable : L->global->mt[ttype(rb)];
|
LuaTable* mt = ttisuserdata(rb) ? uvalue(rb)->metatable : L->global->mt[ttype(rb)];
|
||||||
const TValue* tmi = 0;
|
const TValue* tmi = 0;
|
||||||
|
|
||||||
// fast-path: metatable with __namecall
|
// fast-path: metatable with __namecall
|
||||||
|
@ -858,7 +858,7 @@ reentry:
|
||||||
}
|
}
|
||||||
else if ((tmi = fasttm(L, mt, TM_INDEX)) && ttistable(tmi))
|
else if ((tmi = fasttm(L, mt, TM_INDEX)) && ttistable(tmi))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(tmi);
|
LuaTable* h = hvalue(tmi);
|
||||||
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
int slot = LUAU_INSN_C(insn) & h->nodemask8;
|
||||||
LuaNode* n = &h->node[slot];
|
LuaNode* n = &h->node[slot];
|
||||||
|
|
||||||
|
@ -2126,7 +2126,7 @@ reentry:
|
||||||
// fast-path #1: tables
|
// fast-path #1: tables
|
||||||
if (LUAU_LIKELY(ttistable(rb)))
|
if (LUAU_LIKELY(ttistable(rb)))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(rb);
|
LuaTable* h = hvalue(rb);
|
||||||
|
|
||||||
if (fastnotm(h->metatable, TM_LEN))
|
if (fastnotm(h->metatable, TM_LEN))
|
||||||
{
|
{
|
||||||
|
@ -2196,7 +2196,7 @@ reentry:
|
||||||
L->top = L->ci->top;
|
L->top = L->ci->top;
|
||||||
}
|
}
|
||||||
|
|
||||||
Table* h = hvalue(ra);
|
LuaTable* h = hvalue(ra);
|
||||||
|
|
||||||
// TODO: we really don't need this anymore
|
// TODO: we really don't need this anymore
|
||||||
if (!ttistable(ra))
|
if (!ttistable(ra))
|
||||||
|
@ -2281,7 +2281,7 @@ reentry:
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Table* mt = ttistable(ra) ? hvalue(ra)->metatable : ttisuserdata(ra) ? uvalue(ra)->metatable : cast_to(Table*, NULL);
|
LuaTable* mt = ttistable(ra) ? hvalue(ra)->metatable : ttisuserdata(ra) ? uvalue(ra)->metatable : cast_to(LuaTable*, NULL);
|
||||||
|
|
||||||
if (const TValue* fn = fasttm(L, mt, TM_ITER))
|
if (const TValue* fn = fasttm(L, mt, TM_ITER))
|
||||||
{
|
{
|
||||||
|
@ -2340,7 +2340,7 @@ reentry:
|
||||||
// TODO: remove the table check per guarantee above
|
// TODO: remove the table check per guarantee above
|
||||||
if (ttisnil(ra) && ttistable(ra + 1))
|
if (ttisnil(ra) && ttistable(ra + 1))
|
||||||
{
|
{
|
||||||
Table* h = hvalue(ra + 1);
|
LuaTable* h = hvalue(ra + 1);
|
||||||
int index = int(reinterpret_cast<uintptr_t>(pvalue(ra + 2)));
|
int index = int(reinterpret_cast<uintptr_t>(pvalue(ra + 2)));
|
||||||
|
|
||||||
int sizearray = h->sizearray;
|
int sizearray = h->sizearray;
|
||||||
|
|
|
@ -72,7 +72,7 @@ private:
|
||||||
size_t originalThreshold = 0;
|
size_t originalThreshold = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
void luaV_getimport(lua_State* L, Table* env, TValue* k, StkId res, uint32_t id, bool propagatenil)
|
void luaV_getimport(lua_State* L, LuaTable* env, TValue* k, StkId res, uint32_t id, bool propagatenil)
|
||||||
{
|
{
|
||||||
int count = id >> 30;
|
int count = id >> 30;
|
||||||
LUAU_ASSERT(count > 0);
|
LUAU_ASSERT(count > 0);
|
||||||
|
@ -141,7 +141,7 @@ static TString* readString(TempBuffer<TString*>& strings, const char* data, size
|
||||||
return id == 0 ? NULL : strings[id - 1];
|
return id == 0 ? NULL : strings[id - 1];
|
||||||
}
|
}
|
||||||
|
|
||||||
static void resolveImportSafe(lua_State* L, Table* env, TValue* k, uint32_t id)
|
static void resolveImportSafe(lua_State* L, LuaTable* env, TValue* k, uint32_t id)
|
||||||
{
|
{
|
||||||
struct ResolveImport
|
struct ResolveImport
|
||||||
{
|
{
|
||||||
|
@ -273,7 +273,7 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
|
||||||
const ScopedSetGCThreshold pauseGC{L->global, SIZE_MAX};
|
const ScopedSetGCThreshold pauseGC{L->global, SIZE_MAX};
|
||||||
|
|
||||||
// env is 0 for current environment and a stack index otherwise
|
// env is 0 for current environment and a stack index otherwise
|
||||||
Table* envt = (env == 0) ? L->gt : hvalue(luaA_toobject(L, env));
|
LuaTable* envt = (env == 0) ? L->gt : hvalue(luaA_toobject(L, env));
|
||||||
|
|
||||||
TString* source = luaS_new(L, chunkname);
|
TString* source = luaS_new(L, chunkname);
|
||||||
|
|
||||||
|
@ -481,7 +481,7 @@ int luau_load(lua_State* L, const char* chunkname, const char* data, size_t size
|
||||||
case LBC_CONSTANT_TABLE:
|
case LBC_CONSTANT_TABLE:
|
||||||
{
|
{
|
||||||
int keys = readVarInt(data, size, offset);
|
int keys = readVarInt(data, size, offset);
|
||||||
Table* h = luaH_new(L, 0, keys);
|
LuaTable* h = luaH_new(L, 0, keys);
|
||||||
for (int i = 0; i < keys; ++i)
|
for (int i = 0; i < keys; ++i)
|
||||||
{
|
{
|
||||||
int key = readVarInt(data, size, offset);
|
int key = readVarInt(data, size, offset);
|
||||||
|
|
|
@ -101,7 +101,7 @@ void luaV_gettable(lua_State* L, const TValue* t, TValue* key, StkId val)
|
||||||
const TValue* tm;
|
const TValue* tm;
|
||||||
if (ttistable(t))
|
if (ttistable(t))
|
||||||
{ // `t' is a table?
|
{ // `t' is a table?
|
||||||
Table* h = hvalue(t);
|
LuaTable* h = hvalue(t);
|
||||||
|
|
||||||
const TValue* res = luaH_get(h, key); // do a primitive get
|
const TValue* res = luaH_get(h, key); // do a primitive get
|
||||||
|
|
||||||
|
@ -137,7 +137,7 @@ void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val)
|
||||||
const TValue* tm;
|
const TValue* tm;
|
||||||
if (ttistable(t))
|
if (ttistable(t))
|
||||||
{ // `t' is a table?
|
{ // `t' is a table?
|
||||||
Table* h = hvalue(t);
|
LuaTable* h = hvalue(t);
|
||||||
|
|
||||||
const TValue* oldval = luaH_get(h, key);
|
const TValue* oldval = luaH_get(h, key);
|
||||||
|
|
||||||
|
@ -185,7 +185,7 @@ static int call_binTM(lua_State* L, const TValue* p1, const TValue* p2, StkId re
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const TValue* get_compTM(lua_State* L, Table* mt1, Table* mt2, TMS event)
|
static const TValue* get_compTM(lua_State* L, LuaTable* mt1, LuaTable* mt2, TMS event)
|
||||||
{
|
{
|
||||||
const TValue* tm1 = fasttm(L, mt1, event);
|
const TValue* tm1 = fasttm(L, mt1, event);
|
||||||
const TValue* tm2;
|
const TValue* tm2;
|
||||||
|
@ -533,7 +533,7 @@ void luaV_dolen(lua_State* L, StkId ra, const TValue* rb)
|
||||||
{
|
{
|
||||||
case LUA_TTABLE:
|
case LUA_TTABLE:
|
||||||
{
|
{
|
||||||
Table* h = hvalue(rb);
|
LuaTable* h = hvalue(rb);
|
||||||
if ((tm = fasttm(L, h->metatable, TM_LEN)) == NULL)
|
if ((tm = fasttm(L, h->metatable, TM_LEN)) == NULL)
|
||||||
{
|
{
|
||||||
setnvalue(ra, cast_num(luaH_getn(h)));
|
setnvalue(ra, cast_num(luaH_getn(h)));
|
||||||
|
|
|
@ -19,6 +19,8 @@ LUAU_FASTFLAG(DebugLuauFreezeArena)
|
||||||
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
LUAU_FASTFLAG(DebugLuauMagicTypes)
|
||||||
LUAU_FASTFLAG(StudioReportLuauAny2)
|
LUAU_FASTFLAG(StudioReportLuauAny2)
|
||||||
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
LUAU_FASTFLAG(LuauTrackInteriorFreeTypesOnScope)
|
||||||
|
LUAU_FASTFLAG(LuauAlwaysFillInFunctionCallDiscriminantTypes)
|
||||||
|
LUAU_FASTFLAG(LuauRemoveNotAnyHack)
|
||||||
|
|
||||||
|
|
||||||
struct ATSFixture : BuiltinsFixture
|
struct ATSFixture : BuiltinsFixture
|
||||||
|
@ -410,6 +412,8 @@ TEST_CASE_FIXTURE(ATSFixture, "CannotExtendTable")
|
||||||
ScopedFastFlag sff[] = {
|
ScopedFastFlag sff[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::StudioReportLuauAny2, true},
|
{FFlag::StudioReportLuauAny2, true},
|
||||||
|
{FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes, true},
|
||||||
|
{FFlag::LuauRemoveNotAnyHack, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||||
|
@ -425,7 +429,7 @@ end
|
||||||
)";
|
)";
|
||||||
|
|
||||||
CheckResult result1 = frontend.check("game/Gui/Modules/A");
|
CheckResult result1 = frontend.check("game/Gui/Modules/A");
|
||||||
LUAU_REQUIRE_ERROR_COUNT(3, result1);
|
LUAU_REQUIRE_ERROR_COUNT(1, result1);
|
||||||
|
|
||||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||||
|
|
||||||
|
@ -502,6 +506,8 @@ TEST_CASE_FIXTURE(ATSFixture, "racing_collision_2")
|
||||||
ScopedFastFlag sff[] = {
|
ScopedFastFlag sff[] = {
|
||||||
{FFlag::LuauSolverV2, true},
|
{FFlag::LuauSolverV2, true},
|
||||||
{FFlag::StudioReportLuauAny2, true},
|
{FFlag::StudioReportLuauAny2, true},
|
||||||
|
{FFlag::LuauAlwaysFillInFunctionCallDiscriminantTypes, true},
|
||||||
|
{FFlag::LuauRemoveNotAnyHack, true},
|
||||||
};
|
};
|
||||||
|
|
||||||
fileResolver.source["game/Gui/Modules/A"] = R"(
|
fileResolver.source["game/Gui/Modules/A"] = R"(
|
||||||
|
@ -565,7 +571,7 @@ initialize()
|
||||||
)";
|
)";
|
||||||
|
|
||||||
CheckResult result1 = frontend.check("game/Gui/Modules/A");
|
CheckResult result1 = frontend.check("game/Gui/Modules/A");
|
||||||
LUAU_REQUIRE_ERROR_COUNT(5, result1);
|
LUAU_REQUIRE_ERROR_COUNT(3, result1);
|
||||||
|
|
||||||
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
ModulePtr module = frontend.moduleResolver.getModule("game/Gui/Modules/A");
|
||||||
|
|
||||||
|
|
|
@ -8,8 +8,6 @@
|
||||||
|
|
||||||
using namespace Luau;
|
using namespace Luau;
|
||||||
|
|
||||||
LUAU_FASTFLAG(LuauDocumentationAtPosition)
|
|
||||||
|
|
||||||
struct DocumentationSymbolFixture : BuiltinsFixture
|
struct DocumentationSymbolFixture : BuiltinsFixture
|
||||||
{
|
{
|
||||||
std::optional<DocumentationSymbol> getDocSymbol(const std::string& source, Position position)
|
std::optional<DocumentationSymbol> getDocSymbol(const std::string& source, Position position)
|
||||||
|
@ -167,7 +165,6 @@ TEST_CASE_FIXTURE(DocumentationSymbolFixture, "table_overloaded_function_prop")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DocumentationSymbolFixture, "string_metatable_method")
|
TEST_CASE_FIXTURE(DocumentationSymbolFixture, "string_metatable_method")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauDocumentationAtPosition, true};
|
|
||||||
std::optional<DocumentationSymbol> symbol = getDocSymbol(
|
std::optional<DocumentationSymbol> symbol = getDocSymbol(
|
||||||
R"(
|
R"(
|
||||||
local x: string = "Foo"
|
local x: string = "Foo"
|
||||||
|
@ -181,7 +178,6 @@ TEST_CASE_FIXTURE(DocumentationSymbolFixture, "string_metatable_method")
|
||||||
|
|
||||||
TEST_CASE_FIXTURE(DocumentationSymbolFixture, "parent_class_method")
|
TEST_CASE_FIXTURE(DocumentationSymbolFixture, "parent_class_method")
|
||||||
{
|
{
|
||||||
ScopedFastFlag sff{FFlag::LuauDocumentationAtPosition, true};
|
|
||||||
loadDefinition(R"(
|
loadDefinition(R"(
|
||||||
declare class Foo
|
declare class Foo
|
||||||
function bar(self, x: string): number
|
function bar(self, x: string): number
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#include "Luau/AssemblyBuilderA64.h"
|
#include "Luau/AssemblyBuilderA64.h"
|
||||||
#include "Luau/CodeAllocator.h"
|
#include "Luau/CodeAllocator.h"
|
||||||
#include "Luau/CodeBlockUnwind.h"
|
#include "Luau/CodeBlockUnwind.h"
|
||||||
|
#include "Luau/CodeGen.h"
|
||||||
#include "Luau/UnwindBuilder.h"
|
#include "Luau/UnwindBuilder.h"
|
||||||
#include "Luau/UnwindBuilderDwarf2.h"
|
#include "Luau/UnwindBuilderDwarf2.h"
|
||||||
#include "Luau/UnwindBuilderWin.h"
|
#include "Luau/UnwindBuilderWin.h"
|
||||||
|
|
|
@ -40,8 +40,9 @@ LUAU_FASTFLAG(LuauVectorLibNativeCodegen)
|
||||||
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
LUAU_FASTFLAG(LuauVectorLibNativeDot)
|
||||||
LUAU_FASTFLAG(LuauVectorMetatable)
|
LUAU_FASTFLAG(LuauVectorMetatable)
|
||||||
LUAU_FASTFLAG(LuauVector2Constructor)
|
LUAU_FASTFLAG(LuauVector2Constructor)
|
||||||
LUAU_FASTFLAG(LuauBufferBitMethods)
|
LUAU_FASTFLAG(LuauBufferBitMethods2)
|
||||||
LUAU_FASTFLAG(LuauCodeGenLimitLiveSlotReuse)
|
LUAU_FASTFLAG(LuauCodeGenLimitLiveSlotReuse)
|
||||||
|
LUAU_FASTFLAG(LuauMathMapDefinition)
|
||||||
|
|
||||||
static lua_CompileOptions defaultOptions()
|
static lua_CompileOptions defaultOptions()
|
||||||
{
|
{
|
||||||
|
@ -655,7 +656,7 @@ TEST_CASE("Basic")
|
||||||
|
|
||||||
TEST_CASE("Buffers")
|
TEST_CASE("Buffers")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauBufferBitMethods{FFlag::LuauBufferBitMethods, true};
|
ScopedFastFlag luauBufferBitMethods{FFlag::LuauBufferBitMethods2, true};
|
||||||
|
|
||||||
runConformance("buffers.lua");
|
runConformance("buffers.lua");
|
||||||
}
|
}
|
||||||
|
@ -989,7 +990,8 @@ static void populateRTTI(lua_State* L, Luau::TypeId type)
|
||||||
TEST_CASE("Types")
|
TEST_CASE("Types")
|
||||||
{
|
{
|
||||||
ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true};
|
ScopedFastFlag luauVector2Constructor{FFlag::LuauVector2Constructor, true};
|
||||||
ScopedFastFlag luauMathLerp{FFlag::LuauMathLerp, false}; // waiting for math.lerp to be added to embedded type definitions
|
ScopedFastFlag luauMathLerp{FFlag::LuauMathLerp, true};
|
||||||
|
ScopedFastFlag luauMathMapDefinition{FFlag::LuauMathMapDefinition, true};
|
||||||
|
|
||||||
runConformance(
|
runConformance(
|
||||||
"types.lua",
|
"types.lua",
|
||||||
|
|
|
@ -19,6 +19,7 @@ LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauAllowComplexTypesInGenericParams)
|
LUAU_FASTFLAG(LuauAllowComplexTypesInGenericParams)
|
||||||
LUAU_FASTFLAG(LuauErrorRecoveryForTableTypes)
|
LUAU_FASTFLAG(LuauErrorRecoveryForTableTypes)
|
||||||
LUAU_FASTFLAG(LuauErrorRecoveryForClassNames)
|
LUAU_FASTFLAG(LuauErrorRecoveryForClassNames)
|
||||||
|
LUAU_FASTFLAG(LuauFixFunctionNameStartPosition)
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
@ -3743,5 +3744,27 @@ TEST_CASE_FIXTURE(Fixture, "recover_from_bad_table_type")
|
||||||
CHECK_EQ(result.errors.size(), 2);
|
CHECK_EQ(result.errors.size(), 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(Fixture, "function_name_has_correct_start_location")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauFixFunctionNameStartPosition, true};
|
||||||
|
AstStatBlock* block = parse(R"(
|
||||||
|
function simple()
|
||||||
|
end
|
||||||
|
|
||||||
|
function T:complex()
|
||||||
|
end
|
||||||
|
)");
|
||||||
|
|
||||||
|
REQUIRE_EQ(2, block->body.size);
|
||||||
|
|
||||||
|
const auto function1 = block->body.data[0]->as<AstStatFunction>();
|
||||||
|
LUAU_ASSERT(function1);
|
||||||
|
CHECK_EQ(Position{1, 17}, function1->name->location.begin);
|
||||||
|
|
||||||
|
const auto function2 = block->body.data[1]->as<AstStatFunction>();
|
||||||
|
LUAU_ASSERT(function2);
|
||||||
|
CHECK_EQ(Position{4, 17}, function2->name->location.begin);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -14,6 +14,8 @@ LUAU_FASTFLAG(LuauUserTypeFunPrintToError)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunExportedAndLocal)
|
LUAU_FASTFLAG(LuauUserTypeFunExportedAndLocal)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunThreadBuffer)
|
LUAU_FASTFLAG(LuauUserTypeFunThreadBuffer)
|
||||||
LUAU_FASTFLAG(LuauUserTypeFunUpdateAllEnvs)
|
LUAU_FASTFLAG(LuauUserTypeFunUpdateAllEnvs)
|
||||||
|
LUAU_FASTFLAG(LuauUserTypeFunGenerics)
|
||||||
|
LUAU_FASTFLAG(LuauUserTypeFunCloneTail)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
TEST_SUITE_BEGIN("UserDefinedTypeFunctionTests");
|
||||||
|
|
||||||
|
@ -1414,4 +1416,479 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "print_to_error_plus_no_result")
|
||||||
CHECK(toString(result.errors[3]) == R"(Type function instance t0<string> is uninhabited)");
|
CHECK(toString(result.errors[3]) == R"(Type function instance t0<string> is uninhabited)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_serialization_1")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass(arg)
|
||||||
|
return arg
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = <T, U>(T, { x: <T>(y: T) -> (), y: U }, U) -> ()
|
||||||
|
|
||||||
|
local function ok(idx: pass<test>): test return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_serialization_2")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass(arg)
|
||||||
|
return arg
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = <T, U...>(T) -> (T, U...)
|
||||||
|
|
||||||
|
local function ok(idx: pass<test>): test return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_serialization_3")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass(arg)
|
||||||
|
return arg
|
||||||
|
end
|
||||||
|
|
||||||
|
local function m(a, b)
|
||||||
|
return {x = a, y = b}
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = typeof(m)
|
||||||
|
|
||||||
|
local function ok(idx: pass<test>): test return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_cloning_1")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass(arg)
|
||||||
|
return types.copy(arg)
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = <T, U>(T, { x: <T>(y: T) -> (), y: U }, U) -> ()
|
||||||
|
|
||||||
|
local function ok(idx: pass<test>): test return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_cloning_2")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunCloneTail{FFlag::LuauUserTypeFunCloneTail, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass(arg)
|
||||||
|
return types.copy(arg)
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = <T, U...>(T) -> (T, U...)
|
||||||
|
|
||||||
|
local function ok(idx: pass<test>): test return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_equality")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass(arg)
|
||||||
|
return types.singleton(types.copy(arg) == arg)
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = <T, U...>(T) -> (T, U...)
|
||||||
|
|
||||||
|
local function ok(idx: pass<test>): true return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_1")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass(arg)
|
||||||
|
local generics = arg:generics()
|
||||||
|
local T = generics[1]
|
||||||
|
return types.newfunction({ head = {T} }, { head = {T} }, {T})
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = <T, U>(T, { x: <T>(y: T) -> (), y: U }, U) -> ()
|
||||||
|
|
||||||
|
local function ok(idx: pass<test>): <T>(T) -> (T) return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_2")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass(arg)
|
||||||
|
local generics = arg:generics()
|
||||||
|
local T = generics[1]
|
||||||
|
local f = types.newfunction()
|
||||||
|
f:setparameters({T, T});
|
||||||
|
f:setreturns({T});
|
||||||
|
f:setgenerics({T});
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = <T, U>(T, { x: <T>(y: T) -> (), y: U }, U) -> ()
|
||||||
|
|
||||||
|
local function ok(idx: pass<test>): <T>(T, T) -> (T) return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_3")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass()
|
||||||
|
local T = types.generic("T")
|
||||||
|
assert(T.tag == "generic")
|
||||||
|
assert(T:name() == "T")
|
||||||
|
assert(T:ispack() == false)
|
||||||
|
|
||||||
|
local Us, Vs = types.generic("U", true), types.generic("V", true)
|
||||||
|
assert(Us.tag == "generic")
|
||||||
|
assert(Us:name() == "U")
|
||||||
|
assert(Us:ispack() == true)
|
||||||
|
|
||||||
|
local f = types.newfunction()
|
||||||
|
f:setparameters({T}, Us);
|
||||||
|
f:setreturns({T}, Vs);
|
||||||
|
f:setgenerics({T, Us, Vs});
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ok(idx: pass<>): <T, U..., V...>(T, U...) -> (T, V...) return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_4")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass()
|
||||||
|
local T, U = types.generic("T"), types.generic("U")
|
||||||
|
|
||||||
|
-- <T>(T) -> ()
|
||||||
|
local func = types.newfunction({ head = {T} }, {}, {T});
|
||||||
|
|
||||||
|
-- { x: <T>(T) -> (), y: U }
|
||||||
|
local tbl = types.newtable({ [types.singleton("x")] = func, [types.singleton("y")] = U })
|
||||||
|
|
||||||
|
-- <T, U>(T, { x: <T>(T) -> (), y: U }, U) -> ()
|
||||||
|
return types.newfunction({ head = {T, tbl, U } }, {}, {T, U})
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = <T, U>(T, { x: <T>(y: T) -> (), y: U }, U) -> ()
|
||||||
|
|
||||||
|
local function ok(idx: pass<>): test return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_5")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass()
|
||||||
|
local T = types.generic("T")
|
||||||
|
return types.newfunction({ head = {T} }, {}, {types.copy(T)})
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ok(idx: pass<>): <T>(T) -> () return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_6")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass(arg)
|
||||||
|
local generics = arg:generics()
|
||||||
|
local T, U = generics[1], generics[2]
|
||||||
|
local f = types.newfunction()
|
||||||
|
f:setparameters({T});
|
||||||
|
f:setreturns({U});
|
||||||
|
f:setgenerics({T, U});
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
|
local function m(a, b)
|
||||||
|
return {x = a, y = b}
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = typeof(m)
|
||||||
|
|
||||||
|
local function ok(idx: pass<test>): <T, U>(T) -> (U) return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_7")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass(arg)
|
||||||
|
local p, r = arg:parameters(), arg:returns()
|
||||||
|
local f = types.newfunction()
|
||||||
|
f:setparameters(p.head, p.tail);
|
||||||
|
f:setreturns(r.head, r.tail);
|
||||||
|
f:setgenerics(arg:generics());
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = <T, U...>(T, U...) -> (T, U...)
|
||||||
|
|
||||||
|
local function ok(idx: pass<test>): <T, U...>(T, U...) -> (T, U...) return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_8")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass(arg)
|
||||||
|
local p, r = arg:parameters(), arg:returns()
|
||||||
|
local f = types.newfunction()
|
||||||
|
f:setparameters(p.head, p.tail);
|
||||||
|
f:setreturns(r.head, r.tail);
|
||||||
|
f:setgenerics(arg:generics());
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = <U...>(U...) -> (U...)
|
||||||
|
|
||||||
|
local function ok(idx: pass<test>): <T>(T, T) -> (T) return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_equality_2")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function get()
|
||||||
|
local T, Us = types.generic("T"), types.generic("U", true)
|
||||||
|
|
||||||
|
local tbl1 = types.newtable({ [types.singleton("x")] = T })
|
||||||
|
local tbl2 = types.newtable({ [types.singleton("x")] = Us }) -- it is possible to have invalid types in-flight
|
||||||
|
|
||||||
|
return types.singleton(tbl1 == tbl2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function ok(idx: get<>): false return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_error_1")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function get()
|
||||||
|
local T, Us = types.generic("T"), types.generic("U", true)
|
||||||
|
return types.newfunction({}, {}, {Us, T})
|
||||||
|
end
|
||||||
|
local function ok(idx: get<>): false return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
|
CHECK(
|
||||||
|
toString(result.errors[0]) ==
|
||||||
|
R"('get' type function errored at runtime: [string "get"]:4: types.newfunction: generic type cannot follow a generic pack)"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_error_2")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function get()
|
||||||
|
local T, Us = types.generic("T"), types.generic("U", true)
|
||||||
|
return types.newfunction({ head = {T} }, {}, {})
|
||||||
|
end
|
||||||
|
local function ok(idx: get<>): false return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
|
CHECK(toString(result.errors[0]) == R"(Generic type 'T' is not in a scope of the active generic function)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_error_3")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function get()
|
||||||
|
local T, U = types.generic("T"), types.generic("U")
|
||||||
|
|
||||||
|
-- <U>(U) -> ()
|
||||||
|
local func = types.newfunction({ head = {U} }, {}, {U});
|
||||||
|
|
||||||
|
-- broken: <T>(T, <U>(U) -> (), U) -> ()
|
||||||
|
return types.newfunction({ head = {T, func, U } }, {}, {T})
|
||||||
|
end
|
||||||
|
local function ok(idx: get<>): false return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
|
CHECK(toString(result.errors[0]) == R"(Generic type 'U' is not in a scope of the active generic function)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_error_4")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function get()
|
||||||
|
local T, Us = types.generic("T"), types.generic("U", true)
|
||||||
|
return types.newfunction({ head = {T} }, { tail = Us }, {T, T})
|
||||||
|
end
|
||||||
|
local function ok(idx: get<>): false return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
|
CHECK(toString(result.errors[0]) == R"(Duplicate type parameter 'T')");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_error_5")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function get()
|
||||||
|
local T, Ts = types.generic("T"), types.generic("T", true)
|
||||||
|
return types.newfunction({ head = {T} }, { tail = Ts }, {T, Ts})
|
||||||
|
end
|
||||||
|
local function ok(idx: get<>): false return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
|
CHECK(toString(result.errors[0]) == R"(Duplicate type parameter 'T')");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_error_6")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function get()
|
||||||
|
local T, Us = types.generic("T"), types.generic("U", true)
|
||||||
|
return types.newfunction({ head = {Us} }, {}, {T, Us})
|
||||||
|
end
|
||||||
|
local function ok(idx: get<>): false return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
|
CHECK(toString(result.errors[0]) == R"(Generic type pack 'U...' cannot be placed in a type position)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_generic_api_error_7")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function get()
|
||||||
|
local T, Us = types.generic("T"), types.generic("U", true)
|
||||||
|
return types.newfunction({ tail = Us }, {}, {T})
|
||||||
|
end
|
||||||
|
local function ok(idx: get<>): false return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(4, result);
|
||||||
|
CHECK(toString(result.errors[0]) == R"(Generic type pack 'U...' is not in a scope of the active generic function)");
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(ClassFixture, "udtf_variadic_api")
|
||||||
|
{
|
||||||
|
ScopedFastFlag newSolver{FFlag::LuauSolverV2, true};
|
||||||
|
ScopedFastFlag luauUserTypeFunGenerics{FFlag::LuauUserTypeFunGenerics, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
type function pass(arg)
|
||||||
|
local p, r = arg:parameters(), arg:returns()
|
||||||
|
local f = types.newfunction()
|
||||||
|
f:setparameters({p.tail}, p.head[1]);
|
||||||
|
f:setreturns({r.tail}, r.head[1]);
|
||||||
|
return f
|
||||||
|
end
|
||||||
|
|
||||||
|
type test = (string, ...number) -> (number, ...string)
|
||||||
|
|
||||||
|
local function ok(idx: pass<test>): (number, ...string) -> (string, ...number) return idx end
|
||||||
|
)");
|
||||||
|
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -12,6 +12,7 @@ using namespace Luau;
|
||||||
LUAU_FASTFLAG(LuauSolverV2)
|
LUAU_FASTFLAG(LuauSolverV2)
|
||||||
LUAU_FASTFLAG(LuauTypestateBuiltins2)
|
LUAU_FASTFLAG(LuauTypestateBuiltins2)
|
||||||
LUAU_FASTFLAG(LuauStringFormatArityFix)
|
LUAU_FASTFLAG(LuauStringFormatArityFix)
|
||||||
|
LUAU_FASTFLAG(LuauStringFormatErrorSuppression)
|
||||||
|
|
||||||
TEST_SUITE_BEGIN("BuiltinTests");
|
TEST_SUITE_BEGIN("BuiltinTests");
|
||||||
|
|
||||||
|
@ -1586,4 +1587,19 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "string_find_should_not_crash")
|
||||||
)"));
|
)"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_CASE_FIXTURE(BuiltinsFixture, "string_format_should_support_any")
|
||||||
|
{
|
||||||
|
ScopedFastFlag _{FFlag::LuauSolverV2, true};
|
||||||
|
|
||||||
|
CheckResult result = check(R"(
|
||||||
|
local x: any = "world"
|
||||||
|
print(string.format("Hello, %s!", x))
|
||||||
|
)");
|
||||||
|
|
||||||
|
if (FFlag::LuauStringFormatErrorSuppression)
|
||||||
|
LUAU_REQUIRE_NO_ERRORS(result);
|
||||||
|
else
|
||||||
|
LUAU_REQUIRE_ERROR_COUNT(1, result);
|
||||||
|
}
|
||||||
|
|
||||||
TEST_SUITE_END();
|
TEST_SUITE_END();
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
|
||||||
#include "Luau/Common.h"
|
#include "Luau/Common.h"
|
||||||
|
|
||||||
|
#include "Luau/CodeGenCommon.h"
|
||||||
|
|
||||||
#define DOCTEST_CONFIG_IMPLEMENT
|
#define DOCTEST_CONFIG_IMPLEMENT
|
||||||
// Our calls to parseOption/parseFlag don't provide a prefix so set the prefix to the empty string.
|
// Our calls to parseOption/parseFlag don't provide a prefix so set the prefix to the empty string.
|
||||||
#define DOCTEST_CONFIG_OPTIONS_PREFIX ""
|
#define DOCTEST_CONFIG_OPTIONS_PREFIX ""
|
||||||
|
|
|
@ -77,7 +77,7 @@
|
||||||
<DisplayString Condition="key.tt == lua_Type::LUA_TNIL && val.tt == 0">---</DisplayString>
|
<DisplayString Condition="key.tt == lua_Type::LUA_TNIL && val.tt == 0">---</DisplayString>
|
||||||
</Type>
|
</Type>
|
||||||
|
|
||||||
<Type Name="Table">
|
<Type Name="LuaTable">
|
||||||
<DisplayString>table</DisplayString>
|
<DisplayString>table</DisplayString>
|
||||||
<Expand>
|
<Expand>
|
||||||
<Item Name="metatable" Condition="metatable">metatable</Item>
|
<Item Name="metatable" Condition="metatable">metatable</Item>
|
||||||
|
|
Loading…
Reference in a new issue