Merge branch 'upstream' into merge

This commit is contained in:
Arseny Kapoulkine 2022-08-04 14:30:15 -07:00
commit 5522631be4
111 changed files with 3546 additions and 1493 deletions

2
.gitignore vendored
View File

@ -8,4 +8,6 @@
/default.prof* /default.prof*
/fuzz-* /fuzz-*
/luau /luau
/luau-tests
/luau-analyze
__pycache__ __pycache__

View File

@ -0,0 +1,32 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include "Luau/Substitution.h"
#include "Luau/TxnLog.h"
#include "Luau/TypeVar.h"
namespace Luau
{
// A substitution which replaces the type parameters of a type function by arguments
struct ApplyTypeFunction : Substitution
{
ApplyTypeFunction(TypeArena* arena)
: Substitution(TxnLog::empty(), arena)
, encounteredForwardedType(false)
{
}
// Never set under deferred constraint resolution.
bool encounteredForwardedType;
std::unordered_map<TypeId, TypeId> typeArguments;
std::unordered_map<TypePackId, TypePackId> typePackArguments;
bool ignoreChildren(TypeId ty) override;
bool ignoreChildren(TypePackId tp) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
} // namespace Luau

View File

@ -4,6 +4,7 @@
#include "Luau/Ast.h" // Used for some of the enumerations #include "Luau/Ast.h" // Used for some of the enumerations
#include "Luau/NotNull.h" #include "Luau/NotNull.h"
#include "Luau/Variant.h" #include "Luau/Variant.h"
#include "Luau/TypeVar.h"
#include <string> #include <string>
#include <memory> #include <memory>
@ -71,8 +72,15 @@ struct NameConstraint
std::string name; std::string name;
}; };
// target ~ inst target
struct TypeAliasExpansionConstraint
{
// Must be a PendingExpansionTypeVar.
TypeId target;
};
using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, UnaryConstraint, using ConstraintV = Variant<SubtypeConstraint, PackSubtypeConstraint, GeneralizationConstraint, InstantiationConstraint, UnaryConstraint,
BinaryConstraint, NameConstraint>; BinaryConstraint, NameConstraint, TypeAliasExpansionConstraint>;
using ConstraintPtr = std::unique_ptr<struct Constraint>; using ConstraintPtr = std::unique_ptr<struct Constraint>;
struct Constraint struct Constraint

View File

@ -42,6 +42,8 @@ struct ConstraintGraphBuilder
DenseHashMap<const AstType*, TypeId> astResolvedTypes{nullptr}; DenseHashMap<const AstType*, TypeId> astResolvedTypes{nullptr};
// Type packs resolved from type annotations. Analogous to astTypePacks. // Type packs resolved from type annotations. Analogous to astTypePacks.
DenseHashMap<const AstTypePack*, TypePackId> astResolvedTypePacks{nullptr}; DenseHashMap<const AstTypePack*, TypePackId> astResolvedTypePacks{nullptr};
// Defining scopes for AST nodes.
DenseHashMap<const AstStatTypeAlias*, ScopePtr> astTypeAliasDefiningScopes{nullptr};
int recursionCount = 0; int recursionCount = 0;
@ -107,6 +109,9 @@ struct ConstraintGraphBuilder
void visit(const ScopePtr& scope, AstStatAssign* assign); void visit(const ScopePtr& scope, AstStatAssign* assign);
void visit(const ScopePtr& scope, AstStatIf* ifStatement); void visit(const ScopePtr& scope, AstStatIf* ifStatement);
void visit(const ScopePtr& scope, AstStatTypeAlias* alias); void visit(const ScopePtr& scope, AstStatTypeAlias* alias);
void visit(const ScopePtr& scope, AstStatDeclareGlobal* declareGlobal);
void visit(const ScopePtr& scope, AstStatDeclareClass* declareClass);
void visit(const ScopePtr& scope, AstStatDeclareFunction* declareFunction);
TypePackId checkExprList(const ScopePtr& scope, const AstArray<AstExpr*>& exprs); TypePackId checkExprList(const ScopePtr& scope, const AstArray<AstExpr*>& exprs);
@ -153,9 +158,10 @@ struct ConstraintGraphBuilder
* Resolves a type from its AST annotation. * Resolves a type from its AST annotation.
* @param scope the scope that the type annotation appears within. * @param scope the scope that the type annotation appears within.
* @param ty the AST annotation to resolve. * @param ty the AST annotation to resolve.
* @param topLevel whether the annotation is a "top-level" annotation.
* @return the type of the AST annotation. * @return the type of the AST annotation.
**/ **/
TypeId resolveType(const ScopePtr& scope, AstType* ty); TypeId resolveType(const ScopePtr& scope, AstType* ty, bool topLevel = false);
/** /**
* Resolves a type pack from its AST annotation. * Resolves a type pack from its AST annotation.

View File

@ -17,16 +17,36 @@ namespace Luau
// never dereference this pointer. // never dereference this pointer.
using BlockedConstraintId = const void*; using BlockedConstraintId = const void*;
struct InstantiationSignature
{
TypeFun fn;
std::vector<TypeId> arguments;
std::vector<TypePackId> packArguments;
bool operator==(const InstantiationSignature& rhs) const;
bool operator!=(const InstantiationSignature& rhs) const
{
return !((*this) == rhs);
}
};
struct HashInstantiationSignature
{
size_t operator()(const InstantiationSignature& signature) const;
};
struct ConstraintSolver struct ConstraintSolver
{ {
TypeArena* arena; TypeArena* arena;
InternalErrorReporter iceReporter; InternalErrorReporter iceReporter;
// The entire set of constraints that the solver is trying to resolve. It // The entire set of constraints that the solver is trying to resolve.
// is important to not add elements to this vector, lest the underlying std::vector<NotNull<Constraint>> constraints;
// storage that we retain pointers to be mutated underneath us.
const std::vector<NotNull<Constraint>> constraints;
NotNull<Scope> rootScope; NotNull<Scope> rootScope;
// Constraints that the solver has generated, rather than sourcing from the
// scope tree.
std::vector<std::unique_ptr<Constraint>> solverConstraints;
// This includes every constraint that has not been fully solved. // This includes every constraint that has not been fully solved.
// A constraint can be both blocked and unsolved, for instance. // A constraint can be both blocked and unsolved, for instance.
std::vector<NotNull<const Constraint>> unsolvedConstraints; std::vector<NotNull<const Constraint>> unsolvedConstraints;
@ -37,6 +57,8 @@ struct ConstraintSolver
std::unordered_map<NotNull<const Constraint>, size_t> blockedConstraints; std::unordered_map<NotNull<const Constraint>, size_t> blockedConstraints;
// A mapping of type/pack pointers to the constraints they block. // A mapping of type/pack pointers to the constraints they block.
std::unordered_map<BlockedConstraintId, std::vector<NotNull<const Constraint>>> blocked; std::unordered_map<BlockedConstraintId, std::vector<NotNull<const Constraint>>> blocked;
// Memoized instantiations of type aliases.
DenseHashMap<InstantiationSignature, TypeId, HashInstantiationSignature> instantiatedAliases{{}};
ConstraintSolverLogger logger; ConstraintSolverLogger logger;
@ -62,6 +84,7 @@ struct ConstraintSolver
bool tryDispatch(const UnaryConstraint& c, NotNull<const Constraint> constraint, bool force); bool tryDispatch(const UnaryConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const BinaryConstraint& c, NotNull<const Constraint> constraint, bool force); bool tryDispatch(const BinaryConstraint& c, NotNull<const Constraint> constraint, bool force);
bool tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint); bool tryDispatch(const NameConstraint& c, NotNull<const Constraint> constraint);
bool tryDispatch(const TypeAliasExpansionConstraint& c, NotNull<const Constraint> constraint);
void block(NotNull<const Constraint> target, NotNull<const Constraint> constraint); void block(NotNull<const Constraint> target, NotNull<const Constraint> constraint);
/** /**
@ -102,6 +125,11 @@ struct ConstraintSolver
*/ */
void unify(TypePackId subPack, TypePackId superPack); void unify(TypePackId subPack, TypePackId superPack);
/** Pushes a new solver constraint to the solver.
* @param cv the body of the constraint.
**/
void pushConstraint(ConstraintV cv);
private: private:
/** /**
* Marks a constraint as being blocked on a type or type pack. The constraint * Marks a constraint as being blocked on a type or type pack. The constraint

View File

@ -152,6 +152,8 @@ struct Frontend
void registerBuiltinDefinition(const std::string& name, std::function<void(TypeChecker&, ScopePtr)>); void registerBuiltinDefinition(const std::string& name, std::function<void(TypeChecker&, ScopePtr)>);
void applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName); void applyBuiltinDefinitionToEnvironment(const std::string& environmentName, const std::string& definitionName);
LoadDefinitionFileResult loadDefinitionFile(std::string_view source, const std::string& packageName);
NotNull<Scope> getGlobalScope(); NotNull<Scope> getGlobalScope();
private: private:
@ -169,7 +171,7 @@ private:
std::unordered_map<std::string, ScopePtr> environments; std::unordered_map<std::string, ScopePtr> environments;
std::unordered_map<std::string, std::function<void(TypeChecker&, ScopePtr)>> builtinDefinitions; std::unordered_map<std::string, std::function<void(TypeChecker&, ScopePtr)>> builtinDefinitions;
std::unique_ptr<Scope> globalScope; ScopePtr globalScope;
public: public:
FileResolver* fileResolver; FileResolver* fileResolver;
@ -180,6 +182,7 @@ public:
ConfigResolver* configResolver; ConfigResolver* configResolver;
FrontendOptions options; FrontendOptions options;
InternalErrorReporter iceHandler; InternalErrorReporter iceHandler;
TypeArena globalTypes;
TypeArena arenaForAutocomplete; TypeArena arenaForAutocomplete;
std::unordered_map<ModuleName, SourceNode> sourceNodes; std::unordered_map<ModuleName, SourceNode> sourceNodes;

View File

@ -0,0 +1,235 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#pragma once
#include <type_traits>
#include <string>
#include <optional>
#include <vector>
#include "Luau/NotNull.h"
namespace Luau::Json
{
struct JsonEmitter;
/// Writes a value to the JsonEmitter. Note that this can produce invalid JSON
/// if you do not insert commas or appropriate object / array syntax.
template<typename T>
void write(JsonEmitter&, T) = delete;
/// Writes a boolean to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param b the boolean to write.
void write(JsonEmitter& emitter, bool b);
/// Writes an integer to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param i the integer to write.
void write(JsonEmitter& emitter, int i);
/// Writes an integer to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param i the integer to write.
void write(JsonEmitter& emitter, long i);
/// Writes an integer to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param i the integer to write.
void write(JsonEmitter& emitter, long long i);
/// Writes an integer to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param i the integer to write.
void write(JsonEmitter& emitter, unsigned int i);
/// Writes an integer to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param i the integer to write.
void write(JsonEmitter& emitter, unsigned long i);
/// Writes an integer to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param i the integer to write.
void write(JsonEmitter& emitter, unsigned long long i);
/// Writes a double to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param d the double to write.
void write(JsonEmitter& emitter, double d);
/// Writes a string to a JsonEmitter. The string will be escaped.
/// @param emitter the emitter to write to.
/// @param sv the string to write.
void write(JsonEmitter& emitter, std::string_view sv);
/// Writes a character to a JsonEmitter as a single-character string. The
/// character will be escaped.
/// @param emitter the emitter to write to.
/// @param c the string to write.
void write(JsonEmitter& emitter, char c);
/// Writes a string to a JsonEmitter. The string will be escaped.
/// @param emitter the emitter to write to.
/// @param str the string to write.
void write(JsonEmitter& emitter, const char* str);
/// Writes a string to a JsonEmitter. The string will be escaped.
/// @param emitter the emitter to write to.
/// @param str the string to write.
void write(JsonEmitter& emitter, const std::string& str);
/// Writes null to a JsonEmitter.
/// @param emitter the emitter to write to.
void write(JsonEmitter& emitter, std::nullptr_t);
/// Writes null to a JsonEmitter.
/// @param emitter the emitter to write to.
void write(JsonEmitter& emitter, std::nullopt_t);
struct ObjectEmitter;
struct ArrayEmitter;
struct JsonEmitter
{
JsonEmitter();
/// Converts the current contents of the JsonEmitter to a string value. This
/// does not invalidate the emitter, but it does not clear it either.
std::string str();
/// Returns the current comma state and resets it to false. Use popComma to
/// restore the old state.
/// @returns the previous comma state.
bool pushComma();
/// Restores a previous comma state.
/// @param c the comma state to restore.
void popComma(bool c);
/// Writes a raw sequence of characters to the buffer, without escaping or
/// other processing.
/// @param sv the character sequence to write.
void writeRaw(std::string_view sv);
/// Writes a character to the buffer, without escaping or other processing.
/// @param c the character to write.
void writeRaw(char c);
/// Writes a comma if this wasn't the first time writeComma has been
/// invoked. Otherwise, sets the comma state to true.
/// @see pushComma
/// @see popComma
void writeComma();
/// Begins writing an object to the emitter.
/// @returns an ObjectEmitter that can be used to write key-value pairs.
ObjectEmitter writeObject();
/// Begins writing an array to the emitter.
/// @returns an ArrayEmitter that can be used to write values.
ArrayEmitter writeArray();
private:
bool comma = false;
std::vector<std::string> chunks;
void newChunk();
};
/// An interface for writing an object into a JsonEmitter instance.
/// @see JsonEmitter::writeObject
struct ObjectEmitter
{
ObjectEmitter(NotNull<JsonEmitter> emitter);
~ObjectEmitter();
NotNull<JsonEmitter> emitter;
bool comma;
bool finished;
/// Writes a key-value pair to the associated JsonEmitter. Keys will be escaped.
/// @param name the name of the key-value pair.
/// @param value the value to write.
template<typename T>
void writePair(std::string_view name, T value)
{
if (finished)
{
return;
}
emitter->writeComma();
write(*emitter, name);
emitter->writeRaw(':');
write(*emitter, value);
}
/// Finishes writing the object, appending a closing `}` character and
/// resetting the comma state of the associated emitter. This can only be
/// called once, and once called will render the emitter unusable. This
/// method is also called when the ObjectEmitter is destructed.
void finish();
};
/// An interface for writing an array into a JsonEmitter instance. Array values
/// do not need to be the same type.
/// @see JsonEmitter::writeArray
struct ArrayEmitter
{
ArrayEmitter(NotNull<JsonEmitter> emitter);
~ArrayEmitter();
NotNull<JsonEmitter> emitter;
bool comma;
bool finished;
/// Writes a value to the array.
/// @param value the value to write.
template<typename T>
void writeValue(T value)
{
if (finished)
{
return;
}
emitter->writeComma();
write(*emitter, value);
}
/// Finishes writing the object, appending a closing `]` character and
/// resetting the comma state of the associated emitter. This can only be
/// called once, and once called will render the emitter unusable. This
/// method is also called when the ArrayEmitter is destructed.
void finish();
};
/// Writes a vector as an array to a JsonEmitter.
/// @param emitter the emitter to write to.
/// @param vec the vector to write.
template<typename T>
void write(JsonEmitter& emitter, const std::vector<T>& vec)
{
ArrayEmitter a = emitter.writeArray();
for (const T& value : vec)
a.writeValue(value);
a.finish();
}
/// Writes an optional to a JsonEmitter. Will write the contained value, if
/// present, or null, if no value is present.
/// @param emitter the emitter to write to.
/// @param v the value to write.
template<typename T>
void write(JsonEmitter& emitter, const std::optional<T>& v)
{
if (v.has_value())
write(emitter, *v);
else
emitter.writeRaw("null");
}
} // namespace Luau::Json

View File

@ -52,6 +52,7 @@ struct LintWarning
Code_DuplicateCondition = 24, Code_DuplicateCondition = 24,
Code_MisleadingAndOr = 25, Code_MisleadingAndOr = 25,
Code_CommentDirective = 26, Code_CommentDirective = 26,
Code_IntegerParsing = 27,
Code__Count Code__Count
}; };

View File

@ -68,7 +68,7 @@ struct Module
std::shared_ptr<Allocator> allocator; std::shared_ptr<Allocator> allocator;
std::shared_ptr<AstNameTable> names; std::shared_ptr<AstNameTable> names;
std::vector<std::pair<Location, ScopePtr>> scopes; // never empty std::vector<std::pair<Location, ScopePtr>> scopes; // never empty
DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr}; DenseHashMap<const AstExpr*, TypeId> astTypes{nullptr};
DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr}; DenseHashMap<const AstExpr*, TypePackId> astTypePacks{nullptr};

View File

@ -36,7 +36,7 @@ struct Scope
// All the children of this scope. // All the children of this scope.
std::vector<NotNull<Scope>> children; std::vector<NotNull<Scope>> children;
std::unordered_map<Symbol, Binding> bindings; std::unordered_map<Symbol, Binding> bindings;
std::unordered_map<Name, TypeId> typeBindings; std::unordered_map<Name, TypeFun> typeBindings;
std::unordered_map<Name, TypePackId> typePackBindings; std::unordered_map<Name, TypePackId> typePackBindings;
TypePackId returnType; TypePackId returnType;
std::optional<TypePackId> varargPack; std::optional<TypePackId> varargPack;
@ -52,7 +52,7 @@ struct Scope
std::unordered_map<Name, std::unordered_map<Name, TypeFun>> importedTypeBindings; std::unordered_map<Name, std::unordered_map<Name, TypeFun>> importedTypeBindings;
std::optional<TypeId> lookup(Symbol sym); std::optional<TypeId> lookup(Symbol sym);
std::optional<TypeId> lookupTypeBinding(const Name& name); std::optional<TypeFun> lookupTypeBinding(const Name& name);
std::optional<TypePackId> lookupTypePackBinding(const Name& name); std::optional<TypePackId> lookupTypePackBinding(const Name& name);
std::optional<TypeFun> lookupType(const Name& name); std::optional<TypeFun> lookupType(const Name& name);

View File

@ -139,6 +139,8 @@ struct FindDirty : Tarjan
{ {
std::vector<bool> dirty; std::vector<bool> dirty;
void clearTarjan();
// Get/set the dirty bit for an index (grows the vector if needed) // Get/set the dirty bit for an index (grows the vector if needed)
bool getDirty(int index); bool getDirty(int index);
void setDirty(int index, bool d); void setDirty(int index, bool d);
@ -176,6 +178,8 @@ public:
TypeArena* arena; TypeArena* arena;
DenseHashMap<TypeId, TypeId> newTypes{nullptr}; DenseHashMap<TypeId, TypeId> newTypes{nullptr};
DenseHashMap<TypePackId, TypePackId> newPacks{nullptr}; DenseHashMap<TypePackId, TypePackId> newPacks{nullptr};
DenseHashSet<TypeId> replacedTypes{nullptr};
DenseHashSet<TypePackId> replacedTypePacks{nullptr};
std::optional<TypeId> substitute(TypeId ty); std::optional<TypeId> substitute(TypeId ty);
std::optional<TypePackId> substitute(TypePackId tp); std::optional<TypePackId> substitute(TypePackId tp);

View File

@ -65,28 +65,6 @@ struct Anyification : Substitution
} }
}; };
// A substitution which replaces the type parameters of a type function by arguments
struct ApplyTypeFunction : Substitution
{
ApplyTypeFunction(TypeArena* arena, TypeLevel level)
: Substitution(TxnLog::empty(), arena)
, level(level)
, encounteredForwardedType(false)
{
}
TypeLevel level;
bool encounteredForwardedType;
std::unordered_map<TypeId, TypeId> typeArguments;
std::unordered_map<TypePackId, TypePackId> typePackArguments;
bool ignoreChildren(TypeId ty) override;
bool ignoreChildren(TypePackId tp) override;
bool isDirty(TypeId ty) override;
bool isDirty(TypePackId tp) override;
TypeId clean(TypeId ty) override;
TypePackId clean(TypePackId tp) override;
};
struct GenericTypeDefinitions struct GenericTypeDefinitions
{ {
std::vector<GenericTypeDefinition> genericTypes; std::vector<GenericTypeDefinition> genericTypes;

View File

@ -223,12 +223,16 @@ struct GenericTypeDefinition
{ {
TypeId ty; TypeId ty;
std::optional<TypeId> defaultValue; std::optional<TypeId> defaultValue;
bool operator==(const GenericTypeDefinition& rhs) const;
}; };
struct GenericTypePackDefinition struct GenericTypePackDefinition
{ {
TypePackId tp; TypePackId tp;
std::optional<TypePackId> defaultValue; std::optional<TypePackId> defaultValue;
bool operator==(const GenericTypePackDefinition& rhs) const;
}; };
struct FunctionArgument struct FunctionArgument
@ -426,6 +430,12 @@ struct TypeFun
TypeId type; TypeId type;
TypeFun() = default; TypeFun() = default;
explicit TypeFun(TypeId ty)
: type(ty)
{
}
TypeFun(std::vector<GenericTypeDefinition> typeParams, TypeId type) TypeFun(std::vector<GenericTypeDefinition> typeParams, TypeId type)
: typeParams(std::move(typeParams)) : typeParams(std::move(typeParams))
, type(type) , type(type)
@ -438,6 +448,27 @@ struct TypeFun
, type(type) , type(type)
{ {
} }
bool operator==(const TypeFun& rhs) const;
};
/** Represents a pending type alias instantiation.
*
* In order to afford (co)recursive type aliases, we need to reason about a
* partially-complete instantiation. This requires encoding more information in
* a type variable than a BlockedTypeVar affords, hence this. Each
* PendingExpansionTypeVar has a corresponding TypeAliasExpansionConstraint
* enqueued in the solver to convert it to an actual instantiated type
*/
struct PendingExpansionTypeVar
{
PendingExpansionTypeVar(TypeFun fn, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments);
TypeFun fn;
std::vector<TypeId> typeArguments;
std::vector<TypePackId> packArguments;
size_t index;
static size_t nextIndex;
}; };
// Anything! All static checking is off. // Anything! All static checking is off.
@ -470,8 +501,10 @@ struct NeverTypeVar
using ErrorTypeVar = Unifiable::Error; using ErrorTypeVar = Unifiable::Error;
using TypeVariant = Unifiable::Variant<TypeId, PrimitiveTypeVar, ConstrainedTypeVar, BlockedTypeVar, SingletonTypeVar, FunctionTypeVar, TableTypeVar, using TypeVariant =
MetatableTypeVar, ClassTypeVar, AnyTypeVar, UnionTypeVar, IntersectionTypeVar, LazyTypeVar, UnknownTypeVar, NeverTypeVar>; Unifiable::Variant<TypeId, PrimitiveTypeVar, ConstrainedTypeVar, BlockedTypeVar, PendingExpansionTypeVar, SingletonTypeVar, FunctionTypeVar,
TableTypeVar, MetatableTypeVar, ClassTypeVar, AnyTypeVar, UnionTypeVar, IntersectionTypeVar, LazyTypeVar, UnknownTypeVar, NeverTypeVar>;
struct TypeVar final struct TypeVar final
{ {

View File

@ -9,7 +9,6 @@
#include "Luau/TypeVar.h" #include "Luau/TypeVar.h"
LUAU_FASTINT(LuauVisitRecursionLimit) LUAU_FASTINT(LuauVisitRecursionLimit)
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
LUAU_FASTFLAG(LuauCompleteVisitor); LUAU_FASTFLAG(LuauCompleteVisitor);
namespace Luau namespace Luau
@ -150,6 +149,10 @@ struct GenericTypeVarVisitor
{ {
return visit(ty); return visit(ty);
} }
virtual bool visit(TypeId ty, const PendingExpansionTypeVar& petv)
{
return visit(ty);
}
virtual bool visit(TypeId ty, const SingletonTypeVar& stv) virtual bool visit(TypeId ty, const SingletonTypeVar& stv)
{ {
return visit(ty); return visit(ty);
@ -285,8 +288,6 @@ struct GenericTypeVarVisitor
traverse(partTy); traverse(partTy);
} }
} }
else if (!FFlag::LuauCompleteVisitor)
return visit_detail::unsee(seen, ty);
else if (get<LazyTypeVar>(ty)) else if (get<LazyTypeVar>(ty))
{ {
// Visiting into LazyTypeVar may necessarily cause infinite expansion, so we don't do that on purpose. // Visiting into LazyTypeVar may necessarily cause infinite expansion, so we don't do that on purpose.
@ -301,6 +302,37 @@ struct GenericTypeVarVisitor
visit(ty, *utv); visit(ty, *utv);
else if (auto ntv = get<NeverTypeVar>(ty)) else if (auto ntv = get<NeverTypeVar>(ty))
visit(ty, *ntv); visit(ty, *ntv);
else if (auto petv = get<PendingExpansionTypeVar>(ty))
{
if (visit(ty, *petv))
{
traverse(petv->fn.type);
for (const GenericTypeDefinition& p : petv->fn.typeParams)
{
traverse(p.ty);
if (p.defaultValue)
traverse(*p.defaultValue);
}
for (const GenericTypePackDefinition& p : petv->fn.typePackParams)
{
traverse(p.tp);
if (p.defaultValue)
traverse(*p.defaultValue);
}
for (TypeId a : petv->typeArguments)
traverse(a);
for (TypePackId a : petv->packArguments)
traverse(a);
}
}
else if (!FFlag::LuauCompleteVisitor)
return visit_detail::unsee(seen, ty);
else else
LUAU_ASSERT(!"GenericTypeVarVisitor::traverse(TypeId) is not exhaustive!"); LUAU_ASSERT(!"GenericTypeVarVisitor::traverse(TypeId) is not exhaustive!");
@ -333,7 +365,7 @@ struct GenericTypeVarVisitor
else if (auto pack = get<TypePack>(tp)) else if (auto pack = get<TypePack>(tp))
{ {
bool res = visit(tp, *pack); bool res = visit(tp, *pack);
if (!FFlag::LuauNormalizeFlagIsConservative || res) if (res)
{ {
for (TypeId ty : pack->head) for (TypeId ty : pack->head)
traverse(ty); traverse(ty);
@ -345,7 +377,7 @@ struct GenericTypeVarVisitor
else if (auto pack = get<VariadicTypePack>(tp)) else if (auto pack = get<VariadicTypePack>(tp))
{ {
bool res = visit(tp, *pack); bool res = visit(tp, *pack);
if (!FFlag::LuauNormalizeFlagIsConservative || res) if (res)
traverse(pack->ty); traverse(pack->ty);
} }
else else

View File

@ -0,0 +1,60 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ApplyTypeFunction.h"
namespace Luau
{
bool ApplyTypeFunction::isDirty(TypeId ty)
{
if (typeArguments.count(ty))
return true;
else if (const FreeTypeVar* ftv = get<FreeTypeVar>(ty))
{
if (ftv->forwardedTypeAlias)
encounteredForwardedType = true;
return false;
}
else
return false;
}
bool ApplyTypeFunction::isDirty(TypePackId tp)
{
if (typePackArguments.count(tp))
return true;
else
return false;
}
bool ApplyTypeFunction::ignoreChildren(TypeId ty)
{
if (get<GenericTypeVar>(ty))
return true;
else
return false;
}
bool ApplyTypeFunction::ignoreChildren(TypePackId tp)
{
if (get<GenericTypePack>(tp))
return true;
else
return false;
}
TypeId ApplyTypeFunction::clean(TypeId ty)
{
TypeId& arg = typeArguments[ty];
LUAU_ASSERT(arg);
return arg;
}
TypePackId ApplyTypeFunction::clean(TypePackId tp)
{
TypePackId& arg = typePackArguments[tp];
LUAU_ASSERT(arg);
return arg;
}
} // namespace Luau

View File

@ -1,5 +1,5 @@
// 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/JsonEncoder.h" #include "Luau/AstJsonEncoder.h"
#include "Luau/Ast.h" #include "Luau/Ast.h"
#include "Luau/ParseResult.h" #include "Luau/ParseResult.h"
@ -773,7 +773,7 @@ struct AstJsonEncoder : public AstVisitor
PROP(indexer); PROP(indexer);
}); });
} }
void write(struct AstTableIndexer* indexer) void write(struct AstTableIndexer* indexer)
{ {
if (indexer) if (indexer)
@ -1178,7 +1178,6 @@ struct AstJsonEncoder : public AstVisitor
write("location", comment.location); write("location", comment.location);
popComma(c); popComma(c);
writeRaw("}"); writeRaw("}");
} }
} }
}; };

View File

@ -314,7 +314,7 @@ std::optional<Binding> findBindingAtPosition(const Module& module, const SourceM
auto iter = currentScope->bindings.find(name); auto iter = currentScope->bindings.find(name);
if (iter != currentScope->bindings.end() && iter->second.location.begin <= pos) if (iter != currentScope->bindings.end() && iter->second.location.begin <= pos)
{ {
/* Ignore this binding if we're inside its definition. e.g. local abc = abc -- Will take the definition of abc from outer scope */ // Ignore this binding if we're inside its definition. e.g. local abc = abc -- Will take the definition of abc from outer scope
std::optional<AstStatLocal*> bindingStatement = findBindingLocalStatement(source, iter->second); std::optional<AstStatLocal*> bindingStatement = findBindingLocalStatement(source, iter->second);
if (!bindingStatement || !(*bindingStatement)->location.contains(pos)) if (!bindingStatement || !(*bindingStatement)->location.contains(pos))
return iter->second; return iter->second;

View File

@ -10,6 +10,7 @@
LUAU_FASTFLAGVARIABLE(LuauSetMetaTableArgsCheck, false) LUAU_FASTFLAGVARIABLE(LuauSetMetaTableArgsCheck, false)
LUAU_FASTFLAG(LuauUnknownAndNeverType) LUAU_FASTFLAG(LuauUnknownAndNeverType)
LUAU_FASTFLAGVARIABLE(LuauBuiltInMetatableNoBadSynthetic, false)
/** FIXME: Many of these type definitions are not quite completely accurate. /** FIXME: Many of these type definitions are not quite completely accurate.
* *
@ -349,7 +350,7 @@ static std::optional<WithPredicate<TypePackId>> magicFunctionSetMetaTable(
if (tableName == metatableName) if (tableName == metatableName)
mtv.syntheticName = tableName; mtv.syntheticName = tableName;
else else if (!FFlag::LuauBuiltInMetatableNoBadSynthetic)
mtv.syntheticName = "{ @metatable: " + metatableName + ", " + tableName + " }"; mtv.syntheticName = "{ @metatable: " + metatableName + ", " + tableName + " }";
} }

View File

@ -48,6 +48,7 @@ struct TypeCloner
void operator()(const Unifiable::Bound<TypeId>& t); void operator()(const Unifiable::Bound<TypeId>& t);
void operator()(const Unifiable::Error& t); void operator()(const Unifiable::Error& t);
void operator()(const BlockedTypeVar& t); void operator()(const BlockedTypeVar& t);
void operator()(const PendingExpansionTypeVar& t);
void operator()(const PrimitiveTypeVar& t); void operator()(const PrimitiveTypeVar& t);
void operator()(const ConstrainedTypeVar& t); void operator()(const ConstrainedTypeVar& t);
void operator()(const SingletonTypeVar& t); void operator()(const SingletonTypeVar& t);
@ -166,6 +167,52 @@ void TypeCloner::operator()(const BlockedTypeVar& t)
defaultClone(t); defaultClone(t);
} }
void TypeCloner::operator()(const PendingExpansionTypeVar& t)
{
TypeId res = dest.addType(PendingExpansionTypeVar{t.fn, t.typeArguments, t.packArguments});
PendingExpansionTypeVar* petv = getMutable<PendingExpansionTypeVar>(res);
LUAU_ASSERT(petv);
seenTypes[typeId] = res;
std::vector<TypeId> typeArguments;
for (TypeId arg : t.typeArguments)
typeArguments.push_back(clone(arg, dest, cloneState));
std::vector<TypePackId> packArguments;
for (TypePackId arg : t.packArguments)
packArguments.push_back(clone(arg, dest, cloneState));
TypeFun fn;
fn.type = clone(t.fn.type, dest, cloneState);
for (const GenericTypeDefinition& param : t.fn.typeParams)
{
TypeId ty = clone(param.ty, dest, cloneState);
std::optional<TypeId> defaultValue = param.defaultValue;
if (defaultValue)
defaultValue = clone(*defaultValue, dest, cloneState);
fn.typeParams.push_back(GenericTypeDefinition{ty, defaultValue});
}
for (const GenericTypePackDefinition& param : t.fn.typePackParams)
{
TypePackId tp = clone(param.tp, dest, cloneState);
std::optional<TypePackId> defaultValue = param.defaultValue;
if (defaultValue)
defaultValue = clone(*defaultValue, dest, cloneState);
fn.typePackParams.push_back(GenericTypePackDefinition{tp, defaultValue});
}
petv->fn = std::move(fn);
petv->typeArguments = std::move(typeArguments);
petv->packArguments = std::move(packArguments);
}
void TypeCloner::operator()(const PrimitiveTypeVar& t) void TypeCloner::operator()(const PrimitiveTypeVar& t)
{ {
defaultClone(t); defaultClone(t);
@ -452,6 +499,11 @@ TypeId shallowClone(TypeId ty, TypeArena& dest, const TxnLog* log)
ConstrainedTypeVar clone{ctv->level, ctv->parts}; ConstrainedTypeVar clone{ctv->level, ctv->parts};
result = dest.addType(std::move(clone)); result = dest.addType(std::move(clone));
} }
else if (const PendingExpansionTypeVar* petv = get<PendingExpansionTypeVar>(ty))
{
PendingExpansionTypeVar clone{petv->fn, petv->typeArguments, petv->packArguments};
result = dest.addType(std::move(clone));
}
else else
return result; return result;

View File

@ -70,11 +70,11 @@ void ConstraintGraphBuilder::visit(AstStatBlock* block)
prepopulateGlobalScope(scope, block); prepopulateGlobalScope(scope, block);
// TODO: We should share the global scope. // TODO: We should share the global scope.
rootScope->typeBindings["nil"] = singletonTypes.nilType; rootScope->typeBindings["nil"] = TypeFun{singletonTypes.nilType};
rootScope->typeBindings["number"] = singletonTypes.numberType; rootScope->typeBindings["number"] = TypeFun{singletonTypes.numberType};
rootScope->typeBindings["string"] = singletonTypes.stringType; rootScope->typeBindings["string"] = TypeFun{singletonTypes.stringType};
rootScope->typeBindings["boolean"] = singletonTypes.booleanType; rootScope->typeBindings["boolean"] = TypeFun{singletonTypes.booleanType};
rootScope->typeBindings["thread"] = singletonTypes.threadType; rootScope->typeBindings["thread"] = TypeFun{singletonTypes.threadType};
visitBlockWithoutChildScope(scope, block); visitBlockWithoutChildScope(scope, block);
} }
@ -89,6 +89,53 @@ void ConstraintGraphBuilder::visitBlockWithoutChildScope(const ScopePtr& scope,
return; return;
} }
std::unordered_map<Name, Location> aliasDefinitionLocations;
// In order to enable mutually-recursive type aliases, we need to
// populate the type bindings before we actually check any of the
// alias statements. Since we're not ready to actually resolve
// any of the annotations, we just use a fresh type for now.
for (AstStat* stat : block->body)
{
if (auto alias = stat->as<AstStatTypeAlias>())
{
if (scope->typeBindings.count(alias->name.value) != 0)
{
auto it = aliasDefinitionLocations.find(alias->name.value);
LUAU_ASSERT(it != aliasDefinitionLocations.end());
reportError(alias->location, DuplicateTypeDefinition{alias->name.value, it->second});
continue;
}
bool hasGenerics = alias->generics.size > 0 || alias->genericPacks.size > 0;
ScopePtr defnScope = scope;
if (hasGenerics)
{
defnScope = childScope(alias->location, scope);
}
TypeId initialType = freshType(scope);
TypeFun initialFun = TypeFun{initialType};
for (const auto& [name, gen] : createGenerics(defnScope, alias->generics))
{
initialFun.typeParams.push_back(gen);
defnScope->typeBindings[name] = TypeFun{gen.ty};
}
for (const auto& [name, genPack] : createGenericPacks(defnScope, alias->genericPacks))
{
initialFun.typePackParams.push_back(genPack);
defnScope->typePackBindings[name] = genPack.tp;
}
scope->typeBindings[alias->name.value] = std::move(initialFun);
astTypeAliasDefiningScopes[alias] = defnScope;
aliasDefinitionLocations[alias->name.value] = alias->location;
}
}
for (AstStat* stat : block->body) for (AstStat* stat : block->body)
visit(scope, stat); visit(scope, stat);
} }
@ -117,6 +164,12 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStat* stat)
visit(scope, i); visit(scope, i);
else if (auto a = stat->as<AstStatTypeAlias>()) else if (auto a = stat->as<AstStatTypeAlias>())
visit(scope, a); visit(scope, a);
else if (auto s = stat->as<AstStatDeclareGlobal>())
visit(scope, s);
else if (auto s = stat->as<AstStatDeclareClass>())
visit(scope, s);
else if (auto s = stat->as<AstStatDeclareFunction>())
visit(scope, s);
else else
LUAU_ASSERT(0); LUAU_ASSERT(0);
} }
@ -133,7 +186,7 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
if (local->annotation) if (local->annotation)
{ {
location = local->annotation->location; location = local->annotation->location;
TypeId annotation = resolveType(scope, local->annotation); TypeId annotation = resolveType(scope, local->annotation, /* topLevel */ true);
addConstraint(scope, SubtypeConstraint{ty, annotation}); addConstraint(scope, SubtypeConstraint{ty, annotation});
} }
@ -171,11 +224,10 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatLocal* local)
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFor* for_) void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatFor* for_)
{ {
auto checkNumber = [&](AstExpr* expr) auto checkNumber = [&](AstExpr* expr) {
{
if (!expr) if (!expr)
return; return;
TypeId t = check(scope, expr); TypeId t = check(scope, expr);
addConstraint(scope, SubtypeConstraint{t, singletonTypes.numberType}); addConstraint(scope, SubtypeConstraint{t, singletonTypes.numberType});
}; };
@ -307,19 +359,6 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatBlock* block)
{ {
ScopePtr innerScope = childScope(block->location, scope); ScopePtr innerScope = childScope(block->location, scope);
// In order to enable mutually-recursive type aliases, we need to
// populate the type bindings before we actually check any of the
// alias statements. Since we're not ready to actually resolve
// any of the annotations, we just use a fresh type for now.
for (AstStat* stat : block->body)
{
if (auto alias = stat->as<AstStatTypeAlias>())
{
TypeId initialType = freshType(scope);
scope->typeBindings[alias->name.value] = initialType;
}
}
visitBlockWithoutChildScope(innerScope, block); visitBlockWithoutChildScope(innerScope, block);
} }
@ -348,29 +387,48 @@ void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatIf* ifStatement
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatTypeAlias* alias) void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatTypeAlias* alias)
{ {
// TODO: Exported type aliases // TODO: Exported type aliases
// TODO: Generic type aliases
auto it = scope->typeBindings.find(alias->name.value); auto bindingIt = scope->typeBindings.find(alias->name.value);
// This should always be here since we do a separate pass over the ScopePtr* defnIt = astTypeAliasDefiningScopes.find(alias);
// AST to set up typeBindings. If it's not, we've somehow skipped // These will be undefined if the alias was a duplicate definition, in which
// this alias in that first pass. // case we just skip over it.
LUAU_ASSERT(it != scope->typeBindings.end()); if (bindingIt == scope->typeBindings.end() || defnIt == nullptr)
if (it == scope->typeBindings.end())
{ {
ice->ice("Type alias does not have a pre-populated binding", alias->location); return;
} }
TypeId ty = resolveType(scope, alias->type); ScopePtr resolvingScope = *defnIt;
TypeId ty = resolveType(resolvingScope, alias->type, /* topLevel */ true);
LUAU_ASSERT(get<FreeTypeVar>(bindingIt->second.type));
// Rather than using a subtype constraint, we instead directly bind // Rather than using a subtype constraint, we instead directly bind
// the free type we generated in the first pass to the resolved type. // the free type we generated in the first pass to the resolved type.
// This prevents a case where you could cause another constraint to // This prevents a case where you could cause another constraint to
// bind the free alias type to an unrelated type, causing havoc. // bind the free alias type to an unrelated type, causing havoc.
asMutable(it->second)->ty.emplace<BoundTypeVar>(ty); asMutable(bindingIt->second.type)->ty.emplace<BoundTypeVar>(ty);
addConstraint(scope, NameConstraint{ty, alias->name.value}); addConstraint(scope, NameConstraint{ty, alias->name.value});
} }
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareGlobal* global)
{
LUAU_ASSERT(global->type);
TypeId globalTy = resolveType(scope, global->type);
scope->bindings[global->name] = Binding{globalTy, global->location};
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareClass* global)
{
LUAU_ASSERT(false); // TODO: implement
}
void ConstraintGraphBuilder::visit(const ScopePtr& scope, AstStatDeclareFunction* global)
{
LUAU_ASSERT(false); // TODO: implement
}
TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstArray<AstExpr*> exprs) TypePackId ConstraintGraphBuilder::checkPack(const ScopePtr& scope, AstArray<AstExpr*> exprs)
{ {
if (exprs.size == 0) if (exprs.size == 0)
@ -707,7 +765,7 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
for (const auto& [name, g] : genericDefinitions) for (const auto& [name, g] : genericDefinitions)
{ {
genericTypes.push_back(g.ty); genericTypes.push_back(g.ty);
signatureScope->typeBindings[name] = g.ty; signatureScope->typeBindings[name] = TypeFun{g.ty};
} }
for (const auto& [name, g] : genericPackDefinitions) for (const auto& [name, g] : genericPackDefinitions)
@ -745,7 +803,7 @@ ConstraintGraphBuilder::FunctionSignature ConstraintGraphBuilder::checkFunctionS
if (local->annotation) if (local->annotation)
{ {
TypeId argAnnotation = resolveType(signatureScope, local->annotation); TypeId argAnnotation = resolveType(signatureScope, local->annotation, /* topLevel */ true);
addConstraint(signatureScope, SubtypeConstraint{t, argAnnotation}); addConstraint(signatureScope, SubtypeConstraint{t, argAnnotation});
} }
} }
@ -784,20 +842,65 @@ void ConstraintGraphBuilder::checkFunctionBody(const ScopePtr& scope, AstExprFun
} }
} }
TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty) TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty, bool topLevel)
{ {
TypeId result = nullptr; TypeId result = nullptr;
if (auto ref = ty->as<AstTypeReference>()) if (auto ref = ty->as<AstTypeReference>())
{ {
// TODO: Support imported types w/ require tracing. // TODO: Support imported types w/ require tracing.
// TODO: Support generic type references.
LUAU_ASSERT(!ref->prefix); LUAU_ASSERT(!ref->prefix);
LUAU_ASSERT(!ref->hasParameterList);
// TODO: If it doesn't exist, should we introduce a free binding? std::optional<TypeFun> alias = scope->lookupTypeBinding(ref->name.value);
// This is probably important for handling type aliases.
result = scope->lookupTypeBinding(ref->name.value).value_or(singletonTypes.errorRecoveryType()); if (alias.has_value())
{
// If the alias is not generic, we don't need to set up a blocked
// type and an instantiation constraint.
if (alias->typeParams.empty() && alias->typePackParams.empty())
{
result = alias->type;
}
else
{
std::vector<TypeId> parameters;
std::vector<TypePackId> packParameters;
for (const AstTypeOrPack& p : ref->parameters)
{
// We do not enforce the ordering of types vs. type packs here;
// that is done in the parser.
if (p.type)
{
parameters.push_back(resolveType(scope, p.type));
}
else if (p.typePack)
{
packParameters.push_back(resolveTypePack(scope, p.typePack));
}
else
{
// This indicates a parser bug: one of these two pointers
// should be set.
LUAU_ASSERT(false);
}
}
result = arena->addType(PendingExpansionTypeVar{*alias, parameters, packParameters});
if (topLevel)
{
addConstraint(scope, TypeAliasExpansionConstraint{
/* target */ result,
});
}
}
}
else
{
reportError(ty->location, UnknownSymbol{ref->name.value, UnknownSymbol::Context::Type});
result = singletonTypes.errorRecoveryType();
}
} }
else if (auto tab = ty->as<AstTypeTable>()) else if (auto tab = ty->as<AstTypeTable>())
{ {
@ -846,7 +949,7 @@ TypeId ConstraintGraphBuilder::resolveType(const ScopePtr& scope, AstType* ty)
for (const auto& [name, g] : genericDefinitions) for (const auto& [name, g] : genericDefinitions)
{ {
genericTypes.push_back(g.ty); genericTypes.push_back(g.ty);
signatureScope->typeBindings[name] = g.ty; signatureScope->typeBindings[name] = TypeFun{g.ty};
} }
for (const auto& [name, g] : genericPackDefinitions) for (const auto& [name, g] : genericPackDefinitions)
@ -956,7 +1059,15 @@ TypePackId ConstraintGraphBuilder::resolveTypePack(const ScopePtr& scope, AstTyp
} }
else if (auto gen = tp->as<AstTypePackGeneric>()) else if (auto gen = tp->as<AstTypePackGeneric>())
{ {
result = arena->addTypePack(TypePackVar{GenericTypePack{scope.get(), gen->genericName.value}}); if (std::optional<TypePackId> lookup = scope->lookupTypePackBinding(gen->genericName.value))
{
result = *lookup;
}
else
{
reportError(tp->location, UnknownSymbol{gen->genericName.value, UnknownSymbol::Context::Type});
result = singletonTypes.errorRecoveryTypePack();
}
} }
else else
{ {

View File

@ -1,11 +1,13 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details // This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/ApplyTypeFunction.h"
#include "Luau/ConstraintSolver.h" #include "Luau/ConstraintSolver.h"
#include "Luau/Instantiation.h" #include "Luau/Instantiation.h"
#include "Luau/Location.h" #include "Luau/Location.h"
#include "Luau/Quantify.h" #include "Luau/Quantify.h"
#include "Luau/ToString.h" #include "Luau/ToString.h"
#include "Luau/Unifier.h" #include "Luau/Unifier.h"
#include "Luau/VisitTypeVar.h"
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false); LUAU_FASTFLAGVARIABLE(DebugLuauLogSolver, false);
LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false); LUAU_FASTFLAGVARIABLE(DebugLuauLogSolverToJson, false);
@ -37,6 +39,170 @@ static void dumpConstraints(NotNull<Scope> scope, ToStringOptions& opts)
dumpConstraints(child, opts); dumpConstraints(child, opts);
} }
static std::pair<std::vector<TypeId>, std::vector<TypePackId>> saturateArguments(
const TypeFun& fn, const std::vector<TypeId>& rawTypeArguments, const std::vector<TypePackId>& rawPackArguments, TypeArena* arena)
{
std::vector<TypeId> saturatedTypeArguments;
std::vector<TypeId> extraTypes;
std::vector<TypePackId> saturatedPackArguments;
for (size_t i = 0; i < rawTypeArguments.size(); ++i)
{
TypeId ty = rawTypeArguments[i];
if (i < fn.typeParams.size())
saturatedTypeArguments.push_back(ty);
else
extraTypes.push_back(ty);
}
// If we collected extra types, put them in a type pack now. This case is
// mutually exclusive with the type pack -> type conversion we do below:
// extraTypes will only have elements in it if we have more types than we
// have parameter slots for them to go into.
if (!extraTypes.empty())
{
saturatedPackArguments.push_back(arena->addTypePack(extraTypes));
}
for (size_t i = 0; i < rawPackArguments.size(); ++i)
{
TypePackId tp = rawPackArguments[i];
// If we are short on regular type saturatedTypeArguments and we have a single
// element type pack, we can decompose that to the type it contains and
// use that as a type parameter.
if (saturatedTypeArguments.size() < fn.typeParams.size() && size(tp) == 1 && finite(tp) && first(tp) && saturatedPackArguments.empty())
{
saturatedTypeArguments.push_back(*first(tp));
}
else
{
saturatedPackArguments.push_back(tp);
}
}
size_t typesProvided = saturatedTypeArguments.size();
size_t typesRequired = fn.typeParams.size();
size_t packsProvided = saturatedPackArguments.size();
size_t packsRequired = fn.typePackParams.size();
// Extra types should be accumulated in extraTypes, not saturatedTypeArguments. Extra
// packs will be accumulated in saturatedPackArguments, so we don't have an
// assertion for that.
LUAU_ASSERT(typesProvided <= typesRequired);
// If we didn't provide enough types, but we did provide a type pack, we
// don't want to use defaults. The rationale for this is that if the user
// provides a pack but doesn't provide enough types, we want to report an
// error, rather than simply using the default saturatedTypeArguments, if they exist. If
// they did provide enough types, but not enough packs, we of course want to
// use the default packs.
bool needsDefaults = (typesProvided < typesRequired && packsProvided == 0) || (typesProvided == typesRequired && packsProvided < packsRequired);
if (needsDefaults)
{
// Default types can reference earlier types. It's legal to write
// something like
// type T<A, B = A> = (A, B) -> number
// and we need to respect that. We use an ApplyTypeFunction for this.
ApplyTypeFunction atf{arena};
for (size_t i = 0; i < typesProvided; ++i)
atf.typeArguments[fn.typeParams[i].ty] = saturatedTypeArguments[i];
for (size_t i = typesProvided; i < typesRequired; ++i)
{
TypeId defaultTy = fn.typeParams[i].defaultValue.value_or(nullptr);
// We will fill this in with the error type later.
if (!defaultTy)
break;
TypeId instantiatedDefault = atf.substitute(defaultTy).value_or(getSingletonTypes().errorRecoveryType());
atf.typeArguments[fn.typeParams[i].ty] = instantiatedDefault;
saturatedTypeArguments.push_back(instantiatedDefault);
}
for (size_t i = 0; i < packsProvided; ++i)
{
atf.typePackArguments[fn.typePackParams[i].tp] = saturatedPackArguments[i];
}
for (size_t i = packsProvided; i < packsRequired; ++i)
{
TypePackId defaultTp = fn.typePackParams[i].defaultValue.value_or(nullptr);
// We will fill this in with the error type pack later.
if (!defaultTp)
break;
TypePackId instantiatedDefault = atf.substitute(defaultTp).value_or(getSingletonTypes().errorRecoveryTypePack());
atf.typePackArguments[fn.typePackParams[i].tp] = instantiatedDefault;
saturatedPackArguments.push_back(instantiatedDefault);
}
}
// If we didn't create an extra type pack from overflowing parameter packs,
// and we're still missing a type pack, plug in an empty type pack as the
// value of the empty packs.
if (extraTypes.empty() && saturatedPackArguments.size() + 1 == fn.typePackParams.size())
{
saturatedPackArguments.push_back(arena->addTypePack({}));
}
// We need to have _something_ when we substitute the generic saturatedTypeArguments,
// even if they're missing, so we use the error type as a filler.
for (size_t i = saturatedTypeArguments.size(); i < typesRequired; ++i)
{
saturatedTypeArguments.push_back(getSingletonTypes().errorRecoveryType());
}
for (size_t i = saturatedPackArguments.size(); i < packsRequired; ++i)
{
saturatedPackArguments.push_back(getSingletonTypes().errorRecoveryTypePack());
}
// At this point, these two conditions should be true. If they aren't we
// will run into access violations.
LUAU_ASSERT(saturatedTypeArguments.size() == fn.typeParams.size());
LUAU_ASSERT(saturatedPackArguments.size() == fn.typePackParams.size());
return {saturatedTypeArguments, saturatedPackArguments};
}
bool InstantiationSignature::operator==(const InstantiationSignature& rhs) const
{
return fn == rhs.fn && arguments == rhs.arguments && packArguments == rhs.packArguments;
}
size_t HashInstantiationSignature::operator()(const InstantiationSignature& signature) const
{
size_t hash = std::hash<TypeId>{}(signature.fn.type);
for (const GenericTypeDefinition& p : signature.fn.typeParams)
{
hash ^= (std::hash<TypeId>{}(p.ty) << 1);
}
for (const GenericTypePackDefinition& p : signature.fn.typePackParams)
{
hash ^= (std::hash<TypePackId>{}(p.tp) << 1);
}
for (const TypeId a : signature.arguments)
{
hash ^= (std::hash<TypeId>{}(a) << 1);
}
for (const TypePackId a : signature.packArguments)
{
hash ^= (std::hash<TypePackId>{}(a) << 1);
}
return hash;
}
void dump(NotNull<Scope> rootScope, ToStringOptions& opts) void dump(NotNull<Scope> rootScope, ToStringOptions& opts)
{ {
printf("constraints:\n"); printf("constraints:\n");
@ -77,6 +243,7 @@ void ConstraintSolver::run()
return; return;
ToStringOptions opts; ToStringOptions opts;
opts.exhaustive = true;
if (FFlag::DebugLuauLogSolver) if (FFlag::DebugLuauLogSolver)
{ {
@ -186,6 +353,8 @@ bool ConstraintSolver::tryDispatch(NotNull<const Constraint> constraint, bool fo
success = tryDispatch(*bc, constraint, force); success = tryDispatch(*bc, constraint, force);
else if (auto nc = get<NameConstraint>(*constraint)) else if (auto nc = get<NameConstraint>(*constraint))
success = tryDispatch(*nc, constraint); success = tryDispatch(*nc, constraint);
else if (auto taec = get<TypeAliasExpansionConstraint>(*constraint))
success = tryDispatch(*taec, constraint);
else else
LUAU_ASSERT(0); LUAU_ASSERT(0);
@ -325,6 +494,198 @@ bool ConstraintSolver::tryDispatch(const NameConstraint& c, NotNull<const Constr
return true; return true;
} }
struct InfiniteTypeFinder : TypeVarOnceVisitor
{
ConstraintSolver* solver;
const InstantiationSignature& signature;
bool foundInfiniteType = false;
explicit InfiniteTypeFinder(ConstraintSolver* solver, const InstantiationSignature& signature)
: solver(solver)
, signature(signature)
{
}
bool visit(TypeId ty, const PendingExpansionTypeVar& petv) override
{
auto [typeArguments, packArguments] = saturateArguments(petv.fn, petv.typeArguments, petv.packArguments, solver->arena);
if (follow(petv.fn.type) == follow(signature.fn.type) && (signature.arguments != typeArguments || signature.packArguments != packArguments))
{
foundInfiniteType = true;
return false;
}
return true;
}
};
struct InstantiationQueuer : TypeVarOnceVisitor
{
ConstraintSolver* solver;
const InstantiationSignature& signature;
explicit InstantiationQueuer(ConstraintSolver* solver, const InstantiationSignature& signature)
: solver(solver)
, signature(signature)
{
}
bool visit(TypeId ty, const PendingExpansionTypeVar& petv) override
{
solver->pushConstraint(TypeAliasExpansionConstraint{ty});
return false;
}
};
bool ConstraintSolver::tryDispatch(const TypeAliasExpansionConstraint& c, NotNull<const Constraint> constraint)
{
const PendingExpansionTypeVar* petv = get<PendingExpansionTypeVar>(follow(c.target));
if (!petv)
{
unblock(c.target);
return true;
}
auto bindResult = [this, &c](TypeId result) {
asMutable(c.target)->ty.emplace<BoundTypeVar>(result);
unblock(c.target);
};
// If there are no parameters to the type function we can just use the type
// directly.
if (petv->fn.typeParams.empty() && petv->fn.typePackParams.empty())
{
bindResult(petv->fn.type);
return true;
}
auto [typeArguments, packArguments] = saturateArguments(petv->fn, petv->typeArguments, petv->packArguments, arena);
bool sameTypes =
std::equal(typeArguments.begin(), typeArguments.end(), petv->fn.typeParams.begin(), petv->fn.typeParams.end(), [](auto&& itp, auto&& p) {
return itp == p.ty;
});
bool samePacks = std::equal(
packArguments.begin(), packArguments.end(), petv->fn.typePackParams.begin(), petv->fn.typePackParams.end(), [](auto&& itp, auto&& p) {
return itp == p.tp;
});
// If we're instantiating the type with its generic saturatedTypeArguments we are
// performing the identity substitution. We can just short-circuit and bind
// to the TypeFun's type.
if (sameTypes && samePacks)
{
bindResult(petv->fn.type);
return true;
}
InstantiationSignature signature{
petv->fn,
typeArguments,
packArguments,
};
// If we use the same signature, we don't need to bother trying to
// instantiate the alias again, since the instantiation should be
// deterministic.
if (TypeId* cached = instantiatedAliases.find(signature))
{
bindResult(*cached);
return true;
}
// In order to prevent infinite types from being expanded and causing us to
// cycle infinitely, we need to scan the type function for cases where we
// expand the same alias with different type saturatedTypeArguments. See
// https://github.com/Roblox/luau/pull/68 for the RFC responsible for this.
// This is a little nicer than using a recursion limit because we can catch
// the infinite expansion before actually trying to expand it.
InfiniteTypeFinder itf{this, signature};
itf.traverse(petv->fn.type);
if (itf.foundInfiniteType)
{
// TODO (CLI-56761): Report an error.
bindResult(getSingletonTypes().errorRecoveryType());
return true;
}
ApplyTypeFunction applyTypeFunction{arena};
for (size_t i = 0; i < typeArguments.size(); ++i)
{
applyTypeFunction.typeArguments[petv->fn.typeParams[i].ty] = typeArguments[i];
}
for (size_t i = 0; i < packArguments.size(); ++i)
{
applyTypeFunction.typePackArguments[petv->fn.typePackParams[i].tp] = packArguments[i];
}
std::optional<TypeId> maybeInstantiated = applyTypeFunction.substitute(petv->fn.type);
// Note that ApplyTypeFunction::encounteredForwardedType is never set in
// DCR, because we do not use free types for forward-declared generic
// aliases.
if (!maybeInstantiated.has_value())
{
// TODO (CLI-56761): Report an error.
bindResult(getSingletonTypes().errorRecoveryType());
return true;
}
TypeId instantiated = *maybeInstantiated;
TypeId target = follow(instantiated);
// Type function application will happily give us the exact same type if
// there are e.g. generic saturatedTypeArguments that go unused.
bool needsClone = follow(petv->fn.type) == target;
// Only tables have the properties we're trying to set.
TableTypeVar* ttv = getMutableTableType(target);
if (ttv)
{
if (needsClone)
{
// Substitution::clone is a shallow clone. If this is a
// metatable type, we want to mutate its table, so we need to
// explicitly clone that table as well. If we don't, we will
// mutate another module's type surface and cause a
// use-after-free.
if (get<MetatableTypeVar>(target))
{
instantiated = applyTypeFunction.clone(target);
MetatableTypeVar* mtv = getMutable<MetatableTypeVar>(instantiated);
mtv->table = applyTypeFunction.clone(mtv->table);
ttv = getMutable<TableTypeVar>(mtv->table);
}
else if (get<TableTypeVar>(target))
{
instantiated = applyTypeFunction.clone(target);
ttv = getMutable<TableTypeVar>(instantiated);
}
target = follow(instantiated);
}
ttv->instantiatedTypeParams = typeArguments;
ttv->instantiatedTypePackParams = packArguments;
// TODO: Fill in definitionModuleName.
}
bindResult(target);
// The application is not recursive, so we need to queue up application of
// any child type function instantiations within the result in order for it
// to be complete.
InstantiationQueuer queuer{this, signature};
queuer.traverse(target);
instantiatedAliases[signature] = target;
return true;
}
void ConstraintSolver::block_(BlockedConstraintId target, NotNull<const Constraint> constraint) void ConstraintSolver::block_(BlockedConstraintId target, NotNull<const Constraint> constraint)
{ {
blocked[target].push_back(constraint); blocked[target].push_back(constraint);
@ -388,7 +749,7 @@ void ConstraintSolver::unblock(TypePackId progressed)
bool ConstraintSolver::isBlocked(TypeId ty) bool ConstraintSolver::isBlocked(TypeId ty)
{ {
return nullptr != get<BlockedTypeVar>(follow(ty)); return nullptr != get<BlockedTypeVar>(follow(ty)) || nullptr != get<PendingExpansionTypeVar>(follow(ty));
} }
bool ConstraintSolver::isBlocked(NotNull<const Constraint> constraint) bool ConstraintSolver::isBlocked(NotNull<const Constraint> constraint)
@ -415,4 +776,12 @@ void ConstraintSolver::unify(TypePackId subPack, TypePackId superPack)
u.log.commit(); u.log.commit();
} }
void ConstraintSolver::pushConstraint(ConstraintV cv)
{
std::unique_ptr<Constraint> c = std::make_unique<Constraint>(std::move(cv));
NotNull<Constraint> borrow = NotNull(c.get());
solverConstraints.push_back(std::move(c));
unsolvedConstraints.push_back(borrow);
}
} // namespace Luau } // namespace Luau

View File

@ -2,45 +2,39 @@
#include "Luau/ConstraintSolverLogger.h" #include "Luau/ConstraintSolverLogger.h"
#include "Luau/JsonEmitter.h"
namespace Luau namespace Luau
{ {
static std::string dumpScopeAndChildren(const Scope* scope, ToStringOptions& opts) static void dumpScopeAndChildren(const Scope* scope, Json::JsonEmitter& emitter, ToStringOptions& opts)
{ {
std::string output = "{\"bindings\":{"; emitter.writeRaw("{");
Json::write(emitter, "bindings");
emitter.writeRaw(":");
Json::ObjectEmitter o = emitter.writeObject();
bool comma = false;
for (const auto& [name, binding] : scope->bindings) for (const auto& [name, binding] : scope->bindings)
{ {
if (comma)
output += ",";
output += "\"";
output += name.c_str();
output += "\": \"";
ToStringResult result = toStringDetailed(binding.typeId, opts); ToStringResult result = toStringDetailed(binding.typeId, opts);
opts.nameMap = std::move(result.nameMap); opts.nameMap = std::move(result.nameMap);
output += result.name; o.writePair(name.c_str(), result.name);
output += "\"";
comma = true;
} }
output += "},\"children\":["; o.finish();
comma = false; emitter.writeRaw(",");
Json::write(emitter, "children");
emitter.writeRaw(":");
Json::ArrayEmitter a = emitter.writeArray();
for (const Scope* child : scope->children) for (const Scope* child : scope->children)
{ {
if (comma) dumpScopeAndChildren(child, emitter, opts);
output += ",";
output += dumpScopeAndChildren(child, opts);
comma = true;
} }
output += "]}"; a.finish();
return output; emitter.writeRaw("}");
} }
static std::string dumpConstraintsToDot(std::vector<NotNull<const Constraint>>& constraints, ToStringOptions& opts) static std::string dumpConstraintsToDot(std::vector<NotNull<const Constraint>>& constraints, ToStringOptions& opts)
@ -80,51 +74,49 @@ static std::string dumpConstraintsToDot(std::vector<NotNull<const Constraint>>&
std::string ConstraintSolverLogger::compileOutput() std::string ConstraintSolverLogger::compileOutput()
{ {
std::string output = "["; Json::JsonEmitter emitter;
bool comma = false; emitter.writeRaw("[");
for (const std::string& snapshot : snapshots) for (const std::string& snapshot : snapshots)
{ {
if (comma) emitter.writeComma();
output += ","; emitter.writeRaw(snapshot);
output += snapshot;
comma = true;
} }
output += "]"; emitter.writeRaw("]");
return output; return emitter.str();
} }
void ConstraintSolverLogger::captureBoundarySnapshot(const Scope* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints) void ConstraintSolverLogger::captureBoundarySnapshot(const Scope* rootScope, std::vector<NotNull<const Constraint>>& unsolvedConstraints)
{ {
std::string snapshot = "{\"type\":\"boundary\",\"rootScope\":"; Json::JsonEmitter emitter;
Json::ObjectEmitter o = emitter.writeObject();
o.writePair("type", "boundary");
o.writePair("constraintGraph", dumpConstraintsToDot(unsolvedConstraints, opts));
emitter.writeComma();
Json::write(emitter, "rootScope");
emitter.writeRaw(":");
dumpScopeAndChildren(rootScope, emitter, opts);
o.finish();
snapshot += dumpScopeAndChildren(rootScope, opts); snapshots.push_back(emitter.str());
snapshot += ",\"constraintGraph\":\"";
snapshot += dumpConstraintsToDot(unsolvedConstraints, opts);
snapshot += "\"}";
snapshots.push_back(std::move(snapshot));
} }
void ConstraintSolverLogger::prepareStepSnapshot( void ConstraintSolverLogger::prepareStepSnapshot(
const Scope* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints) const Scope* rootScope, NotNull<const Constraint> current, std::vector<NotNull<const Constraint>>& unsolvedConstraints)
{ {
// LUAU_ASSERT(!preparedSnapshot); Json::JsonEmitter emitter;
Json::ObjectEmitter o = emitter.writeObject();
o.writePair("type", "step");
o.writePair("constraintGraph", dumpConstraintsToDot(unsolvedConstraints, opts));
o.writePair("currentId", std::to_string(reinterpret_cast<size_t>(current.get())));
o.writePair("current", toString(*current, opts));
emitter.writeComma();
Json::write(emitter, "rootScope");
emitter.writeRaw(":");
dumpScopeAndChildren(rootScope, emitter, opts);
o.finish();
std::string snapshot = "{\"type\":\"step\",\"rootScope\":"; preparedSnapshot = emitter.str();
snapshot += dumpScopeAndChildren(rootScope, opts);
snapshot += ",\"constraintGraph\":\"";
snapshot += dumpConstraintsToDot(unsolvedConstraints, opts);
snapshot += "\",\"currentId\":\"";
snapshot += std::to_string(reinterpret_cast<size_t>(current.get()));
snapshot += "\",\"current\":\"";
snapshot += toString(*current, opts);
snapshot += "\"}";
preparedSnapshot = std::move(snapshot);
} }
void ConstraintSolverLogger::commitPreparedStepSnapshot() void ConstraintSolverLogger::commitPreparedStepSnapshot()

View File

@ -77,6 +77,58 @@ static void generateDocumentationSymbols(TypeId ty, const std::string& rootName)
} }
} }
LoadDefinitionFileResult Frontend::loadDefinitionFile(std::string_view source, const std::string& packageName)
{
if (!FFlag::DebugLuauDeferredConstraintResolution)
return Luau::loadDefinitionFile(typeChecker, typeChecker.globalScope, source, packageName);
LUAU_TIMETRACE_SCOPE("loadDefinitionFile", "Frontend");
Luau::Allocator allocator;
Luau::AstNameTable names(allocator);
ParseOptions options;
options.allowDeclarationSyntax = true;
Luau::ParseResult parseResult = Luau::Parser::parse(source.data(), source.size(), names, allocator, options);
if (parseResult.errors.size() > 0)
return LoadDefinitionFileResult{false, parseResult, nullptr};
Luau::SourceModule module;
module.root = parseResult.root;
module.mode = Mode::Definition;
ModulePtr checkedModule = check(module, Mode::Definition, globalScope);
if (checkedModule->errors.size() > 0)
return LoadDefinitionFileResult{false, parseResult, checkedModule};
CloneState cloneState;
for (const auto& [name, ty] : checkedModule->declaredGlobals)
{
TypeId globalTy = clone(ty, globalTypes, cloneState);
std::string documentationSymbol = packageName + "/global/" + name;
generateDocumentationSymbols(globalTy, documentationSymbol);
globalScope->bindings[typeChecker.globalNames.names->getOrAdd(name.c_str())] = {globalTy, Location(), false, {}, documentationSymbol};
persist(globalTy);
}
for (const auto& [name, ty] : checkedModule->getModuleScope()->exportedTypeBindings)
{
TypeFun globalTy = clone(ty, globalTypes, cloneState);
std::string documentationSymbol = packageName + "/globaltype/" + name;
generateDocumentationSymbols(globalTy.type, documentationSymbol);
globalScope->exportedTypeBindings[name] = globalTy;
persist(globalTy.type);
}
return LoadDefinitionFileResult{true, parseResult, checkedModule};
}
LoadDefinitionFileResult loadDefinitionFile(TypeChecker& typeChecker, ScopePtr targetScope, std::string_view source, const std::string& packageName) LoadDefinitionFileResult loadDefinitionFile(TypeChecker& typeChecker, ScopePtr targetScope, std::string_view source, const std::string& packageName)
{ {
LUAU_TIMETRACE_SCOPE("loadDefinitionFile", "Frontend"); LUAU_TIMETRACE_SCOPE("loadDefinitionFile", "Frontend");
@ -770,14 +822,7 @@ NotNull<Scope> Frontend::getGlobalScope()
{ {
if (!globalScope) if (!globalScope)
{ {
const SingletonTypes& singletonTypes = getSingletonTypes(); globalScope = typeChecker.globalScope;
globalScope = std::make_unique<Scope>(singletonTypes.anyTypePack);
globalScope->typeBindings["nil"] = singletonTypes.nilType;
globalScope->typeBindings["number"] = singletonTypes.numberType;
globalScope->typeBindings["string"] = singletonTypes.stringType;
globalScope->typeBindings["boolean"] = singletonTypes.booleanType;
globalScope->typeBindings["thread"] = singletonTypes.threadType;
} }
return NotNull(globalScope.get()); return NotNull(globalScope.get());

View File

@ -4,6 +4,8 @@
#include "Luau/TxnLog.h" #include "Luau/TxnLog.h"
#include "Luau/TypeArena.h" #include "Luau/TypeArena.h"
LUAU_FASTFLAG(LuauClassTypeVarsInSubstitution)
namespace Luau namespace Luau
{ {
@ -31,6 +33,8 @@ bool Instantiation::ignoreChildren(TypeId ty)
{ {
if (log->getMutable<FunctionTypeVar>(ty)) if (log->getMutable<FunctionTypeVar>(ty))
return true; return true;
else if (FFlag::LuauClassTypeVarsInSubstitution && get<ClassTypeVar>(ty))
return true;
else else
return false; return false;
} }

View File

@ -0,0 +1,220 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/JsonEmitter.h"
#include "Luau/StringUtils.h"
#include <string.h>
namespace Luau::Json
{
static constexpr int CHUNK_SIZE = 1024;
ObjectEmitter::ObjectEmitter(NotNull<JsonEmitter> emitter)
: emitter(emitter), finished(false)
{
comma = emitter->pushComma();
emitter->writeRaw('{');
}
ObjectEmitter::~ObjectEmitter()
{
finish();
}
void ObjectEmitter::finish()
{
if (finished)
return;
emitter->writeRaw('}');
emitter->popComma(comma);
finished = true;
}
ArrayEmitter::ArrayEmitter(NotNull<JsonEmitter> emitter)
: emitter(emitter), finished(false)
{
comma = emitter->pushComma();
emitter->writeRaw('[');
}
ArrayEmitter::~ArrayEmitter()
{
finish();
}
void ArrayEmitter::finish()
{
if (finished)
return;
emitter->writeRaw(']');
emitter->popComma(comma);
finished = true;
}
JsonEmitter::JsonEmitter()
{
newChunk();
}
std::string JsonEmitter::str()
{
return join(chunks, "");
}
bool JsonEmitter::pushComma()
{
bool current = comma;
comma = false;
return current;
}
void JsonEmitter::popComma(bool c)
{
comma = c;
}
void JsonEmitter::writeRaw(std::string_view sv)
{
if (sv.size() > CHUNK_SIZE)
{
chunks.emplace_back(sv);
newChunk();
return;
}
auto& chunk = chunks.back();
if (chunk.size() + sv.size() < CHUNK_SIZE)
{
chunk.append(sv.data(), sv.size());
return;
}
size_t prefix = CHUNK_SIZE - chunk.size();
chunk.append(sv.data(), prefix);
newChunk();
chunks.back().append(sv.data() + prefix, sv.size() - prefix);
}
void JsonEmitter::writeRaw(char c)
{
writeRaw(std::string_view{&c, 1});
}
void write(JsonEmitter& emitter, bool b)
{
if (b)
emitter.writeRaw("true");
else
emitter.writeRaw("false");
}
void write(JsonEmitter& emitter, double d)
{
emitter.writeRaw(std::to_string(d));
}
void write(JsonEmitter& emitter, int i)
{
emitter.writeRaw(std::to_string(i));
}
void write(JsonEmitter& emitter, long i)
{
emitter.writeRaw(std::to_string(i));
}
void write(JsonEmitter& emitter, long long i)
{
emitter.writeRaw(std::to_string(i));
}
void write(JsonEmitter& emitter, unsigned int i)
{
emitter.writeRaw(std::to_string(i));
}
void write(JsonEmitter& emitter, unsigned long i)
{
emitter.writeRaw(std::to_string(i));
}
void write(JsonEmitter& emitter, unsigned long long i)
{
emitter.writeRaw(std::to_string(i));
}
void write(JsonEmitter& emitter, std::string_view sv)
{
emitter.writeRaw('\"');
for (char c : sv)
{
if (c == '"')
emitter.writeRaw("\\\"");
else if (c == '\\')
emitter.writeRaw("\\\\");
else if (c == '\n')
emitter.writeRaw("\\n");
else if (c < ' ')
emitter.writeRaw(format("\\u%04x", c));
else
emitter.writeRaw(c);
}
emitter.writeRaw('\"');
}
void write(JsonEmitter& emitter, char c)
{
write(emitter, std::string_view{&c, 1});
}
void write(JsonEmitter& emitter, const char* str)
{
write(emitter, std::string_view{str, strlen(str)});
}
void write(JsonEmitter& emitter, const std::string& str)
{
write(emitter, std::string_view{str});
}
void write(JsonEmitter& emitter, std::nullptr_t)
{
emitter.writeRaw("null");
}
void write(JsonEmitter& emitter, std::nullopt_t)
{
emitter.writeRaw("null");
}
void JsonEmitter::writeComma()
{
if (comma)
writeRaw(',');
else
comma = true;
}
ObjectEmitter JsonEmitter::writeObject()
{
return ObjectEmitter{NotNull(this)};
}
ArrayEmitter JsonEmitter::writeArray()
{
return ArrayEmitter{NotNull(this)};
}
void JsonEmitter::newChunk()
{
chunks.emplace_back();
chunks.back().reserve(CHUNK_SIZE);
}
} // namespace Luau::Json

View File

@ -48,6 +48,7 @@ static const char* kWarningNames[] = {
"DuplicateCondition", "DuplicateCondition",
"MisleadingAndOr", "MisleadingAndOr",
"CommentDirective", "CommentDirective",
"IntegerParsing",
}; };
// clang-format on // clang-format on
@ -2589,6 +2590,45 @@ private:
} }
}; };
class LintIntegerParsing : AstVisitor
{
public:
LUAU_NOINLINE static void process(LintContext& context)
{
LintIntegerParsing pass;
pass.context = &context;
context.root->visit(&pass);
}
private:
LintContext* context;
bool visit(AstExprConstantNumber* node) override
{
switch (node->parseResult)
{
case ConstantNumberParseResult::Ok:
case ConstantNumberParseResult::Malformed:
break;
case ConstantNumberParseResult::BinOverflow:
emitWarning(*context, LintWarning::Code_IntegerParsing, node->location,
"Binary number literal exceeded available precision and has been truncated to 2^64");
break;
case ConstantNumberParseResult::HexOverflow:
emitWarning(*context, LintWarning::Code_IntegerParsing, node->location,
"Hexadecimal number literal exceeded available precision and has been truncated to 2^64");
break;
case ConstantNumberParseResult::DoublePrefix:
emitWarning(*context, LintWarning::Code_IntegerParsing, node->location,
"Hexadecimal number literal has a double prefix, which will fail to parse in the future; remove the extra 0x to fix");
break;
}
return true;
}
};
static void fillBuiltinGlobals(LintContext& context, const AstNameTable& names, const ScopePtr& env) static void fillBuiltinGlobals(LintContext& context, const AstNameTable& names, const ScopePtr& env)
{ {
ScopePtr current = env; ScopePtr current = env;
@ -2810,6 +2850,9 @@ std::vector<LintWarning> lint(AstStat* root, const AstNameTable& names, const Sc
if (context.warningEnabled(LintWarning::Code_CommentDirective)) if (context.warningEnabled(LintWarning::Code_CommentDirective))
lintComments(context, hotcomments); lintComments(context, hotcomments);
if (context.warningEnabled(LintWarning::Code_IntegerParsing))
LintIntegerParsing::process(context);
std::sort(context.result.begin(), context.result.end(), WarningComparator()); std::sort(context.result.begin(), context.result.end(), WarningComparator());
return context.result; return context.result;

View File

@ -15,7 +15,6 @@
#include <algorithm> #include <algorithm>
LUAU_FASTFLAG(LuauLowerBoundsCalculation); LUAU_FASTFLAG(LuauLowerBoundsCalculation);
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative);
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAGVARIABLE(LuauForceExportSurfacesToBeNormal, false); LUAU_FASTFLAGVARIABLE(LuauForceExportSurfacesToBeNormal, false);
@ -140,20 +139,17 @@ void Module::clonePublicInterface(InternalErrorReporter& ice)
{ {
normalize(tf.type, interfaceTypes, ice); normalize(tf.type, interfaceTypes, ice);
if (FFlag::LuauNormalizeFlagIsConservative) // We're about to freeze the memory. We know that the flag is conservative by design. Cyclic tables
// won't be marked normal. If the types aren't normal by now, they never will be.
forceNormal.traverse(tf.type);
for (GenericTypeDefinition param : tf.typeParams)
{ {
// We're about to freeze the memory. We know that the flag is conservative by design. Cyclic tables forceNormal.traverse(param.ty);
// won't be marked normal. If the types aren't normal by now, they never will be.
forceNormal.traverse(tf.type);
for (GenericTypeDefinition param : tf.typeParams)
{
forceNormal.traverse(param.ty);
if (param.defaultValue) if (param.defaultValue)
{ {
normalize(*param.defaultValue, interfaceTypes, ice); normalize(*param.defaultValue, interfaceTypes, ice);
forceNormal.traverse(*param.defaultValue); forceNormal.traverse(*param.defaultValue);
}
} }
} }
} }

View File

@ -13,7 +13,6 @@ LUAU_FASTFLAGVARIABLE(DebugLuauCopyBeforeNormalizing, false)
// This could theoretically be 2000 on amd64, but x86 requires this. // This could theoretically be 2000 on amd64, but x86 requires this.
LUAU_FASTINTVARIABLE(LuauNormalizeIterationLimit, 1200); LUAU_FASTINTVARIABLE(LuauNormalizeIterationLimit, 1200);
LUAU_FASTFLAGVARIABLE(LuauNormalizeCombineTableFix, false); LUAU_FASTFLAGVARIABLE(LuauNormalizeCombineTableFix, false);
LUAU_FASTFLAGVARIABLE(LuauNormalizeFlagIsConservative, false);
LUAU_FASTFLAGVARIABLE(LuauFixNormalizationOfCyclicUnions, false); LUAU_FASTFLAGVARIABLE(LuauFixNormalizationOfCyclicUnions, false);
LUAU_FASTFLAG(LuauUnknownAndNeverType) LUAU_FASTFLAG(LuauUnknownAndNeverType)
LUAU_FASTFLAG(LuauQuantifyConstrained) LUAU_FASTFLAG(LuauQuantifyConstrained)
@ -89,13 +88,7 @@ static bool areNormal_(const T& t, const std::unordered_set<void*>& seen, Intern
if (count >= FInt::LuauNormalizeIterationLimit) if (count >= FInt::LuauNormalizeIterationLimit)
ice.ice("Luau::areNormal hit iteration limit"); ice.ice("Luau::areNormal hit iteration limit");
if (FFlag::LuauNormalizeFlagIsConservative) return ty->normal;
return ty->normal;
else
{
// The follow is here because a bound type may not be normal, but the bound type is normal.
return ty->normal || follow(ty)->normal || seen.find(asMutable(ty)) != seen.end();
}
}; };
return std::all_of(begin(t), end(t), isNormal); return std::all_of(begin(t), end(t), isNormal);

View File

@ -7,7 +7,6 @@
#include "Luau/TxnLog.h" #include "Luau/TxnLog.h"
#include "Luau/VisitTypeVar.h" #include "Luau/VisitTypeVar.h"
LUAU_FASTFLAG(LuauAlwaysQuantify);
LUAU_FASTFLAG(DebugLuauSharedSelf) LUAU_FASTFLAG(DebugLuauSharedSelf)
LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution); LUAU_FASTFLAG(DebugLuauDeferredConstraintResolution);
LUAU_FASTFLAGVARIABLE(LuauQuantifyConstrained, false) LUAU_FASTFLAGVARIABLE(LuauQuantifyConstrained, false)
@ -203,16 +202,8 @@ void quantify(TypeId ty, TypeLevel level)
FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty); FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty);
LUAU_ASSERT(ftv); LUAU_ASSERT(ftv);
if (FFlag::LuauAlwaysQuantify) ftv->generics.insert(ftv->generics.end(), q.generics.begin(), q.generics.end());
{ ftv->genericPacks.insert(ftv->genericPacks.end(), q.genericPacks.begin(), q.genericPacks.end());
ftv->generics.insert(ftv->generics.end(), q.generics.begin(), q.generics.end());
ftv->genericPacks.insert(ftv->genericPacks.end(), q.genericPacks.begin(), q.genericPacks.end());
}
else
{
ftv->generics = q.generics;
ftv->genericPacks = q.genericPacks;
}
} }
} }
@ -223,16 +214,8 @@ void quantify(TypeId ty, Scope* scope)
FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty); FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty);
LUAU_ASSERT(ftv); LUAU_ASSERT(ftv);
if (FFlag::LuauAlwaysQuantify) ftv->generics.insert(ftv->generics.end(), q.generics.begin(), q.generics.end());
{ ftv->genericPacks.insert(ftv->genericPacks.end(), q.genericPacks.begin(), q.genericPacks.end());
ftv->generics.insert(ftv->generics.end(), q.generics.begin(), q.generics.end());
ftv->genericPacks.insert(ftv->genericPacks.end(), q.genericPacks.begin(), q.genericPacks.end());
}
else
{
ftv->generics = q.generics;
ftv->genericPacks = q.genericPacks;
}
if (ftv->generics.empty() && ftv->genericPacks.empty() && !q.seenMutableType && !q.seenGenericType) if (ftv->generics.empty() && ftv->genericPacks.empty() && !q.seenMutableType && !q.seenGenericType)
ftv->hasNoGenerics = true; ftv->hasNoGenerics = true;

View File

@ -122,7 +122,7 @@ std::optional<TypeId> Scope::lookup(Symbol sym)
} }
} }
std::optional<TypeId> Scope::lookupTypeBinding(const Name& name) std::optional<TypeFun> Scope::lookupTypeBinding(const Name& name)
{ {
Scope* s = this; Scope* s = this;
while (s) while (s)

View File

@ -9,9 +9,12 @@
#include <stdexcept> #include <stdexcept>
LUAU_FASTFLAGVARIABLE(LuauAnyificationMustClone, false) LUAU_FASTFLAGVARIABLE(LuauAnyificationMustClone, false)
LUAU_FASTFLAGVARIABLE(LuauSubstitutionFixMissingFields, false)
LUAU_FASTFLAG(LuauLowerBoundsCalculation) LUAU_FASTFLAG(LuauLowerBoundsCalculation)
LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000) LUAU_FASTINTVARIABLE(LuauTarjanChildLimit, 10000)
LUAU_FASTFLAGVARIABLE(LuauClassTypeVarsInSubstitution, false)
LUAU_FASTFLAG(LuauUnknownAndNeverType) LUAU_FASTFLAG(LuauUnknownAndNeverType)
LUAU_FASTFLAGVARIABLE(LuauSubstitutionReentrant, false)
namespace Luau namespace Luau
{ {
@ -28,6 +31,14 @@ void Tarjan::visitChildren(TypeId ty, int index)
if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty)) if (const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty))
{ {
if (FFlag::LuauSubstitutionFixMissingFields)
{
for (TypeId generic : ftv->generics)
visitChild(generic);
for (TypePackId genericPack : ftv->genericPacks)
visitChild(genericPack);
}
visitChild(ftv->argTypes); visitChild(ftv->argTypes);
visitChild(ftv->retTypes); visitChild(ftv->retTypes);
} }
@ -68,6 +79,25 @@ void Tarjan::visitChildren(TypeId ty, int index)
for (TypeId part : ctv->parts) for (TypeId part : ctv->parts)
visitChild(part); visitChild(part);
} }
else if (const PendingExpansionTypeVar* petv = get<PendingExpansionTypeVar>(ty))
{
for (TypeId a : petv->typeArguments)
visitChild(a);
for (TypePackId a : petv->packArguments)
visitChild(a);
}
else if (const ClassTypeVar* ctv = get<ClassTypeVar>(ty); FFlag::LuauClassTypeVarsInSubstitution && ctv)
{
for (auto [name, prop] : ctv->props)
visitChild(prop.type);
if (ctv->parent)
visitChild(*ctv->parent);
if (ctv->metatable)
visitChild(*ctv->metatable);
}
} }
void Tarjan::visitChildren(TypePackId tp, int index) void Tarjan::visitChildren(TypePackId tp, int index)
@ -267,6 +297,24 @@ TarjanResult Tarjan::visitRoot(TypePackId tp)
return loop(); return loop();
} }
void FindDirty::clearTarjan()
{
dirty.clear();
typeToIndex.clear();
packToIndex.clear();
indexToType.clear();
indexToPack.clear();
stack.clear();
onStack.clear();
lowlink.clear();
edgesTy.clear();
edgesTp.clear();
worklist.clear();
}
bool FindDirty::getDirty(int index) bool FindDirty::getDirty(int index)
{ {
if (dirty.size() <= size_t(index)) if (dirty.size() <= size_t(index))
@ -330,16 +378,46 @@ std::optional<TypeId> Substitution::substitute(TypeId ty)
{ {
ty = log->follow(ty); ty = log->follow(ty);
// clear algorithm state for reentrancy
if (FFlag::LuauSubstitutionReentrant)
clearTarjan();
auto result = findDirty(ty); auto result = findDirty(ty);
if (result != TarjanResult::Ok) if (result != TarjanResult::Ok)
return std::nullopt; return std::nullopt;
for (auto [oldTy, newTy] : newTypes) for (auto [oldTy, newTy] : newTypes)
if (!ignoreChildren(oldTy)) {
replaceChildren(newTy); if (FFlag::LuauSubstitutionReentrant)
{
if (!ignoreChildren(oldTy) && !replacedTypes.contains(newTy))
{
replaceChildren(newTy);
replacedTypes.insert(newTy);
}
}
else
{
if (!ignoreChildren(oldTy))
replaceChildren(newTy);
}
}
for (auto [oldTp, newTp] : newPacks) for (auto [oldTp, newTp] : newPacks)
if (!ignoreChildren(oldTp)) {
replaceChildren(newTp); if (FFlag::LuauSubstitutionReentrant)
{
if (!ignoreChildren(oldTp) && !replacedTypePacks.contains(newTp))
{
replaceChildren(newTp);
replacedTypePacks.insert(newTp);
}
}
else
{
if (!ignoreChildren(oldTp))
replaceChildren(newTp);
}
}
TypeId newTy = replace(ty); TypeId newTy = replace(ty);
return newTy; return newTy;
} }
@ -348,16 +426,46 @@ std::optional<TypePackId> Substitution::substitute(TypePackId tp)
{ {
tp = log->follow(tp); tp = log->follow(tp);
// clear algorithm state for reentrancy
if (FFlag::LuauSubstitutionReentrant)
clearTarjan();
auto result = findDirty(tp); auto result = findDirty(tp);
if (result != TarjanResult::Ok) if (result != TarjanResult::Ok)
return std::nullopt; return std::nullopt;
for (auto [oldTy, newTy] : newTypes) for (auto [oldTy, newTy] : newTypes)
if (!ignoreChildren(oldTy)) {
replaceChildren(newTy); if (FFlag::LuauSubstitutionReentrant)
{
if (!ignoreChildren(oldTy) && !replacedTypes.contains(newTy))
{
replaceChildren(newTy);
replacedTypes.insert(newTy);
}
}
else
{
if (!ignoreChildren(oldTy))
replaceChildren(newTy);
}
}
for (auto [oldTp, newTp] : newPacks) for (auto [oldTp, newTp] : newPacks)
if (!ignoreChildren(oldTp)) {
replaceChildren(newTp); if (FFlag::LuauSubstitutionReentrant)
{
if (!ignoreChildren(oldTp) && !replacedTypePacks.contains(newTp))
{
replaceChildren(newTp);
replacedTypePacks.insert(newTp);
}
}
else
{
if (!ignoreChildren(oldTp))
replaceChildren(newTp);
}
}
TypePackId newTp = replace(tp); TypePackId newTp = replace(tp);
return newTp; return newTp;
} }
@ -385,6 +493,8 @@ TypePackId Substitution::clone(TypePackId tp)
{ {
VariadicTypePack clone; VariadicTypePack clone;
clone.ty = vtp->ty; clone.ty = vtp->ty;
if (FFlag::LuauSubstitutionFixMissingFields)
clone.hidden = vtp->hidden;
return addTypePack(std::move(clone)); return addTypePack(std::move(clone));
} }
else else
@ -395,6 +505,9 @@ void Substitution::foundDirty(TypeId ty)
{ {
ty = log->follow(ty); ty = log->follow(ty);
if (FFlag::LuauSubstitutionReentrant && newTypes.contains(ty))
return;
if (isDirty(ty)) if (isDirty(ty))
newTypes[ty] = follow(clean(ty)); newTypes[ty] = follow(clean(ty));
else else
@ -405,6 +518,9 @@ void Substitution::foundDirty(TypePackId tp)
{ {
tp = log->follow(tp); tp = log->follow(tp);
if (FFlag::LuauSubstitutionReentrant && newPacks.contains(tp))
return;
if (isDirty(tp)) if (isDirty(tp))
newPacks[tp] = follow(clean(tp)); newPacks[tp] = follow(clean(tp));
else else
@ -446,6 +562,14 @@ void Substitution::replaceChildren(TypeId ty)
if (FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty)) if (FunctionTypeVar* ftv = getMutable<FunctionTypeVar>(ty))
{ {
if (FFlag::LuauSubstitutionFixMissingFields)
{
for (TypeId& generic : ftv->generics)
generic = replace(generic);
for (TypePackId& genericPack : ftv->genericPacks)
genericPack = replace(genericPack);
}
ftv->argTypes = replace(ftv->argTypes); ftv->argTypes = replace(ftv->argTypes);
ftv->retTypes = replace(ftv->retTypes); ftv->retTypes = replace(ftv->retTypes);
} }
@ -486,6 +610,25 @@ void Substitution::replaceChildren(TypeId ty)
for (TypeId& part : ctv->parts) for (TypeId& part : ctv->parts)
part = replace(part); part = replace(part);
} }
else if (PendingExpansionTypeVar* petv = getMutable<PendingExpansionTypeVar>(ty))
{
for (TypeId& a : petv->typeArguments)
a = replace(a);
for (TypePackId& a : petv->packArguments)
a = replace(a);
}
else if (ClassTypeVar* ctv = getMutable<ClassTypeVar>(ty); FFlag::LuauClassTypeVarsInSubstitution && ctv)
{
for (auto& [name, prop] : ctv->props)
prop.type = replace(prop.type);
if (ctv->parent)
ctv->parent = replace(*ctv->parent);
if (ctv->metatable)
ctv->metatable = replace(*ctv->metatable);
}
} }
void Substitution::replaceChildren(TypePackId tp) void Substitution::replaceChildren(TypePackId tp)

View File

@ -232,6 +232,11 @@ struct StringifierState
emit(std::to_string(i).c_str()); emit(std::to_string(i).c_str());
} }
void emit(size_t i)
{
emit(std::to_string(i).c_str());
}
void indent() void indent()
{ {
indentation += 4; indentation += 4;
@ -410,6 +415,13 @@ struct TypeVarStringifier
state.emit("*"); state.emit("*");
} }
void operator()(TypeId ty, const PendingExpansionTypeVar& petv)
{
state.emit("*pending-expansion-");
state.emit(petv.index);
state.emit("*");
}
void operator()(TypeId, const PrimitiveTypeVar& ptv) void operator()(TypeId, const PrimitiveTypeVar& ptv)
{ {
switch (ptv.type) switch (ptv.type)
@ -1459,6 +1471,12 @@ std::string toString(const Constraint& constraint, ToStringOptions& opts)
opts.nameMap = std::move(namedStr.nameMap); opts.nameMap = std::move(namedStr.nameMap);
return "@name(" + namedStr.name + ") = " + c.name; return "@name(" + namedStr.name + ") = " + c.name;
} }
else if constexpr (std::is_same_v<T, TypeAliasExpansionConstraint>)
{
ToStringResult targetStr = toStringDetailed(c.target, opts);
opts.nameMap = std::move(targetStr.nameMap);
return "expand " + targetStr.name;
}
else else
static_assert(always_false_v<T>, "Non-exhaustive constraint switch"); static_assert(always_false_v<T>, "Non-exhaustive constraint switch");
}; };

View File

@ -99,6 +99,11 @@ public:
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("*blocked*")); return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("*blocked*"));
} }
AstType* operator()(const PendingExpansionTypeVar& petv)
{
return allocator->alloc<AstTypeReference>(Location(), std::nullopt, AstName("*pending-expansion*"));
}
AstType* operator()(const ConstrainedTypeVar& ctv) AstType* operator()(const ConstrainedTypeVar& ctv)
{ {
AstArray<AstType*> types; AstArray<AstType*> types;

View File

@ -67,6 +67,13 @@ struct TypeChecker2 : public AstVisitor
return follow(*ty); return follow(*ty);
} }
TypePackId lookupPackAnnotation(AstTypePack* annotation)
{
TypePackId* tp = module->astResolvedTypePacks.find(annotation);
LUAU_ASSERT(tp);
return follow(*tp);
}
TypePackId reconstructPack(AstArray<AstExpr*> exprs, TypeArena& arena) TypePackId reconstructPack(AstArray<AstExpr*> exprs, TypeArena& arena)
{ {
if (exprs.size == 0) if (exprs.size == 0)
@ -363,12 +370,153 @@ struct TypeChecker2 : public AstVisitor
bool visit(AstTypeReference* ty) override bool visit(AstTypeReference* ty) override
{ {
Scope* scope = findInnermostScope(ty->location); Scope* scope = findInnermostScope(ty->location);
LUAU_ASSERT(scope);
// TODO: Imported types // TODO: Imported types
// TODO: Generic types
if (!scope->lookupTypeBinding(ty->name.value)) std::optional<TypeFun> alias = scope->lookupTypeBinding(ty->name.value);
if (alias.has_value())
{ {
reportError(UnknownSymbol{ty->name.value, UnknownSymbol::Context::Type}, ty->location); size_t typesRequired = alias->typeParams.size();
size_t packsRequired = alias->typePackParams.size();
bool hasDefaultTypes = std::any_of(alias->typeParams.begin(), alias->typeParams.end(), [](auto&& el) {
return el.defaultValue.has_value();
});
bool hasDefaultPacks = std::any_of(alias->typePackParams.begin(), alias->typePackParams.end(), [](auto&& el) {
return el.defaultValue.has_value();
});
if (!ty->hasParameterList)
{
if ((!alias->typeParams.empty() && !hasDefaultTypes) || (!alias->typePackParams.empty() && !hasDefaultPacks))
{
reportError(GenericError{"Type parameter list is required"}, ty->location);
}
}
size_t typesProvided = 0;
size_t extraTypes = 0;
size_t packsProvided = 0;
for (const AstTypeOrPack& p : ty->parameters)
{
if (p.type)
{
if (packsProvided != 0)
{
reportError(GenericError{"Type parameters must come before type pack parameters"}, ty->location);
}
if (typesProvided < typesRequired)
{
typesProvided += 1;
}
else
{
extraTypes += 1;
}
}
else if (p.typePack)
{
TypePackId tp = lookupPackAnnotation(p.typePack);
if (typesProvided < typesRequired && size(tp) == 1 && finite(tp) && first(tp))
{
typesProvided += 1;
}
else
{
packsProvided += 1;
}
}
}
if (extraTypes != 0 && packsProvided == 0)
{
packsProvided += 1;
}
for (size_t i = typesProvided; i < typesRequired; ++i)
{
if (alias->typeParams[i].defaultValue)
{
typesProvided += 1;
}
}
for (size_t i = packsProvided; i < packsProvided; ++i)
{
if (alias->typePackParams[i].defaultValue)
{
packsProvided += 1;
}
}
if (extraTypes == 0 && packsProvided + 1 == packsRequired)
{
packsProvided += 1;
}
if (typesProvided != typesRequired || packsProvided != packsRequired)
{
reportError(IncorrectGenericParameterCount{
/* name */ ty->name.value,
/* typeFun */ *alias,
/* actualParameters */ typesProvided,
/* actualPackParameters */ packsProvided,
},
ty->location);
}
}
else
{
if (scope->lookupTypePackBinding(ty->name.value))
{
reportError(
SwappedGenericTypeParameter{
ty->name.value,
SwappedGenericTypeParameter::Kind::Type,
},
ty->location);
}
else
{
reportError(UnknownSymbol{ty->name.value, UnknownSymbol::Context::Type}, ty->location);
}
}
return true;
}
bool visit(AstTypePack*) override
{
return true;
}
bool visit(AstTypePackGeneric* tp) override
{
Scope* scope = findInnermostScope(tp->location);
LUAU_ASSERT(scope);
std::optional<TypePackId> alias = scope->lookupTypePackBinding(tp->genericName.value);
if (!alias.has_value())
{
if (scope->lookupTypeBinding(tp->genericName.value))
{
reportError(
SwappedGenericTypeParameter{
tp->genericName.value,
SwappedGenericTypeParameter::Kind::Pack,
},
tp->location);
}
else
{
reportError(UnknownSymbol{tp->genericName.value, UnknownSymbol::Context::Type}, tp->location);
}
} }
return true; return true;

View File

@ -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/TypeInfer.h" #include "Luau/TypeInfer.h"
#include "Luau/ApplyTypeFunction.h"
#include "Luau/Clone.h" #include "Luau/Clone.h"
#include "Luau/Common.h" #include "Luau/Common.h"
#include "Luau/Instantiation.h" #include "Luau/Instantiation.h"
@ -36,11 +37,8 @@ LUAU_FASTFLAGVARIABLE(LuauIndexSilenceErrors, false)
LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false) LUAU_FASTFLAGVARIABLE(LuauLowerBoundsCalculation, false)
LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false) LUAU_FASTFLAGVARIABLE(DebugLuauFreezeDuringUnification, false)
LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix3, false) LUAU_FASTFLAGVARIABLE(LuauSelfCallAutocompleteFix3, false)
LUAU_FASTFLAGVARIABLE(LuauReduceUnionRecursion, false)
LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false. LUAU_FASTFLAGVARIABLE(LuauReturnAnyInsteadOfICE, false) // Eventually removed as false.
LUAU_FASTFLAG(LuauNormalizeFlagIsConservative)
LUAU_FASTFLAGVARIABLE(DebugLuauSharedSelf, false); LUAU_FASTFLAGVARIABLE(DebugLuauSharedSelf, false);
LUAU_FASTFLAGVARIABLE(LuauAlwaysQuantify, false);
LUAU_FASTFLAGVARIABLE(LuauReportErrorsOnIndexerKeyMismatch, false) LUAU_FASTFLAGVARIABLE(LuauReportErrorsOnIndexerKeyMismatch, false)
LUAU_FASTFLAGVARIABLE(LuauUnknownAndNeverType, false) LUAU_FASTFLAGVARIABLE(LuauUnknownAndNeverType, false)
LUAU_FASTFLAG(LuauQuantifyConstrained) LUAU_FASTFLAG(LuauQuantifyConstrained)
@ -2108,35 +2106,16 @@ std::vector<TypeId> TypeChecker::reduceUnion(const std::vector<TypeId>& types)
if (const UnionTypeVar* utv = get<UnionTypeVar>(t)) if (const UnionTypeVar* utv = get<UnionTypeVar>(t))
{ {
if (FFlag::LuauReduceUnionRecursion) for (TypeId ty : utv)
{ {
for (TypeId ty : utv) ty = follow(ty);
{ if (get<NeverTypeVar>(ty))
if (FFlag::LuauNormalizeFlagIsConservative) continue;
ty = follow(ty); if (get<ErrorTypeVar>(ty) || get<AnyTypeVar>(ty))
if (get<NeverTypeVar>(ty)) return {ty};
continue;
if (get<ErrorTypeVar>(ty) || get<AnyTypeVar>(ty))
return {ty};
if (result.end() == std::find(result.begin(), result.end(), ty)) if (result.end() == std::find(result.begin(), result.end(), ty))
result.push_back(ty); result.push_back(ty);
}
}
else
{
std::vector<TypeId> r = reduceUnion(utv->options);
for (TypeId ty : r)
{
ty = follow(ty);
if (get<NeverTypeVar>(ty))
continue;
if (get<ErrorTypeVar>(ty) || get<AnyTypeVar>(ty))
return {ty};
if (std::find(result.begin(), result.end(), ty) == result.end())
result.push_back(ty);
}
} }
} }
else if (std::find(result.begin(), result.end(), t) == result.end()) else if (std::find(result.begin(), result.end(), t) == result.end())
@ -4770,16 +4749,8 @@ TypeId TypeChecker::quantify(const ScopePtr& scope, TypeId ty, Location location
{ {
const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty); const FunctionTypeVar* ftv = get<FunctionTypeVar>(ty);
if (FFlag::LuauAlwaysQuantify) if (ftv)
{ Luau::quantify(ty, scope->level);
if (ftv)
Luau::quantify(ty, scope->level);
}
else
{
if (ftv && ftv->generics.empty() && ftv->genericPacks.empty())
Luau::quantify(ty, scope->level);
}
if (FFlag::LuauLowerBoundsCalculation && ftv) if (FFlag::LuauLowerBoundsCalculation && ftv)
{ {
@ -5253,7 +5224,7 @@ TypeId TypeChecker::resolveTypeWorker(const ScopePtr& scope, const AstType& anno
if (notEnoughParameters && hasDefaultParameters) if (notEnoughParameters && hasDefaultParameters)
{ {
// 'applyTypeFunction' is used to substitute default types that reference previous generic types // 'applyTypeFunction' is used to substitute default types that reference previous generic types
ApplyTypeFunction applyTypeFunction{&currentModule->internalTypes, scope->level}; ApplyTypeFunction applyTypeFunction{&currentModule->internalTypes};
for (size_t i = 0; i < typesProvided; ++i) for (size_t i = 0; i < typesProvided; ++i)
applyTypeFunction.typeArguments[tf->typeParams[i].ty] = typeParams[i]; applyTypeFunction.typeArguments[tf->typeParams[i].ty] = typeParams[i];
@ -5494,65 +5465,13 @@ TypePackId TypeChecker::resolveTypePack(const ScopePtr& scope, const AstTypePack
return result; return result;
} }
bool ApplyTypeFunction::isDirty(TypeId ty)
{
if (typeArguments.count(ty))
return true;
else if (const FreeTypeVar* ftv = get<FreeTypeVar>(ty))
{
if (ftv->forwardedTypeAlias)
encounteredForwardedType = true;
return false;
}
else
return false;
}
bool ApplyTypeFunction::isDirty(TypePackId tp)
{
if (typePackArguments.count(tp))
return true;
else
return false;
}
bool ApplyTypeFunction::ignoreChildren(TypeId ty)
{
if (get<GenericTypeVar>(ty))
return true;
else
return false;
}
bool ApplyTypeFunction::ignoreChildren(TypePackId tp)
{
if (get<GenericTypePack>(tp))
return true;
else
return false;
}
TypeId ApplyTypeFunction::clean(TypeId ty)
{
TypeId& arg = typeArguments[ty];
LUAU_ASSERT(arg);
return arg;
}
TypePackId ApplyTypeFunction::clean(TypePackId tp)
{
TypePackId& arg = typePackArguments[tp];
LUAU_ASSERT(arg);
return arg;
}
TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, const std::vector<TypeId>& typeParams, TypeId TypeChecker::instantiateTypeFun(const ScopePtr& scope, const TypeFun& tf, const std::vector<TypeId>& typeParams,
const std::vector<TypePackId>& typePackParams, const Location& location) const std::vector<TypePackId>& typePackParams, const Location& location)
{ {
if (tf.typeParams.empty() && tf.typePackParams.empty()) if (tf.typeParams.empty() && tf.typePackParams.empty())
return tf.type; return tf.type;
ApplyTypeFunction applyTypeFunction{&currentModule->internalTypes, scope->level}; ApplyTypeFunction applyTypeFunction{&currentModule->internalTypes};
for (size_t i = 0; i < tf.typeParams.size(); ++i) for (size_t i = 0; i < tf.typeParams.size(); ++i)
applyTypeFunction.typeArguments[tf.typeParams[i].ty] = typeParams[i]; applyTypeFunction.typeArguments[tf.typeParams[i].ty] = typeParams[i];

View File

@ -445,6 +445,16 @@ BlockedTypeVar::BlockedTypeVar()
int BlockedTypeVar::nextIndex = 0; int BlockedTypeVar::nextIndex = 0;
PendingExpansionTypeVar::PendingExpansionTypeVar(TypeFun fn, std::vector<TypeId> typeArguments, std::vector<TypePackId> packArguments)
: fn(fn)
, typeArguments(typeArguments)
, packArguments(packArguments)
, index(++nextIndex)
{
}
size_t PendingExpansionTypeVar::nextIndex = 0;
FunctionTypeVar::FunctionTypeVar(TypePackId argTypes, TypePackId retTypes, std::optional<FunctionDefinition> defn, bool hasSelf) FunctionTypeVar::FunctionTypeVar(TypePackId argTypes, TypePackId retTypes, std::optional<FunctionDefinition> defn, bool hasSelf)
: argTypes(argTypes) : argTypes(argTypes)
, retTypes(retTypes) , retTypes(retTypes)
@ -1412,4 +1422,19 @@ bool hasTag(const Property& prop, const std::string& tagName)
return hasTag(prop.tags, tagName); return hasTag(prop.tags, tagName);
} }
bool TypeFun::operator==(const TypeFun& rhs) const
{
return type == rhs.type && typeParams == rhs.typeParams && typePackParams == rhs.typePackParams;
}
bool GenericTypeDefinition::operator==(const GenericTypeDefinition& rhs) const
{
return ty == rhs.ty && defaultValue == rhs.defaultValue;
}
bool GenericTypePackDefinition::operator==(const GenericTypePackDefinition& rhs) const
{
return tp == rhs.tp && defaultValue == rhs.defaultValue;
}
} // namespace Luau } // namespace Luau

View File

@ -474,16 +474,26 @@ public:
bool value; bool value;
}; };
enum class ConstantNumberParseResult
{
Ok,
Malformed,
BinOverflow,
HexOverflow,
DoublePrefix,
};
class AstExprConstantNumber : public AstExpr class AstExprConstantNumber : public AstExpr
{ {
public: public:
LUAU_RTTI(AstExprConstantNumber) LUAU_RTTI(AstExprConstantNumber)
AstExprConstantNumber(const Location& location, double value); AstExprConstantNumber(const Location& location, double value, ConstantNumberParseResult parseResult = ConstantNumberParseResult::Ok);
void visit(AstVisitor* visitor) override; void visit(AstVisitor* visitor) override;
double value; double value;
ConstantNumberParseResult parseResult;
}; };
class AstExprConstantString : public AstExpr class AstExprConstantString : public AstExpr

View File

@ -50,9 +50,10 @@ void AstExprConstantBool::visit(AstVisitor* visitor)
visitor->visit(this); visitor->visit(this);
} }
AstExprConstantNumber::AstExprConstantNumber(const Location& location, double value) AstExprConstantNumber::AstExprConstantNumber(const Location& location, double value, ConstantNumberParseResult parseResult)
: AstExpr(ClassIndex(), location) : AstExpr(ClassIndex(), location)
, value(value) , value(value)
, parseResult(parseResult)
{ {
} }

View File

@ -21,7 +21,8 @@ LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseWrongNamedType, false)
bool lua_telemetry_parsed_named_non_function_type = false; bool lua_telemetry_parsed_named_non_function_type = false;
LUAU_FASTFLAGVARIABLE(LuauErrorParseIntegerIssues, false) LUAU_FASTFLAGVARIABLE(LuauErrorDoubleHexPrefix, false)
LUAU_FASTFLAGVARIABLE(LuauLintParseIntegerIssues, false)
LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseIntegerIssues, false) LUAU_DYNAMIC_FASTFLAGVARIABLE(LuaReportParseIntegerIssues, false)
bool lua_telemetry_parsed_out_of_range_bin_integer = false; bool lua_telemetry_parsed_out_of_range_bin_integer = false;
@ -2032,8 +2033,10 @@ AstExpr* Parser::parseAssertionExpr()
return expr; return expr;
} }
static const char* parseInteger(double& result, const char* data, int base) static const char* parseInteger_DEPRECATED(double& result, const char* data, int base)
{ {
LUAU_ASSERT(!FFlag::LuauLintParseIntegerIssues);
char* end = nullptr; char* end = nullptr;
unsigned long long value = strtoull(data, &end, base); unsigned long long value = strtoull(data, &end, base);
@ -2053,9 +2056,6 @@ static const char* parseInteger(double& result, const char* data, int base)
else else
lua_telemetry_parsed_out_of_range_hex_integer = true; lua_telemetry_parsed_out_of_range_hex_integer = true;
} }
if (FFlag::LuauErrorParseIntegerIssues)
return "Integer number value is out of range";
} }
} }
@ -2063,11 +2063,13 @@ static const char* parseInteger(double& result, const char* data, int base)
return *end == 0 ? nullptr : "Malformed number"; return *end == 0 ? nullptr : "Malformed number";
} }
static const char* parseNumber(double& result, const char* data) static const char* parseNumber_DEPRECATED2(double& result, const char* data)
{ {
LUAU_ASSERT(!FFlag::LuauLintParseIntegerIssues);
// binary literal // binary literal
if (data[0] == '0' && (data[1] == 'b' || data[1] == 'B') && data[2]) if (data[0] == '0' && (data[1] == 'b' || data[1] == 'B') && data[2])
return parseInteger(result, data + 2, 2); return parseInteger_DEPRECATED(result, data + 2, 2);
// hexadecimal literal // hexadecimal literal
if (data[0] == '0' && (data[1] == 'x' || data[1] == 'X') && data[2]) if (data[0] == '0' && (data[1] == 'x' || data[1] == 'X') && data[2])
@ -2075,10 +2077,7 @@ static const char* parseNumber(double& result, const char* data)
if (DFFlag::LuaReportParseIntegerIssues && data[2] == '0' && (data[3] == 'x' || data[3] == 'X')) if (DFFlag::LuaReportParseIntegerIssues && data[2] == '0' && (data[3] == 'x' || data[3] == 'X'))
lua_telemetry_parsed_double_prefix_hex_integer = true; lua_telemetry_parsed_double_prefix_hex_integer = true;
if (FFlag::LuauErrorParseIntegerIssues) return parseInteger_DEPRECATED(result, data + 2, 16);
return parseInteger(result, data, 16); // keep prefix, it's handled by 'strtoull'
else
return parseInteger(result, data + 2, 16);
} }
char* end = nullptr; char* end = nullptr;
@ -2090,6 +2089,8 @@ static const char* parseNumber(double& result, const char* data)
static bool parseNumber_DEPRECATED(double& result, const char* data) static bool parseNumber_DEPRECATED(double& result, const char* data)
{ {
LUAU_ASSERT(!FFlag::LuauLintParseIntegerIssues);
// binary literal // binary literal
if (data[0] == '0' && (data[1] == 'b' || data[1] == 'B') && data[2]) if (data[0] == '0' && (data[1] == 'b' || data[1] == 'B') && data[2])
{ {
@ -2118,6 +2119,73 @@ static bool parseNumber_DEPRECATED(double& result, const char* data)
} }
} }
static ConstantNumberParseResult parseInteger(double& result, const char* data, int base)
{
LUAU_ASSERT(FFlag::LuauLintParseIntegerIssues);
LUAU_ASSERT(base == 2 || base == 16);
char* end = nullptr;
unsigned long long value = strtoull(data, &end, base);
if (*end != 0)
return ConstantNumberParseResult::Malformed;
result = double(value);
if (value == ULLONG_MAX && errno == ERANGE)
{
// 'errno' might have been set before we called 'strtoull', but we don't want the overhead of resetting a TLS variable on each call
// so we only reset it when we get a result that might be an out-of-range error and parse again to make sure
errno = 0;
value = strtoull(data, &end, base);
if (errno == ERANGE)
{
if (DFFlag::LuaReportParseIntegerIssues)
{
if (base == 2)
lua_telemetry_parsed_out_of_range_bin_integer = true;
else
lua_telemetry_parsed_out_of_range_hex_integer = true;
}
return base == 2 ? ConstantNumberParseResult::BinOverflow : ConstantNumberParseResult::HexOverflow;
}
}
return ConstantNumberParseResult::Ok;
}
static ConstantNumberParseResult parseNumber(double& result, const char* data)
{
LUAU_ASSERT(FFlag::LuauLintParseIntegerIssues);
// binary literal
if (data[0] == '0' && (data[1] == 'b' || data[1] == 'B') && data[2])
return parseInteger(result, data + 2, 2);
// hexadecimal literal
if (data[0] == '0' && (data[1] == 'x' || data[1] == 'X') && data[2])
{
if (!FFlag::LuauErrorDoubleHexPrefix && data[2] == '0' && (data[3] == 'x' || data[3] == 'X'))
{
if (DFFlag::LuaReportParseIntegerIssues)
lua_telemetry_parsed_double_prefix_hex_integer = true;
ConstantNumberParseResult parseResult = parseInteger(result, data + 2, 16);
return parseResult == ConstantNumberParseResult::Malformed ? parseResult : ConstantNumberParseResult::DoublePrefix;
}
return parseInteger(result, data, 16); // pass in '0x' prefix, it's handled by 'strtoull'
}
char* end = nullptr;
double value = strtod(data, &end);
result = value;
return *end == 0 ? ConstantNumberParseResult::Ok : ConstantNumberParseResult::Malformed;
}
// simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor | FUNCTION body | primaryexp // simpleexp -> NUMBER | STRING | NIL | true | false | ... | constructor | FUNCTION body | primaryexp
AstExpr* Parser::parseSimpleExpr() AstExpr* Parser::parseSimpleExpr()
{ {
@ -2158,10 +2226,21 @@ AstExpr* Parser::parseSimpleExpr()
scratchData.erase(std::remove(scratchData.begin(), scratchData.end(), '_'), scratchData.end()); scratchData.erase(std::remove(scratchData.begin(), scratchData.end(), '_'), scratchData.end());
} }
if (DFFlag::LuaReportParseIntegerIssues || FFlag::LuauErrorParseIntegerIssues) if (FFlag::LuauLintParseIntegerIssues)
{ {
double value = 0; double value = 0;
if (const char* error = parseNumber(value, scratchData.c_str())) ConstantNumberParseResult result = parseNumber(value, scratchData.c_str());
nextLexeme();
if (result == ConstantNumberParseResult::Malformed)
return reportExprError(start, {}, "Malformed number");
return allocator.alloc<AstExprConstantNumber>(start, value, result);
}
else if (DFFlag::LuaReportParseIntegerIssues)
{
double value = 0;
if (const char* error = parseNumber_DEPRECATED2(value, scratchData.c_str()))
{ {
nextLexeme(); nextLexeme();

View File

@ -3,7 +3,7 @@
#include "Luau/Common.h" #include "Luau/Common.h"
#include "Luau/Ast.h" #include "Luau/Ast.h"
#include "Luau/JsonEncoder.h" #include "Luau/AstJsonEncoder.h"
#include "Luau/Parser.h" #include "Luau/Parser.h"
#include "Luau/ParseOptions.h" #include "Luau/ParseOptions.h"

View File

@ -109,8 +109,8 @@ static int lua_loadstring(lua_State* L)
return 1; return 1;
lua_pushnil(L); lua_pushnil(L);
lua_insert(L, -2); /* put before error message */ lua_insert(L, -2); // put before error message
return 2; /* return nil plus error message */ return 2; // return nil plus error message
} }
static int finishrequire(lua_State* L) static int finishrequire(lua_State* L)

View File

@ -37,6 +37,14 @@
// Note that Luau runtime doesn't provide indefinite bytecode compatibility: support for older versions gets removed over time. As such, bytecode isn't a durable storage format and it's expected // Note that Luau runtime doesn't provide indefinite bytecode compatibility: support for older versions gets removed over time. As such, bytecode isn't a durable storage format and it's expected
// that Luau users can recompile bytecode from source on Luau version upgrades if necessary. // that Luau users can recompile bytecode from source on Luau version upgrades if necessary.
// # Bytecode version history
//
// Note: due to limitations of the versioning scheme, some bytecode blobs that carry version 2 are using features from version 3. Starting from version 3, version should be sufficient to indicate bytecode compatibility.
//
// Version 1: Baseline version for the open-source release. Supported until 0.521.
// Version 2: Adds Proto::linedefined. Currently supported.
// Version 3: Adds FORGPREP/JUMPXEQK* and enhances AUX encoding for FORGLOOP. Removes FORGLOOP_NEXT/INEXT and JUMPIFEQK/JUMPIFNOTEQK. Currently supported.
// Bytecode opcode, part of the instruction header // Bytecode opcode, part of the instruction header
enum LuauOpcode enum LuauOpcode
{ {
@ -367,6 +375,20 @@ enum LuauOpcode
// D: jump offset (-32768..32767) // D: jump offset (-32768..32767)
LOP_FORGPREP, LOP_FORGPREP,
// JUMPXEQKNIL, JUMPXEQKB: jumps to target offset if the comparison with constant is true (or false, see AUX)
// A: source register 1
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
// AUX: constant value (for boolean) in low bit, NOT flag (that flips comparison result) in high bit
LOP_JUMPXEQKNIL,
LOP_JUMPXEQKB,
// JUMPXEQKN, JUMPXEQKS: jumps to target offset if the comparison with constant is true (or false, see AUX)
// A: source register 1
// D: jump offset (-32768..32767; 0 means "next instruction" aka "don't jump")
// AUX: constant table index in low 24 bits, NOT flag (that flips comparison result) in high bit
LOP_JUMPXEQKN,
LOP_JUMPXEQKS,
// Enum entry for number of opcodes, not a valid opcode by itself! // Enum entry for number of opcodes, not a valid opcode by itself!
LOP__COUNT LOP__COUNT
}; };
@ -391,7 +413,7 @@ enum LuauBytecodeTag
{ {
// Bytecode version; runtime supports [MIN, MAX], compiler emits TARGET by default but may emit a higher version when flags are enabled // Bytecode version; runtime supports [MIN, MAX], compiler emits TARGET by default but may emit a higher version when flags are enabled
LBC_VERSION_MIN = 2, LBC_VERSION_MIN = 2,
LBC_VERSION_MAX = 2, LBC_VERSION_MAX = 3,
LBC_VERSION_TARGET = 2, LBC_VERSION_TARGET = 2,
// Types of constant table entries // Types of constant table entries
LBC_CONSTANT_NIL = 0, LBC_CONSTANT_NIL = 0,

View File

@ -20,12 +20,6 @@
#define LUAU_DEBUGBREAK() __builtin_trap() #define LUAU_DEBUGBREAK() __builtin_trap()
#endif #endif
namespace Luau namespace Luau
{ {
@ -67,16 +61,13 @@ struct FValue
const char* name; const char* name;
FValue* next; FValue* next;
FValue(const char* name, T def, bool dynamic, void (*reg)(const char*, T*, bool) = nullptr) FValue(const char* name, T def, bool dynamic)
: value(def) : value(def)
, dynamic(dynamic) , dynamic(dynamic)
, name(name) , name(name)
, next(list) , next(list)
{ {
list = this; list = this;
if (reg)
reg(name, &value, dynamic);
} }
operator T() const operator T() const
@ -98,7 +89,7 @@ FValue<T>* FValue<T>::list = nullptr;
#define LUAU_FASTFLAGVARIABLE(flag, def) \ #define LUAU_FASTFLAGVARIABLE(flag, def) \
namespace FFlag \ namespace FFlag \
{ \ { \
Luau::FValue<bool> flag(#flag, def, false, nullptr); \ Luau::FValue<bool> flag(#flag, def, false); \
} }
#define LUAU_FASTINT(flag) \ #define LUAU_FASTINT(flag) \
namespace FInt \ namespace FInt \
@ -108,7 +99,7 @@ FValue<T>* FValue<T>::list = nullptr;
#define LUAU_FASTINTVARIABLE(flag, def) \ #define LUAU_FASTINTVARIABLE(flag, def) \
namespace FInt \ namespace FInt \
{ \ { \
Luau::FValue<int> flag(#flag, def, false, nullptr); \ Luau::FValue<int> flag(#flag, def, false); \
} }
#define LUAU_DYNAMIC_FASTFLAG(flag) \ #define LUAU_DYNAMIC_FASTFLAG(flag) \
@ -119,7 +110,7 @@ FValue<T>* FValue<T>::list = nullptr;
#define LUAU_DYNAMIC_FASTFLAGVARIABLE(flag, def) \ #define LUAU_DYNAMIC_FASTFLAGVARIABLE(flag, def) \
namespace DFFlag \ namespace DFFlag \
{ \ { \
Luau::FValue<bool> flag(#flag, def, true, nullptr); \ Luau::FValue<bool> flag(#flag, def, true); \
} }
#define LUAU_DYNAMIC_FASTINT(flag) \ #define LUAU_DYNAMIC_FASTINT(flag) \
namespace DFInt \ namespace DFInt \
@ -129,5 +120,5 @@ FValue<T>* FValue<T>::list = nullptr;
#define LUAU_DYNAMIC_FASTINTVARIABLE(flag, def) \ #define LUAU_DYNAMIC_FASTINTVARIABLE(flag, def) \
namespace DFInt \ namespace DFInt \
{ \ { \
Luau::FValue<int> flag(#flag, def, true, nullptr); \ Luau::FValue<int> flag(#flag, def, true); \
} }

View File

@ -10,17 +10,16 @@ inline bool isFlagExperimental(const char* flag)
{ {
// Flags in this list are disabled by default in various command-line tools. They may have behavior that is not fully final, // Flags in this list are disabled by default in various command-line tools. They may have behavior that is not fully final,
// or critical bugs that are found after the code has been submitted. // or critical bugs that are found after the code has been submitted.
static const char* kList[] = static const char* kList[] = {
{
"LuauLowerBoundsCalculation", "LuauLowerBoundsCalculation",
nullptr, // makes sure we always have at least one entry nullptr, // makes sure we always have at least one entry
}; };
for (const char* item: kList) for (const char* item : kList)
if (item && strcmp(item, flag) == 0) if (item && strcmp(item, flag) == 0)
return true; return true;
return false; return false;
} }
} } // namespace Luau

View File

@ -3,7 +3,7 @@
#include <stddef.h> #include <stddef.h>
/* Can be used to reconfigure visibility/exports for public APIs */ // Can be used to reconfigure visibility/exports for public APIs
#ifndef LUACODE_API #ifndef LUACODE_API
#define LUACODE_API extern #define LUACODE_API extern
#endif #endif
@ -35,5 +35,5 @@ struct lua_CompileOptions
const char** mutableGlobals; const char** mutableGlobals;
}; };
/* compile source to bytecode; when source compilation fails, the resulting bytecode contains the encoded error. use free() to destroy */ // compile source to bytecode; when source compilation fails, the resulting bytecode contains the encoded error. use free() to destroy
LUACODE_API char* luau_compile(const char* source, size_t size, lua_CompileOptions* options, size_t* outsize); LUACODE_API char* luau_compile(const char* source, size_t size, lua_CompileOptions* options, size_t* outsize);

View File

@ -4,8 +4,6 @@
#include "Luau/Bytecode.h" #include "Luau/Bytecode.h"
#include "Luau/Compiler.h" #include "Luau/Compiler.h"
LUAU_FASTFLAGVARIABLE(LuauCompileRawlen, false)
namespace Luau namespace Luau
{ {
namespace Compile namespace Compile
@ -57,7 +55,7 @@ static int getBuiltinFunctionId(const Builtin& builtin, const CompileOptions& op
return LBF_RAWGET; return LBF_RAWGET;
if (builtin.isGlobal("rawequal")) if (builtin.isGlobal("rawequal"))
return LBF_RAWEQUAL; return LBF_RAWEQUAL;
if (FFlag::LuauCompileRawlen && builtin.isGlobal("rawlen")) if (builtin.isGlobal("rawlen"))
return LBF_RAWLEN; return LBF_RAWLEN;
if (builtin.isGlobal("unpack")) if (builtin.isGlobal("unpack"))

View File

@ -6,6 +6,8 @@
#include <algorithm> #include <algorithm>
#include <string.h> #include <string.h>
LUAU_FASTFLAGVARIABLE(LuauCompileBytecodeV3, false)
namespace Luau namespace Luau
{ {
@ -77,6 +79,10 @@ static int getOpLength(LuauOpcode op)
case LOP_JUMPIFNOTEQK: case LOP_JUMPIFNOTEQK:
case LOP_FASTCALL2: case LOP_FASTCALL2:
case LOP_FASTCALL2K: case LOP_FASTCALL2K:
case LOP_JUMPXEQKNIL:
case LOP_JUMPXEQKB:
case LOP_JUMPXEQKN:
case LOP_JUMPXEQKS:
return 2; return 2;
default: default:
@ -108,6 +114,10 @@ inline bool isJumpD(LuauOpcode op)
case LOP_JUMPBACK: case LOP_JUMPBACK:
case LOP_JUMPIFEQK: case LOP_JUMPIFEQK:
case LOP_JUMPIFNOTEQK: case LOP_JUMPIFNOTEQK:
case LOP_JUMPXEQKNIL:
case LOP_JUMPXEQKB:
case LOP_JUMPXEQKN:
case LOP_JUMPXEQKS:
return true; return true;
default: default:
@ -1069,6 +1079,9 @@ std::string BytecodeBuilder::getError(const std::string& message)
uint8_t BytecodeBuilder::getVersion() uint8_t BytecodeBuilder::getVersion()
{ {
if (FFlag::LuauCompileBytecodeV3)
return 3;
// This function usually returns LBC_VERSION_TARGET but may sometimes return a higher number (within LBC_VERSION_MIN/MAX) under fast flags // This function usually returns LBC_VERSION_TARGET but may sometimes return a higher number (within LBC_VERSION_MIN/MAX) under fast flags
return LBC_VERSION_TARGET; return LBC_VERSION_TARGET;
} }
@ -1246,6 +1259,24 @@ void BytecodeBuilder::validate() const
VJUMP(LUAU_INSN_D(insn)); VJUMP(LUAU_INSN_D(insn));
break; break;
case LOP_JUMPXEQKNIL:
case LOP_JUMPXEQKB:
VREG(LUAU_INSN_A(insn));
VJUMP(LUAU_INSN_D(insn));
break;
case LOP_JUMPXEQKN:
VREG(LUAU_INSN_A(insn));
VCONST(insns[i + 1] & 0xffffff, Number);
VJUMP(LUAU_INSN_D(insn));
break;
case LOP_JUMPXEQKS:
VREG(LUAU_INSN_A(insn));
VCONST(insns[i + 1] & 0xffffff, String);
VJUMP(LUAU_INSN_D(insn));
break;
case LOP_ADD: case LOP_ADD:
case LOP_SUB: case LOP_SUB:
case LOP_MUL: case LOP_MUL:
@ -1779,6 +1810,26 @@ void BytecodeBuilder::dumpInstruction(const uint32_t* code, std::string& result,
formatAppend(result, "JUMPIFNOTEQK R%d K%d L%d\n", LUAU_INSN_A(insn), *code++, targetLabel); formatAppend(result, "JUMPIFNOTEQK R%d K%d L%d\n", LUAU_INSN_A(insn), *code++, targetLabel);
break; break;
case LOP_JUMPXEQKNIL:
formatAppend(result, "JUMPXEQKNIL R%d L%d%s\n", LUAU_INSN_A(insn), targetLabel, *code >> 31 ? " NOT" : "");
code++;
break;
case LOP_JUMPXEQKB:
formatAppend(result, "JUMPXEQKB R%d %d L%d%s\n", LUAU_INSN_A(insn), *code & 1, targetLabel, *code >> 31 ? " NOT" : "");
code++;
break;
case LOP_JUMPXEQKN:
formatAppend(result, "JUMPXEQKN R%d K%d L%d%s\n", LUAU_INSN_A(insn), *code & 0xffffff, targetLabel, *code >> 31 ? " NOT" : "");
code++;
break;
case LOP_JUMPXEQKS:
formatAppend(result, "JUMPXEQKS R%d K%d L%d%s\n", LUAU_INSN_A(insn), *code & 0xffffff, targetLabel, *code >> 31 ? " NOT" : "");
code++;
break;
default: default:
LUAU_ASSERT(!"Unsupported opcode"); LUAU_ASSERT(!"Unsupported opcode");
} }

View File

@ -26,6 +26,7 @@ LUAU_FASTINTVARIABLE(LuauCompileInlineDepth, 5)
LUAU_FASTFLAGVARIABLE(LuauCompileNoIpairs, false) LUAU_FASTFLAGVARIABLE(LuauCompileNoIpairs, false)
LUAU_FASTFLAGVARIABLE(LuauCompileFreeReassign, false) LUAU_FASTFLAGVARIABLE(LuauCompileFreeReassign, false)
LUAU_FASTFLAGVARIABLE(LuauCompileXEQ, false)
namespace Luau namespace Luau
{ {
@ -1008,9 +1009,8 @@ struct Compiler
size_t compileCompareJump(AstExprBinary* expr, bool not_ = false) size_t compileCompareJump(AstExprBinary* expr, bool not_ = false)
{ {
RegScope rs(this); RegScope rs(this);
LuauOpcode opc = getJumpOpCompare(expr->op, not_);
bool isEq = (opc == LOP_JUMPIFEQ || opc == LOP_JUMPIFNOTEQ); bool isEq = (expr->op == AstExprBinary::CompareEq || expr->op == AstExprBinary::CompareNe);
AstExpr* left = expr->left; AstExpr* left = expr->left;
AstExpr* right = expr->right; AstExpr* right = expr->right;
@ -1022,36 +1022,112 @@ struct Compiler
std::swap(left, right); std::swap(left, right);
} }
uint8_t rl = compileExprAuto(left, rs); if (FFlag::LuauCompileXEQ)
int32_t rr = -1;
if (isEq && operandIsConstant)
{ {
if (opc == LOP_JUMPIFEQ) uint8_t rl = compileExprAuto(left, rs);
opc = LOP_JUMPIFEQK;
else if (opc == LOP_JUMPIFNOTEQ)
opc = LOP_JUMPIFNOTEQK;
rr = getConstantIndex(right); if (isEq && operandIsConstant)
LUAU_ASSERT(rr >= 0); {
} const Constant* cv = constants.find(right);
else LUAU_ASSERT(cv && cv->type != Constant::Type_Unknown);
rr = compileExprAuto(right, rs);
size_t jumpLabel = bytecode.emitLabel(); LuauOpcode opc = LOP_NOP;
int32_t cid = -1;
uint32_t flip = (expr->op == AstExprBinary::CompareEq) == not_ ? 0x80000000 : 0;
if (expr->op == AstExprBinary::CompareGt || expr->op == AstExprBinary::CompareGe) switch (cv->type)
{ {
bytecode.emitAD(opc, uint8_t(rr), 0); case Constant::Type_Nil:
bytecode.emitAux(rl); opc = LOP_JUMPXEQKNIL;
cid = 0;
break;
case Constant::Type_Boolean:
opc = LOP_JUMPXEQKB;
cid = cv->valueBoolean;
break;
case Constant::Type_Number:
opc = LOP_JUMPXEQKN;
cid = getConstantIndex(right);
break;
case Constant::Type_String:
opc = LOP_JUMPXEQKS;
cid = getConstantIndex(right);
break;
default:
LUAU_ASSERT(!"Unexpected constant type");
}
if (cid < 0)
CompileError::raise(expr->location, "Exceeded constant limit; simplify the code to compile");
size_t jumpLabel = bytecode.emitLabel();
bytecode.emitAD(opc, rl, 0);
bytecode.emitAux(cid | flip);
return jumpLabel;
}
else
{
LuauOpcode opc = getJumpOpCompare(expr->op, not_);
uint8_t rr = compileExprAuto(right, rs);
size_t jumpLabel = bytecode.emitLabel();
if (expr->op == AstExprBinary::CompareGt || expr->op == AstExprBinary::CompareGe)
{
bytecode.emitAD(opc, rr, 0);
bytecode.emitAux(rl);
}
else
{
bytecode.emitAD(opc, rl, 0);
bytecode.emitAux(rr);
}
return jumpLabel;
}
} }
else else
{ {
bytecode.emitAD(opc, rl, 0); LuauOpcode opc = getJumpOpCompare(expr->op, not_);
bytecode.emitAux(rr);
}
return jumpLabel; uint8_t rl = compileExprAuto(left, rs);
int32_t rr = -1;
if (isEq && operandIsConstant)
{
if (opc == LOP_JUMPIFEQ)
opc = LOP_JUMPIFEQK;
else if (opc == LOP_JUMPIFNOTEQ)
opc = LOP_JUMPIFNOTEQK;
rr = getConstantIndex(right);
LUAU_ASSERT(rr >= 0);
}
else
rr = compileExprAuto(right, rs);
size_t jumpLabel = bytecode.emitLabel();
if (expr->op == AstExprBinary::CompareGt || expr->op == AstExprBinary::CompareGe)
{
bytecode.emitAD(opc, uint8_t(rr), 0);
bytecode.emitAux(rl);
}
else
{
bytecode.emitAD(opc, rl, 0);
bytecode.emitAux(rr);
}
return jumpLabel;
}
} }
int32_t getConstantNumber(AstExpr* node) int32_t getConstantNumber(AstExpr* node)

View File

@ -55,6 +55,7 @@ ifneq ($(opt),)
endif endif
OBJECTS=$(AST_OBJECTS) $(COMPILER_OBJECTS) $(ANALYSIS_OBJECTS) $(CODEGEN_OBJECTS) $(VM_OBJECTS) $(ISOCLINE_OBJECTS) $(TESTS_OBJECTS) $(CLI_OBJECTS) $(FUZZ_OBJECTS) OBJECTS=$(AST_OBJECTS) $(COMPILER_OBJECTS) $(ANALYSIS_OBJECTS) $(CODEGEN_OBJECTS) $(VM_OBJECTS) $(ISOCLINE_OBJECTS) $(TESTS_OBJECTS) $(CLI_OBJECTS) $(FUZZ_OBJECTS)
EXECUTABLE_ALIASES = luau luau-analyze luau-tests
# common flags # common flags
CXXFLAGS=-g -Wall CXXFLAGS=-g -Wall
@ -121,14 +122,14 @@ fuzz-proto fuzz-prototest: LDFLAGS+=build/libprotobuf-mutator/src/libfuzzer/libp
all: $(REPL_CLI_TARGET) $(ANALYZE_CLI_TARGET) $(TESTS_TARGET) aliases all: $(REPL_CLI_TARGET) $(ANALYZE_CLI_TARGET) $(TESTS_TARGET) aliases
aliases: luau luau-analyze aliases: $(EXECUTABLE_ALIASES)
test: $(TESTS_TARGET) test: $(TESTS_TARGET)
$(TESTS_TARGET) $(TESTS_ARGS) $(TESTS_TARGET) $(TESTS_ARGS)
clean: clean:
rm -rf $(BUILD) rm -rf $(BUILD)
rm -rf luau luau-analyze rm -rf $(EXECUTABLE_ALIASES)
coverage: $(TESTS_TARGET) coverage: $(TESTS_TARGET)
$(TESTS_TARGET) --fflags=true $(TESTS_TARGET) --fflags=true
@ -154,6 +155,9 @@ luau: $(REPL_CLI_TARGET)
luau-analyze: $(ANALYZE_CLI_TARGET) luau-analyze: $(ANALYZE_CLI_TARGET)
ln -fs $^ $@ ln -fs $^ $@
luau-tests: $(TESTS_TARGET)
ln -fs $^ $@
# executable targets # executable targets
$(TESTS_TARGET): $(TESTS_OBJECTS) $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET) $(TESTS_TARGET): $(TESTS_OBJECTS) $(ANALYSIS_TARGET) $(COMPILER_TARGET) $(AST_TARGET) $(CODEGEN_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET)
$(REPL_CLI_TARGET): $(REPL_CLI_OBJECTS) $(COMPILER_TARGET) $(AST_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET) $(REPL_CLI_TARGET): $(REPL_CLI_OBJECTS) $(COMPILER_TARGET) $(AST_TARGET) $(VM_TARGET) $(ISOCLINE_TARGET)

View File

@ -66,6 +66,8 @@ target_sources(Luau.CodeGen PRIVATE
# Luau.Analysis Sources # Luau.Analysis Sources
target_sources(Luau.Analysis PRIVATE target_sources(Luau.Analysis PRIVATE
Analysis/include/Luau/ApplyTypeFunction.h
Analysis/include/Luau/AstJsonEncoder.h
Analysis/include/Luau/AstQuery.h Analysis/include/Luau/AstQuery.h
Analysis/include/Luau/Autocomplete.h Analysis/include/Luau/Autocomplete.h
Analysis/include/Luau/BuiltinDefinitions.h Analysis/include/Luau/BuiltinDefinitions.h
@ -81,7 +83,7 @@ target_sources(Luau.Analysis PRIVATE
Analysis/include/Luau/Frontend.h Analysis/include/Luau/Frontend.h
Analysis/include/Luau/Instantiation.h Analysis/include/Luau/Instantiation.h
Analysis/include/Luau/IostreamHelpers.h Analysis/include/Luau/IostreamHelpers.h
Analysis/include/Luau/JsonEncoder.h Analysis/include/Luau/JsonEmitter.h
Analysis/include/Luau/Linter.h Analysis/include/Luau/Linter.h
Analysis/include/Luau/LValue.h Analysis/include/Luau/LValue.h
Analysis/include/Luau/Module.h Analysis/include/Luau/Module.h
@ -113,6 +115,8 @@ target_sources(Luau.Analysis PRIVATE
Analysis/include/Luau/Variant.h Analysis/include/Luau/Variant.h
Analysis/include/Luau/VisitTypeVar.h Analysis/include/Luau/VisitTypeVar.h
Analysis/src/ApplyTypeFunction.cpp
Analysis/src/AstJsonEncoder.cpp
Analysis/src/AstQuery.cpp Analysis/src/AstQuery.cpp
Analysis/src/Autocomplete.cpp Analysis/src/Autocomplete.cpp
Analysis/src/BuiltinDefinitions.cpp Analysis/src/BuiltinDefinitions.cpp
@ -126,7 +130,7 @@ target_sources(Luau.Analysis PRIVATE
Analysis/src/Frontend.cpp Analysis/src/Frontend.cpp
Analysis/src/Instantiation.cpp Analysis/src/Instantiation.cpp
Analysis/src/IostreamHelpers.cpp Analysis/src/IostreamHelpers.cpp
Analysis/src/JsonEncoder.cpp Analysis/src/JsonEmitter.cpp
Analysis/src/Linter.cpp Analysis/src/Linter.cpp
Analysis/src/LValue.cpp Analysis/src/LValue.cpp
Analysis/src/Module.cpp Analysis/src/Module.cpp
@ -255,6 +259,7 @@ if(TARGET Luau.UnitTest)
tests/ScopedFlags.h tests/ScopedFlags.h
tests/Fixture.cpp tests/Fixture.cpp
tests/AssemblyBuilderX64.test.cpp tests/AssemblyBuilderX64.test.cpp
tests/AstJsonEncoder.test.cpp
tests/AstQuery.test.cpp tests/AstQuery.test.cpp
tests/AstVisitor.test.cpp tests/AstVisitor.test.cpp
tests/Autocomplete.test.cpp tests/Autocomplete.test.cpp
@ -266,7 +271,7 @@ if(TARGET Luau.UnitTest)
tests/CostModel.test.cpp tests/CostModel.test.cpp
tests/Error.test.cpp tests/Error.test.cpp
tests/Frontend.test.cpp tests/Frontend.test.cpp
tests/JsonEncoder.test.cpp tests/JsonEmitter.test.cpp
tests/Lexer.test.cpp tests/Lexer.test.cpp
tests/Linter.test.cpp tests/Linter.test.cpp
tests/LValue.test.cpp tests/LValue.test.cpp

View File

@ -11,7 +11,7 @@
/* option for multiple returns in `lua_pcall' and `lua_call' */ // option for multiple returns in `lua_pcall' and `lua_call'
#define LUA_MULTRET (-1) #define LUA_MULTRET (-1)
/* /*
@ -23,7 +23,7 @@
#define lua_upvalueindex(i) (LUA_GLOBALSINDEX - (i)) #define lua_upvalueindex(i) (LUA_GLOBALSINDEX - (i))
#define lua_ispseudo(i) ((i) <= LUA_REGISTRYINDEX) #define lua_ispseudo(i) ((i) <= LUA_REGISTRYINDEX)
/* thread status; 0 is OK */ // thread status; 0 is OK
enum lua_Status enum lua_Status
{ {
LUA_OK = 0, LUA_OK = 0,
@ -32,7 +32,7 @@ enum lua_Status
LUA_ERRSYNTAX, LUA_ERRSYNTAX,
LUA_ERRMEM, LUA_ERRMEM,
LUA_ERRERR, LUA_ERRERR,
LUA_BREAK, /* yielded for a debug breakpoint */ LUA_BREAK, // yielded for a debug breakpoint
}; };
typedef struct lua_State lua_State; typedef struct lua_State lua_State;
@ -46,7 +46,7 @@ typedef int (*lua_Continuation)(lua_State* L, int status);
typedef void* (*lua_Alloc)(void* ud, void* ptr, size_t osize, size_t nsize); typedef void* (*lua_Alloc)(void* ud, void* ptr, size_t osize, size_t nsize);
/* non-return type */ // non-return type
#define l_noret void LUA_NORETURN #define l_noret void LUA_NORETURN
/* /*
@ -61,15 +61,15 @@ typedef void* (*lua_Alloc)(void* ud, void* ptr, size_t osize, size_t nsize);
// clang-format off // clang-format off
enum lua_Type enum lua_Type
{ {
LUA_TNIL = 0, /* must be 0 due to lua_isnoneornil */ LUA_TNIL = 0, // must be 0 due to lua_isnoneornil
LUA_TBOOLEAN = 1, /* must be 1 due to l_isfalse */ LUA_TBOOLEAN = 1, // must be 1 due to l_isfalse
LUA_TLIGHTUSERDATA, LUA_TLIGHTUSERDATA,
LUA_TNUMBER, LUA_TNUMBER,
LUA_TVECTOR, LUA_TVECTOR,
LUA_TSTRING, /* all types above this must be value types, all types below this must be GC types - see iscollectable */ LUA_TSTRING, // all types above this must be value types, all types below this must be GC types - see iscollectable
LUA_TTABLE, LUA_TTABLE,
@ -77,23 +77,23 @@ enum lua_Type
LUA_TUSERDATA, LUA_TUSERDATA,
LUA_TTHREAD, LUA_TTHREAD,
/* values below this line are used in GCObject tags but may never show up in TValue type tags */ // values below this line are used in GCObject tags but may never show up in TValue type tags
LUA_TPROTO, LUA_TPROTO,
LUA_TUPVAL, LUA_TUPVAL,
LUA_TDEADKEY, LUA_TDEADKEY,
/* the count of TValue type tags */ // the count of TValue type tags
LUA_T_COUNT = LUA_TPROTO LUA_T_COUNT = LUA_TPROTO
}; };
// clang-format on // clang-format on
/* type of numbers in Luau */ // type of numbers in Luau
typedef double lua_Number; typedef double lua_Number;
/* type for integer functions */ // type for integer functions
typedef int lua_Integer; typedef int lua_Integer;
/* unsigned integer type */ // unsigned integer type
typedef unsigned lua_Unsigned; typedef unsigned lua_Unsigned;
/* /*
@ -117,7 +117,7 @@ LUA_API void lua_remove(lua_State* L, int idx);
LUA_API void lua_insert(lua_State* L, int idx); LUA_API void lua_insert(lua_State* L, int idx);
LUA_API void lua_replace(lua_State* L, int idx); LUA_API void lua_replace(lua_State* L, int idx);
LUA_API int lua_checkstack(lua_State* L, int sz); LUA_API int lua_checkstack(lua_State* L, int sz);
LUA_API void lua_rawcheckstack(lua_State* L, int sz); /* allows for unlimited stack frames */ LUA_API void lua_rawcheckstack(lua_State* L, int sz); // allows for unlimited stack frames
LUA_API void lua_xmove(lua_State* from, lua_State* to, int n); LUA_API void lua_xmove(lua_State* from, lua_State* to, int n);
LUA_API void lua_xpush(lua_State* from, lua_State* to, int idx); LUA_API void lua_xpush(lua_State* from, lua_State* to, int idx);
@ -231,18 +231,18 @@ LUA_API void lua_setthreaddata(lua_State* L, void* data);
enum lua_GCOp enum lua_GCOp
{ {
/* stop and resume incremental garbage collection */ // stop and resume incremental garbage collection
LUA_GCSTOP, LUA_GCSTOP,
LUA_GCRESTART, LUA_GCRESTART,
/* run a full GC cycle; not recommended for latency sensitive applications */ // run a full GC cycle; not recommended for latency sensitive applications
LUA_GCCOLLECT, LUA_GCCOLLECT,
/* return the heap size in KB and the remainder in bytes */ // return the heap size in KB and the remainder in bytes
LUA_GCCOUNT, LUA_GCCOUNT,
LUA_GCCOUNTB, LUA_GCCOUNTB,
/* return 1 if GC is active (not stopped); note that GC may not be actively collecting even if it's running */ // return 1 if GC is active (not stopped); note that GC may not be actively collecting even if it's running
LUA_GCISRUNNING, LUA_GCISRUNNING,
/* /*
@ -359,9 +359,9 @@ LUA_API void lua_unref(lua_State* L, int ref);
** ======================================================================= ** =======================================================================
*/ */
typedef struct lua_Debug lua_Debug; /* activation record */ typedef struct lua_Debug lua_Debug; // activation record
/* Functions to be called by the debugger in specific events */ // Functions to be called by the debugger in specific events
typedef void (*lua_Hook)(lua_State* L, lua_Debug* ar); typedef void (*lua_Hook)(lua_State* L, lua_Debug* ar);
LUA_API int lua_stackdepth(lua_State* L); LUA_API int lua_stackdepth(lua_State* L);
@ -379,24 +379,24 @@ typedef void (*lua_Coverage)(void* context, const char* function, int linedefine
LUA_API void lua_getcoverage(lua_State* L, int funcindex, void* context, lua_Coverage callback); LUA_API void lua_getcoverage(lua_State* L, int funcindex, void* context, lua_Coverage callback);
/* Warning: this function is not thread-safe since it stores the result in a shared global array! Only use for debugging. */ // Warning: this function is not thread-safe since it stores the result in a shared global array! Only use for debugging.
LUA_API const char* lua_debugtrace(lua_State* L); LUA_API const char* lua_debugtrace(lua_State* L);
struct lua_Debug struct lua_Debug
{ {
const char* name; /* (n) */ const char* name; // (n)
const char* what; /* (s) `Lua', `C', `main', `tail' */ const char* what; // (s) `Lua', `C', `main', `tail'
const char* source; /* (s) */ const char* source; // (s)
int linedefined; /* (s) */ int linedefined; // (s)
int currentline; /* (l) */ int currentline; // (l)
unsigned char nupvals; /* (u) number of upvalues */ unsigned char nupvals; // (u) number of upvalues
unsigned char nparams; /* (a) number of parameters */ unsigned char nparams; // (a) number of parameters
char isvararg; /* (a) */ char isvararg; // (a)
char short_src[LUA_IDSIZE]; /* (s) */ char short_src[LUA_IDSIZE]; // (s)
void* userdata; /* only valid in luau_callhook */ void* userdata; // only valid in luau_callhook
}; };
/* }====================================================================== */ // }======================================================================
/* Callbacks that can be used to reconfigure behavior of the VM dynamically. /* Callbacks that can be used to reconfigure behavior of the VM dynamically.
* These are shared between all coroutines. * These are shared between all coroutines.
@ -405,18 +405,18 @@ struct lua_Debug
* can only be changed when the VM is not running any code */ * can only be changed when the VM is not running any code */
struct lua_Callbacks struct lua_Callbacks
{ {
void* userdata; /* arbitrary userdata pointer that is never overwritten by Luau */ void* userdata; // arbitrary userdata pointer that is never overwritten by Luau
void (*interrupt)(lua_State* L, int gc); /* gets called at safepoints (loop back edges, call/ret, gc) if set */ void (*interrupt)(lua_State* L, int gc); // gets called at safepoints (loop back edges, call/ret, gc) if set
void (*panic)(lua_State* L, int errcode); /* gets called when an unprotected error is raised (if longjmp is used) */ void (*panic)(lua_State* L, int errcode); // gets called when an unprotected error is raised (if longjmp is used)
void (*userthread)(lua_State* LP, lua_State* L); /* gets called when L is created (LP == parent) or destroyed (LP == NULL) */ void (*userthread)(lua_State* LP, lua_State* L); // gets called when L is created (LP == parent) or destroyed (LP == NULL)
int16_t (*useratom)(const char* s, size_t l); /* gets called when a string is created; returned atom can be retrieved via tostringatom */ int16_t (*useratom)(const char* s, size_t l); // gets called when a string is created; returned atom can be retrieved via tostringatom
void (*debugbreak)(lua_State* L, lua_Debug* ar); /* gets called when BREAK instruction is encountered */ void (*debugbreak)(lua_State* L, lua_Debug* ar); // gets called when BREAK instruction is encountered
void (*debugstep)(lua_State* L, lua_Debug* ar); /* gets called after each instruction in single step mode */ void (*debugstep)(lua_State* L, lua_Debug* ar); // gets called after each instruction in single step mode
void (*debuginterrupt)(lua_State* L, lua_Debug* ar); /* gets called when thread execution is interrupted by break in another thread */ void (*debuginterrupt)(lua_State* L, lua_Debug* ar); // gets called when thread execution is interrupted by break in another thread
void (*debugprotectederror)(lua_State* L); /* gets called when protected call results in an error */ void (*debugprotectederror)(lua_State* L); // gets called when protected call results in an error
}; };
typedef struct lua_Callbacks lua_Callbacks; typedef struct lua_Callbacks lua_Callbacks;

View File

@ -33,14 +33,14 @@
#define LUA_NORETURN __attribute__((__noreturn__)) #define LUA_NORETURN __attribute__((__noreturn__))
#endif #endif
/* Can be used to reconfigure visibility/exports for public APIs */ // Can be used to reconfigure visibility/exports for public APIs
#ifndef LUA_API #ifndef LUA_API
#define LUA_API extern #define LUA_API extern
#endif #endif
#define LUALIB_API LUA_API #define LUALIB_API LUA_API
/* Can be used to reconfigure visibility for internal APIs */ // Can be used to reconfigure visibility for internal APIs
#if defined(__GNUC__) #if defined(__GNUC__)
#define LUAI_FUNC __attribute__((visibility("hidden"))) extern #define LUAI_FUNC __attribute__((visibility("hidden"))) extern
#define LUAI_DATA LUAI_FUNC #define LUAI_DATA LUAI_FUNC
@ -49,67 +49,67 @@
#define LUAI_DATA extern #define LUAI_DATA extern
#endif #endif
/* Can be used to reconfigure internal error handling to use longjmp instead of C++ EH */ // Can be used to reconfigure internal error handling to use longjmp instead of C++ EH
#ifndef LUA_USE_LONGJMP #ifndef LUA_USE_LONGJMP
#define LUA_USE_LONGJMP 0 #define LUA_USE_LONGJMP 0
#endif #endif
/* LUA_IDSIZE gives the maximum size for the description of the source */ // LUA_IDSIZE gives the maximum size for the description of the source
#ifndef LUA_IDSIZE #ifndef LUA_IDSIZE
#define LUA_IDSIZE 256 #define LUA_IDSIZE 256
#endif #endif
/* LUA_MINSTACK is the guaranteed number of Lua stack slots available to a C function */ // LUA_MINSTACK is the guaranteed number of Lua stack slots available to a C function
#ifndef LUA_MINSTACK #ifndef LUA_MINSTACK
#define LUA_MINSTACK 20 #define LUA_MINSTACK 20
#endif #endif
/* LUAI_MAXCSTACK limits the number of Lua stack slots that a C function can use */ // LUAI_MAXCSTACK limits the number of Lua stack slots that a C function can use
#ifndef LUAI_MAXCSTACK #ifndef LUAI_MAXCSTACK
#define LUAI_MAXCSTACK 8000 #define LUAI_MAXCSTACK 8000
#endif #endif
/* LUAI_MAXCALLS limits the number of nested calls */ // LUAI_MAXCALLS limits the number of nested calls
#ifndef LUAI_MAXCALLS #ifndef LUAI_MAXCALLS
#define LUAI_MAXCALLS 20000 #define LUAI_MAXCALLS 20000
#endif #endif
/* LUAI_MAXCCALLS is the maximum depth for nested C calls; this limit depends on native stack size */ // LUAI_MAXCCALLS is the maximum depth for nested C calls; this limit depends on native stack size
#ifndef LUAI_MAXCCALLS #ifndef LUAI_MAXCCALLS
#define LUAI_MAXCCALLS 200 #define LUAI_MAXCCALLS 200
#endif #endif
/* buffer size used for on-stack string operations; this limit depends on native stack size */ // buffer size used for on-stack string operations; this limit depends on native stack size
#ifndef LUA_BUFFERSIZE #ifndef LUA_BUFFERSIZE
#define LUA_BUFFERSIZE 512 #define LUA_BUFFERSIZE 512
#endif #endif
/* number of valid Lua userdata tags */ // number of valid Lua userdata tags
#ifndef LUA_UTAG_LIMIT #ifndef LUA_UTAG_LIMIT
#define LUA_UTAG_LIMIT 128 #define LUA_UTAG_LIMIT 128
#endif #endif
/* upper bound for number of size classes used by page allocator */ // upper bound for number of size classes used by page allocator
#ifndef LUA_SIZECLASSES #ifndef LUA_SIZECLASSES
#define LUA_SIZECLASSES 32 #define LUA_SIZECLASSES 32
#endif #endif
/* available number of separate memory categories */ // available number of separate memory categories
#ifndef LUA_MEMORY_CATEGORIES #ifndef LUA_MEMORY_CATEGORIES
#define LUA_MEMORY_CATEGORIES 256 #define LUA_MEMORY_CATEGORIES 256
#endif #endif
/* minimum size for the string table (must be power of 2) */ // minimum size for the string table (must be power of 2)
#ifndef LUA_MINSTRTABSIZE #ifndef LUA_MINSTRTABSIZE
#define LUA_MINSTRTABSIZE 32 #define LUA_MINSTRTABSIZE 32
#endif #endif
/* maximum number of captures supported by pattern matching */ // maximum number of captures supported by pattern matching
#ifndef LUA_MAXCAPTURES #ifndef LUA_MAXCAPTURES
#define LUA_MAXCAPTURES 32 #define LUA_MAXCAPTURES 32
#endif #endif
/* }================================================================== */ // }==================================================================
/* /*
@@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment. @@ LUAI_USER_ALIGNMENT_T is a type that requires maximum alignment.
@ -126,6 +126,6 @@
long l; \ long l; \
} }
#define LUA_VECTOR_SIZE 3 /* must be 3 or 4 */ #define LUA_VECTOR_SIZE 3 // must be 3 or 4
#define LUA_EXTRA_SIZE LUA_VECTOR_SIZE - 2 #define LUA_EXTRA_SIZE LUA_VECTOR_SIZE - 2

View File

@ -72,7 +72,7 @@ LUALIB_API const char* luaL_typename(lua_State* L, int idx);
#define luaL_opt(L, f, n, d) (lua_isnoneornil(L, (n)) ? (d) : f(L, (n))) #define luaL_opt(L, f, n, d) (lua_isnoneornil(L, (n)) ? (d) : f(L, (n)))
/* generic buffer manipulation */ // generic buffer manipulation
struct luaL_Buffer struct luaL_Buffer
{ {
@ -102,7 +102,7 @@ LUALIB_API void luaL_addvalue(luaL_Buffer* B);
LUALIB_API void luaL_pushresult(luaL_Buffer* B); LUALIB_API void luaL_pushresult(luaL_Buffer* B);
LUALIB_API void luaL_pushresultsize(luaL_Buffer* B, size_t size); LUALIB_API void luaL_pushresultsize(luaL_Buffer* B, size_t size);
/* builtin libraries */ // builtin libraries
LUALIB_API int luaopen_base(lua_State* L); LUALIB_API int luaopen_base(lua_State* L);
#define LUA_COLIBNAME "coroutine" #define LUA_COLIBNAME "coroutine"
@ -129,9 +129,9 @@ LUALIB_API int luaopen_math(lua_State* L);
#define LUA_DBLIBNAME "debug" #define LUA_DBLIBNAME "debug"
LUALIB_API int luaopen_debug(lua_State* L); LUALIB_API int luaopen_debug(lua_State* L);
/* open all builtin libraries */ // open all builtin libraries
LUALIB_API void luaL_openlibs(lua_State* L); LUALIB_API void luaL_openlibs(lua_State* L);
/* sandbox libraries and globals */ // sandbox libraries and globals
LUALIB_API void luaL_sandbox(lua_State* L); LUALIB_API void luaL_sandbox(lua_State* L);
LUALIB_API void luaL_sandboxthread(lua_State* L); LUALIB_API void luaL_sandboxthread(lua_State* L);

View File

@ -59,8 +59,8 @@ const char* luau_ident = "$Luau: Copyright (C) 2019-2022 Roblox Corporation $\n"
static Table* getcurrenv(lua_State* L) static Table* getcurrenv(lua_State* L)
{ {
if (L->ci == L->base_ci) /* no enclosing function? */ if (L->ci == L->base_ci) // no enclosing function?
return L->gt; /* use global table as environment */ return L->gt; // use global table as environment
else else
return curr_func(L)->env; return curr_func(L)->env;
} }
@ -69,7 +69,7 @@ static LUAU_NOINLINE TValue* pseudo2addr(lua_State* L, int idx)
{ {
api_check(L, lua_ispseudo(idx)); api_check(L, lua_ispseudo(idx));
switch (idx) switch (idx)
{ /* pseudo-indices */ { // pseudo-indices
case LUA_REGISTRYINDEX: case LUA_REGISTRYINDEX:
return registry(L); return registry(L);
case LUA_ENVIRONINDEX: case LUA_ENVIRONINDEX:
@ -129,7 +129,7 @@ int lua_checkstack(lua_State* L, int size)
{ {
int res = 1; int res = 1;
if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK) if (size > LUAI_MAXCSTACK || (L->top - L->base + size) > LUAI_MAXCSTACK)
res = 0; /* stack overflow */ res = 0; // stack overflow
else if (size > 0) else if (size > 0)
{ {
luaD_checkstack(L, size); luaD_checkstack(L, size);
@ -219,7 +219,7 @@ void lua_settop(lua_State* L, int idx)
else else
{ {
api_check(L, -(idx + 1) <= (L->top - L->base)); api_check(L, -(idx + 1) <= (L->top - L->base));
L->top += idx + 1; /* `subtract' index (index is negative) */ L->top += idx + 1; // `subtract' index (index is negative)
} }
return; return;
} }
@ -267,7 +267,7 @@ void lua_replace(lua_State* L, int idx)
else else
{ {
setobj(L, o, L->top - 1); setobj(L, o, L->top - 1);
if (idx < LUA_GLOBALSINDEX) /* function upvalue? */ if (idx < LUA_GLOBALSINDEX) // function upvalue?
luaC_barrier(L, curr_func(L), L->top - 1); luaC_barrier(L, curr_func(L), L->top - 1);
} }
L->top--; L->top--;
@ -429,13 +429,13 @@ const char* lua_tolstring(lua_State* L, int idx, size_t* len)
{ {
luaC_checkthreadsleep(L); luaC_checkthreadsleep(L);
if (!luaV_tostring(L, o)) if (!luaV_tostring(L, o))
{ /* conversion failed? */ { // conversion failed?
if (len != NULL) if (len != NULL)
*len = 0; *len = 0;
return NULL; return NULL;
} }
luaC_checkGC(L); luaC_checkGC(L);
o = index2addr(L, idx); /* previous call may reallocate the stack */ o = index2addr(L, idx); // previous call may reallocate the stack
} }
if (len != NULL) if (len != NULL)
*len = tsvalue(o)->len; *len = tsvalue(o)->len;
@ -660,7 +660,7 @@ void lua_pushcclosurek(lua_State* L, lua_CFunction fn, const char* debugname, in
void lua_pushboolean(lua_State* L, int b) void lua_pushboolean(lua_State* L, int b)
{ {
setbvalue(L->top, (b != 0)); /* ensure that true is 1 */ setbvalue(L->top, (b != 0)); // ensure that true is 1
api_incr_top(L); api_incr_top(L);
return; return;
} }
@ -829,7 +829,7 @@ void lua_settable(lua_State* L, int idx)
StkId t = index2addr(L, idx); StkId t = index2addr(L, idx);
api_checkvalidindex(L, t); api_checkvalidindex(L, t);
luaV_settable(L, t, L->top - 2, L->top - 1); luaV_settable(L, t, L->top - 2, L->top - 1);
L->top -= 2; /* pop index and value */ L->top -= 2; // pop index and value
return; return;
} }
@ -967,7 +967,7 @@ void lua_call(lua_State* L, int nargs, int nresults)
** Execute a protected call. ** Execute a protected call.
*/ */
struct CallS struct CallS
{ /* data to `f_call' */ { // data to `f_call'
StkId func; StkId func;
int nresults; int nresults;
}; };
@ -992,7 +992,7 @@ int lua_pcall(lua_State* L, int nargs, int nresults, int errfunc)
func = savestack(L, o); func = savestack(L, o);
} }
struct CallS c; struct CallS c;
c.func = L->top - (nargs + 1); /* function to be called */ c.func = L->top - (nargs + 1); // function to be called
c.nresults = nresults; c.nresults = nresults;
int status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func); int status = luaD_pcall(L, f_call, &c, savestack(L, c.func), func);
@ -1044,7 +1044,7 @@ int lua_gc(lua_State* L, int what, int data)
} }
case LUA_GCCOUNT: case LUA_GCCOUNT:
{ {
/* GC values are expressed in Kbytes: #bytes/2^10 */ // GC values are expressed in Kbytes: #bytes/2^10
res = cast_int(g->totalbytes >> 10); res = cast_int(g->totalbytes >> 10);
break; break;
} }
@ -1084,8 +1084,8 @@ int lua_gc(lua_State* L, int what, int data)
actualwork += stepsize; actualwork += stepsize;
if (g->gcstate == GCSpause) if (g->gcstate == GCSpause)
{ /* end of cycle? */ { // end of cycle?
res = 1; /* signal it */ res = 1; // signal it
break; break;
} }
} }
@ -1137,13 +1137,13 @@ int lua_gc(lua_State* L, int what, int data)
} }
case LUA_GCSETSTEPSIZE: case LUA_GCSETSTEPSIZE:
{ {
/* GC values are expressed in Kbytes: #bytes/2^10 */ // GC values are expressed in Kbytes: #bytes/2^10
res = g->gcstepsize >> 10; res = g->gcstepsize >> 10;
g->gcstepsize = data << 10; g->gcstepsize = data << 10;
break; break;
} }
default: default:
res = -1; /* invalid option */ res = -1; // invalid option
} }
return res; return res;
} }
@ -1169,8 +1169,8 @@ int lua_next(lua_State* L, int idx)
{ {
api_incr_top(L); api_incr_top(L);
} }
else /* no more elements */ else // no more elements
L->top -= 1; /* remove key */ L->top -= 1; // remove key
return more; return more;
} }
@ -1185,12 +1185,12 @@ void lua_concat(lua_State* L, int n)
L->top -= (n - 1); L->top -= (n - 1);
} }
else if (n == 0) else if (n == 0)
{ /* push empty string */ { // push empty string
luaC_checkthreadsleep(L); luaC_checkthreadsleep(L);
setsvalue2s(L, L->top, luaS_newlstr(L, "", 0)); setsvalue2s(L, L->top, luaS_newlstr(L, "", 0));
api_incr_top(L); api_incr_top(L);
} }
/* else n == 1; nothing to do */ // else n == 1; nothing to do
return; return;
} }
@ -1277,7 +1277,7 @@ uintptr_t lua_encodepointer(lua_State* L, uintptr_t p)
int lua_ref(lua_State* L, int idx) int lua_ref(lua_State* L, int idx)
{ {
api_check(L, idx != LUA_REGISTRYINDEX); /* idx is a stack index for value */ api_check(L, idx != LUA_REGISTRYINDEX); // idx is a stack index for value
int ref = LUA_REFNIL; int ref = LUA_REFNIL;
global_State* g = L->global; global_State* g = L->global;
StkId p = index2addr(L, idx); StkId p = index2addr(L, idx);
@ -1286,13 +1286,13 @@ int lua_ref(lua_State* L, int idx)
Table* reg = hvalue(registry(L)); Table* reg = hvalue(registry(L));
if (g->registryfree != 0) if (g->registryfree != 0)
{ /* reuse existing slot */ { // reuse existing slot
ref = g->registryfree; ref = g->registryfree;
} }
else else
{ /* no free elements */ { // no free elements
ref = luaH_getn(reg); ref = luaH_getn(reg);
ref++; /* create new reference */ ref++; // create new reference
} }
TValue* slot = luaH_setnum(L, reg, ref); TValue* slot = luaH_setnum(L, reg, ref);
@ -1312,7 +1312,7 @@ void lua_unref(lua_State* L, int ref)
global_State* g = L->global; global_State* g = L->global;
Table* reg = hvalue(registry(L)); Table* 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;
return; return;
} }

View File

@ -11,7 +11,7 @@
#include <string.h> #include <string.h>
/* convert a stack index to positive */ // convert a stack index to positive
#define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1) #define abs_index(L, i) ((i) > 0 || (i) <= LUA_REGISTRYINDEX ? (i) : lua_gettop(L) + (i) + 1)
/* /*
@ -75,7 +75,7 @@ void luaL_where(lua_State* L, int level)
lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline); lua_pushfstring(L, "%s:%d: ", ar.short_src, ar.currentline);
return; return;
} }
lua_pushliteral(L, ""); /* else, no information available... */ lua_pushliteral(L, ""); // else, no information available...
} }
l_noret luaL_errorL(lua_State* L, const char* fmt, ...) l_noret luaL_errorL(lua_State* L, const char* fmt, ...)
@ -89,7 +89,7 @@ l_noret luaL_errorL(lua_State* L, const char* fmt, ...)
lua_error(L); lua_error(L);
} }
/* }====================================================== */ // }======================================================
int luaL_checkoption(lua_State* L, int narg, const char* def, const char* const lst[]) int luaL_checkoption(lua_State* L, int narg, const char* def, const char* const lst[])
{ {
@ -104,13 +104,13 @@ int luaL_checkoption(lua_State* L, int narg, const char* def, const char* const
int luaL_newmetatable(lua_State* L, const char* tname) int luaL_newmetatable(lua_State* L, const char* tname)
{ {
lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get registry.name */ lua_getfield(L, LUA_REGISTRYINDEX, tname); // get registry.name
if (!lua_isnil(L, -1)) /* name already in use? */ if (!lua_isnil(L, -1)) // name already in use?
return 0; /* leave previous value on top, but return 0 */ return 0; // leave previous value on top, but return 0
lua_pop(L, 1); lua_pop(L, 1);
lua_newtable(L); /* create metatable */ lua_newtable(L); // create metatable
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
lua_setfield(L, LUA_REGISTRYINDEX, tname); /* registry.name = metatable */ lua_setfield(L, LUA_REGISTRYINDEX, tname); // registry.name = metatable
return 1; return 1;
} }
@ -118,18 +118,18 @@ void* luaL_checkudata(lua_State* L, int ud, const char* tname)
{ {
void* p = lua_touserdata(L, ud); void* p = lua_touserdata(L, ud);
if (p != NULL) if (p != NULL)
{ /* value is a userdata? */ { // value is a userdata?
if (lua_getmetatable(L, ud)) if (lua_getmetatable(L, ud))
{ /* does it have a metatable? */ { // does it have a metatable?
lua_getfield(L, LUA_REGISTRYINDEX, tname); /* get correct metatable */ lua_getfield(L, LUA_REGISTRYINDEX, tname); // get correct metatable
if (lua_rawequal(L, -1, -2)) if (lua_rawequal(L, -1, -2))
{ /* does it have the correct mt? */ { // does it have the correct mt?
lua_pop(L, 2); /* remove both metatables */ lua_pop(L, 2); // remove both metatables
return p; return p;
} }
} }
} }
luaL_typeerrorL(L, ud, tname); /* else error */ luaL_typeerrorL(L, ud, tname); // else error
} }
void luaL_checkstack(lua_State* L, int space, const char* mes) void luaL_checkstack(lua_State* L, int space, const char* mes)
@ -243,18 +243,18 @@ const float* luaL_optvector(lua_State* L, int narg, const float* def)
int luaL_getmetafield(lua_State* L, int obj, const char* event) int luaL_getmetafield(lua_State* L, int obj, const char* event)
{ {
if (!lua_getmetatable(L, obj)) /* no metatable? */ if (!lua_getmetatable(L, obj)) // no metatable?
return 0; return 0;
lua_pushstring(L, event); lua_pushstring(L, event);
lua_rawget(L, -2); lua_rawget(L, -2);
if (lua_isnil(L, -1)) if (lua_isnil(L, -1))
{ {
lua_pop(L, 2); /* remove metatable and metafield */ lua_pop(L, 2); // remove metatable and metafield
return 0; return 0;
} }
else else
{ {
lua_remove(L, -2); /* remove only metatable */ lua_remove(L, -2); // remove only metatable
return 1; return 1;
} }
} }
@ -262,7 +262,7 @@ int luaL_getmetafield(lua_State* L, int obj, const char* event)
int luaL_callmeta(lua_State* L, int obj, const char* event) int luaL_callmeta(lua_State* L, int obj, const char* event)
{ {
obj = abs_index(L, obj); obj = abs_index(L, obj);
if (!luaL_getmetafield(L, obj, event)) /* no metafield? */ if (!luaL_getmetafield(L, obj, event)) // no metafield?
return 0; return 0;
lua_pushvalue(L, obj); lua_pushvalue(L, obj);
lua_call(L, 1, 1); lua_call(L, 1, 1);
@ -282,19 +282,19 @@ void luaL_register(lua_State* L, const char* libname, const luaL_Reg* l)
if (libname) if (libname)
{ {
int size = libsize(l); int size = libsize(l);
/* check whether lib already exists */ // check whether lib already exists
luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1); luaL_findtable(L, LUA_REGISTRYINDEX, "_LOADED", 1);
lua_getfield(L, -1, libname); /* get _LOADED[libname] */ lua_getfield(L, -1, libname); // get _LOADED[libname]
if (!lua_istable(L, -1)) if (!lua_istable(L, -1))
{ /* not found? */ { // not found?
lua_pop(L, 1); /* remove previous result */ lua_pop(L, 1); // remove previous result
/* try global variable (and create one if it does not exist) */ // try global variable (and create one if it does not exist)
if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL) if (luaL_findtable(L, LUA_GLOBALSINDEX, libname, size) != NULL)
luaL_error(L, "name conflict for module '%s'", libname); luaL_error(L, "name conflict for module '%s'", libname);
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
lua_setfield(L, -3, libname); /* _LOADED[libname] = new table */ lua_setfield(L, -3, libname); // _LOADED[libname] = new table
} }
lua_remove(L, -2); /* remove _LOADED table */ lua_remove(L, -2); // remove _LOADED table
} }
for (; l->name; l++) for (; l->name; l++)
{ {
@ -315,19 +315,19 @@ const char* luaL_findtable(lua_State* L, int idx, const char* fname, int szhint)
lua_pushlstring(L, fname, e - fname); lua_pushlstring(L, fname, e - fname);
lua_rawget(L, -2); lua_rawget(L, -2);
if (lua_isnil(L, -1)) if (lua_isnil(L, -1))
{ /* no such field? */ { // no such field?
lua_pop(L, 1); /* remove this nil */ lua_pop(L, 1); // remove this nil
lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); /* new table for field */ lua_createtable(L, 0, (*e == '.' ? 1 : szhint)); // new table for field
lua_pushlstring(L, fname, e - fname); lua_pushlstring(L, fname, e - fname);
lua_pushvalue(L, -2); lua_pushvalue(L, -2);
lua_settable(L, -4); /* set new table into field */ lua_settable(L, -4); // set new table into field
} }
else if (!lua_istable(L, -1)) else if (!lua_istable(L, -1))
{ /* field has a non-table value? */ { // field has a non-table value?
lua_pop(L, 2); /* remove table and value */ lua_pop(L, 2); // remove table and value
return fname; /* return problematic part of the name */ return fname; // return problematic part of the name
} }
lua_remove(L, -2); /* remove previous table */ lua_remove(L, -2); // remove previous table
fname = e + 1; fname = e + 1;
} while (*e == '.'); } while (*e == '.');
return NULL; return NULL;
@ -470,11 +470,11 @@ void luaL_pushresultsize(luaL_Buffer* B, size_t size)
luaL_pushresult(B); luaL_pushresult(B);
} }
/* }====================================================== */ // }======================================================
const char* luaL_tolstring(lua_State* L, int idx, size_t* len) const char* luaL_tolstring(lua_State* L, int idx, size_t* len)
{ {
if (luaL_callmeta(L, idx, "__tostring")) /* is there a metafield? */ if (luaL_callmeta(L, idx, "__tostring")) // is there a metafield?
{ {
if (!lua_isstring(L, -1)) if (!lua_isstring(L, -1))
luaL_error(L, "'__tostring' must return a string"); luaL_error(L, "'__tostring' must return a string");

View File

@ -11,8 +11,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
LUAU_FASTFLAG(LuauLenTM)
static void writestring(const char* s, size_t l) static void writestring(const char* s, size_t l)
{ {
fwrite(s, 1, l, stdout); fwrite(s, 1, l, stdout);
@ -20,15 +18,15 @@ static void writestring(const char* s, size_t l)
static int luaB_print(lua_State* L) static int luaB_print(lua_State* L)
{ {
int n = lua_gettop(L); /* number of arguments */ int n = lua_gettop(L); // number of arguments
for (int i = 1; i <= n; i++) for (int i = 1; i <= n; i++)
{ {
size_t l; size_t l;
const char* s = luaL_tolstring(L, i, &l); /* convert to string using __tostring et al */ const char* s = luaL_tolstring(L, i, &l); // convert to string using __tostring et al
if (i > 1) if (i > 1)
writestring("\t", 1); writestring("\t", 1);
writestring(s, l); writestring(s, l);
lua_pop(L, 1); /* pop result */ lua_pop(L, 1); // pop result
} }
writestring("\n", 1); writestring("\n", 1);
return 0; return 0;
@ -38,7 +36,7 @@ static int luaB_tonumber(lua_State* L)
{ {
int base = luaL_optinteger(L, 2, 10); int base = luaL_optinteger(L, 2, 10);
if (base == 10) if (base == 10)
{ /* standard conversion */ { // standard conversion
int isnum = 0; int isnum = 0;
double n = lua_tonumberx(L, 1, &isnum); double n = lua_tonumberx(L, 1, &isnum);
if (isnum) if (isnum)
@ -46,7 +44,7 @@ static int luaB_tonumber(lua_State* L)
lua_pushnumber(L, n); lua_pushnumber(L, n);
return 1; return 1;
} }
luaL_checkany(L, 1); /* error if we don't have any argument */ luaL_checkany(L, 1); // error if we don't have any argument
} }
else else
{ {
@ -56,17 +54,17 @@ static int luaB_tonumber(lua_State* L)
unsigned long long n; unsigned long long n;
n = strtoull(s1, &s2, base); n = strtoull(s1, &s2, base);
if (s1 != s2) if (s1 != s2)
{ /* at least one valid digit? */ { // at least one valid digit?
while (isspace((unsigned char)(*s2))) while (isspace((unsigned char)(*s2)))
s2++; /* skip trailing spaces */ s2++; // skip trailing spaces
if (*s2 == '\0') if (*s2 == '\0')
{ /* no invalid trailing characters? */ { // no invalid trailing characters?
lua_pushnumber(L, (double)n); lua_pushnumber(L, (double)n);
return 1; return 1;
} }
} }
} }
lua_pushnil(L); /* else not a number */ lua_pushnil(L); // else not a number
return 1; return 1;
} }
@ -75,7 +73,7 @@ static int luaB_error(lua_State* L)
int level = luaL_optinteger(L, 2, 1); int level = luaL_optinteger(L, 2, 1);
lua_settop(L, 1); lua_settop(L, 1);
if (lua_isstring(L, 1) && level > 0) if (lua_isstring(L, 1) && level > 0)
{ /* add extra information? */ { // add extra information?
luaL_where(L, level); luaL_where(L, level);
lua_pushvalue(L, 1); lua_pushvalue(L, 1);
lua_concat(L, 2); lua_concat(L, 2);
@ -89,10 +87,10 @@ static int luaB_getmetatable(lua_State* L)
if (!lua_getmetatable(L, 1)) if (!lua_getmetatable(L, 1))
{ {
lua_pushnil(L); lua_pushnil(L);
return 1; /* no metatable */ return 1; // no metatable
} }
luaL_getmetafield(L, 1, "__metatable"); luaL_getmetafield(L, 1, "__metatable");
return 1; /* returns either __metatable field (if present) or metatable */ return 1; // returns either __metatable field (if present) or metatable
} }
static int luaB_setmetatable(lua_State* L) static int luaB_setmetatable(lua_State* L)
@ -126,8 +124,8 @@ static void getfunc(lua_State* L, int opt)
static int luaB_getfenv(lua_State* L) static int luaB_getfenv(lua_State* L)
{ {
getfunc(L, 1); getfunc(L, 1);
if (lua_iscfunction(L, -1)) /* is a C function? */ if (lua_iscfunction(L, -1)) // is a C function?
lua_pushvalue(L, LUA_GLOBALSINDEX); /* return the thread's global env. */ lua_pushvalue(L, LUA_GLOBALSINDEX); // return the thread's global env.
else else
lua_getfenv(L, -1); lua_getfenv(L, -1);
lua_setsafeenv(L, -1, false); lua_setsafeenv(L, -1, false);
@ -142,7 +140,7 @@ static int luaB_setfenv(lua_State* L)
lua_setsafeenv(L, -1, false); lua_setsafeenv(L, -1, false);
if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0) if (lua_isnumber(L, 1) && lua_tonumber(L, 1) == 0)
{ {
/* change environment of current thread */ // change environment of current thread
lua_pushthread(L); lua_pushthread(L);
lua_insert(L, -2); lua_insert(L, -2);
lua_setfenv(L, -2); lua_setfenv(L, -2);
@ -182,9 +180,6 @@ static int luaB_rawset(lua_State* L)
static int luaB_rawlen(lua_State* L) static int luaB_rawlen(lua_State* L)
{ {
if (!FFlag::LuauLenTM)
luaL_error(L, "'rawlen' is not available");
int tt = lua_type(L, 1); int tt = lua_type(L, 1);
luaL_argcheck(L, tt == LUA_TTABLE || tt == LUA_TSTRING, 1, "table or string expected"); luaL_argcheck(L, tt == LUA_TTABLE || tt == LUA_TSTRING, 1, "table or string expected");
int len = lua_objlen(L, 1); int len = lua_objlen(L, 1);
@ -201,7 +196,7 @@ static int luaB_gcinfo(lua_State* L)
static int luaB_type(lua_State* L) static int luaB_type(lua_State* L)
{ {
luaL_checkany(L, 1); luaL_checkany(L, 1);
/* resulting name doesn't differentiate between userdata types */ // resulting name doesn't differentiate between userdata types
lua_pushstring(L, lua_typename(L, lua_type(L, 1))); lua_pushstring(L, lua_typename(L, lua_type(L, 1)));
return 1; return 1;
} }
@ -209,7 +204,7 @@ static int luaB_type(lua_State* L)
static int luaB_typeof(lua_State* L) static int luaB_typeof(lua_State* L)
{ {
luaL_checkany(L, 1); luaL_checkany(L, 1);
/* resulting name returns __type if specified unless the input is a newproxy-created userdata */ // resulting name returns __type if specified unless the input is a newproxy-created userdata
lua_pushstring(L, luaL_typename(L, 1)); lua_pushstring(L, luaL_typename(L, 1));
return 1; return 1;
} }
@ -217,7 +212,7 @@ static int luaB_typeof(lua_State* L)
int luaB_next(lua_State* L) int luaB_next(lua_State* L)
{ {
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 2); /* create a 2nd argument if there isn't one */ lua_settop(L, 2); // create a 2nd argument if there isn't one
if (lua_next(L, 1)) if (lua_next(L, 1))
return 2; return 2;
else else
@ -230,9 +225,9 @@ int luaB_next(lua_State* L)
static int luaB_pairs(lua_State* L) static int luaB_pairs(lua_State* L)
{ {
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ lua_pushvalue(L, lua_upvalueindex(1)); // return generator,
lua_pushvalue(L, 1); /* state, */ lua_pushvalue(L, 1); // state,
lua_pushnil(L); /* and initial value */ lua_pushnil(L); // and initial value
return 3; return 3;
} }
@ -240,7 +235,7 @@ int luaB_inext(lua_State* L)
{ {
int i = luaL_checkinteger(L, 2); int i = luaL_checkinteger(L, 2);
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
i++; /* next value */ i++; // next value
lua_pushinteger(L, i); lua_pushinteger(L, i);
lua_rawgeti(L, 1, i); lua_rawgeti(L, 1, i);
return (lua_isnil(L, -1)) ? 0 : 2; return (lua_isnil(L, -1)) ? 0 : 2;
@ -249,9 +244,9 @@ int luaB_inext(lua_State* L)
static int luaB_ipairs(lua_State* L) static int luaB_ipairs(lua_State* L)
{ {
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
lua_pushvalue(L, lua_upvalueindex(1)); /* return generator, */ lua_pushvalue(L, lua_upvalueindex(1)); // return generator,
lua_pushvalue(L, 1); /* state, */ lua_pushvalue(L, 1); // state,
lua_pushinteger(L, 0); /* and initial value */ lua_pushinteger(L, 0); // and initial value
return 3; return 3;
} }
@ -340,12 +335,12 @@ static int luaB_xpcally(lua_State* L)
{ {
luaL_checktype(L, 2, LUA_TFUNCTION); luaL_checktype(L, 2, LUA_TFUNCTION);
/* swap function & error function */ // swap function & error function
lua_pushvalue(L, 1); lua_pushvalue(L, 1);
lua_pushvalue(L, 2); lua_pushvalue(L, 2);
lua_replace(L, 1); lua_replace(L, 1);
lua_replace(L, 2); lua_replace(L, 2);
/* at this point the stack looks like err, f, args */ // at this point the stack looks like err, f, args
// any errors from this point on are handled by continuation // any errors from this point on are handled by continuation
L->ci->flags |= LUA_CALLINFO_HANDLE; L->ci->flags |= LUA_CALLINFO_HANDLE;
@ -386,7 +381,7 @@ static int luaB_xpcallcont(lua_State* L, int status)
lua_rawcheckstack(L, 1); lua_rawcheckstack(L, 1);
lua_pushboolean(L, true); lua_pushboolean(L, true);
lua_replace(L, 1); // replace error function with status lua_replace(L, 1); // replace error function with status
return lua_gettop(L); /* return status + all results */ return lua_gettop(L); // return status + all results
} }
else else
{ {
@ -462,16 +457,16 @@ static void auxopen(lua_State* L, const char* name, lua_CFunction f, lua_CFuncti
int luaopen_base(lua_State* L) int luaopen_base(lua_State* L)
{ {
/* set global _G */ // set global _G
lua_pushvalue(L, LUA_GLOBALSINDEX); lua_pushvalue(L, LUA_GLOBALSINDEX);
lua_setglobal(L, "_G"); lua_setglobal(L, "_G");
/* open lib into global table */ // open lib into global table
luaL_register(L, "_G", base_funcs); luaL_register(L, "_G", base_funcs);
lua_pushliteral(L, "Luau"); lua_pushliteral(L, "Luau");
lua_setglobal(L, "_VERSION"); /* set global _VERSION */ lua_setglobal(L, "_VERSION"); // set global _VERSION
/* `ipairs' and `pairs' need auxiliary functions as upvalues */ // `ipairs' and `pairs' need auxiliary functions as upvalues
auxopen(L, "ipairs", luaB_ipairs, luaB_inext); auxopen(L, "ipairs", luaB_ipairs, luaB_inext);
auxopen(L, "pairs", luaB_pairs, luaB_next); auxopen(L, "pairs", luaB_pairs, luaB_next);

View File

@ -8,10 +8,10 @@
#define ALLONES ~0u #define ALLONES ~0u
#define NBITS int(8 * sizeof(unsigned)) #define NBITS int(8 * sizeof(unsigned))
/* macro to trim extra bits */ // macro to trim extra bits
#define trim(x) ((x)&ALLONES) #define trim(x) ((x)&ALLONES)
/* builds a number with 'n' ones (1 <= n <= NBITS) */ // builds a number with 'n' ones (1 <= n <= NBITS)
#define mask(n) (~((ALLONES << 1) << ((n)-1))) #define mask(n) (~((ALLONES << 1) << ((n)-1)))
typedef unsigned b_uint; typedef unsigned b_uint;
@ -69,7 +69,7 @@ static int b_not(lua_State* L)
static int b_shift(lua_State* L, b_uint r, int i) static int b_shift(lua_State* L, b_uint r, int i)
{ {
if (i < 0) if (i < 0)
{ /* shift right? */ { // shift right?
i = -i; i = -i;
r = trim(r); r = trim(r);
if (i >= NBITS) if (i >= NBITS)
@ -78,7 +78,7 @@ static int b_shift(lua_State* L, b_uint r, int i)
r >>= i; r >>= i;
} }
else else
{ /* shift left */ { // shift left
if (i >= NBITS) if (i >= NBITS)
r = 0; r = 0;
else else
@ -106,11 +106,11 @@ static int b_arshift(lua_State* L)
if (i < 0 || !(r & ((b_uint)1 << (NBITS - 1)))) if (i < 0 || !(r & ((b_uint)1 << (NBITS - 1))))
return b_shift(L, r, -i); return b_shift(L, r, -i);
else else
{ /* arithmetic shift for 'negative' number */ { // arithmetic shift for 'negative' number
if (i >= NBITS) if (i >= NBITS)
r = ALLONES; r = ALLONES;
else else
r = trim((r >> i) | ~(~(b_uint)0 >> i)); /* add signal bit */ r = trim((r >> i) | ~(~(b_uint)0 >> i)); // add signal bit
lua_pushunsigned(L, r); lua_pushunsigned(L, r);
return 1; return 1;
} }
@ -119,9 +119,9 @@ static int b_arshift(lua_State* L)
static int b_rot(lua_State* L, int i) static int b_rot(lua_State* L, int i)
{ {
b_uint r = luaL_checkunsigned(L, 1); b_uint r = luaL_checkunsigned(L, 1);
i &= (NBITS - 1); /* i = i % NBITS */ i &= (NBITS - 1); // i = i % NBITS
r = trim(r); r = trim(r);
if (i != 0) /* avoid undefined shift of NBITS when i == 0 */ if (i != 0) // avoid undefined shift of NBITS when i == 0
r = (r << i) | (r >> (NBITS - i)); r = (r << i) | (r >> (NBITS - i));
lua_pushunsigned(L, trim(r)); lua_pushunsigned(L, trim(r));
return 1; return 1;
@ -172,7 +172,7 @@ static int b_replace(lua_State* L)
b_uint v = luaL_checkunsigned(L, 2); b_uint v = luaL_checkunsigned(L, 2);
int f = fieldargs(L, 3, &w); int f = fieldargs(L, 3, &w);
int m = mask(w); int m = mask(w);
v &= m; /* erase bits outside given width */ v &= m; // erase bits outside given width
r = (r & ~(m << f)) | (v << f); r = (r & ~(m << f)) | (v << f);
lua_pushunsigned(L, r); lua_pushunsigned(L, r);
return 1; return 1;

View File

@ -11,7 +11,7 @@
typedef LUAI_USER_ALIGNMENT_T L_Umaxalign; typedef LUAI_USER_ALIGNMENT_T L_Umaxalign;
/* internal assertions for in-house debugging */ // internal assertions for in-house debugging
#define check_exp(c, e) (LUAU_ASSERT(c), (e)) #define check_exp(c, e) (LUAU_ASSERT(c), (e))
#define api_check(l, e) LUAU_ASSERT(e) #define api_check(l, e) LUAU_ASSERT(e)

View File

@ -5,9 +5,9 @@
#include "lstate.h" #include "lstate.h"
#include "lvm.h" #include "lvm.h"
#define CO_RUN 0 /* running */ #define CO_RUN 0 // running
#define CO_SUS 1 /* suspended */ #define CO_SUS 1 // suspended
#define CO_NOR 2 /* 'normal' (it resumed another coroutine) */ #define CO_NOR 2 // 'normal' (it resumed another coroutine)
#define CO_DEAD 3 #define CO_DEAD 3
#define CO_STATUS_ERROR -1 #define CO_STATUS_ERROR -1
@ -23,13 +23,13 @@ static int auxstatus(lua_State* L, lua_State* co)
return CO_SUS; return CO_SUS;
if (co->status == LUA_BREAK) if (co->status == LUA_BREAK)
return CO_NOR; return CO_NOR;
if (co->status != 0) /* some error occurred */ if (co->status != 0) // some error occurred
return CO_DEAD; return CO_DEAD;
if (co->ci != co->base_ci) /* does it have frames? */ if (co->ci != co->base_ci) // does it have frames?
return CO_NOR; return CO_NOR;
if (co->top == co->base) if (co->top == co->base)
return CO_DEAD; return CO_DEAD;
return CO_SUS; /* initial state */ return CO_SUS; // initial state
} }
static int costatus(lua_State* L) static int costatus(lua_State* L)
@ -68,10 +68,10 @@ static int auxresume(lua_State* L, lua_State* co, int narg)
int nres = cast_int(co->top - co->base); int nres = cast_int(co->top - co->base);
if (nres) if (nres)
{ {
/* +1 accounts for true/false status in resumefinish */ // +1 accounts for true/false status in resumefinish
if (nres + 1 > LUA_MINSTACK && !lua_checkstack(L, nres + 1)) if (nres + 1 > LUA_MINSTACK && !lua_checkstack(L, nres + 1))
luaL_error(L, "too many results to resume"); luaL_error(L, "too many results to resume");
lua_xmove(co, L, nres); /* move yielded values */ lua_xmove(co, L, nres); // move yielded values
} }
return nres; return nres;
} }
@ -81,7 +81,7 @@ static int auxresume(lua_State* L, lua_State* co, int narg)
} }
else else
{ {
lua_xmove(co, L, 1); /* move error message */ lua_xmove(co, L, 1); // move error message
return CO_STATUS_ERROR; return CO_STATUS_ERROR;
} }
} }
@ -102,13 +102,13 @@ static int auxresumecont(lua_State* L, lua_State* co)
int nres = cast_int(co->top - co->base); int nres = cast_int(co->top - co->base);
if (!lua_checkstack(L, nres + 1)) if (!lua_checkstack(L, nres + 1))
luaL_error(L, "too many results to resume"); luaL_error(L, "too many results to resume");
lua_xmove(co, L, nres); /* move yielded values */ lua_xmove(co, L, nres); // move yielded values
return nres; return nres;
} }
else else
{ {
lua_rawcheckstack(L, 2); lua_rawcheckstack(L, 2);
lua_xmove(co, L, 1); /* move error message */ lua_xmove(co, L, 1); // move error message
return CO_STATUS_ERROR; return CO_STATUS_ERROR;
} }
} }
@ -119,13 +119,13 @@ static int coresumefinish(lua_State* L, int r)
{ {
lua_pushboolean(L, 0); lua_pushboolean(L, 0);
lua_insert(L, -2); lua_insert(L, -2);
return 2; /* return false + error message */ return 2; // return false + error message
} }
else else
{ {
lua_pushboolean(L, 1); lua_pushboolean(L, 1);
lua_insert(L, -(r + 1)); lua_insert(L, -(r + 1));
return r + 1; /* return true + `resume' returns */ return r + 1; // return true + `resume' returns
} }
} }
@ -161,12 +161,12 @@ static int auxwrapfinish(lua_State* L, int r)
if (r < 0) if (r < 0)
{ {
if (lua_isstring(L, -1)) if (lua_isstring(L, -1))
{ /* error object is a string? */ { // error object is a string?
luaL_where(L, 1); /* add extra info */ luaL_where(L, 1); // add extra info
lua_insert(L, -2); lua_insert(L, -2);
lua_concat(L, 2); lua_concat(L, 2);
} }
lua_error(L); /* propagate error */ lua_error(L); // propagate error
} }
return r; return r;
} }
@ -221,7 +221,7 @@ static int coyield(lua_State* L)
static int corunning(lua_State* L) static int corunning(lua_State* L)
{ {
if (lua_pushthread(L)) if (lua_pushthread(L))
lua_pushnil(L); /* main thread is not a coroutine */ lua_pushnil(L); // main thread is not a coroutine
return 1; return 1;
} }
@ -250,7 +250,7 @@ static int coclose(lua_State* L)
{ {
lua_pushboolean(L, false); lua_pushboolean(L, false);
if (lua_gettop(co)) if (lua_gettop(co))
lua_xmove(co, L, 1); /* move error message */ lua_xmove(co, L, 1); // move error message
lua_resetthread(co); lua_resetthread(co);
return 2; return 2;
} }

View File

@ -82,9 +82,9 @@ static int db_info(lua_State* L)
case 'f': case 'f':
if (L1 == L) if (L1 == L)
lua_pushvalue(L, -1 - results); /* function is right before results */ lua_pushvalue(L, -1 - results); // function is right before results
else else
lua_xmove(L1, L, 1); /* function is at top of L1 */ lua_xmove(L1, L, 1); // function is at top of L1
results++; results++;
break; break;

View File

@ -86,7 +86,7 @@ const char* lua_setlocal(lua_State* L, int level, int n)
const LocVar* var = fp ? luaF_getlocal(fp, n, currentpc(L, ci)) : NULL; const LocVar* var = fp ? luaF_getlocal(fp, n, currentpc(L, ci)) : NULL;
if (var) if (var)
setobjs2s(L, ci->base + var->reg, L->top - 1); setobjs2s(L, ci->base + var->reg, L->top - 1);
L->top--; /* pop value */ L->top--; // pop value
const char* name = var ? getstr(var->varname) : NULL; const char* name = var ? getstr(var->varname) : NULL;
return name; return name;
} }
@ -269,6 +269,13 @@ l_noret luaG_indexerror(lua_State* L, const TValue* p1, const TValue* p2)
luaG_runerror(L, "attempt to index %s with %s", t1, t2); luaG_runerror(L, "attempt to index %s with %s", t1, t2);
} }
l_noret luaG_methoderror(lua_State* L, const TValue* p1, const TString* p2)
{
const char* t1 = luaT_objtypename(L, p1);
luaG_runerror(L, "attempt to call missing method '%s' of %s", getstr(p2), t1);
}
l_noret luaG_readonlyerror(lua_State* L) l_noret luaG_readonlyerror(lua_State* L)
{ {
luaG_runerror(L, "attempt to modify a readonly table"); luaG_runerror(L, "attempt to modify a readonly table");
@ -279,7 +286,7 @@ static void pusherror(lua_State* L, const char* msg)
CallInfo* ci = L->ci; CallInfo* ci = L->ci;
if (isLua(ci)) if (isLua(ci))
{ {
char buff[LUA_IDSIZE]; /* add file:line information */ char buff[LUA_IDSIZE]; // add file:line information
luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE); luaO_chunkid(buff, getstr(getluaproto(ci)->source), LUA_IDSIZE);
int line = currentline(L, ci); int line = currentline(L, ci);
luaO_pushfstring(L, "%s:%d: %s", buff, line, msg); luaO_pushfstring(L, "%s:%d: %s", buff, line, msg);

View File

@ -19,6 +19,7 @@ LUAI_FUNC l_noret luaG_concaterror(lua_State* L, StkId p1, StkId p2);
LUAI_FUNC l_noret luaG_aritherror(lua_State* L, const TValue* p1, const TValue* p2, TMS op); LUAI_FUNC l_noret luaG_aritherror(lua_State* L, const TValue* p1, const TValue* p2, TMS op);
LUAI_FUNC l_noret luaG_ordererror(lua_State* L, const TValue* p1, const TValue* p2, TMS op); LUAI_FUNC l_noret luaG_ordererror(lua_State* L, const TValue* p1, const TValue* p2, TMS op);
LUAI_FUNC l_noret luaG_indexerror(lua_State* L, const TValue* p1, const TValue* p2); LUAI_FUNC l_noret luaG_indexerror(lua_State* L, const TValue* p1, const TValue* p2);
LUAI_FUNC l_noret luaG_methoderror(lua_State* L, const TValue* p1, const TString* p2);
LUAI_FUNC l_noret luaG_readonlyerror(lua_State* L); LUAI_FUNC l_noret luaG_readonlyerror(lua_State* L);
LUAI_FUNC LUA_PRINTF_ATTR(2, 3) l_noret luaG_runerrorL(lua_State* L, const char* fmt, ...); LUAI_FUNC LUA_PRINTF_ATTR(2, 3) l_noret luaG_runerrorL(lua_State* L, const char* fmt, ...);

View File

@ -31,7 +31,7 @@ struct lua_jmpbuf
jmp_buf buf; jmp_buf buf;
}; };
/* use POSIX versions of setjmp/longjmp if possible: they don't save/restore signal mask and are therefore faster */ // use POSIX versions of setjmp/longjmp if possible: they don't save/restore signal mask and are therefore faster
#if defined(__linux__) || defined(__APPLE__) #if defined(__linux__) || defined(__APPLE__)
#define LUAU_SETJMP(buf) _setjmp(buf) #define LUAU_SETJMP(buf) _setjmp(buf)
#define LUAU_LONGJMP(buf, code) _longjmp(buf, code) #define LUAU_LONGJMP(buf, code) _longjmp(buf, code)
@ -153,7 +153,7 @@ l_noret luaD_throw(lua_State* L, int errcode)
} }
#endif #endif
/* }====================================================== */ // }======================================================
static void correctstack(lua_State* L, TValue* oldstack) static void correctstack(lua_State* L, TValue* oldstack)
{ {
@ -177,7 +177,7 @@ void luaD_reallocstack(lua_State* L, int newsize)
luaM_reallocarray(L, L->stack, L->stacksize, realsize, TValue, L->memcat); luaM_reallocarray(L, L->stack, L->stacksize, realsize, TValue, L->memcat);
TValue* newstack = L->stack; TValue* newstack = L->stack;
for (int i = L->stacksize; i < realsize; i++) for (int i = L->stacksize; i < realsize; i++)
setnilvalue(newstack + i); /* erase new segment */ setnilvalue(newstack + i); // erase new segment
L->stacksize = realsize; L->stacksize = realsize;
L->stack_last = newstack + newsize; L->stack_last = newstack + newsize;
correctstack(L, oldstack); correctstack(L, oldstack);
@ -194,7 +194,7 @@ void luaD_reallocCI(lua_State* L, int newsize)
void luaD_growstack(lua_State* L, int n) void luaD_growstack(lua_State* L, int n)
{ {
if (n <= L->stacksize) /* double size is enough? */ if (n <= L->stacksize) // double size is enough?
luaD_reallocstack(L, 2 * L->stacksize); luaD_reallocstack(L, 2 * L->stacksize);
else else
luaD_reallocstack(L, L->stacksize + n); luaD_reallocstack(L, L->stacksize + n);
@ -202,11 +202,11 @@ void luaD_growstack(lua_State* L, int n)
CallInfo* luaD_growCI(lua_State* L) CallInfo* luaD_growCI(lua_State* L)
{ {
/* allow extra stack space to handle stack overflow in xpcall */ // allow extra stack space to handle stack overflow in xpcall
const int hardlimit = LUAI_MAXCALLS + (LUAI_MAXCALLS >> 3); const int hardlimit = LUAI_MAXCALLS + (LUAI_MAXCALLS >> 3);
if (L->size_ci >= hardlimit) if (L->size_ci >= hardlimit)
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ luaD_throw(L, LUA_ERRERR); // error while handling stack error
int request = L->size_ci * 2; int request = L->size_ci * 2;
luaD_reallocCI(L, L->size_ci >= LUAI_MAXCALLS ? hardlimit : request < LUAI_MAXCALLS ? request : LUAI_MAXCALLS); luaD_reallocCI(L, L->size_ci >= LUAI_MAXCALLS ? hardlimit : request < LUAI_MAXCALLS ? request : LUAI_MAXCALLS);
@ -219,13 +219,13 @@ CallInfo* luaD_growCI(lua_State* L)
void luaD_checkCstack(lua_State* L) void luaD_checkCstack(lua_State* L)
{ {
/* allow extra stack space to handle stack overflow in xpcall */ // allow extra stack space to handle stack overflow in xpcall
const int hardlimit = LUAI_MAXCCALLS + (LUAI_MAXCCALLS >> 3); const int hardlimit = LUAI_MAXCCALLS + (LUAI_MAXCCALLS >> 3);
if (L->nCcalls == LUAI_MAXCCALLS) if (L->nCcalls == LUAI_MAXCCALLS)
luaG_runerror(L, "C stack overflow"); luaG_runerror(L, "C stack overflow");
else if (L->nCcalls >= hardlimit) else if (L->nCcalls >= hardlimit)
luaD_throw(L, LUA_ERRERR); /* error while handling stack error */ luaD_throw(L, LUA_ERRERR); // error while handling stack error
} }
/* /*
@ -240,14 +240,14 @@ void luaD_call(lua_State* L, StkId func, int nResults)
luaD_checkCstack(L); luaD_checkCstack(L);
if (luau_precall(L, func, nResults) == PCRLUA) if (luau_precall(L, func, nResults) == PCRLUA)
{ /* is a Lua function? */ { // is a Lua function?
L->ci->flags |= LUA_CALLINFO_RETURN; /* luau_execute will stop after returning from the stack frame */ L->ci->flags |= LUA_CALLINFO_RETURN; // luau_execute will stop after returning from the stack frame
int oldactive = luaC_threadactive(L); int oldactive = luaC_threadactive(L);
l_setbit(L->stackstate, THREAD_ACTIVEBIT); l_setbit(L->stackstate, THREAD_ACTIVEBIT);
luaC_checkthreadsleep(L); luaC_checkthreadsleep(L);
luau_execute(L); /* call it */ luau_execute(L); // call it
if (!oldactive) if (!oldactive)
resetbit(L->stackstate, THREAD_ACTIVEBIT); resetbit(L->stackstate, THREAD_ACTIVEBIT);
@ -263,18 +263,18 @@ static void seterrorobj(lua_State* L, int errcode, StkId oldtop)
{ {
case LUA_ERRMEM: case LUA_ERRMEM:
{ {
setsvalue2s(L, oldtop, luaS_newliteral(L, LUA_MEMERRMSG)); /* can not fail because string is pinned in luaopen */ setsvalue2s(L, oldtop, luaS_newliteral(L, LUA_MEMERRMSG)); // can not fail because string is pinned in luaopen
break; break;
} }
case LUA_ERRERR: case LUA_ERRERR:
{ {
setsvalue2s(L, oldtop, luaS_newliteral(L, LUA_ERRERRMSG)); /* can not fail because string is pinned in luaopen */ setsvalue2s(L, oldtop, luaS_newliteral(L, LUA_ERRERRMSG)); // can not fail because string is pinned in luaopen
break; break;
} }
case LUA_ERRSYNTAX: case LUA_ERRSYNTAX:
case LUA_ERRRUN: case LUA_ERRRUN:
{ {
setobjs2s(L, oldtop, L->top - 1); /* error message on current top */ setobjs2s(L, oldtop, L->top - 1); // error message on current top
break; break;
} }
} }
@ -430,8 +430,8 @@ static void resume_finish(lua_State* L, int status)
resetbit(L->stackstate, THREAD_ACTIVEBIT); resetbit(L->stackstate, THREAD_ACTIVEBIT);
if (status != 0) if (status != 0)
{ /* error? */ { // error?
L->status = cast_byte(status); /* mark thread as `dead' */ L->status = cast_byte(status); // mark thread as `dead'
seterrorobj(L, status, L->top); seterrorobj(L, status, L->top);
L->ci->top = L->top; L->ci->top = L->top;
} }
@ -503,7 +503,7 @@ int lua_yield(lua_State* L, int nresults)
{ {
if (L->nCcalls > L->baseCcalls) if (L->nCcalls > L->baseCcalls)
luaG_runerror(L, "attempt to yield across metamethod/C-call boundary"); luaG_runerror(L, "attempt to yield across metamethod/C-call boundary");
L->base = L->top - nresults; /* protect stack slots below */ L->base = L->top - nresults; // protect stack slots below
L->status = LUA_YIELD; L->status = LUA_YIELD;
return -1; return -1;
} }
@ -535,9 +535,9 @@ static void restore_stack_limit(lua_State* L)
{ {
LUAU_ASSERT(L->stack_last - L->stack == L->stacksize - EXTRA_STACK); LUAU_ASSERT(L->stack_last - L->stack == L->stacksize - EXTRA_STACK);
if (L->size_ci > LUAI_MAXCALLS) if (L->size_ci > LUAI_MAXCALLS)
{ /* there was an overflow? */ { // there was an overflow?
int inuse = cast_int(L->ci - L->base_ci); int inuse = cast_int(L->ci - L->base_ci);
if (inuse + 1 < LUAI_MAXCALLS) /* can `undo' overflow? */ if (inuse + 1 < LUAI_MAXCALLS) // can `undo' overflow?
luaD_reallocCI(L, LUAI_MAXCALLS); luaD_reallocCI(L, LUAI_MAXCALLS);
} }
} }
@ -576,7 +576,7 @@ int luaD_pcall(lua_State* L, Pfunc func, void* u, ptrdiff_t old_top, ptrdiff_t e
} }
StkId oldtop = restorestack(L, old_top); StkId oldtop = restorestack(L, old_top);
luaF_close(L, oldtop); /* close eventual pending closures */ luaF_close(L, oldtop); // close eventual pending closures
seterrorobj(L, status, oldtop); seterrorobj(L, status, oldtop);
L->ci = restoreci(L, old_ci); L->ci = restoreci(L, old_ci);
L->base = L->ci->base; L->base = L->ci->base;

View File

@ -34,12 +34,12 @@
#define saveci(L, p) ((char*)(p) - (char*)L->base_ci) #define saveci(L, p) ((char*)(p) - (char*)L->base_ci)
#define restoreci(L, n) ((CallInfo*)((char*)L->base_ci + (n))) #define restoreci(L, n) ((CallInfo*)((char*)L->base_ci + (n)))
/* results from luaD_precall */ // results from luaD_precall
#define PCRLUA 0 /* initiated a call to a Lua function */ #define PCRLUA 0 // initiated a call to a Lua function
#define PCRC 1 /* did a call to a C function */ #define PCRC 1 // did a call to a C function
#define PCRYIELD 2 /* C function yielded */ #define PCRYIELD 2 // C function yielded
/* type of protected functions, to be ran by `runprotected' */ // type of protected functions, to be ran by `runprotected'
typedef void (*Pfunc)(lua_State* L, void* ud); typedef void (*Pfunc)(lua_State* L, void* ud);
LUAI_FUNC CallInfo* luaD_growCI(lua_State* L); LUAI_FUNC CallInfo* luaD_growCI(lua_State* L);

View File

@ -73,20 +73,20 @@ UpVal* luaF_findupval(lua_State* L, StkId level)
{ {
LUAU_ASSERT(p->v != &p->u.value); LUAU_ASSERT(p->v != &p->u.value);
if (p->v == level) if (p->v == level)
{ /* found a corresponding upvalue? */ { // found a corresponding upvalue?
if (isdead(g, obj2gco(p))) /* is it dead? */ if (isdead(g, obj2gco(p))) // is it dead?
changewhite(obj2gco(p)); /* resurrect it */ changewhite(obj2gco(p)); // resurrect it
return p; return p;
} }
pp = &p->u.l.threadnext; pp = &p->u.l.threadnext;
} }
UpVal* uv = luaM_newgco(L, UpVal, sizeof(UpVal), L->activememcat); /* not found: create a new one */ UpVal* uv = luaM_newgco(L, UpVal, sizeof(UpVal), L->activememcat); // not found: create a new one
uv->tt = LUA_TUPVAL; uv->tt = LUA_TUPVAL;
uv->marked = luaC_white(g); uv->marked = luaC_white(g);
uv->memcat = L->activememcat; uv->memcat = L->activememcat;
uv->v = level; /* current value lives in the stack */ uv->v = level; // current value lives in the stack
// chain the upvalue in the threads open upvalue list at the proper position // chain the upvalue in the threads open upvalue list at the proper position
UpVal* next = *pp; UpVal* next = *pp;
@ -121,9 +121,9 @@ void luaF_unlinkupval(UpVal* uv)
void luaF_freeupval(lua_State* L, UpVal* uv, lua_Page* page) void luaF_freeupval(lua_State* L, UpVal* uv, lua_Page* page)
{ {
if (uv->v != &uv->u.value) /* is it open? */ if (uv->v != &uv->u.value) // is it open?
luaF_unlinkupval(uv); /* remove from open list */ luaF_unlinkupval(uv); // remove from open list
luaM_freegco(L, uv, sizeof(UpVal), uv->memcat, page); /* free upvalue */ luaM_freegco(L, uv, sizeof(UpVal), uv->memcat, page); // free upvalue
} }
void luaF_close(lua_State* L, StkId level) void luaF_close(lua_State* L, StkId level)
@ -179,11 +179,11 @@ const LocVar* luaF_getlocal(const Proto* f, int local_number, int pc)
for (i = 0; i < f->sizelocvars; i++) for (i = 0; i < f->sizelocvars; i++)
{ {
if (pc >= f->locvars[i].startpc && pc < f->locvars[i].endpc) if (pc >= f->locvars[i].startpc && pc < f->locvars[i].endpc)
{ /* is variable active? */ { // is variable active?
local_number--; local_number--;
if (local_number == 0) if (local_number == 0)
return &f->locvars[i]; return &f->locvars[i];
} }
} }
return NULL; /* not found */ return NULL; // not found
} }

View File

@ -125,7 +125,7 @@ static void removeentry(LuaNode* n)
{ {
LUAU_ASSERT(ttisnil(gval(n))); LUAU_ASSERT(ttisnil(gval(n)));
if (iscollectable(gkey(n))) if (iscollectable(gkey(n)))
setttype(gkey(n), LUA_TDEADKEY); /* dead key; remove it */ setttype(gkey(n), LUA_TDEADKEY); // dead key; remove it
} }
static void reallymarkobject(global_State* g, GCObject* o) static void reallymarkobject(global_State* g, GCObject* o)
@ -141,7 +141,7 @@ static void reallymarkobject(global_State* g, GCObject* o)
case LUA_TUSERDATA: case LUA_TUSERDATA:
{ {
Table* mt = gco2u(o)->metatable; Table* 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);
return; return;
@ -150,8 +150,8 @@ static void reallymarkobject(global_State* g, GCObject* o)
{ {
UpVal* uv = gco2uv(o); UpVal* uv = gco2uv(o);
markvalue(g, uv->v); markvalue(g, uv->v);
if (uv->v == &uv->u.value) /* closed? */ if (uv->v == &uv->u.value) // closed?
gray2black(o); /* open upvalues are never black */ gray2black(o); // open upvalues are never black
return; return;
} }
case LUA_TFUNCTION: case LUA_TFUNCTION:
@ -201,15 +201,15 @@ static int traversetable(global_State* g, Table* h)
if (h->metatable) if (h->metatable)
markobject(g, cast_to(Table*, h->metatable)); markobject(g, cast_to(Table*, 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))
{ {
weakkey = (strchr(modev, 'k') != NULL); weakkey = (strchr(modev, 'k') != NULL);
weakvalue = (strchr(modev, 'v') != NULL); weakvalue = (strchr(modev, 'v') != NULL);
if (weakkey || weakvalue) if (weakkey || weakvalue)
{ /* is really weak? */ { // is really weak?
h->gclist = g->weak; /* must be cleared after GC, ... */ h->gclist = g->weak; // must be cleared after GC, ...
g->weak = obj2gco(h); /* ... so put in the appropriate list */ g->weak = obj2gco(h); // ... so put in the appropriate list
} }
} }
@ -227,7 +227,7 @@ static int traversetable(global_State* g, Table* h)
LuaNode* n = gnode(h, i); LuaNode* n = gnode(h, i);
LUAU_ASSERT(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n))); LUAU_ASSERT(ttype(gkey(n)) != LUA_TDEADKEY || ttisnil(gval(n)));
if (ttisnil(gval(n))) if (ttisnil(gval(n)))
removeentry(n); /* remove empty entries */ removeentry(n); // remove empty entries
else else
{ {
LUAU_ASSERT(!ttisnil(gkey(n))); LUAU_ASSERT(!ttisnil(gkey(n)));
@ -251,20 +251,20 @@ static void traverseproto(global_State* g, Proto* f)
stringmark(f->source); stringmark(f->source);
if (f->debugname) if (f->debugname)
stringmark(f->debugname); stringmark(f->debugname);
for (i = 0; i < f->sizek; i++) /* mark literals */ for (i = 0; i < f->sizek; i++) // mark literals
markvalue(g, &f->k[i]); markvalue(g, &f->k[i]);
for (i = 0; i < f->sizeupvalues; i++) for (i = 0; i < f->sizeupvalues; i++)
{ /* mark upvalue names */ { // mark upvalue names
if (f->upvalues[i]) if (f->upvalues[i])
stringmark(f->upvalues[i]); stringmark(f->upvalues[i]);
} }
for (i = 0; i < f->sizep; i++) for (i = 0; i < f->sizep; i++)
{ /* mark nested protos */ { // mark nested protos
if (f->p[i]) if (f->p[i])
markobject(g, f->p[i]); markobject(g, f->p[i]);
} }
for (i = 0; i < f->sizelocvars; i++) for (i = 0; i < f->sizelocvars; i++)
{ /* mark local-variable names */ { // mark local-variable names
if (f->locvars[i].varname) if (f->locvars[i].varname)
stringmark(f->locvars[i].varname); stringmark(f->locvars[i].varname);
} }
@ -276,7 +276,7 @@ static void traverseclosure(global_State* g, Closure* cl)
if (cl->isC) if (cl->isC)
{ {
int i; int i;
for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ for (i = 0; i < cl->nupvalues; i++) // mark its upvalues
markvalue(g, &cl->c.upvals[i]); markvalue(g, &cl->c.upvals[i]);
} }
else else
@ -284,7 +284,7 @@ static void traverseclosure(global_State* g, Closure* cl)
int i; int i;
LUAU_ASSERT(cl->nupvalues == cl->l.p->nups); LUAU_ASSERT(cl->nupvalues == cl->l.p->nups);
markobject(g, cast_to(Proto*, cl->l.p)); markobject(g, cast_to(Proto*, cl->l.p));
for (i = 0; i < cl->nupvalues; i++) /* mark its upvalues */ for (i = 0; i < cl->nupvalues; i++) // mark its upvalues
markvalue(g, &cl->l.uprefs[i]); markvalue(g, &cl->l.uprefs[i]);
} }
} }
@ -296,11 +296,11 @@ static void traversestack(global_State* g, lua_State* l, bool clearstack)
stringmark(l->namecall); stringmark(l->namecall);
for (StkId o = l->stack; o < l->top; o++) for (StkId o = l->stack; o < l->top; o++)
markvalue(g, o); markvalue(g, o);
/* final traversal? */ // final traversal?
if (g->gcstate == GCSatomic || clearstack) if (g->gcstate == GCSatomic || clearstack)
{ {
StkId stack_end = l->stack + l->stacksize; StkId stack_end = l->stack + l->stacksize;
for (StkId o = l->top; o < stack_end; o++) /* clear not-marked stack slice */ for (StkId o = l->top; o < stack_end; o++) // clear not-marked stack slice
setnilvalue(o); setnilvalue(o);
} }
} }
@ -320,8 +320,8 @@ static size_t propagatemark(global_State* g)
{ {
Table* h = gco2h(o); Table* 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(Table) + sizeof(TValue) * h->sizearray + sizeof(LuaNode) * sizenode(h);
} }
case LUA_TFUNCTION: case LUA_TFUNCTION:
@ -393,7 +393,7 @@ static int isobjcleared(GCObject* o)
{ {
if (o->gch.tt == LUA_TSTRING) if (o->gch.tt == LUA_TSTRING)
{ {
stringmark(&o->ts); /* strings are `values', so are never weak */ stringmark(&o->ts); // strings are `values', so are never weak
return 0; return 0;
} }
@ -417,8 +417,8 @@ static size_t cleartable(lua_State* L, GCObject* l)
while (i--) while (i--)
{ {
TValue* o = &h->array[i]; TValue* o = &h->array[i];
if (iscleared(o)) /* value was collected? */ if (iscleared(o)) // value was collected?
setnilvalue(o); /* remove value */ setnilvalue(o); // remove value
} }
i = sizenode(h); i = sizenode(h);
int activevalues = 0; int activevalues = 0;
@ -432,8 +432,8 @@ static size_t cleartable(lua_State* L, GCObject* l)
// can we clear key or value? // can we clear key or value?
if (iscleared(gkey(n)) || iscleared(gval(n))) if (iscleared(gkey(n)) || iscleared(gval(n)))
{ {
setnilvalue(gval(n)); /* remove value ... */ setnilvalue(gval(n)); // remove value ...
removeentry(n); /* remove entry from table */ removeentry(n); // remove entry from table
} }
else else
{ {
@ -460,7 +460,7 @@ static size_t cleartable(lua_State* L, GCObject* l)
static void shrinkstack(lua_State* L) static void shrinkstack(lua_State* L)
{ {
/* compute used stack - note that we can't use th->top if we're in the middle of vararg call */ // compute used stack - note that we can't use th->top if we're in the middle of vararg call
StkId lim = L->top; StkId lim = L->top;
for (CallInfo* ci = L->base_ci; ci <= L->ci; ci++) for (CallInfo* ci = L->base_ci; ci <= L->ci; ci++)
{ {
@ -469,16 +469,16 @@ static void shrinkstack(lua_State* L)
lim = ci->top; lim = ci->top;
} }
/* shrink stack and callinfo arrays if we aren't using most of the space */ // shrink stack and callinfo arrays if we aren't using most of the space
int ci_used = cast_int(L->ci - L->base_ci); /* number of `ci' in use */ int ci_used = cast_int(L->ci - L->base_ci); // number of `ci' in use
int s_used = cast_int(lim - L->stack); /* part of stack in use */ int s_used = cast_int(lim - L->stack); // part of stack in use
if (L->size_ci > LUAI_MAXCALLS) /* handling overflow? */ if (L->size_ci > LUAI_MAXCALLS) // handling overflow?
return; /* do not touch the stacks */ return; // do not touch the stacks
if (3 * ci_used < L->size_ci && 2 * BASIC_CI_SIZE < L->size_ci) if (3 * ci_used < L->size_ci && 2 * BASIC_CI_SIZE < L->size_ci)
luaD_reallocCI(L, L->size_ci / 2); /* still big enough... */ luaD_reallocCI(L, L->size_ci / 2); // still big enough...
condhardstacktests(luaD_reallocCI(L, ci_used + 1)); condhardstacktests(luaD_reallocCI(L, ci_used + 1));
if (3 * s_used < L->stacksize && 2 * (BASIC_STACK_SIZE + EXTRA_STACK) < L->stacksize) if (3 * s_used < L->stacksize && 2 * (BASIC_STACK_SIZE + EXTRA_STACK) < L->stacksize)
luaD_reallocstack(L, L->stacksize / 2); /* still big enough... */ luaD_reallocstack(L, L->stacksize / 2); // still big enough...
condhardstacktests(luaD_reallocstack(L, s_used)); condhardstacktests(luaD_reallocstack(L, s_used));
} }
@ -516,20 +516,20 @@ static void freeobj(lua_State* L, GCObject* o, lua_Page* page)
static void shrinkbuffers(lua_State* L) static void shrinkbuffers(lua_State* L)
{ {
global_State* g = L->global; global_State* g = L->global;
/* check size of string hash */ // check size of string hash
if (g->strt.nuse < cast_to(uint32_t, g->strt.size / 4) && g->strt.size > LUA_MINSTRTABSIZE * 2) if (g->strt.nuse < cast_to(uint32_t, g->strt.size / 4) && g->strt.size > LUA_MINSTRTABSIZE * 2)
luaS_resize(L, g->strt.size / 2); /* table is too big */ luaS_resize(L, g->strt.size / 2); // table is too big
} }
static void shrinkbuffersfull(lua_State* L) static void shrinkbuffersfull(lua_State* L)
{ {
global_State* g = L->global; global_State* g = L->global;
/* check size of string hash */ // check size of string hash
int hashsize = g->strt.size; int hashsize = g->strt.size;
while (g->strt.nuse < cast_to(uint32_t, hashsize / 4) && hashsize > LUA_MINSTRTABSIZE * 2) while (g->strt.nuse < cast_to(uint32_t, hashsize / 4) && hashsize > LUA_MINSTRTABSIZE * 2)
hashsize /= 2; hashsize /= 2;
if (hashsize != g->strt.size) if (hashsize != g->strt.size)
luaS_resize(L, hashsize); /* table is too big */ luaS_resize(L, hashsize); // table is too big
} }
static bool deletegco(void* context, lua_Page* page, GCObject* gco) static bool deletegco(void* context, lua_Page* page, GCObject* gco)
@ -562,7 +562,7 @@ void luaC_freeall(lua_State* L)
luaM_visitgco(L, L, deletegco); luaM_visitgco(L, L, deletegco);
for (int i = 0; i < g->strt.size; i++) /* free all string lists */ for (int i = 0; i < g->strt.size; i++) // free all string lists
LUAU_ASSERT(g->strt.hash[i] == NULL); LUAU_ASSERT(g->strt.hash[i] == NULL);
LUAU_ASSERT(L->global->strt.nuse == 0); LUAU_ASSERT(L->global->strt.nuse == 0);
@ -577,7 +577,7 @@ static void markmt(global_State* g)
markobject(g, g->mt[i]); markobject(g, g->mt[i]);
} }
/* mark root set */ // mark root set
static void markroot(lua_State* L) static void markroot(lua_State* L)
{ {
global_State* g = L->global; global_State* g = L->global;
@ -585,7 +585,7 @@ static void markroot(lua_State* L)
g->grayagain = NULL; g->grayagain = NULL;
g->weak = NULL; g->weak = NULL;
markobject(g, g->mainthread); markobject(g, g->mainthread);
/* make global table be traversed before main stack */ // make global table be traversed before main stack
markobject(g, g->mainthread->gt); markobject(g, g->mainthread->gt);
markvalue(g, registry(L)); markvalue(g, registry(L));
markmt(g); markmt(g);
@ -616,28 +616,28 @@ static size_t atomic(lua_State* L)
double currts = lua_clock(); double currts = lua_clock();
#endif #endif
/* remark occasional upvalues of (maybe) dead threads */ // remark occasional upvalues of (maybe) dead threads
work += remarkupvals(g); work += remarkupvals(g);
/* traverse objects caught by write barrier and by 'remarkupvals' */ // traverse objects caught by write barrier and by 'remarkupvals'
work += propagateall(g); work += propagateall(g);
#ifdef LUAI_GCMETRICS #ifdef LUAI_GCMETRICS
g->gcmetrics.currcycle.atomictimeupval += recordGcDeltaTime(currts); g->gcmetrics.currcycle.atomictimeupval += recordGcDeltaTime(currts);
#endif #endif
/* remark weak tables */ // remark weak tables
g->gray = g->weak; g->gray = g->weak;
g->weak = NULL; g->weak = NULL;
LUAU_ASSERT(!iswhite(obj2gco(g->mainthread))); LUAU_ASSERT(!iswhite(obj2gco(g->mainthread)));
markobject(g, L); /* mark running thread */ markobject(g, L); // mark running thread
markmt(g); /* mark basic metatables (again) */ markmt(g); // mark basic metatables (again)
work += propagateall(g); work += propagateall(g);
#ifdef LUAI_GCMETRICS #ifdef LUAI_GCMETRICS
g->gcmetrics.currcycle.atomictimeweak += recordGcDeltaTime(currts); g->gcmetrics.currcycle.atomictimeweak += recordGcDeltaTime(currts);
#endif #endif
/* remark gray again */ // remark gray again
g->gray = g->grayagain; g->gray = g->grayagain;
g->grayagain = NULL; g->grayagain = NULL;
work += propagateall(g); work += propagateall(g);
@ -646,7 +646,7 @@ static size_t atomic(lua_State* L)
g->gcmetrics.currcycle.atomictimegray += recordGcDeltaTime(currts); g->gcmetrics.currcycle.atomictimegray += recordGcDeltaTime(currts);
#endif #endif
/* remove collected objects from weak tables */ // remove collected objects from weak tables
work += cleartable(L, g->weak); work += cleartable(L, g->weak);
g->weak = NULL; g->weak = NULL;
@ -654,7 +654,7 @@ static size_t atomic(lua_State* L)
g->gcmetrics.currcycle.atomictimeclear += recordGcDeltaTime(currts); g->gcmetrics.currcycle.atomictimeclear += recordGcDeltaTime(currts);
#endif #endif
/* flip current white */ // flip current white
g->currentwhite = cast_byte(otherwhite(g)); g->currentwhite = cast_byte(otherwhite(g));
g->sweepgcopage = g->allgcopages; g->sweepgcopage = g->allgcopages;
g->gcstate = GCSsweep; g->gcstate = GCSsweep;
@ -733,7 +733,7 @@ static size_t gcstep(lua_State* L, size_t limit)
{ {
case GCSpause: case GCSpause:
{ {
markroot(L); /* start a new collection */ markroot(L); // start a new collection
LUAU_ASSERT(g->gcstate == GCSpropagate); LUAU_ASSERT(g->gcstate == GCSpropagate);
break; break;
} }
@ -765,7 +765,7 @@ static size_t gcstep(lua_State* L, size_t limit)
cost += propagatemark(g); cost += propagatemark(g);
} }
if (!g->gray) /* no more `gray' objects */ if (!g->gray) // no more `gray' objects
{ {
#ifdef LUAI_GCMETRICS #ifdef LUAI_GCMETRICS
g->gcmetrics.currcycle.propagateagainwork = g->gcmetrics.currcycle.propagateagainwork =
@ -786,7 +786,7 @@ static size_t gcstep(lua_State* L, size_t limit)
g->gcstats.atomicstarttimestamp = lua_clock(); g->gcstats.atomicstarttimestamp = lua_clock();
g->gcstats.atomicstarttotalsizebytes = g->totalbytes; g->gcstats.atomicstarttotalsizebytes = g->totalbytes;
cost = atomic(L); /* finish mark phase */ cost = atomic(L); // finish mark phase
LUAU_ASSERT(g->gcstate == GCSsweep); LUAU_ASSERT(g->gcstate == GCSsweep);
break; break;
@ -810,7 +810,7 @@ static size_t gcstep(lua_State* L, size_t limit)
sweepgco(L, NULL, obj2gco(g->mainthread)); sweepgco(L, NULL, obj2gco(g->mainthread));
shrinkbuffers(L); shrinkbuffers(L);
g->gcstate = GCSpause; /* end collection */ g->gcstate = GCSpause; // end collection
} }
break; break;
} }
@ -878,7 +878,7 @@ size_t luaC_step(lua_State* L, bool assist)
{ {
global_State* g = L->global; global_State* g = L->global;
int lim = g->gcstepsize * g->gcstepmul / 100; /* how much to work */ int lim = g->gcstepsize * g->gcstepmul / 100; // how much to work
LUAU_ASSERT(g->totalbytes >= g->GCthreshold); LUAU_ASSERT(g->totalbytes >= g->GCthreshold);
size_t debt = g->totalbytes - g->GCthreshold; size_t debt = g->totalbytes - g->GCthreshold;
@ -947,16 +947,16 @@ void luaC_fullgc(lua_State* L)
if (g->gcstate <= GCSatomic) if (g->gcstate <= GCSatomic)
{ {
/* reset sweep marks to sweep all elements (returning them to white) */ // reset sweep marks to sweep all elements (returning them to white)
g->sweepgcopage = g->allgcopages; g->sweepgcopage = g->allgcopages;
/* reset other collector lists */ // reset other collector lists
g->gray = NULL; g->gray = NULL;
g->grayagain = NULL; g->grayagain = NULL;
g->weak = NULL; g->weak = NULL;
g->gcstate = GCSsweep; g->gcstate = GCSsweep;
} }
LUAU_ASSERT(g->gcstate == GCSsweep); LUAU_ASSERT(g->gcstate == GCSsweep);
/* finish any pending sweep phase */ // finish any pending sweep phase
while (g->gcstate != GCSpause) while (g->gcstate != GCSpause)
{ {
LUAU_ASSERT(g->gcstate == GCSsweep); LUAU_ASSERT(g->gcstate == GCSsweep);
@ -968,13 +968,13 @@ void luaC_fullgc(lua_State* L)
startGcCycleMetrics(g); startGcCycleMetrics(g);
#endif #endif
/* run a full collection cycle */ // run a full collection cycle
markroot(L); markroot(L);
while (g->gcstate != GCSpause) while (g->gcstate != GCSpause)
{ {
gcstep(L, SIZE_MAX); gcstep(L, SIZE_MAX);
} }
/* reclaim as much buffer memory as possible (shrinkbuffers() called during sweep is incremental) */ // reclaim as much buffer memory as possible (shrinkbuffers() called during sweep is incremental)
shrinkbuffersfull(L); shrinkbuffersfull(L);
size_t heapgoalsizebytes = (g->totalbytes / 100) * g->gcgoal; size_t heapgoalsizebytes = (g->totalbytes / 100) * g->gcgoal;
@ -1011,11 +1011,11 @@ void luaC_barrierf(lua_State* L, GCObject* o, GCObject* v)
global_State* g = L->global; global_State* g = L->global;
LUAU_ASSERT(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o)); LUAU_ASSERT(isblack(o) && iswhite(v) && !isdead(g, v) && !isdead(g, o));
LUAU_ASSERT(g->gcstate != GCSpause); LUAU_ASSERT(g->gcstate != GCSpause);
/* must keep invariant? */ // must keep invariant?
if (keepinvariant(g)) if (keepinvariant(g))
reallymarkobject(g, v); /* restore invariant */ reallymarkobject(g, v); // restore invariant
else /* don't mind */ else // don't mind
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, Table* t, GCObject* v)
@ -1033,7 +1033,7 @@ void luaC_barriertable(lua_State* L, Table* t, GCObject* v)
LUAU_ASSERT(isblack(o) && !isdead(g, o)); LUAU_ASSERT(isblack(o) && !isdead(g, o));
LUAU_ASSERT(g->gcstate != GCSpause); LUAU_ASSERT(g->gcstate != GCSpause);
black2gray(o); /* make table gray (again) */ black2gray(o); // make table gray (again)
t->gclist = g->grayagain; t->gclist = g->grayagain;
g->grayagain = o; g->grayagain = o;
} }
@ -1044,7 +1044,7 @@ void luaC_barrierback(lua_State* L, Table* t)
GCObject* o = obj2gco(t); GCObject* o = obj2gco(t);
LUAU_ASSERT(isblack(o) && !isdead(g, o)); LUAU_ASSERT(isblack(o) && !isdead(g, o));
LUAU_ASSERT(g->gcstate != GCSpause); LUAU_ASSERT(g->gcstate != GCSpause);
black2gray(o); /* make table gray (again) */ black2gray(o); // make table gray (again)
t->gclist = g->grayagain; t->gclist = g->grayagain;
g->grayagain = o; g->grayagain = o;
} }
@ -1066,11 +1066,11 @@ void luaC_initupval(lua_State* L, UpVal* uv)
{ {
if (keepinvariant(g)) if (keepinvariant(g))
{ {
gray2black(o); /* closed upvalues need barrier */ gray2black(o); // closed upvalues need barrier
luaC_barrier(L, uv, uv->v); luaC_barrier(L, uv, uv->v);
} }
else else
{ /* sweep phase: sweep it (turning it into white) */ { // sweep phase: sweep it (turning it into white)
makewhite(g, o); makewhite(g, o);
LUAU_ASSERT(g->gcstate != GCSpause); LUAU_ASSERT(g->gcstate != GCSpause);
} }

View File

@ -9,9 +9,9 @@
/* /*
** Default settings for GC tunables (settable via lua_gc) ** Default settings for GC tunables (settable via lua_gc)
*/ */
#define LUAI_GCGOAL 200 /* 200% (allow heap to double compared to live heap size) */ #define LUAI_GCGOAL 200 // 200% (allow heap to double compared to live heap size)
#define LUAI_GCSTEPMUL 200 /* GC runs 'twice the speed' of memory allocation */ #define LUAI_GCSTEPMUL 200 // GC runs 'twice the speed' of memory allocation
#define LUAI_GCSTEPSIZE 1 /* GC runs every KB of memory allocation */ #define LUAI_GCSTEPSIZE 1 // GC runs every KB of memory allocation
/* /*
** Possible states of the Garbage Collector ** Possible states of the Garbage Collector

View File

@ -19,7 +19,7 @@ static void validateobjref(global_State* g, GCObject* f, GCObject* t)
if (keepinvariant(g)) if (keepinvariant(g))
{ {
/* basic incremental invariant: black can't point to white */ // basic incremental invariant: black can't point to white
LUAU_ASSERT(!(isblack(f) && iswhite(t))); LUAU_ASSERT(!(isblack(f) && iswhite(t)));
} }
} }
@ -135,7 +135,7 @@ static void validateproto(global_State* g, Proto* f)
static void validateobj(global_State* g, GCObject* o) static void validateobj(global_State* g, GCObject* o)
{ {
/* dead objects can only occur during sweep */ // dead objects can only occur during sweep
if (isdead(g, o)) if (isdead(g, o))
{ {
LUAU_ASSERT(g->gcstate == GCSsweep); LUAU_ASSERT(g->gcstate == GCSsweep);

View File

@ -195,7 +195,7 @@ static int math_ldexp(lua_State* L)
static int math_min(lua_State* L) static int math_min(lua_State* L)
{ {
int n = lua_gettop(L); /* number of arguments */ int n = lua_gettop(L); // number of arguments
double dmin = luaL_checknumber(L, 1); double dmin = luaL_checknumber(L, 1);
int i; int i;
for (i = 2; i <= n; i++) for (i = 2; i <= n; i++)
@ -210,7 +210,7 @@ static int math_min(lua_State* L)
static int math_max(lua_State* L) static int math_max(lua_State* L)
{ {
int n = lua_gettop(L); /* number of arguments */ int n = lua_gettop(L); // number of arguments
double dmax = luaL_checknumber(L, 1); double dmax = luaL_checknumber(L, 1);
int i; int i;
for (i = 2; i <= n; i++) for (i = 2; i <= n; i++)
@ -227,29 +227,29 @@ static int math_random(lua_State* L)
{ {
global_State* g = L->global; global_State* g = L->global;
switch (lua_gettop(L)) switch (lua_gettop(L))
{ /* check number of arguments */ { // check number of arguments
case 0: case 0:
{ /* no arguments */ { // no arguments
// Using ldexp instead of division for speed & clarity. // Using ldexp instead of division for speed & clarity.
// See http://mumble.net/~campbell/tmp/random_real.c for details on generating doubles from integer ranges. // See http://mumble.net/~campbell/tmp/random_real.c for details on generating doubles from integer ranges.
uint32_t rl = pcg32_random(&g->rngstate); uint32_t rl = pcg32_random(&g->rngstate);
uint32_t rh = pcg32_random(&g->rngstate); uint32_t rh = pcg32_random(&g->rngstate);
double rd = ldexp(double(rl | (uint64_t(rh) << 32)), -64); double rd = ldexp(double(rl | (uint64_t(rh) << 32)), -64);
lua_pushnumber(L, rd); /* number between 0 and 1 */ lua_pushnumber(L, rd); // number between 0 and 1
break; break;
} }
case 1: case 1:
{ /* only upper limit */ { // only upper limit
int u = luaL_checkinteger(L, 1); int u = luaL_checkinteger(L, 1);
luaL_argcheck(L, 1 <= u, 1, "interval is empty"); luaL_argcheck(L, 1 <= u, 1, "interval is empty");
uint64_t x = uint64_t(u) * pcg32_random(&g->rngstate); uint64_t x = uint64_t(u) * pcg32_random(&g->rngstate);
int r = int(1 + (x >> 32)); int r = int(1 + (x >> 32));
lua_pushinteger(L, r); /* int between 1 and `u' */ lua_pushinteger(L, r); // int between 1 and `u'
break; break;
} }
case 2: case 2:
{ /* lower and upper limits */ { // lower and upper limits
int l = luaL_checkinteger(L, 1); int l = luaL_checkinteger(L, 1);
int u = luaL_checkinteger(L, 2); int u = luaL_checkinteger(L, 2);
luaL_argcheck(L, l <= u, 2, "interval is empty"); luaL_argcheck(L, l <= u, 2, "interval is empty");
@ -258,7 +258,7 @@ static int math_random(lua_State* L)
luaL_argcheck(L, ul < UINT_MAX, 2, "interval is too large"); // -INT_MIN..INT_MAX interval can result in integer overflow luaL_argcheck(L, ul < UINT_MAX, 2, "interval is too large"); // -INT_MIN..INT_MAX interval can result in integer overflow
uint64_t x = uint64_t(ul + 1) * pcg32_random(&g->rngstate); uint64_t x = uint64_t(ul + 1) * pcg32_random(&g->rngstate);
int r = int(l + (x >> 32)); int r = int(l + (x >> 32));
lua_pushinteger(L, r); /* int between `l' and `u' */ lua_pushinteger(L, r); // int between `l' and `u'
break; break;
} }
default: default:

View File

@ -42,7 +42,7 @@ LUAU_FASTMATH_END
#define luai_num2int(i, d) ((i) = (int)(d)) #define luai_num2int(i, d) ((i) = (int)(d))
/* On MSVC in 32-bit, double to unsigned cast compiles into a call to __dtoui3, so we invoke x87->int64 conversion path manually */ // On MSVC in 32-bit, double to unsigned cast compiles into a call to __dtoui3, so we invoke x87->int64 conversion path manually
#if defined(_MSC_VER) && defined(_M_IX86) #if defined(_MSC_VER) && defined(_M_IX86)
#define luai_num2unsigned(i, n) \ #define luai_num2unsigned(i, n) \
{ \ { \

View File

@ -48,7 +48,7 @@ int luaO_rawequalObj(const TValue* t1, const TValue* t2)
case LUA_TVECTOR: case LUA_TVECTOR:
return luai_veceq(vvalue(t1), vvalue(t2)); return luai_veceq(vvalue(t1), vvalue(t2));
case LUA_TBOOLEAN: case LUA_TBOOLEAN:
return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */ return bvalue(t1) == bvalue(t2); // boolean true must be 1 !!
case LUA_TLIGHTUSERDATA: case LUA_TLIGHTUSERDATA:
return pvalue(t1) == pvalue(t2); return pvalue(t1) == pvalue(t2);
default: default:
@ -71,7 +71,7 @@ int luaO_rawequalKey(const TKey* t1, const TValue* t2)
case LUA_TVECTOR: case LUA_TVECTOR:
return luai_veceq(vvalue(t1), vvalue(t2)); return luai_veceq(vvalue(t1), vvalue(t2));
case LUA_TBOOLEAN: case LUA_TBOOLEAN:
return bvalue(t1) == bvalue(t2); /* boolean true must be 1 !! */ return bvalue(t1) == bvalue(t2); // boolean true must be 1 !!
case LUA_TLIGHTUSERDATA: case LUA_TLIGHTUSERDATA:
return pvalue(t1) == pvalue(t2); return pvalue(t1) == pvalue(t2);
default: default:
@ -85,15 +85,15 @@ int luaO_str2d(const char* s, double* result)
char* endptr; char* endptr;
*result = luai_str2num(s, &endptr); *result = luai_str2num(s, &endptr);
if (endptr == s) if (endptr == s)
return 0; /* conversion failed */ return 0; // conversion failed
if (*endptr == 'x' || *endptr == 'X') /* maybe an hexadecimal constant? */ if (*endptr == 'x' || *endptr == 'X') // maybe an hexadecimal constant?
*result = cast_num(strtoul(s, &endptr, 16)); *result = cast_num(strtoul(s, &endptr, 16));
if (*endptr == '\0') if (*endptr == '\0')
return 1; /* most common case */ return 1; // most common case
while (isspace(cast_to(unsigned char, *endptr))) while (isspace(cast_to(unsigned char, *endptr)))
endptr++; endptr++;
if (*endptr != '\0') if (*endptr != '\0')
return 0; /* invalid trailing characters? */ return 0; // invalid trailing characters?
return 1; return 1;
} }
@ -121,7 +121,7 @@ void luaO_chunkid(char* out, const char* source, size_t bufflen)
{ {
if (*source == '=') if (*source == '=')
{ {
source++; /* skip the `=' */ source++; // skip the `='
size_t srclen = strlen(source); size_t srclen = strlen(source);
size_t dstlen = srclen < bufflen ? srclen : bufflen - 1; size_t dstlen = srclen < bufflen ? srclen : bufflen - 1;
memcpy(out, source, dstlen); memcpy(out, source, dstlen);
@ -130,26 +130,26 @@ void luaO_chunkid(char* out, const char* source, size_t bufflen)
else if (*source == '@') else if (*source == '@')
{ {
size_t l; size_t l;
source++; /* skip the `@' */ source++; // skip the `@'
bufflen -= sizeof("..."); bufflen -= sizeof("...");
l = strlen(source); l = strlen(source);
strcpy(out, ""); strcpy(out, "");
if (l > bufflen) if (l > bufflen)
{ {
source += (l - bufflen); /* get last part of file name */ source += (l - bufflen); // get last part of file name
strcat(out, "..."); strcat(out, "...");
} }
strcat(out, source); strcat(out, source);
} }
else else
{ /* out = [string "string"] */ { // out = [string "string"]
size_t len = strcspn(source, "\n\r"); /* stop at first newline */ size_t len = strcspn(source, "\n\r"); // stop at first newline
bufflen -= sizeof("[string \"...\"]"); bufflen -= sizeof("[string \"...\"]");
if (len > bufflen) if (len > bufflen)
len = bufflen; len = bufflen;
strcpy(out, "[string \""); strcpy(out, "[string \"");
if (source[len] != '\0') if (source[len] != '\0')
{ /* must truncate? */ { // must truncate?
strncat(out, source, len); strncat(out, source, len);
strcat(out, "..."); strcat(out, "...");
} }

View File

@ -49,7 +49,7 @@ typedef struct lua_TValue
int tt; int tt;
} TValue; } TValue;
/* Macros to test type */ // Macros to test type
#define ttisnil(o) (ttype(o) == LUA_TNIL) #define ttisnil(o) (ttype(o) == LUA_TNIL)
#define ttisnumber(o) (ttype(o) == LUA_TNUMBER) #define ttisnumber(o) (ttype(o) == LUA_TNUMBER)
#define ttisstring(o) (ttype(o) == LUA_TSTRING) #define ttisstring(o) (ttype(o) == LUA_TSTRING)
@ -62,7 +62,7 @@ typedef struct lua_TValue
#define ttisvector(o) (ttype(o) == LUA_TVECTOR) #define ttisvector(o) (ttype(o) == LUA_TVECTOR)
#define ttisupval(o) (ttype(o) == LUA_TUPVAL) #define ttisupval(o) (ttype(o) == LUA_TUPVAL)
/* Macros to access values */ // Macros to access values
#define ttype(o) ((o)->tt) #define ttype(o) ((o)->tt)
#define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc) #define gcvalue(o) check_exp(iscollectable(o), (o)->value.gc)
#define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p) #define pvalue(o) check_exp(ttislightuserdata(o), (o)->value.p)
@ -85,7 +85,7 @@ typedef struct lua_TValue
#define checkliveness(g, obj) LUAU_ASSERT(!iscollectable(obj) || ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc))) #define checkliveness(g, obj) LUAU_ASSERT(!iscollectable(obj) || ((ttype(obj) == (obj)->value.gc->gch.tt) && !isdead(g, (obj)->value.gc)))
/* Macros to set values */ // Macros to set values
#define setnilvalue(obj) ((obj)->tt = LUA_TNIL) #define setnilvalue(obj) ((obj)->tt = LUA_TNIL)
#define setnvalue(obj, x) \ #define setnvalue(obj, x) \
@ -200,18 +200,18 @@ typedef struct lua_TValue
** different types of sets, according to destination ** different types of sets, according to destination
*/ */
/* from stack to (same) stack */ // from stack to (same) stack
#define setobjs2s setobj #define setobjs2s setobj
/* to stack (not from same stack) */ // to stack (not from same stack)
#define setobj2s setobj #define setobj2s setobj
#define setsvalue2s setsvalue #define setsvalue2s setsvalue
#define sethvalue2s sethvalue #define sethvalue2s sethvalue
#define setptvalue2s setptvalue #define setptvalue2s setptvalue
/* from table to same table */ // from table to same table
#define setobjt2t setobj #define setobjt2t setobj
/* to table */ // to table
#define setobj2t setobj #define setobj2t setobj
/* to new object */ // to new object
#define setobj2n setobj #define setobj2n setobj
#define setsvalue2n setsvalue #define setsvalue2n setsvalue
@ -219,7 +219,7 @@ typedef struct lua_TValue
#define iscollectable(o) (ttype(o) >= LUA_TSTRING) #define iscollectable(o) (ttype(o) >= LUA_TSTRING)
typedef TValue* StkId; /* index to stack elements */ typedef TValue* StkId; // index to stack elements
/* /*
** String headers for string table ** String headers for string table
@ -269,13 +269,13 @@ typedef struct Proto
CommonHeader; CommonHeader;
TValue* k; /* constants used by the function */ TValue* k; // constants used by the function
Instruction* code; /* function bytecode */ Instruction* code; // function bytecode
struct Proto** p; /* functions defined inside the function */ struct Proto** p; // functions defined inside the function
uint8_t* lineinfo; /* for each instruction, line number as a delta from baseline */ uint8_t* lineinfo; // for each instruction, line number as a delta from baseline
int* abslineinfo; /* baseline line info, one entry for each 1<<linegaplog2 instructions; allocated after lineinfo */ int* abslineinfo; // baseline line info, one entry for each 1<<linegaplog2 instructions; allocated after lineinfo
struct LocVar* locvars; /* information about local variables */ struct LocVar* locvars; // information about local variables
TString** upvalues; /* upvalue names */ TString** upvalues; // upvalue names
TString* source; TString* source;
TString* debugname; TString* debugname;
@ -294,7 +294,7 @@ typedef struct Proto
int linedefined; int linedefined;
uint8_t nups; /* number of upvalues */ uint8_t nups; // number of upvalues
uint8_t numparams; uint8_t numparams;
uint8_t is_vararg; uint8_t is_vararg;
uint8_t maxstacksize; uint8_t maxstacksize;
@ -304,9 +304,9 @@ typedef struct Proto
typedef struct LocVar typedef struct LocVar
{ {
TString* varname; TString* varname;
int startpc; /* first point where variable is active */ int startpc; // first point where variable is active
int endpc; /* first point where variable is dead */ int endpc; // first point where variable is dead
uint8_t reg; /* register slot, relative to base, where variable is stored */ uint8_t reg; // register slot, relative to base, where variable is stored
} LocVar; } LocVar;
/* /*
@ -317,19 +317,19 @@ typedef struct UpVal
{ {
CommonHeader; CommonHeader;
// 1 (x86) or 5 (x64) byte padding // 1 (x86) or 5 (x64) byte padding
TValue* v; /* points to stack or to its own value */ TValue* v; // points to stack or to its own value
union union
{ {
TValue value; /* the value (when closed) */ TValue value; // the value (when closed)
struct struct
{ {
/* global double linked list (when open) */ // global double linked list (when open)
struct UpVal* prev; struct UpVal* prev;
struct UpVal* next; struct UpVal* next;
/* thread double linked list (when open) */ // thread double linked list (when open)
struct UpVal* threadnext; struct UpVal* threadnext;
/* note: this is the location of a pointer to this upvalue in the previous element that can be either an UpVal or a lua_State */ // note: this is the location of a pointer to this upvalue in the previous element that can be either an UpVal or a lua_State
struct UpVal** threadprev; struct UpVal** threadprev;
} l; } l;
} u; } u;
@ -381,7 +381,7 @@ typedef struct TKey
::Value value; ::Value value;
int extra[LUA_EXTRA_SIZE]; int extra[LUA_EXTRA_SIZE];
unsigned tt : 4; unsigned tt : 4;
int next : 28; /* for chaining */ int next : 28; // for chaining
} TKey; } TKey;
typedef struct LuaNode typedef struct LuaNode
@ -390,7 +390,7 @@ typedef struct LuaNode
TKey key; TKey key;
} LuaNode; } LuaNode;
/* copy a value into a key */ // copy a value into a key
#define setnodekey(L, node, obj) \ #define setnodekey(L, node, obj) \
{ \ { \
LuaNode* n_ = (node); \ LuaNode* n_ = (node); \
@ -401,7 +401,7 @@ typedef struct LuaNode
checkliveness(L->global, i_o); \ checkliveness(L->global, i_o); \
} }
/* copy a value from a key */ // copy a value from a key
#define getnodekey(L, obj, node) \ #define getnodekey(L, obj, node) \
{ \ { \
TValue* i_o = (obj); \ TValue* i_o = (obj); \
@ -418,22 +418,22 @@ typedef struct Table
CommonHeader; CommonHeader;
uint8_t tmcache; /* 1<<p means tagmethod(p) is not present */ uint8_t tmcache; // 1<<p means tagmethod(p) is not present
uint8_t readonly; /* sandboxing feature to prohibit writes to table */ uint8_t readonly; // sandboxing feature to prohibit writes to table
uint8_t safeenv; /* environment doesn't share globals with other scripts */ uint8_t safeenv; // environment doesn't share globals with other scripts
uint8_t lsizenode; /* log2 of size of `node' array */ uint8_t lsizenode; // log2 of size of `node' array
uint8_t nodemask8; /* (1<<lsizenode)-1, truncated to 8 bits */ uint8_t nodemask8; // (1<<lsizenode)-1, truncated to 8 bits
int sizearray; /* size of `array' array */ int sizearray; // size of `array' array
union union
{ {
int lastfree; /* any free position is before this position */ int lastfree; // any free position is before this position
int aboundary; /* negated 'boundary' of `array' array; iff aboundary < 0 */ int aboundary; // negated 'boundary' of `array' array; iff aboundary < 0
}; };
struct Table* metatable; struct Table* metatable;
TValue* array; /* array part */ TValue* array; // array part
LuaNode* node; LuaNode* node;
GCObject* gclist; GCObject* gclist;
} Table; } Table;

View File

@ -46,8 +46,8 @@ static void setfield(lua_State* L, const char* key, int value)
static void setboolfield(lua_State* L, const char* key, int value) static void setboolfield(lua_State* L, const char* key, int value)
{ {
if (value < 0) /* undefined? */ if (value < 0) // undefined?
return; /* does not set field */ return; // does not set field
lua_pushboolean(L, value); lua_pushboolean(L, value);
lua_setfield(L, -2, key); lua_setfield(L, -2, key);
} }
@ -85,9 +85,9 @@ static int os_date(lua_State* L)
struct tm tm; struct tm tm;
struct tm* stm; struct tm* stm;
if (*s == '!') if (*s == '!')
{ /* UTC? */ { // UTC?
stm = gmtime_r(&t, &tm); stm = gmtime_r(&t, &tm);
s++; /* skip `!' */ s++; // skip `!'
} }
else else
{ {
@ -95,13 +95,13 @@ static int os_date(lua_State* L)
stm = t < 0 ? NULL : localtime_r(&t, &tm); stm = t < 0 ? NULL : localtime_r(&t, &tm);
} }
if (stm == NULL) /* invalid date? */ if (stm == NULL) // invalid date?
{ {
lua_pushnil(L); lua_pushnil(L);
} }
else if (strcmp(s, "*t") == 0) else if (strcmp(s, "*t") == 0)
{ {
lua_createtable(L, 0, 9); /* 9 = number of fields */ lua_createtable(L, 0, 9); // 9 = number of fields
setfield(L, "sec", stm->tm_sec); setfield(L, "sec", stm->tm_sec);
setfield(L, "min", stm->tm_min); setfield(L, "min", stm->tm_min);
setfield(L, "hour", stm->tm_hour); setfield(L, "hour", stm->tm_hour);
@ -122,7 +122,7 @@ static int os_date(lua_State* L)
luaL_buffinit(L, &b); luaL_buffinit(L, &b);
for (; *s; s++) for (; *s; s++)
{ {
if (*s != '%' || *(s + 1) == '\0') /* no conversion specifier? */ if (*s != '%' || *(s + 1) == '\0') // no conversion specifier?
{ {
luaL_addchar(&b, *s); luaL_addchar(&b, *s);
} }
@ -133,7 +133,7 @@ static int os_date(lua_State* L)
else else
{ {
size_t reslen; size_t reslen;
char buff[200]; /* should be big enough for any conversion result */ char buff[200]; // should be big enough for any conversion result
cc[1] = *(++s); cc[1] = *(++s);
reslen = strftime(buff, sizeof(buff), cc, stm); reslen = strftime(buff, sizeof(buff), cc, stm);
luaL_addlstring(&b, buff, reslen); luaL_addlstring(&b, buff, reslen);
@ -147,13 +147,13 @@ static int os_date(lua_State* L)
static int os_time(lua_State* L) static int os_time(lua_State* L)
{ {
time_t t; time_t t;
if (lua_isnoneornil(L, 1)) /* called without args? */ if (lua_isnoneornil(L, 1)) // called without args?
t = time(NULL); /* get current time */ t = time(NULL); // get current time
else else
{ {
struct tm ts; struct tm ts;
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
lua_settop(L, 1); /* make sure table is at the top */ lua_settop(L, 1); // make sure table is at the top
ts.tm_sec = getfield(L, "sec", 0); ts.tm_sec = getfield(L, "sec", 0);
ts.tm_min = getfield(L, "min", 0); ts.tm_min = getfield(L, "min", 0);
ts.tm_hour = getfield(L, "hour", 12); ts.tm_hour = getfield(L, "hour", 12);

View File

@ -21,22 +21,22 @@ typedef struct LG
static void stack_init(lua_State* L1, lua_State* L) static void stack_init(lua_State* L1, lua_State* L)
{ {
/* initialize CallInfo array */ // initialize CallInfo array
L1->base_ci = luaM_newarray(L, BASIC_CI_SIZE, CallInfo, L1->memcat); L1->base_ci = luaM_newarray(L, BASIC_CI_SIZE, CallInfo, L1->memcat);
L1->ci = L1->base_ci; L1->ci = L1->base_ci;
L1->size_ci = BASIC_CI_SIZE; L1->size_ci = BASIC_CI_SIZE;
L1->end_ci = L1->base_ci + L1->size_ci - 1; L1->end_ci = L1->base_ci + L1->size_ci - 1;
/* initialize stack array */ // initialize stack array
L1->stack = luaM_newarray(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue, L1->memcat); L1->stack = luaM_newarray(L, BASIC_STACK_SIZE + EXTRA_STACK, TValue, L1->memcat);
L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK; L1->stacksize = BASIC_STACK_SIZE + EXTRA_STACK;
TValue* stack = L1->stack; TValue* stack = L1->stack;
for (int i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++) for (int i = 0; i < BASIC_STACK_SIZE + EXTRA_STACK; i++)
setnilvalue(stack + i); /* erase new stack */ setnilvalue(stack + i); // erase new stack
L1->top = stack; L1->top = stack;
L1->stack_last = stack + (L1->stacksize - EXTRA_STACK); L1->stack_last = stack + (L1->stacksize - EXTRA_STACK);
/* initialize first ci */ // initialize first ci
L1->ci->func = L1->top; L1->ci->func = L1->top;
setnilvalue(L1->top++); /* `function' entry for this `ci' */ setnilvalue(L1->top++); // `function' entry for this `ci'
L1->base = L1->ci->base = L1->top; L1->base = L1->ci->base = L1->top;
L1->ci->top = L1->top + LUA_MINSTACK; L1->ci->top = L1->top + LUA_MINSTACK;
} }
@ -53,13 +53,13 @@ static void freestack(lua_State* L, lua_State* L1)
static void f_luaopen(lua_State* L, void* ud) static void f_luaopen(lua_State* L, void* ud)
{ {
global_State* g = L->global; global_State* g = L->global;
stack_init(L, L); /* init stack */ stack_init(L, L); // init stack
L->gt = luaH_new(L, 0, 2); /* table of globals */ L->gt = luaH_new(L, 0, 2); // table of globals
sethvalue(L, registry(L), luaH_new(L, 0, 2)); /* registry */ sethvalue(L, registry(L), luaH_new(L, 0, 2)); // registry
luaS_resize(L, LUA_MINSTRTABSIZE); /* initial size of string table */ luaS_resize(L, LUA_MINSTRTABSIZE); // initial size of string table
luaT_init(L); luaT_init(L);
luaS_fix(luaS_newliteral(L, LUA_MEMERRMSG)); /* pin to make sure we can always throw this error */ luaS_fix(luaS_newliteral(L, LUA_MEMERRMSG)); // pin to make sure we can always throw this error
luaS_fix(luaS_newliteral(L, LUA_ERRERRMSG)); /* pin to make sure we can always throw this error */ luaS_fix(luaS_newliteral(L, LUA_ERRERRMSG)); // pin to make sure we can always throw this error
g->GCthreshold = 4 * g->totalbytes; g->GCthreshold = 4 * g->totalbytes;
} }
@ -85,8 +85,8 @@ static void preinit_state(lua_State* L, global_State* g)
static void close_state(lua_State* L) static void close_state(lua_State* L)
{ {
global_State* g = L->global; global_State* g = L->global;
luaF_close(L, L->stack); /* close all upvalues for this thread */ luaF_close(L, L->stack); // close all upvalues for this thread
luaC_freeall(L); /* collect all objects */ luaC_freeall(L); // collect all objects
LUAU_ASSERT(g->strbufgc == NULL); LUAU_ASSERT(g->strbufgc == NULL);
LUAU_ASSERT(g->strt.nuse == 0); LUAU_ASSERT(g->strt.nuse == 0);
luaM_freearray(L, L->global->strt.hash, L->global->strt.size, TString*, 0); luaM_freearray(L, L->global->strt.hash, L->global->strt.size, TString*, 0);
@ -110,8 +110,8 @@ lua_State* luaE_newthread(lua_State* L)
luaC_init(L, L1, LUA_TTHREAD); luaC_init(L, L1, LUA_TTHREAD);
preinit_state(L1, L->global); preinit_state(L1, L->global);
L1->activememcat = L->activememcat; // inherit the active memory category L1->activememcat = L->activememcat; // inherit the active memory category
stack_init(L1, L); /* init stack */ stack_init(L1, L); // init stack
L1->gt = L->gt; /* share table of globals */ L1->gt = L->gt; // share table of globals
L1->singlestep = L->singlestep; L1->singlestep = L->singlestep;
LUAU_ASSERT(iswhite(obj2gco(L1))); LUAU_ASSERT(iswhite(obj2gco(L1)));
return L1; return L1;
@ -119,7 +119,7 @@ lua_State* luaE_newthread(lua_State* L)
void luaE_freethread(lua_State* L, lua_State* L1, lua_Page* page) void luaE_freethread(lua_State* L, lua_State* L1, lua_Page* page)
{ {
luaF_close(L1, L1->stack); /* close all upvalues for this thread */ luaF_close(L1, L1->stack); // close all upvalues for this thread
LUAU_ASSERT(L1->openupval == NULL); LUAU_ASSERT(L1->openupval == NULL);
global_State* g = L->global; global_State* g = L->global;
if (g->cb.userthread) if (g->cb.userthread)
@ -130,9 +130,9 @@ void luaE_freethread(lua_State* L, lua_State* L1, lua_Page* page)
void lua_resetthread(lua_State* L) void lua_resetthread(lua_State* L)
{ {
/* close upvalues before clearing anything */ // close upvalues before clearing anything
luaF_close(L, L->stack); luaF_close(L, L->stack);
/* clear call frames */ // clear call frames
CallInfo* ci = L->base_ci; CallInfo* ci = L->base_ci;
ci->func = L->stack; ci->func = L->stack;
ci->base = ci->func + 1; ci->base = ci->func + 1;
@ -141,12 +141,12 @@ void lua_resetthread(lua_State* L)
L->ci = ci; L->ci = ci;
if (L->size_ci != BASIC_CI_SIZE) if (L->size_ci != BASIC_CI_SIZE)
luaD_reallocCI(L, BASIC_CI_SIZE); luaD_reallocCI(L, BASIC_CI_SIZE);
/* clear thread state */ // clear thread state
L->status = LUA_OK; L->status = LUA_OK;
L->base = L->ci->base; L->base = L->ci->base;
L->top = L->ci->base; L->top = L->ci->base;
L->nCcalls = L->baseCcalls = 0; L->nCcalls = L->baseCcalls = 0;
/* clear thread stack */ // clear thread stack
if (L->stacksize != BASIC_STACK_SIZE + EXTRA_STACK) if (L->stacksize != BASIC_STACK_SIZE + EXTRA_STACK)
luaD_reallocstack(L, BASIC_STACK_SIZE); luaD_reallocstack(L, BASIC_STACK_SIZE);
for (int i = 0; i < L->stacksize; i++) for (int i = 0; i < L->stacksize; i++)
@ -177,7 +177,7 @@ lua_State* lua_newstate(lua_Alloc f, void* ud)
g->mainthread = L; g->mainthread = L;
g->uvhead.u.l.prev = &g->uvhead; g->uvhead.u.l.prev = &g->uvhead;
g->uvhead.u.l.next = &g->uvhead; g->uvhead.u.l.next = &g->uvhead;
g->GCthreshold = 0; /* mark it as unfinished state */ g->GCthreshold = 0; // mark it as unfinished state
g->registryfree = 0; g->registryfree = 0;
g->errorjmp = NULL; g->errorjmp = NULL;
g->rngstate = 0; g->rngstate = 0;
@ -224,7 +224,7 @@ lua_State* lua_newstate(lua_Alloc f, void* ud)
if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0) if (luaD_rawrunprotected(L, f_luaopen, NULL) != 0)
{ {
/* memory allocation error: free partial state */ // memory allocation error: free partial state
close_state(L); close_state(L);
L = NULL; L = NULL;
} }
@ -233,7 +233,7 @@ lua_State* lua_newstate(lua_Alloc f, void* ud)
void lua_close(lua_State* L) void lua_close(lua_State* L)
{ {
L = L->global->mainthread; /* only the main thread can be closed */ L = L->global->mainthread; // only the main thread can be closed
luaF_close(L, L->stack); /* close all upvalues for this thread */ luaF_close(L, L->stack); // close all upvalues for this thread
close_state(L); close_state(L);
} }

View File

@ -5,10 +5,10 @@
#include "lobject.h" #include "lobject.h"
#include "ltm.h" #include "ltm.h"
/* registry */ // registry
#define registry(L) (&L->global->registry) #define registry(L) (&L->global->registry)
/* extra stack space to handle TM calls and some other extras */ // extra stack space to handle TM calls and some other extras
#define EXTRA_STACK 5 #define EXTRA_STACK 5
#define BASIC_CI_SIZE 8 #define BASIC_CI_SIZE 8
@ -20,7 +20,7 @@ typedef struct stringtable
{ {
TString** hash; TString** hash;
uint32_t nuse; /* number of elements */ uint32_t nuse; // number of elements
int size; int size;
} stringtable; } stringtable;
// clang-format on // clang-format on
@ -57,18 +57,18 @@ typedef struct stringtable
typedef struct CallInfo typedef struct CallInfo
{ {
StkId base; /* base for this function */ StkId base; // base for this function
StkId func; /* function index in the stack */ StkId func; // function index in the stack
StkId top; /* top for this function */ StkId top; // top for this function
const Instruction* savedpc; const Instruction* savedpc;
int nresults; /* expected number of results from this function */ int nresults; // expected number of results from this function
unsigned int flags; /* call frame flags, see LUA_CALLINFO_* */ unsigned int flags; // call frame flags, see LUA_CALLINFO_*
} CallInfo; } CallInfo;
// clang-format on // clang-format on
#define LUA_CALLINFO_RETURN (1 << 0) /* should the interpreter return after returning from this callinfo? first frame must have this set */ #define LUA_CALLINFO_RETURN (1 << 0) // should the interpreter return after returning from this callinfo? first frame must have this set
#define LUA_CALLINFO_HANDLE (1 << 1) /* should the error thrown during execution get handled by continuation from this callinfo? func must be C */ #define LUA_CALLINFO_HANDLE (1 << 1) // should the error thrown during execution get handled by continuation from this callinfo? func must be C
#define curr_func(L) (clvalue(L->ci->func)) #define curr_func(L) (clvalue(L->ci->func))
#define ci_func(ci) (clvalue((ci)->func)) #define ci_func(ci) (clvalue((ci)->func))
@ -152,55 +152,55 @@ struct GCMetrics
// clang-format off // clang-format off
typedef struct global_State typedef struct global_State
{ {
stringtable strt; /* hash table for strings */ stringtable strt; // hash table for strings
lua_Alloc frealloc; /* function to reallocate memory */ lua_Alloc frealloc; // function to reallocate memory
void* ud; /* auxiliary data to `frealloc' */ void* ud; // auxiliary data to `frealloc'
uint8_t currentwhite; uint8_t currentwhite;
uint8_t gcstate; /* state of garbage collector */ uint8_t gcstate; // state of garbage collector
GCObject* gray; /* list of gray objects */ GCObject* gray; // list of gray objects
GCObject* grayagain; /* list of objects to be traversed atomically */ GCObject* grayagain; // list of objects to be traversed atomically
GCObject* weak; /* list of weak tables (to be cleared) */ GCObject* weak; // list of weak tables (to be cleared)
TString* strbufgc; // list of all string buffer objects TString* strbufgc; // list of all string buffer objects
size_t GCthreshold; // when totalbytes > GCthreshold; run GC step size_t GCthreshold; // when totalbytes > GCthreshold, run GC step
size_t totalbytes; // number of bytes currently allocated size_t totalbytes; // number of bytes currently allocated
int gcgoal; // see LUAI_GCGOAL int gcgoal; // see LUAI_GCGOAL
int gcstepmul; // see LUAI_GCSTEPMUL int gcstepmul; // see LUAI_GCSTEPMUL
int gcstepsize; // see LUAI_GCSTEPSIZE int gcstepsize; // see LUAI_GCSTEPSIZE
struct lua_Page* freepages[LUA_SIZECLASSES]; // free page linked list for each size class for non-collectable objects struct lua_Page* freepages[LUA_SIZECLASSES]; // free page linked list for each size class for non-collectable objects
struct lua_Page* freegcopages[LUA_SIZECLASSES]; // free page linked list for each size class for collectable objects struct lua_Page* freegcopages[LUA_SIZECLASSES]; // free page linked list for each size class for collectable objects
struct lua_Page* allgcopages; // page linked list with all pages for all classes struct lua_Page* allgcopages; // page linked list with all pages for all classes
struct lua_Page* sweepgcopage; // position of the sweep in `allgcopages' struct lua_Page* sweepgcopage; // position of the sweep in `allgcopages'
size_t memcatbytes[LUA_MEMORY_CATEGORIES]; /* total amount of memory used by each memory category */ size_t memcatbytes[LUA_MEMORY_CATEGORIES]; // total amount of memory used by each memory category
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 Table* 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
TValue pseudotemp; /* storage for temporary values used in pseudo2addr */ TValue pseudotemp; // storage for temporary values used in pseudo2addr
TValue registry; /* registry table, used by lua_ref and LUA_REGISTRYINDEX */ TValue registry; // registry table, used by lua_ref and LUA_REGISTRYINDEX
int registryfree; /* next free slot in registry */ int registryfree; // next free slot in registry
struct lua_jmpbuf* errorjmp; /* jump buffer data for longjmp-style error handling */ struct lua_jmpbuf* errorjmp; // jump buffer data for longjmp-style error handling
uint64_t rngstate; /* PCG random number generator state */ uint64_t rngstate; // PCG random number generator state
uint64_t ptrenckey[4]; /* pointer encoding key for display */ uint64_t ptrenckey[4]; // pointer encoding key for display
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
lua_Callbacks cb; lua_Callbacks cb;
@ -221,39 +221,39 @@ struct lua_State
CommonHeader; CommonHeader;
uint8_t status; uint8_t status;
uint8_t activememcat; /* memory category that is used for new GC object allocations */ uint8_t activememcat; // memory category that is used for new GC object allocations
uint8_t stackstate; uint8_t stackstate;
bool singlestep; /* call debugstep hook after each instruction */ bool singlestep; // call debugstep hook after each instruction
StkId top; /* first free slot in the stack */ StkId top; // first free slot in the stack
StkId base; /* base of current function */ StkId base; // base of current function
global_State* global; global_State* global;
CallInfo* ci; /* call info for current function */ CallInfo* ci; // call info for current function
StkId stack_last; /* last free slot in the stack */ StkId stack_last; // last free slot in the stack
StkId stack; /* stack base */ StkId stack; // stack base
CallInfo* end_ci; /* points after end of ci array*/ CallInfo* end_ci; // points after end of ci array
CallInfo* base_ci; /* array of CallInfo's */ CallInfo* base_ci; // array of CallInfo's
int stacksize; int stacksize;
int size_ci; /* size of array `base_ci' */ int size_ci; // size of array `base_ci'
unsigned short nCcalls; /* number of nested C calls */ unsigned short nCcalls; // number of nested C calls
unsigned short baseCcalls; /* nested C calls when resuming coroutine */ unsigned short baseCcalls; // nested C calls when resuming coroutine
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 */ Table* 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;
TString* namecall; /* when invoked from Luau using NAMECALL, what method do we need to invoke? */ TString* namecall; // when invoked from Luau using NAMECALL, what method do we need to invoke?
void* userdata; void* userdata;
}; };
@ -271,10 +271,10 @@ union GCObject
struct Table h; struct Table h;
struct Proto p; struct Proto p;
struct UpVal uv; struct UpVal uv;
struct lua_State th; /* thread */ struct lua_State th; // thread
}; };
/* macros to convert a GCObject into a specific value */ // macros to convert a GCObject into a specific value
#define gco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts)) #define gco2ts(o) check_exp((o)->gch.tt == LUA_TSTRING, &((o)->ts))
#define gco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u)) #define gco2u(o) check_exp((o)->gch.tt == LUA_TUSERDATA, &((o)->u))
#define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl)) #define gco2cl(o) check_exp((o)->gch.tt == LUA_TFUNCTION, &((o)->cl))
@ -283,7 +283,7 @@ union GCObject
#define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv)) #define gco2uv(o) check_exp((o)->gch.tt == LUA_TUPVAL, &((o)->uv))
#define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th)) #define gco2th(o) check_exp((o)->gch.tt == LUA_TTHREAD, &((o)->th))
/* macro to convert any Lua object into a GCObject */ // macro to convert any Lua object into a GCObject
#define obj2gco(v) check_exp(iscollectable(v), cast_to(GCObject*, (v) + 0)) #define obj2gco(v) check_exp(iscollectable(v), cast_to(GCObject*, (v) + 0))
LUAI_FUNC lua_State* luaE_newthread(lua_State* L); LUAI_FUNC lua_State* luaE_newthread(lua_State* L);

View File

@ -48,17 +48,17 @@ void luaS_resize(lua_State* L, int newsize)
stringtable* tb = &L->global->strt; stringtable* tb = &L->global->strt;
for (int i = 0; i < newsize; i++) for (int i = 0; i < newsize; i++)
newhash[i] = NULL; newhash[i] = NULL;
/* rehash */ // rehash
for (int i = 0; i < tb->size; i++) for (int i = 0; i < tb->size; i++)
{ {
TString* p = tb->hash[i]; TString* p = tb->hash[i];
while (p) while (p)
{ /* for each node in the list */ { // for each node in the list
TString* next = p->next; /* save next */ TString* next = p->next; // save next
unsigned int h = p->hash; unsigned int h = p->hash;
int h1 = lmod(h, newsize); /* new position */ int h1 = lmod(h, newsize); // new position
LUAU_ASSERT(cast_int(h % newsize) == lmod(h, newsize)); LUAU_ASSERT(cast_int(h % newsize) == lmod(h, newsize));
p->next = newhash[h1]; /* chain it */ p->next = newhash[h1]; // chain it
newhash[h1] = p; newhash[h1] = p;
p = next; p = next;
} }
@ -81,15 +81,15 @@ static TString* newlstr(lua_State* L, const char* str, size_t l, unsigned int h)
ts->tt = LUA_TSTRING; ts->tt = LUA_TSTRING;
ts->memcat = L->activememcat; ts->memcat = L->activememcat;
memcpy(ts->data, str, l); memcpy(ts->data, str, l);
ts->data[l] = '\0'; /* ending 0 */ ts->data[l] = '\0'; // ending 0
ts->atom = ATOM_UNDEF; ts->atom = ATOM_UNDEF;
tb = &L->global->strt; tb = &L->global->strt;
h = lmod(h, tb->size); h = lmod(h, tb->size);
ts->next = tb->hash[h]; /* chain new entry */ ts->next = tb->hash[h]; // chain new entry
tb->hash[h] = ts; tb->hash[h] = ts;
tb->nuse++; tb->nuse++;
if (tb->nuse > cast_to(uint32_t, tb->size) && tb->size <= INT_MAX / 2) if (tb->nuse > cast_to(uint32_t, tb->size) && tb->size <= INT_MAX / 2)
luaS_resize(L, tb->size * 2); /* too crowded */ luaS_resize(L, tb->size * 2); // too crowded
return ts; return ts;
} }
@ -181,13 +181,13 @@ TString* luaS_newlstr(lua_State* L, const char* str, size_t l)
{ {
if (el->len == l && (memcmp(str, getstr(el), l) == 0)) if (el->len == l && (memcmp(str, getstr(el), l) == 0))
{ {
/* string may be dead */ // string may be dead
if (isdead(L->global, obj2gco(el))) if (isdead(L->global, obj2gco(el)))
changewhite(obj2gco(el)); changewhite(obj2gco(el));
return el; return el;
} }
} }
return newlstr(L, str, l, h); /* not found */ return newlstr(L, str, l, h); // not found
} }
static bool unlinkstr(lua_State* L, TString* ts) static bool unlinkstr(lua_State* L, TString* ts)

View File

@ -5,10 +5,10 @@
#include "lobject.h" #include "lobject.h"
#include "lstate.h" #include "lstate.h"
/* string size limit */ // string size limit
#define MAXSSIZE (1 << 30) #define MAXSSIZE (1 << 30)
/* string atoms are not defined by default; the storage is 16-bit integer */ // string atoms are not defined by default; the storage is 16-bit integer
#define ATOM_UNDEF -32768 #define ATOM_UNDEF -32768
#define sizestring(len) (offsetof(TString, data) + len + 1) #define sizestring(len) (offsetof(TString, data) + len + 1)

View File

@ -10,7 +10,7 @@
LUAU_FASTFLAGVARIABLE(LuauTostringFormatSpecifier, false); LUAU_FASTFLAGVARIABLE(LuauTostringFormatSpecifier, false);
/* macro to `unsign' a character */ // macro to `unsign' a character
#define uchar(c) ((unsigned char)(c)) #define uchar(c) ((unsigned char)(c))
static int str_len(lua_State* L) static int str_len(lua_State* L)
@ -23,7 +23,7 @@ static int str_len(lua_State* L)
static int posrelat(int pos, size_t len) static int posrelat(int pos, size_t len)
{ {
/* relative string position: negative means back from end */ // relative string position: negative means back from end
if (pos < 0) if (pos < 0)
pos += (int)len + 1; pos += (int)len + 1;
return (pos >= 0) ? pos : 0; return (pos >= 0) ? pos : 0;
@ -139,9 +139,9 @@ static int str_byte(lua_State* L)
if ((size_t)pose > l) if ((size_t)pose > l)
pose = (int)l; pose = (int)l;
if (posi > pose) if (posi > pose)
return 0; /* empty interval; return no values */ return 0; // empty interval; return no values
n = (int)(pose - posi + 1); n = (int)(pose - posi + 1);
if (posi + n <= pose) /* overflow? */ if (posi + n <= pose) // overflow?
luaL_error(L, "string slice too long"); luaL_error(L, "string slice too long");
luaL_checkstack(L, n, "string slice too long"); luaL_checkstack(L, n, "string slice too long");
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
@ -151,7 +151,7 @@ static int str_byte(lua_State* L)
static int str_char(lua_State* L) static int str_char(lua_State* L)
{ {
int n = lua_gettop(L); /* number of arguments */ int n = lua_gettop(L); // number of arguments
luaL_Buffer b; luaL_Buffer b;
char* ptr = luaL_buffinitsize(L, &b, n); char* ptr = luaL_buffinitsize(L, &b, n);
@ -178,12 +178,12 @@ static int str_char(lua_State* L)
typedef struct MatchState typedef struct MatchState
{ {
int matchdepth; /* control for recursive depth (to avoid C stack overflow) */ int matchdepth; // control for recursive depth (to avoid C stack overflow)
const char* src_init; /* init of source string */ const char* src_init; // init of source string
const char* src_end; /* end ('\0') of source string */ const char* src_end; // end ('\0') of source string
const char* p_end; /* end ('\0') of pattern */ const char* p_end; // end ('\0') of pattern
lua_State* L; lua_State* L;
int level; /* total number of captures (finished or unfinished) */ int level; // total number of captures (finished or unfinished)
struct struct
{ {
const char* init; const char* init;
@ -191,7 +191,7 @@ typedef struct MatchState
} capture[LUA_MAXCAPTURES]; } capture[LUA_MAXCAPTURES];
} MatchState; } MatchState;
/* recursive function */ // recursive function
static const char* match(MatchState* ms, const char* s, const char* p); static const char* match(MatchState* ms, const char* s, const char* p);
#define L_ESC '%' #define L_ESC '%'
@ -229,11 +229,11 @@ static const char* classend(MatchState* ms, const char* p)
if (*p == '^') if (*p == '^')
p++; p++;
do do
{ /* look for a `]' */ { // look for a `]'
if (p == ms->p_end) if (p == ms->p_end)
luaL_error(ms->L, "malformed pattern (missing ']')"); luaL_error(ms->L, "malformed pattern (missing ']')");
if (*(p++) == L_ESC && p < ms->p_end) if (*(p++) == L_ESC && p < ms->p_end)
p++; /* skip escapes (e.g. `%]') */ p++; // skip escapes (e.g. `%]')
} while (*p != ']'); } while (*p != ']');
return p + 1; return p + 1;
} }
@ -281,7 +281,7 @@ static int match_class(int c, int cl)
break; break;
case 'z': case 'z':
res = (c == 0); res = (c == 0);
break; /* deprecated option */ break; // deprecated option
default: default:
return (cl == c); return (cl == c);
} }
@ -294,7 +294,7 @@ static int matchbracketclass(int c, const char* p, const char* ec)
if (*(p + 1) == '^') if (*(p + 1) == '^')
{ {
sig = 0; sig = 0;
p++; /* skip the `^' */ p++; // skip the `^'
} }
while (++p < ec) while (++p < ec)
{ {
@ -326,7 +326,7 @@ static int singlematch(MatchState* ms, const char* s, const char* p, const char*
switch (*p) switch (*p)
{ {
case '.': case '.':
return 1; /* matches any char */ return 1; // matches any char
case L_ESC: case L_ESC:
return match_class(c, uchar(*(p + 1))); return match_class(c, uchar(*(p + 1)));
case '[': case '[':
@ -359,21 +359,21 @@ static const char* matchbalance(MatchState* ms, const char* s, const char* p)
cont++; cont++;
} }
} }
return NULL; /* string ends out of balance */ return NULL; // string ends out of balance
} }
static const char* max_expand(MatchState* ms, const char* s, const char* p, const char* ep) static const char* max_expand(MatchState* ms, const char* s, const char* p, const char* ep)
{ {
ptrdiff_t i = 0; /* counts maximum expand for item */ ptrdiff_t i = 0; // counts maximum expand for item
while (singlematch(ms, s + i, p, ep)) while (singlematch(ms, s + i, p, ep))
i++; i++;
/* keeps trying to match with the maximum repetitions */ // keeps trying to match with the maximum repetitions
while (i >= 0) while (i >= 0)
{ {
const char* res = match(ms, (s + i), ep + 1); const char* res = match(ms, (s + i), ep + 1);
if (res) if (res)
return res; return res;
i--; /* else didn't match; reduce 1 repetition to try again */ i--; // else didn't match; reduce 1 repetition to try again
} }
return NULL; return NULL;
} }
@ -386,7 +386,7 @@ static const char* min_expand(MatchState* ms, const char* s, const char* p, cons
if (res != NULL) if (res != NULL)
return res; return res;
else if (singlematch(ms, s, p, ep)) else if (singlematch(ms, s, p, ep))
s++; /* try with one more repetition */ s++; // try with one more repetition
else else
return NULL; return NULL;
} }
@ -401,8 +401,8 @@ static const char* start_capture(MatchState* ms, const char* s, const char* p, i
ms->capture[level].init = s; ms->capture[level].init = s;
ms->capture[level].len = what; ms->capture[level].len = what;
ms->level = level + 1; ms->level = level + 1;
if ((res = match(ms, s, p)) == NULL) /* match failed? */ if ((res = match(ms, s, p)) == NULL) // match failed?
ms->level--; /* undo capture */ ms->level--; // undo capture
return res; return res;
} }
@ -410,9 +410,9 @@ static const char* end_capture(MatchState* ms, const char* s, const char* p)
{ {
int l = capture_to_close(ms); int l = capture_to_close(ms);
const char* res; const char* res;
ms->capture[l].len = s - ms->capture[l].init; /* close capture */ ms->capture[l].len = s - ms->capture[l].init; // close capture
if ((res = match(ms, s, p)) == NULL) /* match failed? */ if ((res = match(ms, s, p)) == NULL) // match failed?
ms->capture[l].len = CAP_UNFINISHED; /* undo capture */ ms->capture[l].len = CAP_UNFINISHED; // undo capture
return res; return res;
} }
@ -431,60 +431,60 @@ static const char* match(MatchState* ms, const char* s, const char* p)
{ {
if (ms->matchdepth-- == 0) if (ms->matchdepth-- == 0)
luaL_error(ms->L, "pattern too complex"); luaL_error(ms->L, "pattern too complex");
init: /* using goto's to optimize tail recursion */ init: // using goto's to optimize tail recursion
if (p != ms->p_end) if (p != ms->p_end)
{ /* end of pattern? */ { // end of pattern?
switch (*p) switch (*p)
{ {
case '(': case '(':
{ /* start capture */ { // start capture
if (*(p + 1) == ')') /* position capture? */ if (*(p + 1) == ')') // position capture?
s = start_capture(ms, s, p + 2, CAP_POSITION); s = start_capture(ms, s, p + 2, CAP_POSITION);
else else
s = start_capture(ms, s, p + 1, CAP_UNFINISHED); s = start_capture(ms, s, p + 1, CAP_UNFINISHED);
break; break;
} }
case ')': case ')':
{ /* end capture */ { // end capture
s = end_capture(ms, s, p + 1); s = end_capture(ms, s, p + 1);
break; break;
} }
case '$': case '$':
{ {
if ((p + 1) != ms->p_end) /* is the `$' the last char in pattern? */ if ((p + 1) != ms->p_end) // is the `$' the last char in pattern?
goto dflt; /* no; go to default */ goto dflt; // no; go to default
s = (s == ms->src_end) ? s : NULL; /* check end of string */ s = (s == ms->src_end) ? s : NULL; // check end of string
break; break;
} }
case L_ESC: case L_ESC:
{ /* escaped sequences not in the format class[*+?-]? */ { // escaped sequences not in the format class[*+?-]?
switch (*(p + 1)) switch (*(p + 1))
{ {
case 'b': case 'b':
{ /* balanced string? */ { // balanced string?
s = matchbalance(ms, s, p + 2); s = matchbalance(ms, s, p + 2);
if (s != NULL) if (s != NULL)
{ {
p += 4; p += 4;
goto init; /* return match(ms, s, p + 4); */ goto init; // return match(ms, s, p + 4);
} /* else fail (s == NULL) */ } // else fail (s == NULL)
break; break;
} }
case 'f': case 'f':
{ /* frontier? */ { // frontier?
const char* ep; const char* ep;
char previous; char previous;
p += 2; p += 2;
if (*p != '[') if (*p != '[')
luaL_error(ms->L, "missing '[' after '%%f' in pattern"); luaL_error(ms->L, "missing '[' after '%%f' in pattern");
ep = classend(ms, p); /* points to what is next */ ep = classend(ms, p); // points to what is next
previous = (s == ms->src_init) ? '\0' : *(s - 1); previous = (s == ms->src_init) ? '\0' : *(s - 1);
if (!matchbracketclass(uchar(previous), p, ep - 1) && matchbracketclass(uchar(*s), p, ep - 1)) if (!matchbracketclass(uchar(previous), p, ep - 1) && matchbracketclass(uchar(*s), p, ep - 1))
{ {
p = ep; p = ep;
goto init; /* return match(ms, s, ep); */ goto init; // return match(ms, s, ep);
} }
s = NULL; /* match failed */ s = NULL; // match failed
break; break;
} }
case '0': case '0':
@ -497,12 +497,12 @@ init: /* using goto's to optimize tail recursion */
case '7': case '7':
case '8': case '8':
case '9': case '9':
{ /* capture results (%0-%9)? */ { // capture results (%0-%9)?
s = match_capture(ms, s, uchar(*(p + 1))); s = match_capture(ms, s, uchar(*(p + 1)));
if (s != NULL) if (s != NULL)
{ {
p += 2; p += 2;
goto init; /* return match(ms, s, p + 2) */ goto init; // return match(ms, s, p + 2)
} }
break; break;
} }
@ -513,48 +513,48 @@ init: /* using goto's to optimize tail recursion */
} }
default: default:
dflt: dflt:
{ /* pattern class plus optional suffix */ { // pattern class plus optional suffix
const char* ep = classend(ms, p); /* points to optional suffix */ const char* ep = classend(ms, p); // points to optional suffix
/* does not match at least once? */ // does not match at least once?
if (!singlematch(ms, s, p, ep)) if (!singlematch(ms, s, p, ep))
{ {
if (*ep == '*' || *ep == '?' || *ep == '-') if (*ep == '*' || *ep == '?' || *ep == '-')
{ /* accept empty? */ { // accept empty?
p = ep + 1; p = ep + 1;
goto init; /* return match(ms, s, ep + 1); */ goto init; // return match(ms, s, ep + 1);
} }
else /* '+' or no suffix */ else // '+' or no suffix
s = NULL; /* fail */ s = NULL; // fail
} }
else else
{ /* matched once */ { // matched once
switch (*ep) switch (*ep)
{ /* handle optional suffix */ { // handle optional suffix
case '?': case '?':
{ /* optional */ { // optional
const char* res; const char* res;
if ((res = match(ms, s + 1, ep + 1)) != NULL) if ((res = match(ms, s + 1, ep + 1)) != NULL)
s = res; s = res;
else else
{ {
p = ep + 1; p = ep + 1;
goto init; /* else return match(ms, s, ep + 1); */ goto init; // else return match(ms, s, ep + 1);
} }
break; break;
} }
case '+': /* 1 or more repetitions */ case '+': // 1 or more repetitions
s++; /* 1 match already done */ s++; // 1 match already done
/* go through */ // go through
case '*': /* 0 or more repetitions */ case '*': // 0 or more repetitions
s = max_expand(ms, s, p, ep); s = max_expand(ms, s, p, ep);
break; break;
case '-': /* 0 or more repetitions (minimum) */ case '-': // 0 or more repetitions (minimum)
s = min_expand(ms, s, p, ep); s = min_expand(ms, s, p, ep);
break; break;
default: /* no suffix */ default: // no suffix
s++; s++;
p = ep; p = ep;
goto init; /* return match(ms, s + 1, ep); */ goto init; // return match(ms, s + 1, ep);
} }
} }
break; break;
@ -568,26 +568,26 @@ init: /* using goto's to optimize tail recursion */
static const char* lmemfind(const char* s1, size_t l1, const char* s2, size_t l2) static const char* lmemfind(const char* s1, size_t l1, const char* s2, size_t l2)
{ {
if (l2 == 0) if (l2 == 0)
return s1; /* empty strings are everywhere */ return s1; // empty strings are everywhere
else if (l2 > l1) else if (l2 > l1)
return NULL; /* avoids a negative `l1' */ return NULL; // avoids a negative `l1'
else else
{ {
const char* init; /* to search for a `*s2' inside `s1' */ const char* init; // to search for a `*s2' inside `s1'
l2--; /* 1st char will be checked by `memchr' */ l2--; // 1st char will be checked by `memchr'
l1 = l1 - l2; /* `s2' cannot be found after that */ l1 = l1 - l2; // `s2' cannot be found after that
while (l1 > 0 && (init = (const char*)memchr(s1, *s2, l1)) != NULL) while (l1 > 0 && (init = (const char*)memchr(s1, *s2, l1)) != NULL)
{ {
init++; /* 1st char is already checked */ init++; // 1st char is already checked
if (memcmp(init, s2 + 1, l2) == 0) if (memcmp(init, s2 + 1, l2) == 0)
return init - 1; return init - 1;
else else
{ /* correct `l1' and `s1' to try again */ { // correct `l1' and `s1' to try again
l1 -= init - s1; l1 -= init - s1;
s1 = init; s1 = init;
} }
} }
return NULL; /* not found */ return NULL; // not found
} }
} }
@ -595,8 +595,8 @@ static void push_onecapture(MatchState* ms, int i, const char* s, const char* e)
{ {
if (i >= ms->level) if (i >= ms->level)
{ {
if (i == 0) /* ms->level == 0, too */ if (i == 0) // ms->level == 0, too
lua_pushlstring(ms->L, s, e - s); /* add whole match */ lua_pushlstring(ms->L, s, e - s); // add whole match
else else
luaL_error(ms->L, "invalid capture index"); luaL_error(ms->L, "invalid capture index");
} }
@ -619,20 +619,20 @@ static int push_captures(MatchState* ms, const char* s, const char* e)
luaL_checkstack(ms->L, nlevels, "too many captures"); luaL_checkstack(ms->L, nlevels, "too many captures");
for (i = 0; i < nlevels; i++) for (i = 0; i < nlevels; i++)
push_onecapture(ms, i, s, e); push_onecapture(ms, i, s, e);
return nlevels; /* number of strings pushed */ return nlevels; // number of strings pushed
} }
/* check whether pattern has no special characters */ // check whether pattern has no special characters
static int nospecials(const char* p, size_t l) static int nospecials(const char* p, size_t l)
{ {
size_t upto = 0; size_t upto = 0;
do do
{ {
if (strpbrk(p + upto, SPECIALS)) if (strpbrk(p + upto, SPECIALS))
return 0; /* pattern has a special character */ return 0; // pattern has a special character
upto += strlen(p + upto) + 1; /* may have more after \0 */ upto += strlen(p + upto) + 1; // may have more after \0
} while (upto <= l); } while (upto <= l);
return 1; /* no special chars found */ return 1; // no special chars found
} }
static void prepstate(MatchState* ms, lua_State* L, const char* s, size_t ls, const char* p, size_t lp) static void prepstate(MatchState* ms, lua_State* L, const char* s, size_t ls, const char* p, size_t lp)
@ -659,14 +659,14 @@ static int str_find_aux(lua_State* L, int find)
if (init < 1) if (init < 1)
init = 1; init = 1;
else if (init > (int)ls + 1) else if (init > (int)ls + 1)
{ /* start after string's end? */ { // start after string's end?
lua_pushnil(L); /* cannot find anything */ lua_pushnil(L); // cannot find anything
return 1; return 1;
} }
/* explicit request or no special characters? */ // explicit request or no special characters?
if (find && (lua_toboolean(L, 4) || nospecials(p, lp))) if (find && (lua_toboolean(L, 4) || nospecials(p, lp)))
{ {
/* do a plain search */ // do a plain search
const char* s2 = lmemfind(s + init - 1, ls - init + 1, p, lp); const char* s2 = lmemfind(s + init - 1, ls - init + 1, p, lp);
if (s2) if (s2)
{ {
@ -683,7 +683,7 @@ static int str_find_aux(lua_State* L, int find)
if (anchor) if (anchor)
{ {
p++; p++;
lp--; /* skip anchor character */ lp--; // skip anchor character
} }
prepstate(&ms, L, s, ls, p, lp); prepstate(&ms, L, s, ls, p, lp);
do do
@ -694,8 +694,8 @@ static int str_find_aux(lua_State* L, int find)
{ {
if (find) if (find)
{ {
lua_pushinteger(L, (int)(s1 - s + 1)); /* start */ lua_pushinteger(L, (int)(s1 - s + 1)); // start
lua_pushinteger(L, (int)(res - s)); /* end */ lua_pushinteger(L, (int)(res - s)); // end
return push_captures(&ms, NULL, 0) + 2; return push_captures(&ms, NULL, 0) + 2;
} }
else else
@ -703,7 +703,7 @@ static int str_find_aux(lua_State* L, int find)
} }
} while (s1++ < ms.src_end && !anchor); } while (s1++ < ms.src_end && !anchor);
} }
lua_pushnil(L); /* not found */ lua_pushnil(L); // not found
return 1; return 1;
} }
@ -733,13 +733,13 @@ static int gmatch_aux(lua_State* L)
{ {
int newstart = (int)(e - s); int newstart = (int)(e - s);
if (e == src) if (e == src)
newstart++; /* empty match? go at least one position */ newstart++; // empty match? go at least one position
lua_pushinteger(L, newstart); lua_pushinteger(L, newstart);
lua_replace(L, lua_upvalueindex(3)); lua_replace(L, lua_upvalueindex(3));
return push_captures(&ms, src, e); return push_captures(&ms, src, e);
} }
} }
return 0; /* not found */ return 0; // not found
} }
static int gmatch(lua_State* L) static int gmatch(lua_State* L)
@ -765,7 +765,7 @@ static void add_s(MatchState* ms, luaL_Buffer* b, const char* s, const char* e)
luaL_addchar(b, news[i]); luaL_addchar(b, news[i]);
else else
{ {
i++; /* skip ESC */ i++; // skip ESC
if (!isdigit(uchar(news[i]))) if (!isdigit(uchar(news[i])))
{ {
if (news[i] != L_ESC) if (news[i] != L_ESC)
@ -777,7 +777,7 @@ static void add_s(MatchState* ms, luaL_Buffer* b, const char* s, const char* e)
else else
{ {
push_onecapture(ms, news[i] - '1', s, e); push_onecapture(ms, news[i] - '1', s, e);
luaL_addvalue(b); /* add capture to accumulated result */ luaL_addvalue(b); // add capture to accumulated result
} }
} }
} }
@ -803,19 +803,19 @@ static void add_value(MatchState* ms, luaL_Buffer* b, const char* s, const char*
break; break;
} }
default: default:
{ /* LUA_TNUMBER or LUA_TSTRING */ { // LUA_TNUMBER or LUA_TSTRING
add_s(ms, b, s, e); add_s(ms, b, s, e);
return; return;
} }
} }
if (!lua_toboolean(L, -1)) if (!lua_toboolean(L, -1))
{ /* nil or false? */ { // nil or false?
lua_pop(L, 1); lua_pop(L, 1);
lua_pushlstring(L, s, e - s); /* keep original text */ lua_pushlstring(L, s, e - s); // keep original text
} }
else if (!lua_isstring(L, -1)) else if (!lua_isstring(L, -1))
luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1)); luaL_error(L, "invalid replacement value (a %s)", luaL_typename(L, -1));
luaL_addvalue(b); /* add result to accumulator */ luaL_addvalue(b); // add result to accumulator
} }
static int str_gsub(lua_State* L) static int str_gsub(lua_State* L)
@ -834,7 +834,7 @@ static int str_gsub(lua_State* L)
if (anchor) if (anchor)
{ {
p++; p++;
lp--; /* skip anchor character */ lp--; // skip anchor character
} }
prepstate(&ms, L, src, srcl, p, lp); prepstate(&ms, L, src, srcl, p, lp);
while (n < max_s) while (n < max_s)
@ -847,8 +847,8 @@ static int str_gsub(lua_State* L)
n++; n++;
add_value(&ms, &b, src, e, tr); add_value(&ms, &b, src, e, tr);
} }
if (e && e > src) /* non empty match? */ if (e && e > src) // non empty match?
src = e; /* skip it */ src = e; // skip it
else if (src < ms.src_end) else if (src < ms.src_end)
luaL_addchar(&b, *src++); luaL_addchar(&b, *src++);
else else
@ -858,17 +858,17 @@ static int str_gsub(lua_State* L)
} }
luaL_addlstring(&b, src, ms.src_end - src); luaL_addlstring(&b, src, ms.src_end - src);
luaL_pushresult(&b); luaL_pushresult(&b);
lua_pushinteger(L, n); /* number of substitutions */ lua_pushinteger(L, n); // number of substitutions
return 2; return 2;
} }
/* }====================================================== */ // }======================================================
/* valid flags in a format specification */ // valid flags in a format specification
#define FLAGS "-+ #0" #define FLAGS "-+ #0"
/* maximum size of each formatted item (> len(format('%99.99f', -1e308))) */ // maximum size of each formatted item (> len(format('%99.99f', -1e308)))
#define MAX_ITEM 512 #define MAX_ITEM 512
/* maximum size of each format specification (such as '%-099.99d') */ // maximum size of each format specification (such as '%-099.99d')
#define MAX_FORMAT 32 #define MAX_FORMAT 32
static void addquoted(lua_State* L, luaL_Buffer* b, int arg) static void addquoted(lua_State* L, luaL_Buffer* b, int arg)
@ -916,20 +916,20 @@ static const char* scanformat(lua_State* L, const char* strfrmt, char* form, siz
{ {
const char* p = strfrmt; const char* p = strfrmt;
while (*p != '\0' && strchr(FLAGS, *p) != NULL) while (*p != '\0' && strchr(FLAGS, *p) != NULL)
p++; /* skip flags */ p++; // skip flags
if ((size_t)(p - strfrmt) >= sizeof(FLAGS)) if ((size_t)(p - strfrmt) >= sizeof(FLAGS))
luaL_error(L, "invalid format (repeated flags)"); luaL_error(L, "invalid format (repeated flags)");
if (isdigit(uchar(*p))) if (isdigit(uchar(*p)))
p++; /* skip width */ p++; // skip width
if (isdigit(uchar(*p))) if (isdigit(uchar(*p)))
p++; /* (2 digits at most) */ p++; // (2 digits at most)
if (*p == '.') if (*p == '.')
{ {
p++; p++;
if (isdigit(uchar(*p))) if (isdigit(uchar(*p)))
p++; /* skip precision */ p++; // skip precision
if (isdigit(uchar(*p))) if (isdigit(uchar(*p)))
p++; /* (2 digits at most) */ p++; // (2 digits at most)
} }
if (isdigit(uchar(*p))) if (isdigit(uchar(*p)))
luaL_error(L, "invalid format (width or precision too long)"); luaL_error(L, "invalid format (width or precision too long)");
@ -967,11 +967,11 @@ static int str_format(lua_State* L)
if (*strfrmt != L_ESC) if (*strfrmt != L_ESC)
luaL_addchar(&b, *strfrmt++); luaL_addchar(&b, *strfrmt++);
else if (*++strfrmt == L_ESC) else if (*++strfrmt == L_ESC)
luaL_addchar(&b, *strfrmt++); /* %% */ luaL_addchar(&b, *strfrmt++); // %%
else else
{ /* format item */ { // format item
char form[MAX_FORMAT]; /* to store the format (`%...') */ char form[MAX_FORMAT]; // to store the format (`%...')
char buff[MAX_ITEM]; /* to store the formatted item */ char buff[MAX_ITEM]; // to store the formatted item
if (++arg > top) if (++arg > top)
luaL_error(L, "missing argument #%d", arg); luaL_error(L, "missing argument #%d", arg);
size_t formatItemSize = 0; size_t formatItemSize = 0;
@ -1014,7 +1014,7 @@ static int str_format(lua_State* L)
case 'q': case 'q':
{ {
addquoted(L, &b, arg); addquoted(L, &b, arg);
continue; /* skip the 'addsize' at the end */ continue; // skip the 'addsize' at the end
} }
case 's': case 's':
{ {
@ -1026,7 +1026,7 @@ static int str_format(lua_State* L)
keep original string */ keep original string */
lua_pushvalue(L, arg); lua_pushvalue(L, arg);
luaL_addvalue(&b); luaL_addvalue(&b);
continue; /* skip the `addsize' at the end */ continue; // skip the `addsize' at the end
} }
else else
{ {
@ -1037,25 +1037,19 @@ static int str_format(lua_State* L)
case '*': case '*':
{ {
if (!FFlag::LuauTostringFormatSpecifier) if (!FFlag::LuauTostringFormatSpecifier)
{
luaL_error(L, "invalid option '%%*' to 'format'"); luaL_error(L, "invalid option '%%*' to 'format'");
break;
}
if (formatItemSize != 1) if (formatItemSize != 1)
{
luaL_error(L, "'%%*' does not take a form"); luaL_error(L, "'%%*' does not take a form");
}
size_t length; size_t length;
const char* string = luaL_tolstring(L, arg, &length); const char* string = luaL_tolstring(L, arg, &length);
luaL_addlstring(&b, string, length); luaL_addlstring(&b, string, length);
continue; // skip the `addsize' at the end
continue; /* skip the `addsize' at the end */
} }
default: default:
{ /* also treat cases `pnLlh' */ { // also treat cases `pnLlh'
luaL_error(L, "invalid option '%%%c' to 'format'", *(strfrmt - 1)); luaL_error(L, "invalid option '%%%c' to 'format'", *(strfrmt - 1));
} }
} }
@ -1120,31 +1114,31 @@ static int str_split(lua_State* L)
** ======================================================= ** =======================================================
*/ */
/* value used for padding */ // value used for padding
#if !defined(LUAL_PACKPADBYTE) #if !defined(LUAL_PACKPADBYTE)
#define LUAL_PACKPADBYTE 0x00 #define LUAL_PACKPADBYTE 0x00
#endif #endif
/* maximum size for the binary representation of an integer */ // maximum size for the binary representation of an integer
#define MAXINTSIZE 16 #define MAXINTSIZE 16
/* number of bits in a character */ // number of bits in a character
#define NB CHAR_BIT #define NB CHAR_BIT
/* mask for one character (NB 1's) */ // mask for one character (NB 1's)
#define MC ((1 << NB) - 1) #define MC ((1 << NB) - 1)
/* internal size of integers used for pack/unpack */ // internal size of integers used for pack/unpack
#define SZINT (int)sizeof(long long) #define SZINT (int)sizeof(long long)
/* dummy union to get native endianness */ // dummy union to get native endianness
static const union static const union
{ {
int dummy; int dummy;
char little; /* true iff machine is little endian */ char little; // true iff machine is little endian
} nativeendian = {1}; } nativeendian = {1};
/* assume we need to align for double & pointers */ // assume we need to align for double & pointers
#define MAXALIGN 8 #define MAXALIGN 8
/* /*
@ -1155,7 +1149,7 @@ typedef union Ftypes
float f; float f;
double d; double d;
double n; double n;
char buff[5 * sizeof(double)]; /* enough for any float type */ char buff[5 * sizeof(double)]; // enough for any float type
} Ftypes; } Ftypes;
/* /*
@ -1173,15 +1167,15 @@ typedef struct Header
*/ */
typedef enum KOption typedef enum KOption
{ {
Kint, /* signed integers */ Kint, // signed integers
Kuint, /* unsigned integers */ Kuint, // unsigned integers
Kfloat, /* floating-point numbers */ Kfloat, // floating-point numbers
Kchar, /* fixed-length strings */ Kchar, // fixed-length strings
Kstring, /* strings with prefixed length */ Kstring, // strings with prefixed length
Kzstr, /* zero-terminated strings */ Kzstr, // zero-terminated strings
Kpadding, /* padding */ Kpadding, // padding
Kpaddalign, /* padding for alignment */ Kpaddalign, // padding for alignment
Knop /* no-op (configuration or spaces) */ Knop // no-op (configuration or spaces)
} KOption; } KOption;
/* /*
@ -1195,8 +1189,8 @@ static int digit(int c)
static int getnum(Header* h, const char** fmt, int df) static int getnum(Header* h, const char** fmt, int df)
{ {
if (!digit(**fmt)) /* no number? */ if (!digit(**fmt)) // no number?
return df; /* return default value */ return df; // return default value
else else
{ {
int a = 0; int a = 0;
@ -1238,7 +1232,7 @@ static void initheader(lua_State* L, Header* h)
static KOption getoption(Header* h, const char** fmt, int* size) static KOption getoption(Header* h, const char** fmt, int* size)
{ {
int opt = *((*fmt)++); int opt = *((*fmt)++);
*size = 0; /* default */ *size = 0; // default
switch (opt) switch (opt)
{ {
case 'b': case 'b':
@ -1330,19 +1324,19 @@ static KOption getoption(Header* h, const char** fmt, int* size)
static KOption getdetails(Header* h, size_t totalsize, const char** fmt, int* psize, int* ntoalign) static KOption getdetails(Header* h, size_t totalsize, const char** fmt, int* psize, int* ntoalign)
{ {
KOption opt = getoption(h, fmt, psize); KOption opt = getoption(h, fmt, psize);
int align = *psize; /* usually, alignment follows size */ int align = *psize; // usually, alignment follows size
if (opt == Kpaddalign) if (opt == Kpaddalign)
{ /* 'X' gets alignment from following option */ { // 'X' gets alignment from following option
if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0) if (**fmt == '\0' || getoption(h, fmt, &align) == Kchar || align == 0)
luaL_argerror(h->L, 1, "invalid next option for option 'X'"); luaL_argerror(h->L, 1, "invalid next option for option 'X'");
} }
if (align <= 1 || opt == Kchar) /* need no alignment? */ if (align <= 1 || opt == Kchar) // need no alignment?
*ntoalign = 0; *ntoalign = 0;
else else
{ {
if (align > h->maxalign) /* enforce maximum alignment */ if (align > h->maxalign) // enforce maximum alignment
align = h->maxalign; align = h->maxalign;
if ((align & (align - 1)) != 0) /* is 'align' not a power of 2? */ if ((align & (align - 1)) != 0) // is 'align' not a power of 2?
luaL_argerror(h->L, 1, "format asks for alignment not power of 2"); luaL_argerror(h->L, 1, "format asks for alignment not power of 2");
*ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1); *ntoalign = (align - (int)(totalsize & (align - 1))) & (align - 1);
} }
@ -1360,18 +1354,18 @@ static void packint(luaL_Buffer* b, unsigned long long n, int islittle, int size
LUAU_ASSERT(size <= MAXINTSIZE); LUAU_ASSERT(size <= MAXINTSIZE);
char buff[MAXINTSIZE]; char buff[MAXINTSIZE];
int i; int i;
buff[islittle ? 0 : size - 1] = (char)(n & MC); /* first byte */ buff[islittle ? 0 : size - 1] = (char)(n & MC); // first byte
for (i = 1; i < size; i++) for (i = 1; i < size; i++)
{ {
n >>= NB; n >>= NB;
buff[islittle ? i : size - 1 - i] = (char)(n & MC); buff[islittle ? i : size - 1 - i] = (char)(n & MC);
} }
if (neg && size > SZINT) if (neg && size > SZINT)
{ /* negative number need sign extension? */ { // negative number need sign extension?
for (i = SZINT; i < size; i++) /* correct extra bytes */ for (i = SZINT; i < size; i++) // correct extra bytes
buff[islittle ? i : size - 1 - i] = (char)MC; buff[islittle ? i : size - 1 - i] = (char)MC;
} }
luaL_addlstring(b, buff, size); /* add result to buffer */ luaL_addlstring(b, buff, size); // add result to buffer
} }
/* /*
@ -1397,11 +1391,11 @@ static int str_pack(lua_State* L)
{ {
luaL_Buffer b; luaL_Buffer b;
Header h; Header h;
const char* fmt = luaL_checkstring(L, 1); /* format string */ const char* fmt = luaL_checkstring(L, 1); // format string
int arg = 1; /* current argument to pack */ int arg = 1; // current argument to pack
size_t totalsize = 0; /* accumulate total size of result */ size_t totalsize = 0; // accumulate total size of result
initheader(L, &h); initheader(L, &h);
lua_pushnil(L); /* mark to separate arguments from string buffer */ lua_pushnil(L); // mark to separate arguments from string buffer
luaL_buffinit(L, &b); luaL_buffinit(L, &b);
while (*fmt != '\0') while (*fmt != '\0')
{ {
@ -1409,15 +1403,15 @@ static int str_pack(lua_State* L)
KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
totalsize += ntoalign + size; totalsize += ntoalign + size;
while (ntoalign-- > 0) while (ntoalign-- > 0)
luaL_addchar(&b, LUAL_PACKPADBYTE); /* fill alignment */ luaL_addchar(&b, LUAL_PACKPADBYTE); // fill alignment
arg++; arg++;
switch (opt) switch (opt)
{ {
case Kint: case Kint:
{ /* signed integers */ { // signed integers
long long n = (long long)luaL_checknumber(L, arg); long long n = (long long)luaL_checknumber(L, arg);
if (size < SZINT) if (size < SZINT)
{ /* need overflow check? */ { // need overflow check?
long long lim = (long long)1 << ((size * NB) - 1); long long lim = (long long)1 << ((size * NB) - 1);
luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow"); luaL_argcheck(L, -lim <= n && n < lim, arg, "integer overflow");
} }
@ -1425,64 +1419,64 @@ static int str_pack(lua_State* L)
break; break;
} }
case Kuint: case Kuint:
{ /* unsigned integers */ { // unsigned integers
long long n = (long long)luaL_checknumber(L, arg); long long n = (long long)luaL_checknumber(L, arg);
if (size < SZINT) /* need overflow check? */ if (size < SZINT) // need overflow check?
luaL_argcheck(L, (unsigned long long)n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow"); luaL_argcheck(L, (unsigned long long)n < ((unsigned long long)1 << (size * NB)), arg, "unsigned overflow");
packint(&b, (unsigned long long)n, h.islittle, size, 0); packint(&b, (unsigned long long)n, h.islittle, size, 0);
break; break;
} }
case Kfloat: case Kfloat:
{ /* floating-point options */ { // floating-point options
volatile Ftypes u; volatile Ftypes u;
char buff[MAXINTSIZE]; char buff[MAXINTSIZE];
double n = luaL_checknumber(L, arg); /* get argument */ double n = luaL_checknumber(L, arg); // get argument
if (size == sizeof(u.f)) if (size == sizeof(u.f))
u.f = (float)n; /* copy it into 'u' */ u.f = (float)n; // copy it into 'u'
else if (size == sizeof(u.d)) else if (size == sizeof(u.d))
u.d = (double)n; u.d = (double)n;
else else
u.n = n; u.n = n;
/* move 'u' to final result, correcting endianness if needed */ // move 'u' to final result, correcting endianness if needed
copywithendian(buff, u.buff, size, h.islittle); copywithendian(buff, u.buff, size, h.islittle);
luaL_addlstring(&b, buff, size); luaL_addlstring(&b, buff, size);
break; break;
} }
case Kchar: case Kchar:
{ /* fixed-size string */ { // fixed-size string
size_t len; size_t len;
const char* s = luaL_checklstring(L, arg, &len); const char* s = luaL_checklstring(L, arg, &len);
luaL_argcheck(L, len <= (size_t)size, arg, "string longer than given size"); luaL_argcheck(L, len <= (size_t)size, arg, "string longer than given size");
luaL_addlstring(&b, s, len); /* add string */ luaL_addlstring(&b, s, len); // add string
while (len++ < (size_t)size) /* pad extra space */ while (len++ < (size_t)size) // pad extra space
luaL_addchar(&b, LUAL_PACKPADBYTE); luaL_addchar(&b, LUAL_PACKPADBYTE);
break; break;
} }
case Kstring: case Kstring:
{ /* strings with length count */ { // strings with length count
size_t len; size_t len;
const char* s = luaL_checklstring(L, arg, &len); const char* s = luaL_checklstring(L, arg, &len);
luaL_argcheck(L, size >= (int)sizeof(size_t) || len < ((size_t)1 << (size * NB)), arg, "string length does not fit in given size"); luaL_argcheck(L, size >= (int)sizeof(size_t) || len < ((size_t)1 << (size * NB)), arg, "string length does not fit in given size");
packint(&b, len, h.islittle, size, 0); /* pack length */ packint(&b, len, h.islittle, size, 0); // pack length
luaL_addlstring(&b, s, len); luaL_addlstring(&b, s, len);
totalsize += len; totalsize += len;
break; break;
} }
case Kzstr: case Kzstr:
{ /* zero-terminated string */ { // zero-terminated string
size_t len; size_t len;
const char* s = luaL_checklstring(L, arg, &len); const char* s = luaL_checklstring(L, arg, &len);
luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros"); luaL_argcheck(L, strlen(s) == len, arg, "string contains zeros");
luaL_addlstring(&b, s, len); luaL_addlstring(&b, s, len);
luaL_addchar(&b, '\0'); /* add zero at the end */ luaL_addchar(&b, '\0'); // add zero at the end
totalsize += len + 1; totalsize += len + 1;
break; break;
} }
case Kpadding: case Kpadding:
luaL_addchar(&b, LUAL_PACKPADBYTE); /* FALLTHROUGH */ luaL_addchar(&b, LUAL_PACKPADBYTE); // FALLTHROUGH
case Kpaddalign: case Kpaddalign:
case Knop: case Knop:
arg--; /* undo increment */ arg--; // undo increment
break; break;
} }
} }
@ -1493,15 +1487,15 @@ static int str_pack(lua_State* L)
static int str_packsize(lua_State* L) static int str_packsize(lua_State* L)
{ {
Header h; Header h;
const char* fmt = luaL_checkstring(L, 1); /* format string */ const char* fmt = luaL_checkstring(L, 1); // format string
int totalsize = 0; /* accumulate total size of result */ int totalsize = 0; // accumulate total size of result
initheader(L, &h); initheader(L, &h);
while (*fmt != '\0') while (*fmt != '\0')
{ {
int size, ntoalign; int size, ntoalign;
KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign); KOption opt = getdetails(&h, totalsize, &fmt, &size, &ntoalign);
luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1, "variable-length format"); luaL_argcheck(L, opt != Kstring && opt != Kzstr, 1, "variable-length format");
size += ntoalign; /* total space used by option */ size += ntoalign; // total space used by option
luaL_argcheck(L, totalsize <= MAXSSIZE - size, 1, "format result too large"); luaL_argcheck(L, totalsize <= MAXSSIZE - size, 1, "format result too large");
totalsize += size; totalsize += size;
} }
@ -1528,15 +1522,15 @@ static long long unpackint(lua_State* L, const char* str, int islittle, int size
res |= (unsigned char)str[islittle ? i : size - 1 - i]; res |= (unsigned char)str[islittle ? i : size - 1 - i];
} }
if (size < SZINT) if (size < SZINT)
{ /* real size smaller than int? */ { // real size smaller than int?
if (issigned) if (issigned)
{ /* needs sign extension? */ { // needs sign extension?
unsigned long long mask = (unsigned long long)1 << (size * NB - 1); unsigned long long mask = (unsigned long long)1 << (size * NB - 1);
res = ((res ^ mask) - mask); /* do sign extension */ res = ((res ^ mask) - mask); // do sign extension
} }
} }
else if (size > SZINT) else if (size > SZINT)
{ /* must check unread bytes */ { // must check unread bytes
int mask = (!issigned || (long long)res >= 0) ? 0 : MC; int mask = (!issigned || (long long)res >= 0) ? 0 : MC;
for (i = limit; i < size; i++) for (i = limit; i < size; i++)
{ {
@ -1556,7 +1550,7 @@ static int str_unpack(lua_State* L)
int pos = posrelat(luaL_optinteger(L, 3, 1), ld) - 1; int pos = posrelat(luaL_optinteger(L, 3, 1), ld) - 1;
if (pos < 0) if (pos < 0)
pos = 0; pos = 0;
int n = 0; /* number of results */ int n = 0; // number of results
luaL_argcheck(L, size_t(pos) <= ld, 3, "initial position out of string"); luaL_argcheck(L, size_t(pos) <= ld, 3, "initial position out of string");
initheader(L, &h); initheader(L, &h);
while (*fmt != '\0') while (*fmt != '\0')
@ -1564,8 +1558,8 @@ static int str_unpack(lua_State* L)
int size, ntoalign; int size, ntoalign;
KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign); KOption opt = getdetails(&h, pos, &fmt, &size, &ntoalign);
luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2, "data string too short"); luaL_argcheck(L, (size_t)ntoalign + size <= ld - pos, 2, "data string too short");
pos += ntoalign; /* skip alignment */ pos += ntoalign; // skip alignment
/* stack space for item + next position */ // stack space for item + next position
luaL_checkstack(L, 2, "too many results"); luaL_checkstack(L, 2, "too many results");
n++; n++;
switch (opt) switch (opt)
@ -1606,7 +1600,7 @@ static int str_unpack(lua_State* L)
size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0); size_t len = (size_t)unpackint(L, data + pos, h.islittle, size, 0);
luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short"); luaL_argcheck(L, len <= ld - pos - size, 2, "data string too short");
lua_pushlstring(L, data + pos + size, len); lua_pushlstring(L, data + pos + size, len);
pos += (int)len; /* skip string */ pos += (int)len; // skip string
break; break;
} }
case Kzstr: case Kzstr:
@ -1614,22 +1608,22 @@ static int str_unpack(lua_State* L)
size_t len = strlen(data + pos); size_t len = strlen(data + pos);
luaL_argcheck(L, pos + len < ld, 2, "unfinished string for format 'z'"); luaL_argcheck(L, pos + len < ld, 2, "unfinished string for format 'z'");
lua_pushlstring(L, data + pos, len); lua_pushlstring(L, data + pos, len);
pos += (int)len + 1; /* skip string plus final '\0' */ pos += (int)len + 1; // skip string plus final '\0'
break; break;
} }
case Kpaddalign: case Kpaddalign:
case Kpadding: case Kpadding:
case Knop: case Knop:
n--; /* undo increment */ n--; // undo increment
break; break;
} }
pos += size; pos += size;
} }
lua_pushinteger(L, pos + 1); /* next position */ lua_pushinteger(L, pos + 1); // next position
return n + 1; return n + 1;
} }
/* }====================================================== */ // }======================================================
static const luaL_Reg strlib[] = { static const luaL_Reg strlib[] = {
{"byte", str_byte}, {"byte", str_byte},
@ -1654,14 +1648,14 @@ static const luaL_Reg strlib[] = {
static void createmetatable(lua_State* L) static void createmetatable(lua_State* L)
{ {
lua_createtable(L, 0, 1); /* create metatable for strings */ lua_createtable(L, 0, 1); // create metatable for strings
lua_pushliteral(L, ""); /* dummy string */ lua_pushliteral(L, ""); // dummy string
lua_pushvalue(L, -2); lua_pushvalue(L, -2);
lua_setmetatable(L, -2); /* set string metatable */ lua_setmetatable(L, -2); // set string metatable
lua_pop(L, 1); /* pop dummy string */ lua_pop(L, 1); // pop dummy string
lua_pushvalue(L, -2); /* string library... */ lua_pushvalue(L, -2); // string library...
lua_setfield(L, -2, "__index"); /* ...is the __index metamethod */ lua_setfield(L, -2, "__index"); // ...is the __index metamethod
lua_pop(L, 1); /* pop metatable */ lua_pop(L, 1); // pop metatable
} }
/* /*

View File

@ -46,8 +46,8 @@ static_assert(TKey{{NULL}, {0}, LUA_TNIL, -(MAXSIZE - 1)}.next == -(MAXSIZE - 1)
// empty hash data points to dummynode so that we can always dereference it // empty hash data points to dummynode so that we can always dereference it
const LuaNode luaH_dummynode = { const LuaNode luaH_dummynode = {
{{NULL}, {0}, LUA_TNIL}, /* value */ {{NULL}, {0}, LUA_TNIL}, // value
{{NULL}, {0}, LUA_TNIL, 0} /* key */ {{NULL}, {0}, LUA_TNIL, 0} // key
}; };
#define dummynode (&luaH_dummynode) #define dummynode (&luaH_dummynode)
@ -170,52 +170,52 @@ static int findindex(lua_State* L, Table* t, StkId key)
{ {
int i; int i;
if (ttisnil(key)) if (ttisnil(key))
return -1; /* first iteration */ return -1; // first iteration
i = ttisnumber(key) ? arrayindex(nvalue(key)) : -1; i = ttisnumber(key) ? arrayindex(nvalue(key)) : -1;
if (0 < i && i <= t->sizearray) /* is `key' inside array part? */ if (0 < i && i <= t->sizearray) // is `key' inside array part?
return i - 1; /* yes; that's the index (corrected to C) */ return i - 1; // yes; that's the index (corrected to C)
else else
{ {
LuaNode* n = mainposition(t, key); LuaNode* n = mainposition(t, key);
for (;;) for (;;)
{ /* check whether `key' is somewhere in the chain */ { // check whether `key' is somewhere in the chain
/* key may be dead already, but it is ok to use it in `next' */ // key may be dead already, but it is ok to use it in `next'
if (luaO_rawequalKey(gkey(n), key) || (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) && gcvalue(gkey(n)) == gcvalue(key))) if (luaO_rawequalKey(gkey(n), key) || (ttype(gkey(n)) == LUA_TDEADKEY && iscollectable(key) && gcvalue(gkey(n)) == gcvalue(key)))
{ {
i = cast_int(n - gnode(t, 0)); /* key index in hash table */ i = cast_int(n - gnode(t, 0)); // key index in hash table
/* hash elements are numbered after array ones */ // hash elements are numbered after array ones
return i + t->sizearray; return i + t->sizearray;
} }
if (gnext(n) == 0) if (gnext(n) == 0)
break; break;
n += gnext(n); n += gnext(n);
} }
luaG_runerror(L, "invalid key to 'next'"); /* key not found */ luaG_runerror(L, "invalid key to 'next'"); // key not found
} }
} }
int luaH_next(lua_State* L, Table* t, StkId key) int luaH_next(lua_State* L, Table* 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++)
{ /* try first array part */ { // try first array part
if (!ttisnil(&t->array[i])) if (!ttisnil(&t->array[i]))
{ /* a non-nil value? */ { // a non-nil value?
setnvalue(key, cast_num(i + 1)); setnvalue(key, cast_num(i + 1));
setobj2s(L, key + 1, &t->array[i]); setobj2s(L, key + 1, &t->array[i]);
return 1; return 1;
} }
} }
for (i -= t->sizearray; i < sizenode(t); i++) for (i -= t->sizearray; i < sizenode(t); i++)
{ /* then hash part */ { // then hash part
if (!ttisnil(gval(gnode(t, i)))) if (!ttisnil(gval(gnode(t, i))))
{ /* a non-nil value? */ { // a non-nil value?
getnodekey(L, key, gnode(t, i)); getnodekey(L, key, gnode(t, i));
setobj2s(L, key + 1, gval(gnode(t, i))); setobj2s(L, key + 1, gval(gnode(t, i)));
return 1; return 1;
} }
} }
return 0; /* no more elements */ return 0; // no more elements
} }
/* /*
@ -235,23 +235,23 @@ int luaH_next(lua_State* L, Table* t, StkId key)
static int computesizes(int nums[], int* narray) static int computesizes(int nums[], int* narray)
{ {
int i; int i;
int twotoi; /* 2^i */ int twotoi; // 2^i
int a = 0; /* number of elements smaller than 2^i */ int a = 0; // number of elements smaller than 2^i
int na = 0; /* number of elements to go to array part */ int na = 0; // number of elements to go to array part
int n = 0; /* optimal size for array part */ int n = 0; // optimal size for array part
for (i = 0, twotoi = 1; twotoi / 2 < *narray; i++, twotoi *= 2) for (i = 0, twotoi = 1; twotoi / 2 < *narray; i++, twotoi *= 2)
{ {
if (nums[i] > 0) if (nums[i] > 0)
{ {
a += nums[i]; a += nums[i];
if (a > twotoi / 2) if (a > twotoi / 2)
{ /* more than half elements present? */ { // more than half elements present?
n = twotoi; /* optimal size (till now) */ n = twotoi; // optimal size (till now)
na = a; /* all elements smaller than n will go to array part */ na = a; // all elements smaller than n will go to array part
} }
} }
if (a == *narray) if (a == *narray)
break; /* all elements already counted */ break; // all elements already counted
} }
*narray = n; *narray = n;
LUAU_ASSERT(*narray / 2 <= na && na <= *narray); LUAU_ASSERT(*narray / 2 <= na && na <= *narray);
@ -262,8 +262,8 @@ static int countint(double key, int* nums)
{ {
int k = arrayindex(key); int k = arrayindex(key);
if (0 < k && k <= MAXSIZE) if (0 < k && k <= MAXSIZE)
{ /* is `key' an appropriate array index? */ { // is `key' an appropriate array index?
nums[ceillog2(k)]++; /* count as such */ nums[ceillog2(k)]++; // count as such
return 1; return 1;
} }
else else
@ -273,20 +273,20 @@ static int countint(double key, int* nums)
static int numusearray(const Table* t, int* nums) static int numusearray(const Table* t, int* nums)
{ {
int lg; int lg;
int ttlg; /* 2^lg */ int ttlg; // 2^lg
int ause = 0; /* summation of `nums' */ int ause = 0; // summation of `nums'
int i = 1; /* count to traverse all array keys */ int i = 1; // count to traverse all array keys
for (lg = 0, ttlg = 1; lg <= MAXBITS; lg++, ttlg *= 2) for (lg = 0, ttlg = 1; lg <= MAXBITS; lg++, ttlg *= 2)
{ /* for each slice */ { // for each slice
int lc = 0; /* counter */ int lc = 0; // counter
int lim = ttlg; int lim = ttlg;
if (lim > t->sizearray) if (lim > t->sizearray)
{ {
lim = t->sizearray; /* adjust upper limit */ lim = t->sizearray; // adjust upper limit
if (i > lim) if (i > lim)
break; /* no more elements to count */ break; // no more elements to count
} }
/* count elements in range (2^(lg-1), 2^lg] */ // count elements in range (2^(lg-1), 2^lg]
for (; i <= lim; i++) for (; i <= lim; i++)
{ {
if (!ttisnil(&t->array[i - 1])) if (!ttisnil(&t->array[i - 1]))
@ -300,8 +300,8 @@ static int numusearray(const Table* t, int* nums)
static int numusehash(const Table* t, int* nums, int* pnasize) static int numusehash(const Table* 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'
int i = sizenode(t); int i = sizenode(t);
while (i--) while (i--)
{ {
@ -332,8 +332,8 @@ static void setnodevector(lua_State* L, Table* t, int size)
{ {
int lsize; int lsize;
if (size == 0) if (size == 0)
{ /* no elements to hash part? */ { // no elements to hash part?
t->node = cast_to(LuaNode*, dummynode); /* use common `dummynode' */ t->node = cast_to(LuaNode*, dummynode); // use common `dummynode'
lsize = 0; lsize = 0;
} }
else else
@ -354,7 +354,7 @@ static void setnodevector(lua_State* L, Table* t, int size)
} }
t->lsizenode = cast_byte(lsize); t->lsizenode = cast_byte(lsize);
t->nodemask8 = cast_byte((1 << lsize) - 1); t->nodemask8 = cast_byte((1 << lsize) - 1);
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, Table* t, const TValue* key);
@ -379,17 +379,17 @@ static void resize(lua_State* L, Table* t, int nasize, int nhsize)
luaG_runerror(L, "table overflow"); luaG_runerror(L, "table overflow");
int oldasize = t->sizearray; int oldasize = t->sizearray;
int oldhsize = t->lsizenode; int oldhsize = t->lsizenode;
LuaNode* nold = t->node; /* save old hash ... */ LuaNode* nold = t->node; // save old hash ...
if (nasize > oldasize) /* array part must grow? */ if (nasize > oldasize) // array part must grow?
setarrayvector(L, t, nasize); setarrayvector(L, t, nasize);
/* create new hash part with appropriate size */ // create new hash part with appropriate size
setnodevector(L, t, nhsize); setnodevector(L, t, nhsize);
/* used for the migration check at the end */ // used for the migration check at the end
LuaNode* nnew = t->node; LuaNode* nnew = t->node;
if (nasize < oldasize) if (nasize < oldasize)
{ /* array part must shrink? */ { // array part must shrink?
t->sizearray = nasize; t->sizearray = nasize;
/* re-insert elements from vanishing slice */ // re-insert elements from vanishing slice
for (int i = nasize; i < oldasize; i++) for (int i = nasize; i < oldasize; i++)
{ {
if (!ttisnil(&t->array[i])) if (!ttisnil(&t->array[i]))
@ -399,12 +399,12 @@ static void resize(lua_State* L, Table* t, int nasize, int nhsize)
setobjt2t(L, newkey(L, t, &ok), &t->array[i]); setobjt2t(L, newkey(L, t, &ok), &t->array[i]);
} }
} }
/* shrink array */ // shrink array
luaM_reallocarray(L, t->array, oldasize, nasize, TValue, t->memcat); luaM_reallocarray(L, t->array, oldasize, nasize, TValue, t->memcat);
} }
/* used for the migration check at the end */ // used for the migration check at the end
TValue* anew = t->array; TValue* anew = t->array;
/* re-insert elements from hash part */ // re-insert elements from hash part
for (int i = twoto(oldhsize) - 1; i >= 0; i--) for (int i = twoto(oldhsize) - 1; i >= 0; i--)
{ {
LuaNode* old = nold + i; LuaNode* old = nold + i;
@ -416,19 +416,19 @@ static void resize(lua_State* L, Table* t, int nasize, int nhsize)
} }
} }
/* make sure we haven't recursively rehashed during element migration */ // make sure we haven't recursively rehashed during element migration
LUAU_ASSERT(nnew == t->node); LUAU_ASSERT(nnew == t->node);
LUAU_ASSERT(anew == t->array); LUAU_ASSERT(anew == t->array);
if (nold != dummynode) if (nold != dummynode)
luaM_freearray(L, nold, twoto(oldhsize), LuaNode, t->memcat); /* free old array */ luaM_freearray(L, nold, twoto(oldhsize), LuaNode, t->memcat); // free old array
} }
static int adjustasize(Table* t, int size, const TValue* ek) static int adjustasize(Table* 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;
/* move the array size up until the boundary is guaranteed to be inside the array part */ // move the array size up until the boundary is guaranteed to be inside the array part
while (size + 1 == ekindex || (tbound && !ttisnil(luaH_getnum(t, size + 1)))) while (size + 1 == ekindex || (tbound && !ttisnil(luaH_getnum(t, size + 1))))
size++; size++;
return size; return size;
@ -448,22 +448,22 @@ void luaH_resizehash(lua_State* L, Table* t, int nhsize)
static void rehash(lua_State* L, Table* t, const TValue* ek) static void rehash(lua_State* L, Table* 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++)
nums[i] = 0; /* reset counts */ nums[i] = 0; // reset counts
int nasize = numusearray(t, nums); /* count keys in array part */ int nasize = numusearray(t, nums); // count keys in array part
int totaluse = nasize; /* all those keys are integer keys */ int totaluse = nasize; // all those keys are integer keys
totaluse += numusehash(t, nums, &nasize); /* count keys in hash part */ totaluse += numusehash(t, nums, &nasize); // count keys in hash part
/* count extra key */ // count extra key
if (ttisnumber(ek)) if (ttisnumber(ek))
nasize += countint(nvalue(ek), nums); nasize += countint(nvalue(ek), nums);
totaluse++; totaluse++;
/* compute new size for array part */ // compute new size for array part
int na = computesizes(nums, &nasize); int na = computesizes(nums, &nasize);
int nh = totaluse - na; int nh = totaluse - na;
/* enforce the boundary invariant; for performance, only do hash lookups if we must */ // enforce the boundary invariant; for performance, only do hash lookups if we must
nasize = adjustasize(t, nasize, ek); nasize = adjustasize(t, nasize, ek);
/* resize the table to new computed sizes */ // resize the table to new computed sizes
resize(L, t, nasize, nh); resize(L, t, nasize, nh);
} }
@ -511,7 +511,7 @@ static LuaNode* getfreepos(Table* t)
if (ttisnil(gkey(n))) if (ttisnil(gkey(n)))
return n; return n;
} }
return NULL; /* could not find a free place */ return NULL; // could not find a free place
} }
/* /*
@ -523,24 +523,24 @@ static LuaNode* getfreepos(Table* t)
*/ */
static TValue* newkey(lua_State* L, Table* t, const TValue* key) static TValue* newkey(lua_State* L, Table* 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)
{ {
rehash(L, t, key); /* grow table */ rehash(L, t, key); // grow table
/* after rehash, numeric keys might be located in the new array part, but won't be found in the node part */ // after rehash, numeric keys might be located in the new array part, but won't be found in the node part
return arrayornewkey(L, t, key); return arrayornewkey(L, t, key);
} }
LuaNode* mp = mainposition(t, key); LuaNode* mp = mainposition(t, key);
if (!ttisnil(gval(mp)) || mp == dummynode) if (!ttisnil(gval(mp)) || mp == dummynode)
{ {
LuaNode* n = getfreepos(t); /* get a free place */ LuaNode* n = getfreepos(t); // get a free place
if (n == NULL) if (n == NULL)
{ /* cannot find a free place? */ { // cannot find a free place?
rehash(L, t, key); /* grow table */ rehash(L, t, key); // grow table
/* after rehash, numeric keys might be located in the new array part, but won't be found in the node part */ // after rehash, numeric keys might be located in the new array part, but won't be found in the node part
return arrayornewkey(L, t, key); return arrayornewkey(L, t, key);
} }
LUAU_ASSERT(n != dummynode); LUAU_ASSERT(n != dummynode);
@ -548,24 +548,24 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
getnodekey(L, &mk, mp); getnodekey(L, &mk, mp);
LuaNode* othern = mainposition(t, &mk); LuaNode* othern = mainposition(t, &mk);
if (othern != mp) if (othern != mp)
{ /* is colliding node out of its main position? */ { // is colliding node out of its main position?
/* yes; move colliding node into free position */ // yes; move colliding node into free position
while (othern + gnext(othern) != mp) while (othern + gnext(othern) != mp)
othern += gnext(othern); /* find previous */ othern += gnext(othern); // find previous
gnext(othern) = cast_int(n - othern); /* redo the chain with `n' in place of `mp' */ gnext(othern) = cast_int(n - othern); // redo the chain with `n' in place of `mp'
*n = *mp; /* copy colliding node into free pos. (mp->next also goes) */ *n = *mp; // copy colliding node into free pos. (mp->next also goes)
if (gnext(mp) != 0) if (gnext(mp) != 0)
{ {
gnext(n) += cast_int(mp - n); /* correct 'next' */ gnext(n) += cast_int(mp - n); // correct 'next'
gnext(mp) = 0; /* now 'mp' is free */ gnext(mp) = 0; // now 'mp' is free
} }
setnilvalue(gval(mp)); setnilvalue(gval(mp));
} }
else else
{ /* colliding node is in its own main position */ { // colliding node is in its own main position
/* new node will go into free position */ // new node will go into free position
if (gnext(mp) != 0) if (gnext(mp) != 0)
gnext(n) = cast_int((mp + gnext(mp)) - n); /* chain new position */ gnext(n) = cast_int((mp + gnext(mp)) - n); // chain new position
else else
LUAU_ASSERT(gnext(n) == 0); LUAU_ASSERT(gnext(n) == 0);
gnext(mp) = cast_int(n - mp); gnext(mp) = cast_int(n - mp);
@ -583,7 +583,7 @@ static TValue* newkey(lua_State* L, Table* t, const TValue* key)
*/ */
const TValue* luaH_getnum(Table* t, int key) const TValue* luaH_getnum(Table* 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))
return &t->array[key - 1]; return &t->array[key - 1];
else if (t->node != dummynode) else if (t->node != dummynode)
@ -591,9 +591,9 @@ const TValue* luaH_getnum(Table* t, int key)
double nk = cast_num(key); double nk = cast_num(key);
LuaNode* n = hashnum(t, nk); LuaNode* n = hashnum(t, nk);
for (;;) for (;;)
{ /* check whether `key' is somewhere in the chain */ { // check whether `key' is somewhere in the chain
if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk)) if (ttisnumber(gkey(n)) && luai_numeq(nvalue(gkey(n)), nk))
return gval(n); /* that's it */ return gval(n); // that's it
if (gnext(n) == 0) if (gnext(n) == 0)
break; break;
n += gnext(n); n += gnext(n);
@ -611,9 +611,9 @@ const TValue* luaH_getstr(Table* t, TString* key)
{ {
LuaNode* n = hashstr(t, key); LuaNode* n = hashstr(t, key);
for (;;) for (;;)
{ /* check whether `key' is somewhere in the chain */ { // check whether `key' is somewhere in the chain
if (ttisstring(gkey(n)) && tsvalue(gkey(n)) == key) if (ttisstring(gkey(n)) && tsvalue(gkey(n)) == key)
return gval(n); /* that's it */ return gval(n); // that's it
if (gnext(n) == 0) if (gnext(n) == 0)
break; break;
n += gnext(n); n += gnext(n);
@ -637,17 +637,17 @@ const TValue* luaH_get(Table* t, const TValue* key)
int k; int k;
double n = nvalue(key); double n = nvalue(key);
luai_num2int(k, n); luai_num2int(k, n);
if (luai_numeq(cast_num(k), nvalue(key))) /* index is int? */ if (luai_numeq(cast_num(k), nvalue(key))) // index is int?
return luaH_getnum(t, k); /* use specialized version */ return luaH_getnum(t, k); // use specialized version
/* else go through */ // else go through
} }
default: default:
{ {
LuaNode* n = mainposition(t, key); LuaNode* n = mainposition(t, key);
for (;;) for (;;)
{ /* check whether `key' is somewhere in the chain */ { // check whether `key' is somewhere in the chain
if (luaO_rawequalKey(gkey(n), key)) if (luaO_rawequalKey(gkey(n), key))
return gval(n); /* that's it */ return gval(n); // that's it
if (gnext(n) == 0) if (gnext(n) == 0)
break; break;
n += gnext(n); n += gnext(n);
@ -680,10 +680,10 @@ TValue* luaH_newkey(lua_State* L, Table* t, const TValue* key)
TValue* luaH_setnum(lua_State* L, Table* t, int key) TValue* luaH_setnum(lua_State* L, Table* 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))
return &t->array[key - 1]; return &t->array[key - 1];
/* hash fallback */ // hash fallback
const TValue* p = luaH_getnum(t, key); const TValue* p = luaH_getnum(t, key);
if (p != luaO_nilobject) if (p != luaO_nilobject)
return cast_to(TValue*, p); return cast_to(TValue*, p);
@ -739,9 +739,9 @@ int luaH_getn(Table* t)
if (boundary > 0) if (boundary > 0)
{ {
if (!ttisnil(&t->array[t->sizearray - 1]) && t->node == dummynode) if (!ttisnil(&t->array[t->sizearray - 1]) && t->node == dummynode)
return t->sizearray; /* fast-path: the end of the array in `t' already refers to a boundary */ return t->sizearray; // fast-path: the end of the array in `t' already refers to a boundary
if (boundary < t->sizearray && !ttisnil(&t->array[boundary - 1]) && ttisnil(&t->array[boundary])) if (boundary < t->sizearray && !ttisnil(&t->array[boundary - 1]) && ttisnil(&t->array[boundary]))
return boundary; /* fast-path: boundary already refers to a boundary in `t' */ return boundary; // fast-path: boundary already refers to a boundary in `t'
int foundboundary = updateaboundary(t, boundary); int foundboundary = updateaboundary(t, boundary);
if (foundboundary > 0) if (foundboundary > 0)
@ -767,7 +767,7 @@ int luaH_getn(Table* t)
} }
else else
{ {
/* validate boundary invariant */ // validate boundary invariant
LUAU_ASSERT(t->node == dummynode || ttisnil(luaH_getnum(t, j + 1))); LUAU_ASSERT(t->node == dummynode || ttisnil(luaH_getnum(t, j + 1)));
return j; return j;
} }
@ -812,7 +812,7 @@ Table* luaH_clone(lua_State* L, Table* tt)
void luaH_clear(Table* tt) void luaH_clear(Table* tt)
{ {
/* clear array part */ // clear array part
for (int i = 0; i < tt->sizearray; ++i) for (int i = 0; i < tt->sizearray; ++i)
{ {
setnilvalue(&tt->array[i]); setnilvalue(&tt->array[i]);
@ -820,7 +820,7 @@ void luaH_clear(Table* tt)
maybesetaboundary(tt, 0); maybesetaboundary(tt, 0);
/* clear hash part */ // clear hash part
if (tt->node != dummynode) if (tt->node != dummynode)
{ {
int size = sizenode(tt); int size = sizenode(tt);
@ -834,6 +834,6 @@ void luaH_clear(Table* tt)
} }
} }
/* back to empty -> no tag methods present */ // back to empty -> no tag methods present
tt->tmcache = cast_byte(~0); tt->tmcache = cast_byte(~0);
} }

View File

@ -18,13 +18,13 @@ static int foreachi(lua_State* L)
int n = lua_objlen(L, 1); int n = lua_objlen(L, 1);
for (i = 1; i <= n; i++) for (i = 1; i <= n; i++)
{ {
lua_pushvalue(L, 2); /* function */ lua_pushvalue(L, 2); // function
lua_pushinteger(L, i); /* 1st argument */ lua_pushinteger(L, i); // 1st argument
lua_rawgeti(L, 1, i); /* 2nd argument */ lua_rawgeti(L, 1, i); // 2nd argument
lua_call(L, 2, 1); lua_call(L, 2, 1);
if (!lua_isnil(L, -1)) if (!lua_isnil(L, -1))
return 1; return 1;
lua_pop(L, 1); /* remove nil result */ lua_pop(L, 1); // remove nil result
} }
return 0; return 0;
} }
@ -33,16 +33,16 @@ static int foreach (lua_State* L)
{ {
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
luaL_checktype(L, 2, LUA_TFUNCTION); luaL_checktype(L, 2, LUA_TFUNCTION);
lua_pushnil(L); /* first key */ lua_pushnil(L); // first key
while (lua_next(L, 1)) while (lua_next(L, 1))
{ {
lua_pushvalue(L, 2); /* function */ lua_pushvalue(L, 2); // function
lua_pushvalue(L, -3); /* key */ lua_pushvalue(L, -3); // key
lua_pushvalue(L, -3); /* value */ lua_pushvalue(L, -3); // value
lua_call(L, 2, 1); lua_call(L, 2, 1);
if (!lua_isnil(L, -1)) if (!lua_isnil(L, -1))
return 1; return 1;
lua_pop(L, 2); /* remove value and result */ lua_pop(L, 2); // remove value and result
} }
return 0; return 0;
} }
@ -51,10 +51,10 @@ static int maxn(lua_State* L)
{ {
double max = 0; double max = 0;
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
lua_pushnil(L); /* first key */ lua_pushnil(L); // first key
while (lua_next(L, 1)) while (lua_next(L, 1))
{ {
lua_pop(L, 1); /* remove value */ lua_pop(L, 1); // remove value
if (lua_type(L, -1) == LUA_TNUMBER) if (lua_type(L, -1) == LUA_TNUMBER)
{ {
double v = lua_tonumber(L, -1); double v = lua_tonumber(L, -1);
@ -81,7 +81,7 @@ static void moveelements(lua_State* L, int srct, int dstt, int f, int e, int t)
if (dst->readonly) if (dst->readonly)
luaG_readonlyerror(L); luaG_readonlyerror(L);
int n = e - f + 1; /* number of elements to move */ int n = e - f + 1; // number of elements to move
if (cast_to(unsigned int, f - 1) < cast_to(unsigned int, src->sizearray) && if (cast_to(unsigned int, f - 1) < cast_to(unsigned int, src->sizearray) &&
cast_to(unsigned int, t - 1) < cast_to(unsigned int, dst->sizearray) && cast_to(unsigned int, t - 1) < cast_to(unsigned int, dst->sizearray) &&
@ -137,19 +137,19 @@ static int tinsert(lua_State* L)
{ {
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
int n = lua_objlen(L, 1); int n = lua_objlen(L, 1);
int pos; /* where to insert new element */ int pos; // where to insert new element
switch (lua_gettop(L)) switch (lua_gettop(L))
{ {
case 2: case 2:
{ /* called with only 2 arguments */ { // called with only 2 arguments
pos = n + 1; /* insert new element at the end */ pos = n + 1; // insert new element at the end
break; break;
} }
case 3: case 3:
{ {
pos = luaL_checkinteger(L, 2); /* 2nd argument is the position */ pos = luaL_checkinteger(L, 2); // 2nd argument is the position
/* move up elements if necessary */ // move up elements if necessary
if (1 <= pos && pos <= n) if (1 <= pos && pos <= n)
moveelements(L, 1, 1, pos, n, pos + 1); moveelements(L, 1, 1, pos, n, pos + 1);
break; break;
@ -159,7 +159,7 @@ static int tinsert(lua_State* L)
luaL_error(L, "wrong number of arguments to 'insert'"); luaL_error(L, "wrong number of arguments to 'insert'");
} }
} }
lua_rawseti(L, 1, pos); /* t[pos] = v */ lua_rawseti(L, 1, pos); // t[pos] = v
return 0; return 0;
} }
@ -169,14 +169,14 @@ static int tremove(lua_State* L)
int n = lua_objlen(L, 1); int n = lua_objlen(L, 1);
int pos = luaL_optinteger(L, 2, n); int pos = luaL_optinteger(L, 2, n);
if (!(1 <= pos && pos <= n)) /* position is outside bounds? */ if (!(1 <= pos && pos <= n)) // position is outside bounds?
return 0; /* nothing to remove */ return 0; // nothing to remove
lua_rawgeti(L, 1, pos); /* result = t[pos] */ lua_rawgeti(L, 1, pos); // result = t[pos]
moveelements(L, 1, 1, pos + 1, n, pos); moveelements(L, 1, 1, pos + 1, n, pos);
lua_pushnil(L); lua_pushnil(L);
lua_rawseti(L, 1, n); /* t[n] = nil */ lua_rawseti(L, 1, n); // t[n] = nil
return 1; return 1;
} }
@ -192,28 +192,28 @@ static int tmove(lua_State* L)
int f = luaL_checkinteger(L, 2); int f = luaL_checkinteger(L, 2);
int e = luaL_checkinteger(L, 3); int e = luaL_checkinteger(L, 3);
int t = luaL_checkinteger(L, 4); int t = luaL_checkinteger(L, 4);
int tt = !lua_isnoneornil(L, 5) ? 5 : 1; /* destination table */ int tt = !lua_isnoneornil(L, 5) ? 5 : 1; // destination table
luaL_checktype(L, tt, LUA_TTABLE); luaL_checktype(L, tt, LUA_TTABLE);
if (e >= f) if (e >= f)
{ /* otherwise, nothing to move */ { // otherwise, nothing to move
luaL_argcheck(L, f > 0 || e < INT_MAX + f, 3, "too many elements to move"); luaL_argcheck(L, f > 0 || e < INT_MAX + f, 3, "too many elements to move");
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)); Table* 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);
if (t > 0 && (t - 1) <= dst->sizearray && (t - 1 + n) > dst->sizearray) if (t > 0 && (t - 1) <= dst->sizearray && (t - 1 + n) > dst->sizearray)
{ /* grow the destination table array */ { // grow the destination table array
luaH_resizearray(L, dst, t - 1 + n); luaH_resizearray(L, dst, t - 1 + n);
} }
moveelements(L, 1, tt, f, e, t); moveelements(L, 1, tt, f, e, t);
} }
lua_pushvalue(L, tt); /* return destination table */ lua_pushvalue(L, tt); // return destination table
return 1; return 1;
} }
@ -240,7 +240,7 @@ static int tconcat(lua_State* L)
addfield(L, &b, i); addfield(L, &b, i);
luaL_addlstring(&b, sep, lsep); luaL_addlstring(&b, sep, lsep);
} }
if (i == last) /* add last value (if interval was not empty) */ if (i == last) // add last value (if interval was not empty)
addfield(L, &b, i); addfield(L, &b, i);
luaL_pushresult(&b); luaL_pushresult(&b);
return 1; return 1;
@ -248,8 +248,8 @@ static int tconcat(lua_State* L)
static int tpack(lua_State* L) 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); Table* t = hvalue(L->top - 1);
@ -259,11 +259,11 @@ static int tpack(lua_State* L)
setobj2t(L, e, L->base + i); setobj2t(L, e, L->base + i);
} }
/* t.n = number of elements */ // t.n = number of elements
TValue* nv = luaH_setstr(L, t, luaS_newliteral(L, "n")); TValue* nv = luaH_setstr(L, t, luaS_newliteral(L, "n"));
setnvalue(nv, n); setnvalue(nv, n);
return 1; /* return table */ return 1; // return table
} }
static int tunpack(lua_State* L) static int tunpack(lua_State* L)
@ -274,8 +274,8 @@ static int tunpack(lua_State* L)
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));
if (i > e) if (i > e)
return 0; /* empty range */ return 0; // empty range
unsigned n = (unsigned)e - i; /* number of elements minus 1 (avoid overflows) */ unsigned n = (unsigned)e - i; // number of elements minus 1 (avoid overflows)
if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n))) if (n >= (unsigned int)INT_MAX || !lua_checkstack(L, (int)(++n)))
luaL_error(L, "too many results to unpack"); luaL_error(L, "too many results to unpack");
@ -288,10 +288,10 @@ static int tunpack(lua_State* L)
} }
else else
{ {
/* push arg[i..e - 1] (to avoid overflows) */ // push arg[i..e - 1] (to avoid overflows)
for (; i < e; i++) for (; i < e; i++)
lua_rawgeti(L, 1, i); lua_rawgeti(L, 1, i);
lua_rawgeti(L, 1, e); /* push last element */ lua_rawgeti(L, 1, e); // push last element
} }
return (int)n; return (int)n;
} }
@ -312,85 +312,85 @@ static void set2(lua_State* L, int i, int j)
static int sort_comp(lua_State* L, int a, int b) static int sort_comp(lua_State* L, int a, int b)
{ {
if (!lua_isnil(L, 2)) if (!lua_isnil(L, 2))
{ /* function? */ { // function?
int res; int res;
lua_pushvalue(L, 2); lua_pushvalue(L, 2);
lua_pushvalue(L, a - 1); /* -1 to compensate function */ lua_pushvalue(L, a - 1); // -1 to compensate function
lua_pushvalue(L, b - 2); /* -2 to compensate function and `a' */ lua_pushvalue(L, b - 2); // -2 to compensate function and `a'
lua_call(L, 2, 1); lua_call(L, 2, 1);
res = lua_toboolean(L, -1); res = lua_toboolean(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
return res; return res;
} }
else /* a < b? */ else // a < b?
return lua_lessthan(L, a, b); return lua_lessthan(L, a, b);
} }
static void auxsort(lua_State* L, int l, int u) static void auxsort(lua_State* L, int l, int u)
{ {
while (l < u) while (l < u)
{ /* for tail recursion */ { // for tail recursion
int i, j; int i, j;
/* sort elements a[l], a[(l+u)/2] and a[u] */ // sort elements a[l], a[(l+u)/2] and a[u]
lua_rawgeti(L, 1, l); lua_rawgeti(L, 1, l);
lua_rawgeti(L, 1, u); lua_rawgeti(L, 1, u);
if (sort_comp(L, -1, -2)) /* a[u] < a[l]? */ if (sort_comp(L, -1, -2)) // a[u] < a[l]?
set2(L, l, u); /* swap a[l] - a[u] */ set2(L, l, u); // swap a[l] - a[u]
else else
lua_pop(L, 2); lua_pop(L, 2);
if (u - l == 1) if (u - l == 1)
break; /* only 2 elements */ break; // only 2 elements
i = (l + u) / 2; i = (l + u) / 2;
lua_rawgeti(L, 1, i); lua_rawgeti(L, 1, i);
lua_rawgeti(L, 1, l); lua_rawgeti(L, 1, l);
if (sort_comp(L, -2, -1)) /* a[i]<a[l]? */ if (sort_comp(L, -2, -1)) // a[i]<a[l]?
set2(L, i, l); set2(L, i, l);
else else
{ {
lua_pop(L, 1); /* remove a[l] */ lua_pop(L, 1); // remove a[l]
lua_rawgeti(L, 1, u); lua_rawgeti(L, 1, u);
if (sort_comp(L, -1, -2)) /* a[u]<a[i]? */ if (sort_comp(L, -1, -2)) // a[u]<a[i]?
set2(L, i, u); set2(L, i, u);
else else
lua_pop(L, 2); lua_pop(L, 2);
} }
if (u - l == 2) if (u - l == 2)
break; /* only 3 elements */ break; // only 3 elements
lua_rawgeti(L, 1, i); /* Pivot */ lua_rawgeti(L, 1, i); // Pivot
lua_pushvalue(L, -1); lua_pushvalue(L, -1);
lua_rawgeti(L, 1, u - 1); lua_rawgeti(L, 1, u - 1);
set2(L, i, u - 1); set2(L, i, u - 1);
/* a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2 */ // a[l] <= P == a[u-1] <= a[u], only need to sort from l+1 to u-2
i = l; i = l;
j = u - 1; j = u - 1;
for (;;) for (;;)
{ /* invariant: a[l..i] <= P <= a[j..u] */ { // invariant: a[l..i] <= P <= a[j..u]
/* repeat ++i until a[i] >= P */ // repeat ++i until a[i] >= P
while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2)) while (lua_rawgeti(L, 1, ++i), sort_comp(L, -1, -2))
{ {
if (i >= u) if (i >= u)
luaL_error(L, "invalid order function for sorting"); luaL_error(L, "invalid order function for sorting");
lua_pop(L, 1); /* remove a[i] */ lua_pop(L, 1); // remove a[i]
} }
/* repeat --j until a[j] <= P */ // repeat --j until a[j] <= P
while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1)) while (lua_rawgeti(L, 1, --j), sort_comp(L, -3, -1))
{ {
if (j <= l) if (j <= l)
luaL_error(L, "invalid order function for sorting"); luaL_error(L, "invalid order function for sorting");
lua_pop(L, 1); /* remove a[j] */ lua_pop(L, 1); // remove a[j]
} }
if (j < i) if (j < i)
{ {
lua_pop(L, 3); /* pop pivot, a[i], a[j] */ lua_pop(L, 3); // pop pivot, a[i], a[j]
break; break;
} }
set2(L, i, j); set2(L, i, j);
} }
lua_rawgeti(L, 1, u - 1); lua_rawgeti(L, 1, u - 1);
lua_rawgeti(L, 1, i); lua_rawgeti(L, 1, i);
set2(L, u - 1, i); /* swap pivot (a[u-1]) with a[i] */ set2(L, u - 1, i); // swap pivot (a[u-1]) with a[i]
/* a[l..i-1] <= a[i] == P <= a[i+1..u] */ // a[l..i-1] <= a[i] == P <= a[i+1..u]
/* adjust so that smaller half is in [j..i] and larger one in [l..u] */ // adjust so that smaller half is in [j..i] and larger one in [l..u]
if (i - l < u - i) if (i - l < u - i)
{ {
j = l; j = l;
@ -403,23 +403,23 @@ static void auxsort(lua_State* L, int l, int u)
i = u; i = u;
u = j - 2; u = j - 2;
} }
auxsort(L, j, i); /* call recursively the smaller one */ auxsort(L, j, i); // call recursively the smaller one
} /* repeat the routine for the larger one */ } // repeat the routine for the larger one
} }
static int sort(lua_State* L) static int sort(lua_State* L)
{ {
luaL_checktype(L, 1, LUA_TTABLE); luaL_checktype(L, 1, LUA_TTABLE);
int n = lua_objlen(L, 1); int n = lua_objlen(L, 1);
luaL_checkstack(L, 40, ""); /* assume array is smaller than 2^40 */ luaL_checkstack(L, 40, ""); // assume array is smaller than 2^40
if (!lua_isnoneornil(L, 2)) /* is there a 2nd argument? */ if (!lua_isnoneornil(L, 2)) // is there a 2nd argument?
luaL_checktype(L, 2, LUA_TFUNCTION); luaL_checktype(L, 2, LUA_TFUNCTION);
lua_settop(L, 2); /* make sure there is two arguments */ lua_settop(L, 2); // make sure there is two arguments
auxsort(L, 1, n); auxsort(L, 1, n);
return 0; return 0;
} }
/* }====================================================== */ // }======================================================
static int tcreate(lua_State* L) static int tcreate(lua_State* L)
{ {

View File

@ -12,7 +12,7 @@
// clang-format off // clang-format off
const char* const luaT_typenames[] = { const char* const luaT_typenames[] = {
/* ORDER TYPE */ // ORDER TYPE
"nil", "nil",
"boolean", "boolean",
@ -31,7 +31,7 @@ const char* const luaT_typenames[] = {
}; };
const char* const luaT_eventname[] = { const char* const luaT_eventname[] = {
/* ORDER TM */ // ORDER TM
"__index", "__index",
"__newindex", "__newindex",
@ -70,12 +70,12 @@ void luaT_init(lua_State* L)
for (i = 0; i < LUA_T_COUNT; i++) for (i = 0; i < LUA_T_COUNT; i++)
{ {
L->global->ttname[i] = luaS_new(L, luaT_typenames[i]); L->global->ttname[i] = luaS_new(L, luaT_typenames[i]);
luaS_fix(L->global->ttname[i]); /* never collect these names */ luaS_fix(L->global->ttname[i]); // never collect these names
} }
for (i = 0; i < TM_N; i++) for (i = 0; i < TM_N; i++)
{ {
L->global->tmname[i] = luaS_new(L, luaT_eventname[i]); L->global->tmname[i] = luaS_new(L, luaT_eventname[i]);
luaS_fix(L->global->tmname[i]); /* never collect these names */ luaS_fix(L->global->tmname[i]); // never collect these names
} }
} }
@ -88,8 +88,8 @@ const TValue* luaT_gettm(Table* 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);
if (ttisnil(tm)) if (ttisnil(tm))
{ /* no tag method? */ { // no tag method?
events->tmcache |= cast_byte(1u << event); /* cache this fact */ events->tmcache |= cast_byte(1u << event); // cache this fact
return NULL; return NULL;
} }
else else

View File

@ -20,7 +20,7 @@ typedef enum
TM_ITER, TM_ITER,
TM_LEN, TM_LEN,
TM_EQ, /* last tag method with `fast' access */ TM_EQ, // last tag method with `fast' access
TM_ADD, TM_ADD,
@ -37,7 +37,7 @@ typedef enum
TM_CONCAT, TM_CONCAT,
TM_TYPE, TM_TYPE,
TM_N /* number of elements in the enum */ TM_N // number of elements in the enum
} TMS; } TMS;
// clang-format on // clang-format on

View File

@ -4,10 +4,10 @@
#include "lobject.h" #include "lobject.h"
/* special tag value is used for user data with inline dtors */ // special tag value is used for user data with inline dtors
#define UTAG_IDTOR LUA_UTAG_LIMIT #define UTAG_IDTOR LUA_UTAG_LIMIT
/* special tag value is used for newproxy-created user data (all other user data objects are host-exposed) */ // special tag value is used for newproxy-created user data (all other user data objects are host-exposed)
#define UTAG_PROXY (LUA_UTAG_LIMIT + 1) #define UTAG_PROXY (LUA_UTAG_LIMIT + 1)
#define sizeudata(len) (offsetof(Udata, data) + len) #define sizeudata(len) (offsetof(Udata, data) + len)

View File

@ -8,8 +8,8 @@
#define iscont(p) ((*(p)&0xC0) == 0x80) #define iscont(p) ((*(p)&0xC0) == 0x80)
/* from strlib */ // from strlib
/* translate a relative string position: negative means back from end */ // translate a relative string position: negative means back from end
static int u_posrelat(int pos, size_t len) static int u_posrelat(int pos, size_t len)
{ {
if (pos >= 0) if (pos >= 0)
@ -28,28 +28,28 @@ static const char* utf8_decode(const char* o, int* val)
static const unsigned int limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFF}; static const unsigned int limits[] = {0xFF, 0x7F, 0x7FF, 0xFFFF};
const unsigned char* s = (const unsigned char*)o; const unsigned char* s = (const unsigned char*)o;
unsigned int c = s[0]; unsigned int c = s[0];
unsigned int res = 0; /* final result */ unsigned int res = 0; // final result
if (c < 0x80) /* ascii? */ if (c < 0x80) // ascii?
res = c; res = c;
else else
{ {
int count = 0; /* to count number of continuation bytes */ int count = 0; // to count number of continuation bytes
while (c & 0x40) while (c & 0x40)
{ /* still have continuation bytes? */ { // still have continuation bytes?
int cc = s[++count]; /* read next byte */ int cc = s[++count]; // read next byte
if ((cc & 0xC0) != 0x80) /* not a continuation byte? */ if ((cc & 0xC0) != 0x80) // not a continuation byte?
return NULL; /* invalid byte sequence */ return NULL; // invalid byte sequence
res = (res << 6) | (cc & 0x3F); /* add lower 6 bits from cont. byte */ res = (res << 6) | (cc & 0x3F); // add lower 6 bits from cont. byte
c <<= 1; /* to test next bit */ c <<= 1; // to test next bit
} }
res |= ((c & 0x7F) << (count * 5)); /* add first byte */ res |= ((c & 0x7F) << (count * 5)); // add first byte
if (count > 3 || res > MAXUNICODE || res <= limits[count]) if (count > 3 || res > MAXUNICODE || res <= limits[count])
return NULL; /* invalid byte sequence */ return NULL; // invalid byte sequence
s += count; /* skip continuation bytes read */ s += count; // skip continuation bytes read
} }
if (val) if (val)
*val = res; *val = res;
return (const char*)s + 1; /* +1 to include first byte */ return (const char*)s + 1; // +1 to include first byte
} }
/* /*
@ -70,9 +70,9 @@ static int utflen(lua_State* L)
{ {
const char* s1 = utf8_decode(s + posi, NULL); const char* s1 = utf8_decode(s + posi, NULL);
if (s1 == NULL) if (s1 == NULL)
{ /* conversion error? */ { // conversion error?
lua_pushnil(L); /* return nil ... */ lua_pushnil(L); // return nil ...
lua_pushinteger(L, posi + 1); /* ... and current position */ lua_pushinteger(L, posi + 1); // ... and current position
return 2; return 2;
} }
posi = (int)(s1 - s); posi = (int)(s1 - s);
@ -97,8 +97,8 @@ static int codepoint(lua_State* L)
luaL_argcheck(L, posi >= 1, 2, "out of range"); luaL_argcheck(L, posi >= 1, 2, "out of range");
luaL_argcheck(L, pose <= (int)len, 3, "out of range"); luaL_argcheck(L, pose <= (int)len, 3, "out of range");
if (posi > pose) if (posi > pose)
return 0; /* empty interval; return no values */ return 0; // empty interval; return no values
if (pose - posi >= INT_MAX) /* (int -> int) overflow? */ if (pose - posi >= INT_MAX) // (int -> int) overflow?
luaL_error(L, "string slice too long"); luaL_error(L, "string slice too long");
n = (int)(pose - posi) + 1; n = (int)(pose - posi) + 1;
luaL_checkstack(L, n, "string slice too long"); luaL_checkstack(L, n, "string slice too long");
@ -122,20 +122,20 @@ static int codepoint(lua_State* L)
// from Lua 5.3 lobject.c, copied verbatim + static // from Lua 5.3 lobject.c, copied verbatim + static
static int luaO_utf8esc(char* buff, unsigned long x) static int luaO_utf8esc(char* buff, unsigned long x)
{ {
int n = 1; /* number of bytes put in buffer (backwards) */ int n = 1; // number of bytes put in buffer (backwards)
LUAU_ASSERT(x <= 0x10FFFF); LUAU_ASSERT(x <= 0x10FFFF);
if (x < 0x80) /* ascii? */ if (x < 0x80) // ascii?
buff[UTF8BUFFSZ - 1] = cast_to(char, x); buff[UTF8BUFFSZ - 1] = cast_to(char, x);
else else
{ /* need continuation bytes */ { // need continuation bytes
unsigned int mfb = 0x3f; /* maximum that fits in first byte */ unsigned int mfb = 0x3f; // maximum that fits in first byte
do do
{ /* add continuation bytes */ { // add continuation bytes
buff[UTF8BUFFSZ - (n++)] = cast_to(char, 0x80 | (x & 0x3f)); buff[UTF8BUFFSZ - (n++)] = cast_to(char, 0x80 | (x & 0x3f));
x >>= 6; /* remove added bits */ x >>= 6; // remove added bits
mfb >>= 1; /* now there is one less bit available in first byte */ mfb >>= 1; // now there is one less bit available in first byte
} while (x > mfb); /* still needs continuation byte? */ } while (x > mfb); // still needs continuation byte?
buff[UTF8BUFFSZ - n] = cast_to(char, (~mfb << 1) | x); /* add first byte */ buff[UTF8BUFFSZ - n] = cast_to(char, (~mfb << 1) | x); // add first byte
} }
return n; return n;
} }
@ -162,9 +162,9 @@ static int utfchar(lua_State* L)
char buff[UTF8BUFFSZ]; char buff[UTF8BUFFSZ];
const char* charstr; const char* charstr;
int n = lua_gettop(L); /* number of arguments */ int n = lua_gettop(L); // number of arguments
if (n == 1) if (n == 1)
{ /* optimize common case of single char */ { // optimize common case of single char
int l = buffutfchar(L, 1, buff, &charstr); int l = buffutfchar(L, 1, buff, &charstr);
lua_pushlstring(L, charstr, l); lua_pushlstring(L, charstr, l);
} }
@ -196,7 +196,7 @@ static int byteoffset(lua_State* L)
luaL_argcheck(L, 1 <= posi && --posi <= (int)len, 3, "position out of range"); luaL_argcheck(L, 1 <= posi && --posi <= (int)len, 3, "position out of range");
if (n == 0) if (n == 0)
{ {
/* find beginning of current byte sequence */ // find beginning of current byte sequence
while (posi > 0 && iscont(s + posi)) while (posi > 0 && iscont(s + posi))
posi--; posi--;
} }
@ -207,9 +207,9 @@ static int byteoffset(lua_State* L)
if (n < 0) if (n < 0)
{ {
while (n < 0 && posi > 0) while (n < 0 && posi > 0)
{ /* move back */ { // move back
do do
{ /* find beginning of previous character */ { // find beginning of previous character
posi--; posi--;
} while (posi > 0 && iscont(s + posi)); } while (posi > 0 && iscont(s + posi));
n++; n++;
@ -217,20 +217,20 @@ static int byteoffset(lua_State* L)
} }
else else
{ {
n--; /* do not move for 1st character */ n--; // do not move for 1st character
while (n > 0 && posi < (int)len) while (n > 0 && posi < (int)len)
{ {
do do
{ /* find beginning of next character */ { // find beginning of next character
posi++; posi++;
} while (iscont(s + posi)); /* (cannot pass final '\0') */ } while (iscont(s + posi)); // (cannot pass final '\0')
n--; n--;
} }
} }
} }
if (n == 0) /* did it find given character? */ if (n == 0) // did it find given character?
lua_pushinteger(L, posi + 1); lua_pushinteger(L, posi + 1);
else /* no such character */ else // no such character
lua_pushnil(L); lua_pushnil(L);
return 1; return 1;
} }
@ -240,16 +240,16 @@ static int iter_aux(lua_State* L)
size_t len; size_t len;
const char* s = luaL_checklstring(L, 1, &len); const char* s = luaL_checklstring(L, 1, &len);
int n = lua_tointeger(L, 2) - 1; int n = lua_tointeger(L, 2) - 1;
if (n < 0) /* first iteration? */ if (n < 0) // first iteration?
n = 0; /* start from here */ n = 0; // start from here
else if (n < (int)len) else if (n < (int)len)
{ {
n++; /* skip current byte */ n++; // skip current byte
while (iscont(s + n)) while (iscont(s + n))
n++; /* and its continuations */ n++; // and its continuations
} }
if (n >= (int)len) if (n >= (int)len)
return 0; /* no more codepoints */ return 0; // no more codepoints
else else
{ {
int code; int code;
@ -271,7 +271,7 @@ static int iter_codes(lua_State* L)
return 3; return 3;
} }
/* pattern to match a single UTF-8 character */ // pattern to match a single UTF-8 character
#define UTF8PATT "[\0-\x7F\xC2-\xF4][\x80-\xBF]*" #define UTF8PATT "[\0-\x7F\xC2-\xF4][\x80-\xBF]*"
static const luaL_Reg funcs[] = { static const luaL_Reg funcs[] = {

View File

@ -16,7 +16,7 @@
#include <string.h> #include <string.h>
LUAU_FASTFLAGVARIABLE(LuauLenTM, false) LUAU_FASTFLAGVARIABLE(LuauNicerMethodErrors, false)
// Disable c99-designator to avoid the warning in CGOTO dispatch table // Disable c99-designator to avoid the warning in CGOTO dispatch table
#ifdef __clang__ #ifdef __clang__
@ -110,7 +110,8 @@ LUAU_FASTFLAGVARIABLE(LuauLenTM, false)
VM_DISPATCH_OP(LOP_FORGLOOP_NEXT), VM_DISPATCH_OP(LOP_GETVARARGS), VM_DISPATCH_OP(LOP_DUPCLOSURE), VM_DISPATCH_OP(LOP_PREPVARARGS), \ VM_DISPATCH_OP(LOP_FORGLOOP_NEXT), VM_DISPATCH_OP(LOP_GETVARARGS), VM_DISPATCH_OP(LOP_DUPCLOSURE), VM_DISPATCH_OP(LOP_PREPVARARGS), \
VM_DISPATCH_OP(LOP_LOADKX), VM_DISPATCH_OP(LOP_JUMPX), VM_DISPATCH_OP(LOP_FASTCALL), VM_DISPATCH_OP(LOP_COVERAGE), \ VM_DISPATCH_OP(LOP_LOADKX), VM_DISPATCH_OP(LOP_JUMPX), VM_DISPATCH_OP(LOP_FASTCALL), VM_DISPATCH_OP(LOP_COVERAGE), \
VM_DISPATCH_OP(LOP_CAPTURE), VM_DISPATCH_OP(LOP_JUMPIFEQK), VM_DISPATCH_OP(LOP_JUMPIFNOTEQK), VM_DISPATCH_OP(LOP_FASTCALL1), \ VM_DISPATCH_OP(LOP_CAPTURE), VM_DISPATCH_OP(LOP_JUMPIFEQK), VM_DISPATCH_OP(LOP_JUMPIFNOTEQK), VM_DISPATCH_OP(LOP_FASTCALL1), \
VM_DISPATCH_OP(LOP_FASTCALL2), VM_DISPATCH_OP(LOP_FASTCALL2K), VM_DISPATCH_OP(LOP_FORGPREP), VM_DISPATCH_OP(LOP_FASTCALL2), VM_DISPATCH_OP(LOP_FASTCALL2K), VM_DISPATCH_OP(LOP_FORGPREP), VM_DISPATCH_OP(LOP_JUMPXEQKNIL), \
VM_DISPATCH_OP(LOP_JUMPXEQKB), VM_DISPATCH_OP(LOP_JUMPXEQKN), VM_DISPATCH_OP(LOP_JUMPXEQKS), \
#if defined(__GNUC__) || defined(__clang__) #if defined(__GNUC__) || defined(__clang__)
#define VM_USE_CGOTO 1 #define VM_USE_CGOTO 1
@ -158,7 +159,7 @@ LUAU_NOINLINE static bool luau_loopFORG(lua_State* L, int a, int c)
setobjs2s(L, ra + 3 + 1, ra + 1); setobjs2s(L, ra + 3 + 1, ra + 1);
setobjs2s(L, ra + 3, ra); setobjs2s(L, ra + 3, ra);
L->top = ra + 3 + 3; /* func. + 2 args (state and index) */ L->top = ra + 3 + 3; // func. + 2 args (state and index)
LUAU_ASSERT(L->top <= L->stack_last); LUAU_ASSERT(L->top <= L->stack_last);
luaD_call(L, ra + 3, c); luaD_call(L, ra + 3, c);
@ -236,10 +237,10 @@ LUAU_NOINLINE static void luau_tryfuncTM(lua_State* L, StkId func)
const TValue* tm = luaT_gettmbyobj(L, func, TM_CALL); const TValue* tm = luaT_gettmbyobj(L, func, TM_CALL);
if (!ttisfunction(tm)) if (!ttisfunction(tm))
luaG_typeerror(L, func, "call"); luaG_typeerror(L, func, "call");
for (StkId p = L->top; p > func; p--) /* open space for metamethod */ for (StkId p = L->top; p > func; p--) // open space for metamethod
setobjs2s(L, p, p - 1); setobjs2s(L, p, p - 1);
L->top++; /* stack space pre-allocated by the caller */ L->top++; // stack space pre-allocated by the caller
setobj2s(L, func, tm); /* tag method is the new function to be called */ setobj2s(L, func, tm); // tag method is the new function to be called
} }
LUAU_NOINLINE void luau_callhook(lua_State* L, lua_Hook hook, void* userdata) LUAU_NOINLINE void luau_callhook(lua_State* L, lua_Hook hook, void* userdata)
@ -256,7 +257,7 @@ LUAU_NOINLINE void luau_callhook(lua_State* L, lua_Hook hook, void* userdata)
L->base = L->ci->base; L->base = L->ci->base;
} }
luaD_checkstack(L, LUA_MINSTACK); /* ensure minimum stack size */ luaD_checkstack(L, LUA_MINSTACK); // ensure minimum stack size
L->ci->top = L->top + LUA_MINSTACK; L->ci->top = L->top + LUA_MINSTACK;
LUAU_ASSERT(L->ci->top <= L->stack_last); LUAU_ASSERT(L->ci->top <= L->stack_last);
@ -929,6 +930,10 @@ static void luau_execute(lua_State* L)
VM_PROTECT(luaV_gettable(L, rb, kv, ra)); VM_PROTECT(luaV_gettable(L, rb, kv, ra));
// save cachedslot to accelerate future lookups; patches currently executing instruction since pc-2 rolls back two pc++ // save cachedslot to accelerate future lookups; patches currently executing instruction since pc-2 rolls back two pc++
VM_PATCH_C(pc - 2, L->cachedslot); VM_PATCH_C(pc - 2, L->cachedslot);
// recompute ra since stack might have been reallocated
ra = VM_REG(LUAU_INSN_A(insn));
if (FFlag::LuauNicerMethodErrors && ttisnil(ra))
luaG_methoderror(L, ra + 1, tsvalue(kv));
} }
} }
else else
@ -966,6 +971,10 @@ static void luau_execute(lua_State* L)
VM_PROTECT(luaV_gettable(L, rb, kv, ra)); VM_PROTECT(luaV_gettable(L, rb, kv, ra));
// save cachedslot to accelerate future lookups; patches currently executing instruction since pc-2 rolls back two pc++ // save cachedslot to accelerate future lookups; patches currently executing instruction since pc-2 rolls back two pc++
VM_PATCH_C(pc - 2, L->cachedslot); VM_PATCH_C(pc - 2, L->cachedslot);
// recompute ra since stack might have been reallocated
ra = VM_REG(LUAU_INSN_A(insn));
if (FFlag::LuauNicerMethodErrors && ttisnil(ra))
luaG_methoderror(L, ra + 1, tsvalue(kv));
} }
} }
else else
@ -973,6 +982,10 @@ static void luau_execute(lua_State* L)
// slow-path: handles non-table __index // slow-path: handles non-table __index
setobj2s(L, ra + 1, rb); setobj2s(L, ra + 1, rb);
VM_PROTECT(luaV_gettable(L, rb, kv, ra)); VM_PROTECT(luaV_gettable(L, rb, kv, ra));
// recompute ra since stack might have been reallocated
ra = VM_REG(LUAU_INSN_A(insn));
if (FFlag::LuauNicerMethodErrors && ttisnil(ra))
luaG_methoderror(L, ra + 1, tsvalue(kv));
} }
} }
@ -1028,7 +1041,7 @@ static void luau_execute(lua_State* L)
StkId argi = L->top; StkId argi = L->top;
StkId argend = L->base + p->numparams; StkId argend = L->base + p->numparams;
while (argi < argend) while (argi < argend)
setnilvalue(argi++); /* complete missing arguments */ setnilvalue(argi++); // complete missing arguments
L->top = p->is_vararg ? argi : ci->top; L->top = p->is_vararg ? argi : ci->top;
// reentry // reentry
@ -2074,7 +2087,7 @@ static void luau_execute(lua_State* L)
{ {
Table* h = hvalue(rb); Table* h = hvalue(rb);
if (!FFlag::LuauLenTM || fastnotm(h->metatable, TM_LEN)) if (fastnotm(h->metatable, TM_LEN))
{ {
setnvalue(ra, cast_num(luaH_getn(h))); setnvalue(ra, cast_num(luaH_getn(h)));
VM_NEXT(); VM_NEXT();
@ -2214,7 +2227,7 @@ static void luau_execute(lua_State* L)
if (ttisfunction(ra)) if (ttisfunction(ra))
{ {
/* will be called during FORGLOOP */ // will be called during FORGLOOP
} }
else else
{ {
@ -2225,16 +2238,16 @@ static void luau_execute(lua_State* L)
setobj2s(L, ra + 1, ra); setobj2s(L, ra + 1, ra);
setobj2s(L, ra, fn); setobj2s(L, ra, fn);
L->top = ra + 2; /* func + self arg */ L->top = ra + 2; // func + self arg
LUAU_ASSERT(L->top <= L->stack_last); LUAU_ASSERT(L->top <= L->stack_last);
VM_PROTECT(luaD_call(L, ra, 3)); VM_PROTECT(luaD_call(L, ra, 3));
L->top = L->ci->top; L->top = L->ci->top;
/* recompute ra since stack might have been reallocated */ // recompute ra since stack might have been reallocated
ra = VM_REG(LUAU_INSN_A(insn)); ra = VM_REG(LUAU_INSN_A(insn));
/* protect against __iter returning nil, since nil is used as a marker for builtin iteration in FORGLOOP */ // protect against __iter returning nil, since nil is used as a marker for builtin iteration in FORGLOOP
if (ttisnil(ra)) if (ttisnil(ra))
{ {
VM_PROTECT(luaG_typeerror(L, ra, "call")); VM_PROTECT(luaG_typeerror(L, ra, "call"));
@ -2242,12 +2255,12 @@ static void luau_execute(lua_State* L)
} }
else if (fasttm(L, mt, TM_CALL)) else if (fasttm(L, mt, TM_CALL))
{ {
/* table or userdata with __call, will be called during FORGLOOP */ // table or userdata with __call, will be called during FORGLOOP
/* TODO: we might be able to stop supporting this depending on whether it's used in practice */ // TODO: we might be able to stop supporting this depending on whether it's used in practice
} }
else if (ttistable(ra)) else if (ttistable(ra))
{ {
/* set up registers for builtin iteration */ // set up registers for builtin iteration
setobj2s(L, ra + 1, ra); setobj2s(L, ra + 1, ra);
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0))); setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)));
setnilvalue(ra); setnilvalue(ra);
@ -2344,7 +2357,7 @@ static void luau_execute(lua_State* L)
setobjs2s(L, ra + 3 + 1, ra + 1); setobjs2s(L, ra + 3 + 1, ra + 1);
setobjs2s(L, ra + 3, ra); setobjs2s(L, ra + 3, ra);
L->top = ra + 3 + 3; /* func + 2 args (state and index) */ L->top = ra + 3 + 3; // func + 2 args (state and index)
LUAU_ASSERT(L->top <= L->stack_last); LUAU_ASSERT(L->top <= L->stack_last);
VM_PROTECT(luaD_call(L, ra + 3, uint8_t(aux))); VM_PROTECT(luaD_call(L, ra + 3, uint8_t(aux)));
@ -2372,7 +2385,7 @@ static void luau_execute(lua_State* L)
if (cl->env->safeenv && ttistable(ra + 1) && ttisnumber(ra + 2) && nvalue(ra + 2) == 0.0) if (cl->env->safeenv && ttistable(ra + 1) && ttisnumber(ra + 2) && nvalue(ra + 2) == 0.0)
{ {
setnilvalue(ra); setnilvalue(ra);
/* ra+1 is already the table */ // ra+1 is already the table
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0))); setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)));
} }
else if (!ttisfunction(ra)) else if (!ttisfunction(ra))
@ -2444,7 +2457,7 @@ static void luau_execute(lua_State* L)
if (cl->env->safeenv && ttistable(ra + 1) && ttisnil(ra + 2)) if (cl->env->safeenv && ttistable(ra + 1) && ttisnil(ra + 2))
{ {
setnilvalue(ra); setnilvalue(ra);
/* ra+1 is already the table */ // ra+1 is already the table
setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0))); setpvalue(ra + 2, reinterpret_cast<void*>(uintptr_t(0)));
} }
else if (!ttisfunction(ra)) else if (!ttisfunction(ra))
@ -2619,8 +2632,8 @@ static void luau_execute(lua_State* L)
LUAU_ASSERT(cast_int(L->top - base) >= numparams); LUAU_ASSERT(cast_int(L->top - base) >= numparams);
// move fixed parameters to final position // move fixed parameters to final position
StkId fixed = base; /* first fixed argument */ StkId fixed = base; // first fixed argument
base = L->top; /* final position of first argument */ base = L->top; // final position of first argument
for (int i = 0; i < numparams; ++i) for (int i = 0; i < numparams; ++i)
{ {
@ -2983,6 +2996,56 @@ static void luau_execute(lua_State* L)
VM_CONTINUE(op); VM_CONTINUE(op);
} }
VM_CASE(LOP_JUMPXEQKNIL)
{
Instruction insn = *pc++;
uint32_t aux = *pc;
StkId ra = VM_REG(LUAU_INSN_A(insn));
static_assert(LUA_TNIL == 0, "we expect type-1 to be negative iff type is nil");
// condition is equivalent to: int(ttisnil(ra)) != (aux >> 31)
pc += int((ttype(ra) - 1) ^ aux) < 0 ? LUAU_INSN_D(insn) : 1;
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
VM_NEXT();
}
VM_CASE(LOP_JUMPXEQKB)
{
Instruction insn = *pc++;
uint32_t aux = *pc;
StkId ra = VM_REG(LUAU_INSN_A(insn));
pc += int(ttisboolean(ra) && bvalue(ra) == int(aux & 1)) != (aux >> 31) ? LUAU_INSN_D(insn) : 1;
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
VM_NEXT();
}
VM_CASE(LOP_JUMPXEQKN)
{
Instruction insn = *pc++;
uint32_t aux = *pc;
StkId ra = VM_REG(LUAU_INSN_A(insn));
TValue* kv = VM_KV(aux & 0xffffff);
LUAU_ASSERT(ttisnumber(kv));
pc += int(ttisnumber(ra) && nvalue(ra) == nvalue(kv)) != (aux >> 31) ? LUAU_INSN_D(insn) : 1;
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
VM_NEXT();
}
VM_CASE(LOP_JUMPXEQKS)
{
Instruction insn = *pc++;
uint32_t aux = *pc;
StkId ra = VM_REG(LUAU_INSN_A(insn));
TValue* kv = VM_KV(aux & 0xffffff);
LUAU_ASSERT(ttisstring(kv));
pc += int(ttisstring(ra) && gcvalue(ra) == gcvalue(kv)) != (aux >> 31) ? LUAU_INSN_D(insn) : 1;
LUAU_ASSERT(unsigned(pc - cl->l.p->code) < unsigned(cl->l.p->sizecode));
VM_NEXT();
}
#if !VM_USE_CGOTO #if !VM_USE_CGOTO
default: default:
LUAU_ASSERT(!"Unknown opcode"); LUAU_ASSERT(!"Unknown opcode");
@ -3032,7 +3095,7 @@ int luau_precall(lua_State* L, StkId func, int nresults)
StkId argi = L->top; StkId argi = L->top;
StkId argend = L->base + ccl->l.p->numparams; StkId argend = L->base + ccl->l.p->numparams;
while (argi < argend) while (argi < argend)
setnilvalue(argi++); /* complete missing arguments */ setnilvalue(argi++); // complete missing arguments
L->top = ccl->l.p->is_vararg ? argi : ci->top; L->top = ccl->l.p->is_vararg ? argi : ci->top;
L->ci->savedpc = ccl->l.p->code; L->ci->savedpc = ccl->l.p->code;

View File

@ -12,11 +12,9 @@
#include <string.h> #include <string.h>
#include <stdio.h> #include <stdio.h>
LUAU_FASTFLAG(LuauLenTM)
LUAU_FASTFLAGVARIABLE(LuauBetterNewindex, false) LUAU_FASTFLAGVARIABLE(LuauBetterNewindex, false)
/* limit for table tag-method chains (to avoid loops) */ // limit for table tag-method chains (to avoid loops)
#define MAXTAGLOOP 100 #define MAXTAGLOOP 100
const TValue* luaV_tonumber(const TValue* obj, TValue* n) const TValue* luaV_tonumber(const TValue* obj, TValue* n)
@ -67,9 +65,9 @@ static StkId callTMres(lua_State* L, StkId res, const TValue* f, const TValue* p
// * during stack reallocation all of the allocated stack is copied (even beyond stack_last) so these // * during stack reallocation all of the allocated stack is copied (even beyond stack_last) so these
// values will be preserved even if they go past stack_last // values will be preserved even if they go past stack_last
LUAU_ASSERT((L->top + 3) < (L->stack + L->stacksize)); LUAU_ASSERT((L->top + 3) < (L->stack + L->stacksize));
setobj2s(L, L->top, f); /* push function */ setobj2s(L, L->top, f); // push function
setobj2s(L, L->top + 1, p1); /* 1st argument */ setobj2s(L, L->top + 1, p1); // 1st argument
setobj2s(L, L->top + 2, p2); /* 2nd argument */ setobj2s(L, L->top + 2, p2); // 2nd argument
luaD_checkstack(L, 3); luaD_checkstack(L, 3);
L->top += 3; L->top += 3;
luaD_call(L, L->top - 3, 1); luaD_call(L, L->top - 3, 1);
@ -89,10 +87,10 @@ static void callTM(lua_State* L, const TValue* f, const TValue* p1, const TValue
// * during stack reallocation all of the allocated stack is copied (even beyond stack_last) so these // * during stack reallocation all of the allocated stack is copied (even beyond stack_last) so these
// values will be preserved even if they go past stack_last // values will be preserved even if they go past stack_last
LUAU_ASSERT((L->top + 4) < (L->stack + L->stacksize)); LUAU_ASSERT((L->top + 4) < (L->stack + L->stacksize));
setobj2s(L, L->top, f); /* push function */ setobj2s(L, L->top, f); // push function
setobj2s(L, L->top + 1, p1); /* 1st argument */ setobj2s(L, L->top + 1, p1); // 1st argument
setobj2s(L, L->top + 2, p2); /* 2nd argument */ setobj2s(L, L->top + 2, p2); // 2nd argument
setobj2s(L, L->top + 3, p3); /* 3th argument */ setobj2s(L, L->top + 3, p3); // 3th argument
luaD_checkstack(L, 4); luaD_checkstack(L, 4);
L->top += 4; L->top += 4;
luaD_call(L, L->top - 4, 0); luaD_call(L, L->top - 4, 0);
@ -105,21 +103,21 @@ 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); Table* 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
if (res != luaO_nilobject) if (res != luaO_nilobject)
L->cachedslot = gval2slot(h, res); /* remember slot to accelerate future lookups */ L->cachedslot = gval2slot(h, res); // remember slot to accelerate future lookups
if (!ttisnil(res) /* result is no nil? */ if (!ttisnil(res) // result is no nil?
|| (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL) || (tm = fasttm(L, h->metatable, TM_INDEX)) == NULL)
{ /* or no TM? */ { // or no TM?
setobj2s(L, val, res); setobj2s(L, val, res);
return; return;
} }
/* t isn't a table, so see if it has an INDEX meta-method to look up the key with */ // t isn't a table, so see if it has an INDEX meta-method to look up the key with
} }
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX))) else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_INDEX)))
luaG_indexerror(L, t, key); luaG_indexerror(L, t, key);
@ -128,7 +126,7 @@ void luaV_gettable(lua_State* L, const TValue* t, TValue* key, StkId val)
callTMres(L, val, tm, t, key); callTMres(L, val, tm, t, key);
return; return;
} }
t = tm; /* else repeat with `tm' */ t = tm; // else repeat with `tm'
} }
luaG_runerror(L, "'__index' chain too long; possible loop"); luaG_runerror(L, "'__index' chain too long; possible loop");
} }
@ -141,48 +139,48 @@ 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); Table* h = hvalue(t);
if (FFlag::LuauBetterNewindex) if (FFlag::LuauBetterNewindex)
{ {
const TValue* oldval = luaH_get(h, key); const TValue* oldval = luaH_get(h, key);
/* should we assign the key? (if key is valid or __newindex is not set) */ // should we assign the key? (if key is valid or __newindex is not set)
if (!ttisnil(oldval) || (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) if (!ttisnil(oldval) || (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL)
{ {
if (h->readonly) if (h->readonly)
luaG_readonlyerror(L); luaG_readonlyerror(L);
/* luaH_set would work but would repeat the lookup so we use luaH_setslot that can reuse oldval if it's safe */ // luaH_set would work but would repeat the lookup so we use luaH_setslot that can reuse oldval if it's safe
TValue* newval = luaH_setslot(L, h, oldval, key); TValue* newval = luaH_setslot(L, h, oldval, key);
L->cachedslot = gval2slot(h, newval); /* remember slot to accelerate future lookups */ L->cachedslot = gval2slot(h, newval); // remember slot to accelerate future lookups
setobj2t(L, newval, val); setobj2t(L, newval, val);
luaC_barriert(L, h, val); luaC_barriert(L, h, val);
return; return;
} }
/* fallthrough to metamethod */ // fallthrough to metamethod
} }
else else
{ {
if (h->readonly) if (h->readonly)
luaG_readonlyerror(L); luaG_readonlyerror(L);
TValue* oldval = luaH_set(L, h, key); /* do a primitive set */ TValue* oldval = luaH_set(L, h, key); // do a primitive set
L->cachedslot = gval2slot(h, oldval); /* remember slot to accelerate future lookups */ L->cachedslot = gval2slot(h, oldval); // remember slot to accelerate future lookups
if (!ttisnil(oldval) || /* result is no nil? */ if (!ttisnil(oldval) || // result is no nil?
(tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL) (tm = fasttm(L, h->metatable, TM_NEWINDEX)) == NULL)
{ /* or no TM? */ { // or no TM?
setobj2t(L, oldval, val); setobj2t(L, oldval, val);
luaC_barriert(L, h, val); luaC_barriert(L, h, val);
return; return;
} }
/* else will try the tag method */ // else will try the tag method
} }
} }
else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX))) else if (ttisnil(tm = luaT_gettmbyobj(L, t, TM_NEWINDEX)))
@ -193,8 +191,8 @@ void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val)
callTM(L, tm, t, key, val); callTM(L, tm, t, key, val);
return; return;
} }
/* else repeat with `tm' */ // else repeat with `tm'
setobj(L, &temp, tm); /* avoid pointing inside table (may rehash) */ setobj(L, &temp, tm); // avoid pointing inside table (may rehash)
t = &temp; t = &temp;
} }
luaG_runerror(L, "'__newindex' chain too long; possible loop"); luaG_runerror(L, "'__newindex' chain too long; possible loop");
@ -202,9 +200,9 @@ void luaV_settable(lua_State* L, const TValue* t, TValue* key, StkId val)
static int call_binTM(lua_State* L, const TValue* p1, const TValue* p2, StkId res, TMS event) static int call_binTM(lua_State* L, const TValue* p1, const TValue* p2, StkId res, TMS event)
{ {
const TValue* tm = luaT_gettmbyobj(L, p1, event); /* try first operand */ const TValue* tm = luaT_gettmbyobj(L, p1, event); // try first operand
if (ttisnil(tm)) if (ttisnil(tm))
tm = luaT_gettmbyobj(L, p2, event); /* try second operand */ tm = luaT_gettmbyobj(L, p2, event); // try second operand
if (ttisnil(tm)) if (ttisnil(tm))
return 0; return 0;
callTMres(L, res, tm, p1, p2); callTMres(L, res, tm, p1, p2);
@ -216,13 +214,13 @@ static const TValue* get_compTM(lua_State* L, Table* mt1, Table* mt2, TMS event)
const TValue* tm1 = fasttm(L, mt1, event); const TValue* tm1 = fasttm(L, mt1, event);
const TValue* tm2; const TValue* tm2;
if (tm1 == NULL) if (tm1 == NULL)
return NULL; /* no metamethod */ return NULL; // no metamethod
if (mt1 == mt2) if (mt1 == mt2)
return tm1; /* same metatables => same metamethods */ return tm1; // same metatables => same metamethods
tm2 = fasttm(L, mt2, event); tm2 = fasttm(L, mt2, event);
if (tm2 == NULL) if (tm2 == NULL)
return NULL; /* no metamethod */ return NULL; // no metamethod
if (luaO_rawequalObj(tm1, tm2)) /* same metamethods? */ if (luaO_rawequalObj(tm1, tm2)) // same metamethods?
return tm1; return tm1;
return NULL; return NULL;
} }
@ -232,9 +230,9 @@ static int call_orderTM(lua_State* L, const TValue* p1, const TValue* p2, TMS ev
const TValue* tm1 = luaT_gettmbyobj(L, p1, event); const TValue* tm1 = luaT_gettmbyobj(L, p1, event);
const TValue* tm2; const TValue* tm2;
if (ttisnil(tm1)) if (ttisnil(tm1))
return -1; /* no metamethod? */ return -1; // no metamethod?
tm2 = luaT_gettmbyobj(L, p2, event); tm2 = luaT_gettmbyobj(L, p2, event);
if (!luaO_rawequalObj(tm1, tm2)) /* different metamethods? */ if (!luaO_rawequalObj(tm1, tm2)) // different metamethods?
return -1; return -1;
callTMres(L, L->top, tm1, p1, p2); callTMres(L, L->top, tm1, p1, p2);
return !l_isfalse(L->top); return !l_isfalse(L->top);
@ -281,9 +279,9 @@ int luaV_lessequal(lua_State* L, const TValue* l, const TValue* r)
return luai_numle(nvalue(l), nvalue(r)); return luai_numle(nvalue(l), nvalue(r));
else if (ttisstring(l)) else if (ttisstring(l))
return luaV_strcmp(tsvalue(l), tsvalue(r)) <= 0; return luaV_strcmp(tsvalue(l), tsvalue(r)) <= 0;
else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) /* first try `le' */ else if ((res = call_orderTM(L, l, r, TM_LE)) != -1) // first try `le'
return res; return res;
else if ((res = call_orderTM(L, r, l, TM_LT)) == -1) /* error if not `lt' */ else if ((res = call_orderTM(L, r, l, TM_LT)) == -1) // error if not `lt'
luaG_ordererror(L, l, r, TM_LE); luaG_ordererror(L, l, r, TM_LE);
return !res; return !res;
} }
@ -301,7 +299,7 @@ int luaV_equalval(lua_State* L, const TValue* t1, const TValue* t2)
case LUA_TVECTOR: case LUA_TVECTOR:
return luai_veceq(vvalue(t1), vvalue(t2)); return luai_veceq(vvalue(t1), vvalue(t2));
case LUA_TBOOLEAN: case LUA_TBOOLEAN:
return bvalue(t1) == bvalue(t2); /* true must be 1 !! */ return bvalue(t1) == bvalue(t2); // true must be 1 !!
case LUA_TLIGHTUSERDATA: case LUA_TLIGHTUSERDATA:
return pvalue(t1) == pvalue(t2); return pvalue(t1) == pvalue(t2);
case LUA_TUSERDATA: case LUA_TUSERDATA:
@ -309,19 +307,19 @@ int luaV_equalval(lua_State* L, const TValue* t1, const TValue* t2)
tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, TM_EQ); tm = get_compTM(L, uvalue(t1)->metatable, uvalue(t2)->metatable, TM_EQ);
if (!tm) if (!tm)
return uvalue(t1) == uvalue(t2); return uvalue(t1) == uvalue(t2);
break; /* will try TM */ break; // will try TM
} }
case LUA_TTABLE: case LUA_TTABLE:
{ {
tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ); tm = get_compTM(L, hvalue(t1)->metatable, hvalue(t2)->metatable, TM_EQ);
if (!tm) if (!tm)
return hvalue(t1) == hvalue(t2); return hvalue(t1) == hvalue(t2);
break; /* will try TM */ break; // will try TM
} }
default: default:
return gcvalue(t1) == gcvalue(t2); return gcvalue(t1) == gcvalue(t2);
} }
callTMres(L, L->top, tm, t1, t2); /* call TM */ callTMres(L, L->top, tm, t1, t2); // call TM
return !l_isfalse(L->top); return !l_isfalse(L->top);
} }
@ -330,21 +328,21 @@ void luaV_concat(lua_State* L, int total, int last)
do do
{ {
StkId top = L->base + last + 1; StkId top = L->base + last + 1;
int n = 2; /* number of elements handled in this pass (at least 2) */ int n = 2; // number of elements handled in this pass (at least 2)
if (!(ttisstring(top - 2) || ttisnumber(top - 2)) || !tostring(L, top - 1)) if (!(ttisstring(top - 2) || ttisnumber(top - 2)) || !tostring(L, top - 1))
{ {
if (!call_binTM(L, top - 2, top - 1, top - 2, TM_CONCAT)) if (!call_binTM(L, top - 2, top - 1, top - 2, TM_CONCAT))
luaG_concaterror(L, top - 2, top - 1); luaG_concaterror(L, top - 2, top - 1);
} }
else if (tsvalue(top - 1)->len == 0) /* second op is empty? */ else if (tsvalue(top - 1)->len == 0) // second op is empty?
(void)tostring(L, top - 2); /* result is first op (as string) */ (void)tostring(L, top - 2); // result is first op (as string)
else else
{ {
/* at least two string values; get as many as possible */ // at least two string values; get as many as possible
size_t tl = tsvalue(top - 1)->len; size_t tl = tsvalue(top - 1)->len;
char* buffer; char* buffer;
int i; int i;
/* collect total length */ // collect total length
for (n = 1; n < total && tostring(L, top - n - 1); n++) for (n = 1; n < total && tostring(L, top - n - 1); n++)
{ {
size_t l = tsvalue(top - n - 1)->len; size_t l = tsvalue(top - n - 1)->len;
@ -368,7 +366,7 @@ void luaV_concat(lua_State* L, int total, int last)
tl = 0; tl = 0;
for (i = n; i > 0; i--) for (i = n; i > 0; i--)
{ /* concat all strings */ { // concat all strings
size_t l = tsvalue(top - i)->len; size_t l = tsvalue(top - i)->len;
memcpy(buffer + tl, svalue(top - i), l); memcpy(buffer + tl, svalue(top - i), l);
tl += l; tl += l;
@ -383,9 +381,9 @@ void luaV_concat(lua_State* L, int total, int last)
setsvalue2s(L, top - n, luaS_buffinish(L, ts)); setsvalue2s(L, top - n, luaS_buffinish(L, ts));
} }
} }
total -= n - 1; /* got `n' strings to create 1 new */ total -= n - 1; // got `n' strings to create 1 new
last -= n - 1; last -= n - 1;
} while (total > 1); /* repeat until only 1 result left */ } while (total > 1); // repeat until only 1 result left
} }
void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TMS op) void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TMS op)
@ -504,29 +502,6 @@ void luaV_doarith(lua_State* L, StkId ra, const TValue* rb, const TValue* rc, TM
void luaV_dolen(lua_State* L, StkId ra, const TValue* rb) void luaV_dolen(lua_State* L, StkId ra, const TValue* rb)
{ {
if (!FFlag::LuauLenTM)
{
switch (ttype(rb))
{
case LUA_TTABLE:
{
setnvalue(ra, cast_num(luaH_getn(hvalue(rb))));
break;
}
case LUA_TSTRING:
{
setnvalue(ra, cast_num(tsvalue(rb)->len));
break;
}
default:
{ /* try metamethod */
if (!call_binTM(L, rb, luaO_nilobject, ra, TM_LEN))
luaG_typeerror(L, rb, "get length of");
}
}
return;
}
const TValue* tm = NULL; const TValue* tm = NULL;
switch (ttype(rb)) switch (ttype(rb))
{ {
@ -555,5 +530,5 @@ void luaV_dolen(lua_State* L, StkId ra, const TValue* rb)
StkId res = callTMres(L, ra, tm, rb, luaO_nilobject); StkId res = callTMres(L, ra, tm, rb, luaO_nilobject);
if (!ttisnumber(res)) if (!ttisnumber(res))
luaG_runerror(L, "'__len' must return a number"); /* note, we can't access rb since stack may have been reallocated */ luaG_runerror(L, "'__len' must return a number"); // note, we can't access rb since stack may have been reallocated
} }

View File

@ -1,6 +1,6 @@
// 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/Ast.h" #include "Luau/Ast.h"
#include "Luau/JsonEncoder.h" #include "Luau/AstJsonEncoder.h"
#include "Luau/Parser.h" #include "Luau/Parser.h"
#include "doctest.h" #include "doctest.h"
@ -181,7 +181,8 @@ TEST_CASE("encode_AstExprLocal")
AstLocal local{AstName{"foo"}, Location{}, nullptr, 0, 0, nullptr}; AstLocal local{AstName{"foo"}, Location{}, nullptr, 0, 0, nullptr};
AstExprLocal exprLocal{Location{}, &local, false}; AstExprLocal exprLocal{Location{}, &local, false};
CHECK(toJson(&exprLocal) == R"({"type":"AstExprLocal","location":"0,0 - 0,0","local":{"luauType":null,"name":"foo","type":"AstLocal","location":"0,0 - 0,0"}})"); CHECK(toJson(&exprLocal) ==
R"({"type":"AstExprLocal","location":"0,0 - 0,0","local":{"luauType":null,"name":"foo","type":"AstLocal","location":"0,0 - 0,0"}})");
} }
TEST_CASE("encode_AstExprVarargs") TEST_CASE("encode_AstExprVarargs")

View File

@ -1974,7 +1974,7 @@ TEST_CASE_FIXTURE(ACFixture, "function_result_passed_to_function_has_parentheses
check(R"( check(R"(
local function foo() return 1 end local function foo() return 1 end
local function bar(a: number) return -a end local function bar(a: number) return -a end
local abc = bar(@1) local abc = bar(@1)
)"); )");
auto ac = autocomplete('1'); auto ac = autocomplete('1');

View File

@ -2382,11 +2382,13 @@ end
TEST_CASE("DebugLineInfoRepeatUntil") TEST_CASE("DebugLineInfoRepeatUntil")
{ {
ScopedFastFlag sff("LuauCompileXEQ", true);
CHECK_EQ("\n" + compileFunction0Coverage(R"( CHECK_EQ("\n" + compileFunction0Coverage(R"(
local f = 0 local f = 0
repeat repeat
f += 1 f += 1
if f == 1 then if f == 1 then
print(f) print(f)
else else
f = 0 f = 0
@ -2397,13 +2399,13 @@ until f == 0
R"( R"(
2: LOADN R0 0 2: LOADN R0 0
4: L0: ADDK R0 R0 K0 4: L0: ADDK R0 R0 K0
5: JUMPIFNOTEQK R0 K0 L1 5: JUMPXEQKN R0 K0 L1 NOT
6: GETIMPORT R1 2 6: GETIMPORT R1 2
6: MOVE R2 R0 6: MOVE R2 R0
6: CALL R1 1 0 6: CALL R1 1 0
6: JUMP L2 6: JUMP L2
8: L1: LOADN R0 0 8: L1: LOADN R0 0
10: L2: JUMPIFEQK R0 K3 L3 10: L2: JUMPXEQKN R0 K3 L3
10: JUMPBACK L0 10: JUMPBACK L0
11: L3: RETURN R0 0 11: L3: RETURN R0 0
)"); )");
@ -3509,13 +3511,15 @@ RETURN R0 1
TEST_CASE("ConstantJumpCompare") TEST_CASE("ConstantJumpCompare")
{ {
ScopedFastFlag sff("LuauCompileXEQ", true);
CHECK_EQ("\n" + compileFunction0(R"( CHECK_EQ("\n" + compileFunction0(R"(
local obj = ... local obj = ...
local b = obj == 1 local b = obj == 1
)"), )"),
R"( R"(
GETVARARGS R0 1 GETVARARGS R0 1
JUMPIFEQK R0 K0 L0 JUMPXEQKN R0 K0 L0
LOADB R1 0 +1 LOADB R1 0 +1
L0: LOADB R1 1 L0: LOADB R1 1
L1: RETURN R0 0 L1: RETURN R0 0
@ -3527,7 +3531,7 @@ local b = 1 == obj
)"), )"),
R"( R"(
GETVARARGS R0 1 GETVARARGS R0 1
JUMPIFEQK R0 K0 L0 JUMPXEQKN R0 K0 L0
LOADB R1 0 +1 LOADB R1 0 +1
L0: LOADB R1 1 L0: LOADB R1 1
L1: RETURN R0 0 L1: RETURN R0 0
@ -3539,7 +3543,7 @@ local b = "Hello, Sailor!" == obj
)"), )"),
R"( R"(
GETVARARGS R0 1 GETVARARGS R0 1
JUMPIFEQK R0 K0 L0 JUMPXEQKS R0 K0 L0
LOADB R1 0 +1 LOADB R1 0 +1
L0: LOADB R1 1 L0: LOADB R1 1
L1: RETURN R0 0 L1: RETURN R0 0
@ -3551,7 +3555,7 @@ local b = nil == obj
)"), )"),
R"( R"(
GETVARARGS R0 1 GETVARARGS R0 1
JUMPIFEQK R0 K0 L0 JUMPXEQKNIL R0 L0
LOADB R1 0 +1 LOADB R1 0 +1
L0: LOADB R1 1 L0: LOADB R1 1
L1: RETURN R0 0 L1: RETURN R0 0
@ -3563,7 +3567,7 @@ local b = true == obj
)"), )"),
R"( R"(
GETVARARGS R0 1 GETVARARGS R0 1
JUMPIFEQK R0 K0 L0 JUMPXEQKB R0 1 L0
LOADB R1 0 +1 LOADB R1 0 +1
L0: LOADB R1 1 L0: LOADB R1 1
L1: RETURN R0 0 L1: RETURN R0 0
@ -3575,7 +3579,7 @@ local b = nil ~= obj
)"), )"),
R"( R"(
GETVARARGS R0 1 GETVARARGS R0 1
JUMPIFNOTEQK R0 K0 L0 JUMPXEQKNIL R0 L0 NOT
LOADB R1 0 +1 LOADB R1 0 +1
L0: LOADB R1 1 L0: LOADB R1 1
L1: RETURN R0 0 L1: RETURN R0 0
@ -6098,6 +6102,8 @@ L3: RETURN R0 -1
TEST_CASE("BuiltinFoldingMultret") TEST_CASE("BuiltinFoldingMultret")
{ {
ScopedFastFlag sff("LuauCompileXEQ", true);
CHECK_EQ("\n" + compileFunction(R"( CHECK_EQ("\n" + compileFunction(R"(
local NoLanes: Lanes = --[[ ]] 0b0000000000000000000000000000000 local NoLanes: Lanes = --[[ ]] 0b0000000000000000000000000000000
local OffscreenLane: Lane = --[[ ]] 0b1000000000000000000000000000000 local OffscreenLane: Lane = --[[ ]] 0b1000000000000000000000000000000
@ -6120,14 +6126,14 @@ FASTCALL2K 29 R2 K1 L0
LOADK R3 K1 LOADK R3 K1
GETIMPORT R1 4 GETIMPORT R1 4
CALL R1 2 1 CALL R1 2 1
L0: JUMPIFEQK R1 K5 L1 L0: JUMPXEQKN R1 K5 L1
RETURN R1 1 RETURN R1 1
L1: FASTCALL2K 29 R1 K6 L2 L1: FASTCALL2K 29 R1 K6 L2
MOVE R3 R1 MOVE R3 R1
LOADK R4 K6 LOADK R4 K6
GETIMPORT R2 4 GETIMPORT R2 4
CALL R2 2 1 CALL R2 2 1
L2: JUMPIFEQK R2 K5 L3 L2: JUMPXEQKN R2 K5 L3
LOADK R2 K6 LOADK R2 K6
RETURN R2 1 RETURN R2 1
L3: LOADN R2 0 L3: LOADN R2 0
@ -6155,7 +6161,8 @@ local function test(a, b)
local c = a local c = a
return c + b return c + b
end end
)"), R"( )"),
R"(
ADD R2 R0 R1 ADD R2 R0 R1
RETURN R2 1 RETURN R2 1
)"); )");
@ -6166,7 +6173,8 @@ local function test(a, b)
local c = (a :: number) local c = (a :: number)
return c + b return c + b
end end
)"), R"( )"),
R"(
ADD R2 R0 R1 ADD R2 R0 R1
RETURN R2 1 RETURN R2 1
)"); )");
@ -6180,7 +6188,8 @@ local function test(a, b)
b += 0 b += 0
return c + d return c + d
end end
)"), R"( )"),
R"(
MOVE R2 R0 MOVE R2 R0
ADDK R2 R2 K0 ADDK R2 R2 K0
MOVE R3 R1 MOVE R3 R1
@ -6196,7 +6205,8 @@ local function test(a, b)
local d = b local d = b
return c + d return c + d
end end
)"), R"( )"),
R"(
ADD R2 R0 R1 ADD R2 R0 R1
RETURN R2 1 RETURN R2 1
)"); )");
@ -6207,7 +6217,8 @@ local function test(a, b)
local c, d = a, b local c, d = a, b
return c + d return c + d
end end
)"), R"( )"),
R"(
MOVE R2 R0 MOVE R2 R0
MOVE R3 R1 MOVE R3 R1
ADD R4 R2 R3 ADD R4 R2 R3
@ -6221,7 +6232,9 @@ local function test(a, b)
local d = b local d = b
return function() return c + d end return function() return c + d end
end end
)", 1), R"( )",
1),
R"(
NEWCLOSURE R2 P0 NEWCLOSURE R2 P0
CAPTURE VAL R0 CAPTURE VAL R0
CAPTURE VAL R1 CAPTURE VAL R1

View File

@ -70,8 +70,8 @@ static int lua_loadstring(lua_State* L)
return 1; return 1;
lua_pushnil(L); lua_pushnil(L);
lua_insert(L, -2); /* put before error message */ lua_insert(L, -2); // put before error message
return 2; /* return nil plus error message */ return 2; // return nil plus error message
} }
static int lua_vector(lua_State* L) static int lua_vector(lua_State* L)
@ -244,8 +244,6 @@ TEST_CASE("Assert")
TEST_CASE("Basic") TEST_CASE("Basic")
{ {
ScopedFastFlag sff("LuauLenTM", true);
runConformance("basic.lua"); runConformance("basic.lua");
} }
@ -313,13 +311,14 @@ TEST_CASE("Literals")
TEST_CASE("Errors") TEST_CASE("Errors")
{ {
ScopedFastFlag sff("LuauNicerMethodErrors", true);
runConformance("errors.lua"); runConformance("errors.lua");
} }
TEST_CASE("Events") TEST_CASE("Events")
{ {
ScopedFastFlag sff1("LuauLenTM", true); ScopedFastFlag sff("LuauBetterNewindex", true);
ScopedFastFlag sff2("LuauBetterNewindex", true);
runConformance("events.lua"); runConformance("events.lua");
} }

View File

@ -410,7 +410,7 @@ void Fixture::validateErrors(const std::vector<Luau::TypeError>& errors)
LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source) LoadDefinitionFileResult Fixture::loadDefinition(const std::string& source)
{ {
unfreeze(typeChecker.globalTypes); unfreeze(typeChecker.globalTypes);
LoadDefinitionFileResult result = loadDefinitionFile(typeChecker, typeChecker.globalScope, source, "@test"); LoadDefinitionFileResult result = frontend.loadDefinitionFile(source, "@test");
freeze(typeChecker.globalTypes); freeze(typeChecker.globalTypes);
REQUIRE_MESSAGE(result.success, "loadDefinition: unable to load definition file"); REQUIRE_MESSAGE(result.success, "loadDefinition: unable to load definition file");

View File

@ -1030,7 +1030,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "reexport_cyclic_type")
ScopedFastFlag sff[] = { ScopedFastFlag sff[] = {
{"LuauForceExportSurfacesToBeNormal", true}, {"LuauForceExportSurfacesToBeNormal", true},
{"LuauLowerBoundsCalculation", true}, {"LuauLowerBoundsCalculation", true},
{"LuauNormalizeFlagIsConservative", true},
}; };
fileResolver.source["Module/A"] = R"( fileResolver.source["Module/A"] = R"(
@ -1067,7 +1066,6 @@ TEST_CASE_FIXTURE(BuiltinsFixture, "reexport_type_alias")
ScopedFastFlag sff[] = { ScopedFastFlag sff[] = {
{"LuauForceExportSurfacesToBeNormal", true}, {"LuauForceExportSurfacesToBeNormal", true},
{"LuauLowerBoundsCalculation", true}, {"LuauLowerBoundsCalculation", true},
{"LuauNormalizeFlagIsConservative", true},
}; };
fileResolver.source["Module/A"] = R"( fileResolver.source["Module/A"] = R"(

195
tests/JsonEmitter.test.cpp Normal file
View File

@ -0,0 +1,195 @@
// This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
#include "Luau/JsonEmitter.h"
#include "doctest.h"
using namespace Luau::Json;
TEST_SUITE_BEGIN("JsonEmitter");
TEST_CASE("write_array")
{
JsonEmitter emitter;
ArrayEmitter a = emitter.writeArray();
a.writeValue(123);
a.writeValue("foo");
a.finish();
std::string result = emitter.str();
CHECK(result == "[123,\"foo\"]");
}
TEST_CASE("write_object")
{
JsonEmitter emitter;
ObjectEmitter o = emitter.writeObject();
o.writePair("foo", "bar");
o.writePair("bar", "baz");
o.finish();
std::string result = emitter.str();
CHECK(result == "{\"foo\":\"bar\",\"bar\":\"baz\"}");
}
TEST_CASE("write_bool")
{
JsonEmitter emitter;
write(emitter, false);
CHECK(emitter.str() == "false");
emitter = JsonEmitter{};
write(emitter, true);
CHECK(emitter.str() == "true");
}
TEST_CASE("write_null")
{
JsonEmitter emitter;
write(emitter, nullptr);
CHECK(emitter.str() == "null");
}
TEST_CASE("write_string")
{
JsonEmitter emitter;
write(emitter, R"(foo,bar,baz,
"this should be escaped")");
CHECK(emitter.str() == "\"foo,bar,baz,\\n\\\"this should be escaped\\\"\"");
}
TEST_CASE("write_comma")
{
JsonEmitter emitter;
emitter.writeComma();
write(emitter, true);
emitter.writeComma();
write(emitter, false);
CHECK(emitter.str() == "true,false");
}
TEST_CASE("push_and_pop_comma")
{
JsonEmitter emitter;
emitter.writeComma();
write(emitter, true);
emitter.writeComma();
emitter.writeRaw('[');
bool comma = emitter.pushComma();
emitter.writeComma();
write(emitter, true);
emitter.writeComma();
write(emitter, false);
emitter.writeRaw(']');
emitter.popComma(comma);
emitter.writeComma();
write(emitter, false);
CHECK(emitter.str() == "true,[true,false],false");
}
TEST_CASE("write_optional")
{
JsonEmitter emitter;
emitter.writeComma();
write(emitter, std::optional<bool>{true});
emitter.writeComma();
write(emitter, std::nullopt);
CHECK(emitter.str() == "true,null");
}
TEST_CASE("write_vector")
{
std::vector<int> values{1, 2, 3, 4};
JsonEmitter emitter;
write(emitter, values);
CHECK(emitter.str() == "[1,2,3,4]");
}
TEST_CASE("prevent_multiple_object_finish")
{
JsonEmitter emitter;
ObjectEmitter o = emitter.writeObject();
o.writePair("a", "b");
o.finish();
o.finish();
CHECK(emitter.str() == "{\"a\":\"b\"}");
}
TEST_CASE("prevent_multiple_array_finish")
{
JsonEmitter emitter;
ArrayEmitter a = emitter.writeArray();
a.writeValue(1);
a.finish();
a.finish();
CHECK(emitter.str() == "[1]");
}
TEST_CASE("cannot_write_pair_after_finished")
{
JsonEmitter emitter;
ObjectEmitter o = emitter.writeObject();
o.finish();
o.writePair("a", "b");
CHECK(emitter.str() == "{}");
}
TEST_CASE("cannot_write_value_after_finished")
{
JsonEmitter emitter;
ArrayEmitter a = emitter.writeArray();
a.finish();
a.writeValue(1);
CHECK(emitter.str() == "[]");
}
TEST_CASE("finish_when_destructing_object")
{
JsonEmitter emitter;
emitter.writeObject();
CHECK(emitter.str() == "{}");
}
TEST_CASE("finish_when_destructing_array")
{
JsonEmitter emitter;
emitter.writeArray();
CHECK(emitter.str() == "[]");
}
namespace Luau::Json
{
struct Special
{
int foo;
int bar;
};
void write(JsonEmitter& emitter, const Special& value)
{
ObjectEmitter o = emitter.writeObject();
o.writePair("foo", value.foo);
o.writePair("bar", value.bar);
}
} // namespace Luau::Json
TEST_CASE("afford_extensibility")
{
std::vector<Special> vec{Special{1, 2}, Special{3, 4}};
JsonEmitter e;
write(e, vec);
std::string result = e.str();
CHECK(result == R"([{"foo":1,"bar":2},{"foo":3,"bar":4}])");
}
TEST_SUITE_END();

View File

@ -1675,4 +1675,36 @@ TEST_CASE_FIXTURE(Fixture, "WrongCommentOptimize")
CHECK_EQ(result.warnings[3].text, "optimize directive uses unknown optimization level '100500', 0..2 expected"); CHECK_EQ(result.warnings[3].text, "optimize directive uses unknown optimization level '100500', 0..2 expected");
} }
TEST_CASE_FIXTURE(Fixture, "LintIntegerParsing")
{
ScopedFastFlag luauLintParseIntegerIssues{"LuauLintParseIntegerIssues", true};
LintResult result = lint(R"(
local _ = 0b10000000000000000000000000000000000000000000000000000000000000000
local _ = 0x10000000000000000
)");
REQUIRE_EQ(result.warnings.size(), 2);
CHECK_EQ(result.warnings[0].text, "Binary number literal exceeded available precision and has been truncated to 2^64");
CHECK_EQ(result.warnings[1].text, "Hexadecimal number literal exceeded available precision and has been truncated to 2^64");
}
// TODO: remove with FFlagLuauErrorDoubleHexPrefix
TEST_CASE_FIXTURE(Fixture, "LintIntegerParsingDoublePrefix")
{
ScopedFastFlag luauLintParseIntegerIssues{"LuauLintParseIntegerIssues", true};
ScopedFastFlag luauErrorDoubleHexPrefix{"LuauErrorDoubleHexPrefix", false}; // Lint will be available until we start rejecting code
LintResult result = lint(R"(
local _ = 0x0x123
local _ = 0x0xffffffffffffffffffffffffffffffffff
)");
REQUIRE_EQ(result.warnings.size(), 2);
CHECK_EQ(result.warnings[0].text,
"Hexadecimal number literal has a double prefix, which will fail to parse in the future; remove the extra 0x to fix");
CHECK_EQ(result.warnings[1].text,
"Hexadecimal number literal has a double prefix, which will fail to parse in the future; remove the extra 0x to fix");
}
TEST_SUITE_END(); TEST_SUITE_END();

View File

@ -680,26 +680,12 @@ TEST_CASE_FIXTURE(Fixture, "higher_order_function_with_annotation")
CHECK_EQ("<a, b>((a) -> b, a) -> b", toString(requireType("apply"))); CHECK_EQ("<a, b>((a) -> b, a) -> b", toString(requireType("apply")));
} }
TEST_CASE_FIXTURE(Fixture, "cyclic_table_is_marked_normal")
{
ScopedFastFlag flags[] = {{"LuauLowerBoundsCalculation", true}, {"LuauNormalizeFlagIsConservative", false}};
check(R"(
type Fiber = {
return_: Fiber?
}
local f: Fiber
)");
TypeId t = requireType("f");
CHECK(t->normal);
}
// Unfortunately, getting this right in the general case is difficult. // Unfortunately, getting this right in the general case is difficult.
TEST_CASE_FIXTURE(Fixture, "cyclic_table_is_not_marked_normal") TEST_CASE_FIXTURE(Fixture, "cyclic_table_is_not_marked_normal")
{ {
ScopedFastFlag flags[] = {{"LuauLowerBoundsCalculation", true}, {"LuauNormalizeFlagIsConservative", true}}; ScopedFastFlag flags[] = {
{"LuauLowerBoundsCalculation", true},
};
check(R"( check(R"(
type Fiber = { type Fiber = {
@ -1081,7 +1067,6 @@ TEST_CASE_FIXTURE(Fixture, "bound_typevars_should_only_be_marked_normal_if_their
{ {
ScopedFastFlag sff[]{ ScopedFastFlag sff[]{
{"LuauLowerBoundsCalculation", true}, {"LuauLowerBoundsCalculation", true},
{"LuauNormalizeFlagIsConservative", true},
}; };
CheckResult result = check(R"( CheckResult result = check(R"(

View File

@ -682,20 +682,23 @@ TEST_CASE_FIXTURE(Fixture, "parse_numbers_binary")
TEST_CASE_FIXTURE(Fixture, "parse_numbers_error") TEST_CASE_FIXTURE(Fixture, "parse_numbers_error")
{ {
ScopedFastFlag luauErrorParseIntegerIssues{"LuauErrorParseIntegerIssues", true}; ScopedFastFlag luauLintParseIntegerIssues{"LuauLintParseIntegerIssues", true};
ScopedFastFlag luauErrorDoubleHexPrefix{"LuauErrorDoubleHexPrefix", true};
CHECK_EQ(getParseError("return 0b123"), "Malformed number"); CHECK_EQ(getParseError("return 0b123"), "Malformed number");
CHECK_EQ(getParseError("return 123x"), "Malformed number"); CHECK_EQ(getParseError("return 123x"), "Malformed number");
CHECK_EQ(getParseError("return 0xg"), "Malformed number"); CHECK_EQ(getParseError("return 0xg"), "Malformed number");
CHECK_EQ(getParseError("return 0x0x123"), "Malformed number"); CHECK_EQ(getParseError("return 0x0x123"), "Malformed number");
CHECK_EQ(getParseError("return 0xffffffffffffffffffffllllllg"), "Malformed number");
CHECK_EQ(getParseError("return 0x0xffffffffffffffffffffffffffff"), "Malformed number");
} }
TEST_CASE_FIXTURE(Fixture, "parse_numbers_range_error") TEST_CASE_FIXTURE(Fixture, "parse_numbers_error_soft")
{ {
ScopedFastFlag luauErrorParseIntegerIssues{"LuauErrorParseIntegerIssues", true}; ScopedFastFlag luauLintParseIntegerIssues{"LuauLintParseIntegerIssues", true};
ScopedFastFlag luauErrorDoubleHexPrefix{"LuauErrorDoubleHexPrefix", false};
CHECK_EQ(getParseError("return 0x10000000000000000"), "Integer number value is out of range"); CHECK_EQ(getParseError("return 0x0x0x0x0x0x0x0"), "Malformed number");
CHECK_EQ(getParseError("return 0b10000000000000000000000000000000000000000000000000000000000000000"), "Integer number value is out of range");
} }
TEST_CASE_FIXTURE(Fixture, "break_return_not_last_error") TEST_CASE_FIXTURE(Fixture, "break_return_not_last_error")

View File

@ -82,7 +82,7 @@ private:
capturedoutput = "" capturedoutput = ""
function arraytostring(arr) function arraytostring(arr)
local strings = {} local strings = {}
table.foreachi(arr, function(k,v) table.insert(strings, pptostring(v)) end ) table.foreachi(arr, function(k,v) table.insert(strings, pptostring(v)) end )
return "{" .. table.concat(strings, ", ") .. "}" return "{" .. table.concat(strings, ", ") .. "}"
end end

View File

@ -305,7 +305,6 @@ TEST_CASE_FIXTURE(Fixture, "stringifying_type_is_still_capped_when_exhaustive")
CHECK_EQ(toString(requireType("f2"), o), "((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>"); CHECK_EQ(toString(requireType("f2"), o), "((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
CHECK_EQ(toString(requireType("f3"), o), "(((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>"); CHECK_EQ(toString(requireType("f3"), o), "(((() -> ()) -> () -> ()) -> (() -> ()) -> ... <TRUNCATED>");
} }
} }
TEST_CASE_FIXTURE(Fixture, "stringifying_table_type_correctly_use_matching_table_state_braces") TEST_CASE_FIXTURE(Fixture, "stringifying_table_type_correctly_use_matching_table_state_braces")
@ -726,10 +725,6 @@ TEST_CASE_FIXTURE(Fixture, "toStringNamedFunction_overrides_param_names")
TEST_CASE_FIXTURE(Fixture, "pick_distinct_names_for_mixed_explicit_and_implicit_generics") TEST_CASE_FIXTURE(Fixture, "pick_distinct_names_for_mixed_explicit_and_implicit_generics")
{ {
ScopedFastFlag sff[] = {
{"LuauAlwaysQuantify", true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
function foo<a>(x: a, y) end function foo<a>(x: a, y) end
)"); )");

View File

@ -94,6 +94,65 @@ TEST_CASE_FIXTURE(Fixture, "cannot_steal_hoisted_type_alias")
} }
} }
TEST_CASE_FIXTURE(Fixture, "mismatched_generic_type_param")
{
CheckResult result = check(R"(
type T<A> = (A...) -> ()
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(toString(result.errors[0]) ==
"Generic type 'A' is used as a variadic type parameter; consider changing 'A' to 'A...' in the generic argument list");
CHECK(result.errors[0].location == Location{{1, 21}, {1, 25}});
}
TEST_CASE_FIXTURE(Fixture, "mismatched_generic_pack_type_param")
{
CheckResult result = check(R"(
type T<A...> = (A) -> ()
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(toString(result.errors[0]) ==
"Variadic type parameter 'A...' is used as a regular generic type; consider changing 'A...' to 'A' in the generic argument list");
CHECK(result.errors[0].location == Location{{1, 24}, {1, 25}});
}
TEST_CASE_FIXTURE(Fixture, "default_type_parameter")
{
CheckResult result = check(R"(
type T<A = number, B = string> = { a: A, b: B }
local x: T<string> = { a = "foo", b = "bar" }
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK(toString(requireType("x")) == "T<string, string>");
}
TEST_CASE_FIXTURE(Fixture, "default_pack_parameter")
{
CheckResult result = check(R"(
type T<A... = (number, string)> = { fn: (A...) -> () }
local x: T
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK(toString(requireType("x")) == "T<number, string>");
}
TEST_CASE_FIXTURE(Fixture, "saturate_to_first_type_pack")
{
CheckResult result = check(R"(
type T<A, B, C...> = { fn: (A, B) -> C... }
local x: T<string, number, string, boolean>
local f = x.fn
)");
LUAU_REQUIRE_NO_ERRORS(result);
CHECK(toString(requireType("x")) == "T<string, number, string, boolean>");
CHECK(toString(requireType("f")) == "(string, number) -> (string, boolean)");
}
TEST_CASE_FIXTURE(Fixture, "cyclic_types_of_named_table_fields_do_not_expand_when_stringified") TEST_CASE_FIXTURE(Fixture, "cyclic_types_of_named_table_fields_do_not_expand_when_stringified")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(
@ -126,6 +185,40 @@ TEST_CASE_FIXTURE(Fixture, "mutually_recursive_aliases")
LUAU_REQUIRE_NO_ERRORS(result); LUAU_REQUIRE_NO_ERRORS(result);
} }
TEST_CASE_FIXTURE(Fixture, "generic_aliases")
{
ScopedFastFlag sff_DebugLuauDeferredConstraintResolution{"DebugLuauDeferredConstraintResolution", true};
CheckResult result = check(R"(
type T<a> = { v: a }
local x: T<number> = { v = 123 }
local y: T<string> = { v = "foo" }
local bad: T<number> = { v = "foo" }
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(result.errors[0].location == Location{{4, 31}, {4, 44}});
CHECK(toString(result.errors[0]) == "Type '{ v: string }' could not be converted into 'T<number>'");
}
TEST_CASE_FIXTURE(Fixture, "dependent_generic_aliases")
{
ScopedFastFlag sff_DebugLuauDeferredConstraintResolution{"DebugLuauDeferredConstraintResolution", true};
CheckResult result = check(R"(
type T<a> = { v: a }
type U<a> = { t: T<a> }
local x: U<number> = { t = { v = 123 } }
local bad: U<number> = { t = { v = "foo" } }
)");
LUAU_REQUIRE_ERROR_COUNT(1, result);
CHECK(result.errors[0].location == Location{{4, 31}, {4, 52}});
CHECK(toString(result.errors[0]) == "Type '{ t: { v: string } }' could not be converted into 'U<number>'");
}
TEST_CASE_FIXTURE(Fixture, "mutually_recursive_generic_aliases") TEST_CASE_FIXTURE(Fixture, "mutually_recursive_generic_aliases")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(
@ -360,6 +453,7 @@ TEST_CASE_FIXTURE(Fixture, "stringify_optional_parameterized_alias")
LUAU_REQUIRE_ERROR_COUNT(1, result); LUAU_REQUIRE_ERROR_COUNT(1, result);
auto e = get<TypeMismatch>(result.errors[0]); auto e = get<TypeMismatch>(result.errors[0]);
REQUIRE(e != nullptr);
CHECK_EQ("Node<T>?", toString(e->givenType)); CHECK_EQ("Node<T>?", toString(e->givenType));
CHECK_EQ("Node<T>", toString(e->wantedType)); CHECK_EQ("Node<T>", toString(e->wantedType));
} }

View File

@ -1547,7 +1547,6 @@ caused by:
Argument count mismatch. Function expects 1 argument, but none are specified)", Argument count mismatch. Function expects 1 argument, but none are specified)",
toString(result.errors[0])); toString(result.errors[0]));
} }
} }
TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic") TEST_CASE_FIXTURE(Fixture, "too_few_arguments_variadic")

View File

@ -1186,10 +1186,6 @@ end)
TEST_CASE_FIXTURE(Fixture, "quantify_functions_even_if_they_have_an_explicit_generic") TEST_CASE_FIXTURE(Fixture, "quantify_functions_even_if_they_have_an_explicit_generic")
{ {
ScopedFastFlag sff[] = {
{"LuauAlwaysQuantify", true},
};
CheckResult result = check(R"( CheckResult result = check(R"(
function foo<X>(f, x: X) function foo<X>(f, x: X)
return f(x) return f(x)

View File

@ -485,7 +485,6 @@ TEST_CASE_FIXTURE(Fixture, "constrained_is_level_dependent")
{ {
ScopedFastFlag sff[]{ ScopedFastFlag sff[]{
{"LuauLowerBoundsCalculation", true}, {"LuauLowerBoundsCalculation", true},
{"LuauNormalizeFlagIsConservative", true},
{"LuauQuantifyConstrained", true}, {"LuauQuantifyConstrained", true},
}; };

View File

@ -262,7 +262,7 @@ TEST_CASE_FIXTURE(Fixture, "table_properties_type_error_escapes")
{ {
CheckResult result = check(R"( CheckResult result = check(R"(
--!strict --!strict
local x: { ["<>"] : number } local x: { ["<>"] : number }
x = { ["\n"] = 5 } x = { ["\n"] = 5 }
)"); )");

View File

@ -252,7 +252,6 @@ TEST_CASE_FIXTURE(Fixture, "type_errors_infer_types")
CHECK_EQ("<error-type>", toString(requireType("e"))); CHECK_EQ("<error-type>", toString(requireType("e")));
CHECK_EQ("<error-type>", toString(requireType("f"))); CHECK_EQ("<error-type>", toString(requireType("f")));
} }
} }
} }

View File

@ -203,7 +203,7 @@ TEST_CASE_FIXTURE(Fixture, "variadic_packs")
), ),
"@test" "@test"
); );
addGlobalBinding(typeChecker, "bar", addGlobalBinding(typeChecker, "bar",
arena.addType( arena.addType(
FunctionTypeVar{ FunctionTypeVar{
arena.addTypePack({{typeChecker.numberType}, listOfStrings}), arena.addTypePack({{typeChecker.numberType}, listOfStrings}),

View File

@ -388,4 +388,11 @@ assert(ecall(function() for i='a',2 do end end) == "invalid 'for' initial value
assert(ecall(function() for i=1,'a' do end end) == "invalid 'for' limit (number expected, got string)") assert(ecall(function() for i=1,'a' do end end) == "invalid 'for' limit (number expected, got string)")
assert(ecall(function() for i=1,2,'a' do end end) == "invalid 'for' step (number expected, got string)") assert(ecall(function() for i=1,2,'a' do end end) == "invalid 'for' step (number expected, got string)")
-- method call errors
assert(ecall(function() ({}):foo() end) == "attempt to call missing method 'foo' of table")
assert(ecall(function() (""):foo() end) == "attempt to call missing method 'foo' of string")
assert(ecall(function() (42):foo() end) == "attempt to index number with 'foo'")
assert(ecall(function() ({foo=42}):foo() end) == "attempt to call a number value")
assert(ecall(function() local ud = newproxy(true) getmetatable(ud).__index = {} ud:foo() end) == "attempt to call missing method 'foo' of userdata")
return('OK') return('OK')

View File

@ -61,7 +61,7 @@ static bool debuggerPresent()
static int testAssertionHandler(const char* expr, const char* file, int line, const char* function) static int testAssertionHandler(const char* expr, const char* file, int line, const char* function)
{ {
if (debuggerPresent()) if (debuggerPresent())
LUAU_DEBUGBREAK(); LUAU_DEBUGBREAK();
ADD_FAIL_AT(file, line, "Assertion failed: ", std::string(expr)); ADD_FAIL_AT(file, line, "Assertion failed: ", std::string(expr));
return 1; return 1;

View File

@ -4,14 +4,11 @@ AnnotationTests.as_expr_warns_on_unrelated_cast
AnnotationTests.builtin_types_are_not_exported AnnotationTests.builtin_types_are_not_exported
AnnotationTests.cannot_use_nonexported_type AnnotationTests.cannot_use_nonexported_type
AnnotationTests.cloned_interface_maintains_pointers_between_definitions AnnotationTests.cloned_interface_maintains_pointers_between_definitions
AnnotationTests.corecursive_types_error_on_tight_loop
AnnotationTests.define_generic_type_alias AnnotationTests.define_generic_type_alias
AnnotationTests.duplicate_type_param_name AnnotationTests.duplicate_type_param_name
AnnotationTests.for_loop_counter_annotation_is_checked AnnotationTests.for_loop_counter_annotation_is_checked
AnnotationTests.function_return_annotations_are_checked AnnotationTests.function_return_annotations_are_checked
AnnotationTests.generic_aliases_are_cloned_properly AnnotationTests.generic_aliases_are_cloned_properly
AnnotationTests.instantiate_type_fun_should_not_trip_rbxassert
AnnotationTests.instantiation_clone_has_to_follow
AnnotationTests.interface_types_belong_to_interface_arena AnnotationTests.interface_types_belong_to_interface_arena
AnnotationTests.luau_ice_triggers_an_ice AnnotationTests.luau_ice_triggers_an_ice
AnnotationTests.luau_ice_triggers_an_ice_exception_with_flag AnnotationTests.luau_ice_triggers_an_ice_exception_with_flag
@ -24,14 +21,9 @@ AnnotationTests.occurs_check_on_cyclic_union_typevar
AnnotationTests.self_referential_type_alias AnnotationTests.self_referential_type_alias
AnnotationTests.too_many_type_params AnnotationTests.too_many_type_params
AnnotationTests.two_type_params AnnotationTests.two_type_params
AnnotationTests.type_alias_always_resolve_to_a_real_type
AnnotationTests.type_alias_B_should_check_with_another_aliases_until_a_non_aliased_type
AnnotationTests.type_alias_should_alias_to_number
AnnotationTests.type_aliasing_to_number_should_not_check_given_a_string
AnnotationTests.type_annotations_inside_function_bodies AnnotationTests.type_annotations_inside_function_bodies
AnnotationTests.type_assertion_expr AnnotationTests.type_assertion_expr
AnnotationTests.typeof_variable_type_annotation_should_return_its_type AnnotationTests.unknown_type_reference_generates_error
AnnotationTests.use_generic_type_alias
AnnotationTests.use_type_required_from_another_file AnnotationTests.use_type_required_from_another_file
AstQuery.last_argument_function_call_type AstQuery.last_argument_function_call_type
AstQuery::getDocumentationSymbolAtPosition.binding AstQuery::getDocumentationSymbolAtPosition.binding
@ -42,11 +34,8 @@ AutocompleteTest.argument_types
AutocompleteTest.arguments_to_global_lambda AutocompleteTest.arguments_to_global_lambda
AutocompleteTest.as_types AutocompleteTest.as_types
AutocompleteTest.autocomplete_boolean_singleton AutocompleteTest.autocomplete_boolean_singleton
AutocompleteTest.autocomplete_default_type_pack_parameters
AutocompleteTest.autocomplete_default_type_parameters
AutocompleteTest.autocomplete_end_with_fn_exprs AutocompleteTest.autocomplete_end_with_fn_exprs
AutocompleteTest.autocomplete_end_with_lambda AutocompleteTest.autocomplete_end_with_lambda
AutocompleteTest.autocomplete_explicit_type_pack
AutocompleteTest.autocomplete_first_function_arg_expected_type AutocompleteTest.autocomplete_first_function_arg_expected_type
AutocompleteTest.autocomplete_for_in_middle_keywords AutocompleteTest.autocomplete_for_in_middle_keywords
AutocompleteTest.autocomplete_for_middle_keywords AutocompleteTest.autocomplete_for_middle_keywords
@ -93,7 +82,6 @@ AutocompleteTest.local_function_params
AutocompleteTest.local_functions_fall_out_of_scope AutocompleteTest.local_functions_fall_out_of_scope
AutocompleteTest.method_call_inside_function_body AutocompleteTest.method_call_inside_function_body
AutocompleteTest.module_type_members AutocompleteTest.module_type_members
AutocompleteTest.modules_with_types
AutocompleteTest.nested_member_completions AutocompleteTest.nested_member_completions
AutocompleteTest.nested_recursive_function AutocompleteTest.nested_recursive_function
AutocompleteTest.no_function_name_suggestions AutocompleteTest.no_function_name_suggestions
@ -101,8 +89,6 @@ AutocompleteTest.no_incompatible_self_calls
AutocompleteTest.no_incompatible_self_calls_2 AutocompleteTest.no_incompatible_self_calls_2
AutocompleteTest.no_incompatible_self_calls_on_class AutocompleteTest.no_incompatible_self_calls_on_class
AutocompleteTest.no_wrong_compatible_self_calls_with_generics AutocompleteTest.no_wrong_compatible_self_calls_with_generics
AutocompleteTest.optional_members
AutocompleteTest.private_types
AutocompleteTest.recursive_function AutocompleteTest.recursive_function
AutocompleteTest.recursive_function_global AutocompleteTest.recursive_function_global
AutocompleteTest.recursive_function_local AutocompleteTest.recursive_function_local
@ -114,7 +100,6 @@ AutocompleteTest.stop_at_first_stat_when_recommending_keywords
AutocompleteTest.string_prim_non_self_calls_are_avoided AutocompleteTest.string_prim_non_self_calls_are_avoided
AutocompleteTest.string_prim_self_calls_are_fine AutocompleteTest.string_prim_self_calls_are_fine
AutocompleteTest.suggest_external_module_type AutocompleteTest.suggest_external_module_type
AutocompleteTest.suggest_table_keys
AutocompleteTest.table_intersection AutocompleteTest.table_intersection
AutocompleteTest.table_union AutocompleteTest.table_union
AutocompleteTest.type_correct_argument_type_suggestion AutocompleteTest.type_correct_argument_type_suggestion
@ -133,8 +118,6 @@ AutocompleteTest.type_correct_local_type_suggestion
AutocompleteTest.type_correct_sealed_table AutocompleteTest.type_correct_sealed_table
AutocompleteTest.type_correct_suggestion_for_overloads AutocompleteTest.type_correct_suggestion_for_overloads
AutocompleteTest.type_correct_suggestion_in_argument AutocompleteTest.type_correct_suggestion_in_argument
AutocompleteTest.type_correct_suggestion_in_table
AutocompleteTest.type_scoping_easy
AutocompleteTest.unsealed_table AutocompleteTest.unsealed_table
AutocompleteTest.unsealed_table_2 AutocompleteTest.unsealed_table_2
AutocompleteTest.user_defined_local_functions_in_own_definition AutocompleteTest.user_defined_local_functions_in_own_definition
@ -259,7 +242,6 @@ GenericsTests.check_mutual_generic_functions
GenericsTests.correctly_instantiate_polymorphic_member_functions GenericsTests.correctly_instantiate_polymorphic_member_functions
GenericsTests.do_not_always_instantiate_generic_intersection_types GenericsTests.do_not_always_instantiate_generic_intersection_types
GenericsTests.do_not_infer_generic_functions GenericsTests.do_not_infer_generic_functions
GenericsTests.dont_substitute_bound_types
GenericsTests.dont_unify_bound_types GenericsTests.dont_unify_bound_types
GenericsTests.duplicate_generic_type_packs GenericsTests.duplicate_generic_type_packs
GenericsTests.duplicate_generic_types GenericsTests.duplicate_generic_types
@ -275,6 +257,7 @@ GenericsTests.generic_functions_dont_cache_type_parameters
GenericsTests.generic_functions_in_types GenericsTests.generic_functions_in_types
GenericsTests.generic_functions_should_be_memory_safe GenericsTests.generic_functions_should_be_memory_safe
GenericsTests.generic_table_method GenericsTests.generic_table_method
GenericsTests.generic_type_pack_parentheses
GenericsTests.generic_type_pack_syntax GenericsTests.generic_type_pack_syntax
GenericsTests.generic_type_pack_unification1 GenericsTests.generic_type_pack_unification1
GenericsTests.generic_type_pack_unification2 GenericsTests.generic_type_pack_unification2
@ -297,15 +280,12 @@ GenericsTests.properties_can_be_polytypes
GenericsTests.rank_N_types_via_typeof GenericsTests.rank_N_types_via_typeof
GenericsTests.reject_clashing_generic_and_pack_names GenericsTests.reject_clashing_generic_and_pack_names
GenericsTests.self_recursive_instantiated_param GenericsTests.self_recursive_instantiated_param
GenericsTests.substitution_with_bound_table
GenericsTests.typefuns_sharing_types
GenericsTests.variadic_generics GenericsTests.variadic_generics
IntersectionTypes.argument_is_intersection IntersectionTypes.argument_is_intersection
IntersectionTypes.error_detailed_intersection_all IntersectionTypes.error_detailed_intersection_all
IntersectionTypes.error_detailed_intersection_part IntersectionTypes.error_detailed_intersection_part
IntersectionTypes.fx_intersection_as_argument IntersectionTypes.fx_intersection_as_argument
IntersectionTypes.fx_union_as_argument_fails IntersectionTypes.fx_union_as_argument_fails
IntersectionTypes.index_on_an_intersection_type_with_all_parts_missing_the_property
IntersectionTypes.index_on_an_intersection_type_with_mixed_types IntersectionTypes.index_on_an_intersection_type_with_mixed_types
IntersectionTypes.index_on_an_intersection_type_with_one_part_missing_the_property IntersectionTypes.index_on_an_intersection_type_with_one_part_missing_the_property
IntersectionTypes.index_on_an_intersection_type_with_one_property_of_type_any IntersectionTypes.index_on_an_intersection_type_with_one_property_of_type_any
@ -313,12 +293,8 @@ IntersectionTypes.index_on_an_intersection_type_with_property_guaranteed_to_exis
IntersectionTypes.index_on_an_intersection_type_works_at_arbitrary_depth IntersectionTypes.index_on_an_intersection_type_works_at_arbitrary_depth
IntersectionTypes.no_stack_overflow_from_flattenintersection IntersectionTypes.no_stack_overflow_from_flattenintersection
IntersectionTypes.overload_is_not_a_function IntersectionTypes.overload_is_not_a_function
IntersectionTypes.propagates_name
IntersectionTypes.select_correct_union_fn IntersectionTypes.select_correct_union_fn
IntersectionTypes.should_still_pick_an_overload_whose_arguments_are_unions IntersectionTypes.should_still_pick_an_overload_whose_arguments_are_unions
IntersectionTypes.table_combines
IntersectionTypes.table_combines_missing
IntersectionTypes.table_extra_ok
IntersectionTypes.table_intersection_setmetatable IntersectionTypes.table_intersection_setmetatable
IntersectionTypes.table_intersection_write IntersectionTypes.table_intersection_write
IntersectionTypes.table_intersection_write_sealed IntersectionTypes.table_intersection_write_sealed
@ -332,7 +308,6 @@ isSubtype.table_with_table_prop
isSubtype.tables isSubtype.tables
Linter.DeprecatedApi Linter.DeprecatedApi
Linter.TableOperations Linter.TableOperations
ModuleTests.any_persistance_does_not_leak
ModuleTests.builtin_types_point_into_globalTypes_arena ModuleTests.builtin_types_point_into_globalTypes_arena
ModuleTests.clone_self_property ModuleTests.clone_self_property
ModuleTests.deepClone_cyclic_table ModuleTests.deepClone_cyclic_table
@ -355,8 +330,6 @@ NonstrictModeTests.table_props_are_any
Normalize.any_wins_the_battle_over_unknown_in_unions Normalize.any_wins_the_battle_over_unknown_in_unions
Normalize.constrained_intersection_of_intersections Normalize.constrained_intersection_of_intersections
Normalize.cyclic_intersection Normalize.cyclic_intersection
Normalize.cyclic_table_is_marked_normal
Normalize.cyclic_table_is_not_marked_normal
Normalize.cyclic_table_normalizes_sensibly Normalize.cyclic_table_normalizes_sensibly
Normalize.cyclic_union Normalize.cyclic_union
Normalize.fuzz_failure_bound_type_is_normal_but_not_its_bounded_to Normalize.fuzz_failure_bound_type_is_normal_but_not_its_bounded_to
@ -490,16 +463,9 @@ TableTests.cannot_change_type_of_unsealed_table_prop
TableTests.casting_sealed_tables_with_props_into_table_with_indexer TableTests.casting_sealed_tables_with_props_into_table_with_indexer
TableTests.casting_tables_with_props_into_table_with_indexer3 TableTests.casting_tables_with_props_into_table_with_indexer3
TableTests.casting_tables_with_props_into_table_with_indexer4 TableTests.casting_tables_with_props_into_table_with_indexer4
TableTests.casting_unsealed_tables_with_props_into_table_with_indexer
TableTests.checked_prop_too_early TableTests.checked_prop_too_early
TableTests.common_table_element_general
TableTests.common_table_element_inner_index
TableTests.common_table_element_inner_prop
TableTests.common_table_element_list
TableTests.common_table_element_union_assignment
TableTests.common_table_element_union_in_call TableTests.common_table_element_union_in_call
TableTests.common_table_element_union_in_call_tail TableTests.common_table_element_union_in_call_tail
TableTests.common_table_element_union_in_prop
TableTests.confusing_indexing TableTests.confusing_indexing
TableTests.defining_a_method_for_a_builtin_sealed_table_must_fail TableTests.defining_a_method_for_a_builtin_sealed_table_must_fail
TableTests.defining_a_method_for_a_local_sealed_table_must_fail TableTests.defining_a_method_for_a_local_sealed_table_must_fail
@ -583,7 +549,6 @@ TableTests.right_table_missing_key
TableTests.right_table_missing_key2 TableTests.right_table_missing_key2
TableTests.scalar_is_a_subtype_of_a_compatible_polymorphic_shape_type TableTests.scalar_is_a_subtype_of_a_compatible_polymorphic_shape_type
TableTests.scalar_is_not_a_subtype_of_a_compatible_polymorphic_shape_type TableTests.scalar_is_not_a_subtype_of_a_compatible_polymorphic_shape_type
TableTests.setmetatable_cant_be_used_to_mutate_global_types
TableTests.shared_selfs TableTests.shared_selfs
TableTests.shared_selfs_from_free_param TableTests.shared_selfs_from_free_param
TableTests.shared_selfs_through_metatables TableTests.shared_selfs_through_metatables
@ -611,7 +576,6 @@ TableTests.used_colon_correctly
TableTests.used_colon_instead_of_dot TableTests.used_colon_instead_of_dot
TableTests.used_dot_instead_of_colon TableTests.used_dot_instead_of_colon
TableTests.used_dot_instead_of_colon_but_correctly TableTests.used_dot_instead_of_colon_but_correctly
TableTests.user_defined_table_types_are_named
TableTests.width_subtyping TableTests.width_subtyping
ToDot.bound_table ToDot.bound_table
ToDot.class ToDot.class
@ -620,7 +584,6 @@ ToDot.metatable
ToDot.primitive ToDot.primitive
ToDot.table ToDot.table
ToString.exhaustive_toString_of_cyclic_table ToString.exhaustive_toString_of_cyclic_table
ToString.function_type_with_argument_names
ToString.function_type_with_argument_names_and_self ToString.function_type_with_argument_names_and_self
ToString.function_type_with_argument_names_generic ToString.function_type_with_argument_names_generic
ToString.named_metatable_toStringNamedFunction ToString.named_metatable_toStringNamedFunction
@ -640,48 +603,27 @@ TryUnifyTests.members_of_failed_typepack_unification_are_unified_with_errorType
TryUnifyTests.result_of_failed_typepack_unification_is_constrained TryUnifyTests.result_of_failed_typepack_unification_is_constrained
TryUnifyTests.typepack_unification_should_trim_free_tails TryUnifyTests.typepack_unification_should_trim_free_tails
TryUnifyTests.variadics_should_use_reversed_properly TryUnifyTests.variadics_should_use_reversed_properly
TypeAliases.basic_alias
TypeAliases.cannot_steal_hoisted_type_alias
TypeAliases.cli_38393_recursive_intersection_oom TypeAliases.cli_38393_recursive_intersection_oom
TypeAliases.corecursive_function_types
TypeAliases.corecursive_types_generic TypeAliases.corecursive_types_generic
TypeAliases.cyclic_function_type_in_type_alias
TypeAliases.cyclic_types_of_named_table_fields_do_not_expand_when_stringified
TypeAliases.do_not_quantify_unresolved_aliases TypeAliases.do_not_quantify_unresolved_aliases
TypeAliases.dont_stop_typechecking_after_reporting_duplicate_type_definition
TypeAliases.export_type_and_type_alias_are_duplicates
TypeAliases.forward_declared_alias_is_not_clobbered_by_prior_unification_with_any TypeAliases.forward_declared_alias_is_not_clobbered_by_prior_unification_with_any
TypeAliases.forward_declared_alias_is_not_clobbered_by_prior_unification_with_any_2
TypeAliases.free_variables_from_typeof_in_aliases
TypeAliases.general_require_multi_assign TypeAliases.general_require_multi_assign
TypeAliases.generic_param_remap TypeAliases.generic_param_remap
TypeAliases.generic_typevars_are_not_considered_to_escape_their_scope_if_they_are_reused_in_multiple_aliases TypeAliases.mismatched_generic_pack_type_param
TypeAliases.module_export_free_type_leak TypeAliases.mismatched_generic_type_param
TypeAliases.module_export_wrapped_free_type_leak
TypeAliases.mutually_recursive_aliases
TypeAliases.mutually_recursive_generic_aliases TypeAliases.mutually_recursive_generic_aliases
TypeAliases.mutually_recursive_types_errors
TypeAliases.mutually_recursive_types_restriction_not_ok_1 TypeAliases.mutually_recursive_types_restriction_not_ok_1
TypeAliases.mutually_recursive_types_restriction_not_ok_2 TypeAliases.mutually_recursive_types_restriction_not_ok_2
TypeAliases.mutually_recursive_types_restriction_ok
TypeAliases.mutually_recursive_types_swapsies_not_ok TypeAliases.mutually_recursive_types_swapsies_not_ok
TypeAliases.mutually_recursive_types_swapsies_ok
TypeAliases.names_are_ascribed
TypeAliases.non_recursive_aliases_that_reuse_a_generic_name
TypeAliases.recursive_types_restriction_not_ok TypeAliases.recursive_types_restriction_not_ok
TypeAliases.recursive_types_restriction_ok
TypeAliases.reported_location_is_correct_when_type_alias_are_duplicates
TypeAliases.stringify_optional_parameterized_alias TypeAliases.stringify_optional_parameterized_alias
TypeAliases.stringify_type_alias_of_recursive_template_table_type TypeAliases.stringify_type_alias_of_recursive_template_table_type
TypeAliases.stringify_type_alias_of_recursive_template_table_type2 TypeAliases.stringify_type_alias_of_recursive_template_table_type2
TypeAliases.type_alias_fwd_declaration_is_precise
TypeAliases.type_alias_import_mutation TypeAliases.type_alias_import_mutation
TypeAliases.type_alias_local_mutation TypeAliases.type_alias_local_mutation
TypeAliases.type_alias_local_rename TypeAliases.type_alias_local_rename
TypeAliases.type_alias_local_synthetic_mutation
TypeAliases.type_alias_of_an_imported_recursive_generic_type TypeAliases.type_alias_of_an_imported_recursive_generic_type
TypeAliases.type_alias_of_an_imported_recursive_type TypeAliases.type_alias_of_an_imported_recursive_type
TypeAliases.use_table_name_and_generic_params_in_errors
TypeInfer.check_expr_recursion_limit TypeInfer.check_expr_recursion_limit
TypeInfer.checking_should_not_ice TypeInfer.checking_should_not_ice
TypeInfer.cli_50041_committing_txnlog_in_apollo_client_error TypeInfer.cli_50041_committing_txnlog_in_apollo_client_error
@ -698,9 +640,7 @@ TypeInfer.infer_assignment_value_types_mutable_lval
TypeInfer.infer_through_group_expr TypeInfer.infer_through_group_expr
TypeInfer.infer_type_assertion_value_type TypeInfer.infer_type_assertion_value_type
TypeInfer.no_heap_use_after_free_error TypeInfer.no_heap_use_after_free_error
TypeInfer.no_infinite_loop_when_trying_to_unify_uh_this
TypeInfer.no_stack_overflow_from_isoptional TypeInfer.no_stack_overflow_from_isoptional
TypeInfer.no_stack_overflow_from_isoptional2
TypeInfer.recursive_metatable_crash TypeInfer.recursive_metatable_crash
TypeInfer.tc_after_error_recovery_no_replacement_name_in_error TypeInfer.tc_after_error_recovery_no_replacement_name_in_error
TypeInfer.tc_if_else_expressions1 TypeInfer.tc_if_else_expressions1
@ -946,6 +886,8 @@ TypeInferUnknownNever.length_of_never
TypeInferUnknownNever.math_operators_and_never TypeInferUnknownNever.math_operators_and_never
TypeInferUnknownNever.never_is_reflexive TypeInferUnknownNever.never_is_reflexive
TypeInferUnknownNever.never_subtype_and_string_supertype TypeInferUnknownNever.never_subtype_and_string_supertype
TypeInferUnknownNever.pick_never_from_variadic_type_pack
TypeInferUnknownNever.string_subtype_and_never_supertype
TypeInferUnknownNever.string_subtype_and_unknown_supertype TypeInferUnknownNever.string_subtype_and_unknown_supertype
TypeInferUnknownNever.table_with_prop_of_type_never_is_also_reflexive TypeInferUnknownNever.table_with_prop_of_type_never_is_also_reflexive
TypeInferUnknownNever.table_with_prop_of_type_never_is_uninhabitable TypeInferUnknownNever.table_with_prop_of_type_never_is_uninhabitable
@ -953,6 +895,7 @@ TypeInferUnknownNever.type_packs_containing_never_is_itself_uninhabitable
TypeInferUnknownNever.type_packs_containing_never_is_itself_uninhabitable2 TypeInferUnknownNever.type_packs_containing_never_is_itself_uninhabitable2
TypeInferUnknownNever.unary_minus_of_never TypeInferUnknownNever.unary_minus_of_never
TypeInferUnknownNever.unknown_is_reflexive TypeInferUnknownNever.unknown_is_reflexive
TypeInferUnknownNever.unknown_subtype_and_string_supertype
TypePackTests.cyclic_type_packs TypePackTests.cyclic_type_packs
TypePackTests.higher_order_function TypePackTests.higher_order_function
TypePackTests.multiple_varargs_inference_are_not_confused TypePackTests.multiple_varargs_inference_are_not_confused
@ -965,18 +908,13 @@ TypePackTests.type_alias_default_export
TypePackTests.type_alias_default_mixed_self TypePackTests.type_alias_default_mixed_self
TypePackTests.type_alias_default_type_chained TypePackTests.type_alias_default_type_chained
TypePackTests.type_alias_default_type_errors TypePackTests.type_alias_default_type_errors
TypePackTests.type_alias_default_type_explicit
TypePackTests.type_alias_default_type_pack_explicit
TypePackTests.type_alias_default_type_pack_self_chained_tp TypePackTests.type_alias_default_type_pack_self_chained_tp
TypePackTests.type_alias_default_type_pack_self_tp TypePackTests.type_alias_default_type_pack_self_tp
TypePackTests.type_alias_default_type_pack_self_ty
TypePackTests.type_alias_default_type_self TypePackTests.type_alias_default_type_self
TypePackTests.type_alias_default_type_skip_brackets
TypePackTests.type_alias_defaults_confusing_types TypePackTests.type_alias_defaults_confusing_types
TypePackTests.type_alias_defaults_recursive_type TypePackTests.type_alias_defaults_recursive_type
TypePackTests.type_alias_type_pack_explicit TypePackTests.type_alias_type_pack_explicit
TypePackTests.type_alias_type_pack_explicit_multi TypePackTests.type_alias_type_pack_explicit_multi
TypePackTests.type_alias_type_pack_explicit_multi_tostring
TypePackTests.type_alias_type_pack_multi TypePackTests.type_alias_type_pack_multi
TypePackTests.type_alias_type_pack_variadic TypePackTests.type_alias_type_pack_variadic
TypePackTests.type_alias_type_packs TypePackTests.type_alias_type_packs
@ -1010,19 +948,13 @@ TypeSingletons.string_singletons
TypeSingletons.string_singletons_escape_chars TypeSingletons.string_singletons_escape_chars
TypeSingletons.string_singletons_mismatch TypeSingletons.string_singletons_mismatch
TypeSingletons.table_insert_with_a_singleton_argument TypeSingletons.table_insert_with_a_singleton_argument
TypeSingletons.table_properties_alias_or_parens_is_indexer
TypeSingletons.table_properties_singleton_strings
TypeSingletons.table_properties_singleton_strings_mismatch
TypeSingletons.table_properties_type_error_escapes TypeSingletons.table_properties_type_error_escapes
TypeSingletons.tagged_unions_immutable_tag
TypeSingletons.tagged_unions_using_singletons TypeSingletons.tagged_unions_using_singletons
TypeSingletons.tagged_unions_using_singletons_mismatch
TypeSingletons.taking_the_length_of_string_singleton TypeSingletons.taking_the_length_of_string_singleton
TypeSingletons.taking_the_length_of_union_of_string_singleton TypeSingletons.taking_the_length_of_union_of_string_singleton
TypeSingletons.widen_the_supertype_if_it_is_free_and_subtype_has_singleton TypeSingletons.widen_the_supertype_if_it_is_free_and_subtype_has_singleton
TypeSingletons.widening_happens_almost_everywhere TypeSingletons.widening_happens_almost_everywhere
TypeSingletons.widening_happens_almost_everywhere_except_for_tables TypeSingletons.widening_happens_almost_everywhere_except_for_tables
TypeVarTests.visit_once
UnionTypes.error_detailed_optional UnionTypes.error_detailed_optional
UnionTypes.error_detailed_union_all UnionTypes.error_detailed_union_all
UnionTypes.error_detailed_union_part UnionTypes.error_detailed_union_part
@ -1046,6 +978,5 @@ UnionTypes.optional_union_members
UnionTypes.optional_union_methods UnionTypes.optional_union_methods
UnionTypes.return_types_can_be_disjoint UnionTypes.return_types_can_be_disjoint
UnionTypes.table_union_write_indirect UnionTypes.table_union_write_indirect
UnionTypes.unify_sealed_table_union_check
UnionTypes.unify_unsealed_table_union_check UnionTypes.unify_unsealed_table_union_check
UnionTypes.union_equality_comparisons UnionTypes.union_equality_comparisons

View File

@ -6,6 +6,12 @@
import sys import sys
import svg import svg
import argparse
import json
argumentParser = argparse.ArgumentParser(description='Generate flamegraph SVG from Luau sampling profiler dumps')
argumentParser.add_argument('source_file', type=open)
argumentParser.add_argument('--json', dest='useJson',action='store_const',const=1,default=0,help='Parse source_file as JSON')
class Node(svg.Node): class Node(svg.Node):
def __init__(self): def __init__(self):
@ -27,26 +33,64 @@ class Node(svg.Node):
def details(self, root): def details(self, root):
return "Function: {} [{}:{}] ({:,} usec, {:.1%}); self: {:,} usec".format(self.function, self.source, self.line, self.width, self.width / root.width, self.ticks) return "Function: {} [{}:{}] ({:,} usec, {:.1%}); self: {:,} usec".format(self.function, self.source, self.line, self.width, self.width / root.width, self.ticks)
with open(sys.argv[1]) as f:
dump = f.readlines()
root = Node() def nodeFromCallstackListFile(source_file):
dump = source_file.readlines()
root = Node()
for l in dump: for l in dump:
ticks, stack = l.strip().split(" ", 1) ticks, stack = l.strip().split(" ", 1)
node = root node = root
for f in reversed(stack.split(";")): for f in reversed(stack.split(";")):
source, function, line = f.split(",") source, function, line = f.split(",")
child = node.child(f) child = node.child(f)
child.function = function child.function = function
child.source = source child.source = source
child.line = int(line) if len(line) > 0 else 0 child.line = int(line) if len(line) > 0 else 0
node = child
node.ticks += int(ticks)
return root
def nodeFromJSONbject(node, key, obj):
source, function, line = key.split(",")
node.function = function
node.source = source
node.line = int(line) if len(line) > 0 else 0
node.ticks = obj['Duration']
for key, obj in obj['Children'].items():
nodeFromJSONbject(node.child(key), key, obj)
return node
def nodeFromJSONFile(source_file):
dump = json.load(source_file)
root = Node()
for key, obj in dump['Children'].items():
nodeFromJSONbject(root.child(key), key, obj)
return root
arguments = argumentParser.parse_args()
if arguments.useJson:
root = nodeFromJSONFile(arguments.source_file)
else:
root = nodeFromCallstackListFile(arguments.source_file)
node = child
node.ticks += int(ticks)
svg.layout(root, lambda n: n.ticks) svg.layout(root, lambda n: n.ticks)
svg.display(root, "Flame Graph", "hot", flip = True) svg.display(root, "Flame Graph", "hot", flip = True)

View File

@ -37,17 +37,15 @@ class Handler(x.ContentHandler):
elif name == "OverallResultsAsserts": elif name == "OverallResultsAsserts":
if self.currentTest: if self.currentTest:
failed = 0 != safeParseInt(attrs["failures"]) passed = 0 == safeParseInt(attrs["failures"])
dottedName = ".".join(self.currentTest) dottedName = ".".join(self.currentTest)
shouldFail = dottedName in self.failList
if failed and not shouldFail: # Sometimes we get multiple XML trees for the same test. All of
print("UNEXPECTED: {} should have passed".format(dottedName)) # them must report a pass in order for us to consider the test
elif not failed and shouldFail: # to have passed.
print("UNEXPECTED: {} should have failed".format(dottedName)) r = self.results.get(dottedName, True)
self.results[dottedName] = r and passed
self.results[dottedName] = not failed
elif name == 'OverallResultsTestCases': elif name == 'OverallResultsTestCases':
self.numSkippedTests = safeParseInt(attrs.get("skipped", 0)) self.numSkippedTests = safeParseInt(attrs.get("skipped", 0))
@ -104,6 +102,12 @@ def main():
p.wait() p.wait()
for testName, passed in handler.results.items():
if passed and testName in failList:
print('UNEXPECTED: {} should have failed'.format(testName))
elif not passed and testName not in failList:
print('UNEXPECTED: {} should have passed'.format(testName))
if args.write: if args.write:
newFailList = sorted( newFailList = sorted(
( (